diff options
667 files changed, 59984 insertions, 13452 deletions
diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..ec3de7bb2 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,42 @@ +Steven K. Reinhardt +----------------------- + +Nathan L. Binkert +----------------------- + +Erik G. Hallnor +----------------------- + +Steve E. Raasch +----------------------- + +Lisa R. Hsu +----------------------- + +Ali G. Saidi +----------------------- + +Andrew L. Schultz +----------------------- + +Kevin T. Lim +----------------------- + +Ronald G. Dreslinski Jr +----------------------- + +Gabriel Black +----------------------- + +Korey L. Sewell +----------------------- + +David Green +----------------------- + +Benjamin S. Nash +----------------------- + +Miguel J. Serrano +----------------------- + @@ -1,4 +1,4 @@ -Copyright (c) 2000-2005 The Regents of The University of Michigan +Copyright (c) 2000-2006 The Regents of The University of Michigan All rights reserved. Redistribution and use in source and binary forms, with or without @@ -1,21 +1,27 @@ -This is release m5_1.1 of the M5 simulator. +This is release 2.0 of the M5 simulator. -This file contains brief "getting started" instructions. For more -information, see http://m5.eecs.umich.edu. If you have questions, -please send mail to m5sim-users@lists.sourceforge.net. +For information about building the simulator and getting started please refer +to: http://m5.eecs.umich.edu/ + +Specific Pages of Interest are: +http://m5.eecs.umich.edu/wiki/index.php/Compiling_M5 +http://m5.eecs.umich.edu/wiki/index.php/Running_M5 + +If you have questions, please send mail to m5sim-users@lists.sourceforge.net. WHAT'S INCLUDED (AND NOT) ------------------------- The basic source release includes these subdirectories: - - m5: the simulator itself - - m5-test: regression tests - - ext: less-common external packages needed to build m5 - - alpha-system: source for Alpha console and PALcode + - m5: + - src: source code of the m5 simulator + - test: regression tests + - ext: less-common external packages needed to build m5 + - system/alpha: source for Alpha console and PALcode To run full-system simulations, you will need compiled console, PALcode, and kernel binaries and one or more disk images. These files -are collected in a separate archive, m5_system_1.1.tar.bz2. This file +are collected in a separate archive, m5_system_2.0.tar.bz2. This file is included on the CD release, or you can download it separately from Sourceforge. @@ -31,66 +37,8 @@ set of Linux source patches (linux_m5-2.6.8.1.diff), and the scons program needed to build M5. If you do not have the CD, the same HTML documentation is available online at http://m5.eecs.umich.edu/docs, the Linux source patches are available at -http://m5.eecs.umich.edu/dist/linux_m5-2.6.8.1.diff, and the scons -program is available from http://www.scons.org. - -WHAT'S NEEDED -------------- -- GCC version 3.3 or newer -- Python 2.3 or newer -- SCons 0.96.1 or newer (see http://www.scons.org) - -WHAT'S RECOMMENDED ------------------- -- MySQL (for statistics complex statistics storage/retrieval) -- Python-MysqlDB (for statistics analysis) - -GETTING STARTED ---------------- - -There are two different build targets and three optimizations levels: - -Target: -------- -ALPHA_SE - Syscall emulation simulation -ALPHA_FS - Full system simulation - -Optimization: -------------- -m5.debug - debug version of the code with tracing and without optimization -m5.opt - optimized version of code with tracing -m5.fast - optimized version of the code without tracing and asserts - -Different targets are built in different subdirectories of m5/build. -Binaries with the same target but different optimization levels share -the same directory. Note that you can build m5 in any directory you -choose;p just configure the target directory using the 'mkbuilddir' -script in m5/build. - -The following steps will build and test the simulator. The variable -"$top" refers to the top directory where you've unpacked the files, -i.e., the one containing the m5, m5-test, and ext directories. If you -have a multiprocessor system, you should give scons a "-j N" argument (like -make) to run N jobs in parallel. - -To build and test the syscall-emulation simulator: - - cd $top/m5/build - scons ALPHA_SE/test/opt/quick - -This process takes under 10 minutes on a dual 3GHz Xeon system (using -the '-j 4' option). - -To build and test the full-system simulator: - -1. Unpack the full-system binaries from m5_system_1.1.tar.bz2. (See - above for directions on obtaining this file if you don't have it.) - This package includes disk images and kernel, palcode, and console - binaries for Linux and FreeBSD. -2. Edit the SYSTEMDIR search path in $top/m5-test/SysPaths.py to - include the path to your local copy of the binaries. -3. In $top/m5/build, run "scons ALPHA_FS/test/opt/quick". +http://m5.eecs.umich.edu/dist/linux_m5-2.6.8.1.diff, the scons +program is available from http://www.scons.org, and swig is available from +http://www.swig.org. -This process also takes under 10 minutes on a dual 3GHz Xeon system -(again using the '-j 4' option). diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 983c9b2e9..6eb9b1844 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,11 @@ +XXX. X, 2006: m5_2.0 +-------------------- +Major update to M5 including: +- New CPU model +- Sew memory system +- More extensive python integration +- Preliminary syscall emulation support for MIPS and SPARC + Oct. 8, 2005: m5_1.1 -------------------- Update release for IOSCA workshop mini-tutorial. New features include: diff --git a/SConstruct b/SConstruct index 3fac9eb2e..b18fe66d3 100644 --- a/SConstruct +++ b/SConstruct @@ -25,6 +25,8 @@ # 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. +# +# Authors: Steve Reinhardt ################################################### # @@ -37,17 +39,20 @@ # # You can build M5 in a different directory as long as there is a # 'build/<CONFIG>' somewhere along the target path. The build system -# expdects that all configs under the same build directory are being +# expects that all configs under the same build directory are being # built for the same host system. # # Examples: -# These two commands are equivalent. The '-u' option tells scons to -# search up the directory tree for this SConstruct file. +# +# The following two commands are equivalent. The '-u' option tells +# scons to search up the directory tree for this SConstruct file. # % cd <path-to-src>/m5 ; scons build/ALPHA_FS/m5.debug # % cd <path-to-src>/m5/build/ALPHA_FS; scons -u m5.debug -# These two commands are equivalent and demonstrate building in a -# directory outside of the source tree. The '-C' option tells scons -# to chdir to the specified directory to find this SConstruct file. +# +# The following two commands are equivalent and demonstrate building +# in a directory outside of the source tree. The '-C' option tells +# scons to chdir to the specified directory to find this SConstruct +# file. # % cd <path-to-src>/m5 ; scons /local/foo/build/ALPHA_FS/m5.debug # % cd /local/foo/build/ALPHA_FS; scons -C <path-to-src>/m5 m5.debug # @@ -62,9 +67,24 @@ import sys import os -# Check for recent-enough Python and SCons versions -EnsurePythonVersion(2,3) -EnsureSConsVersion(0,96,91) +# Check for recent-enough Python and SCons versions. If your system's +# default installation of Python is not recent enough, you can use a +# non-default installation of the Python interpreter by either (1) +# rearranging your PATH so that scons finds the non-default 'python' +# first or (2) explicitly invoking an alternative interpreter on the +# scons script, e.g., "/usr/local/bin/python2.4 `which scons` [args]". +EnsurePythonVersion(2,4) + +# Ironically, SCons 0.96 dies if you give EnsureSconsVersion a +# 3-element version number. +min_scons_version = (0,96,91) +try: + EnsureSConsVersion(*min_scons_version) +except: + print "Error checking current SCons version." + print "SCons", ".".join(map(str,min_scons_version)), "or greater required." + Exit(2) + # The absolute path to the current directory (where this file lives). ROOT = Dir('.').abspath @@ -139,7 +159,13 @@ env = Environment(ENV = os.environ, # inherit user's environment vars ROOT = ROOT, SRCDIR = SRCDIR) -env.SConsignFile("sconsign") +env.SConsignFile(os.path.join(build_root,"sconsign")) + +# Default duplicate option is to use hard links, but this messes up +# when you use emacs to edit a file in the target dir, as emacs moves +# file to file~ then copies to file, breaking the link. Symbolic +# (soft) links work better. +env.SetOption('duplicate', 'soft-copy') # I waffle on this setting... it does avoid a few painful but # unnecessary builds, but it also seems to make trivial builds take @@ -159,7 +185,37 @@ if sys.platform == 'cygwin': env.Append(CCFLAGS=Split("-Wno-uninitialized")) env.Append(CPPPATH=[Dir('ext/dnet')]) -# Default libraries +# Find Python include and library directories for embedding the +# interpreter. For consistency, we will use the same Python +# installation used to run scons (and thus this script). If you want +# to link in an alternate version, see above for instructions on how +# to invoke scons with a different copy of the Python interpreter. + +# Get brief Python version name (e.g., "python2.4") for locating +# include & library files +py_version_name = 'python' + sys.version[:3] + +# include path, e.g. /usr/local/include/python2.4 +env.Append(CPPPATH = os.path.join(sys.exec_prefix, 'include', py_version_name)) +env.Append(LIBS = py_version_name) +# add library path too if it's not in the default place +if sys.exec_prefix != '/usr': + env.Append(LIBPATH = os.path.join(sys.exec_prefix, 'lib')) + +# Set up SWIG flags & scanner + +env.Append(SWIGFLAGS=Split('-c++ -python -modern $_CPPINCFLAGS')) + +import SCons.Scanner + +swig_inc_re = '^[ \t]*[%,#][ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")' + +swig_scanner = SCons.Scanner.ClassicCPP("SwigScan", ".i", "CPPPATH", + swig_inc_re) + +env.Append(SCANNERS = swig_scanner) + +# Other default libraries env.Append(LIBS=['z']) # Platform-specific configuration. Note again that we assume that all @@ -207,7 +263,8 @@ env['ALL_ISA_LIST'] = ['alpha', 'sparc', 'mips'] # Define the universe of supported CPU models env['ALL_CPU_LIST'] = ['AtomicSimpleCPU', 'TimingSimpleCPU', - 'FullCPU', 'AlphaFullCPU'] + 'FullCPU', 'O3CPU', + 'OzoneCPU'] # Sticky options get saved in the options file so they persist from # one invocation to the next (unless overridden, in which case the new @@ -233,9 +290,9 @@ sticky_opts.AddOptions( BoolOption('USE_SSE2', 'Compile for SSE2 (-msse2) to get IEEE FP on x86 hosts', False), - BoolOption('STATS_BINNING', 'Bin statistics by CPU mode', have_mysql), BoolOption('USE_MYSQL', 'Use MySQL for stats output', have_mysql), BoolOption('USE_FENV', 'Use <fenv.h> IEEE mode control', have_fenv), + BoolOption('USE_CHECKER', 'Use checker for detailed CPU models', False), ('CC', 'C compiler', os.environ.get('CC', env['CC'])), ('CXX', 'C++ compiler', os.environ.get('CXX', env['CXX'])), BoolOption('BATCH', 'Use batch pool for build and tests', False), @@ -248,10 +305,10 @@ nonsticky_opts.AddOptions( BoolOption('update_ref', 'Update test reference outputs', False) ) -# These options get exported to #defines in config/*.hh (see m5/SConscript). +# These options get exported to #defines in config/*.hh (see src/SConscript). env.ExportOptions = ['FULL_SYSTEM', 'ALPHA_TLASER', 'USE_FENV', \ 'USE_MYSQL', 'NO_FAST_ALLOC', 'SS_COMPATIBLE_FP', \ - 'STATS_BINNING'] + 'USE_CHECKER'] # Define a handy 'no-op' action def no_action(target, source, env): @@ -300,6 +357,32 @@ config_builder = Builder(emitter = config_emitter, action = config_action) env.Append(BUILDERS = { 'ConfigFile' : config_builder }) +################################################### +# +# Define a SCons builder for copying files. This is used by the +# Python zipfile code in src/python/SConscript, but is placed up here +# since it's potentially more generally applicable. +# +################################################### + +copy_builder = Builder(action = Copy("$TARGET", "$SOURCE")) + +env.Append(BUILDERS = { 'CopyFile' : copy_builder }) + +################################################### +# +# Define a simple SCons builder to concatenate files. +# +# Used to append the Python zip archive to the executable. +# +################################################### + +concat_builder = Builder(action = Action(['cat $SOURCES > $TARGET', + 'chmod +x $TARGET'])) + +env.Append(BUILDERS = { 'Concat' : concat_builder }) + + # base help text help_text = ''' Usage: scons [scons options] [build options] [target(s)] @@ -404,11 +487,11 @@ for build_path in build_paths: if env['USE_SSE2']: env.Append(CCFLAGS='-msse2') - # The m5/SConscript file sets up the build rules in 'env' according + # The src/SConscript file sets up the build rules in 'env' according # to the configured options. It returns a list of environments, # one for each variant build (debug, opt, etc.) envList = SConscript('src/SConscript', build_dir = build_path, - exports = 'env', duplicate = False) + exports = 'env') # Set up the regression tests for each build. # for e in envList: diff --git a/build_opts/SPARC_FS b/build_opts/SPARC_FS new file mode 100644 index 000000000..59d17eee9 --- /dev/null +++ b/build_opts/SPARC_FS @@ -0,0 +1,2 @@ +TARGET_ISA = 'sparc' +FULL_SYSTEM = 1 diff --git a/configs/test/SysPaths.py b/configs/test/SysPaths.py index c7c7db4e7..e458d5225 100644 --- a/configs/test/SysPaths.py +++ b/configs/test/SysPaths.py @@ -1,32 +1,42 @@ -from m5 import * - -import os.path -import sys - -# Edit the following list to include the possible paths to the binary -# and disk image directories. The first directory on the list that -# exists will be selected. -SYSTEMDIR_PATH = ['/n/poolfs/z/dist/m5/system'] - -SYSTEMDIR = None -for d in SYSTEMDIR_PATH: - if os.path.exists(d): - SYSTEMDIR = d - break - -if not SYSTEMDIR: - print >>sys.stderr, "Can't find a path to system files." - sys.exit(1) - -BINDIR = SYSTEMDIR + '/binaries' -DISKDIR = SYSTEMDIR + '/disks' +import os, sys +from os.path import isdir, join as joinpath +from os import environ as env + +systemdir = None +bindir = None +diskdir = None +scriptdir = None + +def load_defaults(): + global systemdir, bindir, diskdir, scriptdir + if not systemdir: + try: + path = env['M5_PATH'].split(':') + except KeyError: + path = [ '/dist/m5/system', '/n/poolfs/z/dist/m5/system' ] + + for systemdir in path: + if os.path.isdir(systemdir): + break + else: + raise ImportError, "Can't find a path to system files." + + if not bindir: + bindir = joinpath(systemdir, 'binaries') + if not diskdir: + diskdir = joinpath(systemdir, 'disks') + if not scriptdir: + scriptdir = joinpath(systemdir, 'boot') def disk(file): - return os.path.join(DISKDIR, file) + load_defaults() + return joinpath(diskdir, file) def binary(file): - return os.path.join(BINDIR, file) + load_defaults() + return joinpath(bindir, file) def script(file): - return os.path.join(SYSTEMDIR, 'boot', file) + load_defaults() + return joinpath(scriptdir, file) diff --git a/configs/test/fs.py b/configs/test/fs.py index fdbf86abe..aa530dd55 100644 --- a/configs/test/fs.py +++ b/configs/test/fs.py @@ -1,7 +1,19 @@ -from m5 import * +import m5 +from m5.objects import * import os from SysPaths import * +parser = optparse.OptionParser(option_list=m5.standardOptions) + +parser.add_option("-t", "--timing", action="store_true") + +(options, args) = parser.parse_args() +m5.setStandardOptions(options) + +if args: + print "Error: script doesn't take any positional arguments" + sys.exit(1) + # Base for tests is directory containing this file. test_base = os.path.dirname(__file__) @@ -118,17 +130,7 @@ class BaseTsunami(Tsunami): ethernet = NSGigE(configdata=NSGigEPciData(), pci_bus=0, pci_dev=1, pci_func=0) etherint = NSGigEInt(device=Parent.ethernet) -# ethernet = Sinic(configdata=SinicPciData(), -# pci_bus=0, pci_dev=1, pci_func=0) -# etherint = SinicInt(device=Parent.ethernet) console = AlphaConsole(pio_addr=0x80200000000, disk=Parent.simple_disk) -# bridge = PciFake(configdata=BridgePciData(), pci_bus=0, pci_dev=2, pci_func=0) - -#class FreeBSDTsunami(BaseTsunami): -# disk0 = FreeBSDRootDisk(delay='0us', driveID='master') -# ide = IdeController(disks=[Parent.disk0], -# configdata=IdeControllerPciData(), -# pci_func=0, pci_dev=0, pci_bus=0) class LinuxTsunami(BaseTsunami): disk0 = LinuxRootDisk(driveID='master') @@ -138,50 +140,56 @@ class LinuxTsunami(BaseTsunami): configdata=IdeControllerPciData(), pci_func=0, pci_dev=0, pci_bus=0) -class LinuxAlphaSystem(LinuxAlphaSystem): +class MyLinuxAlphaSystem(LinuxAlphaSystem): magicbus = Bus(bus_id=0) magicbus2 = Bus(bus_id=1) bridge = Bridge() physmem = PhysicalMemory(range = AddrRange('128MB')) - c0a = Connector(side_a=Parent.magicbus, side_b=Parent.bridge, side_b_name="side_a") - c0b = Connector(side_a=Parent.magicbus2, side_b=Parent.bridge, side_b_name="side_b") - c1 = Connector(side_a=Parent.physmem, side_b=Parent.magicbus2) + bridge.side_a = magicbus.port + bridge.side_b = magicbus2.port + physmem.port = magicbus2.port tsunami = LinuxTsunami() - c2 = Connector(side_a=Parent.tsunami.cchip, side_a_name='pio', side_b=Parent.magicbus) - c3 = Connector(side_a=Parent.tsunami.pchip, side_a_name='pio', side_b=Parent.magicbus) - c4 = Connector(side_a=Parent.tsunami.pciconfig, side_a_name='pio', side_b=Parent.magicbus) - c5 = Connector(side_a=Parent.tsunami.fake_sm_chip, side_a_name='pio', side_b=Parent.magicbus) - c6 = Connector(side_a=Parent.tsunami.ethernet, side_a_name='pio', side_b=Parent.magicbus) - c6a = Connector(side_a=Parent.tsunami.ethernet, side_a_name='dma', side_b=Parent.magicbus) - c7 = Connector(side_a=Parent.tsunami.fake_uart1, side_a_name='pio', side_b=Parent.magicbus) - c8 = Connector(side_a=Parent.tsunami.fake_uart2, side_a_name='pio', side_b=Parent.magicbus) - c9 = Connector(side_a=Parent.tsunami.fake_uart3, side_a_name='pio', side_b=Parent.magicbus) - c10 = Connector(side_a=Parent.tsunami.fake_uart4, side_a_name='pio', side_b=Parent.magicbus) - c11 = Connector(side_a=Parent.tsunami.ide, side_a_name='pio', side_b=Parent.magicbus) - c13 = Connector(side_a=Parent.tsunami.ide, side_a_name='dma', side_b=Parent.magicbus) - c12 = Connector(side_a=Parent.tsunami.fake_ppc, side_a_name='pio', side_b=Parent.magicbus) - c14 = Connector(side_a=Parent.tsunami.fake_OROM, side_a_name='pio', side_b=Parent.magicbus) - c16 = Connector(side_a=Parent.tsunami.fake_pnp_addr, side_a_name='pio', side_b=Parent.magicbus) - c17 = Connector(side_a=Parent.tsunami.fake_pnp_write, side_a_name='pio', side_b=Parent.magicbus) - c18 = Connector(side_a=Parent.tsunami.fake_pnp_read0, side_a_name='pio', side_b=Parent.magicbus) - c19 = Connector(side_a=Parent.tsunami.fake_pnp_read1, side_a_name='pio', side_b=Parent.magicbus) - c20 = Connector(side_a=Parent.tsunami.fake_pnp_read2, side_a_name='pio', side_b=Parent.magicbus) - c21 = Connector(side_a=Parent.tsunami.fake_pnp_read3, side_a_name='pio', side_b=Parent.magicbus) - c22 = Connector(side_a=Parent.tsunami.fake_pnp_read4, side_a_name='pio', side_b=Parent.magicbus) - c23 = Connector(side_a=Parent.tsunami.fake_pnp_read5, side_a_name='pio', side_b=Parent.magicbus) - c24 = Connector(side_a=Parent.tsunami.fake_pnp_read6, side_a_name='pio', side_b=Parent.magicbus) - c25 = Connector(side_a=Parent.tsunami.fake_pnp_read7, side_a_name='pio', side_b=Parent.magicbus) - c27 = Connector(side_a=Parent.tsunami.fake_ata0, side_a_name='pio', side_b=Parent.magicbus) - c28 = Connector(side_a=Parent.tsunami.fake_ata1, side_a_name='pio', side_b=Parent.magicbus) - c30 = Connector(side_a=Parent.tsunami.fb, side_a_name='pio', side_b=Parent.magicbus) - c31 = Connector(side_a=Parent.tsunami.io, side_a_name='pio', side_b=Parent.magicbus) - c32 = Connector(side_a=Parent.tsunami.uart, side_a_name='pio', side_b=Parent.magicbus) - c33 = Connector(side_a=Parent.tsunami.console, side_a_name='pio', side_b=Parent.magicbus) + tsunami.cchip.pio = magicbus.port + tsunami.pchip.pio = magicbus.port + tsunami.pciconfig.pio = magicbus.port + tsunami.fake_sm_chip.pio = magicbus.port + tsunami.ethernet.pio = magicbus.port + tsunami.ethernet.dma = magicbus.port + tsunami.fake_uart1.pio = magicbus.port + tsunami.fake_uart2.pio = magicbus.port + tsunami.fake_uart3.pio = magicbus.port + tsunami.fake_uart4.pio = magicbus.port + tsunami.ide.pio = magicbus.port + tsunami.ide.dma = magicbus.port + tsunami.fake_ppc.pio = magicbus.port + tsunami.fake_OROM.pio = magicbus.port + tsunami.fake_pnp_addr.pio = magicbus.port + tsunami.fake_pnp_write.pio = magicbus.port + tsunami.fake_pnp_read0.pio = magicbus.port + tsunami.fake_pnp_read1.pio = magicbus.port + tsunami.fake_pnp_read2.pio = magicbus.port + tsunami.fake_pnp_read3.pio = magicbus.port + tsunami.fake_pnp_read4.pio = magicbus.port + tsunami.fake_pnp_read5.pio = magicbus.port + tsunami.fake_pnp_read6.pio = magicbus.port + tsunami.fake_pnp_read7.pio = magicbus.port + tsunami.fake_ata0.pio = magicbus.port + tsunami.fake_ata1.pio = magicbus.port + tsunami.fb.pio = magicbus.port + tsunami.io.pio = magicbus.port + tsunami.uart.pio = magicbus.port + tsunami.console.pio = magicbus.port raw_image = RawDiskImage(image_file=disk('linux-latest.img'), read_only=True) simple_disk = SimpleDisk(disk=Parent.raw_image) intrctrl = IntrControl() - cpu = AtomicSimpleCPU(mem=Parent.magicbus2) + if options.timing: + cpu = TimingSimpleCPU() + else: + cpu = AtomicSimpleCPU() + cpu.mem = magicbus2 + cpu.itb = AlphaITB() + cpu.dtb = AlphaDTB() sim_console = SimConsole(listener=ConsoleListener(port=3456)) kernel = binary('vmlinux') pal = binary('ts_osfpal') @@ -190,18 +198,15 @@ class LinuxAlphaSystem(LinuxAlphaSystem): # readfile = os.path.join(test_base, 'halt.sh') -BaseCPU.itb = AlphaITB() -BaseCPU.dtb = AlphaDTB() -BaseCPU.system = Parent.any class TsunamiRoot(System): pass -def DualRoot(ClientSystem, ServerSystem): +def DualRoot(clientSystem, serverSystem): self = Root() - self.client = ClientSystem() - self.server = ServerSystem() + self.client = clientSystem + self.server = serverSystem self.etherdump = EtherDump(file='ethertrace') self.etherlink = EtherLink(int1 = Parent.client.tsunami.etherint[0], @@ -210,6 +215,12 @@ def DualRoot(ClientSystem, ServerSystem): self.clock = '5GHz' return self -root = DualRoot(ClientSystem = LinuxAlphaSystem(readfile=script('netperf-stream-nt-client.rcS')), - ServerSystem = LinuxAlphaSystem(readfile=script('netperf-server.rcS'))) +root = DualRoot( + MyLinuxAlphaSystem(readfile=script('netperf-stream-nt-client.rcS')), + MyLinuxAlphaSystem(readfile=script('netperf-server.rcS'))) + +m5.instantiate(root) + +exit_event = m5.simulate() +print 'Exiting @ cycle', m5.curTick(), 'because', exit_event.getCause() diff --git a/configs/test/test.py b/configs/test/test.py index 75e832f5e..625304a08 100644 --- a/configs/test/test.py +++ b/configs/test/test.py @@ -1,12 +1,93 @@ -from m5 import * +# Simple test script +# +# Alpha: "m5 test.py" +# MIPS: "m5 test.py -a Mips -c hello_mips" -class HelloWorld(AlphaLiveProcess): - executable = '../configs/test/hello' - cmd = 'hello' +import os, optparse, sys +import m5 +from m5.objects import * +from FullO3Config import * + +# parse command-line arguments +parser = optparse.OptionParser(option_list=m5.standardOptions) + +parser.add_option("-c", "--cmd", default="hello", + help="The binary to run in syscall emulation mode.") +parser.add_option("-o", "--options", default="", + help="The options to pass to the binary, use \" \" around the entire\ + string.") +parser.add_option("-i", "--input", default="", + help="A file of input to give to the binary.") +parser.add_option("-t", "--timing", action="store_true", + help="Use simple timing CPU.") +parser.add_option("-d", "--detailed", action="store_true", + help="Use detailed CPU.") +parser.add_option("-m", "--maxtick", type="int", + help="Set the maximum number of ticks to run for") + +(options, args) = parser.parse_args() +m5.setStandardOptions(options) + +if args: + print "Error: script doesn't take any positional arguments" + sys.exit(1) + +# build configuration +this_dir = os.path.dirname(__file__) + +process = LiveProcess() +process.executable = os.path.join(this_dir, options.cmd) +process.cmd = options.cmd + " " + options.options +if options.input != "": + process.input = options.input magicbus = Bus() mem = PhysicalMemory() -cpu = AtomicSimpleCPU(workload=HelloWorld(), mem=magicbus) -system = System(physmem=mem, cpu=cpu) -system.c1 = Connector(side_a=mem, side_b=magicbus) -root = Root(system=system) + +if options.timing and options.detailed: + print "Error: you may only specify one cpu model"; + sys.exit(1) + +if options.timing: + cpu = TimingSimpleCPU() +elif options.detailed: + #check for SMT workload + workloads = options.cmd.split(';') + if len(workloads) > 1: + process = [] + smt_idx = 0 + inputs = [] + + if options.input != "": + inputs = options.input.split(';') + + for wrkld in workloads: + smt_process = LiveProcess() + smt_process.executable = os.path.join(this_dir, wrkld) + smt_process.cmd = wrkld + " " + options.options + if inputs and inputs[smt_idx]: + smt_process.input = inputs[smt_idx] + process += [smt_process, ] + smt_idx += 1 + + cpu = DetailedO3CPU() +else: + cpu = AtomicSimpleCPU() +cpu.workload = process +cpu.mem = magicbus + +system = System(physmem = mem, cpu = cpu) +mem.port = magicbus.port +root = Root(system = system) + +# instantiate configuration +m5.instantiate(root) + +# simulate until program terminates +if options.maxtick: + exit_event = m5.simulate(options.maxtick) +else: + exit_event = m5.simulate() + +print 'Exiting @ tick', m5.curTick(), 'because', exit_event.getCause() + diff --git a/docs/stl.hh b/docs/stl.hh index bf537efd4..fd9f68140 100644 --- a/docs/stl.hh +++ b/docs/stl.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Erik Hallnor + * Nathan Binkert */ /** diff --git a/src/SConscript b/src/SConscript index 558c447e9..0d0cb2486 100644 --- a/src/SConscript +++ b/src/SConscript @@ -25,6 +25,8 @@ # 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. +# +# Authors: Steve Reinhardt import os import sys @@ -46,9 +48,7 @@ Import('env') base_sources = Split(''' base/circlebuf.cc - base/copyright.cc base/cprintf.cc - base/embedfile.cc base/fast_alloc.cc base/fifo_buffer.cc base/hostinfo.cc @@ -62,7 +62,6 @@ base_sources = Split(''' base/range.cc base/random.cc base/sat_counter.cc - base/serializer.cc base/socket.cc base/statistics.cc base/str.cc @@ -81,38 +80,62 @@ base_sources = Split(''' base/stats/visit.cc base/stats/text.cc + cpu/activity.cc cpu/base.cc - cpu/cpu_exec_context.cc + cpu/cpuevent.cc cpu/exetrace.cc + cpu/func_unit.cc cpu/op_class.cc cpu/pc_event.cc + cpu/quiesce_event.cc cpu/static_inst.cc cpu/sampler/sampler.cc - + cpu/simple_thread.cc + cpu/thread_state.cc + mem/bridge.cc mem/bus.cc - mem/connector.cc mem/mem_object.cc mem/packet.cc mem/physical.cc mem/port.cc - mem/request.cc - python/pyconfig.cc - python/embedded_py.cc + mem/cache/base_cache.cc + mem/cache/cache.cc + mem/cache/coherence/coherence_protocol.cc + mem/cache/coherence/uni_coherence.cc + mem/cache/miss/blocking_buffer.cc + mem/cache/miss/miss_queue.cc + mem/cache/miss/mshr.cc + mem/cache/miss/mshr_queue.cc + mem/cache/prefetch/base_prefetcher.cc + mem/cache/prefetch/ghb_prefetcher.cc + mem/cache/prefetch/prefetcher.cc + mem/cache/prefetch/stride_prefetcher.cc + mem/cache/prefetch/tagged_prefetcher.cc + mem/cache/tags/base_tags.cc + mem/cache/tags/cache_tags.cc + mem/cache/tags/fa_lru.cc + mem/cache/tags/iic.cc + mem/cache/tags/lru.cc + mem/cache/tags/repl/gen.cc + mem/cache/tags/repl/repl.cc + mem/cache/tags/split.cc + mem/cache/tags/split_lifo.cc + mem/cache/tags/split_lru.cc + + mem/cache/cache_builder.cc sim/builder.cc - sim/configfile.cc sim/debug.cc sim/eventq.cc sim/faults.cc sim/main.cc + python/swig/cc_main_wrap.cc sim/param.cc - sim/profile.cc sim/root.cc sim/serialize.cc sim/sim_events.cc - sim/sim_exit.cc sim/sim_object.cc sim/startup.cc sim/stat_context.cc @@ -217,7 +240,6 @@ full_system_sources = Split(''' dev/uart.cc dev/uart8250.cc - kern/kernel_binning.cc kern/kernel_stats.cc kern/system_events.cc kern/linux/events.cc @@ -284,14 +306,18 @@ memtest_sources = Split(''' cpu/memtest/memtest.cc ''') +# Include file paths are rooted in this directory. SCons will +# automatically expand '.' to refer to both the source directory and +# the corresponding build directory to pick up generated include +# files. +env.Append(CPPPATH=Dir('.')) + # Add a flag defining what THE_ISA should be for all compilation env.Append(CPPDEFINES=[('THE_ISA','%s_ISA' % env['TARGET_ISA'].upper())]) -arch_sources = SConscript('arch/SConscript', - exports = 'env', duplicate = False) +arch_sources = SConscript('arch/SConscript', exports = 'env') -cpu_sources = SConscript('cpu/SConscript', - exports = 'env', duplicate = False) +cpu_sources = SConscript('cpu/SConscript', exports = 'env') # This is outside of cpu/SConscript since the source directory isn't # underneath 'cpu'. @@ -326,7 +352,7 @@ env.Command(Split('base/traceflags.hh base/traceflags.cc'), 'base/traceflags.py', 'python $SOURCE $TARGET.base') -SConscript('python/SConscript', exports = ['env'], duplicate=0) +SConscript('python/SConscript', exports = ['env']) # This function adds the specified sources to the given build # environment, and returns a list of all the corresponding SCons @@ -349,49 +375,45 @@ def make_objs(sources, env): # ################################################### -# Include file paths are rooted in this directory. SCons will -# automatically expand '.' to refer to both the source directory and -# the corresponding build directory to pick up generated include -# files. -env.Append(CPPPATH='.') +# List of constructed environments to pass back to SConstruct +envList = [] + +# Function to create a new build environment as clone of current +# environment 'env' with modified object suffix and optional stripped +# binary. Additional keyword arguments are appended to corresponding +# build environment vars. +def makeEnv(label, objsfx, strip = False, **kwargs): + newEnv = env.Copy(OBJSUFFIX=objsfx) + newEnv.Label = label + newEnv.Append(**kwargs) + exe = 'm5.' + label # final executable + bin = exe + '.bin' # executable w/o appended Python zip archive + newEnv.Program(bin, make_objs(sources, newEnv)) + if strip: + stripped_bin = bin + '.stripped' + newEnv.Command(stripped_bin, bin, 'strip $SOURCE -o $TARGET') + bin = stripped_bin + targets = newEnv.Concat(exe, [bin, 'python/m5py.zip']) + newEnv.M5Binary = targets[0] + envList.append(newEnv) # Debug binary -debugEnv = env.Copy(OBJSUFFIX='.do') -debugEnv.Label = 'debug' -debugEnv.Append(CCFLAGS=Split('-g3 -gdwarf-2 -O0')) -debugEnv.Append(CPPDEFINES='DEBUG') -tlist = debugEnv.Program(target = 'm5.debug', - source = make_objs(sources, debugEnv)) -debugEnv.M5Binary = tlist[0] +makeEnv('debug', '.do', + CCFLAGS = Split('-g3 -gdwarf-2 -O0'), + CPPDEFINES = 'DEBUG') # Optimized binary -optEnv = env.Copy() -optEnv.Label = 'opt' -optEnv.Append(CCFLAGS=Split('-g -O3')) -tlist = optEnv.Program(target = 'm5.opt', - source = make_objs(sources, optEnv)) -optEnv.M5Binary = tlist[0] +makeEnv('opt', '.o', + CCFLAGS = Split('-g -O3')) # "Fast" binary -fastEnv = env.Copy(OBJSUFFIX='.fo') -fastEnv.Label = 'fast' -fastEnv.Append(CCFLAGS=Split('-O3')) -fastEnv.Append(CPPDEFINES='NDEBUG') -fastEnv.Program(target = 'm5.fast.unstripped', - source = make_objs(sources, fastEnv)) -tlist = fastEnv.Command(target = 'm5.fast', - source = 'm5.fast.unstripped', - action = 'strip $SOURCE -o $TARGET') -fastEnv.M5Binary = tlist[0] +makeEnv('fast', '.fo', strip = True, + CCFLAGS = Split('-O3'), + CPPDEFINES = 'NDEBUG') # Profiled binary -profEnv = env.Copy(OBJSUFFIX='.po') -profEnv.Label = 'prof' -profEnv.Append(CCFLAGS=Split('-O3 -g -pg'), LINKFLAGS='-pg') -tlist = profEnv.Program(target = 'm5.prof', - source = make_objs(sources, profEnv)) -profEnv.M5Binary = tlist[0] - -envList = [debugEnv, optEnv, fastEnv, profEnv] +makeEnv('prof', '.po', + CCFLAGS = Split('-O3 -g -pg'), + LINKFLAGS = '-pg') Return('envList') diff --git a/src/arch/SConscript b/src/arch/SConscript index 9c193207f..bc517341a 100644 --- a/src/arch/SConscript +++ b/src/arch/SConscript @@ -25,8 +25,10 @@ # 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. +# +# Authors: Steve Reinhardt -import os.path +import os.path, sys # Import build environment variable from SConstruct. Import('env') @@ -126,13 +128,19 @@ isa_desc_gen_files = Split('decoder.cc decoder.hh') isa_desc_gen_files += [CpuModel.dict[cpu].filename for cpu in env['CPU_MODELS']] +# Also include the CheckerCPU as one of the models if it is being +# enabled via command line. +if env['USE_CHECKER']: + isa_desc_gen_files += [CpuModel.dict['CheckerCPU'].filename] + # The emitter patches up the sources & targets to include the # autogenerated files as targets and isa parser itself as a source. def isa_desc_emitter(target, source, env): return (isa_desc_gen_files, [isa_parser, cpu_models_file] + source) # Pieces are in place, so create the builder. -isa_desc_builder = Builder(action='python2.4 $SOURCES $TARGET.dir $CPU_MODELS', +python = sys.executable # use same Python binary used to run scons +isa_desc_builder = Builder(action=python + ' $SOURCES $TARGET.dir $CPU_MODELS', emitter = isa_desc_emitter) env.Append(BUILDERS = { 'ISADesc' : isa_desc_builder }) @@ -144,7 +152,6 @@ env.Append(BUILDERS = { 'ISADesc' : isa_desc_builder }) isa = env['TARGET_ISA'] # someday this may be a list of ISAs # Let the target architecture define what additional sources it needs -sources += SConscript(os.path.join(isa, 'SConscript'), - exports = 'env', duplicate = False) +sources += SConscript(os.path.join(isa, 'SConscript'), exports = 'env') Return('sources') diff --git a/src/arch/alpha/SConscript b/src/arch/alpha/SConscript index 1b20f8b1f..216c88cc7 100644 --- a/src/arch/alpha/SConscript +++ b/src/arch/alpha/SConscript @@ -25,6 +25,9 @@ # 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. +# +# Authors: Gabe Black +# Steve Reinhardt import os import sys diff --git a/src/arch/alpha/aout_machdep.h b/src/arch/alpha/aout_machdep.h index df9d9ac6a..58991256a 100644 --- a/src/arch/alpha/aout_machdep.h +++ b/src/arch/alpha/aout_machdep.h @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __AOUT_MACHDEP_H__ diff --git a/src/arch/alpha/arguments.cc b/src/arch/alpha/arguments.cc index adc371682..9f9002003 100644 --- a/src/arch/alpha/arguments.cc +++ b/src/arch/alpha/arguments.cc @@ -24,11 +24,13 @@ * 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. + * + * Authors: Nathan Binkert */ #include "arch/alpha/arguments.hh" #include "arch/alpha/vtophys.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/vport.hh" using namespace AlphaISA; @@ -54,14 +56,14 @@ AlphaArguments::getArg(bool fp) { if (number < 6) { if (fp) - return xc->readFloatRegBits(16 + number); + return tc->readFloatRegBits(16 + number); else - return xc->readIntReg(16 + number); + return tc->readIntReg(16 + number); } else { - Addr sp = xc->readIntReg(30); - VirtualPort *vp = xc->getVirtPort(xc); + Addr sp = tc->readIntReg(30); + VirtualPort *vp = tc->getVirtPort(tc); uint64_t arg = vp->read<uint64_t>(sp + (number-6) * sizeof(uint64_t)); - xc->delVirtPort(vp); + tc->delVirtPort(vp); return arg; } } diff --git a/src/arch/alpha/arguments.hh b/src/arch/alpha/arguments.hh index bd1c6cb1d..d977d48d6 100644 --- a/src/arch/alpha/arguments.hh +++ b/src/arch/alpha/arguments.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __ARCH_ALPHA_ARGUMENTS_HH__ @@ -35,14 +37,14 @@ #include "base/refcnt.hh" #include "sim/host.hh" -class ExecContext; +class ThreadContext; namespace AlphaISA { class AlphaArguments { protected: - ExecContext *xc; + ThreadContext *tc; int number; uint64_t getArg(bool fp = false); @@ -63,17 +65,17 @@ class AlphaArguments RefCountingPtr<Data> data; public: - AlphaArguments(ExecContext *ctx, int n = 0) - : xc(ctx), number(n), data(NULL) + AlphaArguments(ThreadContext *ctx, int n = 0) + : tc(ctx), number(n), data(NULL) { assert(number >= 0); data = new Data;} AlphaArguments(const AlphaArguments &args) - : xc(args.xc), number(args.number), data(args.data) {} + : tc(args.tc), number(args.number), data(args.data) {} ~AlphaArguments() {} - ExecContext *getExecContext() const { return xc; } + ThreadContext *getThreadContext() const { return tc; } const AlphaArguments &operator=(const AlphaArguments &args) { - xc = args.xc; + tc = args.tc; number = args.number; data = args.data; return *this; @@ -118,7 +120,7 @@ class AlphaArguments } AlphaArguments operator[](int index) { - return AlphaArguments(xc, index); + return AlphaArguments(tc, index); } template <class T> @@ -131,13 +133,13 @@ class AlphaArguments template <class T> operator T *() { T *buf = (T *)data->alloc(sizeof(T)); - CopyData(xc, buf, getArg(), sizeof(T)); + CopyData(tc, buf, getArg(), sizeof(T)); return buf; } operator char *() { char *buf = data->alloc(2048); - CopyStringOut(xc, buf, getArg(), 2048); + CopyStringOut(tc, buf, getArg(), 2048); return buf; } }; diff --git a/src/arch/alpha/ev5.cc b/src/arch/alpha/ev5.cc index 12f7659e6..ae3b668ea 100644 --- a/src/arch/alpha/ev5.cc +++ b/src/arch/alpha/ev5.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #include "arch/alpha/tlb.hh" @@ -34,11 +37,11 @@ #include "base/stats/events.hh" #include "config/full_system.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" -#include "cpu/exec_context.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" #include "kern/kernel_stats.hh" #include "sim/debug.hh" -#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" #if FULL_SYSTEM @@ -49,15 +52,19 @@ using namespace EV5; // Machine dependent functions // void -AlphaISA::initCPU(ExecContext *xc, int cpuId) +AlphaISA::initCPU(ThreadContext *tc, int cpuId) { - initIPRs(xc, cpuId); + initIPRs(tc, cpuId); + + tc->setIntReg(16, cpuId); + tc->setIntReg(0, cpuId); - xc->setIntReg(16, cpuId); - xc->setIntReg(0, cpuId); + AlphaFault *reset = new ResetFault; - xc->setPC(xc->readMiscReg(IPR_PAL_BASE) + (new ResetFault)->vect()); - xc->setNextPC(xc->readPC() + sizeof(MachInst)); + tc->setPC(tc->readMiscReg(IPR_PAL_BASE) + reset->vect()); + tc->setNextPC(tc->readPC() + sizeof(MachInst)); + + delete reset; } //////////////////////////////////////////////////////////////////////// @@ -65,15 +72,15 @@ AlphaISA::initCPU(ExecContext *xc, int cpuId) // // void -AlphaISA::initIPRs(ExecContext *xc, int cpuId) +AlphaISA::initIPRs(ThreadContext *tc, int cpuId) { for (int i = 0; i < NumInternalProcRegs; ++i) { - xc->setMiscReg(i, 0); + tc->setMiscReg(i, 0); } - xc->setMiscReg(IPR_PAL_BASE, PalBase); - xc->setMiscReg(IPR_MCSR, 0x6); - xc->setMiscReg(IPR_PALtemp16, cpuId); + tc->setMiscReg(IPR_PAL_BASE, PalBase); + tc->setMiscReg(IPR_MCSR, 0x6); + tc->setMiscReg(IPR_PALtemp16, cpuId); } @@ -132,12 +139,12 @@ AlphaISA::zeroRegisters(CPU *cpu) // Insure ISA semantics // (no longer very clean due to the change in setIntReg() in the // cpu model. Consider changing later.) - cpu->cpuXC->setIntReg(ZeroReg, 0); - cpu->cpuXC->setFloatReg(ZeroReg, 0.0); + cpu->thread->setIntReg(ZeroReg, 0); + cpu->thread->setFloatReg(ZeroReg, 0.0); } Fault -CPUExecContext::hwrei() +SimpleThread::hwrei() { if (!inPalMode()) return new UnimplementedOpcodeFault; @@ -145,7 +152,8 @@ CPUExecContext::hwrei() setNextPC(readMiscReg(AlphaISA::IPR_EXC_ADDR)); if (!misspeculating()) { - cpu->kernelStats->hwrei(); + if (kernelStats) + kernelStats->hwrei(); cpu->checkInterrupts = true; } @@ -167,7 +175,7 @@ AlphaISA::MiscRegFile::getDataAsid() } AlphaISA::MiscReg -AlphaISA::MiscRegFile::readIpr(int idx, Fault &fault, ExecContext *xc) +AlphaISA::MiscRegFile::readIpr(int idx, Fault &fault, ThreadContext *tc) { uint64_t retval = 0; // return value, default 0 @@ -221,7 +229,7 @@ AlphaISA::MiscRegFile::readIpr(int idx, Fault &fault, ExecContext *xc) case AlphaISA::IPR_CC: retval |= ipr[idx] & ULL(0xffffffff00000000); - retval |= xc->getCpuPtr()->curCycle() & ULL(0x00000000ffffffff); + retval |= tc->getCpuPtr()->curCycle() & ULL(0x00000000ffffffff); break; case AlphaISA::IPR_VA: @@ -238,7 +246,7 @@ AlphaISA::MiscRegFile::readIpr(int idx, Fault &fault, ExecContext *xc) case AlphaISA::IPR_DTB_PTE: { - AlphaISA::PTE &pte = xc->getDTBPtr()->index(!xc->misspeculating()); + AlphaISA::PTE &pte = tc->getDTBPtr()->index(!tc->misspeculating()); retval |= ((u_int64_t)pte.ppn & ULL(0x7ffffff)) << 32; retval |= ((u_int64_t)pte.xre & ULL(0xf)) << 8; @@ -278,11 +286,11 @@ int break_ipl = -1; #endif Fault -AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) +AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ThreadContext *tc) { uint64_t old; - if (xc->misspeculating()) + if (tc->misspeculating()) return NoFault; switch (idx) { @@ -335,7 +343,8 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) // write entire quad w/ no side-effect old = ipr[idx]; ipr[idx] = val; - xc->getCpuPtr()->kernelStats->context(old, val, xc); + if (tc->getKernelStats()) + tc->getKernelStats()->context(old, val, tc); break; case AlphaISA::IPR_DTB_PTE: @@ -362,14 +371,18 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) // only write least significant five bits - interrupt level ipr[idx] = val & 0x1f; - xc->getCpuPtr()->kernelStats->swpipl(ipr[idx]); + if (tc->getKernelStats()) + tc->getKernelStats()->swpipl(ipr[idx]); break; case AlphaISA::IPR_DTB_CM: - if (val & 0x18) - xc->getCpuPtr()->kernelStats->mode(Kernel::user, xc); - else - xc->getCpuPtr()->kernelStats->mode(Kernel::kernel, xc); + if (val & 0x18) { + if (tc->getKernelStats()) + tc->getKernelStats()->mode(Kernel::user, tc); + } else { + if (tc->getKernelStats()) + tc->getKernelStats()->mode(Kernel::kernel, tc); + } case AlphaISA::IPR_ICM: // only write two mode bits - processor mode @@ -443,21 +456,21 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) // really a control write ipr[idx] = 0; - xc->getDTBPtr()->flushAll(); + tc->getDTBPtr()->flushAll(); break; case AlphaISA::IPR_DTB_IAP: // really a control write ipr[idx] = 0; - xc->getDTBPtr()->flushProcesses(); + tc->getDTBPtr()->flushProcesses(); break; case AlphaISA::IPR_DTB_IS: // really a control write ipr[idx] = val; - xc->getDTBPtr()->flushAddr(val, + tc->getDTBPtr()->flushAddr(val, DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN])); break; @@ -481,7 +494,7 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) pte.asn = DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN]); // insert new TAG/PTE value into data TLB - xc->getDTBPtr()->insert(val, pte); + tc->getDTBPtr()->insert(val, pte); } break; @@ -505,7 +518,7 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) pte.asn = ITB_ASN_ASN(ipr[AlphaISA::IPR_ITB_ASN]); // insert new TAG/PTE value into data TLB - xc->getITBPtr()->insert(ipr[AlphaISA::IPR_ITB_TAG], pte); + tc->getITBPtr()->insert(ipr[AlphaISA::IPR_ITB_TAG], pte); } break; @@ -513,21 +526,21 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) // really a control write ipr[idx] = 0; - xc->getITBPtr()->flushAll(); + tc->getITBPtr()->flushAll(); break; case AlphaISA::IPR_ITB_IAP: // really a control write ipr[idx] = 0; - xc->getITBPtr()->flushProcesses(); + tc->getITBPtr()->flushProcesses(); break; case AlphaISA::IPR_ITB_IS: // really a control write ipr[idx] = val; - xc->getITBPtr()->flushAddr(val, + tc->getITBPtr()->flushAddr(val, ITB_ASN_ASN(ipr[AlphaISA::IPR_ITB_ASN])); break; @@ -541,7 +554,7 @@ AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ExecContext *xc) } void -AlphaISA::copyIprs(ExecContext *src, ExecContext *dest) +AlphaISA::copyIprs(ThreadContext *src, ThreadContext *dest) { for (int i = IPR_Base_DepTag; i < NumInternalProcRegs; ++i) { dest->setMiscReg(i, src->readMiscReg(i)); @@ -553,15 +566,16 @@ AlphaISA::copyIprs(ExecContext *src, ExecContext *dest) * If return value is false, actual PAL call will be suppressed. */ bool -CPUExecContext::simPalCheck(int palFunc) +SimpleThread::simPalCheck(int palFunc) { - cpu->kernelStats->callpal(palFunc, proxy); + if (kernelStats) + kernelStats->callpal(palFunc, tc); switch (palFunc) { case PAL::halt: halt(); if (--System::numSystemsRunning == 0) - new SimExitEvent("all cpus halted"); + exitSimLoop("all cpus halted"); break; case PAL::bpt: diff --git a/src/arch/alpha/ev5.hh b/src/arch/alpha/ev5.hh index 7c8465cfb..4dd225786 100644 --- a/src/arch/alpha/ev5.hh +++ b/src/arch/alpha/ev5.hh @@ -24,6 +24,10 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + * Ali Saidi */ #ifndef __ARCH_ALPHA_EV5_HH__ diff --git a/src/arch/alpha/faults.cc b/src/arch/alpha/faults.cc index 0083aa9f3..eef4361fd 100644 --- a/src/arch/alpha/faults.cc +++ b/src/arch/alpha/faults.cc @@ -24,14 +24,20 @@ * 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. + * + * Authors: Gabe Black + * Kevin Lim */ #include "arch/alpha/faults.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "base/trace.hh" #if FULL_SYSTEM #include "arch/alpha/ev5.hh" +#else +#include "sim/process.hh" +#include "mem/page_table.hh" #endif namespace AlphaISA @@ -53,6 +59,12 @@ FaultName ArithmeticFault::_name = "arith"; FaultVect ArithmeticFault::_vect = 0x0501; FaultStat ArithmeticFault::_count; +#if !FULL_SYSTEM +FaultName PageTableFault::_name = "page_table_fault"; +FaultVect PageTableFault::_vect = 0x0000; +FaultStat PageTableFault::_count; +#endif + FaultName InterruptFault::_name = "interrupt"; FaultVect InterruptFault::_vect = 0x0101; FaultStat InterruptFault::_count; @@ -107,67 +119,91 @@ FaultStat IntegerOverflowFault::_count; #if FULL_SYSTEM -void AlphaFault::invoke(ExecContext * xc) +void AlphaFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); countStat()++; // exception restart address - if (setRestartAddress() || !xc->inPalMode()) - xc->setMiscReg(AlphaISA::IPR_EXC_ADDR, xc->readPC()); + if (setRestartAddress() || !tc->inPalMode()) + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, tc->readPC()); if (skipFaultingInstruction()) { // traps... skip faulting instruction. - xc->setMiscReg(AlphaISA::IPR_EXC_ADDR, - xc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, + tc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); } - xc->setPC(xc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect()); - xc->setNextPC(xc->readPC() + sizeof(MachInst)); + tc->setPC(tc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect()); + tc->setNextPC(tc->readPC() + sizeof(MachInst)); } -void ArithmeticFault::invoke(ExecContext * xc) +void ArithmeticFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); panic("Arithmetic traps are unimplemented!"); } -void DtbFault::invoke(ExecContext * xc) +void DtbFault::invoke(ThreadContext * tc) { // Set fault address and flags. Even though we're modeling an // EV5, we use the EV6 technique of not latching fault registers // on VPTE loads (instead of locking the registers until IPR_VA is // read, like the EV5). The EV6 approach is cleaner and seems to // work with EV5 PAL code, but not the other way around. - if (!xc->misspeculating() + if (!tc->misspeculating() && !(reqFlags & VPTE) && !(reqFlags & NO_FAULT)) { // set VA register with faulting address - xc->setMiscReg(AlphaISA::IPR_VA, vaddr); + tc->setMiscReg(AlphaISA::IPR_VA, vaddr); // set MM_STAT register flags - xc->setMiscReg(AlphaISA::IPR_MM_STAT, - (((EV5::Opcode(xc->getInst()) & 0x3f) << 11) - | ((EV5::Ra(xc->getInst()) & 0x1f) << 6) + tc->setMiscReg(AlphaISA::IPR_MM_STAT, + (((EV5::Opcode(tc->getInst()) & 0x3f) << 11) + | ((EV5::Ra(tc->getInst()) & 0x1f) << 6) | (flags & 0x3f))); // set VA_FORM register with faulting formatted address - xc->setMiscReg(AlphaISA::IPR_VA_FORM, - xc->readMiscReg(AlphaISA::IPR_MVPTBR) | (vaddr.vpn() << 3)); + tc->setMiscReg(AlphaISA::IPR_VA_FORM, + tc->readMiscReg(AlphaISA::IPR_MVPTBR) | (vaddr.vpn() << 3)); } - AlphaFault::invoke(xc); + AlphaFault::invoke(tc); } -void ItbFault::invoke(ExecContext * xc) +void ItbFault::invoke(ThreadContext * tc) { - if (!xc->misspeculating()) { - xc->setMiscReg(AlphaISA::IPR_ITB_TAG, pc); - xc->setMiscReg(AlphaISA::IPR_IFAULT_VA_FORM, - xc->readMiscReg(AlphaISA::IPR_IVPTBR) | + if (!tc->misspeculating()) { + tc->setMiscReg(AlphaISA::IPR_ITB_TAG, pc); + tc->setMiscReg(AlphaISA::IPR_IFAULT_VA_FORM, + tc->readMiscReg(AlphaISA::IPR_IVPTBR) | (AlphaISA::VAddr(pc).vpn() << 3)); } - AlphaFault::invoke(xc); + AlphaFault::invoke(tc); +} + +#else //!FULL_SYSTEM + +void PageTableFault::invoke(ThreadContext *tc) +{ + Process *p = tc->getProcessPtr(); + + // address is higher than the stack region or in the current stack region + if (vaddr > p->stack_base || vaddr > p->stack_min) + FaultBase::invoke(tc); + + // We've accessed the next page + if (vaddr > p->stack_min - PageBytes) { + warn("Increasing stack %#x:%#x to %#x:%#x because of access to %#x", + p->stack_min, p->stack_base, p->stack_min - PageBytes, + p->stack_base, vaddr); + p->stack_min -= PageBytes; + if (p->stack_base - p->stack_min > 8*1024*1024) + fatal("Over max stack size for one thread\n"); + p->pTable->allocate(p->stack_min, PageBytes); + } else { + FaultBase::invoke(tc); + } } #endif diff --git a/src/arch/alpha/faults.hh b/src/arch/alpha/faults.hh index e8ccc6b79..11a568174 100644 --- a/src/arch/alpha/faults.hh +++ b/src/arch/alpha/faults.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Gabe Black + * Kevin Lim */ #ifndef __ALPHA_FAULTS_HH__ @@ -46,7 +49,7 @@ class AlphaFault : public FaultBase virtual bool setRestartAddress() {return true;} public: #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif virtual FaultVect vect() = 0; virtual FaultStat & countStat() = 0; @@ -78,6 +81,29 @@ class AlignmentFault : public AlphaFault bool isAlignmentFault() {return true;} }; +#if !FULL_SYSTEM +class PageTableFault : public AlphaFault +{ + private: + Addr vaddr; + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + PageTableFault(Addr va) + : vaddr(va) {} + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} + void invoke(ThreadContext * tc); +}; + +static inline Fault genPageTableFault(Addr va) +{ + return new PageTableFault(va); +} +#endif + static inline Fault genMachineCheckFault() { return new MachineCheckFault; @@ -113,7 +139,7 @@ class ArithmeticFault : public AlphaFault FaultVect vect() {return _vect;} FaultStat & countStat() {return _count;} #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; @@ -147,7 +173,7 @@ class DtbFault : public AlphaFault FaultVect vect() = 0; FaultStat & countStat() = 0; #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; @@ -248,7 +274,7 @@ class ItbFault : public AlphaFault FaultVect vect() = 0; FaultStat & countStat() = 0; #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; diff --git a/src/arch/alpha/freebsd/system.cc b/src/arch/alpha/freebsd/system.cc index 3e50fb9a5..7cf68e0db 100644 --- a/src/arch/alpha/freebsd/system.cc +++ b/src/arch/alpha/freebsd/system.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ben Nash */ /** @@ -36,7 +38,7 @@ #include "arch/alpha/system.hh" #include "arch/alpha/freebsd/system.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/physical.hh" #include "mem/port.hh" #include "arch/isa_traits.hh" @@ -70,13 +72,13 @@ FreebsdAlphaSystem::~FreebsdAlphaSystem() void -FreebsdAlphaSystem::doCalibrateClocks(ExecContext *xc) +FreebsdAlphaSystem::doCalibrateClocks(ThreadContext *tc) { Addr ppc_vaddr = 0; Addr timer_vaddr = 0; - ppc_vaddr = (Addr)xc->readIntReg(ArgumentReg1); - timer_vaddr = (Addr)xc->readIntReg(ArgumentReg2); + ppc_vaddr = (Addr)tc->readIntReg(ArgumentReg1); + timer_vaddr = (Addr)tc->readIntReg(ArgumentReg2); virtPort.write(ppc_vaddr, (uint32_t)Clock::Frequency); virtPort.write(timer_vaddr, (uint32_t)TIMER_FREQUENCY); @@ -84,10 +86,10 @@ FreebsdAlphaSystem::doCalibrateClocks(ExecContext *xc) void -FreebsdAlphaSystem::SkipCalibrateClocksEvent::process(ExecContext *xc) +FreebsdAlphaSystem::SkipCalibrateClocksEvent::process(ThreadContext *tc) { - SkipFuncEvent::process(xc); - ((FreebsdAlphaSystem *)xc->getSystemPtr())->doCalibrateClocks(xc); + SkipFuncEvent::process(tc); + ((FreebsdAlphaSystem *)tc->getSystemPtr())->doCalibrateClocks(tc); } @@ -107,10 +109,6 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) Param<uint64_t> system_type; Param<uint64_t> system_rev; - Param<bool> bin; - VectorParam<string> binned_fns; - Param<bool> bin_int; - END_DECLARE_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) BEGIN_INIT_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) @@ -125,10 +123,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), - INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10), - INIT_PARAM_DFLT(bin, "is this system to be binned", false), - INIT_PARAM(binned_fns, "functions to be broken down and binned"), - INIT_PARAM_DFLT(bin_int, "is interrupt code binned seperately?", true) + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10) END_INIT_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) @@ -146,9 +141,6 @@ CREATE_SIM_OBJECT(FreebsdAlphaSystem) p->readfile = readfile; p->system_type = system_type; p->system_rev = system_rev; - p->bin = bin; - p->binned_fns = binned_fns; - p->bin_int = bin_int; return new FreebsdAlphaSystem(p); } diff --git a/src/arch/alpha/freebsd/system.hh b/src/arch/alpha/freebsd/system.hh index 5d996955e..e0d874e8f 100644 --- a/src/arch/alpha/freebsd/system.hh +++ b/src/arch/alpha/freebsd/system.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ben Nash */ #ifndef __KERN_FREEBSD_FREEBSD_SYSTEM_HH__ @@ -40,7 +42,7 @@ class FreebsdAlphaSystem : public AlphaSystem SkipCalibrateClocksEvent(PCEventQueue *q, const std::string &desc, Addr addr) : SkipFuncEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; SkipFuncEvent *skipDelayEvent; @@ -49,7 +51,7 @@ class FreebsdAlphaSystem : public AlphaSystem public: FreebsdAlphaSystem(Params *p); ~FreebsdAlphaSystem(); - void doCalibrateClocks(ExecContext *xc); + void doCalibrateClocks(ThreadContext *tc); }; diff --git a/src/arch/alpha/isa/branch.isa b/src/arch/alpha/isa/branch.isa index b528df938..7438e7e18 100644 --- a/src/arch/alpha/isa/branch.isa +++ b/src/arch/alpha/isa/branch.isa @@ -25,6 +25,13 @@ // 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Control transfer instructions +// output header {{ @@ -99,7 +106,7 @@ output header {{ { } - Addr branchTarget(ExecContext *xc) const; + Addr branchTarget(ThreadContext *tc) const; std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; @@ -114,10 +121,10 @@ output decoder {{ } Addr - Jump::branchTarget(ExecContext *xc) const + Jump::branchTarget(ThreadContext *tc) const { - Addr NPC = xc->readPC() + 4; - uint64_t Rb = xc->readIntReg(_srcRegIdx[0]); + Addr NPC = tc->readPC() + 4; + uint64_t Rb = tc->readIntReg(_srcRegIdx[0]); return (Rb & ~3) | (NPC & 1); } diff --git a/src/arch/alpha/isa/decoder.isa b/src/arch/alpha/isa/decoder.isa index 1adcfb948..f449d2d69 100644 --- a/src/arch/alpha/isa/decoder.isa +++ b/src/arch/alpha/isa/decoder.isa @@ -25,6 +25,13 @@ // 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// The actual decoder specification +// decode OPCODE default Unknown::unknown() { @@ -40,9 +47,11 @@ decode OPCODE default Unknown::unknown() { 0x23: ldt({{ Fa = Mem.df; }}); 0x2a: ldl_l({{ Ra.sl = Mem.sl; }}, mem_flags = LOCKED); 0x2b: ldq_l({{ Ra.uq = Mem.uq; }}, mem_flags = LOCKED); +#ifdef USE_COPY 0x20: MiscPrefetch::copy_load({{ EA = Ra; }}, {{ fault = xc->copySrcTranslate(EA); }}, inst_flags = [IsMemRef, IsLoad, IsCopy]); +#endif } format LoadOrPrefetch { @@ -62,9 +71,11 @@ decode OPCODE default Unknown::unknown() { 0x0f: stq_u({{ Mem.uq = Ra.uq; }}, {{ EA = (Rb + disp) & ~7; }}); 0x26: sts({{ Mem.ul = t_to_s(Fa.uq); }}); 0x27: stt({{ Mem.df = Fa; }}); +#ifdef USE_COPY 0x24: MiscPrefetch::copy_store({{ EA = Rb; }}, {{ fault = xc->copy(EA); }}, inst_flags = [IsMemRef, IsStore, IsCopy]); +#endif } format StoreCond { @@ -73,7 +84,7 @@ decode OPCODE default Unknown::unknown() { uint64_t tmp = write_result; // see stq_c Ra = (tmp == 0 || tmp == 1) ? tmp : Ra; - }}, mem_flags = LOCKED); + }}, mem_flags = LOCKED, inst_flags = IsStoreConditional); 0x2f: stq_c({{ Mem.uq = Ra; }}, {{ uint64_t tmp = write_result; @@ -85,7 +96,7 @@ decode OPCODE default Unknown::unknown() { // mailbox access, and we don't update the // result register at all. Ra = (tmp == 0 || tmp == 1) ? tmp : Ra; - }}, mem_flags = LOCKED); + }}, mem_flags = LOCKED, inst_flags = IsStoreConditional); } format IntegerOperate { @@ -591,8 +602,8 @@ decode OPCODE default Unknown::unknown() { 0x02e: fcmovle({{ Fc = (Fa <= 0) ? Fb : Fc; }}); 0x02f: fcmovgt({{ Fc = (Fa > 0) ? Fb : Fc; }}); - 0x024: mt_fpcr({{ FPCR = Fa.uq; }}); - 0x025: mf_fpcr({{ Fa.uq = FPCR; }}); + 0x024: mt_fpcr({{ FPCR = Fa.uq; }}, IsIprAccess); + 0x025: mf_fpcr({{ Fa.uq = FPCR; }}, IsIprAccess); } } @@ -623,7 +634,7 @@ decode OPCODE default Unknown::unknown() { #else Ra = curTick; #endif - }}); + }}, IsUnverifiable); // All of the barrier instructions below do nothing in // their execute() methods (hence the empty code blocks). @@ -641,8 +652,8 @@ decode OPCODE default Unknown::unknown() { // a barrier on integer and FP traps. "EXCB is thus a // superset of TRAPB." (Alpha ARM, Sec 4.11.4) We treat // them the same though. - 0x0000: trapb({{ }}, IsSerializing, No_OpClass); - 0x0400: excb({{ }}, IsSerializing, No_OpClass); + 0x0000: trapb({{ }}, IsSerializing, IsSerializeBefore, No_OpClass); + 0x0400: excb({{ }}, IsSerializing, IsSerializeBefore, No_OpClass); 0x4000: mb({{ }}, IsMemBarrier, MemReadOp); 0x4400: wmb({{ }}, IsWriteBarrier, MemWriteOp); } @@ -652,11 +663,11 @@ decode OPCODE default Unknown::unknown() { 0xe000: rc({{ Ra = xc->readIntrFlag(); xc->setIntrFlag(0); - }}, IsNonSpeculative); + }}, IsNonSpeculative, IsUnverifiable); 0xf000: rs({{ Ra = xc->readIntrFlag(); xc->setIntrFlag(1); - }}, IsNonSpeculative); + }}, IsNonSpeculative, IsUnverifiable); } #else format FailUnimpl { @@ -690,15 +701,15 @@ decode OPCODE default Unknown::unknown() { 0x00: decode PALFUNC { format EmulatedCallPal { 0x00: halt ({{ - SimExit(curTick, "halt instruction encountered"); + exitSimLoop(curTick, "halt instruction encountered"); }}, IsNonSpeculative); 0x83: callsys({{ xc->syscall(R0); - }}, IsNonSpeculative); + }}, IsSerializeAfter, IsNonSpeculative); // Read uniq reg into ABI return value register (r0) - 0x9e: rduniq({{ R0 = Runiq; }}); + 0x9e: rduniq({{ R0 = Runiq; }}, IsIprAccess); // Write uniq reg with value from ABI arg register (r16) - 0x9f: wruniq({{ Runiq = R16; }}); + 0x9f: wruniq({{ Runiq = R16; }}, IsIprAccess); } } #endif @@ -735,7 +746,7 @@ decode OPCODE default Unknown::unknown() { format HwMoveIPR { 1: hw_mfpr({{ Ra = xc->readMiscRegWithEffect(ipr_index, fault); - }}); + }}, IsIprAccess); } } @@ -745,69 +756,69 @@ decode OPCODE default Unknown::unknown() { 1: hw_mtpr({{ xc->setMiscRegWithEffect(ipr_index, Ra); if (traceData) { traceData->setData(Ra); } - }}); + }}, IsIprAccess); } } format BasicOperate { 0x1e: decode PALMODE { 0: OpcdecFault::hw_rei(); - 1:hw_rei({{ xc->hwrei(); }}, IsSerializing); + 1:hw_rei({{ xc->hwrei(); }}, IsSerializing, IsSerializeBefore); } // M5 special opcodes use the reserved 0x01 opcode space 0x01: decode M5FUNC { 0x00: arm({{ - AlphaPseudo::arm(xc->xcBase()); + AlphaPseudo::arm(xc->tcBase()); }}, IsNonSpeculative); 0x01: quiesce({{ - AlphaPseudo::quiesce(xc->xcBase()); - }}, IsNonSpeculative); + AlphaPseudo::quiesce(xc->tcBase()); + }}, IsNonSpeculative, IsQuiesce); 0x02: quiesceNs({{ - AlphaPseudo::quiesceNs(xc->xcBase(), R16); - }}, IsNonSpeculative); + AlphaPseudo::quiesceNs(xc->tcBase(), R16); + }}, IsNonSpeculative, IsQuiesce); 0x03: quiesceCycles({{ - AlphaPseudo::quiesceCycles(xc->xcBase(), R16); - }}, IsNonSpeculative); + AlphaPseudo::quiesceCycles(xc->tcBase(), R16); + }}, IsNonSpeculative, IsQuiesce); 0x04: quiesceTime({{ - R0 = AlphaPseudo::quiesceTime(xc->xcBase()); + R0 = AlphaPseudo::quiesceTime(xc->tcBase()); }}, IsNonSpeculative); 0x10: ivlb({{ - AlphaPseudo::ivlb(xc->xcBase()); + AlphaPseudo::ivlb(xc->tcBase()); }}, No_OpClass, IsNonSpeculative); 0x11: ivle({{ - AlphaPseudo::ivle(xc->xcBase()); + AlphaPseudo::ivle(xc->tcBase()); }}, No_OpClass, IsNonSpeculative); 0x20: m5exit_old({{ - AlphaPseudo::m5exit_old(xc->xcBase()); + AlphaPseudo::m5exit_old(xc->tcBase()); }}, No_OpClass, IsNonSpeculative); 0x21: m5exit({{ - AlphaPseudo::m5exit(xc->xcBase(), R16); + AlphaPseudo::m5exit(xc->tcBase(), R16); }}, No_OpClass, IsNonSpeculative); - 0x30: initparam({{ Ra = xc->xcBase()->getCpuPtr()->system->init_param; }}); + 0x30: initparam({{ Ra = xc->tcBase()->getCpuPtr()->system->init_param; }}); 0x40: resetstats({{ - AlphaPseudo::resetstats(xc->xcBase(), R16, R17); + AlphaPseudo::resetstats(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x41: dumpstats({{ - AlphaPseudo::dumpstats(xc->xcBase(), R16, R17); + AlphaPseudo::dumpstats(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x42: dumpresetstats({{ - AlphaPseudo::dumpresetstats(xc->xcBase(), R16, R17); + AlphaPseudo::dumpresetstats(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x43: m5checkpoint({{ - AlphaPseudo::m5checkpoint(xc->xcBase(), R16, R17); + AlphaPseudo::m5checkpoint(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x50: m5readfile({{ - R0 = AlphaPseudo::readfile(xc->xcBase(), R16, R17, R18); + R0 = AlphaPseudo::readfile(xc->tcBase(), R16, R17, R18); }}, IsNonSpeculative); 0x51: m5break({{ - AlphaPseudo::debugbreak(xc->xcBase()); + AlphaPseudo::debugbreak(xc->tcBase()); }}, IsNonSpeculative); 0x52: m5switchcpu({{ - AlphaPseudo::switchcpu(xc->xcBase()); + AlphaPseudo::switchcpu(xc->tcBase()); }}, IsNonSpeculative); 0x53: m5addsymbol({{ - AlphaPseudo::addsymbol(xc->xcBase(), R16, R17); + AlphaPseudo::addsymbol(xc->tcBase(), R16, R17); }}, IsNonSpeculative); 0x54: m5panic({{ panic("M5 panic instruction called at pc=%#x.", xc->readPC()); diff --git a/src/arch/alpha/isa/fp.isa b/src/arch/alpha/isa/fp.isa index f34c13c42..b4339a1b7 100644 --- a/src/arch/alpha/isa/fp.isa +++ b/src/arch/alpha/isa/fp.isa @@ -25,6 +25,17 @@ // 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Floating-point instructions +// +// Note that many FP-type instructions which do not support all the +// various rounding & trapping modes use the simpler format +// BasicOperateWithNopCheck. +// output exec {{ /// Check "FP enabled" machine status bit. Called when executing any FP diff --git a/src/arch/alpha/isa/int.isa b/src/arch/alpha/isa/int.isa index 17ecc1a51..45e096ebd 100644 --- a/src/arch/alpha/isa/int.isa +++ b/src/arch/alpha/isa/int.isa @@ -25,6 +25,13 @@ // 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Integer operate instructions +// output header {{ /** diff --git a/src/arch/alpha/isa/main.isa b/src/arch/alpha/isa/main.isa index 03a8e1ff5..1270bf8d8 100644 --- a/src/arch/alpha/isa/main.isa +++ b/src/arch/alpha/isa/main.isa @@ -25,6 +25,20 @@ // 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Alpha ISA description file. +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// +// Output include file directives. +// output header {{ #include <sstream> @@ -42,7 +56,7 @@ output decoder {{ #include "base/fenv.hh" #include "base/loader/symtab.hh" #include "config/ss_compatible_fp.hh" -#include "cpu/exec_context.hh" // for Jump::branchTarget() +#include "cpu/thread_context.hh" // for Jump::branchTarget() #include <math.h> diff --git a/src/arch/alpha/isa/mem.isa b/src/arch/alpha/isa/mem.isa index 98c7ba979..08a0a2343 100644 --- a/src/arch/alpha/isa/mem.isa +++ b/src/arch/alpha/isa/mem.isa @@ -25,6 +25,14 @@ // 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. +// +// Authors: Steve Reinhardt +// Kevin Lim + +//////////////////////////////////////////////////////////////////// +// +// Memory-format instructions: LoadAddress, Load, Store +// output header {{ /** diff --git a/src/arch/alpha/isa/opcdec.isa b/src/arch/alpha/isa/opcdec.isa index bb2f91e5c..d279ae050 100644 --- a/src/arch/alpha/isa/opcdec.isa +++ b/src/arch/alpha/isa/opcdec.isa @@ -25,6 +25,13 @@ // 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. +// +// Authors: Kevin Lim + +//////////////////////////////////////////////////////////////////// +// +// OPCDEC fault instructions +// output header {{ /** diff --git a/src/arch/alpha/isa/pal.isa b/src/arch/alpha/isa/pal.isa index e07bea5a8..f4c10da1d 100644 --- a/src/arch/alpha/isa/pal.isa +++ b/src/arch/alpha/isa/pal.isa @@ -25,6 +25,13 @@ // 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// PAL calls & PAL-specific instructions +// output header {{ /** @@ -259,9 +266,11 @@ output decoder {{ } }}; -def format HwMoveIPR(code) {{ +def format HwMoveIPR(code, *flags) {{ + all_flags = ['IprAccessOp'] + all_flags += flags iop = InstObjParams(name, Name, 'HwMoveIPR', CodeBlock(code), - ['IprAccessOp']) + all_flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) diff --git a/src/arch/alpha/isa/unimp.isa b/src/arch/alpha/isa/unimp.isa index 392522801..6cfaa6991 100644 --- a/src/arch/alpha/isa/unimp.isa +++ b/src/arch/alpha/isa/unimp.isa @@ -25,6 +25,13 @@ // 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Unimplemented instructions +// output header {{ /** diff --git a/src/arch/alpha/isa/unknown.isa b/src/arch/alpha/isa/unknown.isa index 47d166255..1e95ccf68 100644 --- a/src/arch/alpha/isa/unknown.isa +++ b/src/arch/alpha/isa/unknown.isa @@ -25,6 +25,13 @@ // 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Unknown instructions +// output decoder {{ std::string diff --git a/src/arch/alpha/isa/util.isa b/src/arch/alpha/isa/util.isa index 9fbbf6636..8700d1e0b 100644 --- a/src/arch/alpha/isa/util.isa +++ b/src/arch/alpha/isa/util.isa @@ -25,6 +25,13 @@ // 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Utility functions for execute methods +// output exec {{ diff --git a/src/arch/alpha/isa_traits.hh b/src/arch/alpha/isa_traits.hh index 65c72115b..663b144ab 100644 --- a/src/arch/alpha/isa_traits.hh +++ b/src/arch/alpha/isa_traits.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Gabe Black */ #ifndef __ARCH_ALPHA_ISA_TRAITS_HH__ diff --git a/src/arch/alpha/linux/aligned.hh b/src/arch/alpha/linux/aligned.hh index cabecb283..c4687e348 100644 --- a/src/arch/alpha/linux/aligned.hh +++ b/src/arch/alpha/linux/aligned.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __ARCH_ALPHA_LINUX_ALIGNED_HH__ diff --git a/src/arch/alpha/linux/linux.cc b/src/arch/alpha/linux/linux.cc index f123ae1fe..bc0d48e0d 100644 --- a/src/arch/alpha/linux/linux.cc +++ b/src/arch/alpha/linux/linux.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #include "arch/alpha/linux/linux.hh" diff --git a/src/arch/alpha/linux/linux.hh b/src/arch/alpha/linux/linux.hh index f04e2bfa8..09988bab2 100644 --- a/src/arch/alpha/linux/linux.hh +++ b/src/arch/alpha/linux/linux.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #ifndef __ALPHA_ALPHA_LINUX_HH diff --git a/src/arch/alpha/linux/process.cc b/src/arch/alpha/linux/process.cc index 9f4f65db8..997c78ac9 100644 --- a/src/arch/alpha/linux/process.cc +++ b/src/arch/alpha/linux/process.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Ali Saidi */ #include "arch/alpha/linux/linux.hh" @@ -31,7 +34,7 @@ #include "arch/alpha/isa_traits.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/linux.hh" #include "sim/process.hh" @@ -45,9 +48,9 @@ using namespace AlphaISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<Linux::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<Linux::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "Linux"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -55,7 +58,7 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "#1 Mon Aug 18 11:32:15 EDT 2003"); strcpy(name->machine, "alpha"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } @@ -64,18 +67,18 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, /// different in practice from those used by Tru64 processes. static SyscallReturn osf_getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - // unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); switch (op) { case 45: { // GSI_IEEE_FP_CONTROL - TypedBufferArg<uint64_t> fpcr(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); // I don't think this exactly matches the HW FPCR *fpcr = 0; - fpcr.copyOut(xc->getMemPort()); + fpcr.copyOut(tc->getMemPort()); return 0; } @@ -91,17 +94,17 @@ osf_getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, /// Target osf_setsysinfo() handler. static SyscallReturn osf_setsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - // unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); switch (op) { case 14: { // SSI_IEEE_FP_CONTROL - TypedBufferArg<uint64_t> fpcr(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); // I don't think this exactly matches the HW FPCR - fpcr.copyIn(xc->getMemPort()); + fpcr.copyIn(tc->getMemPort()); DPRINTFR(SyscallVerbose, "osf_setsysinfo(SSI_IEEE_FP_CONTROL): " " setting FPCR to 0x%x\n", gtoh(*(uint64_t*)fpcr)); return 0; diff --git a/src/arch/alpha/linux/process.hh b/src/arch/alpha/linux/process.hh index 2e0566665..9cc7dc7b6 100644 --- a/src/arch/alpha/linux/process.hh +++ b/src/arch/alpha/linux/process.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __ALPHA_LINUX_PROCESS_HH__ diff --git a/src/arch/alpha/linux/system.cc b/src/arch/alpha/linux/system.cc index cdb96096c..9fe63c390 100644 --- a/src/arch/alpha/linux/system.cc +++ b/src/arch/alpha/linux/system.cc @@ -24,6 +24,11 @@ * 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. + * + * Authors: Ali Saidi + * Lisa Hsu + * Nathan Binkert + * Steve Reinhardt */ /** @@ -41,7 +46,7 @@ #include "arch/alpha/linux/threadinfo.hh" #include "arch/alpha/system.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "dev/platform.hh" #include "kern/linux/printk.hh" @@ -132,24 +137,6 @@ LinuxAlphaSystem::LinuxAlphaSystem(Params *p) } else { printThreadEvent = NULL; } - - if (params()->bin_int) { - intStartEvent = addPalFuncEvent<InterruptStartEvent>("sys_int_21"); - if (!intStartEvent) - panic("could not find symbol: sys_int_21\n"); - - intEndEvent = addPalFuncEvent<InterruptEndEvent>("rti_to_kern"); - if (!intEndEvent) - panic("could not find symbol: rti_to_kern\n"); - - intEndEvent2 = addPalFuncEvent<InterruptEndEvent>("rti_to_user"); - if (!intEndEvent2) - panic("could not find symbol: rti_to_user\n"); - - intEndEvent3 = addKernelFuncEvent<InterruptEndEvent>("do_softirq"); - if (!intEndEvent3) - panic("could not find symbol: do_softirq\n"); - } } LinuxAlphaSystem::~LinuxAlphaSystem() @@ -163,37 +150,37 @@ LinuxAlphaSystem::~LinuxAlphaSystem() delete debugPrintkEvent; delete idleStartEvent; delete printThreadEvent; - delete intStartEvent; - delete intEndEvent; - delete intEndEvent2; } void -LinuxAlphaSystem::setDelayLoop(ExecContext *xc) +LinuxAlphaSystem::setDelayLoop(ThreadContext *tc) { Addr addr = 0; if (kernelSymtab->findAddress("loops_per_jiffy", addr)) { - Tick cpuFreq = xc->getCpuPtr()->frequency(); + Tick cpuFreq = tc->getCpuPtr()->frequency(); Tick intrFreq = platform->intrFrequency(); - xc->getVirtPort(xc)->write(addr, - (uint32_t)((cpuFreq / intrFreq) * 0.9988)); + VirtualPort *vp; + + vp = tc->getVirtPort(); + vp->writeHtoG(addr, (uint32_t)((cpuFreq / intrFreq) * 0.9988)); + tc->delVirtPort(vp); } } void -LinuxAlphaSystem::SkipDelayLoopEvent::process(ExecContext *xc) +LinuxAlphaSystem::SkipDelayLoopEvent::process(ThreadContext *tc) { - SkipFuncEvent::process(xc); + SkipFuncEvent::process(tc); // calculate and set loops_per_jiffy - ((LinuxAlphaSystem *)xc->getSystemPtr())->setDelayLoop(xc); + ((LinuxAlphaSystem *)tc->getSystemPtr())->setDelayLoop(tc); } void -LinuxAlphaSystem::PrintThreadInfo::process(ExecContext *xc) +LinuxAlphaSystem::PrintThreadInfo::process(ThreadContext *tc) { - Linux::ThreadInfo ti(xc); + Linux::ThreadInfo ti(tc); DPRINTF(Thread, "Currently Executing Thread %s, pid %d, started at: %d\n", ti.curTaskName(), ti.curTaskPID(), ti.curTaskStart()); @@ -216,10 +203,6 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(LinuxAlphaSystem) Param<uint64_t> system_type; Param<uint64_t> system_rev; - Param<bool> bin; - VectorParam<string> binned_fns; - Param<bool> bin_int; - END_DECLARE_SIM_OBJECT_PARAMS(LinuxAlphaSystem) BEGIN_INIT_SIM_OBJECT_PARAMS(LinuxAlphaSystem) @@ -234,10 +217,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(LinuxAlphaSystem) INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), - INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10), - INIT_PARAM_DFLT(bin, "is this system to be binned", false), - INIT_PARAM(binned_fns, "functions to be broken down and binned"), - INIT_PARAM_DFLT(bin_int, "is interrupt code binned seperately?", true) + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10) END_INIT_SIM_OBJECT_PARAMS(LinuxAlphaSystem) @@ -255,9 +235,6 @@ CREATE_SIM_OBJECT(LinuxAlphaSystem) p->readfile = readfile; p->system_type = system_type; p->system_rev = system_rev; - p->bin = bin; - p->binned_fns = binned_fns; - p->bin_int = bin_int; return new LinuxAlphaSystem(p); } diff --git a/src/arch/alpha/linux/system.hh b/src/arch/alpha/linux/system.hh index 0c1fb037e..6921ba820 100644 --- a/src/arch/alpha/linux/system.hh +++ b/src/arch/alpha/linux/system.hh @@ -24,12 +24,16 @@ * 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. + * + * Authors: Ali Saidi + * Lisa Hsu + * Nathan Binkert */ #ifndef __ARCH_ALPHA_LINUX_SYSTEM_HH__ #define __ARCH_ALPHA_LINUX_SYSTEM_HH__ -class ExecContext; +class ThreadContext; class BreakPCEvent; class IdleStartEvent; @@ -41,7 +45,7 @@ using namespace AlphaISA; using namespace Linux; /** - * This class contains linux specific system code (Loading, Events, Binning). + * This class contains linux specific system code (Loading, Events). * It points to objects that are the system binaries to load and patches them * appropriately to work in simulator. */ @@ -53,7 +57,7 @@ class LinuxAlphaSystem : public AlphaSystem public: SkipDelayLoopEvent(PCEventQueue *q, const std::string &desc, Addr addr) : SkipFuncEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; class PrintThreadInfo : public PCEvent @@ -61,7 +65,7 @@ class LinuxAlphaSystem : public AlphaSystem public: PrintThreadInfo(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; @@ -120,18 +124,6 @@ class LinuxAlphaSystem : public AlphaSystem */ PrintThreadInfo *printThreadEvent; - /** - * Event to bin Interrupts seperately from kernel code - */ - InterruptStartEvent *intStartEvent; - - /** - * Event to bin Interrupts seperately from kernel code - */ - InterruptEndEvent *intEndEvent; - InterruptEndEvent *intEndEvent2; - InterruptEndEvent *intEndEvent3; - /** Grab the PCBB of the idle process when it starts */ IdleStartEvent *idleStartEvent; @@ -139,7 +131,7 @@ class LinuxAlphaSystem : public AlphaSystem LinuxAlphaSystem(Params *p); ~LinuxAlphaSystem(); - void setDelayLoop(ExecContext *xc); + void setDelayLoop(ThreadContext *tc); }; #endif // __ARCH_ALPHA_LINUX_SYSTEM_HH__ diff --git a/src/arch/alpha/linux/thread_info.hh b/src/arch/alpha/linux/thread_info.hh index 88791b00d..78257da56 100644 --- a/src/arch/alpha/linux/thread_info.hh +++ b/src/arch/alpha/linux/thread_info.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __ARCH_ALPHA_LINUX_THREAD_INFO_H__ diff --git a/src/arch/alpha/linux/threadinfo.hh b/src/arch/alpha/linux/threadinfo.hh index 8f03c9314..caeb69f15 100644 --- a/src/arch/alpha/linux/threadinfo.hh +++ b/src/arch/alpha/linux/threadinfo.hh @@ -24,13 +24,16 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __ARCH_ALPHA_LINUX_LINUX_TREADNIFO_HH__ #define __ARCH_ALPHA_LINUX_LINUX_TREADNIFO_HH__ #include "arch/alpha/linux/thread_info.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/sched.hh" #include "sim/vptr.hh" @@ -39,10 +42,10 @@ namespace Linux { class ThreadInfo { private: - ExecContext *xc; + ThreadContext *tc; public: - ThreadInfo(ExecContext *exec) : xc(exec) {} + ThreadInfo(ThreadContext *_tc) : tc(_tc) {} ~ThreadInfo() {} inline VPtr<thread_info> @@ -54,15 +57,15 @@ class ThreadInfo * thread_info struct. So we can get the address by masking off * the lower 14 bits. */ - current = xc->readIntReg(TheISA::StackPointerReg) & ~0x3fff; - return VPtr<thread_info>(xc, current); + current = tc->readIntReg(TheISA::StackPointerReg) & ~0x3fff; + return VPtr<thread_info>(tc, current); } inline VPtr<task_struct> curTaskInfo() { Addr task = curThreadInfo()->task; - return VPtr<task_struct>(xc, task); + return VPtr<task_struct>(tc, task); } std::string diff --git a/src/arch/alpha/osfpal.cc b/src/arch/alpha/osfpal.cc index a48bd28d9..ed1d255a6 100644 --- a/src/arch/alpha/osfpal.cc +++ b/src/arch/alpha/osfpal.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include "arch/alpha/osfpal.hh" diff --git a/src/arch/alpha/osfpal.hh b/src/arch/alpha/osfpal.hh index f46d2bce1..cf3940b85 100644 --- a/src/arch/alpha/osfpal.hh +++ b/src/arch/alpha/osfpal.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __OSFPAL_HH__ diff --git a/src/arch/alpha/process.cc b/src/arch/alpha/process.cc index 25ee79692..970292cd8 100644 --- a/src/arch/alpha/process.cc +++ b/src/arch/alpha/process.cc @@ -24,59 +24,22 @@ * 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. + * + * Authors: Gabe Black + * Ali Saidi */ #include "arch/alpha/constants.hh" #include "arch/alpha/process.hh" -#include "arch/alpha/linux/process.hh" -#include "arch/alpha/tru64/process.hh" #include "base/loader/object_file.hh" #include "base/misc.hh" -#include "cpu/exec_context.hh" -#include "sim/builder.hh" +#include "cpu/thread_context.hh" #include "sim/system.hh" using namespace AlphaISA; using namespace std; -AlphaLiveProcess * -AlphaLiveProcess::create(const std::string &nm, System *system, int stdin_fd, - int stdout_fd, int stderr_fd, std::string executable, - std::vector<std::string> &argv, std::vector<std::string> &envp) -{ - AlphaLiveProcess *process = NULL; - - ObjectFile *objFile = createObjectFile(executable); - if (objFile == NULL) { - fatal("Can't load object file %s", executable); - } - - - if (objFile->getArch() != ObjectFile::Alpha) - fatal("Object file does not match architecture."); - switch (objFile->getOpSys()) { - case ObjectFile::Tru64: - process = new AlphaTru64Process(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - - case ObjectFile::Linux: - process = new AlphaLinuxProcess(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - - default: - fatal("Unknown/unsupported operating system."); - } - - if (process == NULL) - fatal("Unknown error creating process object."); - return process; -} - AlphaLiveProcess::AlphaLiveProcess(const std::string &nm, ObjectFile *objFile, System *_system, int stdin_fd, int stdout_fd, int stderr_fd, std::vector<std::string> &argv, std::vector<std::string> &envp) @@ -104,64 +67,7 @@ AlphaLiveProcess::startup() { argsInit(MachineBytes, VMPageSize); - execContexts[0]->setIntReg(GlobalPointerReg, objFile->globalPointer()); + threadContexts[0]->setIntReg(GlobalPointerReg, objFile->globalPointer()); } - - -BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaLiveProcess) - - VectorParam<string> cmd; - Param<string> executable; - Param<string> input; - Param<string> output; - VectorParam<string> env; - SimObjectParam<System *> system; - -END_DECLARE_SIM_OBJECT_PARAMS(AlphaLiveProcess) - - -BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaLiveProcess) - - INIT_PARAM(cmd, "command line (executable plus arguments)"), - INIT_PARAM(executable, "executable (overrides cmd[0] if set)"), - INIT_PARAM(input, "filename for stdin (dflt: use sim stdin)"), - INIT_PARAM(output, "filename for stdout/stderr (dflt: use sim stdout)"), - INIT_PARAM(env, "environment settings"), - INIT_PARAM(system, "system") - -END_INIT_SIM_OBJECT_PARAMS(AlphaLiveProcess) - - -CREATE_SIM_OBJECT(AlphaLiveProcess) -{ - string in = input; - string out = output; - - // initialize file descriptors to default: same as simulator - int stdin_fd, stdout_fd, stderr_fd; - - if (in == "stdin" || in == "cin") - stdin_fd = STDIN_FILENO; - else - stdin_fd = Process::openInputFile(input); - - if (out == "stdout" || out == "cout") - stdout_fd = STDOUT_FILENO; - else if (out == "stderr" || out == "cerr") - stdout_fd = STDERR_FILENO; - else - stdout_fd = Process::openOutputFile(out); - - stderr_fd = (stdout_fd != STDOUT_FILENO) ? stdout_fd : STDERR_FILENO; - - return AlphaLiveProcess::create(getInstanceName(), system, - stdin_fd, stdout_fd, stderr_fd, - (string)executable == "" ? cmd[0] : executable, - cmd, env); -} - - -REGISTER_SIM_OBJECT("AlphaLiveProcess", AlphaLiveProcess) - diff --git a/src/arch/alpha/process.hh b/src/arch/alpha/process.hh index d97b36e2d..5d5c9a52e 100644 --- a/src/arch/alpha/process.hh +++ b/src/arch/alpha/process.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Gabe Black + * Ali Saidi */ #ifndef __ALPHA_PROCESS_HH__ @@ -46,18 +49,6 @@ class AlphaLiveProcess : public LiveProcess std::vector<std::string> &envp); void startup(); - - public: - // this function is used to create the LiveProcess object, since - // we can't tell which subclass of LiveProcess to use until we - // open and look at the object file. - static AlphaLiveProcess *create(const std::string &nm, - System *_system, - int stdin_fd, int stdout_fd, int stderr_fd, - std::string executable, - std::vector<std::string> &argv, - std::vector<std::string> &envp); - }; diff --git a/src/arch/alpha/regfile.hh b/src/arch/alpha/regfile.hh index af01b7829..1025412cd 100644 --- a/src/arch/alpha/regfile.hh +++ b/src/arch/alpha/regfile.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Gabe Black */ #ifndef __ARCH_ALPHA_REGFILE_HH__ @@ -34,7 +36,7 @@ #include "sim/faults.hh" class Checkpoint; -class ExecContext; +class ThreadContext; namespace AlphaISA { @@ -60,6 +62,8 @@ namespace AlphaISA void unserialize(Checkpoint *cp, const std::string §ion); + void clear() + { bzero(regs, sizeof(regs)); } }; class FloatRegFile @@ -75,6 +79,8 @@ namespace AlphaISA void unserialize(Checkpoint *cp, const std::string §ion); + void clear() + { bzero(d, sizeof(d)); } }; class MiscRegFile { @@ -88,7 +94,7 @@ namespace AlphaISA MiscReg readReg(int misc_reg); MiscReg readRegWithEffect(int misc_reg, Fault &fault, - ExecContext *xc); + ThreadContext *tc); //These functions should be removed once the simplescalar cpu model //has been replaced. @@ -98,8 +104,14 @@ namespace AlphaISA Fault setReg(int misc_reg, const MiscReg &val); Fault setRegWithEffect(int misc_reg, const MiscReg &val, - ExecContext *xc); + ThreadContext *tc); + void clear() + { + fpcr = uniq = 0; + lock_flag = 0; + lock_addr = 0; + } #if FULL_SYSTEM protected: typedef uint64_t InternalProcReg; @@ -107,9 +119,9 @@ namespace AlphaISA InternalProcReg ipr[NumInternalProcRegs]; // Internal processor regs private: - InternalProcReg readIpr(int idx, Fault &fault, ExecContext *xc); + InternalProcReg readIpr(int idx, Fault &fault, ThreadContext *tc); - Fault setIpr(int idx, InternalProcReg val, ExecContext *xc); + Fault setIpr(int idx, InternalProcReg val, ThreadContext *tc); #endif friend class RegFile; }; @@ -169,9 +181,9 @@ namespace AlphaISA void clear() { - bzero(&intRegFile, sizeof(intRegFile)); - bzero(&floatRegFile, sizeof(floatRegFile)); - bzero(&miscRegFile, sizeof(miscRegFile)); + intRegFile.clear(); + floatRegFile.clear(); + miscRegFile.clear(); } MiscReg readMiscReg(int miscReg) @@ -180,10 +192,10 @@ namespace AlphaISA } MiscReg readMiscRegWithEffect(int miscReg, - Fault &fault, ExecContext *xc) + Fault &fault, ThreadContext *tc) { fault = NoFault; - return miscRegFile.readRegWithEffect(miscReg, fault, xc); + return miscRegFile.readRegWithEffect(miscReg, fault, tc); } Fault setMiscReg(int miscReg, const MiscReg &val) @@ -192,9 +204,9 @@ namespace AlphaISA } Fault setMiscRegWithEffect(int miscReg, const MiscReg &val, - ExecContext * xc) + ThreadContext * tc) { - return miscRegFile.setRegWithEffect(miscReg, val, xc); + return miscRegFile.setRegWithEffect(miscReg, val, tc); } FloatReg readFloatReg(int floatReg) @@ -266,12 +278,12 @@ namespace AlphaISA } }; - void copyRegs(ExecContext *src, ExecContext *dest); + void copyRegs(ThreadContext *src, ThreadContext *dest); - void copyMiscRegs(ExecContext *src, ExecContext *dest); + void copyMiscRegs(ThreadContext *src, ThreadContext *dest); #if FULL_SYSTEM - void copyIprs(ExecContext *src, ExecContext *dest); + void copyIprs(ThreadContext *src, ThreadContext *dest); #endif } // namespace AlphaISA diff --git a/src/arch/alpha/stacktrace.cc b/src/arch/alpha/stacktrace.cc index 8691e12dc..d70a4d6dd 100644 --- a/src/arch/alpha/stacktrace.cc +++ b/src/arch/alpha/stacktrace.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <string> @@ -34,36 +36,42 @@ #include "base/bitfield.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "sim/system.hh" using namespace std; using namespace AlphaISA; -ProcessInfo::ProcessInfo(ExecContext *_xc) - : xc(_xc) +ProcessInfo::ProcessInfo(ThreadContext *_tc) + : tc(_tc) { Addr addr = 0; - if (!xc->getSystemPtr()->kernelSymtab->findAddress("thread_info_size", addr)) + VirtualPort *vp; + + vp = tc->getVirtPort(); + + if (!tc->getSystemPtr()->kernelSymtab->findAddress("thread_info_size", addr)) panic("thread info not compiled into kernel\n"); - thread_info_size = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + thread_info_size = vp->readGtoH<int32_t>(addr); - if (!xc->getSystemPtr()->kernelSymtab->findAddress("task_struct_size", addr)) + if (!tc->getSystemPtr()->kernelSymtab->findAddress("task_struct_size", addr)) panic("thread info not compiled into kernel\n"); - task_struct_size = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + task_struct_size = vp->readGtoH<int32_t>(addr); - if (!xc->getSystemPtr()->kernelSymtab->findAddress("thread_info_task", addr)) + if (!tc->getSystemPtr()->kernelSymtab->findAddress("thread_info_task", addr)) panic("thread info not compiled into kernel\n"); - task_off = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + task_off = vp->readGtoH<int32_t>(addr); - if (!xc->getSystemPtr()->kernelSymtab->findAddress("task_struct_pid", addr)) + if (!tc->getSystemPtr()->kernelSymtab->findAddress("task_struct_pid", addr)) panic("thread info not compiled into kernel\n"); - pid_off = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + pid_off = vp->readGtoH<int32_t>(addr); - if (!xc->getSystemPtr()->kernelSymtab->findAddress("task_struct_comm", addr)) + if (!tc->getSystemPtr()->kernelSymtab->findAddress("task_struct_comm", addr)) panic("thread info not compiled into kernel\n"); - name_off = gtoh(xc->getVirtPort()->read<int32_t>(addr)); + name_off = vp->readGtoH<int32_t>(addr); + + tc->delVirtPort(vp); } Addr @@ -73,7 +81,15 @@ ProcessInfo::task(Addr ksp) const if (base == ULL(0xfffffc0000000000)) return 0; - return gtoh(xc->getVirtPort()->read<Addr>(base + task_off)); + Addr tsk; + + VirtualPort *vp; + + vp = tc->getVirtPort(); + tsk = vp->readGtoH<Addr>(base + task_off); + tc->delVirtPort(vp); + + return tsk; } int @@ -83,7 +99,15 @@ ProcessInfo::pid(Addr ksp) const if (!task) return -1; - return gtoh(xc->getVirtPort()->read<uint16_t>(task + pid_off)); + uint16_t pd; + + VirtualPort *vp; + + vp = tc->getVirtPort(); + pd = vp->readGtoH<uint16_t>(task + pid_off); + tc->delVirtPort(vp); + + return pd; } string @@ -94,7 +118,7 @@ ProcessInfo::name(Addr ksp) const return "console"; char comm[256]; - CopyStringOut(xc, comm, task + name_off, sizeof(comm)); + CopyStringOut(tc, comm, task + name_off, sizeof(comm)); if (!comm[0]) return "startup"; @@ -102,14 +126,14 @@ ProcessInfo::name(Addr ksp) const } StackTrace::StackTrace() - : xc(0), stack(64) + : tc(0), stack(64) { } -StackTrace::StackTrace(ExecContext *_xc, StaticInstPtr inst) - : xc(0), stack(64) +StackTrace::StackTrace(ThreadContext *_tc, StaticInstPtr inst) + : tc(0), stack(64) { - trace(_xc, inst); + trace(_tc, inst); } StackTrace::~StackTrace() @@ -117,15 +141,15 @@ StackTrace::~StackTrace() } void -StackTrace::trace(ExecContext *_xc, bool is_call) +StackTrace::trace(ThreadContext *_tc, bool is_call) { - xc = _xc; + tc = _tc; - bool usermode = (xc->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; + bool usermode = (tc->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; - Addr pc = xc->readNextPC(); - bool kernel = xc->getSystemPtr()->kernelStart <= pc && - pc <= xc->getSystemPtr()->kernelEnd; + Addr pc = tc->readNextPC(); + bool kernel = tc->getSystemPtr()->kernelStart <= pc && + pc <= tc->getSystemPtr()->kernelEnd; if (usermode) { stack.push_back(user); @@ -137,8 +161,8 @@ StackTrace::trace(ExecContext *_xc, bool is_call) return; } - SymbolTable *symtab = xc->getSystemPtr()->kernelSymtab; - Addr ksp = xc->readIntReg(TheISA::StackPointerReg); + SymbolTable *symtab = tc->getSystemPtr()->kernelSymtab; + Addr ksp = tc->readIntReg(TheISA::StackPointerReg); Addr bottom = ksp & ~0x3fff; Addr addr; @@ -147,7 +171,7 @@ StackTrace::trace(ExecContext *_xc, bool is_call) panic("could not find address %#x", pc); stack.push_back(addr); - pc = xc->readPC(); + pc = tc->readPC(); } Addr ra; @@ -179,8 +203,8 @@ StackTrace::trace(ExecContext *_xc, bool is_call) return; } - bool kernel = xc->getSystemPtr()->kernelStart <= pc && - pc <= xc->getSystemPtr()->kernelEnd; + bool kernel = tc->getSystemPtr()->kernelStart <= pc && + pc <= tc->getSystemPtr()->kernelEnd; if (!kernel) return; @@ -194,22 +218,22 @@ StackTrace::trace(ExecContext *_xc, bool is_call) bool StackTrace::isEntry(Addr addr) { - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp12)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp12)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp7)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp7)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp11)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp11)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp21)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp21)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp9)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp9)) return true; - if (addr == xc->readMiscReg(AlphaISA::IPR_PALtemp2)) + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp2)) return true; return false; @@ -294,7 +318,7 @@ StackTrace::decodePrologue(Addr sp, Addr callpc, Addr func, for (Addr pc = func; pc < callpc; pc += sizeof(MachInst)) { MachInst inst; - CopyOut(xc, (uint8_t *)&inst, pc, sizeof(MachInst)); + CopyOut(tc, (uint8_t *)&inst, pc, sizeof(MachInst)); int reg, disp; if (decodeStack(inst, disp)) { @@ -305,7 +329,7 @@ StackTrace::decodePrologue(Addr sp, Addr callpc, Addr func, size += disp; } else if (decodeSave(inst, reg, disp)) { if (!ra && reg == ReturnAddressReg) { - CopyOut(xc, (uint8_t *)&ra, sp + disp, sizeof(Addr)); + CopyOut(tc, (uint8_t *)&ra, sp + disp, sizeof(Addr)); if (!ra) { // panic("no return address value pc=%#x\n", pc); return false; @@ -321,8 +345,8 @@ StackTrace::decodePrologue(Addr sp, Addr callpc, Addr func, void StackTrace::dump() { - StringWrap name(xc->getCpuPtr()->name()); - SymbolTable *symtab = xc->getSystemPtr()->kernelSymtab; + StringWrap name(tc->getCpuPtr()->name()); + SymbolTable *symtab = tc->getSystemPtr()->kernelSymtab; DPRINTFN("------ Stack ------\n"); diff --git a/src/arch/alpha/stacktrace.hh b/src/arch/alpha/stacktrace.hh index 1d8d97a79..d12aee211 100644 --- a/src/arch/alpha/stacktrace.hh +++ b/src/arch/alpha/stacktrace.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __ARCH_ALPHA_STACKTRACE_HH__ @@ -32,13 +34,13 @@ #include "base/trace.hh" #include "cpu/static_inst.hh" -class ExecContext; +class ThreadContext; class StackTrace; class ProcessInfo { private: - ExecContext *xc; + ThreadContext *tc; int thread_info_size; int task_struct_size; @@ -47,7 +49,7 @@ class ProcessInfo int name_off; public: - ProcessInfo(ExecContext *_xc); + ProcessInfo(ThreadContext *_tc); Addr task(Addr ksp) const; int pid(Addr ksp) const; @@ -59,7 +61,7 @@ class StackTrace protected: typedef TheISA::MachInst MachInst; private: - ExecContext *xc; + ThreadContext *tc; std::vector<Addr> stack; private: @@ -68,21 +70,21 @@ class StackTrace bool decodeSave(MachInst inst, int ®, int &disp); bool decodeStack(MachInst inst, int &disp); - void trace(ExecContext *xc, bool is_call); + void trace(ThreadContext *tc, bool is_call); public: StackTrace(); - StackTrace(ExecContext *xc, StaticInstPtr inst); + StackTrace(ThreadContext *tc, StaticInstPtr inst); ~StackTrace(); void clear() { - xc = 0; + tc = 0; stack.clear(); } - bool valid() const { return xc != NULL; } - bool trace(ExecContext *xc, StaticInstPtr inst); + bool valid() const { return tc != NULL; } + bool trace(ThreadContext *tc, StaticInstPtr inst); public: const std::vector<Addr> &getstack() const { return stack; } @@ -104,7 +106,7 @@ class StackTrace }; inline bool -StackTrace::trace(ExecContext *xc, StaticInstPtr inst) +StackTrace::trace(ThreadContext *tc, StaticInstPtr inst) { if (!inst->isCall() && !inst->isReturn()) return false; @@ -112,7 +114,7 @@ StackTrace::trace(ExecContext *xc, StaticInstPtr inst) if (valid()) clear(); - trace(xc, !inst->isReturn()); + trace(tc, !inst->isReturn()); return true; } diff --git a/src/arch/alpha/system.cc b/src/arch/alpha/system.cc index 4234019cd..dce7365aa 100644 --- a/src/arch/alpha/system.cc +++ b/src/arch/alpha/system.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ #include "arch/alpha/ev5.hh" @@ -230,10 +233,6 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem) Param<uint64_t> system_type; Param<uint64_t> system_rev; - Param<bool> bin; - VectorParam<std::string> binned_fns; - Param<bool> bin_int; - END_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem) BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaSystem) @@ -248,10 +247,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaSystem) INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), - INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10), - INIT_PARAM_DFLT(bin, "is this system to be binned", false), - INIT_PARAM(binned_fns, "functions to be broken down and binned"), - INIT_PARAM_DFLT(bin_int, "is interrupt code binned seperately?", true) + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10) END_INIT_SIM_OBJECT_PARAMS(AlphaSystem) @@ -269,9 +265,6 @@ CREATE_SIM_OBJECT(AlphaSystem) p->readfile = readfile; p->system_type = system_type; p->system_rev = system_rev; - p->bin = bin; - p->binned_fns = binned_fns; - p->bin_int = bin_int; return new AlphaSystem(p); } diff --git a/src/arch/alpha/system.hh b/src/arch/alpha/system.hh index 924e16826..0f4f64581 100644 --- a/src/arch/alpha/system.hh +++ b/src/arch/alpha/system.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __ARCH_ALPHA_SYSTEM_HH__ diff --git a/src/arch/alpha/tlb.cc b/src/arch/alpha/tlb.cc index 05b02d74b..c6684274b 100644 --- a/src/arch/alpha/tlb.cc +++ b/src/arch/alpha/tlb.cc @@ -24,6 +24,10 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Andrew Schultz */ #include <string> @@ -34,7 +38,7 @@ #include "base/str.hh" #include "base/trace.hh" #include "config/alpha_tlaser.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "sim/builder.hh" using namespace std; @@ -282,7 +286,7 @@ AlphaITB::regStats() Fault -AlphaITB::translate(RequestPtr &req, ExecContext *xc) const +AlphaITB::translate(RequestPtr &req, ThreadContext *tc) const { if (AlphaISA::PcPAL(req->getVaddr())) { // strip off PAL PC marker (lsb is 1) @@ -304,13 +308,13 @@ AlphaITB::translate(RequestPtr &req, ExecContext *xc) const // VA<42:41> == 2, VA<39:13> maps directly to PA<39:13> for EV5 // VA<47:41> == 0x7e, VA<40:13> maps directly to PA<40:13> for EV6 #if ALPHA_TLASER - if ((MCSR_SP(xc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && + if ((MCSR_SP(tc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && VAddrSpaceEV5(req->getVaddr()) == 2) { #else if (VAddrSpaceEV6(req->getVaddr()) == 0x7e) { #endif // only valid in kernel mode - if (ICM_CM(xc->readMiscReg(AlphaISA::IPR_ICM)) != + if (ICM_CM(tc->readMiscReg(AlphaISA::IPR_ICM)) != AlphaISA::mode_kernel) { acv++; return new ItbAcvFault(req->getVaddr()); @@ -328,7 +332,7 @@ AlphaITB::translate(RequestPtr &req, ExecContext *xc) const } else { // not a physical address: need to look up pte - int asn = DTB_ASN_ASN(xc->readMiscReg(AlphaISA::IPR_DTB_ASN)); + int asn = DTB_ASN_ASN(tc->readMiscReg(AlphaISA::IPR_DTB_ASN)); AlphaISA::PTE *pte = lookup(AlphaISA::VAddr(req->getVaddr()).vpn(), asn); @@ -343,7 +347,7 @@ AlphaITB::translate(RequestPtr &req, ExecContext *xc) const // check permissions for this access if (!(pte->xre & - (1 << ICM_CM(xc->readMiscReg(AlphaISA::IPR_ICM))))) { + (1 << ICM_CM(tc->readMiscReg(AlphaISA::IPR_ICM))))) { // instruction access fault acv++; return new ItbAcvFault(req->getVaddr()); @@ -439,12 +443,12 @@ AlphaDTB::regStats() } Fault -AlphaDTB::translate(RequestPtr &req, ExecContext *xc, bool write) const +AlphaDTB::translate(RequestPtr &req, ThreadContext *tc, bool write) const { - Addr pc = xc->readPC(); + Addr pc = tc->readPC(); AlphaISA::mode_type mode = - (AlphaISA::mode_type)DTB_CM_CM(xc->readMiscReg(AlphaISA::IPR_DTB_CM)); + (AlphaISA::mode_type)DTB_CM_CM(tc->readMiscReg(AlphaISA::IPR_DTB_CM)); /** @@ -460,7 +464,7 @@ AlphaDTB::translate(RequestPtr &req, ExecContext *xc, bool write) const if (pc & 0x1) { mode = (req->getFlags() & ALTMODE) ? (AlphaISA::mode_type)ALT_MODE_AM( - xc->readMiscReg(AlphaISA::IPR_ALT_MODE)) + tc->readMiscReg(AlphaISA::IPR_ALT_MODE)) : AlphaISA::mode_kernel; } @@ -478,14 +482,14 @@ AlphaDTB::translate(RequestPtr &req, ExecContext *xc, bool write) const // Check for "superpage" mapping #if ALPHA_TLASER - if ((MCSR_SP(xc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && + if ((MCSR_SP(tc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && VAddrSpaceEV5(req->getVaddr()) == 2) { #else if (VAddrSpaceEV6(req->getVaddr()) == 0x7e) { #endif // only valid in kernel mode - if (DTB_CM_CM(xc->readMiscReg(AlphaISA::IPR_DTB_CM)) != + if (DTB_CM_CM(tc->readMiscReg(AlphaISA::IPR_DTB_CM)) != AlphaISA::mode_kernel) { if (write) { write_acv++; } else { read_acv++; } uint64_t flags = ((write ? MM_STAT_WR_MASK : 0) | @@ -509,7 +513,7 @@ AlphaDTB::translate(RequestPtr &req, ExecContext *xc, bool write) const else read_accesses++; - int asn = DTB_ASN_ASN(xc->readMiscReg(AlphaISA::IPR_DTB_ASN)); + int asn = DTB_ASN_ASN(tc->readMiscReg(AlphaISA::IPR_DTB_ASN)); // not a physical address: need to look up pte AlphaISA::PTE *pte = lookup(AlphaISA::VAddr(req->getVaddr()).vpn(), diff --git a/src/arch/alpha/tlb.hh b/src/arch/alpha/tlb.hh index f6256020e..07d01fa5c 100644 --- a/src/arch/alpha/tlb.hh +++ b/src/arch/alpha/tlb.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ALPHA_MEMORY_HH__ @@ -38,7 +41,7 @@ #include "mem/request.hh" #include "sim/sim_object.hh" -class ExecContext; +class ThreadContext; class AlphaTLB : public SimObject { @@ -92,7 +95,7 @@ class AlphaITB : public AlphaTLB AlphaITB(const std::string &name, int size); virtual void regStats(); - Fault translate(RequestPtr &req, ExecContext *xc) const; + Fault translate(RequestPtr &req, ThreadContext *tc) const; }; class AlphaDTB : public AlphaTLB @@ -115,7 +118,7 @@ class AlphaDTB : public AlphaTLB AlphaDTB(const std::string &name, int size); virtual void regStats(); - Fault translate(RequestPtr &req, ExecContext *xc, bool write) const; + Fault translate(RequestPtr &req, ThreadContext *tc, bool write) const; }; #endif // __ALPHA_MEMORY_HH__ diff --git a/src/arch/alpha/tru64/process.cc b/src/arch/alpha/tru64/process.cc index 55f75f7d0..82e44b9e7 100644 --- a/src/arch/alpha/tru64/process.cc +++ b/src/arch/alpha/tru64/process.cc @@ -24,13 +24,16 @@ * 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. + * + * Authors: Steve Reinhardt + * Ali Saidi */ #include "arch/alpha/tru64/tru64.hh" #include "arch/alpha/isa_traits.hh" #include "arch/alpha/tru64/process.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/tru64/tru64.hh" #include "sim/process.hh" @@ -42,9 +45,9 @@ using namespace AlphaISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<AlphaTru64::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<AlphaTru64::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "OSF1"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -52,43 +55,43 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "732"); strcpy(name->machine, "alpha"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } /// Target getsysyinfo() handler. static SyscallReturn getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + unsigned nbytes = tc->getSyscallArg(2); switch (op) { case AlphaTru64::GSI_MAX_CPU: { - TypedBufferArg<uint32_t> max_cpu(xc->getSyscallArg(1)); + TypedBufferArg<uint32_t> max_cpu(tc->getSyscallArg(1)); *max_cpu = htog((uint32_t)process->numCpus()); - max_cpu.copyOut(xc->getMemPort()); + max_cpu.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_CPUS_IN_BOX: { - TypedBufferArg<uint32_t> cpus_in_box(xc->getSyscallArg(1)); + TypedBufferArg<uint32_t> cpus_in_box(tc->getSyscallArg(1)); *cpus_in_box = htog((uint32_t)process->numCpus()); - cpus_in_box.copyOut(xc->getMemPort()); + cpus_in_box.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_PHYSMEM: { - TypedBufferArg<uint64_t> physmem(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> physmem(tc->getSyscallArg(1)); *physmem = htog((uint64_t)1024 * 1024); // physical memory in KB - physmem.copyOut(xc->getMemPort()); + physmem.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_CPU_INFO: { - TypedBufferArg<AlphaTru64::cpu_info> infop(xc->getSyscallArg(1)); + TypedBufferArg<AlphaTru64::cpu_info> infop(tc->getSyscallArg(1)); infop->current_cpu = htog(0); infop->cpus_in_box = htog(process->numCpus()); @@ -100,30 +103,30 @@ getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, infop->cpu_ex_binding = htog(0); infop->mhz = htog(667); - infop.copyOut(xc->getMemPort()); + infop.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_PROC_TYPE: { - TypedBufferArg<uint64_t> proc_type(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> proc_type(tc->getSyscallArg(1)); *proc_type = htog((uint64_t)11); - proc_type.copyOut(xc->getMemPort()); + proc_type.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_PLATFORM_NAME: { - BufferArg bufArg(xc->getSyscallArg(1), nbytes); + BufferArg bufArg(tc->getSyscallArg(1), nbytes); strncpy((char *)bufArg.bufferPtr(), "COMPAQ Professional Workstation XP1000", nbytes); - bufArg.copyOut(xc->getMemPort()); + bufArg.copyOut(tc->getMemPort()); return 1; } case AlphaTru64::GSI_CLK_TCK: { - TypedBufferArg<uint64_t> clk_hz(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> clk_hz(tc->getSyscallArg(1)); *clk_hz = htog((uint64_t)1024); - clk_hz.copyOut(xc->getMemPort()); + clk_hz.copyOut(tc->getMemPort()); return 1; } @@ -138,14 +141,14 @@ getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, /// Target setsysyinfo() handler. static SyscallReturn setsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); + unsigned op = tc->getSyscallArg(0); switch (op) { case AlphaTru64::SSI_IEEE_FP_CONTROL: warn("setsysinfo: ignoring ieee_set_fp_control() arg 0x%x\n", - xc->getSyscallArg(1)); + tc->getSyscallArg(1)); break; default: @@ -160,22 +163,22 @@ setsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, /// Target table() handler. static SyscallReturn tableFunc(SyscallDesc *desc, int callnum,Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; using namespace TheISA; - int id = xc->getSyscallArg(0); // table ID - int index = xc->getSyscallArg(1); // index into table + int id = tc->getSyscallArg(0); // table ID + int index = tc->getSyscallArg(1); // index into table // arg 2 is buffer pointer; type depends on table ID - int nel = xc->getSyscallArg(3); // number of elements - int lel = xc->getSyscallArg(4); // expected element size + int nel = tc->getSyscallArg(3); // number of elements + int lel = tc->getSyscallArg(4); // expected element size switch (id) { case AlphaTru64::TBL_SYSINFO: { if (index != 0 || nel != 1 || lel != sizeof(Tru64::tbl_sysinfo)) return -EINVAL; - TypedBufferArg<Tru64::tbl_sysinfo> elp(xc->getSyscallArg(2)); + TypedBufferArg<Tru64::tbl_sysinfo> elp(tc->getSyscallArg(2)); const int clk_hz = one_million; elp->si_user = htog(curTick / (Clock::Frequency / clk_hz)); @@ -187,7 +190,7 @@ SyscallReturn tableFunc(SyscallDesc *desc, int callnum,Process *process, elp->si_phz = htog(clk_hz); elp->si_boottime = htog(seconds_since_epoch); // seconds since epoch? elp->si_max_procs = htog(process->numCpus()); - elp.copyOut(xc->getMemPort()); + elp.copyOut(tc->getMemPort()); return 0; } diff --git a/src/arch/alpha/tru64/process.hh b/src/arch/alpha/tru64/process.hh index 1cde4cac0..5d91f6ac1 100644 --- a/src/arch/alpha/tru64/process.hh +++ b/src/arch/alpha/tru64/process.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __ALPHA_TRU64_PROCESS_HH__ diff --git a/src/arch/alpha/tru64/system.cc b/src/arch/alpha/tru64/system.cc index 2ad06d679..6c0edc1ee 100644 --- a/src/arch/alpha/tru64/system.cc +++ b/src/arch/alpha/tru64/system.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu */ #include "arch/alpha/tru64/system.hh" @@ -32,7 +35,7 @@ #include "base/loader/symtab.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/tru64/tru64_events.hh" #include "kern/system_events.hh" #include "mem/physical.hh" @@ -104,9 +107,6 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Tru64AlphaSystem) Param<uint64_t> system_type; Param<uint64_t> system_rev; - Param<bool> bin; - VectorParam<string> binned_fns; - END_DECLARE_SIM_OBJECT_PARAMS(Tru64AlphaSystem) BEGIN_INIT_SIM_OBJECT_PARAMS(Tru64AlphaSystem) @@ -121,9 +121,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(Tru64AlphaSystem) INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 12), - INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 2<<1), - INIT_PARAM_DFLT(bin, "is this system to be binned", false), - INIT_PARAM(binned_fns, "functions to be broken down and binned") + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 2<<1) END_INIT_SIM_OBJECT_PARAMS(Tru64AlphaSystem) @@ -141,9 +139,6 @@ CREATE_SIM_OBJECT(Tru64AlphaSystem) p->readfile = readfile; p->system_type = system_type; p->system_rev = system_rev; - p->bin = bin; - p->binned_fns = binned_fns; - p->bin_int = false; return new Tru64AlphaSystem(p); } diff --git a/src/arch/alpha/tru64/system.hh b/src/arch/alpha/tru64/system.hh index 0e0cc1bc8..947e92f50 100644 --- a/src/arch/alpha/tru64/system.hh +++ b/src/arch/alpha/tru64/system.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu */ #ifndef __ARCH_ALPHA_TRU64_SYSTEM_HH__ @@ -33,7 +36,7 @@ #include "arch/isa_traits.hh" #include "sim/system.hh" -class ExecContext; +class ThreadContext; class BreakPCEvent; class BadAddrEvent; diff --git a/src/arch/alpha/tru64/tru64.cc b/src/arch/alpha/tru64/tru64.cc index 4a3e653c1..56b04846f 100644 --- a/src/arch/alpha/tru64/tru64.cc +++ b/src/arch/alpha/tru64/tru64.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #include "arch/alpha/tru64/tru64.hh" diff --git a/src/arch/alpha/tru64/tru64.hh b/src/arch/alpha/tru64/tru64.hh index 19343ba23..f0711b995 100644 --- a/src/arch/alpha/tru64/tru64.hh +++ b/src/arch/alpha/tru64/tru64.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #ifndef __ALPHA_ALPHA_TRU64_HH diff --git a/src/arch/alpha/types.hh b/src/arch/alpha/types.hh index d4cb482d8..5859052e9 100644 --- a/src/arch/alpha/types.hh +++ b/src/arch/alpha/types.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ARCH_ALPHA_TYPES_HH__ diff --git a/src/arch/alpha/utility.hh b/src/arch/alpha/utility.hh index 6cc916307..ec136091c 100644 --- a/src/arch/alpha/utility.hh +++ b/src/arch/alpha/utility.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ARCH_ALPHA_UTILITY_HH__ @@ -106,10 +109,10 @@ namespace AlphaISA /** * Function to insure ISA semantics about 0 registers. - * @param xc The execution context. + * @param tc The thread context. */ - template <class XC> - void zeroRegisters(XC *xc); + template <class TC> + void zeroRegisters(TC *tc); #if FULL_SYSTEM // Alpha IPR register accessors @@ -140,15 +143,15 @@ namespace AlphaISA RoundPage(Addr addr) { return (addr + PageBytes - 1) & ~(PageBytes - 1); } - void initCPU(ExecContext *xc, int cpuId); - void initIPRs(ExecContext *xc, int cpuId); + void initCPU(ThreadContext *tc, int cpuId); + void initIPRs(ThreadContext *tc, int cpuId); /** * Function to check for and process any interrupts. - * @param xc The execution context. + * @param tc The thread context. */ - template <class XC> - void processInterrupts(XC *xc); + template <class TC> + void processInterrupts(TC *tc); #endif } // namespace AlphaISA diff --git a/src/arch/alpha/vtophys.cc b/src/arch/alpha/vtophys.cc index 41e9b80a3..f7fd92c15 100644 --- a/src/arch/alpha/vtophys.cc +++ b/src/arch/alpha/vtophys.cc @@ -24,6 +24,10 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Ali Saidi */ #include <string> @@ -32,7 +36,7 @@ #include "arch/alpha/vtophys.hh" #include "base/chunk_generator.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/vport.hh" using namespace std; @@ -81,10 +85,10 @@ AlphaISA::vtophys(Addr vaddr) } Addr -AlphaISA::vtophys(ExecContext *xc, Addr addr) +AlphaISA::vtophys(ThreadContext *tc, Addr addr) { AlphaISA::VAddr vaddr = addr; - Addr ptbr = xc->readMiscReg(AlphaISA::IPR_PALtemp20); + Addr ptbr = tc->readMiscReg(AlphaISA::IPR_PALtemp20); Addr paddr = 0; //@todo Andrew couldn't remember why he commented some of this code //so I put it back in. Perhaps something to do with gdb debugging? @@ -97,7 +101,7 @@ AlphaISA::vtophys(ExecContext *xc, Addr addr) paddr = vaddr; } else { AlphaISA::PageTableEntry pte = - kernel_pte_lookup(xc->getPhysPort(), ptbr, vaddr); + kernel_pte_lookup(tc->getPhysPort(), ptbr, vaddr); if (pte.valid()) paddr = pte.paddr() | vaddr.offset(); } @@ -111,52 +115,52 @@ AlphaISA::vtophys(ExecContext *xc, Addr addr) void -AlphaISA::CopyOut(ExecContext *xc, void *dest, Addr src, size_t cplen) +AlphaISA::CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen) { uint8_t *dst = (uint8_t *)dest; - VirtualPort *vp = xc->getVirtPort(xc); + VirtualPort *vp = tc->getVirtPort(tc); vp->readBlob(src, dst, cplen); - xc->delVirtPort(vp); + tc->delVirtPort(vp); } void -AlphaISA::CopyIn(ExecContext *xc, Addr dest, void *source, size_t cplen) +AlphaISA::CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) { uint8_t *src = (uint8_t *)source; - VirtualPort *vp = xc->getVirtPort(xc); + VirtualPort *vp = tc->getVirtPort(tc); vp->writeBlob(dest, src, cplen); - xc->delVirtPort(vp); + tc->delVirtPort(vp); } void -AlphaISA::CopyStringOut(ExecContext *xc, char *dst, Addr vaddr, size_t maxlen) +AlphaISA::CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen) { int len = 0; - VirtualPort *vp = xc->getVirtPort(xc); + VirtualPort *vp = tc->getVirtPort(tc); do { vp->readBlob(vaddr++, (uint8_t*)dst++, 1); len++; } while (len < maxlen && dst[len] != 0 ); - xc->delVirtPort(vp); + tc->delVirtPort(vp); dst[len] = 0; } void -AlphaISA::CopyStringIn(ExecContext *xc, char *src, Addr vaddr) +AlphaISA::CopyStringIn(ThreadContext *tc, char *src, Addr vaddr) { - VirtualPort *vp = xc->getVirtPort(xc); + VirtualPort *vp = tc->getVirtPort(tc); for (ChunkGenerator gen(vaddr, strlen(src), AlphaISA::PageBytes); !gen.done(); gen.next()) { vp->writeBlob(gen.addr(), (uint8_t*)src, gen.size()); src += gen.size(); } - xc->delVirtPort(vp); + tc->delVirtPort(vp); } diff --git a/src/arch/alpha/vtophys.hh b/src/arch/alpha/vtophys.hh index 7ab14bc5b..472c694ff 100644 --- a/src/arch/alpha/vtophys.hh +++ b/src/arch/alpha/vtophys.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ARCH_ALPHA_VTOPHYS_H__ @@ -31,7 +34,7 @@ #include "arch/alpha/isa_traits.hh" -class ExecContext; +class ThreadContext; class FunctionalPort; namespace AlphaISA { @@ -40,12 +43,12 @@ PageTableEntry kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, AlphaISA::VAddr vaddr); Addr vtophys(Addr vaddr); -Addr vtophys(ExecContext *xc, Addr vaddr); +Addr vtophys(ThreadContext *tc, Addr vaddr); -void CopyOut(ExecContext *xc, void *dst, Addr src, size_t len); -void CopyIn(ExecContext *xc, Addr dst, void *src, size_t len); -void CopyStringOut(ExecContext *xc, char *dst, Addr vaddr, size_t maxlen); -void CopyStringIn(ExecContext *xc, char *src, Addr vaddr); +void CopyOut(ThreadContext *tc, void *dst, Addr src, size_t len); +void CopyIn(ThreadContext *tc, Addr dst, void *src, size_t len); +void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen); +void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr); }; #endif // __ARCH_ALPHA_VTOPHYS_H__ diff --git a/src/arch/isa_parser.py b/src/arch/isa_parser.py index c0758da50..4d522e18a 100755 --- a/src/arch/isa_parser.py +++ b/src/arch/isa_parser.py @@ -23,6 +23,9 @@ # 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. +# +# Authors: Steve Reinhardt +# Korey Sewell import os import sys @@ -1179,6 +1182,11 @@ class IntRegOperand(Operand): if (self.size == self.dflt_size): return '%s = xc->readIntReg(this, %d);\n' % \ (self.base_name, self.src_reg_idx) + elif (self.size > self.dflt_size): + int_reg_val = 'xc->readIntReg(this, %d)' % (self.src_reg_idx) + if (self.is_signed): + int_reg_val = 'sext<%d>(%s)' % (self.dflt_size, int_reg_val) + return '%s = %s;\n' % (self.base_name, int_reg_val) else: return '%s = bits(xc->readIntReg(this, %d), %d, 0);\n' % \ (self.base_name, self.src_reg_idx, self.size-1) diff --git a/src/arch/isa_specific.hh b/src/arch/isa_specific.hh index 91c9ffb68..181e81302 100644 --- a/src/arch/isa_specific.hh +++ b/src/arch/isa_specific.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Gabe Black */ #ifndef __ARCH_ISA_SPECIFIC_HH__ diff --git a/src/arch/mips/SConscript b/src/arch/mips/SConscript index ef1ef25d6..8353bcde7 100644 --- a/src/arch/mips/SConscript +++ b/src/arch/mips/SConscript @@ -25,6 +25,10 @@ # 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. +# +# Authors: Gabe Black +# Steve Reinhardt +# Korey Sewell import os import sys @@ -43,16 +47,12 @@ Import('env') base_sources = Split(''' faults.cc isa_traits.cc + utility.cc ''') # Full-system sources full_system_sources = Split(''' - memory.cc - arguments.cc - mips34k.cc - osfpal.cc - stacktrace.cc - vtophys.cc + #Insert Full-System Files Here ''') # Syscall emulation (non-full-system) sources diff --git a/src/arch/mips/faults.cc b/src/arch/mips/faults.cc index 1b31dfa69..cfeb045eb 100644 --- a/src/arch/mips/faults.cc +++ b/src/arch/mips/faults.cc @@ -24,12 +24,18 @@ * 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. + * + * Authors: Korey Sewell */ #include "arch/mips/faults.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "base/trace.hh" +#if !FULL_SYSTEM +#include "sim/process.hh" +#include "mem/page_table.hh" +#endif namespace MipsISA { @@ -50,6 +56,12 @@ FaultName ArithmeticFault::_name = "arith"; FaultVect ArithmeticFault::_vect = 0x0501; FaultStat ArithmeticFault::_count; +#if !FULL_SYSTEM +FaultName PageTableFault::_name = "page_table_fault"; +FaultVect PageTableFault::_vect = 0x0000; +FaultStat PageTableFault::_count; +#endif + FaultName InterruptFault::_name = "interrupt"; FaultVect InterruptFault::_vect = 0x0101; FaultStat InterruptFault::_count; @@ -100,32 +112,53 @@ FaultStat IntegerOverflowFault::_count; #if FULL_SYSTEM -void MipsFault::invoke(ExecContext * xc) +void MipsFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); countStat()++; // exception restart address - if (setRestartAddress() || !xc->inPalMode()) - xc->setMiscReg(MipsISA::IPR_EXC_ADDR, xc->readPC()); + if (setRestartAddress() || !tc->inPalMode()) + tc->setMiscReg(MipsISA::IPR_EXC_ADDR, tc->readPC()); if (skipFaultingInstruction()) { // traps... skip faulting instruction. - xc->setMiscReg(MipsISA::IPR_EXC_ADDR, - xc->readMiscReg(MipsISA::IPR_EXC_ADDR) + 4); + tc->setMiscReg(MipsISA::IPR_EXC_ADDR, + tc->readMiscReg(MipsISA::IPR_EXC_ADDR) + 4); } - xc->setPC(xc->readMiscReg(MipsISA::IPR_PAL_BASE) + vect()); - xc->setNextPC(xc->readPC() + sizeof(MachInst)); + tc->setPC(tc->readMiscReg(MipsISA::IPR_PAL_BASE) + vect()); + tc->setNextPC(tc->readPC() + sizeof(MachInst)); } -void ArithmeticFault::invoke(ExecContext * xc) +void ArithmeticFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); panic("Arithmetic traps are unimplemented!"); } -#endif +#else //!FULL_SYSTEM +void PageTableFault::invoke(ThreadContext *tc) +{ + Process *p = tc->getProcessPtr(); + + // address is higher than the stack region or in the current stack region + if (vaddr > p->stack_base || vaddr > p->stack_min) + FaultBase::invoke(tc); + + // We've accessed the next page + if (vaddr > p->stack_min - PageBytes) { + p->stack_min -= PageBytes; + if (p->stack_base - p->stack_min > 8*1024*1024) + fatal("Over max stack size for one thread\n"); + p->pTable->allocate(p->stack_min, PageBytes); + warn("Increasing stack size by one page."); + } else { + FaultBase::invoke(tc); + } +} + +#endif } // namespace MipsISA diff --git a/src/arch/mips/faults.hh b/src/arch/mips/faults.hh index 0bdabe29e..95c61cfbc 100644 --- a/src/arch/mips/faults.hh +++ b/src/arch/mips/faults.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #ifndef __MIPS_FAULTS_HH__ @@ -45,7 +47,7 @@ class MipsFault : public FaultBase virtual bool setRestartAddress() {return true;} public: #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif virtual FaultVect vect() = 0; virtual FaultStat & countStat() = 0; @@ -77,6 +79,30 @@ class AlignmentFault : public MipsFault bool isAlignmentFault() {return true;} }; +#if !FULL_SYSTEM +class PageTableFault : public MipsFault +{ + private: + Addr vaddr; + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + PageTableFault(Addr va) + : vaddr(va) {} + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} + void invoke(ThreadContext * tc); +}; + +static inline Fault genPageTableFault(Addr va) +{ + return new PageTableFault(va); +} +#endif + + static inline Fault genMachineCheckFault() { return new MachineCheckFault; @@ -112,7 +138,7 @@ class ArithmeticFault : public MipsFault FaultVect vect() {return _vect;} FaultStat & countStat() {return _count;} #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; diff --git a/src/arch/mips/isa/base.isa b/src/arch/mips/isa/base.isa index b2a31c018..f07b06e03 100644 --- a/src/arch/mips/isa/base.isa +++ b/src/arch/mips/isa/base.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Base class for MIPS instructions, and some support functions @@ -10,7 +38,6 @@ output header {{ using namespace MipsISA; - /** * Base class for all MIPS static instructions. */ @@ -18,16 +45,6 @@ output header {{ { protected: - /// Make MipsISA register dependence tags directly visible in - /// this class and derived classes. Maybe these should really - /// live here and not in the MipsISA namespace. - /*enum DependenceTags { - FP_Base_DepTag = MipsISA::FP_Base_DepTag, - Fpcr_DepTag = MipsISA::Fpcr_DepTag, - Uniq_DepTag = MipsISA::Uniq_DepTag, - IPR_Base_DepTag = MipsISA::IPR_Base_DepTag - };*/ - // Constructor MipsStaticInst(const char *mnem, MachInst _machInst, OpClass __opClass) : StaticInst(mnem, _machInst, __opClass) @@ -67,12 +84,12 @@ output decoder {{ } if(_numSrcRegs > 0) { - ss << ","; + ss << ", "; printReg(ss, _srcRegIdx[0]); } if(_numSrcRegs > 1) { - ss << ","; + ss << ", "; printReg(ss, _srcRegIdx[1]); } diff --git a/src/arch/mips/isa/bitfields.isa b/src/arch/mips/isa/bitfields.isa index e1124a591..35815bf1f 100644 --- a/src/arch/mips/isa/bitfields.isa +++ b/src/arch/mips/isa/bitfields.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Bitfield definitions. @@ -17,20 +45,19 @@ def bitfield FUNCTION < 5: 0>; def bitfield FUNCTION_HI < 5: 3>; def bitfield FUNCTION_LO < 2: 0>; -// Integer operate format -def bitfield RT <20:16>; -def bitfield RT_HI <20:19>; -def bitfield RT_LO <18:16>; - def bitfield RS <25:21>; def bitfield RS_MSB <25:25>; def bitfield RS_HI <25:24>; def bitfield RS_LO <23:21>; -def bitfield RS_SRL <25:22>; - +def bitfield RS_SRL <25:22>; +def bitfield RS_RT <25:16>; +def bitfield RT <20:16>; +def bitfield RT_HI <20:19>; +def bitfield RT_LO <18:16>; +def bitfield RT_RD <20:11>; def bitfield RD <15:11>; -def bitfield INTIMM <15: 0>; // integer immediate (literal) +def bitfield INTIMM <15: 0>; // Floating-point operate format def bitfield FMT <25:21>; @@ -67,5 +94,9 @@ def bitfield HINT <10: 6>; def bitfield SYSCALLCODE <25: 6>; def bitfield TRAPCODE <15:13>; +// EXT/INS instructions +def bitfield MSB <15:11>; +def bitfield LSB <10: 6>; + // M5 instructions def bitfield M5FUNC <7:0>; diff --git a/src/arch/mips/isa/decoder.isa b/src/arch/mips/isa/decoder.isa index 1454aba39..9ac982e34 100644 --- a/src/arch/mips/isa/decoder.isa +++ b/src/arch/mips/isa/decoder.isa @@ -1,4 +1,32 @@ - // -*- mode:c++ -*- +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell //////////////////////////////////////////////////////////////////// // @@ -6,41 +34,33 @@ // ----------------------------- // The following instructions are specified in the MIPS32 ISA // Specification. Decoding closely follows the style specified -// in the MIPS32 ISAthe specification document starting with Table +// in the MIPS32 ISA specification document starting with Table // A-2 (document available @ www.mips.com) // -//@todo: Distinguish "unknown/future" use insts from "reserved" -// ones decode OPCODE_HI default Unknown::unknown() { - - // Derived From ... Table A-2 MIPS32 ISA Manual + //Table A-2 0x0: decode OPCODE_LO { - 0x0: decode FUNCTION_HI { 0x0: decode FUNCTION_LO { 0x1: decode MOVCI { format BasicOp { - 0: movf({{ if (getFPConditionCode(FCSR, CC) == 0) Rd = Rs}}); - 1: movt({{ if (getFPConditionCode(FCSR, CC) == 1) Rd = Rs}}); + 0: movf({{ Rd = (getCondCode(FCSR, CC) == 0) ? Rd : Rs; }}); + 1: movt({{ Rd = (getCondCode(FCSR, CC) == 1) ? Rd : Rs; }}); } } format BasicOp { - - //Table A-3 Note: "1. Specific encodings of the rt, rd, and sa fields - //are used to distinguish among the SLL, NOP, SSNOP and EHB functions. + //Table A-3 Note: "Specific encodings of the rd, rs, and + //rt fields are used to distinguish SLL, SSNOP, and EHB + //functions 0x0: decode RS { - 0x0: decode RT { //fix Nop traditional vs. Nop converted disassembly later - 0x0: decode RD default Nop::nop(){ - 0x0: decode SA { - 0x1: ssnop({{ ; }}); //really sll r0,r0,1 - 0x3: ehb({{ ; }}); //really sll r0,r0,3 - } - } - - default: sll({{ Rd = Rt.uw << SA; }}); + 0x0: decode RT_RD { + 0x0: decode SA default Nop::nop(){ + 0x1: WarnUnimpl::ssnop(); + 0x3: WarnUnimpl::ehb(); + } + default: sll({{ Rd = Rt.uw << SA; }}); } - } 0x2: decode RS_SRL { @@ -55,7 +75,6 @@ decode OPCODE_HI default Unknown::unknown() { 0x3: decode RS { 0x0: sra({{ uint32_t temp = Rt >> SA; - if ( (Rt & 0x80000000) > 0 ) { uint32_t mask = 0x80000000; for(int i=0; i < SA; i++) { @@ -63,7 +82,6 @@ decode OPCODE_HI default Unknown::unknown() { mask = mask >> 1; } } - Rd = temp; }}); } @@ -96,37 +114,36 @@ decode OPCODE_HI default Unknown::unknown() { } 0x1: decode FUNCTION_LO { - - //Table A-3 Note: "Specific encodings of the hint field are used - //to distinguish JR from JR.HB and JALR from JALR.HB" + //Table A-3 Note: "Specific encodings of the hint field are + //used to distinguish JR from JR.HB and JALR from JALR.HB" format Jump { 0x0: decode HINT { - 0:jr({{ NNPC = Rs & ~1; }},IsReturn); - - 1:jr_hb({{ NNPC = Rs & ~1; clear_exe_inst_hazards(); }},IsReturn); + 0x1: jr_hb({{ NNPC = Rs & ~1; }}, IsReturn, ClearHazards); + default: jr({{ NNPC = Rs & ~1; }}, IsReturn); } 0x1: decode HINT { - 0: jalr({{ Rd = NNPC; NNPC = Rs; }},IsCall,IsReturn); - - 1: jalr_hb({{ Rd = NNPC; NNPC = Rs; clear_exe_inst_hazards();}},IsCall,IsReturn); + 0x1: jalr_hb({{ Rd = NNPC; NNPC = Rs; }}, IsCall, Link + , ClearHazards); + default: jalr({{ Rd = NNPC; NNPC = Rs; }}, IsCall, + Link); } } format BasicOp { - 0x2: movz({{ if (Rt == 0) Rd = Rs; }}); - 0x3: movn({{ if (Rt != 0) Rd = Rs; }}); + 0x2: movz({{ Rd = (Rt == 0) ? Rs : Rd; }}); + 0x3: movn({{ Rd = (Rt != 0) ? Rs : Rd; }}); + 0x4: syscall({{ xc->syscall(R2); }}, IsNonSpeculative); + 0x7: sync({{ ; }}, IsMemBarrier); } - format BasicOp { - 0x4: syscall({{ xc->syscall(R2); }},IsNonSpeculative); - 0x5: break({{ panic("Not implemented break yet"); }},IsNonSpeculative); - 0x7: sync({{ panic("Not implemented sync yet"); }},IsNonSpeculative); + format FailUnimpl { + 0x5: break(); } } 0x2: decode FUNCTION_LO { - format BasicOp { + format HiLoMiscOp { 0x0: mfhi({{ Rd = HI; }}); 0x1: mthi({{ HI = Rs; }}); 0x2: mflo({{ Rd = LO; }}); @@ -135,28 +152,22 @@ decode OPCODE_HI default Unknown::unknown() { } 0x3: decode FUNCTION_LO { - format IntOp { - 0x0: mult({{ - int64_t temp1 = Rs.sd * Rt.sd; - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); - - 0x1: multu({{ - uint64_t temp1 = Rs.ud * Rt.ud; - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); - - 0x2: div({{ - HI = Rs.sd % Rt.sd; - LO = Rs.sd / Rt.sd; - }}); + format HiLoOp { + 0x0: mult({{ val = Rs.sd * Rt.sd; }}); + 0x1: multu({{ val = Rs.ud * Rt.ud; }}); + } - 0x3: divu({{ - HI = Rs.ud % Rt.ud; - LO = Rs.ud / Rt.ud; - }}); + format HiLoMiscOp { + 0x2: div({{ if (Rt.sd != 0) { + HI = Rs.sd % Rt.sd; + LO = Rs.sd / Rt.sd; + } + }}); + 0x3: divu({{ if (Rt.ud != 0) { + HI = Rs.ud % Rt.ud; + LO = Rs.ud / Rt.ud; + } + }}); } } @@ -201,11 +212,8 @@ decode OPCODE_HI default Unknown::unknown() { format Branch { 0x0: bltz({{ cond = (Rs.sw < 0); }}); 0x1: bgez({{ cond = (Rs.sw >= 0); }}); - } - - format BranchLikely { - 0x2: bltzl({{ cond = (Rs.sw < 0); }}); - 0x3: bgezl({{ cond = (Rs.sw >= 0); }}); + 0x2: bltzl({{ cond = (Rs.sw < 0); }}, Likely); + 0x3: bgezl({{ cond = (Rs.sw >= 0); }}, Likely); } } @@ -222,13 +230,13 @@ decode OPCODE_HI default Unknown::unknown() { 0x2: decode REGIMM_LO { format Branch { - 0x0: bltzal({{ cond = (Rs.sw < 0); }}, IsCall,IsReturn); - 0x1: bgezal({{ cond = (Rs.sw >= 0); }}, IsCall,IsReturn); - } - - format BranchLikely { - 0x2: bltzall({{ cond = (Rs.sw < 0); }}, IsCall, IsReturn); - 0x3: bgezall({{ cond = (Rs.sw >= 0); }}, IsCall, IsReturn); + 0x0: bltzal({{ cond = (Rs.sw < 0); }}, Link); + 0x1: decode RS { + 0x0: bal ({{ cond = 1; }}, IsCall, Link); + default: bgezal({{ cond = (Rs.sw >= 0); }}, Link); + } + 0x2: bltzall({{ cond = (Rs.sw < 0); }}, Link, Likely); + 0x3: bgezall({{ cond = (Rs.sw >= 0); }}, Link, Likely); } } @@ -241,25 +249,23 @@ decode OPCODE_HI default Unknown::unknown() { format Jump { 0x2: j({{ NNPC = (NPC & 0xF0000000) | (JMPTARG << 2);}}); - - 0x3: jal({{ NNPC = (NPC & 0xF0000000) | (JMPTARG << 2); }},IsCall,IsReturn); + 0x3: jal({{ NNPC = (NPC & 0xF0000000) | (JMPTARG << 2); }}, IsCall, + Link); } format Branch { - 0x4: beq({{ cond = (Rs.sw == Rt.sw); }}); - 0x5: bne({{ cond = (Rs.sw != Rt.sw); }}); - 0x6: decode RT { - 0x0: blez({{ cond = (Rs.sw <= 0); }}); - } - - 0x7: decode RT { - 0x0: bgtz({{ cond = (Rs.sw > 0); }}); + 0x4: decode RS_RT { + 0x0: b({{ cond = 1; }}); + default: beq({{ cond = (Rs.sw == Rt.sw); }}); } + 0x5: bne({{ cond = (Rs.sw != Rt.sw); }}); + 0x6: blez({{ cond = (Rs.sw <= 0); }}); + 0x7: bgtz({{ cond = (Rs.sw > 0); }}); } } 0x1: decode OPCODE_LO { - format IntOp { + format IntImmOp { 0x0: addi({{ Rt.sw = Rs.sw + imm; /*Trap If Overflow*/}}); 0x1: addiu({{ Rt.sw = Rs.sw + imm;}}); 0x2: slti({{ Rt.sw = ( Rs.sw < imm) ? 1 : 0 }}); @@ -275,112 +281,47 @@ decode OPCODE_HI default Unknown::unknown() { } 0x2: decode OPCODE_LO { - //Table A-11 MIPS32 COP0 Encoding of rs Field 0x0: decode RS_MSB { 0x0: decode RS { - format System { - 0x0: mfc0({{ - //uint64_t reg_num = Rd.uw; - - Rt = xc->readMiscReg(RD << 5 | SEL); - }}); - - 0x4: mtc0({{ - //uint64_t reg_num = Rd.uw; - - xc->setMiscReg(RD << 5 | SEL,Rt); - }}); - - 0x8: mftr({{ - //The contents of the coprocessor 0 register specified by the - //combination of rd and sel are loaded into general register - //rt. Note that not all coprocessor 0 registers support the - //sel field. In those instances, the sel field must be zero. - - //MT Code Needed Here - - }}); - - 0xC: mttr({{ - //The contents of the coprocessor 0 register specified by the - //combination of rd and sel are loaded into general register - //rt. Note that not all coprocessor 0 registers support the - //sel field. In those instances, the sel field must be zero. - - //MT Code Needed Here - }}); - - - 0xA: rdpgpr({{ - //Accessing Previous Shadow Set Register Number - //uint64_t prev = xc->readMiscReg(SRSCtl)/*[PSS]*/; - //uint64_t reg_num = Rt.uw; - - //Rd = xc->regs.IntRegFile[prev]; - //Rd = xc->shadowIntRegFile[prev][reg_num]; - }}); + format CP0Control { + 0x0: mfc0({{ Rt = xc->readMiscReg(RD << 5 | SEL); }}); + 0x4: mtc0({{ xc->setMiscReg(RD << 5 | SEL, Rt); }}); + } + format MipsMT { + 0x8: mftr(); + 0xC: mttr(); 0xB: decode RD { - 0x0: decode SC { - 0x0: dvpe({{ - Rt.sw = xc->readMiscReg(MVPControl); - xc->setMiscReg(MVPControl,0); - }}); - - 0x1: evpe({{ - Rt.sw = xc->readMiscReg(MVPControl); - xc->setMiscReg(MVPControl,1); - }}); + 0x0: dvpe(); + 0x1: evpe(); } - 0x1: decode SC { - 0x0: dmt({{ - Rt.sw = xc->readMiscReg(VPEControl); - xc->setMiscReg(VPEControl,0); - }}); - - 0x1: emt({{ - Rt.sw = xc->readMiscReg(VPEControl); - xc->setMiscReg(VPEControl,1); - }}); - } - - 0xC: decode SC { - 0x0: di({{ - Rt.sw = xc->readMiscReg(Status); - xc->setMiscReg(Status,0); - }}); - - 0x1: ei({{ - Rt.sw = xc->readMiscReg(Status); - xc->setMiscReg(Status,1); - }}); + 0x0: dmt(); + 0x1: emt(); + 0xC: decode SC { + 0x0: di(); + 0x1: ei(); + } } } + } - 0xE: wrpgpr({{ - //Accessing Previous Shadow Set Register Number - //uint64_t prev = xc->readMiscReg(SRSCtl/*[PSS]*/); - //uint64_t reg_num = Rd.uw; - - //xc->regs.IntRegFile[prev]; - //xc->shadowIntRegFile[prev][reg_num] = Rt; - }}); + format FailUnimpl { + 0xA: rdpgpr(); + 0xE: wrpgpr(); } } //Table A-12 MIPS32 COP0 Encoding of Function Field When rs=CO 0x1: decode FUNCTION { - format System { - 0x01: tlbr({{ }}); - 0x02: tlbwi({{ }}); - 0x06: tlbwr({{ }}); - 0x08: tlbp({{ }}); - } + format FailUnimpl { + 0x01: tlbr(); + 0x02: tlbwi(); + 0x06: tlbwr(); + 0x08: tlbp(); - format WarnUnimpl { 0x18: eret(); 0x1F: deret(); 0x20: wait(); @@ -393,18 +334,9 @@ decode OPCODE_HI default Unknown::unknown() { 0x0: decode RS_HI { 0x0: decode RS_LO { - format FloatOp { - 0x0: mfc1 ({{ Rt.uw = Fs.uw<31:0>; }}); - 0x3: mfhc1({{ Rt.uw = Fs.ud<63:32>;}}); - 0x4: mtc1 ({{ Fs.uw = Rt.uw; }}); - 0x7: mthc1({{ - uint64_t fs_hi = Rt.uw; - uint64_t fs_lo = Fs.ud & 0x0000FFFF; - Fs.ud = fs_hi << 32 | fs_lo; - }}); - } + format CP1Control { + 0x0: mfc1 ({{ Rt.uw = Fs.uw; }}); - format System { 0x2: cfc1({{ switch (FS) { @@ -424,11 +356,14 @@ decode OPCODE_HI default Unknown::unknown() { Rt = FCSR; break; default: - panic("FP Control Value (%d) Not Available. Ignoring Access to" - "Floating Control Status Register",FS); + panic("FP Control Value (%d) Not Valid"); } }}); + 0x3: mfhc1({{ Rt.uw = Fs.ud<63:32>;}}); + + 0x4: mtc1 ({{ Fs.uw = Rt.uw; }}); + 0x6: ctc1({{ switch (FS) { @@ -464,21 +399,29 @@ decode OPCODE_HI default Unknown::unknown() { "Floating Control Status Register", FS); } }}); + + 0x7: mthc1({{ + uint64_t fs_hi = Rt.uw; + uint64_t fs_lo = Fs.ud & 0x0FFFFFFFF; + Fs.ud = (fs_hi << 32) | fs_lo; + }}); + } } 0x1: decode ND { - 0x0: decode TF { - format Branch { - 0x0: bc1f({{ cond = (getFPConditionCode(FCSR,CC) == 0); }}); - 0x1: bc1t({{ cond = (getFPConditionCode(FCSR,CC) == 1); }}); + format Branch { + 0x0: decode TF { + 0x0: bc1f({{ cond = getCondCode(FCSR, BRANCH_CC) == 0; + }}); + 0x1: bc1t({{ cond = getCondCode(FCSR, BRANCH_CC) == 1; + }}); } - } - - 0x1: decode TF { - format BranchLikely { - 0x0: bc1fl({{ cond = (getFPConditionCode(FCSR,CC) == 0); }}); - 0x1: bc1tl({{ cond = (getFPConditionCode(FCSR,CC) == 1); }}); + 0x1: decode TF { + 0x0: bc1fl({{ cond = getCondCode(FCSR, BRANCH_CC) == 0; + }}, Likely); + 0x1: bc1tl({{ cond = getCondCode(FCSR, BRANCH_CC) == 1; + }}, Likely); } } } @@ -486,9 +429,8 @@ decode OPCODE_HI default Unknown::unknown() { 0x1: decode RS_HI { 0x2: decode RS_LO { - //Table A-14 MIPS32 COP1 Encoding of Function Field When rs=S - //(( single-word )) + //(( single-precision floating point)) 0x0: decode FUNCTION_HI { 0x0: decode FUNCTION_LO { format FloatOp { @@ -498,195 +440,104 @@ decode OPCODE_HI default Unknown::unknown() { 0x3: div_s({{ Fd.sf = Fs.sf / Ft.sf;}}); 0x4: sqrt_s({{ Fd.sf = sqrt(Fs.sf);}}); 0x5: abs_s({{ Fd.sf = fabs(Fs.sf);}}); - 0x6: mov_s({{ Fd.sf = Fs.sf;}}); - 0x7: neg_s({{ Fd.sf = -1 * Fs.sf;}}); + 0x7: neg_s({{ Fd.sf = -Fs.sf;}}); } + + 0x6: BasicOp::mov_s({{ Fd.sf = Fs.sf;}}); } 0x1: decode FUNCTION_LO { - format Float64Op { - 0x0: round_l_s({{ - Fd.ud = fpConvert(roundFP(Fs.sf,0), SINGLE_TO_LONG); - }}); - - 0x1: trunc_l_s({{ - Fd.ud = fpConvert(truncFP(Fs.sf), SINGLE_TO_LONG); - }}); - - 0x2: ceil_l_s({{ - Fd.ud = fpConvert(ceil(Fs.sf), SINGLE_TO_LONG); - }}); - - 0x3: floor_l_s({{ - Fd.ud = fpConvert(floor(Fs.sf), SINGLE_TO_LONG); - }}); - } - - format FloatOp { - 0x4: round_w_s({{ - Fd.uw = fpConvert(roundFP(Fs.sf,0), SINGLE_TO_WORD); - }}); - - 0x5: trunc_w_s({{ - Fd.uw = fpConvert(truncFP(Fs.sf), SINGLE_TO_WORD); - }}); - - 0x6: ceil_w_s({{ - Fd.uw = fpConvert(ceil(Fs.sf), SINGLE_TO_WORD); - }}); - - 0x7: floor_w_s({{ - Fd.uw = fpConvert(floor(Fs.sf), SINGLE_TO_WORD); - }}); + format FloatConvertOp { + 0x0: round_l_s({{ val = Fs.sf; }}, ToLong, + Round); + 0x1: trunc_l_s({{ val = Fs.sf; }}, ToLong, + Trunc); + 0x2: ceil_l_s({{ val = Fs.sf; }}, ToLong, + Ceil); + 0x3: floor_l_s({{ val = Fs.sf; }}, ToLong, + Floor); + 0x4: round_w_s({{ val = Fs.sf; }}, ToWord, + Round); + 0x5: trunc_w_s({{ val = Fs.sf; }}, ToWord, + Trunc); + 0x6: ceil_w_s({{ val = Fs.sf; }}, ToWord, + Ceil); + 0x7: floor_w_s({{ val = Fs.sf; }}, ToWord, + Floor); } } 0x2: decode FUNCTION_LO { 0x1: decode MOVCF { - format FloatOp { - 0x0: movf_s({{if (getFPConditionCode(FCSR,CC) == 0) Fd = Fs;}}); - 0x1: movt_s({{if (getFPConditionCode(FCSR,CC) == 1) Fd = Fs;}}); + format BasicOp { + 0x0: movf_s({{ Fd = (getCondCode(FCSR,CC) == 0) ? Fs : Fd; }}); + 0x1: movt_s({{ Fd = (getCondCode(FCSR,CC) == 1) ? Fs : Fd; }}); } } + format BasicOp { + 0x2: movz_s({{ Fd = (Rt == 0) ? Fs : Fd; }}); + 0x3: movn_s({{ Fd = (Rt != 0) ? Fs : Fd; }}); + } + format FloatOp { - 0x2: movz_s({{ if (Rt == 0) Fd = Fs; }}); - 0x3: movn_s({{ if (Rt != 0) Fd = Fs; }}); 0x5: recip_s({{ Fd = 1 / Fs; }}); 0x6: rsqrt_s({{ Fd = 1 / sqrt(Fs);}}); } } 0x4: decode FUNCTION_LO { - format FloatConvertOp { - 0x1: cvt_d_s({{ - Fd.ud = fpConvert(Fs.sf, SINGLE_TO_DOUBLE); - }}); - - 0x4: cvt_w_s({{ - Fd.uw = fpConvert(Fs.sf, SINGLE_TO_WORD); - }}); + 0x1: cvt_d_s({{ val = Fs.sf; }}, ToDouble); + 0x4: cvt_w_s({{ val = Fs.sf; }}, ToWord); + 0x5: cvt_l_s({{ val = Fs.sf; }}, ToLong); } - format FloatConvertOp { - 0x5: cvt_l_s({{ - Fd.ud = fpConvert(Fs.sf, SINGLE_TO_LONG); - }}); - - 0x6: cvt_ps_st({{ - Fd.ud = (uint64_t)Fs.uw << 32 | (uint64_t)Ft.uw; + 0x6: FloatOp::cvt_ps_s({{ + Fd.ud = (uint64_t) Fs.uw << 32 | + (uint64_t) Ft.uw; }}); - } } 0x6: decode FUNCTION_LO { format FloatCompareOp { - 0x0: c_f_s({{ cond = 0; }}); - - 0x1: c_un_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = 0; - }}); - - 0x2: c_eq_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf == Ft.sf); - }}); - - 0x3: c_ueq_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf == Ft.sf); - }}); - - 0x4: c_olt_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf < Ft.sf); - }}); - - 0x5: c_ult_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf < Ft.sf); - }}); - - 0x6: c_ole_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf <= Ft.sf); - }}); - - 0x7: c_ule_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf <= Ft.sf); - }}); + 0x0: c_f_s({{ cond = 0; }}, SinglePrecision, + UnorderedFalse); + 0x1: c_un_s({{ cond = 0; }}, SinglePrecision, + UnorderedTrue); + 0x2: c_eq_s({{ cond = (Fs.sf == Ft.sf); }}, + UnorderedFalse); + 0x3: c_ueq_s({{ cond = (Fs.sf == Ft.sf); }}, + UnorderedTrue); + 0x4: c_olt_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedFalse); + 0x5: c_ult_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedTrue); + 0x6: c_ole_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedFalse); + 0x7: c_ule_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedTrue); } } 0x7: decode FUNCTION_LO { - format FloatCompareWithXcptOp { - 0x0: c_sf_s({{ cond = 0; }}); - - 0x1: c_ngle_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = 0; - }}); - - 0x2: c_seq_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf == Ft.sf); - }}); - - 0x3: c_ngl_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf == Ft.sf); - }}); - - 0x4: c_lt_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf < Ft.sf); - }}); - - 0x5: c_nge_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf < Ft.sf); - }}); - - 0x6: c_le_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 0; - else - cond = (Fs.sf <= Ft.sf); - }}); - - 0x7: c_ngt_s({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond = 1; - else - cond = (Fs.sf <= Ft.sf); - }}); + format FloatCompareOp { + 0x0: c_sf_s({{ cond = 0; }}, SinglePrecision, + UnorderedFalse, QnanException); + 0x1: c_ngle_s({{ cond = 0; }}, SinglePrecision, + UnorderedTrue, QnanException); + 0x2: c_seq_s({{ cond = (Fs.sf == Ft.sf);}}, + UnorderedFalse, QnanException); + 0x3: c_ngl_s({{ cond = (Fs.sf == Ft.sf); }}, + UnorderedTrue, QnanException); + 0x4: c_lt_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedFalse, QnanException); + 0x5: c_nge_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedTrue, QnanException); + 0x6: c_le_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedFalse, QnanException); + 0x7: c_ngt_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedTrue, QnanException); } } } @@ -695,197 +546,109 @@ decode OPCODE_HI default Unknown::unknown() { 0x1: decode FUNCTION_HI { 0x0: decode FUNCTION_LO { format FloatOp { - 0x0: add_d({{ Fd.df = Fs.df + Ft.df;}}); - 0x1: sub_d({{ Fd.df = Fs.df - Ft.df;}}); - 0x2: mul_d({{ Fd.df = Fs.df * Ft.df;}}); - 0x3: div_d({{ Fd.df = Fs.df / Ft.df;}}); - 0x4: sqrt_d({{ Fd.df = sqrt(Fs.df);}}); - 0x5: abs_d({{ Fd.df = fabs(Fs.df);}}); - 0x6: mov_d({{ Fd.ud = Fs.ud;}}); - 0x7: neg_d({{ Fd.df = -1 * Fs.df;}}); + 0x0: add_d({{ Fd.df = Fs.df + Ft.df; }}); + 0x1: sub_d({{ Fd.df = Fs.df - Ft.df; }}); + 0x2: mul_d({{ Fd.df = Fs.df * Ft.df; }}); + 0x3: div_d({{ Fd.df = Fs.df / Ft.df; }}); + 0x4: sqrt_d({{ Fd.df = sqrt(Fs.df); }}); + 0x5: abs_d({{ Fd.df = fabs(Fs.df); }}); + 0x7: neg_d({{ Fd.df = -1 * Fs.df; }}); } + + 0x6: BasicOp::mov_d({{ Fd.df = Fs.df; }}); } 0x1: decode FUNCTION_LO { - format FloatOp { - 0x0: round_l_d({{ - Fd.ud = fpConvert(roundFP(Fs.df,0), DOUBLE_TO_LONG); - }}); - - 0x1: trunc_l_d({{ - Fd.ud = fpConvert(truncFP(Fs.df), DOUBLE_TO_LONG); - }}); - - 0x2: ceil_l_d({{ - Fd.ud = fpConvert(ceil(Fs.df), DOUBLE_TO_LONG); - }}); - - 0x3: floor_l_d({{ - Fd.ud = fpConvert(floor(Fs.df), DOUBLE_TO_LONG); - }}); - } - - format FloatOp { - 0x4: round_w_d({{ - Fd.uw = fpConvert(roundFP(Fs.df,0), DOUBLE_TO_WORD); - }}); - - 0x5: trunc_w_d({{ - Fd.uw = fpConvert(truncFP(Fs.df), DOUBLE_TO_WORD); - }}); - - 0x6: ceil_w_d({{ - Fd.uw = fpConvert(ceil(Fs.df), DOUBLE_TO_WORD); - }}); - - 0x7: floor_w_d({{ - Fd.uw = fpConvert(floor(Fs.df), DOUBLE_TO_WORD); - }}); + format FloatConvertOp { + 0x0: round_l_d({{ val = Fs.df; }}, ToLong, + Round); + 0x1: trunc_l_d({{ val = Fs.df; }}, ToLong, + Trunc); + 0x2: ceil_l_d({{ val = Fs.df; }}, ToLong, + Ceil); + 0x3: floor_l_d({{ val = Fs.df; }}, ToLong, + Floor); + 0x4: round_w_d({{ val = Fs.df; }}, ToWord, + Round); + 0x5: trunc_w_d({{ val = Fs.df; }}, ToWord, + Trunc); + 0x6: ceil_w_d({{ val = Fs.df; }}, ToWord, + Ceil); + 0x7: floor_w_d({{ val = Fs.df; }}, ToWord, + Floor); } } 0x2: decode FUNCTION_LO { 0x1: decode MOVCF { - format FloatOp { - 0x0: movf_d({{if (getFPConditionCode(FCSR,CC) == 0) Fd.df = Fs.df; }}); - 0x1: movt_d({{if (getFPConditionCode(FCSR,CC) == 1) Fd.df = Fs.df; }}); + format BasicOp { + 0x0: movf_d({{ Fd.df = (getCondCode(FCSR,CC) == 0) ? + Fs.df : Fd.df; + }}); + 0x1: movt_d({{ Fd.df = (getCondCode(FCSR,CC) == 1) ? + Fs.df : Fd.df; + }}); } } format BasicOp { - 0x2: movz_d({{ if (Rt == 0) Fd.df = Fs.df; }}); - 0x3: movn_d({{ if (Rt != 0) Fd.df = Fs.df; }}); + 0x2: movz_d({{ Fd.df = (Rt == 0) ? Fs.df : Fd.df; }}); + 0x3: movn_d({{ Fd.df = (Rt != 0) ? Fs.df : Fd.df; }}); } format FloatOp { - 0x5: recip_d({{ Fd.df = 1 / Fs.df}}); + 0x5: recip_d({{ Fd.df = 1 / Fs.df }}); 0x6: rsqrt_d({{ Fd.df = 1 / sqrt(Fs.df) }}); } } 0x4: decode FUNCTION_LO { - format FloatOp { - 0x0: cvt_s_d({{ - Fd.uw = fpConvert(Fs.df, DOUBLE_TO_SINGLE); - }}); - - 0x4: cvt_w_d({{ - Fd.uw = fpConvert(Fs.df, DOUBLE_TO_WORD); - }}); - - 0x5: cvt_l_d({{ - Fd.ud = fpConvert(Fs.df, DOUBLE_TO_LONG); - }}); + format FloatConvertOp { + 0x0: cvt_s_d({{ val = Fs.df; }}, ToSingle); + 0x4: cvt_w_d({{ val = Fs.df; }}, ToWord); + 0x5: cvt_l_d({{ val = Fs.df; }}, ToLong); } } 0x6: decode FUNCTION_LO { format FloatCompareOp { - 0x0: c_f_d({{ cond = 0; }}); - - 0x1: c_un_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = 0; - }}); - - 0x2: c_eq_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df == Ft.df); - }}); - - 0x3: c_ueq_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df == Ft.df); - }}); - - 0x4: c_olt_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df < Ft.df); - }}); - - 0x5: c_ult_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df < Ft.df); - }}); - - 0x6: c_ole_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df <= Ft.df); - }}); - - 0x7: c_ule_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df <= Ft.df); - }}); + 0x0: c_f_d({{ cond = 0; }}, DoublePrecision, + UnorderedFalse); + 0x1: c_un_d({{ cond = 0; }}, DoublePrecision, + UnorderedTrue); + 0x2: c_eq_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedFalse); + 0x3: c_ueq_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedTrue); + 0x4: c_olt_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedFalse); + 0x5: c_ult_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedTrue); + 0x6: c_ole_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedFalse); + 0x7: c_ule_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedTrue); } } 0x7: decode FUNCTION_LO { - format FloatCompareWithXcptOp { - 0x0: c_sf_d({{ cond = 0; }}); - - 0x1: c_ngle_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = 0; - }}); - - 0x2: c_seq_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df == Ft.df); - }}); - - 0x3: c_ngl_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df == Ft.df); - }}); - - 0x4: c_lt_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df < Ft.df); - }}); - - 0x5: c_nge_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df < Ft.df); - }}); - - 0x6: c_le_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 0; - else - cond = (Fs.df <= Ft.df); - }}); - - 0x7: c_ngt_d({{ - if (isnan(Fs.df) || isnan(Ft.df)) - cond = 1; - else - cond = (Fs.df <= Ft.df); - }}); + format FloatCompareOp { + 0x0: c_sf_d({{ cond = 0; }}, DoublePrecision, + UnorderedFalse, QnanException); + 0x1: c_ngle_d({{ cond = 0; }}, DoublePrecision, + UnorderedTrue, QnanException); + 0x2: c_seq_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedFalse, QnanException); + 0x3: c_ngl_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedTrue, QnanException); + 0x4: c_lt_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedFalse, QnanException); + 0x5: c_nge_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedTrue, QnanException); + 0x6: c_le_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedFalse, QnanException); + 0x7: c_ngt_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedTrue, QnanException); } } } @@ -893,19 +656,9 @@ decode OPCODE_HI default Unknown::unknown() { //Table A-16 MIPS32 COP1 Encoding of Function Field When rs=W 0x4: decode FUNCTION { format FloatConvertOp { - 0x20: cvt_s_w({{ - Fd.uw = fpConvert(Fs.sf, WORD_TO_SINGLE); - }}); - - 0x21: cvt_d_w({{ - Fd.ud = fpConvert(Fs.sf, WORD_TO_DOUBLE); - }}); - } - - format Float64ConvertOp { - 0x26: cvt_ps_pw({{ - Fd.ud = fpConvert(Fs.ud, WORD_TO_PS); - }}); + 0x20: cvt_s_w({{ val = Fs.uw; }}, ToSingle); + 0x21: cvt_d_w({{ val = Fs.uw; }}, ToDouble); + 0x26: FailUnimpl::cvt_ps_w(); } } @@ -913,18 +666,10 @@ decode OPCODE_HI default Unknown::unknown() { //Note: "1. Format type L is legal only if 64-bit floating point operations //are enabled." 0x5: decode FUNCTION_HI { - format Float64ConvertOp { - 0x20: cvt_s_l({{ - Fd.uw = fpConvert(Fs.ud, LONG_TO_SINGLE); - }}); - - 0x21: cvt_d_l({{ - Fd.ud = fpConvert(Fs.ud, LONG_TO_DOUBLE); - }}); - - 0x26: cvt_ps_l({{ - Fd.ud = fpConvert(Fs.ud, LONG_TO_PS); - }}); + format FloatConvertOp { + 0x20: cvt_s_l({{ val = Fs.ud; }}, ToSingle); + 0x21: cvt_d_l({{ val = Fs.ud; }}, ToDouble); + 0x26: FailUnimpl::cvt_ps_l(); } } @@ -938,30 +683,25 @@ decode OPCODE_HI default Unknown::unknown() { Fd1.sf = Fs1.sf + Ft2.sf; Fd2.sf = Fs2.sf + Ft2.sf; }}); - 0x1: sub_ps({{ Fd1.sf = Fs1.sf - Ft2.sf; Fd2.sf = Fs2.sf - Ft2.sf; }}); - 0x2: mul_ps({{ Fd1.sf = Fs1.sf * Ft2.sf; Fd2.sf = Fs2.sf * Ft2.sf; }}); - 0x5: abs_ps({{ Fd1.sf = fabs(Fs1.sf); Fd2.sf = fabs(Fs2.sf); }}); - 0x6: mov_ps({{ Fd1.sf = Fs1.sf; Fd2.sf = Fs2.sf; }}); - 0x7: neg_ps({{ - Fd1.sf = -1 * Fs1.sf; - Fd2.sf = -1 * Fs2.sf; + Fd1.sf = -(Fs1.sf); + Fd2.sf = -(Fs2.sf); }}); } } @@ -970,236 +710,112 @@ decode OPCODE_HI default Unknown::unknown() { 0x1: decode MOVCF { format Float64Op { 0x0: movf_ps({{ - if (getFPConditionCode(FCSR, CC) == 0) - Fd1 = Fs1; - if (getFPConditionCode(FCSR, CC+1) == 0) - Fd2 = Fs2; + Fd1 = (getCondCode(FCSR, CC) == 0) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC+1) == 0) ? + Fs2 : Fd2; }}); - 0x1: movt_ps({{ - if (getFPConditionCode(FCSR, CC) == 1) - Fd1 = Fs1; - if (getFPConditionCode(FCSR, CC+1) == 1) - Fd2 = Fs2; + Fd2 = (getCondCode(FCSR, CC) == 1) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC+1) == 1) ? + Fs2 : Fd2; }}); } } format Float64Op { 0x2: movz_ps({{ - if (getFPConditionCode(FCSR, CC) == 0) - Fd1 = Fs1; - if (getFPConditionCode(FCSR, CC) == 0) - Fd2 = Fs2; + Fd1 = (getCondCode(FCSR, CC) == 0) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC) == 0) ? + Fs2 : Fd2; }}); - 0x3: movn_ps({{ - if (getFPConditionCode(FCSR, CC) == 1) - Fd1 = Fs1; - if (getFPConditionCode(FCSR, CC) == 1) - Fd2 = Fs2; + Fd1 = (getCondCode(FCSR, CC) == 1) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC) == 1) ? + Fs2 : Fd2; }}); } } 0x4: decode FUNCTION_LO { - 0x0: Float64Op::cvt_s_pu({{ - Fd.uw = fpConvert(Fs2.uw, PU_TO_SINGLE); - }}); + 0x0: FloatOp::cvt_s_pu({{ Fd.sf = Fs2.sf; }}); } 0x5: decode FUNCTION_LO { - format Float64Op { - 0x0: cvt_s_pl({{ - Fd.uw = fpConvert(Fs1.uw, PL_TO_SINGLE); - }}); + 0x0: FloatOp::cvt_s_pl({{ Fd.sf = Fs1.sf; }}); - 0x4: pll({{ Fd.ud = (uint64_t) Fs1.uw << 32 | Ft1.uw; }}); - 0x5: plu({{ Fd.ud = (uint64_t) Fs1.uw << 32 | Ft2.uw; }}); - 0x6: pul({{ Fd.ud = (uint64_t) Fs2.uw << 32 | Ft1.uw; }}); - 0x7: puu({{ Fd.ud = (uint64_t) Fs2.uw << 32 | Ft2.uw; }}); + format Float64Op { + 0x4: pll({{ Fd.ud = (uint64_t) Fs1.uw << 32 | + Ft1.uw; + }}); + 0x5: plu({{ Fd.ud = (uint64_t) Fs1.uw << 32 | + Ft2.uw; + }}); + 0x6: pul({{ Fd.ud = (uint64_t) Fs2.uw << 32 | + Ft1.uw; + }}); + 0x7: puu({{ Fd.ud = (uint64_t) Fs2.uw << 32 | + Ft2.uw; + }}); } } 0x6: decode FUNCTION_LO { format FloatPSCompareOp { - 0x0: c_f_ps({{ cond1 = 0; cond2 = 0; }}); - - 0x1: c_un_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = 0; - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = 0; - - }}); - - 0x2: c_eq_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf == Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf == Ft2.sf); - }}); - - 0x3: c_ueq_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf == Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf == Ft2.sf); - }}); - - 0x4: c_olt_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf < Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf < Ft2.sf); - }}); - - 0x5: c_ult_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs.sf < Ft.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf < Ft2.sf); - }}); - - 0x6: c_ole_ps({{ - if (isnan(Fs.sf) || isnan(Ft.sf)) - cond1 = 0; - else - cond1 = (Fs.sf <= Ft.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf <= Ft2.sf); - }}); - - 0x7: c_ule_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf <= Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf <= Ft2.sf); - }}); + 0x0: c_f_ps({{ cond1 = 0; }}, {{ cond2 = 0; }}, + UnorderedFalse); + 0x1: c_un_ps({{ cond1 = 0; }}, {{ cond2 = 0; }}, + UnorderedTrue); + 0x2: c_eq_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedFalse); + 0x3: c_ueq_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedTrue); + 0x4: c_olt_ps({{ cond1 = (Fs1.sf < Ft1.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedFalse); + 0x5: c_ult_ps({{ cond1 = (Fs.sf < Ft.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedTrue); + 0x6: c_ole_ps({{ cond1 = (Fs.sf <= Ft.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedFalse); + 0x7: c_ule_ps({{ cond1 = (Fs1.sf <= Ft1.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedTrue); } } 0x7: decode FUNCTION_LO { - format FloatPSCompareWithXcptOp { - 0x0: c_sf_ps({{ cond1 = 0; cond2 = 0; }}); - - 0x1: c_ngle_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = 0; - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = 0; - }}); - - 0x2: c_seq_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf == Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf == Ft2.sf); - }}); - - 0x3: c_ngl_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf == Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf == Ft2.sf); - }}); - - 0x4: c_lt_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf < Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf < Ft2.sf); - }}); - - 0x5: c_nge_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf < Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf < Ft2.sf); - }}); - - 0x6: c_le_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 0; - else - cond1 = (Fs1.sf <= Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 0; - else - cond2 = (Fs2.sf <= Ft2.sf); - }}); - - 0x7: c_ngt_ps({{ - if (isnan(Fs1.sf) || isnan(Ft1.sf)) - cond1 = 1; - else - cond1 = (Fs1.sf <= Ft1.sf); - - if (isnan(Fs2.sf) || isnan(Ft2.sf)) - cond2 = 1; - else - cond2 = (Fs2.sf <= Ft2.sf); - }}); + format FloatPSCompareOp { + 0x0: c_sf_ps({{ cond1 = 0; }}, {{ cond2 = 0; }}, + UnorderedFalse, QnanException); + 0x1: c_ngle_ps({{ cond1 = 0; }}, + {{ cond2 = 0; }}, + UnorderedTrue, QnanException); + 0x2: c_seq_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedFalse, QnanException); + 0x3: c_ngl_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedTrue, QnanException); + 0x4: c_lt_ps({{ cond1 = (Fs1.sf < Ft1.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedFalse, QnanException); + 0x5: c_nge_ps({{ cond1 = (Fs1.sf < Ft1.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedTrue, QnanException); + 0x6: c_le_ps({{ cond1 = (Fs1.sf <= Ft1.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedFalse, QnanException); + 0x7: c_ngt_ps({{ cond1 = (Fs1.sf <= Ft1.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedTrue, QnanException); } } } @@ -1209,9 +825,9 @@ decode OPCODE_HI default Unknown::unknown() { //Table A-19 MIPS32 COP2 Encoding of rs Field 0x2: decode RS_MSB { - 0x0: decode RS_HI { - 0x0: decode RS_LO { - format WarnUnimpl { + format FailUnimpl { + 0x0: decode RS_HI { + 0x0: decode RS_LO { 0x0: mfc2(); 0x2: cfc2(); 0x3: mfhc2(); @@ -1219,18 +835,14 @@ decode OPCODE_HI default Unknown::unknown() { 0x6: ctc2(); 0x7: mftc2(); } - } - 0x1: decode ND { - 0x0: decode TF { - format WarnUnimpl { + 0x1: decode ND { + 0x0: decode TF { 0x0: bc2f(); 0x1: bc2t(); } - } - 0x1: decode TF { - format WarnUnimpl { + 0x1: decode TF { 0x0: bc2fl(); 0x1: bc2tl(); } @@ -1244,441 +856,242 @@ decode OPCODE_HI default Unknown::unknown() { //operations are enabled." 0x3: decode FUNCTION_HI { 0x0: decode FUNCTION_LO { - format LoadFloatMemory { - 0x0: lwxc1({{ Ft.uw = Mem.uw;}}, {{ EA = Rs + Rt; }}); - 0x1: ldxc1({{ Ft.ud = Mem.ud;}}, {{ EA = Rs + Rt; }}); - 0x5: luxc1({{ Ft.uw = Mem.ud;}}, {{ EA = Rs + Rt; }}); + format LoadIndexedMemory { + 0x0: lwxc1({{ Fd.uw = Mem.uw;}}); + 0x1: ldxc1({{ Fd.ud = Mem.ud;}}); + 0x5: luxc1({{ Fd.ud = Mem.ud;}}, + {{ EA = (Rs + Rt) & ~7; }}); } } 0x1: decode FUNCTION_LO { - format StoreFloatMemory { - 0x0: swxc1({{ Mem.uw = Ft.uw;}}, {{ EA = Rs + Rt; }}); - 0x1: sdxc1({{ Mem.ud = Ft.ud;}}, {{ EA = Rs + Rt; }}); - 0x5: suxc1({{ Mem.ud = Ft.ud;}}, {{ EA = Rs + Rt; }}); + format StoreIndexedMemory { + 0x0: swxc1({{ Mem.uw = Fs.uw;}}); + 0x1: sdxc1({{ Mem.ud = Fs.ud;}}); + 0x5: suxc1({{ Mem.ud = Fs.ud;}}, + {{ EA = (Rs + Rt) & ~7; }}); } - 0x7: WarnUnimpl::prefx(); + 0x7: Prefetch::prefx({{ EA = Rs + Rt; }}); } - format FloatOp { - 0x3: WarnUnimpl::alnv_ps(); + 0x3: decode FUNCTION_LO { + 0x6: Float64Op::alnv_ps({{ if (Rs<2:0> == 0) { + Fd.ud = Fs.ud; + } else if (Rs<2:0> == 4) { + #if BYTE_ORDER == BIG_ENDIAN + Fd.ud = Fs.ud<31:0> << 32 | + Ft.ud<63:32>; + #elif BYTE_ORDER == LITTLE_ENDIAN + Fd.ud = Ft.ud<31:0> << 32 | + Fs.ud<63:32>; + #endif + } else { + Fd.ud = Fd.ud; + } + }}); + } - format BasicOp { - 0x4: decode FUNCTION_LO { - 0x0: madd_s({{ Fd.sf = (Fs.sf * Ft.sf) + Fr.sf; }}); - 0x1: madd_d({{ Fd.df = (Fs.df * Ft.df) + Fr.df; }}); - 0x6: madd_ps({{ - Fd1.sf = (Fs1.df * Ft1.df) + Fr1.df; - Fd2.sf = (Fs2.df * Ft2.df) + Fr2.df; - }}); - } + format FloatAccOp { + 0x4: decode FUNCTION_LO { + 0x0: madd_s({{ Fd.sf = (Fs.sf * Ft.sf) + Fr.sf; }}); + 0x1: madd_d({{ Fd.df = (Fs.df * Ft.df) + Fr.df; }}); + 0x6: madd_ps({{ + Fd1.sf = (Fs1.df * Ft1.df) + Fr1.df; + Fd2.sf = (Fs2.df * Ft2.df) + Fr2.df; + }}); + } - 0x5: decode FUNCTION_LO { - 0x0: msub_s({{ Fd.sf = (Fs.sf * Ft.sf) - Fr.sf; }}); - 0x1: msub_d({{ Fd.df = (Fs.df * Ft.df) - Fr.df; }}); - 0x6: msub_ps({{ - Fd1.sf = (Fs1.df * Ft1.df) - Fr1.df; - Fd2.sf = (Fs2.df * Ft2.df) - Fr2.df; - }}); - } + 0x5: decode FUNCTION_LO { + 0x0: msub_s({{ Fd.sf = (Fs.sf * Ft.sf) - Fr.sf; }}); + 0x1: msub_d({{ Fd.df = (Fs.df * Ft.df) - Fr.df; }}); + 0x6: msub_ps({{ + Fd1.sf = (Fs1.df * Ft1.df) - Fr1.df; + Fd2.sf = (Fs2.df * Ft2.df) - Fr2.df; + }}); + } - 0x6: decode FUNCTION_LO { - 0x0: nmadd_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); - 0x1: nmadd_d({{ Fd.df = (-1 * Fs.df * Ft.df) + Fr.df; }}); - 0x6: nmadd_ps({{ - Fd1.sf = -1 * ((Fs1.df * Ft1.df) + Fr1.df); - Fd2.sf = -1 * ((Fs2.df * Ft2.df) + Fr2.df); - }}); - } + 0x6: decode FUNCTION_LO { + 0x0: nmadd_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); + 0x1: nmadd_d({{ Fd.df = (-1 * Fs.df * Ft.df) + Fr.df; }}); + 0x6: nmadd_ps({{ + Fd1.sf = -((Fs1.df * Ft1.df) + Fr1.df); + Fd2.sf = -((Fs2.df * Ft2.df) + Fr2.df); + }}); + } - 0x7: decode FUNCTION_LO { - 0x0: nmsub_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); - 0x1: nmsub_d({{ Fd.df = (-1 * Fs.df * Ft.df) - Fr.df; }}); - 0x6: nmsub_ps({{ - Fd1.sf = -1 * ((Fs1.df * Ft1.df) - Fr1.df); - Fd2.sf = -1 * ((Fs2.df * Ft2.df) - Fr2.df); - }}); - } + 0x7: decode FUNCTION_LO { + 0x0: nmsub_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); + 0x1: nmsub_d({{ Fd.df = (-1 * Fs.df * Ft.df) - Fr.df; }}); + 0x6: nmsub_ps({{ + Fd1.sf = -((Fs1.df * Ft1.df) - Fr1.df); + Fd2.sf = -((Fs2.df * Ft2.df) - Fr2.df); + }}); } + } } - format BranchLikely { - 0x4: beql({{ cond = (Rs.sw == 0); }}); - 0x5: bnel({{ cond = (Rs.sw != 0); }}); - 0x6: blezl({{ cond = (Rs.sw <= 0); }}); - 0x7: bgtzl({{ cond = (Rs.sw > 0); }}); + format Branch { + 0x4: beql({{ cond = (Rs.sw == Rt.sw); }}, Likely); + 0x5: bnel({{ cond = (Rs.sw != Rt.sw); }}, Likely); + 0x6: blezl({{ cond = (Rs.sw <= 0); }}, Likely); + 0x7: bgtzl({{ cond = (Rs.sw > 0); }}, Likely); } } - 0x3: decode OPCODE_LO default FailUnimpl::reserved() { - + 0x3: decode OPCODE_LO { //Table A-5 MIPS32 SPECIAL2 Encoding of Function Field 0x4: decode FUNCTION_HI { - 0x0: decode FUNCTION_LO { - format IntOp { - 0x0: madd({{ - int64_t temp1 = (int64_t) HI << 32 | LO; - temp1 = temp1 + (Rs.sw * Rt.sw); - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); - - 0x1: maddu({{ - int64_t temp1 = (int64_t) HI << 32 | LO; - temp1 = temp1 + (Rs.uw * Rt.uw); - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); - - 0x2: mul({{ Rd.sw = Rs.sw * Rt.sw; }}); - - 0x4: msub({{ - int64_t temp1 = (int64_t) HI << 32 | LO; - temp1 = temp1 - (Rs.sw * Rt.sw); - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); + 0x2: IntOp::mul({{ int64_t temp1 = Rs.sd * Rt.sd; + Rd.sw = temp1<31:0> + }}); - 0x5: msubu({{ - int64_t temp1 = (int64_t) HI << 32 | LO; - temp1 = temp1 - (Rs.uw * Rt.uw); - HI = temp1<63:32>; - LO = temp1<31:0>; - }}); + format HiLoOp { + 0x0: madd({{ val = ((int64_t) HI << 32 | LO) + + (Rs.sd * Rt.sd); + }}); + 0x1: maddu({{ val = ((uint64_t) HI << 32 | LO) + + (Rs.ud * Rt.ud); + }}); + 0x4: msub({{ val = ((int64_t) HI << 32 | LO) - + (Rs.sd * Rt.sd); + }}); + 0x5: msubu({{ val = ((uint64_t) HI << 32 | LO) - + (Rs.ud * Rt.ud); + }}); } } 0x4: decode FUNCTION_LO { format BasicOp { - 0x0: clz({{ - int cnt = 0; - uint32_t mask = 0x80000000; - for (int i=0; i < 32; i++) { - if( (Rs & mask) == 0) { - cnt++; - } else { - break; - } - } - Rd.uw = cnt; - }}); - - 0x1: clo({{ - int cnt = 0; - uint32_t mask = 0x80000000; - for (int i=0; i < 32; i++) { - if( (Rs & mask) != 0) { - cnt++; - } else { - break; - } - } - Rd.uw = cnt; - }}); + 0x0: clz({{ int cnt = 32; + for (int idx = 31; idx >= 0; idx--) { + if( Rs<idx:idx> == 1) { + cnt = 31 - idx; + break; + } + } + Rd.uw = cnt; + }}); + 0x1: clo({{ int cnt = 32; + for (int idx = 31; idx >= 0; idx--) { + if( Rs<idx:idx> == 0) { + cnt = 31 - idx; + break; + } + } + Rd.uw = cnt; + }}); } } 0x7: decode FUNCTION_LO { - 0x7: WarnUnimpl::sdbbp(); + 0x7: FailUnimpl::sdbbp(); } } - //Table A-6 MIPS32 SPECIAL3 Encoding of Function Field for Release 2 of the Architecture + //Table A-6 MIPS32 SPECIAL3 Encoding of Function Field for Release 2 + //of the Architecture 0x7: decode FUNCTION_HI { - 0x0: decode FUNCTION_LO { - format FailUnimpl { - 0x1: ext(); - 0x4: ins(); + format BasicOp { + 0x0: ext({{ Rt.uw = bits(Rs.uw, MSB+LSB, LSB); }}); + 0x4: ins({{ Rt.uw = bits(Rt.uw, 31, MSB+1) << (MSB+1) | + bits(Rs.uw, MSB-LSB, 0) << LSB | + bits(Rt.uw, LSB-1, 0); + }}); } } 0x1: decode FUNCTION_LO { - format FailUnimpl { + format MipsMT { 0x0: fork(); 0x1: yield(); } } - //Table A-10 MIPS32 BSHFL Encoding of sa Field 0x4: decode SA { - - 0x02: FailUnimpl::wsbh(); - format BasicOp { - 0x10: seb({{ Rd.sw = Rt.sw<7:0>}}); - 0x18: seh({{ Rd.sw = Rt.sw<15:0>}}); + 0x02: wsbh({{ Rd.uw = Rt.uw<23:16> << 24 | + Rt.uw<31:24> << 16 | + Rt.uw<7:0> << 8 | + Rt.uw<15:8>; + }}); + 0x10: seb({{ Rd.sw = Rt.sb; }}); + 0x18: seh({{ Rd.sw = Rt.sh; }}); } } 0x6: decode FUNCTION_LO { - 0x7: FailUnimpl::rdhwr();//{{ /*Rt = xc->hwRegs[RD];*/ }} + 0x7: FailUnimpl::rdhwr(); } } } - 0x4: decode OPCODE_LO default FailUnimpl::reserved() { + 0x4: decode OPCODE_LO { format LoadMemory { 0x0: lb({{ Rt.sw = Mem.sb; }}); 0x1: lh({{ Rt.sw = Mem.sh; }}); - - 0x2: lwl({{ - uint32_t mem_word = Mem.uw; - uint32_t unalign_addr = Rs + disp; - uint32_t offset = unalign_addr & 0x00000003; -#if BYTE_ORDER == BIG_ENDIAN - switch(offset) - { - case 0: - Rt = mem_word; - break; - - case 1: - Rt &= 0x000F; - Rt |= (mem_word << 4); - break; - - case 2: - Rt &= 0x00FF; - Rt |= (mem_word << 8); - break; - - case 3: - Rt &= 0x0FFF; - Rt |= (mem_word << 12); - break; - - default: - panic("lwl: bad offset"); - } -#elif BYTE_ORDER == LITTLE_ENDIAN - switch(offset) - { - case 0: - Rt &= 0x0FFF; - Rt |= (mem_word << 12); - break; - - case 1: - Rt &= 0x00FF; - Rt |= (mem_word << 8); - break; - - case 2: - Rt &= 0x000F; - Rt |= (mem_word << 4); - break; - - case 3: - Rt = mem_word; - break; - - default: - panic("lwl: bad offset"); - } -#endif - }}, {{ EA = (Rs + disp) & ~3; }}); - 0x3: lw({{ Rt.sw = Mem.sw; }}); 0x4: lbu({{ Rt.uw = Mem.ub; }}); 0x5: lhu({{ Rt.uw = Mem.uh; }}); - 0x6: lwr({{ - uint32_t mem_word = Mem.uw; - uint32_t unalign_addr = Rs + disp; - uint32_t offset = unalign_addr & 0x00000003; - -#if BYTE_ORDER == BIG_ENDIAN - switch(offset) - { - case 0: Rt &= 0xFFF0; Rt |= (mem_word >> 12); break; - case 1: Rt &= 0xFF00; Rt |= (mem_word >> 8); break; - case 2: Rt &= 0xF000; Rt |= (mem_word >> 4); break; - case 3: Rt = mem_word; break; - default: panic("lwr: bad offset"); - } -#elif BYTE_ORDER == LITTLE_ENDIAN - switch(offset) - { - case 0: Rt = mem_word; break; - case 1: Rt &= 0xF000; Rt |= (mem_word >> 4); break; - case 2: Rt &= 0xFF00; Rt |= (mem_word >> 8); break; - case 3: Rt &= 0xFFF0; Rt |= (mem_word >> 12); break; - default: panic("lwr: bad offset"); - } -#endif - }}, - {{ EA = (Rs + disp) & ~3; }}); + } + + format LoadUnalignedMemory { + 0x2: lwl({{ uint32_t mem_shift = 24 - (8 * byte_offset); + Rt.uw = mem_word << mem_shift | + Rt.uw & mask(mem_shift); + }}); + 0x6: lwr({{ uint32_t mem_shift = 8 * byte_offset; + Rt.uw = Rt.uw & (mask(mem_shift) << (32 - mem_shift)) | + mem_word >> mem_shift; + }}); } } - 0x5: decode OPCODE_LO default FailUnimpl::reserved() { + 0x5: decode OPCODE_LO { format StoreMemory { 0x0: sb({{ Mem.ub = Rt<7:0>; }}); 0x1: sh({{ Mem.uh = Rt<15:0>; }}); - 0x2: swl({{ - uint32_t mem_word = 0; - uint32_t aligned_addr = (Rs + disp) & ~3; - uint32_t unalign_addr = Rs + disp; - uint32_t offset = unalign_addr & 0x00000003; - - DPRINTF(IEW,"Execute: aligned=0x%x unaligned=0x%x\n offset=0x%x", - aligned_addr,unalign_addr,offset); - - fault = xc->read(aligned_addr, (uint32_t&)mem_word, memAccessFlags); - -#if BYTE_ORDER == BIG_ENDIAN - switch(offset) - { - case 0: - Mem = Rt; - break; - - case 1: - mem_word &= 0xF000; - mem_word |= (Rt >> 4); - Mem = mem_word; - break; - - case 2: - mem_word &= 0xFF00; - mem_word |= (Rt >> 8); - Mem = mem_word; - break; - - case 3: - mem_word &= 0xFFF0; - mem_word |= (Rt >> 12); - Mem = mem_word; - break; - - default: - panic("swl: bad offset"); - } -#elif BYTE_ORDER == LITTLE_ENDIAN - switch(offset) - { - case 0: - mem_word &= 0xFFF0; - mem_word |= (Rt >> 12); - Mem = mem_word; - break; - - case 1: - mem_word &= 0xFF00; - mem_word |= (Rt >> 8); - Mem = mem_word; - break; - - case 2: - mem_word &= 0xF000; - mem_word |= (Rt >> 4); - Mem = mem_word; - break; - - case 3: - Mem = Rt; - break; - - default: - panic("swl: bad offset"); - } -#endif - }},{{ EA = (Rs + disp) & ~3; }},mem_flags = NO_ALIGN_FAULT); - 0x3: sw({{ Mem.uw = Rt<31:0>; }}); - - 0x6: swr({{ - uint32_t mem_word = 0; - uint32_t aligned_addr = (Rs + disp) & ~3; - uint32_t unalign_addr = Rs + disp; - uint32_t offset = unalign_addr & 0x00000003; - - fault = xc->read(aligned_addr, (uint32_t&)mem_word, memAccessFlags); - -#if BYTE_ORDER == BIG_ENDIAN - switch(offset) - { - case 0: - mem_word &= 0x0FFF; - mem_word |= (Rt << 12); - Mem = mem_word; - break; - - case 1: - mem_word &= 0x00FF; - mem_word |= (Rt << 8); - Mem = mem_word; - break; - - case 2: - mem_word &= 0x000F; - mem_word |= (Rt << 4); - Mem = mem_word; - break; - - case 3: - Mem = Rt; - break; - - default: - panic("swr: bad offset"); - } -#elif BYTE_ORDER == LITTLE_ENDIAN - switch(offset) - { - case 0: - Mem = Rt; - break; - - case 1: - mem_word &= 0x000F; - mem_word |= (Rt << 4); - Mem = mem_word; - break; - - case 2: - mem_word &= 0x00FF; - mem_word |= (Rt << 8); - Mem = mem_word; - break; - - case 3: - mem_word &= 0x0FFF; - mem_word |= (Rt << 12); - Mem = mem_word; - break; - - default: - panic("swr: bad offset"); - } -#endif - }},{{ EA = (Rs + disp) & ~3;}},mem_flags = NO_ALIGN_FAULT); } - format WarnUnimpl { - 0x7: cache(); + format StoreUnalignedMemory { + 0x2: swl({{ uint32_t reg_shift = 24 - (8 * byte_offset); + uint32_t mem_shift = 32 - reg_shift; + mem_word = mem_word & (mask(reg_shift) << mem_shift) | + Rt.uw >> reg_shift; + }}); + 0x6: swr({{ uint32_t reg_shift = 8 * byte_offset; + mem_word = Rt.uw << reg_shift | + mem_word & (mask(reg_shift)); + }}); } + 0x7: FailUnimpl::cache(); } - 0x6: decode OPCODE_LO default FailUnimpl::reserved() { - 0x0: LoadMemory::ll({{Rt.uw = Mem.uw}},mem_flags=LOCKED); - - format LoadFloatMemory { - 0x1: lwc1({{ Ft.uw = Mem.uw; }}); + 0x6: decode OPCODE_LO { + format LoadMemory { + 0x0: ll({{ Rt.uw = Mem.uw; }}, mem_flags=LOCKED); + 0x1: lwc1({{ Ft.uw = Mem.uw; }}); 0x5: ldc1({{ Ft.ud = Mem.ud; }}); } + + 0x3: Prefetch::pref(); } - 0x7: decode OPCODE_LO default FailUnimpl::reserved() { - 0x0: StoreMemory::sc({{ Mem.uw = Rt.uw; Rt.uw = 1; }}); + 0x7: decode OPCODE_LO { + 0x0: StoreCond::sc({{ Mem.uw = Rt.uw;}}, + {{ uint64_t tmp = write_result; + Rt.uw = (tmp == 0 || tmp == 1) ? tmp : Rt.uw; + }}, mem_flags=LOCKED); - format StoreFloatMemory { + format StoreMemory { 0x1: swc1({{ Mem.uw = Ft.uw; }}); 0x5: sdc1({{ Mem.ud = Ft.ud; }}); } diff --git a/src/arch/mips/isa/formats/basic.isa b/src/arch/mips/isa/formats/basic.isa index c02af7ddc..29dafd541 100644 --- a/src/arch/mips/isa/formats/basic.isa +++ b/src/arch/mips/isa/formats/basic.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + // Declarations for execute() methods. def template BasicExecDeclare {{ Fault execute(%(CPU_exec_context)s *, Trace::InstRecord *) const; @@ -12,11 +40,11 @@ def template BasicDeclare {{ */ class %(class_name)s : public %(base_class)s { - public: + public: /// Constructor. %(class_name)s(MachInst machInst); %(BasicExecDeclare)s - }; + }; }}; // Basic instruction class constructor template. @@ -27,6 +55,7 @@ def template BasicConstructor {{ } }}; + // Basic instruction class execute method template. def template BasicExecute {{ Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const diff --git a/src/arch/mips/isa/formats/branch.isa b/src/arch/mips/isa/formats/branch.isa index 8cfa37a20..5230ce9cc 100644 --- a/src/arch/mips/isa/formats/branch.isa +++ b/src/arch/mips/isa/formats/branch.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Control transfer instructions @@ -68,29 +96,6 @@ output header {{ }; /** - * Base class for branch likely branches (PC-relative control transfers), - */ - class BranchLikely : public PCDependentDisassembly - { - protected: - /// target address (signed) Displacement . - int32_t disp; - - /// Constructor. - BranchLikely(const char *mnem, MachInst _machInst, OpClass __opClass) - : PCDependentDisassembly(mnem, _machInst, __opClass), - disp(OFFSET << 2) - { - - } - - Addr branchTarget(Addr branchPC) const; - - std::string - generateDisassembly(Addr pc, const SymbolTable *symtab) const; - }; - - /** * Base class for jumps (register-indirect control transfers). In * the Mips ISA, these are always unconditional. */ @@ -111,7 +116,7 @@ output header {{ { } - Addr branchTarget(ExecContext *xc) const; + Addr branchTarget(ThreadContext *tc) const; std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; @@ -126,16 +131,10 @@ output decoder {{ } Addr - BranchLikely::branchTarget(Addr branchPC) const - { - return branchPC + 4 + disp; - } - - Addr - Jump::branchTarget(ExecContext *xc) const + Jump::branchTarget(ThreadContext *tc) const { - Addr NPC = xc->readPC() + 4; - uint64_t Rb = xc->readIntReg(_srcRegIdx[0]); + Addr NPC = tc->readPC() + 4; + uint64_t Rb = tc->readIntReg(_srcRegIdx[0]); return (Rb & ~3) | (NPC & 1); } @@ -171,49 +170,12 @@ output decoder {{ // unconditional branches) if (_numSrcRegs == 1) { printReg(ss, _srcRegIdx[0]); - ss << ","; + ss << ", "; } else if(_numSrcRegs == 2) { printReg(ss, _srcRegIdx[0]); - ss << ","; + ss << ", "; printReg(ss, _srcRegIdx[1]); - ss << ","; - } - - Addr target = pc + 4 + disp; - - std::string str; - if (symtab && symtab->findSymbol(target, str)) - ss << str; - else - ccprintf(ss, "0x%x", target); - - string inst_name = mnemonic; - - if (inst_name.substr(inst_name.length()-2,inst_name.length()) == "al"){ - ccprintf(ss, " (r31=0x%x)",pc+8); - } - - return ss.str(); - } - - std::string - BranchLikely::generateDisassembly(Addr pc, const SymbolTable *symtab) const - { - std::stringstream ss; - - ccprintf(ss, "%-10s ", mnemonic); - - // There's only one register arg (RA), but it could be - // either a source (the condition for conditional - // branches) or a destination (the link reg for - // unconditional branches) - if (_numSrcRegs > 0) { - printReg(ss, _srcRegIdx[0]); - ss << ","; - } - else if (_numDestRegs > 0) { - printReg(ss, _destRegIdx[0]); - ss << ","; + ss << ", "; } Addr target = pc + 4 + disp; @@ -247,72 +209,64 @@ output decoder {{ printReg(ss, _srcRegIdx[0]); } else if(_numSrcRegs == 2) { printReg(ss, _srcRegIdx[0]); - ss << ","; + ss << ", "; printReg(ss, _srcRegIdx[1]); - } else { - panic(">= 3 Source Registers!!!"); } return ss.str(); } }}; -def format Branch(code,*flags) {{ - #Add Link Code if Link instruction - strlen = len(name) - if name[strlen-2:] == 'al': - code += 'R31 = NNPC;\n' +def format Branch(code,*opt_flags) {{ + not_taken_code = ' NNPC = NNPC;\n' + not_taken_code += '} \n' + + #Build Instruction Flags + #Use Link & Likely Flags to Add Link/Condition Code + inst_flags = ('IsDirectControl', ) + for x in opt_flags: + if x == 'Link': + code += 'R31 = NNPC;\n' + elif x == 'Likely': + not_taken_code = ' NPC = NNPC;\n' + not_taken_code += ' NNPC = NNPC + 4;\n' + not_taken_code += '} \n' + inst_flags = ('IsCondDelaySlot', ) + else: + inst_flags += (x, ) + + if 'cond == 1' in code: + inst_flags += ('IsCondControl', ) + else: + inst_flags += ('IsUncondControl', ) #Condition code code = 'bool cond;\n' + code code += 'if (cond) {\n' code += ' NNPC = NPC + disp;\n' code += '} else {\n' - code += ' NNPC = NNPC;\n' - code += '} \n' - - iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), - ('IsDirectControl', 'IsCondControl')) - - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) -}}; - - -def format BranchLikely(code,*flags) {{ - #Add Link Code if Link instruction - strlen = len(name) - if name[strlen-3:] == 'all': - code += 'R31 = NNPC;\n' - - #Condition code - code = 'bool cond;\n' + code - code += 'if (cond) {' - code += 'NNPC = NPC + disp;\n' - code += '} \n' - - - iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), - ('IsDirectControl', 'IsCondControl','IsCondDelaySlot')) + code += not_taken_code + iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), inst_flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) exec_output = BasicExecute.subst(iop) }}; -def format Jump(code,*flags) {{ - #Add Link Code if Link instruction - strlen = len(name) - if strlen > 1 and name[1:] == 'al': +def format Jump(code, *opt_flags) {{ + #Build Instruction Flags + #Use Link Flag to Add Link Code + inst_flags = ('IsIndirectControl', 'IsUncondControl') + for x in opt_flags: + if x == 'Link': code = 'R31 = NNPC;\n' + code + elif x == 'ClearHazards': + code += '/* Code Needed to Clear Execute & Inst Hazards */\n' + else: + inst_flags += (x, ) - - iop = InstObjParams(name, Name, 'Jump', CodeBlock(code),\ - ('IsIndirectControl', 'IsUncondControl')) - + iop = InstObjParams(name, Name, 'Jump', CodeBlock(code), inst_flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) diff --git a/src/arch/mips/isa/formats/control.isa b/src/arch/mips/isa/formats/control.isa new file mode 100644 index 000000000..6c7d396f3 --- /dev/null +++ b/src/arch/mips/isa/formats/control.isa @@ -0,0 +1,156 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Integer operate instructions +// + +//Outputs to decoder.hh +output header {{ + + class Control : public MipsStaticInst + { + protected: + + /// Constructor + Control(const char *mnem, MachInst _machInst, OpClass __opClass) : + MipsStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class CP0Control : public Control + { + protected: + + /// Constructor + CP0Control(const char *mnem, MachInst _machInst, OpClass __opClass) : + Control(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class CP1Control : public Control + { + protected: + + /// Constructor + CP1Control(const char *mnem, MachInst _machInst, OpClass __opClass) : + Control(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + +}}; + +//Outputs to decoder.cc +output decoder {{ + std::string Control::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + if (mnemonic == "mfc0" || mnemonic == "mtc0") { + ccprintf(ss, "%-10s %d,%d,%d", mnemonic,RT,RD,SEL); + } else { + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + } + + ss << ", "; + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + + if (_numSrcRegs > 1) { + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + } + + return ss.str(); + } + + std::string CP0Control::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + ccprintf(ss, "%-10s r%d, r%d, %d", mnemonic, RT, RD, SEL); + return ss.str(); + } + + std::string CP1Control::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + ccprintf(ss, "%-10s r%d, f%d", mnemonic, RT, FS); + return ss.str(); + } + +}}; + +def format System(code, *flags) {{ + iop = InstObjParams(name, Name, 'Control', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format CP0Control(code, *flags) {{ + iop = InstObjParams(name, Name, 'CP0Control', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format CP1Control(code, *flags) {{ + iop = InstObjParams(name, Name, 'CP1Control', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + + diff --git a/src/arch/mips/isa/formats/formats.isa b/src/arch/mips/isa/formats/formats.isa index 7d493ffae..4c3eec132 100644 --- a/src/arch/mips/isa/formats/formats.isa +++ b/src/arch/mips/isa/formats/formats.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2003-2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //Templates from this format are used later //Include the basic format ##include "basic.isa" @@ -10,8 +38,8 @@ //Include utility functions ##include "util.isa" -//Include the cop0 formats -##include "cop0.isa" +//Include the control/cp0/cp1 formats +##include "control.isa" //Include the integer formats ##include "int.isa" @@ -22,6 +50,9 @@ //Include the mem format ##include "mem.isa" +//Include the mem format +##include "mt.isa" + //Include the trap format ##include "trap.isa" diff --git a/src/arch/mips/isa/formats/fp.isa b/src/arch/mips/isa/formats/fp.isa index 9f2c24755..1e5d62626 100644 --- a/src/arch/mips/isa/formats/fp.isa +++ b/src/arch/mips/isa/formats/fp.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Floating Point operate instructions @@ -18,49 +46,264 @@ output header {{ { } - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + //std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + + //needs function to check for fpEnable or not + }; + + class FPCompareOp : public FPOp + { + protected: + FPCompareOp(const char *mnem, MachInst _machInst, OpClass __opClass) : FPOp(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; }}; output decoder {{ - std::string FPOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + std::string FPCompareOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const { - return "Disassembly of integer instruction\n"; + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + ccprintf(ss,"%d",CC); + + if(_numSrcRegs > 0) { + ss << ", "; + printReg(ss, _srcRegIdx[0]); + } + + if(_numSrcRegs > 1) { + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + + return ss.str(); } }}; +output exec {{ -// Primary format for float operate instructions: -def format FloatOp(code, *flags) {{ - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) + //If any operand is Nan return the appropriate QNaN + template <class T> + bool + fpNanOperands(FPOp *inst, %(CPU_exec_context)s *xc, const T &src_type, + Trace::InstRecord *traceData) + { + uint64_t mips_nan = 0; + T src_op = 0; + int size = sizeof(src_op) * 8; + + for (int i = 0; i < inst->numSrcRegs(); i++) { + uint64_t src_bits = xc->readFloatRegBits(inst, 0, size); + + if (isNan(&src_bits, size) ) { + if (isSnan(&src_bits, size)) { + switch (size) + { + case 32: mips_nan = MIPS32_QNAN; break; + case 64: mips_nan = MIPS64_QNAN; break; + default: panic("Unsupported Floating Point Size (%d)", size); + } + } else { + mips_nan = src_bits; + } + + xc->setFloatRegBits(inst, 0, mips_nan, size); + if (traceData) { traceData->setData(mips_nan); } + return true; + } + } + return false; + } + + template <class T> + bool + fpInvalidOp(FPOp *inst, %(CPU_exec_context)s *cpu, const T dest_val, + Trace::InstRecord *traceData) + { + uint64_t mips_nan = 0; + T src_op = dest_val; + int size = sizeof(src_op) * 8; + + if (isNan(&src_op, size)) { + switch (size) + { + case 32: mips_nan = MIPS32_QNAN; break; + case 64: mips_nan = MIPS64_QNAN; break; + default: panic("Unsupported Floating Point Size (%d)", size); + } + + //Set value to QNAN + cpu->setFloatRegBits(inst, 0, mips_nan, size); + + //Read FCSR from FloatRegFile + uint32_t fcsr_bits = cpu->tc->readFloatRegBits(FCSR); + + //Write FCSR from FloatRegFile + cpu->tc->setFloatRegBits(FCSR, genInvalidVector(fcsr_bits)); + + if (traceData) { traceData->setData(mips_nan); } + return true; + } + + return false; + } + + void + fpResetCauseBits(%(CPU_exec_context)s *cpu) + { + //Read FCSR from FloatRegFile + uint32_t fcsr = cpu->tc->readFloatRegBits(FCSR); + + fcsr = bits(fcsr, 31, 18) << 18 | bits(fcsr, 11, 0); + + //Write FCSR from FloatRegFile + cpu->tc->setFloatRegBits(FCSR, fcsr); + } }}; -def format FloatCompareOp(code, *flags) {{ - code = 'bool cond;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC,cond);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) +def template FloatingPointExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + + //When is the right time to reset cause bits? + //start of every instruction or every cycle? + fpResetCauseBits(xc); + + %(op_decl)s; + %(op_rd)s; + + //Check if any FP operand is a NaN value + if (!fpNanOperands((FPOp*)this, xc, Fd, traceData)) { + %(code)s; + + //Change this code for Full-System/Sycall Emulation + //separation + //---- + //Should Full System-Mode throw a fault here? + //---- + //Check for IEEE 754 FP Exceptions + //fault = fpNanOperands((FPOp*)this, xc, Fd, traceData); + if (!fpInvalidOp((FPOp*)this, xc, Fd, traceData) && + fault == NoFault) + { + %(op_wb)s; + } + } + + return fault; + } }}; -def format FloatCompareWithXcptOp(code, *flags) {{ - code = 'bool cond;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC,cond);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) +// Primary format for float point operate instructions: +def format FloatOp(code, *flags) {{ + iop = InstObjParams(name, Name, 'FPOp', CodeBlock(code), flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) + exec_output = FloatingPointExecute.subst(iop) +}}; + +def format FloatCompareOp(cond_code, *flags) {{ + import sys + + code = 'bool cond;\n' + if '.sf' in cond_code or 'SinglePrecision' in flags: + if 'QnanException' in flags: + code += 'if (isQnan(&Fs.sf, 32) || isQnan(&Ft.sf, 32)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += '\treturn NoFault;' + code += '}\n else ' + code += 'if (isNan(&Fs.sf, 32) || isNan(&Ft.sf, 32)) {\n' + elif '.df' in cond_code or 'DoublePrecision' in flags: + if 'QnanException' in flags: + code += 'if (isQnan(&Fs.df, 64) || isQnan(&Ft.df, 64)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += '\treturn NoFault;' + code += '}\n else ' + code += 'if (isNan(&Fs.df, 64) || isNan(&Ft.df, 64)) {\n' + else: + sys.exit('Decoder Failed: Can\'t Determine Operand Type\n') + + if 'UnorderedTrue' in flags: + code += 'cond = 1;\n' + elif 'UnorderedFalse' in flags: + code += 'cond = 0;\n' + else: + sys.exit('Decoder Failed: Float Compare Instruction Needs A Unordered Flag\n') + + code += '} else {\n' + code += cond_code + '}' + code += 'FCSR = genCCVector(FCSR, CC, cond);\n' + + iop = InstObjParams(name, Name, 'FPCompareOp', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; def format FloatConvertOp(code, *flags) {{ - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) + import sys + + #Determine Source Type + convert = 'fpConvert(' + if '.sf' in code: + code = 'float ' + code + '\n' + convert += 'SINGLE_TO_' + elif '.df' in code: + code = 'double ' + code + '\n' + convert += 'DOUBLE_TO_' + elif '.uw' in code: + code = 'uint32_t ' + code + '\n' + convert += 'WORD_TO_' + elif '.ud' in code: + code = 'uint64_t ' + code + '\n' + convert += 'LONG_TO_' + else: + sys.exit("Error Determining Source Type for Conversion") + + #Determine Destination Type + if 'ToSingle' in flags: + code += 'Fd.uw = ' + convert + 'SINGLE, ' + elif 'ToDouble' in flags: + code += 'Fd.ud = ' + convert + 'DOUBLE, ' + elif 'ToWord' in flags: + code += 'Fd.uw = ' + convert + 'WORD, ' + elif 'ToLong' in flags: + code += 'Fd.ud = ' + convert + 'LONG, ' + else: + sys.exit("Error Determining Destination Type for Conversion") + + #Figure out how to round value + if 'Ceil' in flags: + code += 'ceil(val)); ' + elif 'Floor' in flags: + code += 'floor(val)); ' + elif 'Round' in flags: + code += 'roundFP(val, 0)); ' + elif 'Trunc' in flags: + code += 'truncFP(val));' + else: + code += 'val); ' + + iop = InstObjParams(name, Name, 'FPOp', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format FloatAccOp(code, *flags) {{ + iop = InstObjParams(name, Name, 'FPOp', CodeBlock(code), flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) decode_block = BasicDecode.subst(iop) @@ -76,34 +319,51 @@ def format Float64Op(code, *flags) {{ exec_output = BasicExecute.subst(iop) }}; -def format Float64ConvertOp(code, *flags) {{ - code = 'bool cond;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC,cond);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) -}}; +def format FloatPSCompareOp(cond_code1, cond_code2, *flags) {{ + import sys -def format FloatPSCompareOp(code, *flags) {{ - code = 'bool cond1;\nbool cond2;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC+1, cond1);\n' - code += 'FCSR = makeCCVector(FCSR, CC, cond2);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) -}}; + code = 'bool cond1, cond2;\n' + code += 'bool code_block1, code_block2;\n' + code += 'code_block1 = code_block2 = true;\n' -def format FloatPSCompareWithXcptOp(code, *flags) {{ - code = 'bool cond1;\nbool cond2;\n' + code - code += 'FCSR = makeCCVector(FCSR, CC+1, cond1);\n' - code += 'FCSR = makeCCVector(FCSR, CC, cond2);\n' - iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) + if 'QnanException' in flags: + code += 'if (isQnan(&Fs1.sf, 32) || isQnan(&Ft1.sf, 32)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += 'code_block1 = false;' + code += '}\n' + code += 'if (isQnan(&Fs2.sf, 32) || isQnan(&Ft2.sf, 32)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += 'code_block2 = false;' + code += '}\n' + + code += 'if (code_block1) {' + code += '\tif (isNan(&Fs1.sf, 32) || isNan(&Ft1.sf, 32)) {\n' + if 'UnorderedTrue' in flags: + code += 'cond1 = 1;\n' + elif 'UnorderedFalse' in flags: + code += 'cond1 = 0;\n' + else: + sys.exit('Decoder Failed: Float Compare Instruction Needs A Unordered Flag\n') + code += '} else {\n' + code += cond_code1 + code += 'FCSR = genCCVector(FCSR, CC, cond1);}\n}\n' + + code += 'if (code_block2) {' + code += '\tif (isNan(&Fs2.sf, 32) || isNan(&Ft2.sf, 32)) {\n' + if 'UnorderedTrue' in flags: + code += 'cond2 = 1;\n' + elif 'UnorderedFalse' in flags: + code += 'cond2 = 0;\n' + else: + sys.exit('Decoder Failed: Float Compare Instruction Needs A Unordered Flag\n') + code += '} else {\n' + code += cond_code2 + code += 'FCSR = genCCVector(FCSR, CC, cond2);}\n}' + + iop = InstObjParams(name, Name, 'FPCompareOp', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; + diff --git a/src/arch/mips/isa/formats/int.isa b/src/arch/mips/isa/formats/int.isa index 7d38b9ff5..56a4ec204 100644 --- a/src/arch/mips/isa/formats/int.isa +++ b/src/arch/mips/isa/formats/int.isa @@ -1,11 +1,37 @@ // -*- mode:c++ -*- +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Integer operate instructions // - -//Outputs to decoder.hh output header {{ #include <iostream> using namespace std; @@ -25,6 +51,34 @@ output header {{ std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; }; + + class HiLoOp: public IntOp + { + protected: + + /// Constructor + HiLoOp(const char *mnem, MachInst _machInst, OpClass __opClass) : + IntOp(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class HiLoMiscOp: public HiLoOp + { + protected: + + /// Constructor + HiLoMiscOp(const char *mnem, MachInst _machInst, OpClass __opClass) : + HiLoOp(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class IntImmOp : public MipsStaticInst { protected: @@ -52,6 +106,33 @@ output header {{ }}; +// HiLo<Misc> instruction class execute method template. +// Mainly to get instruction trace data to print out +// correctly +def template HiLoExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(code)s; + + if(fault == NoFault) + { + %(op_wb)s; + //If there are 2 Destination Registers then + //concatenate the values for the traceData + if(traceData && _numDestRegs == 2) { + uint64_t hilo_final_val = (uint64_t)HI << 32 | LO; + traceData->setData(hilo_final_val); + } + } + return fault; + } +}}; + //Outputs to decoder.cc output decoder {{ std::string IntOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const @@ -64,7 +145,7 @@ output decoder {{ // it's generally implicit if (_numDestRegs > 0) { printReg(ss, _destRegIdx[0]); - ss << ","; + ss << ", "; } // just print the first two source regs... if there's @@ -75,13 +156,47 @@ output decoder {{ } if (_numSrcRegs > 1) { - ss << ","; + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + + return ss.str(); + } + + std::string HiLoOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + //Destination Registers are implicit for HI/LO ops + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + + if (_numSrcRegs > 1) { + ss << ", "; printReg(ss, _srcRegIdx[1]); } return ss.str(); } + std::string HiLoMiscOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + if (_numDestRegs > 0 && _destRegIdx[0] < 32) { + printReg(ss, _destRegIdx[0]); + } else if (_numSrcRegs > 0 && _srcRegIdx[0] < 32) { + printReg(ss, _srcRegIdx[0]); + } + + return ss.str(); + } + std::string IntImmOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const { std::stringstream ss; @@ -92,15 +207,15 @@ output decoder {{ printReg(ss, _destRegIdx[0]); } - ss << ","; + ss << ", "; if (_numSrcRegs > 0) { printReg(ss, _srcRegIdx[0]); - ss << ","; + ss << ", "; } if( mnemonic == "lui") - ccprintf(ss, "%08p ", sextImm); + ccprintf(ss, "0x%x ", sextImm); else ss << (int) sextImm; @@ -109,23 +224,47 @@ output decoder {{ }}; -//Used by decoder.isa def format IntOp(code, *opt_flags) {{ - orig_code = code - cblk = CodeBlock(code) - - # Figure out if we are creating a IntImmOp or a IntOp - # by looking at the instruction name - iop = InstObjParams(name, Name, 'IntOp', cblk, opt_flags) - strlen = len(name) - if name[strlen-1] == 'i' or name[strlen-2:] == 'iu': - iop = InstObjParams(name, Name, 'IntImmOp', cblk, opt_flags) - - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = OperateNopCheckDecode.subst(iop) - exec_output = BasicExecute.subst(iop) + iop = InstObjParams(name, Name, 'IntOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = RegNopCheckDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format IntImmOp(code, *opt_flags) {{ + iop = InstObjParams(name, Name, 'IntImmOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = ImmNopCheckDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format HiLoOp(code, *opt_flags) {{ + if '.sd' in code: + code = 'int64_t ' + code + elif '.ud' in code: + code = 'uint64_t ' + code + + code += 'HI = val<63:32>;\n' + code += 'LO = val<31:0>;\n' + + iop = InstObjParams(name, Name, 'HiLoOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = HiLoExecute.subst(iop) }}; +def format HiLoMiscOp(code, *opt_flags) {{ + iop = InstObjParams(name, Name, 'HiLoMiscOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = HiLoExecute.subst(iop) +}}; + + + diff --git a/src/arch/mips/isa/formats/mem.isa b/src/arch/mips/isa/formats/mem.isa index e2afc7252..f03f7becd 100644 --- a/src/arch/mips/isa/formats/mem.isa +++ b/src/arch/mips/isa/formats/mem.isa @@ -1,6 +1,6 @@ // -*- mode:c++ -*- -// Copyright (c) 2003-2005 The Regents of The University of Michigan +// Copyright (c) 2006 The Regents of The University of Michigan // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -25,6 +25,14 @@ // 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. +// +// Authors: Gabe Black +// Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Memory-format instructions +// output header {{ /** @@ -50,14 +58,8 @@ output header {{ StaticInstPtr _memAccPtr = nullStaticInstPtr) : MipsStaticInst(mnem, _machInst, __opClass), memAccessFlags(0), eaCompPtr(_eaCompPtr), memAccPtr(_memAccPtr), - disp(OFFSET) + disp(sext<16>(OFFSET)) { - //If Bit 15 is 1 then Sign Extend - int32_t temp = disp & 0x00008000; - - if (temp > 0) { - disp |= 0xFFFF0000; - } } std::string @@ -69,6 +71,24 @@ output header {{ const StaticInstPtr &memAccInst() const { return memAccPtr; } }; + /** + * Base class for a few miscellaneous memory-format insts + * that don't interpret the disp field + */ + class MemoryNoDisp : public Memory + { + protected: + /// Constructor + MemoryNoDisp(const char *mnem, ExtMachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr = nullStaticInstPtr, + StaticInstPtr _memAccPtr = nullStaticInstPtr) + : Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr) + { + } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; }}; @@ -76,21 +96,20 @@ output decoder {{ std::string Memory::generateDisassembly(Addr pc, const SymbolTable *symtab) const { - return csprintf("%-10s %c%d,%d(r%d)", mnemonic, + return csprintf("%-10s %c%d, %d(r%d)", mnemonic, flags[IsFloating] ? 'f' : 'r', RT, disp, RS); } + std::string + MemoryNoDisp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return csprintf("%-10s %c%d, r%d(r%d)", mnemonic, + flags[IsFloating] ? 'f' : 'r', + flags[IsFloating] ? FD : RD, + RS, RT); + } }}; -def format LoadAddress(code) {{ - iop = InstObjParams(name, Name, 'MemoryDisp32', CodeBlock(code)) - header_output = BasicDeclare.subst(iop) - decoder_output = BasicConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = BasicExecute.subst(iop) -}}; - - def template LoadStoreDeclare {{ /** * Static instruction class for "%(mnemonic)s". @@ -418,27 +437,76 @@ def template StoreCompleteAcc {{ } }}; -// load instructions use Rt as dest, so check for -// Rt == 31 to detect nops -def template LoadNopCheckDecode {{ - { - MipsStaticInst *i = new %(class_name)s(machInst); - if (RT == 0) { - i = makeNop(i); - } - return i; - } + +def template MiscMemAccExecute {{ + Fault %(class_name)s::MemAcc::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + EA = xc->getEA(); + + if (fault == NoFault) { + %(code)s; + } + + return NoFault; + } +}}; + +def template MiscExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + %(memacc_code)s; + } + + return NoFault; + } +}}; + +def template MiscInitiateAcc {{ + Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("Misc instruction does not support split access method!"); + return NoFault; + } +}}; + + +def template MiscCompleteAcc {{ + Fault %(class_name)s::completeAcc(uint8_t *data, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("Misc instruction does not support split access method!"); + + return NoFault; + } }}; def format LoadMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, mem_flags = [], inst_flags = []) {{ (header_output, decoder_output, decode_block, exec_output) = \ LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, - decode_template = LoadNopCheckDecode, + decode_template = ImmNopCheckDecode, exec_template_base = 'Load') }}; - def format StoreMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, mem_flags = [], inst_flags = []) {{ (header_output, decoder_output, decode_block, exec_output) = \ @@ -446,26 +514,69 @@ def format StoreMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, exec_template_base = 'Store') }}; -//FP loads are offloaded to these formats for now ... -def format LoadFloatMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, +def format LoadIndexedMemory(memacc_code, ea_code = {{ EA = Rs + Rt; }}, mem_flags = [], inst_flags = []) {{ (header_output, decoder_output, decode_block, exec_output) = \ LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, - decode_template = BasicDecode, + decode_template = ImmNopCheckDecode, exec_template_base = 'Load') }}; +def format StoreIndexedMemory(memacc_code, ea_code = {{ EA = Rs + Rt; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + exec_template_base = 'Store') +}}; + +def format LoadUnalignedMemory(memacc_code, ea_code = {{ EA = (Rs + disp) & ~3; }}, + mem_flags = [], inst_flags = []) {{ + decl_code = 'uint32_t mem_word = Mem.uw;\n' + decl_code += 'uint32_t unalign_addr = Rs + disp;\n' + decl_code += 'uint32_t byte_offset = unalign_addr & 3;\n' + decl_code += '#if BYTE_ORDER == BIG_ENDIAN\n' + decl_code += '\tbyte_offset ^= 3;\n' + decl_code += '#endif\n' + + memacc_code = decl_code + memacc_code + + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + decode_template = ImmNopCheckDecode, + exec_template_base = 'Load') +}}; -def format StoreFloatMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, +def format StoreUnalignedMemory(memacc_code, ea_code = {{ EA = (Rs + disp) & ~3; }}, mem_flags = [], inst_flags = []) {{ + decl_code = 'uint32_t mem_word = 0;\n' + decl_code += 'uint32_t unaligned_addr = Rs + disp;\n' + decl_code += 'uint32_t byte_offset = unaligned_addr & 3;\n' + decl_code += '#if BYTE_ORDER == BIG_ENDIAN\n' + decl_code += '\tbyte_offset ^= 3;\n' + decl_code += '#endif\n' + decl_code += 'fault = xc->read(EA, (uint32_t&)mem_word, memAccessFlags);\n' + memacc_code = decl_code + memacc_code + '\nMem = mem_word;\n' + (header_output, decoder_output, decode_block, exec_output) = \ LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, exec_template_base = 'Store') }}; +def format Prefetch(ea_code = {{ EA = Rs + disp; }}, + mem_flags = [], pf_flags = [], inst_flags = []) {{ + pf_mem_flags = mem_flags + pf_flags + ['NO_FAULT'] + pf_inst_flags = inst_flags + ['IsMemRef', 'IsLoad', + 'IsDataPrefetch', 'MemReadOp'] + + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, + 'xc->prefetch(EA, memAccessFlags);', + pf_mem_flags, pf_inst_flags, exec_template_base = 'Misc') + +}}; -def format UnalignedStore(memacc_code, postacc_code, - ea_code = {{ EA = Rb + disp; }}, +def format StoreCond(memacc_code, postacc_code, + ea_code = {{ EA = Rs + disp; }}, mem_flags = [], inst_flags = []) {{ (header_output, decoder_output, decode_block, exec_output) = \ LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, diff --git a/src/arch/mips/isa/formats/mt.isa b/src/arch/mips/isa/formats/mt.isa new file mode 100644 index 000000000..96435f8c9 --- /dev/null +++ b/src/arch/mips/isa/formats/mt.isa @@ -0,0 +1,82 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// MT instructions +// + +output header {{ + /** + * Base class for MIPS MT ASE operations. + */ + class MT : public MipsStaticInst + { + protected: + + /// Constructor + MT(const char *mnem, MachInst _machInst, OpClass __opClass) : + MipsStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + //Edit This Template When MT is Implemented + std::string MT::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return "Disassembly of MT instruction\n"; + } +}}; + +def template MTExecute {{ + //Edit This Template When MT is Implemented + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + //Write the resulting state to the execution context + %(op_wb)s; + + //Call into the trap handler with the appropriate fault + return No_Fault; + } +}}; + +// Primary format for integer operate instructions: +def format MipsMT() {{ + code = 'panic(\"Mips MT Is Currently Unimplemented.\");\n' + iop = InstObjParams(name, Name, 'MT', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; diff --git a/src/arch/mips/isa/formats/noop.isa b/src/arch/mips/isa/formats/noop.isa index 2aa4816e3..7f3d313ad 100644 --- a/src/arch/mips/isa/formats/noop.isa +++ b/src/arch/mips/isa/formats/noop.isa @@ -1,5 +1,33 @@ // -*- mode:c++ -*- +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Nop @@ -36,11 +64,7 @@ output decoder {{ std::string Nop::generateDisassembly(Addr pc, const SymbolTable *symtab) const { -#ifdef SS_COMPATIBLE_DISASSEMBLY - return originalDisassembly; -#else - return csprintf("%-10s (%s)", "nop", originalDisassembly); -#endif + return csprintf("%-10s %s", "nop", originalDisassembly); } /// Helper function for decoding nops. Substitute Nop object @@ -49,7 +73,8 @@ output decoder {{ MipsStaticInst * makeNop(MipsStaticInst *inst) { - MipsStaticInst *nop = new Nop(inst->disassemble(0), inst->machInst); + std::string nop_str = "(" + inst->disassemble(0) + ")"; + MipsStaticInst *nop = new Nop(nop_str, inst->machInst); delete inst; return nop; } @@ -63,16 +88,36 @@ output exec {{ } }}; -// integer & FP operate instructions use RT as dest, so check for -// RT == 0 to detect nops -def template OperateNopCheckDecode {{ +// Int & FP operate instructions use RD as dest, so check for +// RD == 0 to detect nops +def template RegNopCheckDecode {{ { MipsStaticInst *i = new %(class_name)s(machInst); + //if (RD == 0) { + //i = makeNop(i); + //} + return i; + } +}}; +def template OperateNopCheckDecode {{ + { + MipsStaticInst *i = new %(class_name)s(machInst); //if (RD == 0) { - // i = makeNop(i); + // i = makeNop(i); //} + return i; + } +}}; +// IntImm & Memory instructions use Rt as dest, so check for +// Rt == 0 to detect nops +def template ImmNopCheckDecode {{ + { + MipsStaticInst *i = new %(class_name)s(machInst); + //if (RT == 0) { + // i = makeNop(i); + // } return i; } }}; @@ -89,6 +134,6 @@ def format BasicOperateWithNopCheck(code, *opt_args) {{ }}; def format Nop() {{ - decode_block = 'return new Nop(\"sll r0,r0,0\",machInst);\n' + decode_block = 'return new Nop(\"\",machInst);\n' }}; diff --git a/src/arch/mips/isa/formats/tlbop.isa b/src/arch/mips/isa/formats/tlbop.isa index f5e4076f2..b974ccbed 100644 --- a/src/arch/mips/isa/formats/tlbop.isa +++ b/src/arch/mips/isa/formats/tlbop.isa @@ -1,3 +1,33 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // TlbOp instructions @@ -30,13 +60,10 @@ output decoder {{ def template TlbOpExecute {{ Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const { - //Call into the trap handler with the appropriate fault - return No_Fault; - } - //Write the resulting state to the execution context %(op_wb)s; + //Call into the trap handler with the appropriate fault return No_Fault; } }}; diff --git a/src/arch/mips/isa/formats/trap.isa b/src/arch/mips/isa/formats/trap.isa index 6884d4fa8..b9066f374 100644 --- a/src/arch/mips/isa/formats/trap.isa +++ b/src/arch/mips/isa/formats/trap.isa @@ -1,3 +1,33 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Trap instructions @@ -23,27 +53,26 @@ output header {{ output decoder {{ std::string Trap::generateDisassembly(Addr pc, const SymbolTable *symtab) const { - return "Disassembly of integer instruction\n"; + return "Disassembly of trap instruction\n"; } }}; def template TrapExecute {{ + //Edit This Template When Traps Are Implemented Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const { - //Call into the trap handler with the appropriate fault - return No_Fault; - } - //Write the resulting state to the execution context %(op_wb)s; + //Call into the trap handler with the appropriate fault return No_Fault; } }}; -// Primary format for integer operate instructions: def format Trap(code, *flags) {{ - code = 'bool cond;\n' + code; + code = 'warn(\"' + code += 'Trap Exception Handler Is Currently Not Implemented.' + code += '\");' iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) header_output = BasicDeclare.subst(iop) decoder_output = BasicConstructor.subst(iop) diff --git a/src/arch/mips/isa/formats/unimp.isa b/src/arch/mips/isa/formats/unimp.isa index 475a88752..03068fa74 100644 --- a/src/arch/mips/isa/formats/unimp.isa +++ b/src/arch/mips/isa/formats/unimp.isa @@ -1,6 +1,7 @@ // -*- mode:c++ -*- -// Copyright (c) 2003-2005 The Regents of The University of Michigan + +// Copyright (c) 2006 The Regents of The University of Michigan // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -25,6 +26,13 @@ // 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Unimplemented instructions +// output header {{ /** @@ -96,11 +104,7 @@ output decoder {{ WarnUnimplemented::generateDisassembly(Addr pc, const SymbolTable *symtab) const { -#ifdef SS_COMPATIBLE_DISASSEMBLY - return csprintf("%-10s", mnemonic); -#else return csprintf("%-10s (unimplemented)", mnemonic); -#endif } }}; @@ -120,7 +124,7 @@ output exec {{ Trace::InstRecord *traceData) const { if (!warned) { - warn("instruction '%s' unimplemented\n", mnemonic); + warn("\tinstruction '%s' unimplemented\n", mnemonic); warned = true; } @@ -139,28 +143,3 @@ def format WarnUnimpl() {{ decode_block = BasicDecodeWithMnemonic.subst(iop) }}; -output header {{ - /** - * Static instruction class for unknown (illegal) instructions. - * These cause simulator termination if they are executed in a - * non-speculative mode. This is a leaf class. - */ - class Unknown : public MipsStaticInst - { - public: - /// Constructor - Unknown(MachInst _machInst) - : MipsStaticInst("unknown", _machInst, No_OpClass) - { - // don't call execute() (which panics) if we're on a - // speculative path - flags[IsNonSpeculative] = true; - } - - %(BasicExecDeclare)s - - std::string - generateDisassembly(Addr pc, const SymbolTable *symtab) const; - }; -}}; - diff --git a/src/arch/mips/isa/formats/unknown.isa b/src/arch/mips/isa/formats/unknown.isa index ba83c007e..70b3901e9 100644 --- a/src/arch/mips/isa/formats/unknown.isa +++ b/src/arch/mips/isa/formats/unknown.isa @@ -1,6 +1,6 @@ // -*- mode:c++ -*- -// Copyright (c) 2003-2005 The Regents of The University of Michigan +// Copyright (c) 2006 The Regents of The University of Michigan // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -25,30 +25,40 @@ // 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. +// +// Authors: Korey Sewell -output header {{ - std::string inst2string(MachInst machInst); -}}; -output decoder {{ - -std::string inst2string(MachInst machInst) -{ - string str = ""; - uint32_t mask = 0x80000000; +//////////////////////////////////////////////////////////////////// +// +// Unknown instructions +// - for(int i=0; i < 32; i++) { - if ((machInst & mask) == 0) { - str += "0"; - } else { - str += "1"; +output header {{ + /** + * Static instruction class for unknown (illegal) instructions. + * These cause simulator termination if they are executed in a + * non-speculative mode. This is a leaf class. + */ + class Unknown : public MipsStaticInst + { + public: + /// Constructor + Unknown(MachInst _machInst) + : MipsStaticInst("unknown", _machInst, No_OpClass) + { + // don't call execute() (which panics) if we're on a + // speculative path + flags[IsNonSpeculative] = true; } - mask = mask >> 1; - } + %(BasicExecDeclare)s - return str; -} + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; +output decoder {{ std::string Unknown::generateDisassembly(Addr pc, const SymbolTable *symtab) const { diff --git a/src/arch/mips/isa/formats/util.isa b/src/arch/mips/isa/formats/util.isa index 615160931..0cc375af3 100644 --- a/src/arch/mips/isa/formats/util.isa +++ b/src/arch/mips/isa/formats/util.isa @@ -1,5 +1,34 @@ // -*- mode:c++ -*- +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Steve Reinhardt +// Korey Sewell + let {{ def LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, postacc_code = '', base_class = 'Memory', @@ -90,7 +119,31 @@ def LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + completeAccTemplate.subst(completeacc_iop)) }}; +output header {{ + std::string inst2string(MachInst machInst); +}}; + +output decoder {{ + +std::string inst2string(MachInst machInst) +{ + string str = ""; + uint32_t mask = 0x80000000; + + for(int i=0; i < 32; i++) { + if ((machInst & mask) == 0) { + str += "0"; + } else { + str += "1"; + } + mask = mask >> 1; + } + + return str; +} + +}}; output exec {{ using namespace MipsISA; @@ -124,6 +177,7 @@ output exec {{ #endif + }}; diff --git a/src/arch/mips/isa/includes.isa b/src/arch/mips/isa/includes.isa index 9c370fbe3..6b5f3c588 100644 --- a/src/arch/mips/isa/includes.isa +++ b/src/arch/mips/isa/includes.isa @@ -1,3 +1,33 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + //////////////////////////////////////////////////////////////////// // // Output include file directives. @@ -16,9 +46,10 @@ output decoder {{ #include "arch/mips/isa_traits.hh" #include "base/cprintf.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" // for Jump::branchTarget() +#include "cpu/thread_context.hh" #include "arch/mips/faults.hh" #include "arch/mips/isa_traits.hh" +#include "arch/mips/utility.hh" #include <math.h> #if defined(linux) @@ -31,6 +62,8 @@ using namespace MipsISA; output exec {{ #include "arch/mips/faults.hh" #include "arch/mips/isa_traits.hh" +#include "arch/mips/utility.hh" + #include <math.h> #if defined(linux) #include <fenv.h> diff --git a/src/arch/mips/isa/main.isa b/src/arch/mips/isa/main.isa index 01d81323e..2d7c63cd5 100644 --- a/src/arch/mips/isa/main.isa +++ b/src/arch/mips/isa/main.isa @@ -1,6 +1,6 @@ // -*- mode:c++ -*- -// Copyright (c) 2003-2005 The Regents of The University of Michigan +// Copyright (c) 2006 The Regents of The University of Michigan // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -25,7 +25,16 @@ // 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// MIPS ISA description file. +// +//////////////////////////////////////////////////////////////////// +//Include the C++ include directives ##include "includes.isa" //////////////////////////////////////////////////////////////////// diff --git a/src/arch/mips/isa/operands.isa b/src/arch/mips/isa/operands.isa index 0f9c74b48..3843dc053 100644 --- a/src/arch/mips/isa/operands.isa +++ b/src/arch/mips/isa/operands.isa @@ -1,3 +1,33 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Korey Sewell + def operand_types {{ 'sb' : ('signed int', 8), 'ub' : ('unsigned int', 8), @@ -23,8 +53,8 @@ def operands {{ 'R2': ('IntReg', 'uw','2', 'IsInteger', 5), #Special Integer Reg operands - 'HI': ('IntReg', 'uw','32', 'IsInteger', 6), - 'LO': ('IntReg', 'uw','33', 'IsInteger', 7), + 'HI': ('IntReg', 'uw','MipsISA::HI', 'IsInteger', 6), + 'LO': ('IntReg', 'uw','MipsISA::LO', 'IsInteger', 7), #Immediate Value operand 'IntImm': ('IntReg', 'uw', 'INTIMM', 'IsInteger', 3), @@ -36,11 +66,11 @@ def operands {{ 'Fr': ('FloatReg', 'sf', 'FR', 'IsFloating', 3), #Special Floating Point Control Reg Operands - 'FIR': ('FloatReg', 'uw', '32', 'IsFloating', 1), - 'FCCR': ('FloatReg', 'uw', '33', 'IsFloating', 2), - 'FEXR': ('FloatReg', 'uw', '34', 'IsFloating', 3), - 'FENR': ('FloatReg', 'uw', '35', 'IsFloating', 3), - 'FCSR': ('FloatReg', 'uw', '36', 'IsFloating', 3), + 'FIR': ('FloatReg', 'uw', 'MipsISA::FIR', 'IsFloating', 1), + 'FCCR': ('FloatReg', 'uw', 'MipsISA::FCCR', 'IsFloating', 2), + 'FEXR': ('FloatReg', 'uw', 'MipsISA::FEXR', 'IsFloating', 3), + 'FENR': ('FloatReg', 'uw', 'MipsISA::FENR', 'IsFloating', 3), + 'FCSR': ('FloatReg', 'uw', 'MipsISA::FCSR', 'IsFloating', 3), #Operands For Paired Singles FP Operations 'Fd1': ('FloatReg', 'sf', 'FD', 'IsFloating', 4), diff --git a/src/arch/mips/isa_traits.cc b/src/arch/mips/isa_traits.cc index 216a6e2ec..85acc4e8c 100644 --- a/src/arch/mips/isa_traits.cc +++ b/src/arch/mips/isa_traits.cc @@ -24,10 +24,13 @@ * 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. + * + * Authors: Gabe Black + * Korey Sewell */ #include "arch/mips/isa_traits.hh" -#include "config/full_system.hh" +//#include "config/full_system.hh" #include "cpu/static_inst.hh" #include "sim/serialize.hh" #include "base/bitfield.hh" @@ -37,111 +40,17 @@ using namespace std; void -MipsISA::copyRegs(ExecContext *src, ExecContext *dest) +MipsISA::copyRegs(ThreadContext *src, ThreadContext *dest) { - /*fpcr = xc->readMiscReg(MipsISA::Fpcr_DepTag); - uniq = xc->readMiscReg(MipsISA::Uniq_DepTag); - lock_flag = xc->readMiscReg(MipsISA::Lock_Flag_DepTag); - lock_addr = xc->readMiscReg(MipsISA::Lock_Addr_DepTag); - -#if FULL_SYSTEM - copyIprs(xc); - #endif*/ + panic("Copy Regs Not Implemented Yet\n"); } void -MipsISA::MiscRegFile::copyMiscRegs(ExecContext *xc) -{ - /*fpcr = xc->readMiscReg(MipsISA::Fpcr_DepTag); - uniq = xc->readMiscReg(MipsISA::Uniq_DepTag); - lock_flag = xc->readMiscReg(MipsISA::Lock_Flag_DepTag); - lock_addr = xc->readMiscReg(MipsISA::Lock_Addr_DepTag); - - #endif*/ -} - -uint64_t -MipsISA::fpConvert(double fp_val, ConvertType cvt_type) -{ - - switch (cvt_type) - { - case SINGLE_TO_DOUBLE: - double sdouble_val = fp_val; - void *sdouble_ptr = &sdouble_val; - uint64_t sdp_bits = *(uint64_t *) sdouble_ptr; - return sdp_bits; - - case SINGLE_TO_WORD: - int32_t sword_val = (int32_t) fp_val; - void *sword_ptr = &sword_val; - uint64_t sword_bits= *(uint32_t *) sword_ptr; - return sword_bits; - - case WORD_TO_SINGLE: - float wfloat_val = fp_val; - void *wfloat_ptr = &wfloat_val; - uint64_t wfloat_bits = *(uint32_t *) wfloat_ptr; - return wfloat_bits; - - case WORD_TO_DOUBLE: - double wdouble_val = fp_val; - void *wdouble_ptr = &wdouble_val; - uint64_t wdp_bits = *(uint64_t *) wdouble_ptr; - return wdp_bits; - - default: - panic("Invalid Floating Point Conversion Type (%d). See \"types.hh\" for List of Conversions\n",cvt_type); - return 0; - } -} - -double -MipsISA::roundFP(double val, int digits) -{ - double digit_offset = pow(10.0,digits); - val = val * digit_offset; - val = val + 0.5; - val = floor(val); - val = val / digit_offset; - return val; -} - -double -MipsISA::truncFP(double val) -{ - int trunc_val = (int) val; - return (double) trunc_val; -} - -bool -MipsISA::getFPConditionCode(uint32_t fcsr_reg, int cc) +MipsISA::MiscRegFile::copyMiscRegs(ThreadContext *tc) { - //uint32_t cc_bits = xc->readFloatReg(35); - return false;//regFile.floatRegfile.getConditionCode(cc); -} - -uint32_t -MipsISA::makeCCVector(uint32_t fcsr, int num, bool val) -{ - int shift = (num == 0) ? 22 : num + 23; - - fcsr = fcsr | (val << shift); - - return fcsr; + panic("Copy Misc. Regs Not Implemented Yet\n"); } -#if FULL_SYSTEM - -static inline Addr -TruncPage(Addr addr) -{ return addr & ~(MipsISA::PageBytes - 1); } - -static inline Addr -RoundPage(Addr addr) -{ return (addr + MipsISA::PageBytes - 1) & ~(MipsISA::PageBytes - 1); } -#endif - void IntRegFile::serialize(std::ostream &os) { @@ -166,12 +75,6 @@ RegFile::serialize(std::ostream &os) SERIALIZE_SCALAR(pc); SERIALIZE_SCALAR(npc); SERIALIZE_SCALAR(nnpc); -#if FULL_SYSTEM - SERIALIZE_ARRAY(palregs, NumIntRegs); - SERIALIZE_ARRAY(ipr, NumInternalProcRegs); - SERIALIZE_SCALAR(intrflag); - SERIALIZE_SCALAR(pal_shadow); -#endif } @@ -187,43 +90,5 @@ RegFile::unserialize(Checkpoint *cp, const std::string §ion) UNSERIALIZE_SCALAR(pc); UNSERIALIZE_SCALAR(npc); UNSERIALIZE_SCALAR(nnpc); -#if FULL_SYSTEM - UNSERIALIZE_ARRAY(palregs, NumIntRegs); - UNSERIALIZE_ARRAY(ipr, NumInternalProcRegs); - UNSERIALIZE_SCALAR(intrflag); - UNSERIALIZE_SCALAR(pal_shadow); -#endif -} - -#if FULL_SYSTEM -void -PTE::serialize(std::ostream &os) -{ - SERIALIZE_SCALAR(tag); - SERIALIZE_SCALAR(ppn); - SERIALIZE_SCALAR(xre); - SERIALIZE_SCALAR(xwe); - SERIALIZE_SCALAR(asn); - SERIALIZE_SCALAR(asma); - SERIALIZE_SCALAR(fonr); - SERIALIZE_SCALAR(fonw); - SERIALIZE_SCALAR(valid); } - - -void -PTE::unserialize(Checkpoint *cp, const std::string §ion) -{ - UNSERIALIZE_SCALAR(tag); - UNSERIALIZE_SCALAR(ppn); - UNSERIALIZE_SCALAR(xre); - UNSERIALIZE_SCALAR(xwe); - UNSERIALIZE_SCALAR(asn); - UNSERIALIZE_SCALAR(asma); - UNSERIALIZE_SCALAR(fonr); - UNSERIALIZE_SCALAR(fonw); - UNSERIALIZE_SCALAR(valid); -} - -#endif //FULL_SYSTEM diff --git a/src/arch/mips/isa_traits.hh b/src/arch/mips/isa_traits.hh index 148c405df..ff994bef9 100644 --- a/src/arch/mips/isa_traits.hh +++ b/src/arch/mips/isa_traits.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Gabe Black + * Korey Sewell */ #ifndef __ARCH_MIPS_ISA_TRAITS_HH__ @@ -45,7 +48,7 @@ class FastCPU; class FullCPU; class Checkpoint; -class ExecContext; +class ThreadContext; namespace LittleEndianGuest {}; @@ -54,12 +57,6 @@ namespace LittleEndianGuest {}; class StaticInst; class StaticInstPtr; -namespace MIPS34K { -int DTB_ASN_ASN(uint64_t reg); -int ITB_ASN_ASN(uint64_t reg); -}; - -#if !FULL_SYSTEM class SyscallReturn { public: template <class T> @@ -92,7 +89,6 @@ class SyscallReturn { uint64_t retval; bool success; }; -#endif namespace MipsISA { @@ -128,20 +124,14 @@ namespace MipsISA /** * Function to insure ISA semantics about 0 registers. - * @param xc The execution context. + * @param tc The thread context. */ - template <class XC> - void zeroRegisters(XC *xc); + template <class TC> + void zeroRegisters(TC *tc); const Addr MaxAddr = (Addr)-1; - void copyRegs(ExecContext *src, ExecContext *dest); - - uint64_t fpConvert(double fp_val, ConvertType cvt_type); - double roundFP(double val, int digits); - double truncFP(double val); - bool getFPConditionCode(uint32_t fcsr_reg, int cc); - uint32_t makeCCVector(uint32_t fcsr, int num, bool val); + void copyRegs(ThreadContext *src, ThreadContext *dest); // Machine operations @@ -188,12 +178,6 @@ namespace MipsISA }; -#if FULL_SYSTEM - -#include "arch/mips/mips34k.hh" - -#endif - using namespace MipsISA; #endif // __ARCH_MIPS_ISA_TRAITS_HH__ diff --git a/src/arch/mips/linux/process.cc b/src/arch/mips/linux/process.cc index ffc5da2e1..17e735527 100644 --- a/src/arch/mips/linux/process.cc +++ b/src/arch/mips/linux/process.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #include "arch/mips/linux/linux.hh" @@ -31,7 +33,7 @@ #include "arch/mips/isa_traits.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/linux.hh" #include "sim/process.hh" @@ -43,9 +45,9 @@ using namespace MipsISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<Linux::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<Linux::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "Linux"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -53,7 +55,7 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "#1 Mon Aug 18 11:32:15 EDT 2003"); strcpy(name->machine, "mips"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } @@ -62,18 +64,18 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, /// different in practice from those used by Tru64 processes. static SyscallReturn sys_getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - // unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); switch (op) { case 45: { // GSI_IEEE_FP_CONTROL - TypedBufferArg<uint64_t> fpcr(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); // I don't think this exactly matches the HW FPCR *fpcr = 0; - fpcr.copyOut(xc->getMemPort()); + fpcr.copyOut(tc->getMemPort()); return 0; } @@ -89,17 +91,17 @@ sys_getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, /// Target sys_setsysinfo() handler. static SyscallReturn sys_setsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned op = xc->getSyscallArg(0); - // unsigned nbytes = xc->getSyscallArg(2); + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); switch (op) { case 14: { // SSI_IEEE_FP_CONTROL - TypedBufferArg<uint64_t> fpcr(xc->getSyscallArg(1)); + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); // I don't think this exactly matches the HW FPCR - fpcr.copyIn(xc->getMemPort()); + fpcr.copyIn(tc->getMemPort()); DPRINTFR(SyscallVerbose, "sys_setsysinfo(SSI_IEEE_FP_CONTROL): " " setting FPCR to 0x%x\n", gtoh(*(uint64_t*)fpcr)); return 0; @@ -133,7 +135,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 14 */ SyscallDesc("mknod", unimplementedFunc), /* 15 */ SyscallDesc("chmod", chmodFunc<MipsLinux>), /* 16 */ SyscallDesc("lchown", chownFunc), - /* 17 */ SyscallDesc("break", obreakFunc), /*obreak*/ + /* 17 */ SyscallDesc("break", obreakFunc), /* 18 */ SyscallDesc("unused#18", unimplementedFunc), /* 19 */ SyscallDesc("lseek", lseekFunc), /* 20 */ SyscallDesc("getpid", getpidFunc), @@ -161,7 +163,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 42 */ SyscallDesc("pipe", unimplementedFunc), /* 43 */ SyscallDesc("times", unimplementedFunc), /* 44 */ SyscallDesc("prof", unimplementedFunc), - /* 45 */ SyscallDesc("brk", obreakFunc),/*openFunc<MipsLinux>*/ + /* 45 */ SyscallDesc("brk", obreakFunc), /* 46 */ SyscallDesc("setgid", unimplementedFunc), /* 47 */ SyscallDesc("getgid", getgidFunc), /* 48 */ SyscallDesc("signal", ignoreFunc), @@ -171,7 +173,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 52 */ SyscallDesc("umount2", unimplementedFunc), /* 53 */ SyscallDesc("lock", unimplementedFunc), /* 54 */ SyscallDesc("ioctl", ioctlFunc<MipsLinux>), - /* 55 */ SyscallDesc("fcntl", unimplementedFunc), + /* 55 */ SyscallDesc("fcntl", fcntlFunc), /* 56 */ SyscallDesc("mpx", unimplementedFunc), /* 57 */ SyscallDesc("setpgid", unimplementedFunc), /* 58 */ SyscallDesc("ulimit", unimplementedFunc), @@ -208,8 +210,8 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 89 */ SyscallDesc("readdir", unimplementedFunc), /* 90 */ SyscallDesc("mmap", mmapFunc<MipsLinux>), /* 91 */ SyscallDesc("munmap",munmapFunc), - /* 92 */ SyscallDesc("truncate", fcntlFunc), - /* 93 */ SyscallDesc("ftruncate", unimplementedFunc), + /* 92 */ SyscallDesc("truncate", truncateFunc), + /* 93 */ SyscallDesc("ftruncate", ftruncateFunc), /* 94 */ SyscallDesc("fchmod", unimplementedFunc), /* 95 */ SyscallDesc("fchown", unimplementedFunc), /* 96 */ SyscallDesc("getpriority", unimplementedFunc), @@ -260,7 +262,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 141 */ SyscallDesc("getdents", unimplementedFunc), /* 142 */ SyscallDesc("newselect", unimplementedFunc), /* 143 */ SyscallDesc("flock", unimplementedFunc), - /* 144 */ SyscallDesc("msync", unimplementedFunc),/*getrlimitFunc<MipsLinux>*/ + /* 144 */ SyscallDesc("msync", unimplementedFunc), /* 145 */ SyscallDesc("readv", unimplementedFunc), /* 146 */ SyscallDesc("writev", writevFunc<MipsLinux>), /* 147 */ SyscallDesc("cacheflush", unimplementedFunc), @@ -336,7 +338,7 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 217 */ SyscallDesc("mincore", unimplementedFunc), /* 218 */ SyscallDesc("madvise", unimplementedFunc), /* 219 */ SyscallDesc("getdents64", unimplementedFunc), - /* 220 */ SyscallDesc("fcntl64", fcntlFunc), + /* 220 */ SyscallDesc("fcntl64", fcntl64Func), /* 221 */ SyscallDesc("reserved#221", unimplementedFunc), /* 222 */ SyscallDesc("gettid", unimplementedFunc), /* 223 */ SyscallDesc("readahead", unimplementedFunc), @@ -412,9 +414,7 @@ MipsLinuxProcess::MipsLinuxProcess(const std::string &name, : MipsLiveProcess(name, objFile, system, stdin_fd, stdout_fd, stderr_fd, argv, envp), Num_Syscall_Descs(sizeof(syscallDescs) / sizeof(SyscallDesc)) -{ - //init_regs->intRegFile[0] = 0; -} +{ } SyscallDesc* MipsLinuxProcess::getDesc(int callnum) diff --git a/src/arch/mips/process.cc b/src/arch/mips/process.cc index 7831551be..031c2030e 100644 --- a/src/arch/mips/process.cc +++ b/src/arch/mips/process.cc @@ -1,3 +1,4 @@ + /* * Copyright (c) 2003-2004 The Regents of The University of Michigan * All rights reserved. @@ -24,74 +25,41 @@ * 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. + * + * Authors: Gabe Black + * Ali Saidi + * Korey Sewell */ #include "arch/mips/isa_traits.hh" #include "arch/mips/process.hh" -#include "arch/mips/linux/process.hh" #include "base/loader/object_file.hh" #include "base/misc.hh" -#include "cpu/exec_context.hh" -#include "sim/builder.hh" +#include "cpu/thread_context.hh" #include "sim/system.hh" using namespace std; using namespace MipsISA; - -MipsLiveProcess * -MipsLiveProcess::create(const std::string &nm, System *system, int stdin_fd, - int stdout_fd, int stderr_fd, std::string executable, - std::vector<std::string> &argv, std::vector<std::string> &envp) -{ - MipsLiveProcess *process = NULL; - - ObjectFile *objFile = createObjectFile(executable); - if (objFile == NULL) { - fatal("Can't load object file %s", executable); - } - - - if (objFile->getArch() != ObjectFile::Mips) - fatal("Object file does not match architecture."); - switch (objFile->getOpSys()) { - case ObjectFile::Linux: - process = new MipsLinuxProcess(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - - default: - fatal("Unknown/unsupported operating system."); - } - - if (process == NULL) - fatal("Unknown error creating process object."); - return process; -} - MipsLiveProcess::MipsLiveProcess(const std::string &nm, ObjectFile *objFile, System *_system, int stdin_fd, int stdout_fd, int stderr_fd, std::vector<std::string> &argv, std::vector<std::string> &envp) : LiveProcess(nm, objFile, _system, stdin_fd, stdout_fd, stderr_fd, argv, envp) { + // Set up stack. On MIPS, stack starts at the top of kuseg + // user address space. MIPS stack grows down from here + stack_base = 0x7FFFFFFF; + + // Set pointer for next thread stack. Reserve 8M for main stack. + next_thread_stack_base = stack_base - (8 * 1024 * 1024); - // XXX all the below need to be updated for SPARC - Ali + // Set up break point (Top of Heap) brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); brk_point = roundUp(brk_point, VMPageSize); - // Set up stack. On Alpha, stack goes below text section. This - // code should get moved to some architecture-specific spot. - stack_base = objFile->textBase() - (409600+4096); - - // Set up region for mmaps. Tru64 seems to start just above 0 and - // grow up from there. + // Set up region for mmaps. For now, start at bottom of kuseg space. mmap_start = mmap_end = 0x10000; - - // Set pointer for next thread stack. Reserve 8M for main stack. - next_thread_stack_base = stack_base - (8 * 1024 * 1024); - } void @@ -99,63 +67,3 @@ MipsLiveProcess::startup() { argsInit(MachineBytes, VMPageSize); } - - - - -BEGIN_DECLARE_SIM_OBJECT_PARAMS(MipsLiveProcess) - - VectorParam<string> cmd; - Param<string> executable; - Param<string> input; - Param<string> output; - VectorParam<string> env; - SimObjectParam<System *> system; - -END_DECLARE_SIM_OBJECT_PARAMS(MipsLiveProcess) - - -BEGIN_INIT_SIM_OBJECT_PARAMS(MipsLiveProcess) - - INIT_PARAM(cmd, "command line (executable plus arguments)"), - INIT_PARAM(executable, "executable (overrides cmd[0] if set)"), - INIT_PARAM(input, "filename for stdin (dflt: use sim stdin)"), - INIT_PARAM(output, "filename for stdout/stderr (dflt: use sim stdout)"), - INIT_PARAM(env, "environment settings"), - INIT_PARAM(system, "system") - -END_INIT_SIM_OBJECT_PARAMS(MipsLiveProcess) - - -CREATE_SIM_OBJECT(MipsLiveProcess) -{ - string in = input; - string out = output; - - // initialize file descriptors to default: same as simulator - int stdin_fd, stdout_fd, stderr_fd; - - if (in == "stdin" || in == "cin") - stdin_fd = STDIN_FILENO; - else - stdin_fd = Process::openInputFile(input); - - if (out == "stdout" || out == "cout") - stdout_fd = STDOUT_FILENO; - else if (out == "stderr" || out == "cerr") - stdout_fd = STDERR_FILENO; - else - stdout_fd = Process::openOutputFile(out); - - stderr_fd = (stdout_fd != STDOUT_FILENO) ? stdout_fd : STDERR_FILENO; - - return MipsLiveProcess::create(getInstanceName(), system, - stdin_fd, stdout_fd, stderr_fd, - (string)executable == "" ? cmd[0] : executable, - cmd, env); -} - - -REGISTER_SIM_OBJECT("MipsLiveProcess", MipsLiveProcess) - - diff --git a/src/arch/mips/process.hh b/src/arch/mips/process.hh index 2a13dc955..b0ef20399 100644 --- a/src/arch/mips/process.hh +++ b/src/arch/mips/process.hh @@ -24,6 +24,10 @@ * 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. + * + * Authors: Gabe Black + * Ali Saidi + * Korey Sewell */ #ifndef __MIPS_PROCESS_HH__ @@ -46,18 +50,6 @@ class MipsLiveProcess : public LiveProcess std::vector<std::string> &envp); void startup(); - - public: - // this function is used to create the LiveProcess object, since - // we can't tell which subclass of LiveProcess to use until we - // open and look at the object file. - static MipsLiveProcess *create(const std::string &nm, - System *_system, - int stdin_fd, int stdout_fd, int stderr_fd, - std::string executable, - std::vector<std::string> &argv, - std::vector<std::string> &envp); - }; diff --git a/src/arch/mips/regfile/float_regfile.hh b/src/arch/mips/regfile/float_regfile.hh index 15c6f97f4..61efbb416 100644 --- a/src/arch/mips/regfile/float_regfile.hh +++ b/src/arch/mips/regfile/float_regfile.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2005 The Regents of The University of Michigan + * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +32,7 @@ #include "arch/mips/types.hh" #include "arch/mips/constants.hh" #include "base/misc.hh" +#include "base/bitfield.hh" #include "config/full_system.hh" #include "sim/byteswap.hh" #include "sim/faults.hh" @@ -60,13 +61,17 @@ namespace MipsISA switch(width) { case SingleWidth: - void *float_ptr = ®s[floatReg]; - return *(float *) float_ptr; + { + void *float_ptr = ®s[floatReg]; + return *(float *) float_ptr; + } case DoubleWidth: - uint64_t double_val = (FloatReg64)regs[floatReg + 1] << 32 | regs[floatReg]; - void *double_ptr = &double_val; - return *(double *) double_ptr; + { + uint64_t double_val = (FloatReg64)regs[floatReg + 1] << 32 | regs[floatReg]; + void *double_ptr = &double_val; + return *(double *) double_ptr; + } default: panic("Attempted to read a %d bit floating point register!", width); @@ -95,23 +100,27 @@ namespace MipsISA } } - Fault setReg(int floatReg, const FloatReg &val, int width) + Fault setReg(int floatReg, const FloatRegVal &val, int width) { - + using namespace std; switch(width) { case SingleWidth: - float temp = val; - void *float_ptr = &temp; - regs[floatReg] = *(FloatReg32 *) float_ptr; - break; + { + float temp = val; + void *float_ptr = &temp; + regs[floatReg] = *(FloatReg32 *) float_ptr; + break; + } case DoubleWidth: - const void *double_ptr = &val; - FloatReg64 temp_double = *(FloatReg64 *) double_ptr; - regs[floatReg + 1] = temp_double >> 32; - regs[floatReg] = temp_double; - break; + { + const void *double_ptr = &val; + FloatReg64 temp_double = *(FloatReg64 *) double_ptr; + regs[floatReg + 1] = bits(temp_double, 63, 32); + regs[floatReg] = bits(temp_double, 31, 0); + break; + } default: panic("Attempted to read a %d bit floating point register!", width); @@ -131,8 +140,8 @@ namespace MipsISA break; case DoubleWidth: - regs[floatReg + 1] = val >> 32; - regs[floatReg] = val; + regs[floatReg + 1] = bits(val, 63, 32); + regs[floatReg] = bits(val, 31, 0); break; default: @@ -146,14 +155,6 @@ namespace MipsISA void unserialize(Checkpoint *cp, const std::string §ion); }; - enum MiscFloatRegNums { - FIR = NumFloatArchRegs, - FCCR, - FEXR, - FENR, - FCSR - }; - } // namespace MipsISA #endif diff --git a/src/arch/mips/regfile/int_regfile.hh b/src/arch/mips/regfile/int_regfile.hh index 3cd87734d..5add1b7be 100644 --- a/src/arch/mips/regfile/int_regfile.hh +++ b/src/arch/mips/regfile/int_regfile.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2005 The Regents of The University of Michigan + * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #ifndef __ARCH_MIPS_INT_REGFILE_HH__ @@ -35,7 +37,7 @@ #include "sim/faults.hh" class Checkpoint; -class ExecContext; +class ThreadContext; class Regfile; namespace MipsISA @@ -63,11 +65,6 @@ namespace MipsISA }; - enum MiscIntRegNums { - HI = NumIntArchRegs, - LO - }; - } // namespace MipsISA #endif diff --git a/src/arch/mips/regfile/misc_regfile.hh b/src/arch/mips/regfile/misc_regfile.hh index 9f054e5f7..87961f97e 100644 --- a/src/arch/mips/regfile/misc_regfile.hh +++ b/src/arch/mips/regfile/misc_regfile.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2005 The Regents of The University of Michigan + * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #ifndef __ARCH_MIPS_MISC_REGFILE_HH__ @@ -34,7 +36,7 @@ #include "sim/faults.hh" class Checkpoint; -class ExecContext; +class ThreadContext; class Regfile; namespace MipsISA @@ -43,26 +45,20 @@ namespace MipsISA protected: uint64_t fpcr; // floating point condition codes - uint64_t uniq; // process-unique register bool lock_flag; // lock flag for LL/SC Addr lock_addr; // lock address for LL/SC MiscReg miscRegFile[NumMiscRegs]; public: - //These functions should be removed once the simplescalar cpu model - //has been replaced. - int getInstAsid(); - int getDataAsid(); - - void copyMiscRegs(ExecContext *xc); + void copyMiscRegs(ThreadContext *tc); MiscReg readReg(int misc_reg) { return miscRegFile[misc_reg]; } - MiscReg readRegWithEffect(int misc_reg, Fault &fault, ExecContext *xc) + MiscReg readRegWithEffect(int misc_reg, Fault &fault, ThreadContext *tc) { return miscRegFile[misc_reg]; } @@ -73,22 +69,11 @@ namespace MipsISA } Fault setRegWithEffect(int misc_reg, const MiscReg &val, - ExecContext *xc) + ThreadContext *tc) { miscRegFile[misc_reg] = val; return NoFault; } -#if FULL_SYSTEM - void clearIprs() { } - - protected: - InternalProcReg ipr[NumInternalProcRegs]; // Internal processor regs - - private: - MiscReg readIpr(int idx, Fault &fault, ExecContext *xc) { } - - Fault setIpr(int idx, uint64_t val, ExecContext *xc) { } -#endif friend class RegFile; }; } // namespace MipsISA diff --git a/src/arch/mips/regfile/regfile.hh b/src/arch/mips/regfile/regfile.hh index e77571b33..a68120299 100644 --- a/src/arch/mips/regfile/regfile.hh +++ b/src/arch/mips/regfile/regfile.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2005 The Regents of The University of Michigan + * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #ifndef __ARCH_MIPS_REGFILE_HH__ @@ -37,7 +39,7 @@ #include "sim/faults.hh" class Checkpoint; -class ExecContext; +class ThreadContext; namespace MipsISA { @@ -62,10 +64,10 @@ namespace MipsISA } MiscReg readMiscRegWithEffect(int miscReg, - Fault &fault, ExecContext *xc) + Fault &fault, ThreadContext *tc) { fault = NoFault; - return miscRegFile.readRegWithEffect(miscReg, fault, xc); + return miscRegFile.readRegWithEffect(miscReg, fault, tc); } Fault setMiscReg(int miscReg, const MiscReg &val) @@ -74,17 +76,17 @@ namespace MipsISA } Fault setMiscRegWithEffect(int miscReg, const MiscReg &val, - ExecContext * xc) + ThreadContext * tc) { - return miscRegFile.setRegWithEffect(miscReg, val, xc); + return miscRegFile.setRegWithEffect(miscReg, val, tc); } - FloatReg readFloatReg(int floatReg) + FloatRegVal readFloatReg(int floatReg) { return floatRegFile.readReg(floatReg,SingleWidth); } - FloatReg readFloatReg(int floatReg, int width) + FloatRegVal readFloatReg(int floatReg, int width) { return floatRegFile.readReg(floatReg,width); } @@ -99,12 +101,12 @@ namespace MipsISA return floatRegFile.readRegBits(floatReg,width); } - Fault setFloatReg(int floatReg, const FloatReg &val) + Fault setFloatReg(int floatReg, const FloatRegVal &val) { return floatRegFile.setReg(floatReg, val, SingleWidth); } - Fault setFloatReg(int floatReg, const FloatReg &val, int width) + Fault setFloatReg(int floatReg, const FloatRegVal &val, int width) { return floatRegFile.setReg(floatReg, val, width); } @@ -166,16 +168,6 @@ namespace MipsISA nnpc = val; } - -#if FULL_SYSTEM - IntReg palregs[NumIntRegs]; // PAL shadow registers - InternalProcReg ipr[NumInternalProcRegs]; // internal processor regs - int intrflag; // interrupt flag - bool pal_shadow; // using pal_shadow registers - inline int instAsid() { return MIPS34K::ITB_ASN_ASN(ipr[IPR_ITB_ASN]); } - inline int dataAsid() { return MIPS34K::DTB_ASN_ASN(ipr[IPR_DTB_ASN]); } -#endif // FULL_SYSTEM - void serialize(std::ostream &os); void unserialize(Checkpoint *cp, const std::string §ion); @@ -187,13 +179,10 @@ namespace MipsISA } }; - void copyRegs(ExecContext *src, ExecContext *dest); + void copyRegs(ThreadContext *src, ThreadContext *dest); - void copyMiscRegs(ExecContext *src, ExecContext *dest); + void copyMiscRegs(ThreadContext *src, ThreadContext *dest); -#if FULL_SYSTEM - void copyIprs(ExecContext *src, ExecContext *dest); -#endif } // namespace MipsISA #endif diff --git a/src/arch/mips/stacktrace.hh b/src/arch/mips/stacktrace.hh index 1d8d97a79..f9e092dbd 100644 --- a/src/arch/mips/stacktrace.hh +++ b/src/arch/mips/stacktrace.hh @@ -24,21 +24,23 @@ * 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. + * + * Authors: Ali Saidi */ -#ifndef __ARCH_ALPHA_STACKTRACE_HH__ -#define __ARCH_ALPHA_STACKTRACE_HH__ +#ifndef __ARCH_MIPS_STACKTRACE_HH__ +#define __ARCH_MIPS_STACKTRACE_HH__ #include "base/trace.hh" #include "cpu/static_inst.hh" -class ExecContext; +class ThreadContext; class StackTrace; class ProcessInfo { private: - ExecContext *xc; + ThreadContext *tc; int thread_info_size; int task_struct_size; @@ -47,7 +49,7 @@ class ProcessInfo int name_off; public: - ProcessInfo(ExecContext *_xc); + ProcessInfo(ThreadContext *_tc); Addr task(Addr ksp) const; int pid(Addr ksp) const; @@ -59,7 +61,7 @@ class StackTrace protected: typedef TheISA::MachInst MachInst; private: - ExecContext *xc; + ThreadContext *tc; std::vector<Addr> stack; private: @@ -68,21 +70,21 @@ class StackTrace bool decodeSave(MachInst inst, int ®, int &disp); bool decodeStack(MachInst inst, int &disp); - void trace(ExecContext *xc, bool is_call); + void trace(ThreadContext *tc, bool is_call); public: StackTrace(); - StackTrace(ExecContext *xc, StaticInstPtr inst); + StackTrace(ThreadContext *tc, StaticInstPtr inst); ~StackTrace(); void clear() { - xc = 0; + tc = 0; stack.clear(); } - bool valid() const { return xc != NULL; } - bool trace(ExecContext *xc, StaticInstPtr inst); + bool valid() const { return tc != NULL; } + bool trace(ThreadContext *tc, StaticInstPtr inst); public: const std::vector<Addr> &getstack() const { return stack; } @@ -104,7 +106,7 @@ class StackTrace }; inline bool -StackTrace::trace(ExecContext *xc, StaticInstPtr inst) +StackTrace::trace(ThreadContext *tc, StaticInstPtr inst) { if (!inst->isCall() && !inst->isReturn()) return false; @@ -112,8 +114,8 @@ StackTrace::trace(ExecContext *xc, StaticInstPtr inst) if (valid()) clear(); - trace(xc, !inst->isReturn()); + trace(tc, !inst->isReturn()); return true; } -#endif // __ARCH_ALPHA_STACKTRACE_HH__ +#endif // __ARCH_MIPS_STACKTRACE_HH__ diff --git a/src/arch/mips/types.hh b/src/arch/mips/types.hh index 4d5fb3456..6330044d9 100644 --- a/src/arch/mips/types.hh +++ b/src/arch/mips/types.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2005 The Regents of The University of Michigan + * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +24,8 @@ * 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. + * + * Authors: Korey Sewell */ #ifndef __ARCH_MIPS_TYPES_HH__ @@ -40,14 +42,15 @@ namespace MipsISA typedef uint32_t IntReg; // floating point register file entry type - typedef double FloatReg; typedef uint32_t FloatReg32; typedef uint64_t FloatReg64; typedef uint64_t FloatRegBits; + typedef double FloatRegVal; + typedef double FloatReg; + // cop-0/cop-1 system control register typedef uint64_t MiscReg; - typedef uint64_t InternalProcReg; typedef union { IntReg intreg; diff --git a/src/arch/mips/utility.cc b/src/arch/mips/utility.cc new file mode 100644 index 000000000..e7455fdbf --- /dev/null +++ b/src/arch/mips/utility.cc @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2003-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell + */ + +#include "arch/mips/isa_traits.hh" +#include "arch/mips/utility.hh" +#include "config/full_system.hh" +#include "cpu/static_inst.hh" +#include "sim/serialize.hh" +#include "base/bitfield.hh" + +using namespace MipsISA; +using namespace std; + +uint64_t +MipsISA::fpConvert(ConvertType cvt_type, double fp_val) +{ + + switch (cvt_type) + { + case SINGLE_TO_DOUBLE: + { + double sdouble_val = fp_val; + void *sdouble_ptr = &sdouble_val; + uint64_t sdp_bits = *(uint64_t *) sdouble_ptr; + return sdp_bits; + } + + case SINGLE_TO_WORD: + { + int32_t sword_val = (int32_t) fp_val; + void *sword_ptr = &sword_val; + uint64_t sword_bits= *(uint32_t *) sword_ptr; + return sword_bits; + } + + case WORD_TO_SINGLE: + { + float wfloat_val = fp_val; + void *wfloat_ptr = &wfloat_val; + uint64_t wfloat_bits = *(uint32_t *) wfloat_ptr; + return wfloat_bits; + } + + case WORD_TO_DOUBLE: + { + double wdouble_val = fp_val; + void *wdouble_ptr = &wdouble_val; + uint64_t wdp_bits = *(uint64_t *) wdouble_ptr; + return wdp_bits; + } + + default: + panic("Invalid Floating Point Conversion Type (%d). See \"types.hh\" for List of Conversions\n",cvt_type); + return 0; + } +} + +double +MipsISA::roundFP(double val, int digits) +{ + double digit_offset = pow(10.0,digits); + val = val * digit_offset; + val = val + 0.5; + val = floor(val); + val = val / digit_offset; + return val; +} + +double +MipsISA::truncFP(double val) +{ + int trunc_val = (int) val; + return (double) trunc_val; +} + +bool +MipsISA::getCondCode(uint32_t fcsr, int cc_idx) +{ + int shift = (cc_idx == 0) ? 23 : cc_idx + 24; + bool cc_val = (fcsr >> shift) & 0x00000001; + return cc_val; +} + +uint32_t +MipsISA::genCCVector(uint32_t fcsr, int cc_num, uint32_t cc_val) +{ + int cc_idx = (cc_num == 0) ? 23 : cc_num + 24; + + fcsr = bits(fcsr, 31, cc_idx + 1) << cc_idx + 1 | + cc_val << cc_idx | + bits(fcsr, cc_idx - 1, 0); + + return fcsr; +} + +uint32_t +MipsISA::genInvalidVector(uint32_t fcsr_bits) +{ + //Set FCSR invalid in "flag" field + int invalid_offset = Invalid + Flag_Field; + fcsr_bits = fcsr_bits | (1 << invalid_offset); + + //Set FCSR invalid in "cause" flag + int cause_offset = Invalid + Cause_Field; + fcsr_bits = fcsr_bits | (1 << cause_offset); + + return fcsr_bits; +} + +bool +MipsISA::isNan(void *val_ptr, int size) +{ + switch (size) + { + case 32: + { + uint32_t val_bits = *(uint32_t *) val_ptr; + return (bits(val_bits, 30, 23) == 0xFF); + } + + case 64: + { + uint64_t val_bits = *(uint64_t *) val_ptr; + return (bits(val_bits, 62, 52) == 0x7FF); + } + + default: + panic("Type unsupported. Size mismatch\n"); + } +} + + +bool +MipsISA::isQnan(void *val_ptr, int size) +{ + switch (size) + { + case 32: + { + uint32_t val_bits = *(uint32_t *) val_ptr; + return (bits(val_bits, 30, 22) == 0x1FE); + } + + case 64: + { + uint64_t val_bits = *(uint64_t *) val_ptr; + return (bits(val_bits, 62, 51) == 0xFFE); + } + + default: + panic("Type unsupported. Size mismatch\n"); + } +} + +bool +MipsISA::isSnan(void *val_ptr, int size) +{ + switch (size) + { + case 32: + { + uint32_t val_bits = *(uint32_t *) val_ptr; + return (bits(val_bits, 30, 22) == 0x1FF); + } + + case 64: + { + uint64_t val_bits = *(uint64_t *) val_ptr; + return (bits(val_bits, 62, 51) == 0xFFF); + } + + default: + panic("Type unsupported. Size mismatch\n"); + } +} diff --git a/src/arch/mips/utility.hh b/src/arch/mips/utility.hh index 5c7dc3ea4..c5c69ddcd 100644 --- a/src/arch/mips/utility.hh +++ b/src/arch/mips/utility.hh @@ -39,6 +39,19 @@ namespace MipsISA { + //Floating Point Utility Functions + uint64_t fpConvert(ConvertType cvt_type, double fp_val); + double roundFP(double val, int digits); + double truncFP(double val); + + bool getCondCode(uint32_t fcsr, int cc); + uint32_t genCCVector(uint32_t fcsr, int num, uint32_t cc_val); + uint32_t genInvalidVector(uint32_t fcsr); + + bool isNan(void *val_ptr, int size); + bool isQnan(void *val_ptr, int size); + bool isSnan(void *val_ptr, int size); }; + #endif diff --git a/src/arch/sparc/SConscript b/src/arch/sparc/SConscript index 7362c9275..e7a8278db 100644 --- a/src/arch/sparc/SConscript +++ b/src/arch/sparc/SConscript @@ -25,6 +25,9 @@ # 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. +# +# Authors: Gabe Black +# Steve Reinhardt import os import sys @@ -47,12 +50,8 @@ base_sources = Split(''' # Full-system sources full_system_sources = Split(''' - tlb.cc - arguments.cc - ev5.cc - osfpal.cc - stacktrace.cc vtophys.cc + ua2005.cc ''') # Syscall emulation (non-full-system) sources diff --git a/src/arch/sparc/faults.cc b/src/arch/sparc/faults.cc index 67a89ab0e..7b7765935 100644 --- a/src/arch/sparc/faults.cc +++ b/src/arch/sparc/faults.cc @@ -24,12 +24,19 @@ * 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. + * + * Authors: Gabe Black + * Kevin Lim */ #include "arch/sparc/faults.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "base/trace.hh" +#if !FULL_SYSTEM +#include "sim/process.hh" +#include "mem/page_table.hh" +#endif namespace SparcISA { @@ -215,40 +222,66 @@ TrapType TrapInstruction::_baseTrapType = 0x100; FaultPriority TrapInstruction::_priority = 16; FaultStat TrapInstruction::_count; +#if !FULL_SYSTEM +FaultName PageTableFault::_name = "page_table_fault"; +TrapType PageTableFault::_trapType = 0x0000; +FaultPriority PageTableFault::_priority = 0; +FaultStat PageTableFault::_count; +#endif + #if FULL_SYSTEM -void SparcFault::invoke(ExecContext * xc) +void SparcFault::invoke(ThreadContext * tc) { - FaultBase::invoke(xc); + FaultBase::invoke(tc); countStat()++; //Use the SPARC trap state machine /*// exception restart address - if (setRestartAddress() || !xc->inPalMode()) - xc->setMiscReg(AlphaISA::IPR_EXC_ADDR, xc->regs.pc); + if (setRestartAddress() || !tc->inPalMode()) + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, tc->regs.pc); if (skipFaultingInstruction()) { // traps... skip faulting instruction. - xc->setMiscReg(AlphaISA::IPR_EXC_ADDR, - xc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, + tc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); } - if (!xc->inPalMode()) - AlphaISA::swap_palshadow(&(xc->regs), true); + if (!tc->inPalMode()) + AlphaISA::swap_palshadow(&(tc->regs), true); - xc->regs.pc = xc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect(); - xc->regs.npc = xc->regs.pc + sizeof(MachInst);*/ + tc->regs.pc = tc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect(); + tc->regs.npc = tc->regs.pc + sizeof(MachInst);*/ } #endif #if !FULL_SYSTEM -void TrapInstruction::invoke(ExecContext * xc) +void TrapInstruction::invoke(ThreadContext * tc) { - xc->syscall(syscall_num); + // Should be handled in ISA. } +void PageTableFault::invoke(ThreadContext *tc) +{ + Process *p = tc->getProcessPtr(); + + // address is higher than the stack region or in the current stack region + if (vaddr > p->stack_base || vaddr > p->stack_min) + FaultBase::invoke(tc); + + // We've accessed the next page + if (vaddr > p->stack_min - PageBytes) { + p->stack_min -= PageBytes; + if (p->stack_base - p->stack_min > 8*1024*1024) + fatal("Over max stack size for one thread\n"); + p->pTable->allocate(p->stack_min, PageBytes); + warn("Increasing stack size by one page."); + } else { + FaultBase::invoke(tc); + } +} #endif } // namespace SparcISA diff --git a/src/arch/sparc/faults.hh b/src/arch/sparc/faults.hh index e8fb8dfc5..b279f4911 100644 --- a/src/arch/sparc/faults.hh +++ b/src/arch/sparc/faults.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Gabe Black + * Kevin Lim */ #ifndef __ALPHA_FAULTS_HH__ @@ -43,7 +46,7 @@ class SparcFault : public FaultBase { public: #if FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif virtual TrapType trapType() = 0; virtual FaultPriority priority() = 0; @@ -80,6 +83,31 @@ class MemAddressNotAligned : public SparcFault bool isAlignmentFault() {return true;} }; +#if !FULL_SYSTEM +class PageTableFault : public SparcFault +{ + private: + Addr vaddr; + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + PageTableFault(Addr va) + : vaddr(va) {} + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} + void invoke(ThreadContext * tc); +}; + +static inline Fault genPageTableFault(Addr va) +{ + return new PageTableFault(va); +} +#endif + static inline Fault genMachineCheckFault() { return new InternalProcessorError; @@ -582,10 +610,11 @@ class TrapInstruction : public EnumeratedFault FaultPriority priority() {return _priority;} FaultStat & countStat() {return _count;} #if !FULL_SYSTEM - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); #endif }; + } // SparcISA namespace #endif // __FAULTS_HH__ diff --git a/src/arch/sparc/isa/decoder.isa b/src/arch/sparc/isa/decoder.isa index c6e05bf6f..fa8832920 100644 --- a/src/arch/sparc/isa/decoder.isa +++ b/src/arch/sparc/isa/decoder.isa @@ -44,13 +44,13 @@ decode OP default Unknown::unknown() format Branch19 { 0x0: bpcci({{ - if(passesCondition(CcrIcc, COND2)) + if(passesCondition(Ccr<3:0>, COND2)) NNPC = xc->readPC() + disp; else handle_annul }}); 0x2: bpccx({{ - if(passesCondition(CcrXcc, COND2)) + if(passesCondition(Ccr<7:4>, COND2)) NNPC = xc->readPC() + disp; else handle_annul @@ -58,7 +58,7 @@ decode OP default Unknown::unknown() } } 0x2: Branch22::bicc({{ - if(passesCondition(CcrIcc, COND2)) + if(passesCondition(Ccr<3:0>, COND2)) NNPC = xc->readPC() + disp; else handle_annul @@ -124,17 +124,17 @@ decode OP default Unknown::unknown() 0x05: andn({{Rd = Rs1.udw & ~Rs2_or_imm13;}}); 0x06: orn({{Rd = Rs1.udw | ~Rs2_or_imm13;}}); 0x07: xnor({{Rd = ~(Rs1.udw ^ Rs2_or_imm13);}}); - 0x08: addc({{Rd = Rs1.sdw + Rs2_or_imm13 + CcrIccC;}}); + 0x08: addc({{Rd = Rs1.sdw + Rs2_or_imm13 + Ccr<0:0>;}}); 0x09: mulx({{Rd = Rs1 * Rs2_or_imm13;}}); 0x0A: umul({{ Rd = Rs1.udw<31:0> * Rs2_or_imm13<31:0>; - YValue = Rd<63:32>; + Y = Rd<63:32>; }}); 0x0B: smul({{ Rd.sdw = Rs1.sdw<31:0> * Rs2_or_imm13<31:0>; - YValue = Rd.sdw; + Y = Rd.sdw; }}); - 0x0C: subc({{Rd.sdw = Rs1.sdw + (~Rs2_or_imm13) + 1 + CcrIccC;}}); + 0x0C: subc({{Rd.sdw = Rs1.sdw + (~Rs2_or_imm13) + 1 + Ccr<0:0>}}); 0x0D: udivx({{ if(Rs2_or_imm13 == 0) fault = new DivisionByZero; else Rd.udw = Rs1.udw / Rs2_or_imm13; @@ -143,7 +143,7 @@ decode OP default Unknown::unknown() if(Rs2_or_imm13 == 0) fault = new DivisionByZero; else { - Rd.udw = ((YValue << 32) | Rs1.udw<31:0>) / Rs2_or_imm13; + Rd.udw = ((Y << 32) | Rs1.udw<31:0>) / Rs2_or_imm13; if(Rd.udw >> 32 != 0) Rd.udw = 0xFFFFFFFF; } @@ -153,7 +153,7 @@ decode OP default Unknown::unknown() fault = new DivisionByZero; else { - Rd.udw = ((int64_t)((YValue << 32) | Rs1.sdw<31:0>)) / Rs2_or_imm13.sdw; + Rd.udw = ((int64_t)((Y << 32) | Rs1.sdw<31:0>)) / Rs2_or_imm13.sdw; if(Rd.udw<63:31> != 0) Rd.udw = 0x7FFFFFFF; else if(Rd.udw<63:> && Rd.udw<62:31> != 0xFFFFFFFF) @@ -187,7 +187,7 @@ decode OP default Unknown::unknown() 0x17: IntOpCcRes::xnorcc({{Rd = ~(Rs1 ^ Rs2_or_imm13);}}); 0x18: addccc({{ int64_t resTemp, val2 = Rs2_or_imm13; - int64_t carryin = CcrIccC; + int64_t carryin = Ccr<0:0>; Rd = resTemp = Rs1 + val2 + carryin;}}, {{(Rs1<31:0> + val2<31:0> + carryin)<32:>}}, {{Rs1<31:> == val2<31:> && val2<31:> != resTemp<31:>}}, @@ -198,16 +198,16 @@ decode OP default Unknown::unknown() 0x1A: umulcc({{ uint64_t resTemp; Rd = resTemp = Rs1.udw<31:0> * Rs2_or_imm13.udw<31:0>; - YValue = resTemp<63:32>;}}, + Y = resTemp<63:32>;}}, {{0}},{{0}},{{0}},{{0}}); 0x1B: smulcc({{ int64_t resTemp; Rd = resTemp = Rs1.sdw<31:0> * Rs2_or_imm13.sdw<31:0>; - YValue = resTemp<63:32>;}}, + Y = resTemp<63:32>;}}, {{0}},{{0}},{{0}},{{0}}); 0x1C: subccc({{ int64_t resTemp, val2 = Rs2_or_imm13; - int64_t carryin = CcrIccC; + int64_t carryin = Ccr<0:0>; Rd = resTemp = Rs1 + ~(val2 + carryin) + 1;}}, {{(~((Rs1<31:0> + (~(val2 + carryin))<31:0> + 1))<32:>)}}, {{Rs1<31:> != val2<31:> && Rs1<31:> != resTemp<31:>}}, @@ -224,7 +224,7 @@ decode OP default Unknown::unknown() if(val2 == 0) fault = new DivisionByZero; else { - resTemp = (uint64_t)((YValue << 32) | Rs1.udw<31:0>) / val2; + resTemp = (uint64_t)((Y << 32) | Rs1.udw<31:0>) / val2; overflow = (resTemp<63:32> != 0); if(overflow) Rd = resTemp = 0xFFFFFFFF; else Rd = resTemp; @@ -240,7 +240,7 @@ decode OP default Unknown::unknown() if(val2 == 0) fault = new DivisionByZero; else { - Rd = resTemp = (int64_t)((YValue << 32) | Rs1.sdw<31:0>) / val2; + Rd = resTemp = (int64_t)((Y << 32) | Rs1.sdw<31:0>) / val2; overflow = (resTemp<63:31> != 0); underflow = (resTemp<63:> && resTemp<62:31> != 0xFFFFFFFF); if(overflow) Rd = resTemp = 0x7FFFFFFF; @@ -295,12 +295,12 @@ decode OP default Unknown::unknown() int32_t multiplier = Rs1<31:0>; int32_t savedLSB = Rs1<0:>; multiplier = multiplier<31:1> | - ((CcrIccN - ^ CcrIccV) << 32); - if(!YValue<0:>) + ((Ccr<3:3> + ^ Ccr<1:1>) << 32); + if(!Y<0:>) multiplicand = 0; Rd = resTemp = multiplicand + multiplier; - YValue = YValue<31:1> | (savedLSB << 31);}}, + Y = Y<31:1> | (savedLSB << 31);}}, {{((multiplicand & 0xFFFFFFFF + multiplier & 0xFFFFFFFF) >> 31)}}, {{multiplicand<31:> == multiplier<31:> && multiplier<31:> != resTemp<31:>}}, {{((multiplicand >> 1) + (multiplier >> 1) + (multiplicand & multiplier & 0x1))<63:>}}, @@ -321,56 +321,27 @@ decode OP default Unknown::unknown() 0x0: sra({{Rd = Rs1.sw >> (I ? SHCNT32 : Rs2<4:0>);}}); 0x1: srax({{Rd = Rs1.sdw >> (I ? SHCNT64 : Rs2<5:0>);}}); } - 0x28: decode RS1 { - 0x0: rdy({{Rd = YValue;}}); - 0x2: rdccr({{Rd = Ccr;}}); - 0x3: rdasi({{Rd = Asi;}}); - 0x4: PrivTick::rdtick({{Rd = Tick;}}); - 0x5: rdpc({{Rd = xc->readPC();}}); - 0x6: rdfprs({{Rd = Fprs;}}); - 0xF: decode I { - 0x0: Nop::membar({{/*Membar isn't needed yet*/}}); - 0x1: Nop::stbar({{/*Stbar isn't needed yet*/}}); - } - } - 0x2A: decode RS1 { - format Priv - { - 0x0: rdprtpc({{ - Rd = xc->readMiscReg(MISCREG_TPC_BASE + Tl); - }}); - 0x1: rdprtnpc({{ - Rd = xc->readMiscReg(MISCREG_TNPC_BASE + Tl); - }}); - 0x2: rdprtstate({{ - Rd = xc->readMiscReg(MISCREG_TSTATE_BASE + Tl); - }}); - 0x3: rdprtt({{ - Rd = xc->readMiscReg(MISCREG_TT_BASE + Tl); - }}); - 0x4: rdprtick({{Rd = Tick;}}); - 0x5: rdprtba({{Rd = Tba;}}); - 0x6: rdprpstate({{Rd = Pstate;}}); - 0x7: rdprtl({{Rd = Tl;}}); - 0x8: rdprpil({{Rd = Pil;}}); - 0x9: rdprcwp({{Rd = Cwp;}}); - 0xA: rdprcansave({{Rd = Cansave;}}); - 0xB: rdprcanrestore({{Rd = Canrestore;}}); - 0xC: rdprcleanwin({{Rd = Cleanwin;}}); - 0xD: rdprotherwin({{Rd = Otherwin;}}); - 0xE: rdprwstate({{Rd = Wstate;}}); - } - //The floating point queue isn't implemented right now. - 0xF: Trap::rdprfq({{fault = new IllegalInstruction;}}); - 0x1F: Priv::rdprver({{Rd = Ver;}}); - } + // XXX might want a format rdipr thing here + 0x28: rdasr({{ + Rd = xc->readMiscRegWithEffect(RS1 + AsrStart, fault); + }}); + 0x29: rdhpr({{ + // XXX Need to protect with format that traps non-priv/priv + // access + Rd = xc->readMiscRegWithEffect(RS1 + HprStart, fault); + }}); + 0x2A: rdpr({{ + // XXX Need to protect with format that traps non-priv + // access + Rd = xc->readMiscRegWithEffect(RS1 + PrStart, fault); + }}); 0x2B: BasicOperate::flushw({{ if(NWindows - 2 - Cansave == 0) { if(Otherwin) - fault = new SpillNOther(WstateOther); + fault = new SpillNOther(Wstate<5:3>); else - fault = new SpillNNormal(WstateNormal); + fault = new SpillNNormal(Wstate<2:0>); } }}); 0x2C: decode MOVCC3 @@ -379,13 +350,13 @@ decode OP default Unknown::unknown() 0x1: decode CC { 0x0: movcci({{ - if(passesCondition(CcrIcc, COND4)) + if(passesCondition(Ccr<3:0>, COND4)) Rd = Rs2_or_imm11; else Rd = Rd; }}); 0x2: movccx({{ - if(passesCondition(CcrXcc, COND4)) + if(passesCondition(Ccr<7:4>, COND4)) Rd = Rs2_or_imm11; else Rd = Rd; @@ -419,49 +390,23 @@ decode OP default Unknown::unknown() 0x6: movrg({{Rd = (Rs1.sdw > 0) ? Rs2_or_imm10 : Rd;}}); 0x7: movrge({{Rd = (Rs1.sdw >= 0) ? Rs2_or_imm10 : Rd;}}); } - 0x30: decode RD { - 0x0: wry({{Y = Rs1 ^ Rs2_or_imm13;}}); - 0x2: wrccr({{Ccr = Rs1 ^ Rs2_or_imm13;}}); - 0x3: wrasi({{Asi = Rs1 ^ Rs2_or_imm13;}}); - 0x6: wrfprs({{Asi = Rs1 ^ Rs2_or_imm13;}}); - 0xF: Trap::sir({{fault = new SoftwareInitiatedReset;}}); - } + 0x30: wrasr({{ + xc->setMiscRegWithEffect(RD + AsrStart, Rs1 ^ Rs2_or_imm13); + }}); 0x31: decode FCN { 0x0: BasicOperate::saved({{/*Boogy Boogy*/}}); 0x1: BasicOperate::restored({{/*Boogy Boogy*/}}); } - 0x32: decode RD { - format Priv - { - 0x0: wrprtpc({{ - xc->setMiscReg(MISCREG_TPC_BASE + Tl, - Rs1 ^ Rs2_or_imm13); - }}); - 0x1: wrprtnpc({{ - xc->setMiscReg(MISCREG_TNPC_BASE + Tl, - Rs1 ^ Rs2_or_imm13); - }}); - 0x2: wrprtstate({{ - xc->setMiscReg(MISCREG_TSTATE_BASE + Tl, - Rs1 ^ Rs2_or_imm13); - }}); - 0x3: wrprtt({{ - xc->setMiscReg(MISCREG_TT_BASE + Tl, - Rs1 ^ Rs2_or_imm13); - }}); - 0x4: wrprtick({{Tick = Rs1 ^ Rs2_or_imm13;}}); - 0x5: wrprtba({{Tba = Rs1 ^ Rs2_or_imm13;}}); - 0x6: wrprpstate({{Pstate = Rs1 ^ Rs2_or_imm13;}}); - 0x7: wrprtl({{Tl = Rs1 ^ Rs2_or_imm13;}}); - 0x8: wrprpil({{Pil = Rs1 ^ Rs2_or_imm13;}}); - 0x9: wrprcwp({{Cwp = Rs1 ^ Rs2_or_imm13;}}); - 0xA: wrprcansave({{Cansave = Rs1 ^ Rs2_or_imm13;}}); - 0xB: wrprcanrestore({{Canrestore = Rs1 ^ Rs2_or_imm13;}}); - 0xC: wrprcleanwin({{Cleanwin = Rs1 ^ Rs2_or_imm13;}}); - 0xD: wrprotherwin({{Otherwin = Rs1 ^ Rs2_or_imm13;}}); - 0xE: wrprwstate({{Wstate = Rs1 ^ Rs2_or_imm13;}}); - } - } + 0x32: wrpr({{ + // XXX Need to protect with format that traps non-priv + // access + xc->setMiscRegWithEffect(RD + PrStart, Rs1 ^ Rs2_or_imm13); + }}); + 0x33: wrhpr({{ + // XXX Need to protect with format that traps non-priv/priv + // access + xc->setMiscRegWithEffect(RD + HprStart, Rs1 ^ Rs2_or_imm13); + }}); 0x34: Trap::fpop1({{fault = new FpDisabled;}}); 0x35: Trap::fpop2({{fault = new FpDisabled;}}); 0x38: Branch::jmpl({{ @@ -492,9 +437,9 @@ decode OP default Unknown::unknown() if(Canrestore == 0) { if(Otherwin) - fault = new FillNOther(WstateOther); + fault = new FillNOther(Wstate<5:3>); else - fault = new FillNNormal(WstateNormal); + fault = new FillNNormal(Wstate<2:0>); } else { @@ -511,7 +456,7 @@ decode OP default Unknown::unknown() 0x3A: decode CC { 0x0: Trap::tcci({{ - if(passesCondition(CcrIcc, COND2)) + if(passesCondition(Ccr<3:0>, COND2)) { int lTrapNum = I ? (Rs1 + SW_TRAP) : (Rs1 + Rs2); DPRINTF(Sparc, "The trap number is %d\n", lTrapNum); @@ -524,7 +469,7 @@ decode OP default Unknown::unknown() } }}); 0x2: Trap::tccx({{ - if(passesCondition(CcrXcc, COND2)) + if(passesCondition(Ccr<7:4>, COND2)) { int lTrapNum = I ? (Rs1 + SW_TRAP) : (Rs1 + Rs2); DPRINTF(Sparc, "The trap number is %d\n", lTrapNum); @@ -545,9 +490,9 @@ decode OP default Unknown::unknown() if(Cansave == 0) { if(Otherwin) - fault = new SpillNOther(WstateOther); + fault = new SpillNOther(Wstate<5:3>); else - fault = new SpillNNormal(WstateNormal); + fault = new SpillNNormal(Wstate<2:0>); Cwp = (Cwp + 2) % NWindows; } else if(Cleanwin - Canrestore == 0) @@ -575,9 +520,9 @@ decode OP default Unknown::unknown() if(Canrestore == 0) { if(Otherwin) - fault = new FillNOther(WstateOther); + fault = new FillNOther(Wstate<5:3>); else - fault = new FillNNormal(WstateNormal); + fault = new FillNNormal(Wstate<2:0>); } else { @@ -594,23 +539,26 @@ decode OP default Unknown::unknown() 0x0: Priv::done({{ if(Tl == 0) return new IllegalInstruction; - Cwp = xc->readMiscReg(MISCREG_TSTATE_CWP_BASE + Tl); - Asi = xc->readMiscReg(MISCREG_TSTATE_ASI_BASE + Tl); - Ccr = xc->readMiscReg(MISCREG_TSTATE_CCR_BASE + Tl); - Pstate = xc->readMiscReg(MISCREG_TSTATE_PSTATE_BASE + Tl); - NPC = xc->readMiscReg(MISCREG_TNPC_BASE + Tl); - NNPC = NPC + 4; + + Cwp = Tstate<4:0>; + Pstate = Tstate<20:8>; + Asi = Tstate<31:24>; + Ccr = Tstate<39:32>; + Gl = Tstate<42:40>; + NPC = Tnpc; + NNPC = Tnpc + 4; Tl = Tl - 1; }}); 0x1: BasicOperate::retry({{ if(Tl == 0) return new IllegalInstruction; - Cwp = xc->readMiscReg(MISCREG_TSTATE_CWP_BASE + Tl); - Asi = xc->readMiscReg(MISCREG_TSTATE_ASI_BASE + Tl); - Ccr = xc->readMiscReg(MISCREG_TSTATE_CCR_BASE + Tl); - Pstate = xc->readMiscReg(MISCREG_TSTATE_PSTATE_BASE + Tl); - NPC = xc->readMiscReg(MISCREG_TPC_BASE + Tl); - NNPC = xc->readMiscReg(MISCREG_TNPC_BASE + Tl); + Cwp = Tstate<4:0>; + Pstate = Tstate<20:8>; + Asi = Tstate<31:24>; + Ccr = Tstate<39:32>; + Gl = Tstate<42:40>; + NPC = Tpc; + NNPC = Tnpc + 4; Tl = Tl - 1; }}); } diff --git a/src/arch/sparc/isa/formats/integerop.isa b/src/arch/sparc/isa/formats/integerop.isa index 1fd87b1d3..1894ce541 100644 --- a/src/arch/sparc/isa/formats/integerop.isa +++ b/src/arch/sparc/isa/formats/integerop.isa @@ -316,22 +316,38 @@ let {{ return (header_output, decoder_output, exec_output, decode_block) calcCcCode = ''' - CcrIccN = (Rd >> 31) & 1; - CcrIccZ = ((Rd & 0xFFFFFFFF) == 0); - CcrXccN = (Rd >> 63) & 1; - CcrXccZ = (Rd == 0); - CcrIccV = %(ivValue)s; - CcrIccC = %(icValue)s; - CcrXccV = %(xvValue)s; - CcrXccC = %(xcValue)s; - DPRINTF(Sparc, "in = %%d\\n", CcrIccN); - DPRINTF(Sparc, "iz = %%d\\n", CcrIccZ); - DPRINTF(Sparc, "xn = %%d\\n", CcrXccN); - DPRINTF(Sparc, "xz = %%d\\n", CcrXccZ); - DPRINTF(Sparc, "iv = %%d\\n", CcrIccV); - DPRINTF(Sparc, "ic = %%d\\n", CcrIccC); - DPRINTF(Sparc, "xv = %%d\\n", CcrXccV); - DPRINTF(Sparc, "xc = %%d\\n", CcrXccC); + uint8_t tmp_ccriccc; + uint8_t tmp_ccriccv; + uint8_t tmp_ccriccz; + uint8_t tmp_ccriccn; + uint8_t tmp_ccrxccc; + uint8_t tmp_ccrxccv; + uint8_t tmp_ccrxccz; + uint8_t tmp_ccrxccn; + + tmp_ccriccn = (Rd >> 31) & 1; + tmp_ccriccz = ((Rd & 0xFFFFFFFF) == 0); + tmp_ccrxccn = (Rd >> 63) & 1; + tmp_ccrxccz = (Rd == 0); + tmp_ccriccv = %(ivValue)s & 1; + tmp_ccriccc = %(icValue)s & 1; + tmp_ccrxccv = %(xvValue)s & 1; + tmp_ccrxccc = %(xcValue)s & 1; + + Ccr = tmp_ccriccc | tmp_ccriccv << 1 | + tmp_ccriccz << 2 | tmp_ccriccn << 3| + tmp_ccrxccc << 4 | tmp_ccrxccv << 5| + tmp_ccrxccz << 6| tmp_ccrxccn << 7; + + + DPRINTF(Sparc, "in = %%d\\n", (uint16_t)tmp_ccriccn); + DPRINTF(Sparc, "iz = %%d\\n", (uint16_t)tmp_ccriccz); + DPRINTF(Sparc, "xn = %%d\\n", (uint16_t)tmp_ccrxccn); + DPRINTF(Sparc, "xz = %%d\\n", (uint16_t)tmp_ccrxccz); + DPRINTF(Sparc, "iv = %%d\\n", (uint16_t)tmp_ccriccv); + DPRINTF(Sparc, "ic = %%d\\n", (uint16_t)tmp_ccriccc); + DPRINTF(Sparc, "xv = %%d\\n", (uint16_t)tmp_ccrxccv); + DPRINTF(Sparc, "xc = %%d\\n", (uint16_t)tmp_ccrxccc); ''' }}; diff --git a/src/arch/sparc/isa/formats/priv.isa b/src/arch/sparc/isa/formats/priv.isa index a8de22c0e..7df59d736 100644 --- a/src/arch/sparc/isa/formats/priv.isa +++ b/src/arch/sparc/isa/formats/priv.isa @@ -51,23 +51,6 @@ output header {{ }; /** - * Base class for user mode "tick" access. - */ - class PrivTick : public SparcStaticInst - { - protected: - // Constructor - PrivTick(const char *mnem, ExtMachInst _machInst, - OpClass __opClass) : - SparcStaticInst(mnem, _machInst, __opClass) - { - } - - std::string generateDisassembly(Addr pc, - const SymbolTable *symtab) const; - }; - - /** * Base class for privelege mode operations with immediates. */ class PrivImm : public Priv @@ -83,21 +66,6 @@ output header {{ int32_t imm; }; - /** - * Base class for user mode "tick" access with immediates. - */ - class PrivTickImm : public PrivTick - { - protected: - // Constructor - PrivTickImm(const char *mnem, ExtMachInst _machInst, - OpClass __opClass) : - PrivTick(mnem, _machInst, __opClass), imm(SIMM13) - { - } - - int32_t imm; - }; }}; output decoder {{ @@ -106,12 +74,6 @@ output decoder {{ { return "Privileged Instruction"; } - - std::string PrivTick::generateDisassembly(Addr pc, - const SymbolTable *symtab) const - { - return "Regular access to Tick"; - } }}; def template PrivExecute {{ @@ -154,16 +116,10 @@ let {{ // Primary format for integer operate instructions: def format Priv(code, *opt_flags) {{ - checkCode = "(!PstatePriv)" + checkCode = "((xc->readMiscReg(PrStart + MISCREG_PSTATE))<2:2>)" (header_output, decoder_output, exec_output, decode_block) = doPrivFormat(code, checkCode, name, Name, opt_flags) }}; -// Primary format for integer operate instructions: -def format PrivTick(code, *opt_flags) {{ - checkCode = "(!PstatePriv && TickNpt)" - (header_output, decoder_output, - exec_output, decode_block) = doPrivFormat(code, - checkCode, name, Name, opt_flags) -}}; + diff --git a/src/arch/sparc/isa/includes.isa b/src/arch/sparc/isa/includes.isa index 762de243a..40afb3722 100644 --- a/src/arch/sparc/isa/includes.isa +++ b/src/arch/sparc/isa/includes.isa @@ -48,7 +48,7 @@ output header {{ output decoder {{ #include "base/cprintf.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" // for Jump::branchTarget() +#include "cpu/thread_context.hh" // for Jump::branchTarget() #include <math.h> #if defined(linux) diff --git a/src/arch/sparc/isa/main.isa b/src/arch/sparc/isa/main.isa index 35167d6b7..14acf54fa 100644 --- a/src/arch/sparc/isa/main.isa +++ b/src/arch/sparc/isa/main.isa @@ -25,7 +25,16 @@ // 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// SPARC ISA description file. +// +//////////////////////////////////////////////////////////////////// +//Include the C++ include directives ##include "includes.isa" //////////////////////////////////////////////////////////////////// diff --git a/src/arch/sparc/isa/operands.isa b/src/arch/sparc/isa/operands.isa index a89034e30..9e5c783e8 100644 --- a/src/arch/sparc/isa/operands.isa +++ b/src/arch/sparc/isa/operands.isa @@ -63,83 +63,26 @@ def operands {{ 'R1': ('IntReg', 'udw', '1', None, 7), 'R15': ('IntReg', 'udw', '15', 'IsInteger', 8), 'R16': ('IntReg', 'udw', '16', None, 9), + # Control registers - 'Pstate': ('ControlReg', 'udw', 'MISCREG_PSTATE', None, 1), - 'PstateAg': ('ControlReg', 'udw', 'MISCREG_PSTATE_AG', None, 2), - 'PstateIe': ('ControlReg', 'udw', 'MISCREG_PSTATE_IE', None, 3), - 'PstatePriv': ('ControlReg', 'udw', 'MISCREG_PSTATE_PRIV', None, 4), - 'PstateAm': ('ControlReg', 'udw', 'MISCREG_PSTATE_AM', None, 5), - 'PstatePef': ('ControlReg', 'udw', 'MISCREG_PSTATE_PEF', None, 6), - 'PstateRed': ('ControlReg', 'udw', 'MISCREG_PSTATE_RED', None, 7), - 'PstateMm': ('ControlReg', 'udw', 'MISCREG_PSTATE_MM', None, 8), - 'PstateTle': ('ControlReg', 'udw', 'MISCREG_PSTATE_TLE', None, 9), - 'PstateCle': ('ControlReg', 'udw', 'MISCREG_PSTATE_CLE', None, 10), - 'Tba': ('ControlReg', 'udw', 'MISCREG_TBA', None, 11), 'Y': ('ControlReg', 'udw', 'MISCREG_Y', None, 12), - 'YValue': ('ControlReg', 'udw', 'MISCREG_Y_VALUE', None, 13), - 'Pil': ('ControlReg', 'udw', 'MISCREG_PIL', None, 14), - 'Cwp': ('ControlReg', 'udw', 'MISCREG_CWP', None, 15), - #'Tt': ('ControlReg', 'udw', 'MISCREG_TT_BASE + tl', None, 16), 'Ccr': ('ControlReg', 'udw', 'MISCREG_CCR', None, 17), - 'CcrIcc': ('ControlReg', 'udw', 'MISCREG_CCR_ICC', None, 18), - 'CcrIccC': ('ControlReg', 'udw', 'MISCREG_CCR_ICC_C', None, 19), - 'CcrIccV': ('ControlReg', 'udw', 'MISCREG_CCR_ICC_V', None, 20), - 'CcrIccZ': ('ControlReg', 'udw', 'MISCREG_CCR_ICC_Z', None, 21), - 'CcrIccN': ('ControlReg', 'udw', 'MISCREG_CCR_ICC_N', None, 22), - 'CcrXcc': ('ControlReg', 'udw', 'MISCREG_CCR_XCC', None, 23), - 'CcrXccC': ('ControlReg', 'udw', 'MISCREG_CCR_XCC_C', None, 22), - 'CcrXccV': ('ControlReg', 'udw', 'MISCREG_CCR_XCC_V', None, 23), - 'CcrXccZ': ('ControlReg', 'udw', 'MISCREG_CCR_XCC_Z', None, 24), - 'CcrXccN': ('ControlReg', 'udw', 'MISCREG_CCR_XCC_N', None, 25), 'Asi': ('ControlReg', 'udw', 'MISCREG_ASI', None, 26), + + 'Tpc': ('ControlReg', 'udw', 'MISCREG_TPC', None, 28), + 'Tnpc': ('ControlReg', 'udw', 'MISCREG_TNPC', None, 28), + 'Tstate': ('ControlReg', 'udw', 'MISCREG_TSTATE', None, 28), + 'Pstate': ('ControlReg', 'udw', 'MISCREG_PSTATE', None, 1), 'Tl': ('ControlReg', 'udw', 'MISCREG_TL', None, 27), - #'Tpc': ('ControlReg', 'udw', 'MISCREG_TPC', None, 28), - 'Tick': ('ControlReg', 'udw', 'MISCREG_TICK', None, 29), - 'TickCounter': ('ControlReg', 'udw', 'MISCREG_TICK_COUNTER', None, 32), - 'TickNpt': ('ControlReg', 'udw', 'MISCREG_TICK_NPT', None, 33), + + 'Cwp': ('ControlReg', 'udw', 'MISCREG_CWP', None, 15), 'Cansave': ('ControlReg', 'udw', 'MISCREG_CANSAVE', None, 34), 'Canrestore': ('ControlReg', 'udw', 'MISCREG_CANRESTORE', None, 35), - 'Otherwin': ('ControlReg', 'udw', 'MISCREG_OTHERWIN', None, 36), 'Cleanwin': ('ControlReg', 'udw', 'MISCREG_CLEANWIN', None, 37), + 'Otherwin': ('ControlReg', 'udw', 'MISCREG_OTHERWIN', None, 36), 'Wstate': ('ControlReg', 'udw', 'MISCREG_WSTATE', None, 38), - 'WstateNormal': ('ControlReg', 'udw', 'MISCREG_WSTATE_NORMAL', None,39), - 'WstateOther': ('ControlReg', 'udw', 'MISCREG_WSTATE_OTHER', None, 40), - 'Ver': ('ControlReg', 'udw', 'MISCREG_VER', None, 41), - 'VerMaxwin': ('ControlReg', 'udw', 'MISCREG_VER_MAXWIN', None, 42), - 'VerMaxtl': ('ControlReg', 'udw', 'MISCREG_VER_MAXTL', None, 43), - 'VerMask': ('ControlReg', 'udw', 'MISCREG_VER_MASK', None, 44), - 'VerImpl': ('ControlReg', 'udw', 'MISCREG_VER_MASK', None, 45), - 'VerManuf': ('ControlReg', 'udw', 'MISCREG_VER_MANUF', None, 46), - 'Fsr': ('ControlReg', 'udw', 'MISCREG_FSR', None, 47), - 'FsrCexc': ('ControlReg', 'udw', 'MISCREG_FSR_CEXC', None, 48), - 'FsrCexcNxc': ('ControlReg', 'udw', 'MISCREG_FSR_CEXC_NXC', None, 49), - 'FsrCexcDzc': ('ControlReg', 'udw', 'MISCREG_FSR_CEXC_DZC', None, 50), - 'FsrCexcUfc': ('ControlReg', 'udw', 'MISCREG_FSR_CEXC_UFC', None, 51), - 'FsrCexcOfc': ('ControlReg', 'udw', 'MISCREG_FSR_CEXC_OFC', None, 52), - 'FsrCexcNvc': ('ControlReg', 'udw', 'MISCREG_FSR_CEXC_NVC', None, 53), - 'FsrAexc': ('ControlReg', 'udw', 'MISCREG_FSR_AEXC', None, 54), - 'FsrAexcNxc': ('ControlReg', 'udw', 'MISCREG_FSR_AEXC_NXC', None, 55), - 'FsrAexcDzc': ('ControlReg', 'udw', 'MISCREG_FSR_AEXC_DZC', None, 56), - 'FsrAexcUfc': ('ControlReg', 'udw', 'MISCREG_FSR_AEXC_UFC', None, 57), - 'FsrAexcOfc': ('ControlReg', 'udw', 'MISCREG_FSR_AEXC_OFC', None, 58), - 'FsrAexcNvc': ('ControlReg', 'udw', 'MISCREC_FSR_AEXC_NVC', None, 59), - 'FsrFcc0': ('ControlReg', 'udw', 'MISCREG_FSR_FCC0', None, 60), - 'FsrQne': ('ControlReg', 'udw', 'MISCREG_FSR_QNE', None, 61), - 'FsrFtt': ('ControlReg', 'udw', 'MISCREG_FSR_FTT', None, 62), - 'FsrVer': ('ControlReg', 'udw', 'MISCREG_FSR_VER', None, 63), - 'FsrNs': ('ControlReg', 'udw', 'MISCREG_FSR_NS', None, 64), - 'FsrTem': ('ControlReg', 'udw', 'MISCREG_FSR_TEM', None, 65), - 'FsrTemNxm': ('ControlReg', 'udw', 'MISCREG_FSR_TEM_NXM', None, 66), - 'FsrTemDzm': ('ControlReg', 'udw', 'MISCREG_FSR_TEM_DZM', None, 67), - 'FsrTemUfm': ('ControlReg', 'udw', 'MISCREG_FSR_TEM_UFM', None, 68), - 'FsrTemOfm': ('ControlReg', 'udw', 'MISCREG_FSR_TEM_OFM', None, 69), - 'FsrTemNvm': ('ControlReg', 'udw', 'MISCREG_FSR_TEM_NVM', None, 70), - 'FsrRd': ('ControlReg', 'udw', 'MISCREG_FSR_RD', None, 71), - 'FsrFcc1': ('ControlReg', 'udw', 'MISCREG_FSR_FCC1', None, 72), - 'FsrFcc2': ('ControlReg', 'udw', 'MISCREG_FSR_FCC2', None, 73), - 'FsrFcc3': ('ControlReg', 'udw', 'MISCREG_FSR_FCC3', None, 74), - 'Fprs': ('ControlReg', 'udw', 'MISCREG_FPRS', None, 75), - 'FprsDl': ('ControlReg', 'udw', 'MISCREG_FPRS_DL', None, 76), - 'FprsDu': ('ControlReg', 'udw', 'MISCREG_FPRS_DU', None, 77), - 'FprsFef': ('ControlReg', 'udw', 'MISCREG_FPRS_FEF', None, 78) + 'Gl': ('ControlReg', 'udw', 'MISCREG_GL', None, 12), + + 'Fsr': ('ControlReg', 'udw', 'MISCREG_FSR', None, 47) + }}; diff --git a/src/arch/sparc/isa_traits.hh b/src/arch/sparc/isa_traits.hh index 453d14664..346f7b730 100644 --- a/src/arch/sparc/isa_traits.hh +++ b/src/arch/sparc/isa_traits.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Korey Sewell + * Gabe Black */ #ifndef __ARCH_SPARC_ISA_TRAITS_HH__ @@ -33,7 +36,7 @@ #include "config/full_system.hh" #include "sim/host.hh" -class ExecContext; +class ThreadContext; class FastCPU; //class FullCPU; class Checkpoint; @@ -80,6 +83,9 @@ class SyscallReturn #endif +#if FULL_SYSTEM +#include "arch/sparc/isa_fullsys_traits.hh" +#endif namespace SparcISA { @@ -172,12 +178,12 @@ namespace SparcISA // indicate success/failure in reg the carry bit of the ccr // and put the return value itself in the standard return value reg (). if (return_value.successful()) { - // no error - regs->setMiscReg(MISCREG_CCR_XCC_C, 0); + // no error, clear XCC.C + regs->setMiscReg(MISCREG_CCR, regs->readMiscReg(MISCREG_CCR) & 0xEF); regs->setIntReg(ReturnValueReg, return_value.value()); } else { - // got an error, return details - regs->setMiscReg(MISCREG_CCR_XCC_C, 1); + // got an error, set XCC.C + regs->setMiscReg(MISCREG_CCR, regs->readMiscReg(MISCREG_CCR) | 0x10); regs->setIntReg(ReturnValueReg, return_value.value()); } } diff --git a/src/arch/sparc/linux/linux.cc b/src/arch/sparc/linux/linux.cc index c7ed29358..ae6ffbc2a 100644 --- a/src/arch/sparc/linux/linux.cc +++ b/src/arch/sparc/linux/linux.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Gabe Black */ #include "arch/sparc/linux/linux.hh" diff --git a/src/arch/sparc/linux/linux.hh b/src/arch/sparc/linux/linux.hh index 9cde5bb9c..926c2cb77 100644 --- a/src/arch/sparc/linux/linux.hh +++ b/src/arch/sparc/linux/linux.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Gabe Black */ #ifndef __ARCH_SPARC_LINUX_LINUX_HH__ diff --git a/src/arch/sparc/linux/process.cc b/src/arch/sparc/linux/process.cc index 71be6a83a..e27255e67 100644 --- a/src/arch/sparc/linux/process.cc +++ b/src/arch/sparc/linux/process.cc @@ -24,6 +24,10 @@ * 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. + * + * Authors: Steve Reinhardt + * Gabe Black + * Ali Saidi */ #include "arch/sparc/isa_traits.hh" @@ -31,7 +35,7 @@ #include "arch/sparc/regfile.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/linux.hh" #include "sim/process.hh" @@ -44,9 +48,9 @@ using namespace SparcISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<Linux::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<Linux::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "Linux"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -54,40 +58,40 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "#1 Mon Aug 18 11:32:15 EDT 2003"); strcpy(name->machine, "sparc"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } SyscallReturn SparcISA::getresuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc) + Process *p, ThreadContext *tc) { const IntReg id = htog(100); - Addr ruid = xc->getSyscallArg(0); - Addr euid = xc->getSyscallArg(1); - Addr suid = xc->getSyscallArg(2); + Addr ruid = tc->getSyscallArg(0); + Addr euid = tc->getSyscallArg(1); + Addr suid = tc->getSyscallArg(2); //Handle the EFAULT case //Set the ruid if(ruid) { BufferArg ruidBuff(ruid, sizeof(IntReg)); memcpy(ruidBuff.bufferPtr(), &id, sizeof(IntReg)); - ruidBuff.copyOut(xc->getMemPort()); + ruidBuff.copyOut(tc->getMemPort()); } //Set the euid if(euid) { BufferArg euidBuff(euid, sizeof(IntReg)); memcpy(euidBuff.bufferPtr(), &id, sizeof(IntReg)); - euidBuff.copyOut(xc->getMemPort()); + euidBuff.copyOut(tc->getMemPort()); } //Set the suid if(suid) { BufferArg suidBuff(suid, sizeof(IntReg)); memcpy(suidBuff.bufferPtr(), &id, sizeof(IntReg)); - suidBuff.copyOut(xc->getMemPort()); + suidBuff.copyOut(tc->getMemPort()); } return 0; } diff --git a/src/arch/sparc/linux/process.hh b/src/arch/sparc/linux/process.hh index 23ce66d02..f4819ba84 100644 --- a/src/arch/sparc/linux/process.hh +++ b/src/arch/sparc/linux/process.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __SPARC_LINUX_PROCESS_HH__ @@ -59,7 +61,7 @@ class SparcLinuxProcess : public SparcLiveProcess }; SyscallReturn getresuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); } // namespace SparcISA #endif // __ALPHA_LINUX_PROCESS_HH__ diff --git a/src/arch/sparc/process.cc b/src/arch/sparc/process.cc index 250c1bec4..75f01e038 100644 --- a/src/arch/sparc/process.cc +++ b/src/arch/sparc/process.cc @@ -24,60 +24,23 @@ * 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. + * + * Authors: Gabe Black + * Ali Saidi */ #include "arch/sparc/isa_traits.hh" #include "arch/sparc/process.hh" -#include "arch/sparc/linux/process.hh" -#include "arch/sparc/solaris/process.hh" #include "base/loader/object_file.hh" #include "base/misc.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/page_table.hh" #include "mem/translating_port.hh" -#include "sim/builder.hh" #include "sim/system.hh" using namespace std; using namespace SparcISA; -SparcLiveProcess * -SparcLiveProcess::create(const std::string &nm, System *system, int stdin_fd, - int stdout_fd, int stderr_fd, std::string executable, - std::vector<std::string> &argv, std::vector<std::string> &envp) -{ - SparcLiveProcess *process = NULL; - - ObjectFile *objFile = createObjectFile(executable); - if (objFile == NULL) { - fatal("Can't load object file %s", executable); - } - - - if (objFile->getArch() != ObjectFile::SPARC) - fatal("Object file with arch %x does not match architecture %x.", - objFile->getArch(), ObjectFile::SPARC); - switch (objFile->getOpSys()) { - case ObjectFile::Linux: - process = new SparcLinuxProcess(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - - - case ObjectFile::Solaris: - process = new SparcSolarisProcess(nm, objFile, system, - stdin_fd, stdout_fd, stderr_fd, - argv, envp); - break; - default: - fatal("Unknown/unsupported operating system."); - } - - if (process == NULL) - fatal("Unknown error creating process object."); - return process; -} SparcLiveProcess::SparcLiveProcess(const std::string &nm, ObjectFile *objFile, System *_system, int stdin_fd, int stdout_fd, int stderr_fd, @@ -110,36 +73,27 @@ SparcLiveProcess::startup() //From the SPARC ABI //The process runs in user mode - execContexts[0]->setMiscRegWithEffect(MISCREG_PSTATE_PRIV, 0); - //Interrupts are enabled - execContexts[0]->setMiscRegWithEffect(MISCREG_PSTATE_IE, 1); - //Round to nearest - execContexts[0]->setMiscRegWithEffect(MISCREG_FSR_RD, 0); - //Floating point traps are not enabled - execContexts[0]->setMiscRegWithEffect(MISCREG_FSR_TEM, 0); - //Turn non standard mode off - execContexts[0]->setMiscRegWithEffect(MISCREG_FSR_NS, 0); - //The floating point queue is empty - execContexts[0]->setMiscRegWithEffect(MISCREG_FSR_QNE, 0); - //There are no accrued eexecContext[0]eptions - execContexts[0]->setMiscRegWithEffect(MISCREG_FSR_AEXC, 0); - //There are no current eexecContext[0]eptions - execContexts[0]->setMiscRegWithEffect(MISCREG_FSR_CEXC, 0); + threadContexts[0]->setMiscRegWithEffect(MISCREG_PSTATE, 0x02); + + //Setup default FP state + threadContexts[0]->setMiscReg(MISCREG_FSR, 0); + threadContexts[0]->setMiscReg(MISCREG_TICK, 0); + // /* * Register window management registers */ //No windows contain info from other programs - execContexts[0]->setMiscRegWithEffect(MISCREG_OTHERWIN, 0); + threadContexts[0]->setMiscRegWithEffect(MISCREG_OTHERWIN, 0); //There are no windows to pop - execContexts[0]->setMiscRegWithEffect(MISCREG_CANRESTORE, 0); + threadContexts[0]->setMiscRegWithEffect(MISCREG_CANRESTORE, 0); //All windows are available to save into - execContexts[0]->setMiscRegWithEffect(MISCREG_CANSAVE, NWindows - 2); + threadContexts[0]->setMiscRegWithEffect(MISCREG_CANSAVE, NWindows - 2); //All windows are "clean" - execContexts[0]->setMiscRegWithEffect(MISCREG_CLEANWIN, NWindows); + threadContexts[0]->setMiscRegWithEffect(MISCREG_CLEANWIN, NWindows); //Start with register window 0 - execContexts[0]->setMiscRegWithEffect(MISCREG_CWP, 0); + threadContexts[0]->setMiscRegWithEffect(MISCREG_CWP, 0); } m5_auxv_t buildAuxVect(int64_t type, int64_t val) @@ -317,72 +271,14 @@ SparcLiveProcess::argsInit(int intSize, int pageSize) initVirtMem->writeBlob(argc_base, (uint8_t*)&guestArgc, intSize); - execContexts[0]->setIntReg(ArgumentReg0, argc); - execContexts[0]->setIntReg(ArgumentReg1, argv_array_base); - execContexts[0]->setIntReg(StackPointerReg, stack_min - StackBias); + threadContexts[0]->setIntReg(ArgumentReg0, argc); + threadContexts[0]->setIntReg(ArgumentReg1, argv_array_base); + threadContexts[0]->setIntReg(StackPointerReg, stack_min - StackBias); Addr prog_entry = objFile->entryPoint(); - execContexts[0]->setPC(prog_entry); - execContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); - execContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); + threadContexts[0]->setPC(prog_entry); + threadContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); + threadContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); // num_processes++; } - - -BEGIN_DECLARE_SIM_OBJECT_PARAMS(SparcLiveProcess) - - VectorParam<string> cmd; - Param<string> executable; - Param<string> input; - Param<string> output; - VectorParam<string> env; - SimObjectParam<System *> system; - -END_DECLARE_SIM_OBJECT_PARAMS(SparcLiveProcess) - - -BEGIN_INIT_SIM_OBJECT_PARAMS(SparcLiveProcess) - - INIT_PARAM(cmd, "command line (executable plus arguments)"), - INIT_PARAM(executable, "executable (overrides cmd[0] if set)"), - INIT_PARAM(input, "filename for stdin (dflt: use sim stdin)"), - INIT_PARAM(output, "filename for stdout/stderr (dflt: use sim stdout)"), - INIT_PARAM(env, "environment settings"), - INIT_PARAM(system, "system") - -END_INIT_SIM_OBJECT_PARAMS(SparcLiveProcess) - - -CREATE_SIM_OBJECT(SparcLiveProcess) -{ - string in = input; - string out = output; - - // initialize file descriptors to default: same as simulator - int stdin_fd, stdout_fd, stderr_fd; - - if (in == "stdin" || in == "cin") - stdin_fd = STDIN_FILENO; - else - stdin_fd = Process::openInputFile(input); - - if (out == "stdout" || out == "cout") - stdout_fd = STDOUT_FILENO; - else if (out == "stderr" || out == "cerr") - stdout_fd = STDERR_FILENO; - else - stdout_fd = Process::openOutputFile(out); - - stderr_fd = (stdout_fd != STDOUT_FILENO) ? stdout_fd : STDERR_FILENO; - - return SparcLiveProcess::create(getInstanceName(), system, - stdin_fd, stdout_fd, stderr_fd, - (string)executable == "" ? cmd[0] : executable, - cmd, env); -} - - -REGISTER_SIM_OBJECT("SparcLiveProcess", SparcLiveProcess) - - diff --git a/src/arch/sparc/process.hh b/src/arch/sparc/process.hh index c177f20a5..7ba8d7109 100644 --- a/src/arch/sparc/process.hh +++ b/src/arch/sparc/process.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Gabe Black + * Ali Saidi */ #ifndef __SPARC_PROCESS_HH__ @@ -62,15 +65,6 @@ class SparcLiveProcess : public LiveProcess void startup(); public: - // this function is used to create the LiveProcess object, since - // we can't tell which subclass of LiveProcess to use until we - // open and look at the object file. - static SparcLiveProcess *create(const std::string &nm, - System *_system, - int stdin_fd, int stdout_fd, int stderr_fd, - std::string executable, - std::vector<std::string> &argv, - std::vector<std::string> &envp); void argsInit(int intSize, int pageSize); diff --git a/src/arch/sparc/regfile.hh b/src/arch/sparc/regfile.hh index 96f36eb91..cbeb3c7b9 100644 --- a/src/arch/sparc/regfile.hh +++ b/src/arch/sparc/regfile.hh @@ -24,14 +24,19 @@ * 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. + * + * Authors: Gabe Black + * Ali Saidi */ #ifndef __ARCH_SPARC_REGFILE_HH__ #define __ARCH_SPARC_REGFILE_HH__ +#include "arch/sparc/exceptions.hh" #include "arch/sparc/faults.hh" #include "base/trace.hh" #include "sim/byteswap.hh" +#include "cpu/cpuevent.hh" #include "sim/host.hh" class Checkpoint; @@ -42,7 +47,7 @@ namespace SparcISA typedef uint8_t RegIndex; // MAXTL - maximum trap level - const int MaxPTL = 6; + const int MaxPTL = 2; const int MaxTL = 6; const int MaxGL = 3; const int MaxPGL = 2; @@ -50,6 +55,14 @@ namespace SparcISA // NWINDOWS - number of register windows, can be 3 to 32 const int NWindows = 32; + + const int AsrStart = 0; + const int PrStart = 32; + const int HprStart = 64; + const int MiscStart = 96; + + const uint64_t Bit64 = (1ULL << 63); + class IntRegFile { protected: @@ -231,17 +244,22 @@ namespace SparcISA //In each of these cases, we have to copy the value into a temporary //variable. This is because we may otherwise try to access an //unaligned portion of memory. + + uint32_t result32; + uint64_t result64; switch(width) { case SingleWidth: - uint32_t result32 = gtoh((uint32_t)val); + result32 = gtoh((uint32_t)val); memcpy(regSpace + 4 * floatReg, &result32, width); + break; case DoubleWidth: - uint64_t result64 = gtoh((uint64_t)val); + result64 = gtoh((uint64_t)val); memcpy(regSpace + 4 * floatReg, &result64, width); + break; case QuadWidth: - uint64_t result128 = gtoh((uint64_t)val); - memcpy(regSpace + 4 * floatReg, &result128, width); + panic("Quad width FP not implemented."); + break; default: panic("Attempted to read a %d bit floating point register!", width); } @@ -253,17 +271,21 @@ namespace SparcISA //In each of these cases, we have to copy the value into a temporary //variable. This is because we may otherwise try to access an //unaligned portion of memory. + uint32_t result32; + uint64_t result64; switch(width) { case SingleWidth: - uint32_t result32 = gtoh((uint32_t)val); + result32 = gtoh((uint32_t)val); memcpy(regSpace + 4 * floatReg, &result32, width); + break; case DoubleWidth: - uint64_t result64 = gtoh((uint64_t)val); + result64 = gtoh((uint64_t)val); memcpy(regSpace + 4 * floatReg, &result64, width); + break; case QuadWidth: - uint64_t result128 = gtoh((uint64_t)val); - memcpy(regSpace + 4 * floatReg, &result128, width); + panic("Quad width FP not implemented."); + break; default: panic("Attempted to read a %d bit floating point register!", width); } @@ -277,156 +299,83 @@ namespace SparcISA enum MiscRegIndex { - MISCREG_PSTATE, - MISCREG_PSTATE_AG, - MISCREG_PSTATE_IE, - MISCREG_PSTATE_PRIV, - MISCREG_PSTATE_AM, - MISCREG_PSTATE_PEF, - MISCREG_PSTATE_RED, - MISCREG_PSTATE_MM, - MISCREG_PSTATE_TLE, - MISCREG_PSTATE_CLE, - MISCREG_TBA, - MISCREG_Y, - MISCREG_Y_VALUE, - MISCREG_PIL, - MISCREG_CWP, - MISCREG_TT_BASE, - MISCREG_TT_END = MISCREG_TT_BASE + MaxTL, - MISCREG_CCR, - MISCREG_CCR_ICC, - MISCREG_CCR_ICC_C, - MISCREG_CCR_ICC_V, - MISCREG_CCR_ICC_Z, - MISCREG_CCR_ICC_N, - MISCREG_CCR_XCC, - MISCREG_CCR_XCC_C, - MISCREG_CCR_XCC_V, - MISCREG_CCR_XCC_Z, - MISCREG_CCR_XCC_N, - MISCREG_ASI, - MISCREG_TL, - MISCREG_TPC_BASE, - MISCREG_TPC_END = MISCREG_TPC_BASE + MaxTL, - MISCREG_TNPC_BASE, - MISCREG_TNPC_END = MISCREG_TNPC_BASE + MaxTL, - MISCREG_TSTATE_BASE, - MISCREG_TSTATE_END = MISCREG_TSTATE_BASE + MaxTL, - MISCREG_TSTATE_CWP_BASE, - MISCREG_TSTATE_CWP_END = MISCREG_TSTATE_CWP_BASE + MaxTL, - MISCREG_TSTATE_PSTATE_BASE, - MISCREG_TSTATE_PSTATE_END = MISCREG_TSTATE_PSTATE_BASE + MaxTL, - MISCREG_TSTATE_ASI_BASE, - MISCREG_TSTATE_ASI_END = MISCREG_TSTATE_ASI_BASE + MaxTL, - MISCREG_TSTATE_CCR_BASE, - MISCREG_TSTATE_CCR_END = MISCREG_TSTATE_CCR_BASE + MaxTL, - MISCREG_TICK, - MISCREG_TICK_COUNTER, - MISCREG_TICK_NPT, - MISCREG_CANSAVE, - MISCREG_CANRESTORE, - MISCREG_OTHERWIN, - MISCREG_CLEANWIN, - MISCREG_WSTATE, - MISCREG_WSTATE_NORMAL, - MISCREG_WSTATE_OTHER, - MISCREG_VER, - MISCREG_VER_MAXWIN, - MISCREG_VER_MAXTL, - MISCREG_VER_MASK, - MISCREG_VER_IMPL, - MISCREG_VER_MANUF, - MISCREG_FSR, - MISCREG_FSR_CEXC, - MISCREG_FSR_CEXC_NXC, - MISCREG_FSR_CEXC_DZC, - MISCREG_FSR_CEXC_UFC, - MISCREG_FSR_CEXC_OFC, - MISCREG_FSR_CEXC_NVC, - MISCREG_FSR_AEXC, - MISCREG_FSR_AEXC_NXC, - MISCREG_FSR_AEXC_DZC, - MISCREG_FSR_AEXC_UFC, - MISCREG_FSR_AEXC_OFC, - MISCREG_FSR_AEXC_NVC, - MISCREG_FSR_FCC0, - MISCREG_FSR_QNE, - MISCREG_FSR_FTT, - MISCREG_FSR_VER, - MISCREG_FSR_NS, - MISCREG_FSR_TEM, - MISCREG_FSR_TEM_NXM, - MISCREG_FSR_TEM_DZM, - MISCREG_FSR_TEM_UFM, - MISCREG_FSR_TEM_OFM, - MISCREG_FSR_TEM_NVM, - MISCREG_FSR_RD, - MISCREG_FSR_FCC1, - MISCREG_FSR_FCC2, - MISCREG_FSR_FCC3, - MISCREG_FPRS, - MISCREG_FPRS_DL, - MISCREG_FPRS_DU, - MISCREG_FPRS_FEF, - numMiscRegs + /** Ancillary State Registers */ + MISCREG_Y = AsrStart + 0, + MISCREG_CCR = AsrStart + 2, + MISCREG_ASI = AsrStart + 3, + MISCREG_TICK = AsrStart + 4, + MISCREG_PC = AsrStart + 5, + MISCREG_FPRS = AsrStart + 6, + MISCREG_PCR = AsrStart + 16, + MISCREG_PIC = AsrStart + 17, + MISCREG_GSR = AsrStart + 19, + MISCREG_SOFTINT_SET = AsrStart + 20, + MISCREG_SOFTINT_CLR = AsrStart + 21, + MISCREG_SOFTINT = AsrStart + 22, + MISCREG_TICK_CMPR = AsrStart + 23, + MISCREG_STICK = AsrStart + 24, + MISCREG_STICK_CMPR = AsrStart + 25, + + /** Privilged Registers */ + MISCREG_TPC = PrStart + 0, + MISCREG_TNPC = PrStart + 1, + MISCREG_TSTATE = PrStart + 2, + MISCREG_TT = PrStart + 3, + MISCREG_PRIVTICK = PrStart + 4, + MISCREG_TBA = PrStart + 5, + MISCREG_PSTATE = PrStart + 6, + MISCREG_TL = PrStart + 7, + MISCREG_PIL = PrStart + 8, + MISCREG_CWP = PrStart + 9, + MISCREG_CANSAVE = PrStart + 10, + MISCREG_CANRESTORE = PrStart + 11, + MISCREG_CLEANWIN = PrStart + 12, + MISCREG_OTHERWIN = PrStart + 13, + MISCREG_WSTATE = PrStart + 14, + MISCREG_GL = PrStart + 16, + + /** Hyper privileged registers */ + MISCREG_HPSTATE = HprStart + 0, + MISCREG_HTSTATE = HprStart + 1, + MISCREG_HINTP = HprStart + 3, + MISCREG_HTBA = HprStart + 5, + MISCREG_HVER = HprStart + 6, + MISCREG_STRAND_STS_REG = HprStart + 16, + MISCREG_HSTICK_CMPR = HprStart + 31, + + /** Floating Point Status Register */ + MISCREG_FSR = MiscStart + 0 + }; // The control registers, broken out into fields class MiscRegFile { private: - union - { - uint16_t pstate; // Process State Register - struct - { - uint16_t ag:1; // Alternate Globals - uint16_t ie:1; // Interrupt enable - uint16_t priv:1; // Privelege mode - uint16_t am:1; // Address mask - uint16_t pef:1; // PSTATE enable floating-point - uint16_t red:1; // RED (reset, error, debug) state - uint16_t mm:2; // Memory Model - uint16_t tle:1; // Trap little-endian - uint16_t cle:1; // Current little-endian - } pstateFields; - }; - uint64_t tba; // Trap Base Address - union - { + + /* ASR Registers */ + union { uint64_t y; // Y (used in obsolete multiplication) - struct - { + struct { uint64_t value:32; // The actual value stored in y uint64_t :32; // reserved bits } yFields; }; - uint8_t pil; // Process Interrupt Register - uint8_t cwp; // Current Window Pointer - uint16_t tt[MaxTL]; // Trap Type (Type of trap which occured - // on the previous level) - union - { + union { uint8_t ccr; // Condition Code Register - struct - { - union - { + struct { + union { uint8_t icc:4; // 32-bit condition codes - struct - { + struct { uint8_t c:1; // Carry uint8_t v:1; // Overflow uint8_t z:1; // Zero uint8_t n:1; // Negative } iccFields; }; - union - { + union { uint8_t xcc:4; // 64-bit condition codes - struct - { + struct { uint8_t c:1; // Carry uint8_t v:1; // Overflow uint8_t z:1; // Zero @@ -436,73 +385,143 @@ namespace SparcISA } ccrFields; }; uint8_t asi; // Address Space Identifier - uint8_t tl; // Trap Level + union { + uint64_t tick; // Hardware clock-tick counter + struct { + int64_t counter:63; // Clock-tick count + uint64_t npt:1; // Non-priveleged trap + } tickFields; + }; + union { + uint8_t fprs; // Floating-Point Register State + struct { + uint8_t dl:1; // Dirty lower + uint8_t du:1; // Dirty upper + uint8_t fef:1; // FPRS enable floating-Point + } fprsFields; + }; + union { + uint64_t softint; + struct { + uint64_t tm:1; + uint64_t int_level:14; + uint64_t sm:1; + } softintFields; + }; + union { + uint64_t tick_cmpr; // Hardware tick compare registers + struct { + uint64_t tick_cmpr:63; // Clock-tick count + uint64_t int_dis:1; // Non-priveleged trap + } tick_cmprFields; + }; + union { + uint64_t stick; // Hardware clock-tick counter + struct { + int64_t :63; // Not used, storage in SparcSystem + uint64_t npt:1; // Non-priveleged trap + } stickFields; + }; + union { + uint64_t stick_cmpr; // Hardware tick compare registers + struct { + uint64_t tick_cmpr:63; // Clock-tick count + uint64_t int_dis:1; // Non-priveleged trap + } stick_cmprFields; + }; + + + /* Privileged Registers */ uint64_t tpc[MaxTL]; // Trap Program Counter (value from // previous trap level) uint64_t tnpc[MaxTL]; // Trap Next Program Counter (value from // previous trap level) - union - { + union { uint64_t tstate[MaxTL]; // Trap State - struct - { + struct { //Values are from previous trap level uint64_t cwp:5; // Current Window Pointer - uint64_t :2; // Reserved bits - uint64_t pstate:10; // Process State - uint64_t :6; // Reserved bits + uint64_t :3; // Reserved bits + uint64_t pstate:13; // Process State + uint64_t :3; // Reserved bits uint64_t asi:8; // Address Space Identifier uint64_t ccr:8; // Condition Code Register + uint64_t gl:8; // Global level } tstateFields[MaxTL]; }; - union - { - uint64_t tick; // Hardware clock-tick counter - struct - { - uint64_t counter:63; // Clock-tick count - uint64_t npt:1; // Non-priveleged trap - } tickFields; + uint16_t tt[MaxTL]; // Trap Type (Type of trap which occured + // on the previous level) + uint64_t tba; // Trap Base Address + + union { + uint16_t pstate; // Process State Register + struct { + uint16_t :1; // reserved + uint16_t ie:1; // Interrupt enable + uint16_t priv:1; // Privelege mode + uint16_t am:1; // Address mask + uint16_t pef:1; // PSTATE enable floating-point + uint16_t :1; // reserved2 + uint16_t mm:2; // Memory Model + uint16_t tle:1; // Trap little-endian + uint16_t cle:1; // Current little-endian + } pstateFields; }; + uint8_t tl; // Trap Level + uint8_t pil; // Process Interrupt Register + uint8_t cwp; // Current Window Pointer uint8_t cansave; // Savable windows uint8_t canrestore; // Restorable windows - uint8_t otherwin; // Other windows uint8_t cleanwin; // Clean windows - union - { + uint8_t otherwin; // Other windows + union { uint8_t wstate; // Window State - struct - { + struct { uint8_t normal:3; // Bits TT<4:2> are set to on a normal // register window trap uint8_t other:3; // Bits TT<4:2> are set to on an "otherwin" // register window trap } wstateFields; }; - union - { - uint64_t ver; // Version - struct - { - uint64_t maxwin:5; // Max CWP value - uint64_t :2; // Reserved bits - uint64_t maxtl:8; // Maximum trap level - uint64_t :8; // Reserved bits - uint64_t mask:8; // Processor mask set revision number - uint64_t impl:16; // Implementation identification number - uint64_t manuf:16; // Manufacturer code - } verFields; + uint8_t gl; // Global level register + + + /** Hyperprivileged Registers */ + union { + uint64_t hpstate; // Hyperprivileged State Register + struct { + uint8_t tlz: 1; + uint8_t :1; + uint8_t hpriv:1; + uint8_t :2; + uint8_t red:1; + uint8_t :4; + uint8_t ibe:1; + uint8_t id:1; + } hpstateFields; }; - union - { + + uint64_t htstate[MaxTL]; // Hyperprivileged Trap State Register + uint64_t hintp; + uint64_t htba; // Hyperprivileged Trap Base Address register + union { + uint64_t hstick_cmpr; // Hardware tick compare registers + struct { + uint64_t tick_cmpr:63; // Clock-tick count + uint64_t int_dis:1; // Non-priveleged trap + } hstick_cmprFields; + }; + + uint64_t strandStatusReg; // Per strand status register + + + /** Floating point misc registers. */ + union { uint64_t fsr; // Floating-Point State Register - struct - { - union - { + struct { + union { uint64_t cexc:5; // Current excpetion - struct - { + struct { uint64_t nxc:1; // Inexact uint64_t dzc:1; // Divide by zero uint64_t ufc:1; // Underflow @@ -510,11 +529,9 @@ namespace SparcISA uint64_t nvc:1; // Invalid operand } cexcFields; }; - union - { + union { uint64_t aexc:5; // Accrued exception - struct - { + struct { uint64_t nxc:1; // Inexact uint64_t dzc:1; // Divide by zero uint64_t ufc:1; // Underflow @@ -530,11 +547,9 @@ namespace SparcISA uint64_t ver:3; // Version (of the FPU) uint64_t :2; // Reserved bits uint64_t ns:1; // Nonstandard floating point - union - { + union { uint64_t tem:5; // Trap Enable Mask - struct - { + struct { uint64_t nxm:1; // Inexact uint64_t dzm:1; // Divide by zero uint64_t ufm:1; // Underflow @@ -550,17 +565,34 @@ namespace SparcISA uint64_t :26; // Reserved bits } fsrFields; }; - union - { - uint8_t fprs; // Floating-Point Register State - struct - { - uint8_t dl:1; // Dirty lower - uint8_t du:1; // Dirty upper - uint8_t fef:1; // FPRS enable floating-Point - } fprsFields; - }; + // These need to check the int_dis field and if 0 then + // set appropriate bit in softint and checkinterrutps on the cpu +#if FULL_SYSTEM + /** Process a tick compare event and generate an interrupt on the cpu if + * appropriate. */ + void processTickCompare(ThreadContext *tc); + void processSTickCompare(ThreadContext *tc); + void processHSTickCompare(ThreadContext *tc); + + typedef CpuEventWrapper<MiscRegFile, + &MiscRegFile::processTickCompare> TickCompareEvent; + TickCompareEvent *tickCompare; + + typedef CpuEventWrapper<MiscRegFile, + &MiscRegFile::processSTickCompare> STickCompareEvent; + STickCompareEvent *sTickCompare; + + typedef CpuEventWrapper<MiscRegFile, + &MiscRegFile::processHSTickCompare> HSTickCompareEvent; + HSTickCompareEvent *hSTickCompare; + + /** Fullsystem only register version of ReadRegWithEffect() */ + MiscReg readFSRegWithEffect(int miscReg, Fault &fault, ThreadContext *tc); + /** Fullsystem only register version of SetRegWithEffect() */ + Fault setFSRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext * tc); +#endif public: void reset() @@ -572,18 +604,32 @@ namespace SparcISA //on reset Trap which sets the processor into the following state. //Bits that aren't set aren't defined on startup. tl = MaxTL; - tt[tl] = PowerOnReset.trapType(); - pstateFields.mm = 0; //Total Store Order - pstateFields.red = 1; //Enter RED_State - pstateFields.am = 0; //Address Masking is turned off - pstateFields.priv = 1; //Processor enters privileged mode - pstateFields.ie = 0; //Interrupts are disabled - pstateFields.ag = 1; //Globals are replaced with alternate globals - pstateFields.tle = 0; //Big Endian mode for traps - pstateFields.cle = 0; //Big Endian mode for non-traps - tickFields.counter = 0; //The TICK register is unreadable by - tickFields.npt = 1; //The TICK register is unreadable by - //non-priveleged software + gl = MaxGL; + + tickFields.counter = 0; //The TICK register is unreadable bya + tickFields.npt = 1; //The TICK register is unreadable by by !priv + + softint = 0; // Clear all the soft interrupt bits + tick_cmprFields.int_dis = 1; // disable timer compare interrupts + tick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing + stickFields.npt = 1; //The TICK register is unreadable by by !priv + stick_cmprFields.int_dis = 1; // disable timer compare interrupts + stick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing + + + tt[tl] = power_on_reset; + pstate = 0; // fields 0 but pef + pstateFields.pef = 1; + + hpstate = 0; + hpstateFields.red = 1; + hpstateFields.hpriv = 1; + hpstateFields.tlz = 0; // this is a guess + + hintp = 0; // no interrupts pending + hstick_cmprFields.int_dis = 1; // disable timer compare interrupts + hstick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing + #else /* //This sets up the initial state of the processor for usermode processes pstateFields.priv = 0; //Process runs in user mode @@ -608,20 +654,42 @@ namespace SparcISA reset(); } + /** read a value out of an either an SE or FS IPR. No checking is done + * about SE vs. FS as this is mostly used to copy the regfile. Thus more + * register are copied that are necessary for FS. However this prevents + * a bunch of ifdefs and is rarely called so is not performance + * criticial. */ MiscReg readReg(int miscReg); - MiscReg readRegWithEffect(int miscReg, Fault &fault, ExecContext *xc); - + /** Read a value from an IPR. Only the SE iprs are here and the rest + * are are readFSRegWithEffect (which is called by readRegWithEffect()). + * Checking is done for permission based on state bits in the miscreg + * file. */ + MiscReg readRegWithEffect(int miscReg, Fault &fault, ThreadContext *tc); + + /** write a value into an either an SE or FS IPR. No checking is done + * about SE vs. FS as this is mostly used to copy the regfile. Thus more + * register are copied that are necessary for FS. However this prevents + * a bunch of ifdefs and is rarely called so is not performance + * criticial.*/ Fault setReg(int miscReg, const MiscReg &val); + /** Write a value into an IPR. Only the SE iprs are here and the rest + * are are setFSRegWithEffect (which is called by setRegWithEffect()). + * Checking is done for permission based on state bits in the miscreg + * file. */ Fault setRegWithEffect(int miscReg, - const MiscReg &val, ExecContext * xc); + const MiscReg &val, ThreadContext * tc); void serialize(std::ostream & os); void unserialize(Checkpoint * cp, const std::string & section); - void copyMiscRegs(ExecContext * xc); + void copyMiscRegs(ThreadContext * tc); + + bool isHyperPriv() { return hpstateFields.hpriv; } + bool isPriv() { return hpstateFields.hpriv || pstateFields.priv; } + bool isNonPriv() { return !isPriv(); } }; typedef union @@ -693,9 +761,9 @@ namespace SparcISA } MiscReg readMiscRegWithEffect(int miscReg, - Fault &fault, ExecContext *xc) + Fault &fault, ThreadContext *tc) { - return miscRegFile.readRegWithEffect(miscReg, fault, xc); + return miscRegFile.readRegWithEffect(miscReg, fault, tc); } Fault setMiscReg(int miscReg, const MiscReg &val) @@ -704,9 +772,9 @@ namespace SparcISA } Fault setMiscRegWithEffect(int miscReg, const MiscReg &val, - ExecContext * xc) + ThreadContext * tc) { - return miscRegFile.setRegWithEffect(miscReg, val, xc); + return miscRegFile.setRegWithEffect(miscReg, val, tc); } FloatReg readFloatReg(int floatReg, int width) @@ -775,22 +843,17 @@ namespace SparcISA CONTEXT_CWP, CONTEXT_GLOBALS }; - - union ContextVal - { - MiscReg reg; - int globalLevel; - }; + typedef int ContextVal; void changeContext(ContextParam param, ContextVal val) { switch(param) { case CONTEXT_CWP: - intRegFile.setCWP(val.reg); + intRegFile.setCWP(val); break; case CONTEXT_GLOBALS: - intRegFile.setGlobals(val.globalLevel); + intRegFile.setGlobals(val); break; default: panic("Tried to set illegal context parameter in the SPARC regfile.\n"); @@ -798,9 +861,11 @@ namespace SparcISA } }; - void copyRegs(ExecContext *src, ExecContext *dest); + void copyRegs(ThreadContext *src, ThreadContext *dest); + + void copyMiscRegs(ThreadContext *src, ThreadContext *dest); - void copyMiscRegs(ExecContext *src, ExecContext *dest); + int InterruptLevel(uint64_t softint); } // namespace SparcISA diff --git a/src/arch/sparc/solaris/process.cc b/src/arch/sparc/solaris/process.cc index 95cdb0bd5..af0550910 100644 --- a/src/arch/sparc/solaris/process.cc +++ b/src/arch/sparc/solaris/process.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #include "arch/sparc/isa_traits.hh" @@ -31,7 +33,7 @@ #include "arch/sparc/regfile.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/solaris/solaris.hh" #include "sim/process.hh" @@ -44,9 +46,9 @@ using namespace SparcISA; /// Target uname() handler. static SyscallReturn unameFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<Solaris::utsname> name(xc->getSyscallArg(0)); + TypedBufferArg<Solaris::utsname> name(tc->getSyscallArg(0)); strcpy(name->sysname, "SunOS"); strcpy(name->nodename, "m5.eecs.umich.edu"); @@ -54,7 +56,7 @@ unameFunc(SyscallDesc *desc, int callnum, Process *process, strcpy(name->version, "Generic_118558-21"); strcpy(name->machine, "sun4u"); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } diff --git a/src/arch/sparc/solaris/process.hh b/src/arch/sparc/solaris/process.hh index 24dffdaf0..3c0d7eba7 100644 --- a/src/arch/sparc/solaris/process.hh +++ b/src/arch/sparc/solaris/process.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #ifndef __SPARC_SOLARIS_PROCESS_HH__ diff --git a/src/arch/sparc/solaris/solaris.cc b/src/arch/sparc/solaris/solaris.cc index a56f10740..c588925b0 100644 --- a/src/arch/sparc/solaris/solaris.cc +++ b/src/arch/sparc/solaris/solaris.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #include "arch/sparc/solaris/solaris.hh" diff --git a/src/arch/sparc/solaris/solaris.hh b/src/arch/sparc/solaris/solaris.hh index 6833a2d6a..0564faba4 100644 --- a/src/arch/sparc/solaris/solaris.hh +++ b/src/arch/sparc/solaris/solaris.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #ifndef __ARCH_SPARC_SOLARIS_SOLARIS_HH__ diff --git a/src/arch/sparc/stacktrace.hh b/src/arch/sparc/stacktrace.hh index 1d8d97a79..d12aee211 100644 --- a/src/arch/sparc/stacktrace.hh +++ b/src/arch/sparc/stacktrace.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __ARCH_ALPHA_STACKTRACE_HH__ @@ -32,13 +34,13 @@ #include "base/trace.hh" #include "cpu/static_inst.hh" -class ExecContext; +class ThreadContext; class StackTrace; class ProcessInfo { private: - ExecContext *xc; + ThreadContext *tc; int thread_info_size; int task_struct_size; @@ -47,7 +49,7 @@ class ProcessInfo int name_off; public: - ProcessInfo(ExecContext *_xc); + ProcessInfo(ThreadContext *_tc); Addr task(Addr ksp) const; int pid(Addr ksp) const; @@ -59,7 +61,7 @@ class StackTrace protected: typedef TheISA::MachInst MachInst; private: - ExecContext *xc; + ThreadContext *tc; std::vector<Addr> stack; private: @@ -68,21 +70,21 @@ class StackTrace bool decodeSave(MachInst inst, int ®, int &disp); bool decodeStack(MachInst inst, int &disp); - void trace(ExecContext *xc, bool is_call); + void trace(ThreadContext *tc, bool is_call); public: StackTrace(); - StackTrace(ExecContext *xc, StaticInstPtr inst); + StackTrace(ThreadContext *tc, StaticInstPtr inst); ~StackTrace(); void clear() { - xc = 0; + tc = 0; stack.clear(); } - bool valid() const { return xc != NULL; } - bool trace(ExecContext *xc, StaticInstPtr inst); + bool valid() const { return tc != NULL; } + bool trace(ThreadContext *tc, StaticInstPtr inst); public: const std::vector<Addr> &getstack() const { return stack; } @@ -104,7 +106,7 @@ class StackTrace }; inline bool -StackTrace::trace(ExecContext *xc, StaticInstPtr inst) +StackTrace::trace(ThreadContext *tc, StaticInstPtr inst) { if (!inst->isCall() && !inst->isReturn()) return false; @@ -112,7 +114,7 @@ StackTrace::trace(ExecContext *xc, StaticInstPtr inst) if (valid()) clear(); - trace(xc, !inst->isReturn()); + trace(tc, !inst->isReturn()); return true; } diff --git a/src/arch/sparc/system.cc b/src/arch/sparc/system.cc index 1e2882607..e197e7918 100644 --- a/src/arch/sparc/system.cc +++ b/src/arch/sparc/system.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #include "arch/sparc/system.hh" @@ -40,7 +42,8 @@ using namespace BigEndianGuest; SparcSystem::SparcSystem(Params *p) - : System(p) + : System(p), sysTick(0) + { resetSymtab = new SymbolTable; hypervisorSymtab = new SymbolTable; diff --git a/src/arch/sparc/system.hh b/src/arch/sparc/system.hh index 27aa8768a..614707f6c 100644 --- a/src/arch/sparc/system.hh +++ b/src/arch/sparc/system.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #ifndef __ARCH_SPARC_SYSTEM_HH__ @@ -53,7 +55,7 @@ class SparcSystem : public System SparcSystem(Params *p); - ~SparcaSystem(); + ~SparcSystem(); virtual bool breakpoint(); @@ -82,6 +84,9 @@ class SparcSystem : public System /** Object pointer for the openboot code */ ObjectFile *openboot; + /** System Tick for syncronized tick across all cpus. */ + Tick sysTick; + protected: const Params *params() const { return (const Params *)_params; } diff --git a/src/arch/sparc/tlb.hh b/src/arch/sparc/tlb.hh new file mode 100644 index 000000000..35ff08b43 --- /dev/null +++ b/src/arch/sparc/tlb.hh @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + */ + +#ifndef __ARCH_SPARC_TLB_HH__ +#define __ARCH_SPARC_TLB_HH__ + + +#endif // __ARCH_SPARC_TLB_HH__ diff --git a/src/arch/sparc/ua2005.cc b/src/arch/sparc/ua2005.cc new file mode 100644 index 000000000..b89d48663 --- /dev/null +++ b/src/arch/sparc/ua2005.cc @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + */ + +#include "arch/sparc/regfile.hh" + +Fault +SparcISA::MiscRegFile::setFSRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext *tc) +{ + int64_t time; + SparcSystem *sys; + switch (miscReg) { + /** Full system only ASRs */ + case MISCREG_SOFTINT: + if (isNonPriv()) + return new PrivilegedOpcode; + // Check if we are going to interrupt because of something + int oldLevel = InterruptLevel(softint); + int newLevel = InterruptLevel(val); + setReg(miscReg, val); + if (newLevel > oldLevel) + ; // MUST DO SOMETHING HERE TO TELL CPU TO LOOK FOR INTERRUPTS XXX + //tc->getCpuPtr()->checkInterrupts = true; + return NoFault; + + case MISCREG_SOFTINT_CLR: + return setRegWithEffect(miscReg, ~val & softint, tc); + case MISCREG_SOFTINT_SET: + return setRegWithEffect(miscReg, val | softint, tc); + + case MISCREG_TICK_CMPR: + if (isNonPriv()) + return new PrivilegedOpcode; + if (tickCompare == NULL) + tickCompare = new TickCompareEvent(this, tc); + setReg(miscReg, val); + if (tick_cmprFields.int_dis && tickCompare.scheduled()) + tickCompare.deschedule(); + time = tick_cmprFields.tick_cmpr - tickFields.counter; + if (!tick_cmprFields.int_dis && time > 0) + tickCompare.schedule(time * tc->getCpuPtr()->cycles(1)); + return NoFault; + + case MISCREG_STICK: + if (isNonPriv()) + return new PrivilegedOpcode; + if (isPriv()) + return new PrivilegedAction; + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + sys->sysTick = curTick/Clock::Int::ns - val & ~Bit64; + stickFields.npt = val & Bit64 ? 1 : 0; + return NoFault; + + case MISCREG_STICK_CMPR: + if (isNonPriv()) + return new PrivilegedOpcode; + if (sTickCompare == NULL) + sTickCompare = new STickCompareEvent(this, tc); + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + setReg(miscReg, val); + if (stick_cmprFields.int_dis && sTickCompare.scheduled()) + sTickCompare.deschedule(); + time = stick_cmprFields.tick_cmpr - sys->sysTick; + if (!stick_cmprFields.int_dis && time > 0) + sTickCompare.schedule(time * Clock::Int::ns); + return NoFault; + + /** Fullsystem only Priv registers. */ + case MISCREG_PIL: + if (FULL_SYSTEM) { + setReg(miscReg, val); + //tc->getCpuPtr()->checkInterrupts; + // MUST DO SOMETHING HERE TO TELL CPU TO LOOK FOR INTERRUPTS XXX + return NoFault; + } else + panic("PIL not implemented for syscall emulation\n"); + + /** Hyper privileged registers */ + case MISCREG_HPSTATE: + case MISCREG_HINTP: + setReg(miscReg, val); + return NoFault; + case MISCREG_HTSTATE: + if (tl == 0) + return new IllegalInstruction; + setReg(miscReg, val); + return NoFault; + + case MISCREG_HTBA: + // clear lower 7 bits on writes. + setReg(miscReg, val & ULL(~0x7FFF)); + return NoFault; + + case MISCREG_STRAND_STS_REG: + setReg(miscReg, strandStatusReg); + return NoFault; + case MISCREG_HSTICK_CMPR: + if (isNonPriv()) + return new PrivilegedOpcode; + if (hSTickCompare == NULL) + hSTickCompare = new HSTickCompareEvent(this, tc); + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + setReg(miscReg, val); + if (hstick_cmprFields.int_dis && hSTickCompare.scheduled()) + hSTickCompare.deschedule(); + int64_t time = hstick_cmprFields.tick_cmpr - sys->sysTick; + if (!hstick_cmprFields.int_dis && time > 0) + hSTickCompare.schedule(time * Clock::Int::ns); + return NoFault; + default: + return new IllegalInstruction; + } +} + +MiscReg +MiscRegFile::readFSRegWithEffect(int miscReg, Fault &fault, ThreadContext * tc) +{ + switch (miscReg) { + + /** Privileged registers. */ + case MISCREG_SOFTINT: + if (isNonPriv()) { + fault = new PrivilegedOpcode; + return 0; + } + return readReg(miscReg); + case MISCREG_TICK_CMPR: + if (isNonPriv()) { + fault = new PrivilegedOpcode; + return 0; + } + return readReg(miscReg); + case MISCREG_STICK: + SparcSystem *sys; + if (stickFields.npt && !isNonPriv()) { + fault = new PrivilegedAction; + return 0; + } + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + return curTick/Clock::Int::ns - sys->sysTick | stickFields.npt << 63; + case MISCREG_STICK_CMPR: + if (isNonPriv()) { + fault = new PrivilegedOpcode; + return 0; + } + return readReg(miscReg); + + + /** Hyper privileged registers */ + case MISCREG_HPSTATE: + case MISCREG_HINTP: + return readReg(miscReg); + case MISCREG_HTSTATE: + if (tl == 0) { + fault = new IllegalInstruction; + return 0; + } + return readReg(miscReg); + + case MISCREG_HTBA: + return readReg(miscReg) & ULL(~0x7FFF); + case MISCREG_HVER: + return NWindows | MaxTL << 8 | MaxGL << 16; + case MISCREG_STRAND_STS_REG: + return strandStatusReg; + case MISCREG_HSTICK_CMPR: + return hstick_cmpr; + + default: + fault = new IllegalInstruction; + return 0; + } +} + +void +MiscRegFile::processTickCompare(ThreadContext *tc) +{ + panic("tick compare not implemented\n"); +} + +void +MiscRegFile::processSTickCompare(ThreadContext *tc) +{ + panic("tick compare not implemented\n"); +} + +void +MiscRegFile::processHSTickCompare(ThreadContext *tc) +{ + panic("tick compare not implemented\n"); +} + +}; // namespace SparcISA diff --git a/src/arch/sparc/utility.hh b/src/arch/sparc/utility.hh index 1e67b3370..f1c071148 100644 --- a/src/arch/sparc/utility.hh +++ b/src/arch/sparc/utility.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Gabe Black */ #ifndef __ARCH_SPARC_UTILITY_HH__ @@ -79,10 +81,10 @@ namespace SparcISA /** * Function to insure ISA semantics about 0 registers. - * @param xc The execution context. + * @param tc The thread context. */ - template <class XC> - void zeroRegisters(XC *xc); + template <class TC> + void zeroRegisters(TC *tc); } // namespace SparcISA diff --git a/src/arch/sparc/vtophys.cc b/src/arch/sparc/vtophys.cc new file mode 100644 index 000000000..f7fd92c15 --- /dev/null +++ b/src/arch/sparc/vtophys.cc @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Ali Saidi + */ + +#include <string> + +#include "arch/alpha/ev5.hh" +#include "arch/alpha/vtophys.hh" +#include "base/chunk_generator.hh" +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "mem/vport.hh" + +using namespace std; +using namespace AlphaISA; + +AlphaISA::PageTableEntry +AlphaISA::kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, AlphaISA::VAddr vaddr) +{ + Addr level1_pte = ptbr + vaddr.level1(); + AlphaISA::PageTableEntry level1 = mem->read<uint64_t>(level1_pte); + if (!level1.valid()) { + DPRINTF(VtoPhys, "level 1 PTE not valid, va = %#\n", vaddr); + return 0; + } + + Addr level2_pte = level1.paddr() + vaddr.level2(); + AlphaISA::PageTableEntry level2 = mem->read<uint64_t>(level2_pte); + if (!level2.valid()) { + DPRINTF(VtoPhys, "level 2 PTE not valid, va = %#x\n", vaddr); + return 0; + } + + Addr level3_pte = level2.paddr() + vaddr.level3(); + AlphaISA::PageTableEntry level3 = mem->read<uint64_t>(level3_pte); + if (!level3.valid()) { + DPRINTF(VtoPhys, "level 3 PTE not valid, va = %#x\n", vaddr); + return 0; + } + return level3; +} + +Addr +AlphaISA::vtophys(Addr vaddr) +{ + Addr paddr = 0; + if (AlphaISA::IsUSeg(vaddr)) + DPRINTF(VtoPhys, "vtophys: invalid vaddr %#x", vaddr); + else if (AlphaISA::IsK0Seg(vaddr)) + paddr = AlphaISA::K0Seg2Phys(vaddr); + else + panic("vtophys: ptbr is not set on virtual lookup"); + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + +Addr +AlphaISA::vtophys(ThreadContext *tc, Addr addr) +{ + AlphaISA::VAddr vaddr = addr; + Addr ptbr = tc->readMiscReg(AlphaISA::IPR_PALtemp20); + Addr paddr = 0; + //@todo Andrew couldn't remember why he commented some of this code + //so I put it back in. Perhaps something to do with gdb debugging? + if (AlphaISA::PcPAL(vaddr) && (vaddr < EV5::PalMax)) { + paddr = vaddr & ~ULL(1); + } else { + if (AlphaISA::IsK0Seg(vaddr)) { + paddr = AlphaISA::K0Seg2Phys(vaddr); + } else if (!ptbr) { + paddr = vaddr; + } else { + AlphaISA::PageTableEntry pte = + kernel_pte_lookup(tc->getPhysPort(), ptbr, vaddr); + if (pte.valid()) + paddr = pte.paddr() | vaddr.offset(); + } + } + + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + + +void +AlphaISA::CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen) +{ + uint8_t *dst = (uint8_t *)dest; + VirtualPort *vp = tc->getVirtPort(tc); + + vp->readBlob(src, dst, cplen); + + tc->delVirtPort(vp); + +} + +void +AlphaISA::CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) +{ + uint8_t *src = (uint8_t *)source; + VirtualPort *vp = tc->getVirtPort(tc); + + vp->writeBlob(dest, src, cplen); + + tc->delVirtPort(vp); +} + +void +AlphaISA::CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen) +{ + int len = 0; + VirtualPort *vp = tc->getVirtPort(tc); + + do { + vp->readBlob(vaddr++, (uint8_t*)dst++, 1); + len++; + } while (len < maxlen && dst[len] != 0 ); + + tc->delVirtPort(vp); + dst[len] = 0; +} + +void +AlphaISA::CopyStringIn(ThreadContext *tc, char *src, Addr vaddr) +{ + VirtualPort *vp = tc->getVirtPort(tc); + for (ChunkGenerator gen(vaddr, strlen(src), AlphaISA::PageBytes); !gen.done(); + gen.next()) + { + vp->writeBlob(gen.addr(), (uint8_t*)src, gen.size()); + src += gen.size(); + } + tc->delVirtPort(vp); +} diff --git a/src/arch/sparc/vtophys.hh b/src/arch/sparc/vtophys.hh new file mode 100644 index 000000000..bf2b757d6 --- /dev/null +++ b/src/arch/sparc/vtophys.hh @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ARCH_SPARC_VTOPHYS_H__ +#define __ARCH_SPARC_VTOPHYS_H__ + +#include "arch/sparc/isa_traits.hh" + +class ThreadContext; +class FunctionalPort; + +namespace SparcISA { + +PageTableEntry +kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, SparcISA::VAddr vaddr); + +Addr vtophys(Addr vaddr); +Addr vtophys(ThreadContext *tc, Addr vaddr); + +void CopyOut(ThreadContext *tc, void *dst, Addr src, size_t len); +void CopyIn(ThreadContext *tc, Addr dst, void *src, size_t len); +void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen); +void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr); + +}; +#endif // __ARCH_SPARC_VTOPHYS_H__ + diff --git a/src/base/bitfield.hh b/src/base/bitfield.hh index c59354c7d..f4e49aa8d 100644 --- a/src/base/bitfield.hh +++ b/src/base/bitfield.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __BASE_BITFIELD_HH__ diff --git a/src/base/callback.hh b/src/base/callback.hh index 7b3023505..a39d7df20 100644 --- a/src/base/callback.hh +++ b/src/base/callback.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __CALLBACK_HH__ diff --git a/src/base/chunk_generator.hh b/src/base/chunk_generator.hh index 4f708bd4b..e9d5355ca 100644 --- a/src/base/chunk_generator.hh +++ b/src/base/chunk_generator.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __BASE__CHUNK_GENERATOR_HH__ diff --git a/src/base/circlebuf.cc b/src/base/circlebuf.cc index 89bbfd822..a0c015671 100644 --- a/src/base/circlebuf.cc +++ b/src/base/circlebuf.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <algorithm> diff --git a/src/base/circlebuf.hh b/src/base/circlebuf.hh index 8a64cb5f5..66583b7db 100644 --- a/src/base/circlebuf.hh +++ b/src/base/circlebuf.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __CIRCLEBUF_HH__ diff --git a/src/base/compression/lzss_compression.cc b/src/base/compression/lzss_compression.cc index 3ffdf7e95..eb35fb8f1 100644 --- a/src/base/compression/lzss_compression.cc +++ b/src/base/compression/lzss_compression.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** @file diff --git a/src/base/compression/lzss_compression.hh b/src/base/compression/lzss_compression.hh index c136c6d60..35e4dcb3f 100644 --- a/src/base/compression/lzss_compression.hh +++ b/src/base/compression/lzss_compression.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ #ifndef __LZSS_COMPRESSION_HH__ diff --git a/src/base/compression/null_compression.hh b/src/base/compression/null_compression.hh index 5fbcf562b..5a582d828 100644 --- a/src/base/compression/null_compression.hh +++ b/src/base/compression/null_compression.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Erik Hallnor + * Nathan Binkert */ #ifndef __BASE_COMPRESSION_NULL_COMPRESSION_HH__ diff --git a/src/base/cprintf.cc b/src/base/cprintf.cc index cf332ebf2..dd8ce858b 100644 --- a/src/base/cprintf.cc +++ b/src/base/cprintf.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <cassert> diff --git a/src/base/cprintf.hh b/src/base/cprintf.hh index c468c375f..6b2a77f90 100644 --- a/src/base/cprintf.hh +++ b/src/base/cprintf.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __CPRINTF_HH__ diff --git a/src/base/cprintf_formats.hh b/src/base/cprintf_formats.hh index 05a8723a4..58ee7f795 100644 --- a/src/base/cprintf_formats.hh +++ b/src/base/cprintf_formats.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __CPRINTF_FORMATS_HH__ diff --git a/src/base/crc.hh b/src/base/crc.hh index 6ede07748..16dce79aa 100644 --- a/src/base/crc.hh +++ b/src/base/crc.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_CRC_HH__ diff --git a/src/base/date.cc b/src/base/date.cc index ba7698c29..9ec8a0b33 100644 --- a/src/base/date.cc +++ b/src/base/date.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ const char *compileDate = __DATE__ " " __TIME__; diff --git a/src/base/dbl_list.hh b/src/base/dbl_list.hh index 1d06ff576..8fca72998 100644 --- a/src/base/dbl_list.hh +++ b/src/base/dbl_list.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __DBL_LIST_HH__ diff --git a/src/base/endian.hh b/src/base/endian.hh index 499eb50c5..d2b9dc65c 100644 --- a/src/base/endian.hh +++ b/src/base/endian.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __ENDIAN_HH__ diff --git a/src/base/fast_alloc.cc b/src/base/fast_alloc.cc index 6504e07c2..610dff66c 100644 --- a/src/base/fast_alloc.cc +++ b/src/base/fast_alloc.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ /* @@ -178,13 +180,11 @@ FastAlloc::dump_oldest(int n) // C interfaces to FastAlloc::dump_summary() and FastAlloc::dump_oldest(). // gdb seems to have trouble with calling C++ functions directly. // -extern "C" void fast_alloc_summary() { FastAlloc::dump_summary(); } -extern "C" void fast_alloc_oldest(int n) { FastAlloc::dump_oldest(n); diff --git a/src/base/fast_alloc.hh b/src/base/fast_alloc.hh index 54e35f8e0..3e22e59c1 100644 --- a/src/base/fast_alloc.hh +++ b/src/base/fast_alloc.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ /* diff --git a/src/base/fenv.hh b/src/base/fenv.hh index 3234f5dd3..013d2f09b 100644 --- a/src/base/fenv.hh +++ b/src/base/fenv.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __BASE_FENV_HH__ diff --git a/src/base/fifo_buffer.cc b/src/base/fifo_buffer.cc index 85b306c25..4bafa88bd 100644 --- a/src/base/fifo_buffer.cc +++ b/src/base/fifo_buffer.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Raasch + * Nathan Binkert */ #include "base/fifo_buffer.hh" diff --git a/src/base/fifo_buffer.hh b/src/base/fifo_buffer.hh index 03ce057c7..f6205330b 100644 --- a/src/base/fifo_buffer.hh +++ b/src/base/fifo_buffer.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Raasch + * Nathan Binkert */ #ifndef __FIFO_BUFFER_HH__ diff --git a/src/base/hashmap.hh b/src/base/hashmap.hh index 712366829..570cbc152 100644 --- a/src/base/hashmap.hh +++ b/src/base/hashmap.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __HASHMAP_HH__ diff --git a/src/base/hostinfo.cc b/src/base/hostinfo.cc index d42c96732..a7c93e712 100644 --- a/src/base/hostinfo.cc +++ b/src/base/hostinfo.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <ctype.h> diff --git a/src/base/hostinfo.hh b/src/base/hostinfo.hh index 21a6e5475..b6663ea69 100644 --- a/src/base/hostinfo.hh +++ b/src/base/hostinfo.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __HOSTINFO_HH__ diff --git a/src/base/hybrid_pred.cc b/src/base/hybrid_pred.cc index 21cbdb0fd..73216489e 100644 --- a/src/base/hybrid_pred.cc +++ b/src/base/hybrid_pred.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu */ #include <string> diff --git a/src/base/hybrid_pred.hh b/src/base/hybrid_pred.hh index ea4a9d04c..cb1d6003b 100644 --- a/src/base/hybrid_pred.hh +++ b/src/base/hybrid_pred.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Raasch + * Steve Reinhardt */ //========================================================================== diff --git a/src/base/inet.cc b/src/base/inet.cc index f2665bd2b..b8da12a99 100644 --- a/src/base/inet.cc +++ b/src/base/inet.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <sstream> diff --git a/src/base/inet.hh b/src/base/inet.hh index e5d0473f9..1bf7c585f 100644 --- a/src/base/inet.hh +++ b/src/base/inet.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __BASE_INET_HH__ diff --git a/src/base/inifile.cc b/src/base/inifile.cc index eb5a1335f..4d504d04f 100644 --- a/src/base/inifile.cc +++ b/src/base/inifile.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #define USE_CPP diff --git a/src/base/inifile.hh b/src/base/inifile.hh index 3c6894978..631b29b87 100644 --- a/src/base/inifile.hh +++ b/src/base/inifile.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __INIFILE_HH__ diff --git a/src/base/intmath.cc b/src/base/intmath.cc index f1c1651ba..22414ea4b 100644 --- a/src/base/intmath.cc +++ b/src/base/intmath.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include "base/intmath.hh" diff --git a/src/base/intmath.hh b/src/base/intmath.hh index 51baddb91..227012e30 100644 --- a/src/base/intmath.hh +++ b/src/base/intmath.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __INTMATH_HH__ diff --git a/src/base/loader/aout_object.cc b/src/base/loader/aout_object.cc index 564898ca3..6691bd4ae 100644 --- a/src/base/loader/aout_object.cc +++ b/src/base/loader/aout_object.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #include <string> diff --git a/src/base/loader/aout_object.hh b/src/base/loader/aout_object.hh index aeb710427..d180d69f3 100644 --- a/src/base/loader/aout_object.hh +++ b/src/base/loader/aout_object.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __AOUT_OBJECT_HH__ diff --git a/src/base/loader/ecoff_object.cc b/src/base/loader/ecoff_object.cc index 80917ee9c..134f2d98d 100644 --- a/src/base/loader/ecoff_object.cc +++ b/src/base/loader/ecoff_object.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #include <string> diff --git a/src/base/loader/ecoff_object.hh b/src/base/loader/ecoff_object.hh index 603c70bec..05c604b2b 100644 --- a/src/base/loader/ecoff_object.hh +++ b/src/base/loader/ecoff_object.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __ECOFF_OBJECT_HH__ diff --git a/src/base/loader/elf_object.cc b/src/base/loader/elf_object.cc index 79601e9d1..00d218b76 100644 --- a/src/base/loader/elf_object.cc +++ b/src/base/loader/elf_object.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Ali Saidi */ #include <string> diff --git a/src/base/loader/elf_object.hh b/src/base/loader/elf_object.hh index 72c265edd..46dbfe37b 100644 --- a/src/base/loader/elf_object.hh +++ b/src/base/loader/elf_object.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __ELF_OBJECT_HH__ diff --git a/src/base/loader/object_file.cc b/src/base/loader/object_file.cc index c6dfced1d..42c74d418 100644 --- a/src/base/loader/object_file.cc +++ b/src/base/loader/object_file.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <list> diff --git a/src/base/loader/object_file.hh b/src/base/loader/object_file.hh index b43989cb5..79fa394c6 100644 --- a/src/base/loader/object_file.hh +++ b/src/base/loader/object_file.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __OBJECT_FILE_HH__ diff --git a/src/base/loader/symtab.cc b/src/base/loader/symtab.cc index 25f54f9bf..3e73eb7a3 100644 --- a/src/base/loader/symtab.cc +++ b/src/base/loader/symtab.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/base/loader/symtab.hh b/src/base/loader/symtab.hh index ebcda1345..55ff0c86f 100644 --- a/src/base/loader/symtab.hh +++ b/src/base/loader/symtab.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __SYMTAB_HH__ @@ -32,7 +35,7 @@ #include <iosfwd> #include <map> -#include "arch/isa_traits.hh" // for Addr +#include "sim/host.hh" // for Addr class Checkpoint; class SymbolTable diff --git a/src/base/match.cc b/src/base/match.cc index 4f1f49b57..994209864 100644 --- a/src/base/match.cc +++ b/src/base/match.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include "base/match.hh" diff --git a/src/base/match.hh b/src/base/match.hh index 1b0a083a7..6e1f03b80 100644 --- a/src/base/match.hh +++ b/src/base/match.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/base/misc.cc b/src/base/misc.cc index f3c86827b..991a33736 100644 --- a/src/base/misc.cc +++ b/src/base/misc.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/base/misc.hh b/src/base/misc.hh index 9255c69c6..87faf20e6 100644 --- a/src/base/misc.hh +++ b/src/base/misc.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Dave Greene */ #ifndef __MISC_HH__ diff --git a/src/base/mod_num.hh b/src/base/mod_num.hh index fabbb56a9..6962dc0fd 100644 --- a/src/base/mod_num.hh +++ b/src/base/mod_num.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Raasch */ template<class T, T MV> diff --git a/src/base/mysql.cc b/src/base/mysql.cc index c8d6e933a..2416c766a 100644 --- a/src/base/mysql.cc +++ b/src/base/mysql.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/base/mysql.hh b/src/base/mysql.hh index ae28a9dfb..272a0f07c 100644 --- a/src/base/mysql.hh +++ b/src/base/mysql.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_MYSQL_HH__ diff --git a/src/base/output.cc b/src/base/output.cc index 2b1733f21..afcac03a5 100644 --- a/src/base/output.cc +++ b/src/base/output.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <errno.h> diff --git a/src/base/output.hh b/src/base/output.hh index 3bbe73e3b..0aae4ae81 100644 --- a/src/base/output.hh +++ b/src/base/output.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_OUTPUT_HH__ diff --git a/src/base/pollevent.cc b/src/base/pollevent.cc index 99044fc09..2743cd95d 100644 --- a/src/base/pollevent.cc +++ b/src/base/pollevent.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <sys/ioctl.h> diff --git a/src/base/pollevent.hh b/src/base/pollevent.hh index d39931797..5b84650cb 100644 --- a/src/base/pollevent.hh +++ b/src/base/pollevent.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __POLLEVENT_H__ diff --git a/src/base/predictor.hh b/src/base/predictor.hh index 37aa29989..94f19ca28 100644 --- a/src/base/predictor.hh +++ b/src/base/predictor.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Raasch + * Nathan Binkert */ // diff --git a/src/base/random.cc b/src/base/random.cc index 4aac14101..e135b55f5 100644 --- a/src/base/random.cc +++ b/src/base/random.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ #include <cstdlib> diff --git a/src/base/random.hh b/src/base/random.hh index def7a4bce..b5eb39f94 100644 --- a/src/base/random.hh +++ b/src/base/random.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ #ifndef __BASE_RANDOM_HH__ diff --git a/src/base/range.cc b/src/base/range.cc index a4e50fc4f..442e5fdf8 100644 --- a/src/base/range.cc +++ b/src/base/range.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include "base/intmath.hh" diff --git a/src/base/range.hh b/src/base/range.hh index 4e3e0fd6e..d9542c0ca 100644 --- a/src/base/range.hh +++ b/src/base/range.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __BASE_RANGE_HH__ diff --git a/src/base/refcnt.hh b/src/base/refcnt.hh index de589f7c5..6672d4a5f 100644 --- a/src/base/refcnt.hh +++ b/src/base/refcnt.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __REFCNT_HH__ diff --git a/src/base/remote_gdb.cc b/src/base/remote_gdb.cc index 41d0c1471..0d3b73b1e 100644 --- a/src/base/remote_gdb.cc +++ b/src/base/remote_gdb.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @@ -126,7 +128,7 @@ #include "base/socket.hh" #include "base/trace.hh" #include "config/full_system.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/static_inst.hh" #include "mem/physical.hh" #include "mem/port.hh" @@ -250,7 +252,7 @@ RemoteGDB::Event::process(int revent) gdb->detach(); } -RemoteGDB::RemoteGDB(System *_system, ExecContext *c) +RemoteGDB::RemoteGDB(System *_system, ThreadContext *c) : event(NULL), listener(NULL), number(-1), fd(-1), active(false), attached(false), system(_system), pmem(_system->physmem), context(c) @@ -640,7 +642,9 @@ RemoteGDB::read(Addr vaddr, size_t size, char *data) DPRINTF(GDBRead, "read: addr=%#x, size=%d", vaddr, size); - context->getVirtPort(context)->readBlob(vaddr, (uint8_t*)data, size); + VirtualPort *vp = context->getVirtPort(context); + vp->readBlob(vaddr, (uint8_t*)data, size); + context->delVirtPort(vp); #if TRACING_ON if (DTRACE(GDBRead)) { @@ -677,8 +681,9 @@ RemoteGDB::write(Addr vaddr, size_t size, const char *data) } else DPRINTFNR("\n"); } - - context->getVirtPort(context)->writeBlob(vaddr, (uint8_t*)data, size); + VirtualPort *vp = context->getVirtPort(context); + vp->writeBlob(vaddr, (uint8_t*)data, size); + context->delVirtPort(vp); #ifdef IMB alpha_pal_imb(); @@ -702,11 +707,11 @@ RemoteGDB::HardBreakpoint::HardBreakpoint(RemoteGDB *_gdb, Addr pc) } void -RemoteGDB::HardBreakpoint::process(ExecContext *xc) +RemoteGDB::HardBreakpoint::process(ThreadContext *tc) { DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc()); - if (xc == gdb->context) + if (tc == gdb->context) gdb->trap(ALPHA_KENTRY_INT); } diff --git a/src/base/remote_gdb.hh b/src/base/remote_gdb.hh index b7abf5116..90b53e53f 100644 --- a/src/base/remote_gdb.hh +++ b/src/base/remote_gdb.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __REMOTE_GDB_HH__ @@ -37,7 +39,7 @@ #include "base/socket.hh" class System; -class ExecContext; +class ThreadContext; class PhysicalMemory; class GDBListener; @@ -78,7 +80,7 @@ class RemoteGDB System *system; PhysicalMemory *pmem; - ExecContext *context; + ThreadContext *context; protected: uint8_t getbyte(); @@ -96,10 +98,10 @@ class RemoteGDB template <class T> void write(Addr addr, T data); public: - RemoteGDB(System *system, ExecContext *context); + RemoteGDB(System *system, ThreadContext *context); ~RemoteGDB(); - void replaceExecContext(ExecContext *xc) { context = xc; } + void replaceThreadContext(ThreadContext *tc) { context = tc; } void attach(int fd); void detach(); @@ -131,7 +133,7 @@ class RemoteGDB HardBreakpoint(RemoteGDB *_gdb, Addr addr); std::string name() { return gdb->name() + ".hwbkpt"; } - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; friend class HardBreakpoint; diff --git a/src/base/res_list.hh b/src/base/res_list.hh index 960ed108e..442280e15 100644 --- a/src/base/res_list.hh +++ b/src/base/res_list.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Raasch + * Nathan Binkert */ #ifndef __RES_LIST_HH__ diff --git a/src/base/sat_counter.cc b/src/base/sat_counter.cc index 7920f6c81..8980275eb 100644 --- a/src/base/sat_counter.cc +++ b/src/base/sat_counter.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu */ #include <sstream> diff --git a/src/base/sat_counter.hh b/src/base/sat_counter.hh index d7be17b6f..79de11156 100644 --- a/src/base/sat_counter.hh +++ b/src/base/sat_counter.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Raasch + * Steve Reinhardt */ #ifndef __SAT_COUNTER_HH__ diff --git a/src/base/sched_list.hh b/src/base/sched_list.hh index f794e3514..56ee2f8ec 100644 --- a/src/base/sched_list.hh +++ b/src/base/sched_list.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Raasch */ #ifndef SCHED_LIST_HH diff --git a/src/base/socket.cc b/src/base/socket.cc index 45a60e7e3..adcc48735 100644 --- a/src/base/socket.cc +++ b/src/base/socket.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/base/socket.hh b/src/base/socket.hh index 848405c09..8e55eae72 100644 --- a/src/base/socket.hh +++ b/src/base/socket.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __SOCKET_HH__ diff --git a/src/base/statistics.cc b/src/base/statistics.cc index 20de46347..2acef83c5 100644 --- a/src/base/statistics.cc +++ b/src/base/statistics.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iomanip> @@ -41,7 +43,6 @@ #include "base/time.hh" #include "base/trace.hh" #include "base/stats/statdb.hh" -#include "config/stats_binning.hh" using namespace std; @@ -172,12 +173,6 @@ FormulaBase::size() const return root->size(); } -bool -FormulaBase::binned() const -{ - return root && root->binned(); -} - void FormulaBase::reset() { @@ -236,33 +231,6 @@ Formula::operator+=(Temp r) return *this; } -MainBin::MainBin(const string &name) - : _name(name), mem(NULL), memsize(-1) -{ - Database::regBin(this, name); -} - -MainBin::~MainBin() -{ - if (mem) - delete [] mem; -} - -char * -MainBin::memory(off_t off) -{ - if (memsize == -1) - memsize = ceilPow2((size_t) offset()); - - if (!mem) { - mem = new char[memsize]; - memset(mem, 0, memsize); - } - - assert(offset() <= size()); - return mem + off; -} - void check() { @@ -285,13 +253,6 @@ check() Database::stats().sort(StatData::less); -#if STATS_BINNING - if (MainBin::curBin() == NULL) { - static MainBin mainBin("main bin"); - mainBin.activate(); - } -#endif - if (i == end) return; @@ -311,39 +272,14 @@ CallbackQueue resetQueue; void reset() { - // reset non-binned stats Database::stat_list_t::iterator i = Database::stats().begin(); Database::stat_list_t::iterator end = Database::stats().end(); while (i != end) { StatData *data = *i; - if (!data->binned()) - data->reset(); + data->reset(); ++i; } - // save the bin so we can go back to where we were - MainBin *orig = MainBin::curBin(); - - // reset binned stats - Database::bin_list_t::iterator bi = Database::bins().begin(); - Database::bin_list_t::iterator be = Database::bins().end(); - while (bi != be) { - MainBin *bin = *bi; - bin->activate(); - - i = Database::stats().begin(); - while (i != end) { - StatData *data = *i; - if (data->binned()) - data->reset(); - ++i; - } - ++bi; - } - - // restore bin - MainBin::curBin() = orig; - resetQueue.process(); } diff --git a/src/base/statistics.hh b/src/base/statistics.hh index dd507c091..59f219c07 100644 --- a/src/base/statistics.hh +++ b/src/base/statistics.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Erik Hallnor */ /** @file @@ -57,11 +60,9 @@ #include "base/intmath.hh" #include "base/refcnt.hh" #include "base/str.hh" -#include "base/stats/bin.hh" #include "base/stats/flags.hh" #include "base/stats/visit.hh" #include "base/stats/types.hh" -#include "config/stats_binning.hh" #include "sim/host.hh" class Callback; @@ -100,11 +101,6 @@ struct StatData virtual ~StatData(); /** - * @return true if the stat is binned. - */ - virtual bool binned() const = 0; - - /** * Reset the corresponding stat to the default state. */ virtual void reset() = 0; @@ -157,7 +153,6 @@ class ScalarStatData : public ScalarData public: ScalarStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return s.binned(); } virtual bool check() const { return s.check(); } virtual Counter value() const { return s.value(); } virtual Result result() const { return s.result(); } @@ -200,7 +195,6 @@ class VectorStatData : public VectorData public: VectorStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return s.binned(); } virtual bool check() const { return s.check(); } virtual bool zero() const { return s.zero(); } virtual void reset() { s.reset(); } @@ -258,7 +252,6 @@ class DistStatData : public DistData public: DistStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return s.binned(); } virtual bool check() const { return s.check(); } virtual void reset() { s.reset(); } virtual bool zero() const { return s.zero(); } @@ -297,12 +290,10 @@ class VectorDistStatData : public VectorDistData { protected: Stat &s; - typedef typename Stat::bin_t bin_t; public: VectorDistStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return bin_t::binned; } virtual bool check() const { return s.check(); } virtual void reset() { s.reset(); } virtual size_t size() const { return s.size(); } @@ -339,12 +330,10 @@ class Vector2dStatData : public Vector2dData { protected: Stat &s; - typedef typename Stat::bin_t bin_t; public: Vector2dStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return bin_t::binned; } virtual bool check() const { return s.check(); } virtual void reset() { s.reset(); } virtual bool zero() const { return s.zero(); } @@ -356,7 +345,6 @@ class Vector2dStatData : public Vector2dData } }; - class DataAccess { protected: @@ -612,22 +600,16 @@ struct StatStor * changes. This allows the quick calculation of a per cycle count of the item * being watched. This is good for keeping track of residencies in structures * among other things. - * @todo add lateny to the stat and fix binning. */ struct AvgStor { public: /** The paramaters for this storage type */ - struct Params - { - /** - * The current count. We stash this here because the current - * value is not a binned value. - */ - Counter current; - }; + struct Params { }; private: + /** The current count. */ + Counter current; /** The total count for all cycles. */ mutable Result total; /** The cycle that current last changed. */ @@ -637,7 +619,7 @@ struct AvgStor /** * Build and initializes this stat storage. */ - AvgStor(Params &p) : total(0), last(0) { p.current = Counter(); } + AvgStor(Params &p) : current(0), total(0), last(0) { } /** * Set the current count to the one provided, update the total and last @@ -646,9 +628,9 @@ struct AvgStor * @param p The parameters for this storage. */ void set(Counter val, Params &p) { - total += p.current * (curTick - last); + total += current * (curTick - last); last = curTick; - p.current = val; + current = val; } /** @@ -656,21 +638,21 @@ struct AvgStor * @param val The amount to increment. * @param p The parameters for this storage. */ - void inc(Counter val, Params &p) { set(p.current + val, p); } + void inc(Counter val, Params &p) { set(current + val, p); } /** * Deccrement the current count by the provided value, calls set. * @param val The amount to decrement. * @param p The parameters for this storage. */ - void dec(Counter val, Params &p) { set(p.current - val, p); } + void dec(Counter val, Params &p) { set(current - val, p); } /** * Return the current count. * @param p The parameters for this storage. * @return The current count. */ - Counter value(const Params &p) const { return p.current; } + Counter value(const Params &p) const { return current; } /** * Return the current average. @@ -679,9 +661,9 @@ struct AvgStor */ Result result(const Params &p) const { - total += p.current * (curTick - last); + total += current * (curTick - last); last = curTick; - return (Result)(total + p.current) / (Result)(curTick + 1); + return (Result)(total + current) / (Result)(curTick + 1); } /** @@ -701,39 +683,53 @@ struct AvgStor /** * Implementation of a scalar stat. The type of stat is determined by the - * Storage template. The storage for this stat is held within the Bin class. - * This allows for breaking down statistics across multiple bins easily. + * Storage template. */ -template <class Storage, class Bin> +template <class Stor> class ScalarBase : public DataAccess { public: + typedef Stor Storage; + /** Define the params of the storage class. */ - typedef typename Storage::Params params_t; - /** Define the bin type. */ - typedef typename Bin::template Bin<Storage> bin_t; + typedef typename Storage::Params Params; protected: - /** The bin of this stat. */ - bin_t bin; + /** The storage of this stat. */ + char storage[sizeof(Storage)]; + /** The parameters for this stat. */ - params_t params; + Params params; protected: /** - * Retrieve the storage from the bin. - * @return The storage object for this stat. + * Retrieve the storage. + * @param index The vector index to access. + * @return The storage object at the given index. */ - Storage *data() { return bin.data(params); } + Storage * + data() + { + return reinterpret_cast<Storage *>(storage); + } + /** - * Retrieve a const pointer to the storage from the bin. - * @return A const pointer to the storage object for this stat. + * Retrieve a const pointer to the storage. + * for the given index. + * @param index The vector index to access. + * @return A const pointer to the storage object at the given index. */ - const Storage *data() const + const Storage * + data() const + { + return reinterpret_cast<const Storage *>(storage); + } + + void + doInit() { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(*_params); + new (storage) Storage(params); + setInit(); } public: @@ -748,9 +744,7 @@ class ScalarBase : public DataAccess * Create and initialize this stat, register it with the database. */ ScalarBase() - { - bin.init(params); - } + { } public: // Common operators for stats @@ -799,18 +793,13 @@ class ScalarBase : public DataAccess * @return 1. */ size_t size() const { return 1; } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - bool binned() const { return bin_t::binned; } - bool check() const { return bin.initialized(); } + bool check() const { return true; } /** * Reset stat value to default */ - void reset() { bin.reset(); } + void reset() { data()->reset(); } Counter value() { return data()->value(params); } @@ -826,7 +815,6 @@ class ProxyData : public ScalarData { public: virtual void visit(Visit &visitor) { visitor.visit(*this); } - virtual bool binned() const { return false; } virtual std::string str() const { return to_string(value()); } virtual size_t size() const { return 1; } virtual bool zero() const { return value() == 0; } @@ -888,7 +876,6 @@ class ValueBase : public DataAccess Result total() const { return proxy->total(); }; size_t size() const { return proxy->size(); } - bool binned() const { return proxy->binned(); } std::string str() const { return proxy->str(); } bool zero() const { return proxy->zero(); } bool check() const { return proxy != NULL; } @@ -900,195 +887,62 @@ class ValueBase : public DataAccess // Vector Statistics // ////////////////////////////////////////////////////////////////////// -template <class Storage, class Bin> -class ScalarProxy; - -/** - * Implementation of a vector of stats. The type of stat is determined by the - * Storage class. @sa ScalarBase - */ -template <class Storage, class Bin> -class VectorBase : public DataAccess -{ - public: - /** Define the params of the storage class. */ - typedef typename Storage::Params params_t; - /** Define the bin type. */ - typedef typename Bin::template VectorBin<Storage> bin_t; - - protected: - /** The bin of this stat. */ - bin_t bin; - /** The parameters for this stat. */ - params_t params; - - protected: - /** - * Retrieve the storage from the bin for the given index. - * @param index The vector index to access. - * @return The storage object at the given index. - */ - Storage *data(int index) { return bin.data(index, params); } - /** - * Retrieve a const pointer to the storage from the bin - * for the given index. - * @param index The vector index to access. - * @return A const pointer to the storage object at the given index. - */ - const Storage *data(int index) const - { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(index, *_params); - } - - public: - void value(VCounter &vec) const - { - vec.resize(size()); - for (int i = 0; i < size(); ++i) - vec[i] = data(i)->value(params); - } - - /** - * Copy the values to a local vector and return a reference to it. - * @return A reference to a vector of the stat values. - */ - void result(VResult &vec) const - { - vec.resize(size()); - for (int i = 0; i < size(); ++i) - vec[i] = data(i)->result(params); - } - - /** - * @return True is stat is binned. - */ - bool binned() const { return bin_t::binned; } - - /** - * Return a total of all entries in this vector. - * @return The total of all vector entries. - */ - Result total() const { - Result total = 0.0; - for (int i = 0; i < size(); ++i) - total += data(i)->result(params); - return total; - } - - /** - * @return the number of elements in this vector. - */ - size_t size() const { return bin.size(); } - - bool zero() const - { - for (int i = 0; i < size(); ++i) - if (data(i)->zero()) - return true; - return false; - } - - bool check() const { return bin.initialized(); } - void reset() { bin.reset(); } - - public: - VectorBase() {} - - /** Friend this class with the associated scalar proxy. */ - friend class ScalarProxy<Storage, Bin>; - - /** - * Return a reference (ScalarProxy) to the stat at the given index. - * @param index The vector index to access. - * @return A reference of the stat. - */ - ScalarProxy<Storage, Bin> operator[](int index); - - void update(StatData *data) {} -}; - -const StatData * getStatData(const void *stat); /** * A proxy class to access the stat at a given index in a VectorBase stat. * Behaves like a ScalarBase. */ -template <class Storage, class Bin> +template <class Stat> class ScalarProxy { - public: - /** Define the params of the storage class. */ - typedef typename Storage::Params params_t; - /** Define the bin type. */ - typedef typename Bin::template VectorBin<Storage> bin_t; - private: - /** Pointer to the bin in the parent VectorBase. */ - bin_t *bin; - /** Pointer to the params in the parent VectorBase. */ - params_t *params; + /** Pointer to the parent Vector. */ + Stat *stat; + /** The index to access in the parent VectorBase. */ int index; - /** Keep a pointer to the original stat so was can get data */ - void *stat; - - protected: - /** - * Retrieve the storage from the bin. - * @return The storage from the bin for this stat. - */ - Storage *data() { return bin->data(index, *params); } - /** - * Retrieve a const pointer to the storage from the bin. - * @return A const pointer to the storage for this stat. - */ - const Storage *data() const - { - bin_t *_bin = const_cast<bin_t *>(bin); - params_t *_params = const_cast<params_t *>(params); - return _bin->data(index, *_params); - } public: /** * Return the current value of this stat as its base type. * @return The current value. */ - Counter value() const { return data()->value(*params); } + Counter value() const { return stat->data(index)->value(stat->params); } /** * Return the current value of this statas a result type. * @return The current value. */ - Result result() const { return data()->result(*params); } + Result result() const { return stat->data(index)->result(stat->params); } public: /** * Create and initialize this proxy, do not register it with the database. - * @param b The bin to use. * @param p The params to use. * @param i The index to access. */ - ScalarProxy(bin_t &b, params_t &p, int i, void *s) - : bin(&b), params(&p), index(i), stat(s) {} + ScalarProxy(Stat *s, int i) + : stat(s), index(i) + { + assert(stat); + } + /** * Create a copy of the provided ScalarProxy. * @param sp The proxy to copy. */ ScalarProxy(const ScalarProxy &sp) - : bin(sp.bin), params(sp.params), index(sp.index), stat(sp.stat) {} + : stat(sp.stat), index(sp.index) + {} + /** * Set this proxy equal to the provided one. * @param sp The proxy to copy. * @return A reference to this proxy. */ const ScalarProxy &operator=(const ScalarProxy &sp) { - bin = sp.bin; - params = sp.params; - index = sp.index; stat = sp.stat; + index = sp.index; return *this; } @@ -1098,12 +952,12 @@ class ScalarProxy * Increment the stat by 1. This calls the associated storage object inc * function. */ - void operator++() { data()->inc(1, *params); } + void operator++() { stat->data(index)->inc(1, stat->params); } /** * Decrement the stat by 1. This calls the associated storage object dec * function. */ - void operator--() { data()->dec(1, *params); } + void operator--() { stat->data(index)->dec(1, stat->params); } /** Increment the stat by 1. */ void operator++(int) { ++*this; } @@ -1116,7 +970,7 @@ class ScalarProxy * @param v The new value. */ template <typename U> - void operator=(const U &v) { data()->set(v, *params); } + void operator=(const U &v) { stat->data(index)->set(v, stat->params); } /** * Increment the stat by the given value. This calls the associated @@ -1124,7 +978,7 @@ class ScalarProxy * @param v The value to add. */ template <typename U> - void operator+=(const U &v) { data()->inc(v, *params); } + void operator+=(const U &v) { stat->data(index)->inc(v, stat->params); } /** * Decrement the stat by the given value. This calls the associated @@ -1132,7 +986,7 @@ class ScalarProxy * @param v The value to substract. */ template <typename U> - void operator-=(const U &v) { data()->dec(v, *params); } + void operator-=(const U &v) { stat->data(index)->dec(v, stat->params); } /** * Return the number of elements, always 1 for a scalar. @@ -1141,192 +995,356 @@ class ScalarProxy size_t size() const { return 1; } /** - * Return true if stat is binned. - *@return false since Proxies aren't printed/binned - */ - bool binned() const { return false; } - - /** * This stat has no state. Nothing to reset */ void reset() { } public: - const StatData *statData() const { return getStatData(stat); } - std::string str() const + std::string + str() const { - return csprintf("%s[%d]", this->statData()->name, index); + return csprintf("%s[%d]", stat->str(), index); } }; -template <class Storage, class Bin> -inline ScalarProxy<Storage, Bin> -VectorBase<Storage, Bin>::operator[](int index) +/** + * Implementation of a vector of stats. The type of stat is determined by the + * Storage class. @sa ScalarBase + */ +template <class Stor> +class VectorBase : public DataAccess { - assert (index >= 0 && index < size()); - return ScalarProxy<Storage, Bin>(bin, params, index, this); -} + public: + typedef Stor Storage; -template <class Storage, class Bin> -class VectorProxy; + /** Define the params of the storage class. */ + typedef typename Storage::Params Params; -template <class Storage, class Bin> -class Vector2dBase : public DataAccess -{ - public: - typedef typename Storage::Params params_t; - typedef typename Bin::template VectorBin<Storage> bin_t; + /** Proxy type */ + typedef ScalarProxy<VectorBase<Storage> > Proxy; + + friend class ScalarProxy<VectorBase<Storage> >; protected: - size_t x; - size_t y; - bin_t bin; - params_t params; + /** The storage of this stat. */ + Storage *storage; + size_t _size; + + /** The parameters for this stat. */ + Params params; protected: - Storage *data(int index) { return bin.data(index, params); } - const Storage *data(int index) const + /** + * Retrieve the storage. + * @param index The vector index to access. + * @return The storage object at the given index. + */ + Storage *data(int index) { return &storage[index]; } + + /** + * Retrieve a const pointer to the storage. + * @param index The vector index to access. + * @return A const pointer to the storage object at the given index. + */ + const Storage *data(int index) const { return &storage[index]; } + + void + doInit(int s) { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(index, *_params); + assert(s > 0 && "size must be positive!"); + assert(!storage && "already initialized"); + _size = s; + + char *ptr = new char[_size * sizeof(Storage)]; + storage = reinterpret_cast<Storage *>(ptr); + + for (int i = 0; i < _size; ++i) + new (&storage[i]) Storage(params); + + setInit(); } public: - Vector2dBase() {} + void value(VCounter &vec) const + { + vec.resize(size()); + for (int i = 0; i < size(); ++i) + vec[i] = data(i)->value(params); + } - void update(Vector2dData *data) + /** + * Copy the values to a local vector and return a reference to it. + * @return A reference to a vector of the stat values. + */ + void result(VResult &vec) const { - int size = this->size(); - data->cvec.resize(size); - for (int i = 0; i < size; ++i) - data->cvec[i] = this->data(i)->value(params); + vec.resize(size()); + for (int i = 0; i < size(); ++i) + vec[i] = data(i)->result(params); } - std::string ysubname(int i) const { return (*this->y_subnames)[i]; } + /** + * Return a total of all entries in this vector. + * @return The total of all vector entries. + */ + Result total() const { + Result total = 0.0; + for (int i = 0; i < size(); ++i) + total += data(i)->result(params); + return total; + } + + /** + * @return the number of elements in this vector. + */ + size_t size() const { return _size; } + + bool + zero() const + { + for (int i = 0; i < size(); ++i) + if (data(i)->zero()) + return false; + return true; + } + + bool + check() const + { + return storage != NULL; + } + + void + reset() + { + for (int i = 0; i < size(); ++i) + data(i)->reset(); + } - friend class VectorProxy<Storage, Bin>; - VectorProxy<Storage, Bin> operator[](int index); + public: + VectorBase() + : storage(NULL) + {} - size_t size() const { return bin.size(); } - bool zero() const { return data(0)->value(params) == 0.0; } + ~VectorBase() + { + if (!storage) + return; + + for (int i = 0; i < _size; ++i) + data(i)->~Storage(); + delete [] reinterpret_cast<char *>(storage); + } /** - * Reset stat value to default + * Return a reference (ScalarProxy) to the stat at the given index. + * @param index The vector index to access. + * @return A reference of the stat. */ - void reset() { bin.reset(); } + Proxy + operator[](int index) + { + assert (index >= 0 && index < size()); + return Proxy(this, index); + } - bool check() { return bin.initialized(); } + void update(StatData *data) {} }; -template <class Storage, class Bin> +template <class Stat> class VectorProxy { - public: - typedef typename Storage::Params params_t; - typedef typename Bin::template VectorBin<Storage> bin_t; - private: - bin_t *bin; - params_t *params; + Stat *stat; int offset; int len; - void *stat; private: - mutable VResult *vec; + mutable VResult vec; - Storage *data(int index) { + typename Stat::Storage * + data(int index) + { assert(index < len); - return bin->data(offset + index, *params); + return stat->data(offset + index); } - const Storage *data(int index) const { - bin_t *_bin = const_cast<bin_t *>(bin); - params_t *_params = const_cast<params_t *>(params); - return _bin->data(offset + index, *_params); + const typename Stat::Storage * + data(int index) const + { + assert(index < len); + return const_cast<Stat *>(stat)->data(offset + index); } public: - const VResult &result() const { - if (vec) - vec->resize(size()); - else - vec = new VResult(size()); + const VResult & + result() const + { + vec.resize(size()); for (int i = 0; i < size(); ++i) - (*vec)[i] = data(i)->result(*params); + vec[i] = data(i)->result(stat->params); - return *vec; + return vec; } - Result total() const { - Result total = 0.0; + Result + total() const + { + Result total = 0; for (int i = 0; i < size(); ++i) - total += data(i)->result(*params); + total += data(i)->result(stat->params); return total; } public: - VectorProxy(bin_t &b, params_t &p, int o, int l, void *s) - : bin(&b), params(&p), offset(o), len(l), stat(s), vec(NULL) + VectorProxy(Stat *s, int o, int l) + : stat(s), offset(o), len(l) { } VectorProxy(const VectorProxy &sp) - : bin(sp.bin), params(sp.params), offset(sp.offset), len(sp.len), - stat(sp.stat), vec(NULL) + : stat(sp.stat), offset(sp.offset), len(sp.len) { } - ~VectorProxy() + const VectorProxy & + operator=(const VectorProxy &sp) { - if (vec) - delete vec; - } - - const VectorProxy &operator=(const VectorProxy &sp) - { - bin = sp.bin; - params = sp.params; + stat = sp.stat; offset = sp.offset; len = sp.len; - stat = sp.stat; - if (vec) - delete vec; - vec = NULL; return *this; } - ScalarProxy<Storage, Bin> operator[](int index) + ScalarProxy<Stat> operator[](int index) { assert (index >= 0 && index < size()); - return ScalarProxy<Storage, Bin>(*bin, *params, offset + index, stat); + return ScalarProxy<Stat>(stat, offset + index); } size_t size() const { return len; } /** - * Return true if stat is binned. - *@return false since Proxies aren't printed/binned - */ - bool binned() const { return false; } - - /** * This stat has no state. Nothing to reset. */ void reset() { } }; -template <class Storage, class Bin> -inline VectorProxy<Storage, Bin> -Vector2dBase<Storage, Bin>::operator[](int index) +template <class Stor> +class Vector2dBase : public DataAccess { - int offset = index * y; - assert (index >= 0 && offset < size()); - return VectorProxy<Storage, Bin>(bin, params, offset, y, this); -} + public: + typedef Stor Storage; + typedef typename Storage::Params Params; + typedef VectorProxy<Vector2dBase<Storage> > Proxy; + friend class ScalarProxy<Vector2dBase<Storage> >; + friend class VectorProxy<Vector2dBase<Storage> >; + + protected: + size_t x; + size_t y; + size_t _size; + Storage *storage; + Params params; + + protected: + Storage *data(int index) { return &storage[index]; } + const Storage *data(int index) const { return &storage[index]; } + + void + doInit(int _x, int _y) + { + assert(_x > 0 && _y > 0 && "sizes must be positive!"); + assert(!storage && "already initialized"); + + Vector2dData *statdata = dynamic_cast<Vector2dData *>(find()); + + x = _x; + y = _y; + statdata->x = _x; + statdata->y = _y; + _size = x * y; + + char *ptr = new char[_size * sizeof(Storage)]; + storage = reinterpret_cast<Storage *>(ptr); + + for (int i = 0; i < _size; ++i) + new (&storage[i]) Storage(params); + + setInit(); + } + + public: + Vector2dBase() + : storage(NULL) + {} + + ~Vector2dBase() + { + if (!storage) + return; + + for (int i = 0; i < _size; ++i) + data(i)->~Storage(); + delete [] reinterpret_cast<char *>(storage); + } + + void + update(Vector2dData *newdata) + { + int size = this->size(); + newdata->cvec.resize(size); + for (int i = 0; i < size; ++i) + newdata->cvec[i] = data(i)->value(params); + } + + std::string ysubname(int i) const { return (*this->y_subnames)[i]; } + + Proxy + operator[](int index) + { + int offset = index * y; + assert (index >= 0 && offset + index < size()); + return Proxy(this, offset, y); + } + + + size_t + size() const + { + return _size; + } + + bool + zero() const + { + return data(0)->zero(); +#if 0 + for (int i = 0; i < size(); ++i) + if (!data(i)->zero()) + return false; + return true; +#endif + } + + /** + * Reset stat value to default + */ + void + reset() + { + for (int i = 0; i < size(); ++i) + data(i)->reset(); + } + + bool + check() + { + return storage != NULL; + } +}; ////////////////////////////////////////////////////////////////////// // @@ -1373,14 +1391,8 @@ struct DistStor VCounter cvec; public: - /** - * Construct this storage with the supplied params. - * @param params The parameters. - */ DistStor(const Params ¶ms) - : min_val(INT_MAX), max_val(INT_MIN), underflow(Counter()), - overflow(Counter()), sum(Counter()), squares(Counter()), - samples(Counter()), cvec(params.size) + : cvec(params.size) { reset(); } @@ -1615,36 +1627,46 @@ struct AvgFancy * Implementation of a distribution stat. The type of distribution is * determined by the Storage template. @sa ScalarBase */ -template <class Storage, class Bin> +template <class Stor> class DistBase : public DataAccess { public: + typedef Stor Storage; /** Define the params of the storage class. */ - typedef typename Storage::Params params_t; - /** Define the bin type. */ - typedef typename Bin::template Bin<Storage> bin_t; + typedef typename Storage::Params Params; protected: - /** The bin of this stat. */ - bin_t bin; + /** The storage for this stat. */ + char storage[sizeof(Storage)]; + /** The parameters for this stat. */ - params_t params; + Params params; protected: /** - * Retrieve the storage from the bin. + * Retrieve the storage. * @return The storage object for this stat. */ - Storage *data() { return bin.data(params); } + Storage *data() + { + return reinterpret_cast<Storage *>(storage); + } + /** - * Retrieve a const pointer to the storage from the bin. + * Retrieve a const pointer to the storage. * @return A const pointer to the storage object for this stat. */ - const Storage *data() const + const Storage * + data() const { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(*_params); + return reinterpret_cast<const Storage *>(storage); + } + + void + doInit() + { + new (storage) Storage(params); + setInit(); } public: @@ -1675,65 +1697,122 @@ class DistBase : public DataAccess base->data.fancy = Storage::fancy; data()->update(&(base->data), params); } - /** - * @return True is stat is binned. - */ - bool binned() const { return bin_t::binned; } + /** * Reset stat value to default */ - void reset() + void + reset() { - bin.reset(); + data()->reset(); } - bool check() { return bin.initialized(); } + bool + check() + { + return true; + } }; -template <class Storage, class Bin> +template <class Stat> class DistProxy; -template <class Storage, class Bin> +template <class Stor> class VectorDistBase : public DataAccess { public: - typedef typename Storage::Params params_t; - typedef typename Bin::template VectorBin<Storage> bin_t; + typedef Stor Storage; + typedef typename Storage::Params Params; + typedef DistProxy<VectorDistBase<Storage> > Proxy; + friend class DistProxy<VectorDistBase<Storage> >; protected: - bin_t bin; - params_t params; + Storage *storage; + size_t _size; + Params params; protected: - Storage *data(int index) { return bin.data(index, params); } - const Storage *data(int index) const + Storage * + data(int index) { - bin_t *_bin = const_cast<bin_t *>(&bin); - params_t *_params = const_cast<params_t *>(¶ms); - return _bin->data(index, *_params); + return &storage[index]; + } + + const Storage * + data(int index) const + { + return &storage[index]; + } + + void + doInit(int s) + { + assert(s > 0 && "size must be positive!"); + assert(!storage && "already initialized"); + _size = s; + + char *ptr = new char[_size * sizeof(Storage)]; + storage = reinterpret_cast<Storage *>(ptr); + + for (int i = 0; i < _size; ++i) + new (&storage[i]) Storage(params); + + setInit(); } public: - VectorDistBase() {} + VectorDistBase() + : storage(NULL) + {} + + ~VectorDistBase() + { + if (!storage) + return ; - friend class DistProxy<Storage, Bin>; - DistProxy<Storage, Bin> operator[](int index); - const DistProxy<Storage, Bin> operator[](int index) const; + for (int i = 0; i < _size; ++i) + data(i)->~Storage(); + delete [] reinterpret_cast<char *>(storage); + } + + Proxy operator[](int index); + + size_t + size() const + { + return _size; + } + + bool + zero() const + { + return false; +#if 0 + for (int i = 0; i < size(); ++i) + if (!data(i)->zero(params)) + return false; + return true; +#endif + } - size_t size() const { return bin.size(); } - bool zero() const { return false; } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - bool binned() const { return bin_t::binned; } /** * Reset stat value to default */ - void reset() { bin.reset(); } + void + reset() + { + for (int i = 0; i < size(); ++i) + data(i)->reset(); + } - bool check() { return bin.initialized(); } - void update(VectorDistData *base) + bool + check() + { + return storage != NULL; + } + + void + update(VectorDistData *base) { int size = this->size(); base->data.resize(size); @@ -1744,75 +1823,75 @@ class VectorDistBase : public DataAccess } }; -template <class Storage, class Bin> +template <class Stat> class DistProxy { - public: - typedef typename Storage::Params params_t; - typedef typename Bin::template Bin<Storage> bin_t; - typedef VectorDistBase<Storage, Bin> base_t; - private: - union { - base_t *stat; - const base_t *cstat; - }; + Stat *stat; int index; protected: - Storage *data() { return stat->data(index); } - const Storage *data() const { return cstat->data(index); } + typename Stat::Storage *data() { return stat->data(index); } + const typename Stat::Storage *data() const { return stat->data(index); } public: - DistProxy(const VectorDistBase<Storage, Bin> &s, int i) - : cstat(&s), index(i) {} + DistProxy(Stat *s, int i) + : stat(s), index(i) + {} + DistProxy(const DistProxy &sp) - : cstat(sp.cstat), index(sp.index) {} - const DistProxy &operator=(const DistProxy &sp) { - cstat = sp.cstat; index = sp.index; return *this; + : stat(sp.stat), index(sp.index) + {} + + const DistProxy &operator=(const DistProxy &sp) + { + stat = sp.stat; + index = sp.index; + return *this; } public: template <typename U> - void sample(const U &v, int n = 1) { data()->sample(v, n, cstat->params); } + void + sample(const U &v, int n = 1) + { + data()->sample(v, n, stat->params); + } + + size_t + size() const + { + return 1; + } + + bool + zero() const + { + return data()->zero(stat->params); + } - size_t size() const { return 1; } - bool zero() const { return data()->zero(cstat->params); } - /** - * Return true if stat is binned. - *@return false since Proxies are not binned/printed. - */ - bool binned() const { return false; } /** * Proxy has no state. Nothing to reset. */ void reset() { } }; -template <class Storage, class Bin> -inline DistProxy<Storage, Bin> -VectorDistBase<Storage, Bin>::operator[](int index) -{ - assert (index >= 0 && index < size()); - return DistProxy<Storage, Bin>(*this, index); -} - -template <class Storage, class Bin> -inline const DistProxy<Storage, Bin> -VectorDistBase<Storage, Bin>::operator[](int index) const +template <class Storage> +inline typename VectorDistBase<Storage>::Proxy +VectorDistBase<Storage>::operator[](int index) { assert (index >= 0 && index < size()); - return DistProxy<Storage, Bin>(*this, index); + return typename VectorDistBase<Storage>::Proxy(this, index); } #if 0 -template <class Storage, class Bin> +template <class Storage> Result -VectorDistBase<Storage, Bin>::total(int index) const +VectorDistBase<Storage>::total(int index) const { int total = 0; - for (int i=0; i < x_size(); ++i) { - total += data(i)->result(*params); + for (int i = 0; i < x_size(); ++i) { + total += data(i)->result(stat->params); } } #endif @@ -1845,11 +1924,6 @@ class Node : public RefCounted * @return The total of the result vector. */ virtual Result total() const = 0; - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - virtual bool binned() const = 0; /** * @@ -1876,11 +1950,6 @@ class ScalarStatNode : public Node virtual Result total() const { return data->result(); }; virtual size_t size() const { return 1; } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - virtual bool binned() const { return data->binned(); } /** * @@ -1888,34 +1957,45 @@ class ScalarStatNode : public Node virtual std::string str() const { return data->name; } }; -template <class Storage, class Bin> +template <class Stat> class ScalarProxyNode : public Node { private: - const ScalarProxy<Storage, Bin> proxy; + const ScalarProxy<Stat> proxy; mutable VResult vresult; public: - ScalarProxyNode(const ScalarProxy<Storage, Bin> &p) - : proxy(p), vresult(1) { } - virtual const VResult &result() const + ScalarProxyNode(const ScalarProxy<Stat> &p) + : proxy(p), vresult(1) + { } + + virtual const VResult & + result() const { vresult[0] = proxy.result(); return vresult; } - virtual Result total() const { return proxy.result(); }; - virtual size_t size() const { return 1; } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - virtual bool binned() const { return proxy.binned(); } + virtual Result + total() const + { + return proxy.result(); + } + + virtual size_t + size() const + { + return 1; + } /** * */ - virtual std::string str() const { return proxy.str(); } + virtual std::string + str() const + { + return proxy.str(); + } }; class VectorStatNode : public Node @@ -1929,11 +2009,6 @@ class VectorStatNode : public Node virtual Result total() const { return data->total(); }; virtual size_t size() const { return data->size(); } - /** - * Return true if stat is binned. - *@return True is stat is binned. - */ - virtual bool binned() const { return data->binned(); } virtual std::string str() const { return data->name; } }; @@ -1949,13 +2024,6 @@ class ConstNode : public Node const VResult &result() const { return vresult; } virtual Result total() const { return vresult[0]; }; virtual size_t size() const { return 1; } - - /** - * Return true if stat is binned. - *@return False since constants aren't binned. - */ - virtual bool binned() const { return false; } - virtual std::string str() const { return to_string(vresult[0]); } }; @@ -2029,11 +2097,6 @@ class UnaryNode : public Node } virtual size_t size() const { return l->size(); } - /** - * Return true if child of node is binned. - *@return True if child of node is binned. - */ - virtual bool binned() const { return l->binned(); } virtual std::string str() const { @@ -2100,11 +2163,6 @@ class BinaryNode : public Node return ls; } } - /** - * Return true if any children of node are binned - *@return True if either child of node is binned. - */ - virtual bool binned() const { return (l->binned() || r->binned()); } virtual std::string str() const { @@ -2153,11 +2211,6 @@ class SumNode : public Node } virtual size_t size() const { return 1; } - /** - * Return true if child of node is binned. - *@return True if child of node is binned. - */ - virtual bool binned() const { return l->binned(); } virtual std::string str() const { @@ -2173,40 +2226,24 @@ class SumNode : public Node ////////////////////////////////////////////////////////////////////// /** * @defgroup VisibleStats "Statistic Types" - * These are the statistics that are used in the simulator. By default these - * store counters and don't use binning, but are templatized to accept any type - * and any Bin class. + * These are the statistics that are used in the simulator. * @{ */ /** - * This is an easy way to assign all your stats to be binned or not - * binned. If the typedef is NoBin, nothing is binned. If it is - * MainBin, then all stats are binned under that Bin. - */ -#if STATS_BINNING -typedef MainBin DefaultBin; -#else -typedef NoBin DefaultBin; -#endif - -/** * This is a simple scalar statistic, like a counter. * @sa Stat, ScalarBase, StatStor */ -template <class Bin = DefaultBin> -class Scalar - : public Wrap<Scalar<Bin>, - ScalarBase<StatStor, Bin>, - ScalarStatData> +template<int N = 0> +class Scalar : public Wrap<Scalar<N>, ScalarBase<StatStor>, ScalarStatData> { public: /** The base implementation. */ - typedef ScalarBase<StatStor, Bin> Base; + typedef ScalarBase<StatStor> Base; Scalar() { - this->setInit(); + this->doInit(); } /** @@ -2218,10 +2255,7 @@ class Scalar void operator=(const U &v) { Base::operator=(v); } }; -class Value - : public Wrap<Value, - ValueBase, - ScalarStatData> +class Value : public Wrap<Value, ValueBase, ScalarStatData> { public: /** The base implementation. */ @@ -2246,19 +2280,16 @@ class Value * A stat that calculates the per cycle average of a value. * @sa Stat, ScalarBase, AvgStor */ -template <class Bin = DefaultBin> -class Average - : public Wrap<Average<Bin>, - ScalarBase<AvgStor, Bin>, - ScalarStatData> +template<int N = 0> +class Average : public Wrap<Average<N>, ScalarBase<AvgStor>, ScalarStatData> { public: /** The base implementation. */ - typedef ScalarBase<AvgStor, Bin> Base; + typedef ScalarBase<AvgStor> Base; Average() { - this->setInit(); + this->doInit(); } /** @@ -2274,15 +2305,12 @@ class Average * A vector of scalar stats. * @sa Stat, VectorBase, StatStor */ -template <class Bin = DefaultBin> -class Vector - : public WrapVec<Vector<Bin>, - VectorBase<StatStor, Bin>, - VectorStatData> +template<int N = 0> +class Vector : public WrapVec<Vector<N>, VectorBase<StatStor>, VectorStatData> { public: /** The base implementation. */ - typedef ScalarBase<StatStor, Bin> Base; + typedef ScalarBase<StatStor> Base; /** * Set this vector to have the given size. @@ -2290,9 +2318,7 @@ class Vector * @return A reference to this stat. */ Vector &init(size_t size) { - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2301,11 +2327,9 @@ class Vector * A vector of Average stats. * @sa Stat, VectorBase, AvgStor */ -template <class Bin = DefaultBin> +template<int N = 0> class AverageVector - : public WrapVec<AverageVector<Bin>, - VectorBase<AvgStor, Bin>, - VectorStatData> + : public WrapVec<AverageVector<N>, VectorBase<AvgStor>, VectorStatData> { public: /** @@ -2314,9 +2338,7 @@ class AverageVector * @return A reference to this stat. */ AverageVector &init(size_t size) { - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2325,19 +2347,13 @@ class AverageVector * A 2-Dimensional vecto of scalar stats. * @sa Stat, Vector2dBase, StatStor */ -template <class Bin = DefaultBin> +template<int N = 0> class Vector2d - : public WrapVec2d<Vector2d<Bin>, - Vector2dBase<StatStor, Bin>, - Vector2dStatData> + : public WrapVec2d<Vector2d<N>, Vector2dBase<StatStor>, Vector2dStatData> { public: - Vector2d &init(size_t _x, size_t _y) { - this->statData()->x = this->x = _x; - this->statData()->y = this->y = _y; - this->bin.init(this->x * this->y, this->params); - this->setInit(); - + Vector2d &init(size_t x, size_t y) { + this->doInit(x, y); return *this; } }; @@ -2346,17 +2362,15 @@ class Vector2d * A simple distribution stat. * @sa Stat, DistBase, DistStor */ -template <class Bin = DefaultBin> +template<int N = 0> class Distribution - : public Wrap<Distribution<Bin>, - DistBase<DistStor, Bin>, - DistStatData> + : public Wrap<Distribution<N>, DistBase<DistStor>, DistStatData> { public: /** Base implementation. */ - typedef DistBase<DistStor, Bin> Base; + typedef DistBase<DistStor> Base; /** The Parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2371,9 +2385,7 @@ class Distribution this->params.max = max; this->params.bucket_size = bkt; this->params.size = (int)rint((max - min) / bkt + 1.0); - this->bin.init(this->params); - this->setInit(); - + this->doInit(); return *this; } }; @@ -2382,25 +2394,22 @@ class Distribution * Calculates the mean and variance of all the samples. * @sa Stat, DistBase, FancyStor */ -template <class Bin = DefaultBin> +template<int N = 0> class StandardDeviation - : public Wrap<StandardDeviation<Bin>, - DistBase<FancyStor, Bin>, - DistStatData> + : public Wrap<StandardDeviation<N>, DistBase<FancyStor>, DistStatData> { public: /** The base implementation */ - typedef DistBase<DistStor, Bin> Base; + typedef DistBase<DistStor> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** * Construct and initialize this distribution. */ StandardDeviation() { - this->bin.init(this->params); - this->setInit(); + this->doInit(); } }; @@ -2408,17 +2417,15 @@ class StandardDeviation * Calculates the per cycle mean and variance of the samples. * @sa Stat, DistBase, AvgFancy */ -template <class Bin = DefaultBin> +template<int N = 0> class AverageDeviation - : public Wrap<AverageDeviation<Bin>, - DistBase<AvgFancy, Bin>, - DistStatData> + : public Wrap<AverageDeviation<N>, DistBase<AvgFancy>, DistStatData> { public: /** The base implementation */ - typedef DistBase<DistStor, Bin> Base; + typedef DistBase<DistStor> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2426,8 +2433,7 @@ class AverageDeviation */ AverageDeviation() { - this->bin.init(this->params); - this->setInit(); + this->doInit(); } }; @@ -2435,17 +2441,17 @@ class AverageDeviation * A vector of distributions. * @sa Stat, VectorDistBase, DistStor */ -template <class Bin = DefaultBin> +template<int N = 0> class VectorDistribution - : public WrapVec<VectorDistribution<Bin>, - VectorDistBase<DistStor, Bin>, + : public WrapVec<VectorDistribution<N>, + VectorDistBase<DistStor>, VectorDistStatData> { public: /** The base implementation */ - typedef VectorDistBase<DistStor, Bin> Base; + typedef VectorDistBase<DistStor> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2461,9 +2467,7 @@ class VectorDistribution this->params.max = max; this->params.bucket_size = bkt; this->params.size = (int)rint((max - min) / bkt + 1.0); - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2472,17 +2476,17 @@ class VectorDistribution * This is a vector of StandardDeviation stats. * @sa Stat, VectorDistBase, FancyStor */ -template <class Bin = DefaultBin> +template<int N = 0> class VectorStandardDeviation - : public WrapVec<VectorStandardDeviation<Bin>, - VectorDistBase<FancyStor, Bin>, + : public WrapVec<VectorStandardDeviation<N>, + VectorDistBase<FancyStor>, VectorDistStatData> { public: /** The base implementation */ - typedef VectorDistBase<FancyStor, Bin> Base; + typedef VectorDistBase<FancyStor> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2491,9 +2495,7 @@ class VectorStandardDeviation * @return A reference to this distribution. */ VectorStandardDeviation &init(int size) { - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2502,17 +2504,17 @@ class VectorStandardDeviation * This is a vector of AverageDeviation stats. * @sa Stat, VectorDistBase, AvgFancy */ -template <class Bin = DefaultBin> +template<int N = 0> class VectorAverageDeviation - : public WrapVec<VectorAverageDeviation<Bin>, - VectorDistBase<AvgFancy, Bin>, + : public WrapVec<VectorAverageDeviation<N>, + VectorDistBase<AvgFancy>, VectorDistStatData> { public: /** The base implementation */ - typedef VectorDistBase<AvgFancy, Bin> Base; + typedef VectorDistBase<AvgFancy> Base; /** The parameter type. */ - typedef typename DistStor::Params Params; + typedef DistStor::Params Params; public: /** @@ -2521,9 +2523,7 @@ class VectorAverageDeviation * @return A reference to this distribution. */ VectorAverageDeviation &init(int size) { - this->bin.init(size, this->params); - this->setInit(); - + this->doInit(size); return *this; } }; @@ -2567,13 +2567,6 @@ class FormulaBase : public DataAccess */ size_t size() const; - /** - * Return true if Formula is binned. i.e. any of its children - * nodes are binned - * @return True if Formula is binned. - */ - bool binned() const; - bool check() const { return true; } /** @@ -2612,7 +2605,6 @@ class FormulaStatData : public FormulaData public: FormulaStatData(Stat &stat) : s(stat) {} - virtual bool binned() const { return s.binned(); } virtual bool zero() const { return s.zero(); } virtual void reset() { s.reset(); } @@ -2679,7 +2671,6 @@ class FormulaNode : public Node virtual size_t size() const { return formula.size(); } virtual const VResult &result() const { formula.result(vec); return vec; } virtual Result total() const { return formula.total(); } - virtual bool binned() const { return formula.binned(); } virtual std::string str() const { return formula.str(); } }; @@ -2713,8 +2704,8 @@ class Temp * Create a new ScalarStatNode. * @param s The ScalarStat to place in a node. */ - template <class Bin> - Temp(const Scalar<Bin> &s) + template <int N> + Temp(const Scalar<N> &s) : node(new ScalarStatNode(s.statData())) { } /** @@ -2728,16 +2719,16 @@ class Temp * Create a new ScalarStatNode. * @param s The ScalarStat to place in a node. */ - template <class Bin> - Temp(const Average<Bin> &s) + template <int N> + Temp(const Average<N> &s) : node(new ScalarStatNode(s.statData())) { } /** * Create a new VectorStatNode. * @param s The VectorStat to place in a node. */ - template <class Bin> - Temp(const Vector<Bin> &s) + template <int N> + Temp(const Vector<N> &s) : node(new VectorStatNode(s.statData())) { } /** @@ -2750,9 +2741,9 @@ class Temp * Create a new ScalarProxyNode. * @param p The ScalarProxy to place in a node. */ - template <class Storage, class Bin> - Temp(const ScalarProxy<Storage, Bin> &p) - : node(new ScalarProxyNode<Storage, Bin>(p)) { } + template <class Stat> + Temp(const ScalarProxy<Stat> &p) + : node(new ScalarProxyNode<Stat>(p)) { } /** * Create a ConstNode diff --git a/src/base/stats/events.cc b/src/base/stats/events.cc index 3191aec13..6ecc5434c 100644 --- a/src/base/stats/events.cc +++ b/src/base/stats/events.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <vector> diff --git a/src/base/stats/events.hh b/src/base/stats/events.hh index 2a23240b4..b09b91c7c 100644 --- a/src/base/stats/events.hh +++ b/src/base/stats/events.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_EVENTS_HH__ diff --git a/src/base/stats/flags.hh b/src/base/stats/flags.hh index 00db95bc1..ada1a4a87 100644 --- a/src/base/stats/flags.hh +++ b/src/base/stats/flags.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_FLAGS_HH__ diff --git a/src/base/stats/mysql.cc b/src/base/stats/mysql.cc index 6d12b4fc1..0fb31f4ce 100644 --- a/src/base/stats/mysql.cc +++ b/src/base/stats/mysql.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <cassert> @@ -156,14 +158,6 @@ MySqlRun::cleanup() if (mysql.commit()) panic("could not commit transaction\n%s\n", mysql.error); - mysql.query("DELETE bins " - "FROM bins " - "LEFT JOIN data ON bn_id=dt_bin " - "WHERE dt_bin IS NULL"); - - if (mysql.commit()) - panic("could not commit transaction\n%s\n", mysql.error); - mysql.query("DELETE events" "FROM events" "LEFT JOIN runs ON ev_run=rn_id" @@ -307,52 +301,6 @@ SetupStat::setup() return statid; } -unsigned -SetupBin(const string &bin) -{ - static map<string, int> binmap; - - using namespace MySQL; - map<string,int>::const_iterator i = binmap.find(bin); - if (i != binmap.end()) - return (*i).second; - - Connection &mysql = MySqlDB.conn(); - assert(mysql.connected()); - - uint16_t bin_id; - - stringstream select; - stringstream insert; - ccprintf(select, "SELECT bn_id FROM bins WHERE bn_name=\"%s\"", bin); - - mysql.query(select); - MySQL::Result result = mysql.store_result(); - if (result) { - assert(result.num_fields() == 1); - MySQL::Row row = result.fetch_row(); - if (row) { - to_number(row[0], bin_id); - goto exit; - } - } - - ccprintf(insert, "INSERT INTO bins(bn_name) values(\"%s\")", bin); - - mysql.query(insert); - if (mysql.error) - panic("could not get a bin\n%s\n", mysql.error); - - bin_id = mysql.insert_id(); - if (mysql.commit()) - panic("could not commit transaction\n%s\n", mysql.error); - - binmap.insert(make_pair(bin, bin_id)); - - exit: - return bin_id; -} - InsertData::InsertData() { query = new char[maxsize + 1]; @@ -382,7 +330,7 @@ InsertData::flush() size = 0; first = true; strcpy(query, "INSERT INTO " - "data(dt_stat,dt_x,dt_y,dt_run,dt_tick,dt_bin,dt_data) " + "data(dt_stat,dt_x,dt_y,dt_run,dt_tick,dt_data) " "values"); size = strlen(query); } @@ -400,9 +348,9 @@ InsertData::insert() first = false; - size += sprintf(query + size, "(%u,%d,%d,%u,%llu,%u,\"%f\")", + size += sprintf(query + size, "(%u,%d,%d,%u,%llu,\"%f\")", stat, x, y, MySqlDB.run(), (unsigned long long)tick, - bin, data); + data); } struct InsertSubData @@ -654,29 +602,6 @@ MySql::configure(const FormulaData &data) InsertFormula(find(data.id), data.str()); } -void -MySql::output(MainBin *bin) -{ - MySQL::Connection &mysql = MySqlDB.conn(); - - if (bin) { - bin->activate(); - newdata.bin = SetupBin(bin->name()); - } else { - newdata.bin = 0; - } - - Database::stat_list_t::const_iterator i, end = Database::stats().end(); - for (i = Database::stats().begin(); i != end; ++i) { - StatData *stat = *i; - if (bin && stat->binned() || !bin && !stat->binned()) { - stat->visit(*this); - if (mysql.commit()) - panic("could not commit transaction\n%s\n", mysql.error); - } - } -} - bool MySql::valid() const { @@ -695,11 +620,14 @@ MySql::output() // store sample # newdata.tick = curTick; - output(NULL); - if (!bins().empty()) { - bin_list_t::iterator i, end = bins().end(); - for (i = bins().begin(); i != end; ++i) - output(*i); + MySQL::Connection &mysql = MySqlDB.conn(); + + Database::stat_list_t::const_iterator i, end = Database::stats().end(); + for (i = Database::stats().begin(); i != end; ++i) { + StatData *stat = *i; + stat->visit(*this); + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); } newdata.flush(); diff --git a/src/base/stats/mysql.hh b/src/base/stats/mysql.hh index 25ea22b97..50f7d9e97 100644 --- a/src/base/stats/mysql.hh +++ b/src/base/stats/mysql.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_MYSQL_HH__ @@ -37,7 +39,6 @@ namespace MySQL { class Connection; } namespace Stats { -class MainBin; class DistDataData; class MySqlRun; bool MySqlConnected(); @@ -80,7 +81,6 @@ class InsertData uint64_t tick; double data; uint16_t stat; - uint16_t bin; int16_t x; int16_t y; @@ -131,7 +131,6 @@ class MySql : public Output protected: // Output helper - void output(MainBin *bin); void output(const DistDataData &data); void output(const ScalarData &data); void output(const VectorData &data); diff --git a/src/base/stats/mysql_run.hh b/src/base/stats/mysql_run.hh index d8dcb7594..487224551 100644 --- a/src/base/stats/mysql_run.hh +++ b/src/base/stats/mysql_run.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_MYSQL_RUN_HH__ diff --git a/src/base/stats/output.hh b/src/base/stats/output.hh index ee6b38d63..4fe93791f 100644 --- a/src/base/stats/output.hh +++ b/src/base/stats/output.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_OUTPUT_HH__ diff --git a/src/base/stats/statdb.cc b/src/base/stats/statdb.cc index a6b00ab8a..f9136807a 100644 --- a/src/base/stats/statdb.cc +++ b/src/base/stats/statdb.cc @@ -24,12 +24,13 @@ * 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. + * + * Authors: Nathan Binkert */ #include "base/misc.hh" #include "base/trace.hh" #include "base/statistics.hh" -#include "base/stats/bin.hh" #include "base/stats/statdb.hh" using namespace std; @@ -49,17 +50,6 @@ find(void *stat) } void -regBin(MainBin *bin, const std::string &_name) -{ - bin_list_t::iterator i, end = bins().end(); - for (i = bins().begin(); i != end; ++i) - if ((*i)->name() == _name) - panic("re-registering bin %s", _name); - bins().push_back(bin); - DPRINTF(Stats, "registering %s\n", _name); -} - -void regStat(void *stat, StatData *data) { if (map().find(stat) != map().end()) diff --git a/src/base/stats/statdb.hh b/src/base/stats/statdb.hh index eb56d8fac..a5b9be7eb 100644 --- a/src/base/stats/statdb.hh +++ b/src/base/stats/statdb.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_STATDB_HH__ @@ -38,31 +40,25 @@ class Python; namespace Stats { -class MainBin; class StatData; namespace Database { typedef std::map<void *, StatData *> stat_map_t; typedef std::list<StatData *> stat_list_t; -typedef std::list<MainBin *> bin_list_t; // We wrap the database in a struct to make sure it is built in time. struct TheDatabase { stat_map_t map; stat_list_t stats; - bin_list_t bins; - }; TheDatabase &db(); inline stat_map_t &map() { return db().map; } inline stat_list_t &stats() { return db().stats; } -inline bin_list_t &bins() { return db().bins; } StatData *find(void *stat); -void regBin(MainBin *bin, const std::string &name); void regStat(void *stat, StatData *data); void regPrint(void *stat); diff --git a/src/base/stats/text.cc b/src/base/stats/text.cc index 300737c60..c4448efc9 100644 --- a/src/base/stats/text.cc +++ b/src/base/stats/text.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #if defined(__APPLE__) @@ -127,23 +129,9 @@ Text::output() using namespace Database; ccprintf(*stream, "\n---------- Begin Simulation Statistics ----------\n"); - if (bins().empty() || bins().size() == 1) { - stat_list_t::const_iterator i, end = stats().end(); - for (i = stats().begin(); i != end; ++i) - (*i)->visit(*this); - } else { - ccprintf(*stream, "PRINTING BINNED STATS\n"); - bin_list_t::iterator i, end = bins().end(); - for (i = bins().begin(); i != end; ++i) { - MainBin *bin = *i; - bin->activate(); - ccprintf(*stream,"---%s Bin------------\n", bin->name()); - stat_list_t::const_iterator i, end = stats().end(); - for (i = stats().begin(); i != end; ++i) - (*i)->visit(*this); - ccprintf(*stream, "---------------------------------\n"); - } - } + stat_list_t::const_iterator i, end = stats().end(); + for (i = stats().begin(); i != end; ++i) + (*i)->visit(*this); ccprintf(*stream, "\n---------- End Simulation Statistics ----------\n"); stream->flush(); } diff --git a/src/base/stats/text.hh b/src/base/stats/text.hh index 125cb79fa..b3faf5ad5 100644 --- a/src/base/stats/text.hh +++ b/src/base/stats/text.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_TEXT_HH__ @@ -44,7 +46,6 @@ class Text : public Output protected: bool noOutput(const StatData &data); - void binout(); public: bool compat; diff --git a/src/base/stats/types.hh b/src/base/stats/types.hh index 57f1564a5..b64e8fb17 100644 --- a/src/base/stats/types.hh +++ b/src/base/stats/types.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_TYPES_HH__ diff --git a/src/base/stats/visit.cc b/src/base/stats/visit.cc index dd4d49502..870e4fb77 100644 --- a/src/base/stats/visit.cc +++ b/src/base/stats/visit.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include "base/stats/visit.hh" diff --git a/src/base/stats/visit.hh b/src/base/stats/visit.hh index c0593c670..0087c227c 100644 --- a/src/base/stats/visit.hh +++ b/src/base/stats/visit.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_STATS_VISIT_HH__ diff --git a/src/base/str.cc b/src/base/str.cc index 5f7f50286..0a517dff5 100644 --- a/src/base/str.cc +++ b/src/base/str.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <ctype.h> diff --git a/src/base/str.hh b/src/base/str.hh index 79e33a1be..08953caf3 100644 --- a/src/base/str.hh +++ b/src/base/str.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __STR_HH__ diff --git a/src/base/time.cc b/src/base/time.cc index 5827c9a85..cbc7256ee 100644 --- a/src/base/time.cc +++ b/src/base/time.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/base/time.hh b/src/base/time.hh index 5731e3029..24e8a8a53 100644 --- a/src/base/time.hh +++ b/src/base/time.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __SIM_TIME_HH__ diff --git a/src/base/timebuf.hh b/src/base/timebuf.hh index f6b5b2781..160a97034 100644 --- a/src/base/timebuf.hh +++ b/src/base/timebuf.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Kevin Lim */ #ifndef __BASE_TIMEBUF_HH__ diff --git a/src/base/trace.cc b/src/base/trace.cc index 90db7f045..9fa615f4d 100644 --- a/src/base/trace.cc +++ b/src/base/trace.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <ctype.h> @@ -244,7 +247,6 @@ DebugOut() // // Dump trace buffer to specified file (cout if NULL) // -extern "C" void dumpTrace(const char *filename) { @@ -266,7 +268,6 @@ dumpTrace(const char *filename) // same facility as the "trace to file" feature, and will print error // messages rather than clobbering an existing ostream pointer. // -extern "C" void echoTrace(bool on) { @@ -286,7 +287,6 @@ echoTrace(bool on) } } -extern "C" void printTraceFlags() { @@ -335,14 +335,12 @@ tweakTraceFlag(const char *string, bool value) cprintf("could not find flag %s\n", string); } -extern "C" void setTraceFlag(const char *string) { tweakTraceFlag(string, true); } -extern "C" void clearTraceFlag(const char *string) { diff --git a/src/base/trace.hh b/src/base/trace.hh index 5e14f1bff..8df5dd893 100644 --- a/src/base/trace.hh +++ b/src/base/trace.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __BASE_TRACE_HH__ diff --git a/src/base/traceflags.py b/src/base/traceflags.py index 3b7dd0f81..327ce6075 100644 --- a/src/base/traceflags.py +++ b/src/base/traceflags.py @@ -25,6 +25,9 @@ # 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. +# +# Authors: Nathan Binkert +# Steve Reinhardt # # This file generates the header and source files for the flags @@ -53,6 +56,7 @@ baseFlags = [ 'BusBridge', 'Cache', 'Chains', + 'Checker', 'Clock', 'Commit', 'CommitRate', @@ -103,14 +107,22 @@ baseFlags = [ 'IdeDisk', 'InstExec', 'Interrupt', - 'LDSTQ', + 'LSQ', + 'LSQUnit', 'Loader', 'MC146818', 'MMU', 'MSHR', 'Mbox', 'MemDepUnit', - 'OoOCPU', + 'BaseCPU' + 'O3CPU', + 'OzoneCPU', + 'FE', + 'IBE', + 'BE', + 'O3CPU', + 'OzoneLSQ', 'PCEvent', 'PCIA', 'PCIDEV', @@ -146,6 +158,9 @@ baseFlags = [ 'Uart', 'VtoPhys', 'WriteBarrier', + 'Activity', + 'Scoreboard', + 'Writeback', ] # @@ -163,7 +178,8 @@ compoundFlagMap = { 'EthernetAll' : [ 'Ethernet', 'EthernetPIO', 'EthernetDMA', 'EthernetData' , 'EthernetDesc', 'EthernetIntr', 'EthernetSM', 'EthernetCksum' ], 'EthernetNoData' : [ 'Ethernet', 'EthernetPIO', 'EthernetDesc', 'EthernetIntr', 'EthernetSM', 'EthernetCksum' ], 'IdeAll' : [ 'IdeCtrl', 'IdeDisk' ], - 'FullCPUAll' : [ 'Fetch', 'Decode', 'Rename', 'IEW', 'Commit', 'IQ', 'ROB', 'FreeList', 'RenameMap', 'LDSTQ', 'StoreSet', 'MemDepUnit', 'DynInst', 'FullCPU'] + 'O3CPUAll' : [ 'Fetch', 'Decode', 'Rename', 'IEW', 'Commit', 'IQ', 'ROB', 'FreeList', 'RenameMap', 'LSQ', 'LSQUnit', 'StoreSet', 'MemDepUnit', 'DynInst', 'O3CPU', 'Activity','Scoreboard','Writeback'], + 'OzoneCPUAll' : [ 'BE', 'FE', 'IBE', 'OzoneLSQ', 'OzoneCPU'] } ############################################################# diff --git a/src/base/userinfo.cc b/src/base/userinfo.cc index 15bd72224..62f7b7b5c 100644 --- a/src/base/userinfo.cc +++ b/src/base/userinfo.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/base/userinfo.hh b/src/base/userinfo.hh index d8ebd443c..a2838ab3e 100644 --- a/src/base/userinfo.hh +++ b/src/base/userinfo.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BASE_USERINFO_HH__ diff --git a/src/cpu/SConscript b/src/cpu/SConscript index 34fb6df78..3dcc2f1ec 100644 --- a/src/cpu/SConscript +++ b/src/cpu/SConscript @@ -25,6 +25,8 @@ # 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. +# +# Authors: Steve Reinhardt import os import os.path @@ -58,6 +60,21 @@ virtual Fault completeAcc(Packet *pkt, %s *xc, { panic("completeAcc not defined!"); }; ''' +mem_ini_sig_template = ''' +virtual Fault initiateAcc(%s *xc, Trace::InstRecord *traceData) const { panic("Not defined!"); }; +''' + +mem_comp_sig_template = ''' +virtual Fault completeAcc(uint8_t *data, %s *xc, Trace::InstRecord *traceData) const { panic("Not defined!"); return NoFault; }; +''' + +# Generate a temporary CPU list, including the CheckerCPU if +# it's enabled. This isn't used for anything else other than StaticInst +# headers. +temp_cpu_list = env['CPU_MODELS'] +if env['USE_CHECKER']: + temp_cpu_list.append('CheckerCPU') + # Generate header. def gen_cpu_exec_signatures(target, source, env): f = open(str(target[0]), 'w') @@ -65,7 +82,7 @@ def gen_cpu_exec_signatures(target, source, env): #ifndef __CPU_STATIC_INST_EXEC_SIGS_HH__ #define __CPU_STATIC_INST_EXEC_SIGS_HH__ ''' - for cpu in env['CPU_MODELS']: + for cpu in temp_cpu_list: xc_type = CpuModel.dict[cpu].strings['CPU_exec_context'] print >> f, exec_sig_template % (xc_type, xc_type, xc_type) print >> f, ''' @@ -75,12 +92,19 @@ def gen_cpu_exec_signatures(target, source, env): # Generate string that gets printed when header is rebuilt def gen_sigs_string(target, source, env): return "Generating static_inst_exec_sigs.hh: " \ - + ', '.join(env['CPU_MODELS']) + + ', '.join(temp_cpu_list) # Add command to generate header to environment. env.Command('static_inst_exec_sigs.hh', models_db, Action(gen_cpu_exec_signatures, gen_sigs_string, - varlist = ['CPU_MODELS'])) + varlist = temp_cpu_list)) + +env.Depends('static_inst_exec_sigs.hh', Value(env['USE_CHECKER'])) +env.Depends('static_inst_exec_sigs.hh', Value(env['CPU_MODELS'])) + +# List of suppported CPUs by the Checker. Errors out if USE_CHECKER=True +# and one of these are not being used. +CheckerSupportedCPUList = ['O3CPU', 'OzoneCPU'] ################################################################# # @@ -106,33 +130,64 @@ if need_simple_base: if 'FastCPU' in env['CPU_MODELS']: sources += Split('fast/cpu.cc') -if 'AlphaFullCPU' in env['CPU_MODELS']: +if 'O3CPU' in env['CPU_MODELS']: + sources += SConscript('o3/SConscript', exports = 'env') sources += Split(''' o3/2bit_local_pred.cc - o3/alpha_dyn_inst.cc - o3/alpha_cpu.cc - o3/alpha_cpu_builder.cc + o3/base_dyn_inst.cc o3/bpred_unit.cc o3/btb.cc o3/commit.cc o3/decode.cc o3/fetch.cc o3/free_list.cc + o3/fu_pool.cc o3/cpu.cc o3/iew.cc o3/inst_queue.cc - o3/ldstq.cc + o3/lsq_unit.cc + o3/lsq.cc o3/mem_dep_unit.cc o3/ras.cc o3/rename.cc o3/rename_map.cc o3/rob.cc - o3/sat_counter.cc + o3/scoreboard.cc o3/store_set.cc o3/tournament_pred.cc ''') + if env['USE_CHECKER']: + sources += Split('o3/checker_builder.cc') -# FullCPU sources are included from m5/SConscript since they're not +if 'OzoneCPU' in env['CPU_MODELS']: + sources += Split(''' + ozone/base_dyn_inst.cc + ozone/bpred_unit.cc + ozone/cpu.cc + ozone/cpu_builder.cc + ozone/dyn_inst.cc + ozone/front_end.cc + ozone/lw_back_end.cc + ozone/lw_lsq.cc + ozone/rename_table.cc + ''') + if env['USE_CHECKER']: + sources += Split('ozone/checker_builder.cc') + +if env['USE_CHECKER']: + sources += Split('checker/cpu.cc') + checker_supports = False + for i in CheckerSupportedCPUList: + if i in env['CPU_MODELS']: + checker_supports = True + if not checker_supports: + print "Checker only supports CPU models %s, please " \ + "set USE_CHECKER=False or use one of those CPU models" \ + % CheckerSupportedCPUList + Exit(1) + + +# FullCPU sources are included from src/SConscript since they're not # below this point in the file hierarchy. # Convert file names to SCons File objects. This takes care of the diff --git a/src/cpu/activity.cc b/src/cpu/activity.cc new file mode 100644 index 000000000..9a0f6d98d --- /dev/null +++ b/src/cpu/activity.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "base/timebuf.hh" +#include "cpu/activity.hh" + +ActivityRecorder::ActivityRecorder(int num_stages, int longest_latency, + int activity) + : activityBuffer(longest_latency, 0), longestLatency(longest_latency), + activityCount(activity), numStages(num_stages) +{ + stageActive = new bool[numStages]; + memset(stageActive, 0, numStages); +} + +void +ActivityRecorder::activity() +{ + // If we've already recorded activity for this cycle, we don't + // want to increment the count any more. + if (activityBuffer[0]) { + return; + } + + activityBuffer[0] = true; + + ++activityCount; + + DPRINTF(Activity, "Activity: %i\n", activityCount); +} + +void +ActivityRecorder::advance() +{ + // If there's a 1 in the slot that is about to be erased once the + // time buffer advances, then decrement the activityCount. + if (activityBuffer[-longestLatency]) { + --activityCount; + + assert(activityCount >= 0); + + DPRINTF(Activity, "Activity: %i\n", activityCount); + + if (activityCount == 0) { + DPRINTF(Activity, "No activity left!\n"); + } + } + + activityBuffer.advance(); +} + +void +ActivityRecorder::activateStage(const int idx) +{ + // Increment the activity count if this stage wasn't already active. + if (!stageActive[idx]) { + ++activityCount; + + stageActive[idx] = true; + + DPRINTF(Activity, "Activity: %i\n", activityCount); + } else { + DPRINTF(Activity, "Stage %i already active.\n", idx); + } + +// assert(activityCount < longestLatency + numStages + 1); +} + +void +ActivityRecorder::deactivateStage(const int idx) +{ + // Decrement the activity count if this stage was active. + if (stageActive[idx]) { + --activityCount; + + stageActive[idx] = false; + + DPRINTF(Activity, "Activity: %i\n", activityCount); + } else { + DPRINTF(Activity, "Stage %i already inactive.\n", idx); + } + + assert(activityCount >= 0); +} + +void +ActivityRecorder::reset() +{ + activityCount = 0; + memset(stageActive, 0, numStages); + for (int i = 0; i < longestLatency + 1; ++i) + activityBuffer.advance(); +} + +void +ActivityRecorder::dump() +{ + for (int i = 0; i <= longestLatency; ++i) { + cprintf("[Idx:%i %i] ", i, activityBuffer[-i]); + } + + cprintf("\n"); + + for (int i = 0; i < numStages; ++i) { + cprintf("[Stage:%i %i]\n", i, stageActive[i]); + } + + cprintf("\n"); + + cprintf("Activity count: %i\n", activityCount); +} + +void +ActivityRecorder::validate() +{ + int count = 0; + for (int i = 0; i <= longestLatency; ++i) { + if (activityBuffer[-i]) { + count++; + } + } + + for (int i = 0; i < numStages; ++i) { + if (stageActive[i]) { + count++; + } + } + + assert(count == activityCount); +} diff --git a/src/cpu/activity.hh b/src/cpu/activity.hh new file mode 100644 index 000000000..e99927339 --- /dev/null +++ b/src/cpu/activity.hh @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_ACTIVITY_HH__ +#define __CPU_ACTIVITY_HH__ + +#include "base/timebuf.hh" +#include "base/trace.hh" + +/** + * ActivityRecorder helper class that informs the CPU if it can switch + * over to being idle or not. It works by having a time buffer as + * long as any time buffer in the CPU, and the CPU and all of its + * stages inform the ActivityRecorder when they write to any time + * buffer. The ActivityRecorder marks a 1 in the "0" slot of the time + * buffer any time a stage writes to a time buffer, and it advances + * its time buffer at the same time as all other stages. The + * ActivityRecorder also records if a stage has activity to do next + * cycle. The recorder keeps a count of these two. Thus any time the + * count is non-zero, there is either communication still in flight, + * or activity that still must be done, meaning that the CPU can not + * idle. If count is zero, then the CPU can safely idle as it has no + * more outstanding work to do. + */ +class ActivityRecorder { + public: + ActivityRecorder(int num_stages, int longest_latency, int count); + + /** Records that there is activity this cycle. */ + void activity(); + + /** Advances the activity buffer, decrementing the activityCount + * if active communication just left the time buffer, and + * determining if there is no activity. + */ + void advance(); + + /** Marks a stage as active. */ + void activateStage(const int idx); + + /** Deactivates a stage. */ + void deactivateStage(const int idx); + + /** Returns how many things are active within the recorder. */ + int getActivityCount() { return activityCount; } + + /** Sets the count to a starting value. Can be used to disable + * the idling option. + */ + void setActivityCount(int count) + { activityCount = count; } + + /** Returns if the CPU should be active. */ + bool active() { return activityCount; } + + /** Clears the time buffer and the activity count. */ + void reset(); + + /** Debug function to dump the contents of the time buffer. */ + void dump(); + + /** Debug function to ensure that the activity count matches the + * contents of the time buffer. + */ + void validate(); + + private: + /** Time buffer that tracks if any cycles has active communication + * in them. It should be as long as the longest communication + * latency in the system. Each time any time buffer is written, + * the activity buffer should also be written to. The + * activityBuffer is advanced along with all the other time + * buffers, so it should have a 1 somewhere in it only if there + * is active communication in a time buffer. + */ + TimeBuffer<bool> activityBuffer; + + /** Longest latency time buffer in the CPU. */ + int longestLatency; + + /** Tracks how many stages and cycles of time buffer have + * activity. Stages increment this count when they switch to + * active, and decrement it when they switch to + * inactive. Whenever a cycle that previously had no information + * is written in the time buffer, this is incremented. When a + * cycle that had information exits the time buffer due to age, + * this count is decremented. When the count is 0, there is no + * activity in the CPU, and it can be descheduled. + */ + int activityCount; + + /** Number of stages that can be marked as active or inactive. */ + int numStages; + + /** Records which stages are active/inactive. */ + bool *stageActive; +}; + +#endif // __CPU_ACTIVITY_HH__ diff --git a/src/cpu/base.cc b/src/cpu/base.cc index 9ce458c64..40cec416b 100644 --- a/src/cpu/base.cc +++ b/src/cpu/base.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #include <iostream> @@ -35,7 +38,8 @@ #include "base/misc.hh" #include "base/output.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/cpuevent.hh" +#include "cpu/thread_context.hh" #include "cpu/profile.hh" #include "cpu/sampler/sampler.hh" #include "sim/param.hh" @@ -45,10 +49,6 @@ #include "base/trace.hh" -#if FULL_SYSTEM -#include "kern/kernel_stats.hh" -#endif - using namespace std; vector<BaseCPU *> BaseCPU::cpuList; @@ -89,8 +89,8 @@ BaseCPU::BaseCPU(Params *p) // if (p->max_insts_any_thread != 0) for (int i = 0; i < number_of_threads; ++i) - new SimExitEvent(comInstEventQueue[i], p->max_insts_any_thread, - "a thread reached the max instruction count"); + new SimLoopExitEvent(comInstEventQueue[i], p->max_insts_any_thread, + "a thread reached the max instruction count"); if (p->max_insts_all_threads != 0) { // allocate & initialize shared downcounter: each event will @@ -114,8 +114,8 @@ BaseCPU::BaseCPU(Params *p) // if (p->max_loads_any_thread != 0) for (int i = 0; i < number_of_threads; ++i) - new SimExitEvent(comLoadEventQueue[i], p->max_loads_any_thread, - "a thread reached the max load count"); + new SimLoopExitEvent(comLoadEventQueue[i], p->max_loads_any_thread, + "a thread reached the max load count"); if (p->max_loads_all_threads != 0) { // allocate & initialize shared downcounter: each event will @@ -153,8 +153,6 @@ BaseCPU::BaseCPU(Params *p) profileEvent = NULL; if (params->profile) profileEvent = new ProfileEvent(this, params->profile); - - kernelStats = new Kernel::Statistics(system); #endif } @@ -164,6 +162,7 @@ BaseCPU::Params::Params() #if FULL_SYSTEM profile = false; #endif + checker = NULL; } void @@ -174,17 +173,13 @@ BaseCPU::enableFunctionTrace() BaseCPU::~BaseCPU() { -#if FULL_SYSTEM - if (kernelStats) - delete kernelStats; -#endif } void BaseCPU::init() { if (!params->deferRegistration) - registerExecContexts(); + registerThreadContexts(); } void @@ -207,44 +202,42 @@ BaseCPU::regStats() .desc("number of cpu cycles simulated") ; - int size = execContexts.size(); + int size = threadContexts.size(); if (size > 1) { for (int i = 0; i < size; ++i) { stringstream namestr; ccprintf(namestr, "%s.ctx%d", name(), i); - execContexts[i]->regStats(namestr.str()); + threadContexts[i]->regStats(namestr.str()); } } else if (size == 1) - execContexts[0]->regStats(name()); + threadContexts[0]->regStats(name()); #if FULL_SYSTEM - if (kernelStats) - kernelStats->regStats(name() + ".kern"); #endif } void -BaseCPU::registerExecContexts() +BaseCPU::registerThreadContexts() { - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; #if FULL_SYSTEM int id = params->cpu_id; if (id != -1) id += i; - xc->setCpuId(system->registerExecContext(xc, id)); + tc->setCpuId(system->registerThreadContext(tc, id)); #else - xc->setCpuId(xc->getProcessPtr()->registerExecContext(xc)); + tc->setCpuId(tc->getProcessPtr()->registerThreadContext(tc)); #endif } } void -BaseCPU::switchOut(Sampler *sampler) +BaseCPU::switchOut() { panic("This CPU doesn't support sampling!"); } @@ -252,19 +245,22 @@ BaseCPU::switchOut(Sampler *sampler) void BaseCPU::takeOverFrom(BaseCPU *oldCPU) { - assert(execContexts.size() == oldCPU->execContexts.size()); + assert(threadContexts.size() == oldCPU->threadContexts.size()); + + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *newTC = threadContexts[i]; + ThreadContext *oldTC = oldCPU->threadContexts[i]; - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *newXC = execContexts[i]; - ExecContext *oldXC = oldCPU->execContexts[i]; + newTC->takeOverFrom(oldTC); - newXC->takeOverFrom(oldXC); - assert(newXC->readCpuId() == oldXC->readCpuId()); + CpuEvent::replaceThreadContext(oldTC, newTC); + + assert(newTC->readCpuId() == oldTC->readCpuId()); #if FULL_SYSTEM - system->replaceExecContext(newXC, newXC->readCpuId()); + system->replaceThreadContext(newTC, newTC->readCpuId()); #else - assert(newXC->getProcessPtr() == oldXC->getProcessPtr()); - newXC->getProcessPtr()->replaceExecContext(newXC, newXC->readCpuId()); + assert(newTC->getProcessPtr() == oldTC->getProcessPtr()); + newTC->getProcessPtr()->replaceThreadContext(newTC, newTC->readCpuId()); #endif } @@ -273,8 +269,8 @@ BaseCPU::takeOverFrom(BaseCPU *oldCPU) interrupts[i] = oldCPU->interrupts[i]; intstatus = oldCPU->intstatus; - for (int i = 0; i < execContexts.size(); ++i) - execContexts[i]->profileClear(); + for (int i = 0; i < threadContexts.size(); ++i) + threadContexts[i]->profileClear(); if (profileEvent) profileEvent->schedule(curTick); @@ -290,9 +286,9 @@ BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, int _interval) void BaseCPU::ProfileEvent::process() { - for (int i = 0, size = cpu->execContexts.size(); i < size; ++i) { - ExecContext *xc = cpu->execContexts[i]; - xc->profileSample(); + for (int i = 0, size = cpu->threadContexts.size(); i < size; ++i) { + ThreadContext *tc = cpu->threadContexts[i]; + tc->profileSample(); } schedule(curTick + interval); @@ -345,12 +341,6 @@ BaseCPU::serialize(std::ostream &os) { SERIALIZE_ARRAY(interrupts, TheISA::NumInterruptLevels); SERIALIZE_SCALAR(intstatus); - -#if FULL_SYSTEM - if (kernelStats) - kernelStats->serialize(os); -#endif - } void @@ -358,11 +348,6 @@ BaseCPU::unserialize(Checkpoint *cp, const std::string §ion) { UNSERIALIZE_ARRAY(interrupts, TheISA::NumInterruptLevels); UNSERIALIZE_SCALAR(intstatus); - -#if FULL_SYSTEM - if (kernelStats) - kernelStats->unserialize(cp, section); -#endif } #endif // FULL_SYSTEM diff --git a/src/cpu/base.hh b/src/cpu/base.hh index 79700c117..51f3bb905 100644 --- a/src/cpu/base.hh +++ b/src/cpu/base.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __CPU_BASE_HH__ @@ -38,10 +41,10 @@ #include "sim/sim_object.hh" #include "arch/isa_traits.hh" -class System; -namespace Kernel { class Statistics; } class BranchPred; -class ExecContext; +class CheckerCPU; +class ThreadContext; +class System; class BaseCPU : public SimObject { @@ -89,7 +92,7 @@ class BaseCPU : public SimObject #endif protected: - std::vector<ExecContext *> execContexts; + std::vector<ThreadContext *> threadContexts; public: @@ -125,6 +128,7 @@ class BaseCPU : public SimObject int cpu_id; Tick profile; #endif + BaseCPU *checker; Params(); }; @@ -140,11 +144,11 @@ class BaseCPU : public SimObject virtual void activateWhenReady(int tid) {}; - void registerExecContexts(); + void registerThreadContexts(); /// Prepare for another CPU to take over execution. When it is /// is ready (drained pipe) it signals the sampler. - virtual void switchOut(Sampler *); + virtual void switchOut(); /// Take over execution from the given CPU. Used for warm-up and /// sampling. @@ -232,10 +236,6 @@ class BaseCPU : public SimObject public: // Number of CPU cycles simulated Stats::Scalar<> numCycles; - -#if FULL_SYSTEM - Kernel::Statistics *kernelStats; -#endif }; #endif // __CPU_BASE_HH__ diff --git a/src/cpu/base_dyn_inst.cc b/src/cpu/base_dyn_inst.cc deleted file mode 100644 index bf7c35cad..000000000 --- a/src/cpu/base_dyn_inst.cc +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (c) 2004-2005 The Regents of The University of Michigan - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer; - * redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution; - * neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __CPU_BASE_DYN_INST_CC__ -#define __CPU_BASE_DYN_INST_CC__ - -#include <iostream> -#include <string> -#include <sstream> - -#include "base/cprintf.hh" -#include "base/trace.hh" - -#include "arch/faults.hh" -#include "cpu/exetrace.hh" -#include "mem/mem_req.hh" - -#include "cpu/base_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" -#include "cpu/o3/alpha_cpu.hh" - -using namespace std; -using namespace TheISA; - -#define NOHASH -#ifndef NOHASH - -#include "base/hashmap.hh" - -unsigned int MyHashFunc(const BaseDynInst *addr) -{ - unsigned a = (unsigned)addr; - unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; - - return hash; -} - -typedef m5::hash_map<const BaseDynInst *, const BaseDynInst *, MyHashFunc> my_hash_t; -my_hash_t thishash; -#endif - -template <class Impl> -BaseDynInst<Impl>::BaseDynInst(MachInst machInst, Addr inst_PC, - Addr pred_PC, InstSeqNum seq_num, - FullCPU *cpu) - : staticInst(machInst), traceData(NULL), cpu(cpu), cpuXC(cpu->cpuXCBase()) -{ - seqNum = seq_num; - - PC = inst_PC; - nextPC = PC + sizeof(MachInst); - predPC = pred_PC; - - initVars(); -} - -template <class Impl> -BaseDynInst<Impl>::BaseDynInst(StaticInstPtr &_staticInst) - : staticInst(_staticInst), traceData(NULL) -{ - initVars(); -} - -template <class Impl> -void -BaseDynInst<Impl>::initVars() -{ - effAddr = MemReq::inval_addr; - physEffAddr = MemReq::inval_addr; - - readyRegs = 0; - - completed = false; - canIssue = false; - issued = false; - executed = false; - canCommit = false; - squashed = false; - squashedInIQ = false; - eaCalcDone = false; - - blockingInst = false; - recoverInst = false; - - // Eventually make this a parameter. - threadNumber = 0; - - // Also make this a parameter, or perhaps get it from xc or cpu. - asid = 0; - - // Initialize the fault to be unimplemented opcode. - fault = new UnimplementedOpcodeFault; - - ++instcount; - - DPRINTF(FullCPU, "DynInst: Instruction created. Instcount=%i\n", - instcount); -} - -template <class Impl> -BaseDynInst<Impl>::~BaseDynInst() -{ - --instcount; - DPRINTF(FullCPU, "DynInst: Instruction destroyed. Instcount=%i\n", - instcount); -} - -template <class Impl> -void -BaseDynInst<Impl>::prefetch(Addr addr, unsigned flags) -{ - // This is the "functional" implementation of prefetch. Not much - // happens here since prefetches don't affect the architectural - // state. - - // Generate a MemReq so we can translate the effective address. - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), 1, flags); - req->asid = asid; - - // Prefetches never cause faults. - fault = NoFault; - - // note this is a local, not BaseDynInst::fault - Fault trans_fault = cpuXC->translateDataReadReq(req); - - if (trans_fault == NoFault && !(req->flags & UNCACHEABLE)) { - // It's a valid address to cacheable space. Record key MemReq - // parameters so we can generate another one just like it for - // the timing access without calling translate() again (which - // might mess up the TLB). - effAddr = req->vaddr; - physEffAddr = req->paddr; - memReqFlags = req->flags; - } else { - // Bogus address (invalid or uncacheable space). Mark it by - // setting the eff_addr to InvalidAddr. - effAddr = physEffAddr = MemReq::inval_addr; - } - - /** - * @todo - * Replace the disjoint functional memory with a unified one and remove - * this hack. - */ -#if !FULL_SYSTEM - req->paddr = req->vaddr; -#endif - - if (traceData) { - traceData->setAddr(addr); - } -} - -template <class Impl> -void -BaseDynInst<Impl>::writeHint(Addr addr, int size, unsigned flags) -{ - // Need to create a MemReq here so we can do a translation. This - // will casue a TLB miss trap if necessary... not sure whether - // that's the best thing to do or not. We don't really need the - // MemReq otherwise, since wh64 has no functional effect. - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), size, flags); - req->asid = asid; - - fault = cpuXC->translateDataWriteReq(req); - - if (fault == NoFault && !(req->flags & UNCACHEABLE)) { - // Record key MemReq parameters so we can generate another one - // just like it for the timing access without calling translate() - // again (which might mess up the TLB). - effAddr = req->vaddr; - physEffAddr = req->paddr; - memReqFlags = req->flags; - } else { - // ignore faults & accesses to uncacheable space... treat as no-op - effAddr = physEffAddr = MemReq::inval_addr; - } - - storeSize = size; - storeData = 0; -} - -/** - * @todo Need to find a way to get the cache block size here. - */ -template <class Impl> -Fault -BaseDynInst<Impl>::copySrcTranslate(Addr src) -{ - MemReqPtr req = new MemReq(src, cpuXC->getProxy(), 64); - req->asid = asid; - - // translate to physical address - Fault fault = cpuXC->translateDataReadReq(req); - - if (fault == NoFault) { - cpuXC->copySrcAddr = src; - cpuXC->copySrcPhysAddr = req->paddr; - } else { - cpuXC->copySrcAddr = 0; - cpuXC->copySrcPhysAddr = 0; - } - return fault; -} - -/** - * @todo Need to find a way to get the cache block size here. - */ -template <class Impl> -Fault -BaseDynInst<Impl>::copy(Addr dest) -{ - uint8_t data[64]; - FunctionalMemory *mem = cpuXC->mem; - assert(cpuXC->copySrcPhysAddr || cpuXC->misspeculating()); - MemReqPtr req = new MemReq(dest, cpuXC->getProxy(), 64); - req->asid = asid; - - // translate to physical address - Fault fault = cpuXC->translateDataWriteReq(req); - - if (fault == NoFault) { - Addr dest_addr = req->paddr; - // Need to read straight from memory since we have more than 8 bytes. - req->paddr = cpuXC->copySrcPhysAddr; - mem->read(req, data); - req->paddr = dest_addr; - mem->write(req, data); - } - return fault; -} - -template <class Impl> -void -BaseDynInst<Impl>::dump() -{ - cprintf("T%d : %#08d `", threadNumber, PC); - cout << staticInst->disassemble(PC); - cprintf("'\n"); -} - -template <class Impl> -void -BaseDynInst<Impl>::dump(std::string &outstring) -{ - std::ostringstream s; - s << "T" << threadNumber << " : 0x" << PC << " " - << staticInst->disassemble(PC); - - outstring = s.str(); -} - - -#if 0 -template <class Impl> -Fault -BaseDynInst<Impl>::mem_access(mem_cmd cmd, Addr addr, void *p, int nbytes) -{ - Fault fault; - - // check alignments, even speculative this test should always pass - if ((nbytes & nbytes - 1) != 0 || (addr & nbytes - 1) != 0) { - for (int i = 0; i < nbytes; i++) - ((char *) p)[i] = 0; - - // I added the following because according to the comment above, - // we should never get here. The comment lies -#if 0 - panic("unaligned access. Cycle = %n", curTick); -#endif - return NoFault; - } - - MemReqPtr req = new MemReq(addr, thread, nbytes); - switch(cmd) { - case Read: - fault = spec_mem->read(req, (uint8_t *)p); - break; - - case Write: - fault = spec_mem->write(req, (uint8_t *)p); - if (fault != NoFault) - break; - - specMemWrite = true; - storeSize = nbytes; - switch(nbytes) { - case sizeof(uint8_t): - *(uint8_t)&storeData = (uint8_t *)p; - break; - case sizeof(uint16_t): - *(uint16_t)&storeData = (uint16_t *)p; - break; - case sizeof(uint32_t): - *(uint32_t)&storeData = (uint32_t *)p; - break; - case sizeof(uint64_t): - *(uint64_t)&storeData = (uint64_t *)p; - break; - } - break; - - default: - fault = genMachineCheckFault(); - break; - } - - trace_mem(fault, cmd, addr, p, nbytes); - - return fault; -} - -#endif - -template <class Impl> -bool -BaseDynInst<Impl>::eaSrcsReady() -{ - // For now I am assuming that src registers 1..n-1 are the ones that the - // EA calc depends on. (i.e. src reg 0 is the source of the data to be - // stored) - - for (int i = 1; i < numSrcRegs(); ++i) - { - if (!_readySrcRegIdx[i]) - return false; - } - - return true; -} - -// Forward declaration -template class BaseDynInst<AlphaSimpleImpl>; - -template <> -int -BaseDynInst<AlphaSimpleImpl>::instcount = 0; - -#endif // __CPU_BASE_DYN_INST_CC__ diff --git a/src/cpu/base_dyn_inst.hh b/src/cpu/base_dyn_inst.hh index 3a7852f79..9cc61f74c 100644 --- a/src/cpu/base_dyn_inst.hh +++ b/src/cpu/base_dyn_inst.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,26 +24,27 @@ * 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. + * + * Authors: Kevin Lim */ #ifndef __CPU_BASE_DYN_INST_HH__ #define __CPU_BASE_DYN_INST_HH__ +#include <bitset> +#include <list> #include <string> -#include <vector> +#include "arch/faults.hh" #include "base/fast_alloc.hh" #include "base/trace.hh" #include "config/full_system.hh" #include "cpu/exetrace.hh" #include "cpu/inst_seq.hh" -#include "cpu/o3/comm.hh" +#include "cpu/op_class.hh" #include "cpu/static_inst.hh" -#include "encumbered/cpu/full/bpred_update.hh" -#include "encumbered/cpu/full/op_class.hh" -#include "encumbered/cpu/full/spec_memory.hh" -#include "encumbered/cpu/full/spec_state.hh" -#include "encumbered/mem/functional/main.hh" +#include "mem/packet.hh" +#include "sim/system.hh" /** * @file @@ -58,21 +59,32 @@ class BaseDynInst : public FastAlloc, public RefCounted { public: // Typedef for the CPU. - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::CPUType ImplCPU; + typedef typename ImplCPU::ImplState ImplState; - /// Binary machine instruction type. + // Binary machine instruction type. typedef TheISA::MachInst MachInst; - /// Logical register index type. + // Extended machine instruction type + typedef TheISA::ExtMachInst ExtMachInst; + // Logical register index type. typedef TheISA::RegIndex RegIndex; - /// Integer register index type. + // Integer register type. typedef TheISA::IntReg IntReg; + // Floating point register type. + typedef TheISA::FloatReg FloatReg; + + // The DynInstPtr type. + typedef typename Impl::DynInstPtr DynInstPtr; + + // The list of instructions iterator type. + typedef typename std::list<DynInstPtr>::iterator ListIt; enum { - MaxInstSrcRegs = TheISA::MaxInstSrcRegs, //< Max source regs - MaxInstDestRegs = TheISA::MaxInstDestRegs, //< Max dest regs + MaxInstSrcRegs = TheISA::MaxInstSrcRegs, /// Max source regs + MaxInstDestRegs = TheISA::MaxInstDestRegs, /// Max dest regs }; - /** The static inst used by this dyn inst. */ + /** The StaticInst used by this BaseDynInst. */ StaticInstPtr staticInst; //////////////////////////////////////////// @@ -80,11 +92,27 @@ class BaseDynInst : public FastAlloc, public RefCounted // INSTRUCTION EXECUTION // //////////////////////////////////////////// + /** InstRecord that tracks this instructions. */ Trace::InstRecord *traceData; + /** + * Does a read to a given address. + * @param addr The address to read. + * @param data The read's data is written into this parameter. + * @param flags The request's flags. + * @return Returns any fault due to the read. + */ template <class T> Fault read(Addr addr, T &data, unsigned flags); + /** + * Does a write to a given address. + * @param data The data to be written. + * @param addr The address to write to. + * @param flags The request's flags. + * @param res The result of the write (for load locked/store conditionals). + * @return Returns any fault due to the write. + */ template <class T> Fault write(T data, Addr addr, unsigned flags, uint64_t *res); @@ -96,44 +124,37 @@ class BaseDynInst : public FastAlloc, public RefCounted /** @todo: Consider making this private. */ public: - /** Is this instruction valid. */ - bool valid; - /** The sequence number of the instruction. */ InstSeqNum seqNum; - /** How many source registers are ready. */ - unsigned readyRegs; - - /** Is the instruction completed. */ - bool completed; - - /** Can this instruction issue. */ - bool canIssue; - - /** Has this instruction issued. */ - bool issued; - - /** Has this instruction executed (or made it through execute) yet. */ - bool executed; - - /** Can this instruction commit. */ - bool canCommit; - - /** Is this instruction squashed. */ - bool squashed; - - /** Is this instruction squashed in the instruction queue. */ - bool squashedInIQ; - - /** Is this a recover instruction. */ - bool recoverInst; - - /** Is this a thread blocking instruction. */ - bool blockingInst; /* this inst has called thread_block() */ + enum Status { + IqEntry, /// Instruction is in the IQ + RobEntry, /// Instruction is in the ROB + LsqEntry, /// Instruction is in the LSQ + Completed, /// Instruction has completed + ResultReady, /// Instruction has its result + CanIssue, /// Instruction can issue and execute + Issued, /// Instruction has issued + Executed, /// Instruction has executed + CanCommit, /// Instruction can commit + AtCommit, /// Instruction has reached commit + Committed, /// Instruction has committed + Squashed, /// Instruction is squashed + SquashedInIQ, /// Instruction is squashed in the IQ + SquashedInLSQ, /// Instruction is squashed in the LSQ + SquashedInROB, /// Instruction is squashed in the ROB + RecoverInst, /// Is a recover instruction + BlockingInst, /// Is a blocking instruction + ThreadsyncWait, /// Is a thread synchronization instruction + SerializeBefore, /// Needs to serialize on + /// instructions ahead of it + SerializeAfter, /// Needs to serialize instructions behind it + SerializeHandled, /// Serialization has been handled + NumStatus + }; - /** Is this a thread syncrhonization instruction. */ - bool threadsyncWait; + /** The status of this BaseDynInst. Several bits can be set. */ + std::bitset<NumStatus> status; /** The thread this instruction is from. */ short threadNumber; @@ -141,15 +162,24 @@ class BaseDynInst : public FastAlloc, public RefCounted /** data address space ID, for loads & stores. */ short asid; - /** Pointer to the FullCPU object. */ - FullCPU *cpu; + /** How many source registers are ready. */ + unsigned readyRegs; + + /** Pointer to the Impl's CPU object. */ + ImplCPU *cpu; - /** Pointer to the exec context. Will not exist in the final version. */ - CPUExecContext *cpuXC; + /** Pointer to the thread state. */ + ImplState *thread; /** The kind of fault this instruction has generated. */ Fault fault; + /** The memory request. */ + Request *req; + + /** Pointer to the data for the memory access. */ + uint8_t *memData; + /** The effective virtual address (lds & stores only). */ Addr effAddr; @@ -165,12 +195,6 @@ class BaseDynInst : public FastAlloc, public RefCounted /** The memory request flags (from translation). */ unsigned memReqFlags; - /** The size of the data to be stored. */ - int storeSize; - - /** The data to be stored. */ - IntReg storeData; - union Result { uint64_t integer; float fp; @@ -197,17 +221,29 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Count of total number of dynamic instructions. */ static int instcount; - /** Whether or not the source register is ready. Not sure this should be - * here vs. the derived class. +#ifdef DEBUG + void dumpSNList(); +#endif + + /** Whether or not the source register is ready. + * @todo: Not sure this should be here vs the derived class. */ bool _readySrcRegIdx[MaxInstSrcRegs]; public: - /** BaseDynInst constructor given a binary instruction. */ - BaseDynInst(MachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, - FullCPU *cpu); + /** BaseDynInst constructor given a binary instruction. + * @param inst The binary instruction. + * @param PC The PC of the instruction. + * @param pred_PC The predicted next PC. + * @param seq_num The sequence number of the instruction. + * @param cpu Pointer to the instruction's CPU. + */ + BaseDynInst(ExtMachInst inst, Addr PC, Addr pred_PC, InstSeqNum seq_num, + ImplCPU *cpu); - /** BaseDynInst constructor given a static inst pointer. */ + /** BaseDynInst constructor given a StaticInst pointer. + * @param _staticInst The StaticInst for this BaseDynInst. + */ BaseDynInst(StaticInstPtr &_staticInst); /** BaseDynInst destructor. */ @@ -218,13 +254,6 @@ class BaseDynInst : public FastAlloc, public RefCounted void initVars(); public: - void - trace_mem(Fault fault, // last fault - MemCmd cmd, // last command - Addr addr, // virtual address of access - void *p, // memory accessed - int nbytes); // access size - /** Dumps out contents of this BaseDynInst. */ void dump(); @@ -237,6 +266,7 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Checks whether or not this instruction has had its branch target * calculated yet. For now it is not utilized and is hacked to be * always false. + * @todo: Actually use this instruction. */ bool doneTargCalc() { return false; } @@ -252,12 +282,10 @@ class BaseDynInst : public FastAlloc, public RefCounted Addr readPredTarg() { return predPC; } /** Returns whether the instruction was predicted taken or not. */ - bool predTaken() { - return( predPC != (PC + sizeof(MachInst) ) ); - } + bool predTaken() { return predPC != (PC + sizeof(MachInst)); } /** Returns whether the instruction mispredicted. */ - bool mispredicted() { return (predPC != nextPC); } + bool mispredicted() { return predPC != nextPC; } // // Instruction types. Forward checks to StaticInst object. @@ -266,6 +294,8 @@ class BaseDynInst : public FastAlloc, public RefCounted bool isMemRef() const { return staticInst->isMemRef(); } bool isLoad() const { return staticInst->isLoad(); } bool isStore() const { return staticInst->isStore(); } + bool isStoreConditional() const + { return staticInst->isStoreConditional(); } bool isInstPrefetch() const { return staticInst->isInstPrefetch(); } bool isDataPrefetch() const { return staticInst->isDataPrefetch(); } bool isCopy() const { return staticInst->isCopy(); } @@ -280,9 +310,44 @@ class BaseDynInst : public FastAlloc, public RefCounted bool isUncondCtrl() const { return staticInst->isUncondCtrl(); } bool isThreadSync() const { return staticInst->isThreadSync(); } bool isSerializing() const { return staticInst->isSerializing(); } + bool isSerializeBefore() const + { return staticInst->isSerializeBefore() || status[SerializeBefore]; } + bool isSerializeAfter() const + { return staticInst->isSerializeAfter() || status[SerializeAfter]; } bool isMemBarrier() const { return staticInst->isMemBarrier(); } bool isWriteBarrier() const { return staticInst->isWriteBarrier(); } bool isNonSpeculative() const { return staticInst->isNonSpeculative(); } + bool isQuiesce() const { return staticInst->isQuiesce(); } + bool isIprAccess() const { return staticInst->isIprAccess(); } + bool isUnverifiable() const { return staticInst->isUnverifiable(); } + + /** Temporarily sets this instruction as a serialize before instruction. */ + void setSerializeBefore() { status.set(SerializeBefore); } + + /** Clears the serializeBefore part of this instruction. */ + void clearSerializeBefore() { status.reset(SerializeBefore); } + + /** Checks if this serializeBefore is only temporarily set. */ + bool isTempSerializeBefore() { return status[SerializeBefore]; } + + /** Temporarily sets this instruction as a serialize after instruction. */ + void setSerializeAfter() { status.set(SerializeAfter); } + + /** Clears the serializeAfter part of this instruction.*/ + void clearSerializeAfter() { status.reset(SerializeAfter); } + + /** Checks if this serializeAfter is only temporarily set. */ + bool isTempSerializeAfter() { return status[SerializeAfter]; } + + /** Sets the serialization part of this instruction as handled. */ + void setSerializeHandled() { status.set(SerializeHandled); } + + /** Checks if the serialization part of this instruction has been + * handled. This does not apply to the temporary serializing + * state; it only applies to this instruction's own permanent + * serializing state. + */ + bool isSerializeHandled() { return status[SerializeHandled]; } /** Returns the opclass of this instruction. */ OpClass opClass() const { return staticInst->opClass(); } @@ -290,10 +355,10 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Returns the branch target address. */ Addr branchTarget() const { return staticInst->branchTarget(PC); } - /** Number of source registers. */ - int8_t numSrcRegs() const { return staticInst->numSrcRegs(); } + /** Returns the number of source registers. */ + int8_t numSrcRegs() const { return staticInst->numSrcRegs(); } - /** Number of destination registers. */ + /** Returns the number of destination registers. */ int8_t numDestRegs() const { return staticInst->numDestRegs(); } // the following are used to track physical register usage @@ -302,16 +367,10 @@ class BaseDynInst : public FastAlloc, public RefCounted int8_t numIntDestRegs() const { return staticInst->numIntDestRegs(); } /** Returns the logical register index of the i'th destination register. */ - RegIndex destRegIdx(int i) const - { - return staticInst->destRegIdx(i); - } + RegIndex destRegIdx(int i) const { return staticInst->destRegIdx(i); } /** Returns the logical register index of the i'th source register. */ - RegIndex srcRegIdx(int i) const - { - return staticInst->srcRegIdx(i); - } + RegIndex srcRegIdx(int i) const { return staticInst->srcRegIdx(i); } /** Returns the result of an integer instruction. */ uint64_t readIntResult() { return instResult.integer; } @@ -322,30 +381,47 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Returns the result of a floating point (double) instruction. */ double readDoubleResult() { return instResult.dbl; } - //Push to .cc file. - /** Records that one of the source registers is ready. */ - void markSrcRegReady() + /** Records an integer register being set to a value. */ + void setIntReg(const StaticInst *si, int idx, uint64_t val) { - ++readyRegs; - if(readyRegs == numSrcRegs()) { - canIssue = true; - } + instResult.integer = val; } - /** Marks a specific register as ready. - * @todo: Move this to .cc file. - */ - void markSrcRegReady(RegIndex src_idx) + /** Records an fp register being set to a value. */ + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) { - ++readyRegs; + if (width == 32) + instResult.fp = val; + else if (width == 64) + instResult.dbl = val; + else + panic("Unsupported width!"); + } - _readySrcRegIdx[src_idx] = 1; + /** Records an fp register being set to a value. */ + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + instResult.fp = val; + } - if(readyRegs == numSrcRegs()) { - canIssue = true; - } + /** Records an fp register being set to an integer value. */ + void setFloatRegBits(const StaticInst *si, int idx, uint64_t val, int width) + { + instResult.integer = val; + } + + /** Records an fp register being set to an integer value. */ + void setFloatRegBits(const StaticInst *si, int idx, uint64_t val) + { + instResult.integer = val; } + /** Records that one of the source registers is ready. */ + void markSrcRegReady(); + + /** Marks a specific register as ready. */ + void markSrcRegReady(RegIndex src_idx); + /** Returns if a source register is ready. */ bool isReadySrcRegIdx(int idx) const { @@ -353,66 +429,140 @@ class BaseDynInst : public FastAlloc, public RefCounted } /** Sets this instruction as completed. */ - void setCompleted() { completed = true; } + void setCompleted() { status.set(Completed); } + + /** Returns whether or not this instruction is completed. */ + bool isCompleted() const { return status[Completed]; } + + /** Marks the result as ready. */ + void setResultReady() { status.set(ResultReady); } - /** Returns whethe or not this instruction is completed. */ - bool isCompleted() const { return completed; } + /** Returns whether or not the result is ready. */ + bool isResultReady() const { return status[ResultReady]; } /** Sets this instruction as ready to issue. */ - void setCanIssue() { canIssue = true; } + void setCanIssue() { status.set(CanIssue); } /** Returns whether or not this instruction is ready to issue. */ - bool readyToIssue() const { return canIssue; } + bool readyToIssue() const { return status[CanIssue]; } /** Sets this instruction as issued from the IQ. */ - void setIssued() { issued = true; } + void setIssued() { status.set(Issued); } /** Returns whether or not this instruction has issued. */ - bool isIssued() const { return issued; } + bool isIssued() const { return status[Issued]; } /** Sets this instruction as executed. */ - void setExecuted() { executed = true; } + void setExecuted() { status.set(Executed); } /** Returns whether or not this instruction has executed. */ - bool isExecuted() const { return executed; } + bool isExecuted() const { return status[Executed]; } /** Sets this instruction as ready to commit. */ - void setCanCommit() { canCommit = true; } + void setCanCommit() { status.set(CanCommit); } /** Clears this instruction as being ready to commit. */ - void clearCanCommit() { canCommit = false; } + void clearCanCommit() { status.reset(CanCommit); } /** Returns whether or not this instruction is ready to commit. */ - bool readyToCommit() const { return canCommit; } + bool readyToCommit() const { return status[CanCommit]; } + + void setAtCommit() { status.set(AtCommit); } + + bool isAtCommit() { return status[AtCommit]; } + + /** Sets this instruction as committed. */ + void setCommitted() { status.set(Committed); } + + /** Returns whether or not this instruction is committed. */ + bool isCommitted() const { return status[Committed]; } /** Sets this instruction as squashed. */ - void setSquashed() { squashed = true; } + void setSquashed() { status.set(Squashed); } /** Returns whether or not this instruction is squashed. */ - bool isSquashed() const { return squashed; } + bool isSquashed() const { return status[Squashed]; } + + //Instruction Queue Entry + //----------------------- + /** Sets this instruction as a entry the IQ. */ + void setInIQ() { status.set(IqEntry); } + + /** Sets this instruction as a entry the IQ. */ + void clearInIQ() { status.reset(IqEntry); } + + /** Returns whether or not this instruction has issued. */ + bool isInIQ() const { return status[IqEntry]; } /** Sets this instruction as squashed in the IQ. */ - void setSquashedInIQ() { squashedInIQ = true; } + void setSquashedInIQ() { status.set(SquashedInIQ); status.set(Squashed);} /** Returns whether or not this instruction is squashed in the IQ. */ - bool isSquashedInIQ() const { return squashedInIQ; } + bool isSquashedInIQ() const { return status[SquashedInIQ]; } + + + //Load / Store Queue Functions + //----------------------- + /** Sets this instruction as a entry the LSQ. */ + void setInLSQ() { status.set(LsqEntry); } + + /** Sets this instruction as a entry the LSQ. */ + void removeInLSQ() { status.reset(LsqEntry); } + + /** Returns whether or not this instruction is in the LSQ. */ + bool isInLSQ() const { return status[LsqEntry]; } + + /** Sets this instruction as squashed in the LSQ. */ + void setSquashedInLSQ() { status.set(SquashedInLSQ);} + + /** Returns whether or not this instruction is squashed in the LSQ. */ + bool isSquashedInLSQ() const { return status[SquashedInLSQ]; } + + + //Reorder Buffer Functions + //----------------------- + /** Sets this instruction as a entry the ROB. */ + void setInROB() { status.set(RobEntry); } + + /** Sets this instruction as a entry the ROB. */ + void clearInROB() { status.reset(RobEntry); } + + /** Returns whether or not this instruction is in the ROB. */ + bool isInROB() const { return status[RobEntry]; } + + /** Sets this instruction as squashed in the ROB. */ + void setSquashedInROB() { status.set(SquashedInROB); } + + /** Returns whether or not this instruction is squashed in the ROB. */ + bool isSquashedInROB() const { return status[SquashedInROB]; } /** Read the PC of this instruction. */ const Addr readPC() const { return PC; } /** Set the next PC of this instruction (its actual target). */ - void setNextPC(uint64_t val) { nextPC = val; } + void setNextPC(uint64_t val) + { + nextPC = val; + } - /** Returns the exec context. - * @todo: Remove this once the ExecContext is no longer used. - */ - ExecContext *xcBase() { return cpuXC->getProxy(); } + /** Sets the ASID. */ + void setASID(short addr_space_id) { asid = addr_space_id; } + + /** Sets the thread id. */ + void setTid(unsigned tid) { threadNumber = tid; } + + /** Sets the pointer to the thread state. */ + void setThreadState(ImplState *state) { thread = state; } + + /** Returns the thread context. */ + ThreadContext *tcBase() { return thread->getTC(); } private: /** Instruction effective address. * @todo: Consider if this is necessary or not. */ Addr instEffAddr; + /** Whether or not the effective address calculation is completed. * @todo: Consider if this is necessary or not. */ @@ -431,12 +581,24 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Returns whether or not the eff. addr. source registers are ready. */ bool eaSrcsReady(); + /** Whether or not the memory operation is done. */ + bool memOpDone; + public: /** Load queue index. */ int16_t lqIdx; /** Store queue index. */ int16_t sqIdx; + + /** Iterator pointing to this BaseDynInst in the list of all insts. */ + ListIt instListIt; + + /** Returns iterator to this instruction in the list of all insts. */ + ListIt &getInstListIt() { return instListIt; } + + /** Sets iterator for this instruction in the list of all insts. */ + void setInstListIt(ListIt _instListIt) { instListIt = _instListIt; } }; template<class Impl> @@ -444,34 +606,47 @@ template<class T> inline Fault BaseDynInst<Impl>::read(Addr addr, T &data, unsigned flags) { - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), sizeof(T), flags); - req->asid = asid; + // Sometimes reads will get retried, so they may come through here + // twice. + if (!req) { + req = new Request(); + req->setVirt(asid, addr, sizeof(T), flags, this->PC); + req->setThreadContext(thread->readCpuId(), threadNumber); + } else { + assert(addr == req->getVaddr()); + } - fault = cpu->translateDataReadReq(req); + if ((req->getVaddr() & (TheISA::VMPageSize - 1)) + req->getSize() > + TheISA::VMPageSize) { + return TheISA::genAlignmentFault(); + } - // Record key MemReq parameters so we can generate another one - // just like it for the timing access without calling translate() - // again (which might mess up the TLB). - // Do I ever really need this? -KTL 3/05 - effAddr = req->vaddr; - physEffAddr = req->paddr; - memReqFlags = req->flags; - - /** - * @todo - * Replace the disjoint functional memory with a unified one and remove - * this hack. - */ -#if !FULL_SYSTEM - req->paddr = req->vaddr; -#endif + fault = cpu->translateDataReadReq(req, thread); if (fault == NoFault) { + effAddr = req->getVaddr(); + physEffAddr = req->getPaddr(); + memReqFlags = req->getFlags(); + +#if 0 + if (cpu->system->memctrl->badaddr(physEffAddr)) { + fault = TheISA::genMachineCheckFault(); + data = (T)-1; + this->setExecuted(); + } else { + fault = cpu->read(req, data, lqIdx); + } +#else fault = cpu->read(req, data, lqIdx); +#endif } else { // Return a fixed value to keep simulation deterministic even // along misspeculated paths. data = (T)-1; + + // Commit will have to clean up whatever happened. Set this + // instruction as executed. + this->setExecuted(); } if (traceData) { @@ -492,36 +667,38 @@ BaseDynInst<Impl>::write(T data, Addr addr, unsigned flags, uint64_t *res) traceData->setData(data); } - MemReqPtr req = new MemReq(addr, cpuXC->getProxy(), sizeof(T), flags); - - req->asid = asid; + assert(req == NULL); - fault = cpu->translateDataWriteReq(req); + req = new Request(); + req->setVirt(asid, addr, sizeof(T), flags, this->PC); + req->setThreadContext(thread->readCpuId(), threadNumber); - // Record key MemReq parameters so we can generate another one - // just like it for the timing access without calling translate() - // again (which might mess up the TLB). - effAddr = req->vaddr; - physEffAddr = req->paddr; - memReqFlags = req->flags; + if ((req->getVaddr() & (TheISA::VMPageSize - 1)) + req->getSize() > + TheISA::VMPageSize) { + return TheISA::genAlignmentFault(); + } - /** - * @todo - * Replace the disjoint functional memory with a unified one and remove - * this hack. - */ -#if !FULL_SYSTEM - req->paddr = req->vaddr; -#endif + fault = cpu->translateDataWriteReq(req, thread); if (fault == NoFault) { + effAddr = req->getVaddr(); + physEffAddr = req->getPaddr(); + memReqFlags = req->getFlags(); +#if 0 + if (cpu->system->memctrl->badaddr(physEffAddr)) { + fault = TheISA::genMachineCheckFault(); + } else { + fault = cpu->write(req, data, sqIdx); + } +#else fault = cpu->write(req, data, sqIdx); +#endif } if (res) { // always return some result to keep misspeculated paths // (which will ignore faults) deterministic - *res = (fault == NoFault) ? req->result : 0; + *res = (fault == NoFault) ? req->getScResult() : 0; } return fault; diff --git a/src/cpu/base_dyn_inst_impl.hh b/src/cpu/base_dyn_inst_impl.hh new file mode 100644 index 000000000..91424faad --- /dev/null +++ b/src/cpu/base_dyn_inst_impl.hh @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include <iostream> +#include <set> +#include <string> +#include <sstream> + +#include "base/cprintf.hh" +#include "base/trace.hh" + +#include "arch/faults.hh" +#include "cpu/exetrace.hh" +#include "mem/request.hh" + +#include "cpu/base_dyn_inst.hh" + +using namespace std; +using namespace TheISA; + +#define NOHASH +#ifndef NOHASH + +#include "base/hashmap.hh" + +unsigned int MyHashFunc(const BaseDynInst *addr) +{ + unsigned a = (unsigned)addr; + unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; + + return hash; +} + +typedef m5::hash_map<const BaseDynInst *, const BaseDynInst *, MyHashFunc> +my_hash_t; + +my_hash_t thishash; +#endif + +template <class Impl> +BaseDynInst<Impl>::BaseDynInst(ExtMachInst machInst, Addr inst_PC, + Addr pred_PC, InstSeqNum seq_num, + ImplCPU *cpu) + : staticInst(machInst), traceData(NULL), cpu(cpu) +{ + seqNum = seq_num; + + PC = inst_PC; + nextPC = PC + sizeof(MachInst); + predPC = pred_PC; + + initVars(); +} + +template <class Impl> +BaseDynInst<Impl>::BaseDynInst(StaticInstPtr &_staticInst) + : staticInst(_staticInst), traceData(NULL) +{ + seqNum = 0; + initVars(); +} + +template <class Impl> +void +BaseDynInst<Impl>::initVars() +{ + req = NULL; + memData = NULL; + effAddr = 0; + physEffAddr = 0; + + readyRegs = 0; + + instResult.integer = 0; + + status.reset(); + + eaCalcDone = false; + memOpDone = false; + + lqIdx = -1; + sqIdx = -1; + + // Eventually make this a parameter. + threadNumber = 0; + + // Also make this a parameter, or perhaps get it from xc or cpu. + asid = 0; + + // Initialize the fault to be NoFault. + fault = NoFault; + + ++instcount; + + if (instcount > 1500) { + cpu->dumpInsts(); +#ifdef DEBUG + dumpSNList(); +#endif + assert(instcount <= 1500); + } + + DPRINTF(DynInst, "DynInst: [sn:%lli] Instruction created. Instcount=%i\n", + seqNum, instcount); + +#ifdef DEBUG + cpu->snList.insert(seqNum); +#endif +} + +template <class Impl> +BaseDynInst<Impl>::~BaseDynInst() +{ + if (req) { + delete req; + } + + if (memData) { + delete [] memData; + } + + if (traceData) { + delete traceData; + } + + fault = NoFault; + + --instcount; + + DPRINTF(DynInst, "DynInst: [sn:%lli] Instruction destroyed. Instcount=%i\n", + seqNum, instcount); +#ifdef DEBUG + cpu->snList.erase(seqNum); +#endif +} + +#ifdef DEBUG +template <class Impl> +void +BaseDynInst<Impl>::dumpSNList() +{ + std::set<InstSeqNum>::iterator sn_it = cpu->snList.begin(); + + int count = 0; + while (sn_it != cpu->snList.end()) { + cprintf("%i: [sn:%lli] not destroyed\n", count, (*sn_it)); + count++; + sn_it++; + } +} +#endif + +template <class Impl> +void +BaseDynInst<Impl>::prefetch(Addr addr, unsigned flags) +{ + // This is the "functional" implementation of prefetch. Not much + // happens here since prefetches don't affect the architectural + // state. +/* + // Generate a MemReq so we can translate the effective address. + MemReqPtr req = new MemReq(addr, thread->getXCProxy(), 1, flags); + req->asid = asid; + + // Prefetches never cause faults. + fault = NoFault; + + // note this is a local, not BaseDynInst::fault + Fault trans_fault = cpu->translateDataReadReq(req); + + if (trans_fault == NoFault && !(req->flags & UNCACHEABLE)) { + // It's a valid address to cacheable space. Record key MemReq + // parameters so we can generate another one just like it for + // the timing access without calling translate() again (which + // might mess up the TLB). + effAddr = req->vaddr; + physEffAddr = req->paddr; + memReqFlags = req->flags; + } else { + // Bogus address (invalid or uncacheable space). Mark it by + // setting the eff_addr to InvalidAddr. + effAddr = physEffAddr = MemReq::inval_addr; + } + + if (traceData) { + traceData->setAddr(addr); + } +*/ +} + +template <class Impl> +void +BaseDynInst<Impl>::writeHint(Addr addr, int size, unsigned flags) +{ + // Not currently supported. +} + +/** + * @todo Need to find a way to get the cache block size here. + */ +template <class Impl> +Fault +BaseDynInst<Impl>::copySrcTranslate(Addr src) +{ + // Not currently supported. + return NoFault; +} + +/** + * @todo Need to find a way to get the cache block size here. + */ +template <class Impl> +Fault +BaseDynInst<Impl>::copy(Addr dest) +{ + // Not currently supported. + return NoFault; +} + +template <class Impl> +void +BaseDynInst<Impl>::dump() +{ + cprintf("T%d : %#08d `", threadNumber, PC); + cout << staticInst->disassemble(PC); + cprintf("'\n"); +} + +template <class Impl> +void +BaseDynInst<Impl>::dump(std::string &outstring) +{ + std::ostringstream s; + s << "T" << threadNumber << " : 0x" << PC << " " + << staticInst->disassemble(PC); + + outstring = s.str(); +} + +template <class Impl> +void +BaseDynInst<Impl>::markSrcRegReady() +{ + if (++readyRegs == numSrcRegs()) { + status.set(CanIssue); + } +} + +template <class Impl> +void +BaseDynInst<Impl>::markSrcRegReady(RegIndex src_idx) +{ + _readySrcRegIdx[src_idx] = true; + + markSrcRegReady(); +} + +template <class Impl> +bool +BaseDynInst<Impl>::eaSrcsReady() +{ + // For now I am assuming that src registers 1..n-1 are the ones that the + // EA calc depends on. (i.e. src reg 0 is the source of the data to be + // stored) + + for (int i = 1; i < numSrcRegs(); ++i) { + if (!_readySrcRegIdx[i]) + return false; + } + + return true; +} diff --git a/src/cpu/checker/cpu.cc b/src/cpu/checker/cpu.cc new file mode 100644 index 000000000..1540a6b94 --- /dev/null +++ b/src/cpu/checker/cpu.cc @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include <list> +#include <string> + +#include "cpu/base.hh" +#include "cpu/checker/cpu.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" +#include "cpu/static_inst.hh" +#include "mem/packet_impl.hh" +#include "sim/byteswap.hh" + +#if FULL_SYSTEM +#include "arch/vtophys.hh" +#include "kern/kernel_stats.hh" +#endif // FULL_SYSTEM + +using namespace std; +//The CheckerCPU does alpha only +using namespace AlphaISA; + +void +CheckerCPU::init() +{ +} + +CheckerCPU::CheckerCPU(Params *p) + : BaseCPU(p), thread(NULL), tc(NULL) +{ + memReq = NULL; + + numInst = 0; + startNumInst = 0; + numLoad = 0; + startNumLoad = 0; + youngestSN = 0; + + changedPC = willChangePC = changedNextPC = false; + + exitOnError = p->exitOnError; + warnOnlyOnLoadError = p->warnOnlyOnLoadError; +#if FULL_SYSTEM + itb = p->itb; + dtb = p->dtb; + systemPtr = NULL; +#else + process = p->process; +#endif + + result.integer = 0; +} + +CheckerCPU::~CheckerCPU() +{ +} + +void +CheckerCPU::setMemory(MemObject *mem) +{ +#if !FULL_SYSTEM + memPtr = mem; + thread = new SimpleThread(this, /* thread_num */ 0, process, + /* asid */ 0, mem); + + thread->setStatus(ThreadContext::Suspended); + tc = thread->getTC(); + threadContexts.push_back(tc); +#endif +} + +void +CheckerCPU::setSystem(System *system) +{ +#if FULL_SYSTEM + systemPtr = system; + + thread = new SimpleThread(this, 0, systemPtr, itb, dtb, false); + + thread->setStatus(ThreadContext::Suspended); + tc = thread->getTC(); + threadContexts.push_back(tc); + delete thread->kernelStats; + thread->kernelStats = NULL; +#endif +} + +void +CheckerCPU::setIcachePort(Port *icache_port) +{ + icachePort = icache_port; +} + +void +CheckerCPU::setDcachePort(Port *dcache_port) +{ + dcachePort = dcache_port; +} + +void +CheckerCPU::serialize(ostream &os) +{ +/* + BaseCPU::serialize(os); + SERIALIZE_SCALAR(inst); + nameOut(os, csprintf("%s.xc", name())); + thread->serialize(os); + cacheCompletionEvent.serialize(os); +*/ +} + +void +CheckerCPU::unserialize(Checkpoint *cp, const string §ion) +{ +/* + BaseCPU::unserialize(cp, section); + UNSERIALIZE_SCALAR(inst); + thread->unserialize(cp, csprintf("%s.xc", section)); +*/ +} + +Fault +CheckerCPU::copySrcTranslate(Addr src) +{ + panic("Unimplemented!"); +} + +Fault +CheckerCPU::copy(Addr dest) +{ + panic("Unimplemented!"); +} + +template <class T> +Fault +CheckerCPU::read(Addr addr, T &data, unsigned flags) +{ + // need to fill in CPU & thread IDs here + memReq = new Request(); + + memReq->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + // translate to physical address + translateDataReadReq(memReq); + + Packet *pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast); + + pkt->dataStatic(&data); + + if (!(memReq->getFlags() & UNCACHEABLE)) { + // Access memory to see if we have the same data + dcachePort->sendFunctional(pkt); + } else { + // Assume the data is correct if it's an uncached access + memcpy(&data, &unverifiedResult.integer, sizeof(T)); + } + + delete pkt; + + return NoFault; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template +Fault +CheckerCPU::read(Addr addr, uint64_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint32_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint16_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint8_t &data, unsigned flags); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +CheckerCPU::read(Addr addr, double &data, unsigned flags) +{ + return read(addr, *(uint64_t*)&data, flags); +} + +template<> +Fault +CheckerCPU::read(Addr addr, float &data, unsigned flags) +{ + return read(addr, *(uint32_t*)&data, flags); +} + +template<> +Fault +CheckerCPU::read(Addr addr, int32_t &data, unsigned flags) +{ + return read(addr, (uint32_t&)data, flags); +} + +template <class T> +Fault +CheckerCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + // need to fill in CPU & thread IDs here + memReq = new Request(); + + memReq->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + // translate to physical address + thread->translateDataWriteReq(memReq); + + // Can compare the write data and result only if it's cacheable, + // not a store conditional, or is a store conditional that + // succeeded. + // @todo: Verify that actual memory matches up with these values. + // Right now it only verifies that the instruction data is the + // same as what was in the request that got sent to memory; there + // is no verification that it is the same as what is in memory. + // This is because the LSQ would have to be snooped in the CPU to + // verify this data. + if (unverifiedReq && + !(unverifiedReq->getFlags() & UNCACHEABLE) && + (!(unverifiedReq->getFlags() & LOCKED) || + ((unverifiedReq->getFlags() & LOCKED) && + unverifiedReq->getScResult() == 1))) { + T inst_data; +/* + // This code would work if the LSQ allowed for snooping. + Packet *pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast); + pkt.dataStatic(&inst_data); + + dcachePort->sendFunctional(pkt); + + delete pkt; +*/ + memcpy(&inst_data, unverifiedMemData, sizeof(T)); + + if (data != inst_data) { + warn("%lli: Store value does not match value in memory! " + "Instruction: %#x, memory: %#x", + curTick, inst_data, data); + handleError(); + } + } + + // Assume the result was the same as the one passed in. This checker + // doesn't check if the SC should succeed or fail, it just checks the + // value. + if (res && unverifiedReq->scResultValid()) + *res = unverifiedReq->getScResult(); + + return NoFault; +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +template +Fault +CheckerCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +CheckerCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint64_t*)&data, addr, flags, res); +} + +template<> +Fault +CheckerCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint32_t*)&data, addr, flags, res); +} + +template<> +Fault +CheckerCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) +{ + return write((uint32_t)data, addr, flags, res); +} + + +#if FULL_SYSTEM +Addr +CheckerCPU::dbg_vtophys(Addr addr) +{ + return vtophys(tc, addr); +} +#endif // FULL_SYSTEM + +bool +CheckerCPU::translateInstReq(Request *req) +{ +#if FULL_SYSTEM + return (thread->translateInstReq(req) == NoFault); +#else + thread->translateInstReq(req); + return true; +#endif +} + +void +CheckerCPU::translateDataReadReq(Request *req) +{ + thread->translateDataReadReq(req); + + if (req->getVaddr() != unverifiedReq->getVaddr()) { + warn("%lli: Request virtual addresses do not match! Inst: %#x, " + "checker: %#x", + curTick, unverifiedReq->getVaddr(), req->getVaddr()); + handleError(); + } + req->setPaddr(unverifiedReq->getPaddr()); + + if (checkFlags(req)) { + warn("%lli: Request flags do not match! Inst: %#x, checker: %#x", + curTick, unverifiedReq->getFlags(), req->getFlags()); + handleError(); + } +} + +void +CheckerCPU::translateDataWriteReq(Request *req) +{ + thread->translateDataWriteReq(req); + + if (req->getVaddr() != unverifiedReq->getVaddr()) { + warn("%lli: Request virtual addresses do not match! Inst: %#x, " + "checker: %#x", + curTick, unverifiedReq->getVaddr(), req->getVaddr()); + handleError(); + } + req->setPaddr(unverifiedReq->getPaddr()); + + if (checkFlags(req)) { + warn("%lli: Request flags do not match! Inst: %#x, checker: %#x", + curTick, unverifiedReq->getFlags(), req->getFlags()); + handleError(); + } +} + +bool +CheckerCPU::checkFlags(Request *req) +{ + // Remove any dynamic flags that don't have to do with the request itself. + unsigned flags = unverifiedReq->getFlags(); + unsigned mask = LOCKED | PHYSICAL | VPTE | ALTMODE | UNCACHEABLE | NO_FAULT; + flags = flags & (mask); + if (flags == req->getFlags()) { + return false; + } else { + return true; + } +} + +void +CheckerCPU::dumpAndExit() +{ + warn("%lli: Checker PC:%#x, next PC:%#x", + curTick, thread->readPC(), thread->readNextPC()); + panic("Checker found an error!"); +} diff --git a/src/cpu/checker/cpu.hh b/src/cpu/checker/cpu.hh new file mode 100644 index 000000000..785387e60 --- /dev/null +++ b/src/cpu/checker/cpu.hh @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_CHECKER_CPU_HH__ +#define __CPU_CHECKER_CPU_HH__ + +#include <list> +#include <queue> +#include <map> + +#include "arch/types.hh" +#include "base/statistics.hh" +#include "config/full_system.hh" +#include "cpu/base.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/simple_thread.hh" +#include "cpu/pc_event.hh" +#include "cpu/static_inst.hh" +#include "sim/eventq.hh" + +// forward declarations +#if FULL_SYSTEM +class Processor; +class AlphaITB; +class AlphaDTB; +class PhysicalMemory; + +class RemoteGDB; +class GDBListener; + +#else + +class Process; + +#endif // FULL_SYSTEM +template <class> +class BaseDynInst; +class ThreadContext; +class MemInterface; +class Checkpoint; +class Request; +class Sampler; + +/** + * CheckerCPU class. Dynamically verifies instructions as they are + * completed by making sure that the instruction and its results match + * the independent execution of the benchmark inside the checker. The + * checker verifies instructions in order, regardless of the order in + * which instructions complete. There are certain results that can + * not be verified, specifically the result of a store conditional or + * the values of uncached accesses. In these cases, and with + * instructions marked as "IsUnverifiable", the checker assumes that + * the value from the main CPU's execution is correct and simply + * copies that value. It provides a CheckerThreadContext (see + * checker/thread_context.hh) that provides hooks for updating the + * Checker's state through any ThreadContext accesses. This allows the + * checker to be able to correctly verify instructions, even with + * external accesses to the ThreadContext that change state. + */ +class CheckerCPU : public BaseCPU +{ + protected: + typedef TheISA::MachInst MachInst; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + public: + virtual void init(); + + struct Params : public BaseCPU::Params + { +#if FULL_SYSTEM + AlphaITB *itb; + AlphaDTB *dtb; +#else + Process *process; +#endif + bool exitOnError; + bool warnOnlyOnLoadError; + }; + + public: + CheckerCPU(Params *p); + virtual ~CheckerCPU(); + + Process *process; + + void setMemory(MemObject *mem); + + MemObject *memPtr; + + void setSystem(System *system); + + System *systemPtr; + + void setIcachePort(Port *icache_port); + + Port *icachePort; + + void setDcachePort(Port *dcache_port); + + Port *dcachePort; + + public: + // Primary thread being run. + SimpleThread *thread; + + ThreadContext *tc; + + AlphaITB *itb; + AlphaDTB *dtb; + +#if FULL_SYSTEM + Addr dbg_vtophys(Addr addr); +#endif + + union Result { + uint64_t integer; + float fp; + double dbl; + }; + + Result result; + + // current instruction + MachInst machInst; + + // Pointer to the one memory request. + RequestPtr memReq; + + StaticInstPtr curStaticInst; + + // number of simulated instructions + Counter numInst; + Counter startNumInst; + + std::queue<int> miscRegIdxs; + + virtual Counter totalInstructions() const + { + return numInst - startNumInst; + } + + // number of simulated loads + Counter numLoad; + Counter startNumLoad; + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + // These functions are only used in CPU models that split + // effective address computation from the actual memory access. + void setEA(Addr EA) { panic("SimpleCPU::setEA() not implemented\n"); } + Addr getEA() { panic("SimpleCPU::getEA() not implemented\n"); } + + void prefetch(Addr addr, unsigned flags) + { + // need to do this... + } + + void writeHint(Addr addr, int size, unsigned flags) + { + // need to do this... + } + + Fault copySrcTranslate(Addr src); + + Fault copy(Addr dest); + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return thread->readIntReg(si->srcRegIdx(idx)); + } + + FloatReg readFloatReg(const StaticInst *si, int idx, int width) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatReg(reg_idx, width); + } + + FloatReg readFloatReg(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatReg(reg_idx); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatRegBits(reg_idx, width); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatRegBits(reg_idx); + } + + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + thread->setIntReg(si->destRegIdx(idx), val); + result.integer = val; + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatReg(reg_idx, val, width); + switch(width) { + case 32: + result.fp = val; + break; + case 64: + result.dbl = val; + break; + }; + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatReg(reg_idx, val); + result.fp = val; + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val, + int width) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatRegBits(reg_idx, val, width); + result.integer = val; + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatRegBits(reg_idx, val); + result.integer = val; + } + + uint64_t readPC() { return thread->readPC(); } + + uint64_t readNextPC() { return thread->readNextPC(); } + + void setNextPC(uint64_t val) { + thread->setNextPC(val); + } + + MiscReg readMiscReg(int misc_reg) + { + return thread->readMiscReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return thread->readMiscRegWithEffect(misc_reg, fault); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + result.integer = val; + miscRegIdxs.push(misc_reg); + return thread->setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + miscRegIdxs.push(misc_reg); + return thread->setMiscRegWithEffect(misc_reg, val); + } + + void recordPCChange(uint64_t val) { changedPC = true; } + void recordNextPCChange(uint64_t val) { changedNextPC = true; } + + bool translateInstReq(Request *req); + void translateDataWriteReq(Request *req); + void translateDataReadReq(Request *req); + +#if FULL_SYSTEM + Fault hwrei() { return thread->hwrei(); } + int readIntrFlag() { return thread->readIntrFlag(); } + void setIntrFlag(int val) { thread->setIntrFlag(val); } + bool inPalMode() { return thread->inPalMode(); } + void ev5_trap(Fault fault) { fault->invoke(tc); } + bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); } +#else + // Assume that the normal CPU's call to syscall was successful. + // The checker's state would have already been updated by the syscall. + void syscall(uint64_t callnum) { } +#endif + + void handleError() + { + if (exitOnError) + dumpAndExit(); + } + + bool checkFlags(Request *req); + + void dumpAndExit(); + + ThreadContext *tcBase() { return tc; } + SimpleThread *threadBase() { return thread; } + + Result unverifiedResult; + Request *unverifiedReq; + uint8_t *unverifiedMemData; + + bool changedPC; + bool willChangePC; + uint64_t newPC; + bool changedNextPC; + bool exitOnError; + bool warnOnlyOnLoadError; + + InstSeqNum youngestSN; +}; + +/** + * Templated Checker class. This Checker class is templated on the + * DynInstPtr of the instruction type that will be verified. Proper + * template instantiations of the Checker must be placed at the bottom + * of checker/cpu.cc. + */ +template <class DynInstPtr> +class Checker : public CheckerCPU +{ + public: + Checker(Params *p) + : CheckerCPU(p) + { } + + void switchOut(Sampler *s); + void takeOverFrom(BaseCPU *oldCPU); + + void verify(DynInstPtr &inst); + + void validateInst(DynInstPtr &inst); + void validateExecution(DynInstPtr &inst); + void validateState(); + + void copyResult(DynInstPtr &inst); + + private: + void handleError(DynInstPtr &inst) + { + if (exitOnError) + dumpAndExit(inst); + } + + void dumpAndExit(DynInstPtr &inst); + + std::list<DynInstPtr> instList; + typedef typename std::list<DynInstPtr>::iterator InstListIt; + void dumpInsts(); +}; + +#endif // __CPU_CHECKER_CPU_HH__ diff --git a/src/cpu/checker/cpu_impl.hh b/src/cpu/checker/cpu_impl.hh new file mode 100644 index 000000000..137e1c46d --- /dev/null +++ b/src/cpu/checker/cpu_impl.hh @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include <list> +#include <string> + +#include "base/refcnt.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/checker/cpu.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" +#include "cpu/static_inst.hh" +#include "mem/packet_impl.hh" +#include "sim/byteswap.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +#if FULL_SYSTEM +#include "arch/vtophys.hh" +#endif // FULL_SYSTEM + +using namespace std; +//The CheckerCPU does alpha only +using namespace AlphaISA; + +template <class DynInstPtr> +void +Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) +{ + DynInstPtr inst; + + // Either check this instruction, or add it to a list of + // instructions waiting to be checked. Instructions must be + // checked in program order, so if a store has committed yet not + // completed, there may be some instructions that are waiting + // behind it that have completed and must be checked. + if (!instList.empty()) { + if (youngestSN < completed_inst->seqNum) { + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", + completed_inst->seqNum, completed_inst->readPC()); + instList.push_back(completed_inst); + youngestSN = completed_inst->seqNum; + } + + if (!instList.front()->isCompleted()) { + return; + } else { + inst = instList.front(); + instList.pop_front(); + } + } else { + if (!completed_inst->isCompleted()) { + if (youngestSN < completed_inst->seqNum) { + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", + completed_inst->seqNum, completed_inst->readPC()); + instList.push_back(completed_inst); + youngestSN = completed_inst->seqNum; + } + return; + } else { + if (youngestSN < completed_inst->seqNum) { + inst = completed_inst; + youngestSN = completed_inst->seqNum; + } else { + return; + } + } + } + + // Try to check all instructions that are completed, ending if we + // run out of instructions to check or if an instruction is not + // yet completed. + while (1) { + DPRINTF(Checker, "Processing instruction [sn:%lli] PC:%#x.\n", + inst->seqNum, inst->readPC()); + unverifiedResult.integer = inst->readIntResult(); + unverifiedReq = inst->req; + unverifiedMemData = inst->memData; + numCycles++; + + Fault fault = NoFault; + + // maintain $r0 semantics + thread->setIntReg(ZeroReg, 0); +#ifdef TARGET_ALPHA + thread->setFloatRegDouble(ZeroReg, 0.0); +#endif // TARGET_ALPHA + + // Check if any recent PC changes match up with anything we + // expect to happen. This is mostly to check if traps or + // PC-based events have occurred in both the checker and CPU. + if (changedPC) { + DPRINTF(Checker, "Changed PC recently to %#x\n", + thread->readPC()); + if (willChangePC) { + if (newPC == thread->readPC()) { + DPRINTF(Checker, "Changed PC matches expected PC\n"); + } else { + warn("%lli: Changed PC does not match expected PC, " + "changed: %#x, expected: %#x", + curTick, thread->readPC(), newPC); + CheckerCPU::handleError(); + } + willChangePC = false; + } + changedPC = false; + } + if (changedNextPC) { + DPRINTF(Checker, "Changed NextPC recently to %#x\n", + thread->readNextPC()); + changedNextPC = false; + } + + // Try to fetch the instruction + +#if FULL_SYSTEM +#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0 +#else +#define IFETCH_FLAGS(pc) 0 +#endif + + uint64_t fetch_PC = thread->readPC() & ~3; + + // set up memory request for instruction fetch + memReq = new Request(inst->threadNumber, fetch_PC, + sizeof(uint32_t), + IFETCH_FLAGS(thread->readPC()), + fetch_PC, thread->readCpuId(), inst->threadNumber); + + bool succeeded = translateInstReq(memReq); + + if (!succeeded) { + if (inst->getFault() == NoFault) { + // In this case the instruction was not a dummy + // instruction carrying an ITB fault. In the single + // threaded case the ITB should still be able to + // translate this instruction; in the SMT case it's + // possible that its ITB entry was kicked out. + warn("%lli: Instruction PC %#x was not found in the ITB!", + curTick, thread->readPC()); + handleError(inst); + + // go to the next instruction + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); + + return; + } else { + // The instruction is carrying an ITB fault. Handle + // the fault and see if our results match the CPU on + // the next tick(). + fault = inst->getFault(); + } + } + + if (fault == NoFault) { + Packet *pkt = new Packet(memReq, Packet::ReadReq, + Packet::Broadcast); + + pkt->dataStatic(&machInst); + + icachePort->sendFunctional(pkt); + + delete pkt; + + // keep an instruction count + numInst++; + + // decode the instruction + machInst = gtoh(machInst); + // Checks that the instruction matches what we expected it to be. + // Checks both the machine instruction and the PC. + validateInst(inst); + + curStaticInst = StaticInst::decode(makeExtMI(machInst, + thread->readPC())); + +#if FULL_SYSTEM + thread->setInst(machInst); +#endif // FULL_SYSTEM + + fault = inst->getFault(); + } + + // Discard fetch's memReq. + delete memReq; + memReq = NULL; + + // Either the instruction was a fault and we should process the fault, + // or we should just go ahead execute the instruction. This assumes + // that the instruction is properly marked as a fault. + if (fault == NoFault) { + + thread->funcExeInst++; + + fault = curStaticInst->execute(this, NULL); + + // Checks to make sure instrution results are correct. + validateExecution(inst); + + if (curStaticInst->isLoad()) { + ++numLoad; + } + } + + if (fault != NoFault) { +#if FULL_SYSTEM + fault->invoke(tc); + willChangePC = true; + newPC = thread->readPC(); + DPRINTF(Checker, "Fault, PC is now %#x\n", newPC); +#else // !FULL_SYSTEM + fatal("fault (%d) detected @ PC 0x%08p", fault, thread->readPC()); +#endif // FULL_SYSTEM + } else { +#if THE_ISA != MIPS_ISA + // go to the next instruction + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); +#else + // go to the next instruction + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextNPC()); + thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); +#endif + + } + +#if FULL_SYSTEM + // @todo: Determine if these should happen only if the + // instruction hasn't faulted. In the SimpleCPU case this may + // not be true, but in the O3 or Ozone case this may be true. + Addr oldpc; + int count = 0; + do { + oldpc = thread->readPC(); + system->pcEventQueue.service(tc); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + willChangePC = true; + newPC = thread->readPC(); + DPRINTF(Checker, "PC Event, PC is now %#x\n", newPC); + } +#endif + + // @todo: Optionally can check all registers. (Or just those + // that have been modified). + validateState(); + + if (memReq) { + delete memReq; + memReq = NULL; + } + + // Continue verifying instructions if there's another completed + // instruction waiting to be verified. + if (instList.empty()) { + break; + } else if (instList.front()->isCompleted()) { + inst = instList.front(); + instList.pop_front(); + } else { + break; + } + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::switchOut(Sampler *s) +{ + instList.clear(); +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::takeOverFrom(BaseCPU *oldCPU) +{ +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateInst(DynInstPtr &inst) +{ + if (inst->readPC() != thread->readPC()) { + warn("%lli: PCs do not match! Inst: %#x, checker: %#x", + curTick, inst->readPC(), thread->readPC()); + if (changedPC) { + warn("%lli: Changed PCs recently, may not be an error", + curTick); + } else { + handleError(inst); + } + } + + MachInst mi = static_cast<MachInst>(inst->staticInst->machInst); + + if (mi != machInst) { + warn("%lli: Binary instructions do not match! Inst: %#x, " + "checker: %#x", + curTick, mi, machInst); + handleError(inst); + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateExecution(DynInstPtr &inst) +{ + bool result_mismatch = false; + if (inst->numDestRegs()) { + // @todo: Support more destination registers. + if (inst->isUnverifiable()) { + // Unverifiable instructions assume they were executed + // properly by the CPU. Grab the result from the + // instruction and write it to the register. + copyResult(inst); + } else if (result.integer != inst->readIntResult()) { + result_mismatch = true; + } + } + + if (result_mismatch) { + warn("%lli: Instruction results do not match! (Values may not " + "actually be integers) Inst: %#x, checker: %#x", + curTick, inst->readIntResult(), result.integer); + + // It's useful to verify load values from memory, but in MP + // systems the value obtained at execute may be different than + // the value obtained at completion. Similarly DMA can + // present the same problem on even UP systems. Thus there is + // the option to only warn on loads having a result error. + if (inst->isLoad() && warnOnlyOnLoadError) { + copyResult(inst); + } else { + handleError(inst); + } + } + + if (inst->readNextPC() != thread->readNextPC()) { + warn("%lli: Instruction next PCs do not match! Inst: %#x, " + "checker: %#x", + curTick, inst->readNextPC(), thread->readNextPC()); + handleError(inst); + } + + // Checking side effect registers can be difficult if they are not + // checked simultaneously with the execution of the instruction. + // This is because other valid instructions may have modified + // these registers in the meantime, and their values are not + // stored within the DynInst. + while (!miscRegIdxs.empty()) { + int misc_reg_idx = miscRegIdxs.front(); + miscRegIdxs.pop(); + + if (inst->tcBase()->readMiscReg(misc_reg_idx) != + thread->readMiscReg(misc_reg_idx)) { + warn("%lli: Misc reg idx %i (side effect) does not match! " + "Inst: %#x, checker: %#x", + curTick, misc_reg_idx, + inst->tcBase()->readMiscReg(misc_reg_idx), + thread->readMiscReg(misc_reg_idx)); + handleError(inst); + } + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateState() +{ +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::copyResult(DynInstPtr &inst) +{ + RegIndex idx = inst->destRegIdx(0); + if (idx < TheISA::FP_Base_DepTag) { + thread->setIntReg(idx, inst->readIntResult()); + } else if (idx < TheISA::Fpcr_DepTag) { + thread->setFloatRegBits(idx, inst->readIntResult()); + } else { + thread->setMiscReg(idx, inst->readIntResult()); + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::dumpAndExit(DynInstPtr &inst) +{ + cprintf("Error detected, instruction information:\n"); + cprintf("PC:%#x, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Completed:%i\n", + inst->readPC(), + inst->readNextPC(), + inst->seqNum, + inst->threadNumber, + inst->isCompleted()); + inst->dump(); + CheckerCPU::dumpAndExit(); +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::dumpInsts() +{ + int num = 0; + + InstListIt inst_list_it = --(instList.end()); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Completed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isCompleted()); + + cprintf("\n"); + + inst_list_it--; + ++num; + } + +} diff --git a/src/cpu/checker/thread_context.hh b/src/cpu/checker/thread_context.hh new file mode 100644 index 000000000..c0ac8f01d --- /dev/null +++ b/src/cpu/checker/thread_context.hh @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_CHECKER_THREAD_CONTEXT_HH__ +#define __CPU_CHECKER_THREAD_CONTEXT_HH__ + +#include "cpu/checker/cpu.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" + +class EndQuiesceEvent; +namespace Kernel { + class Statistics; +}; + +/** + * Derived ThreadContext class for use with the Checker. The template + * parameter is the ThreadContext class used by the specific CPU being + * verified. This CheckerThreadContext is then used by the main CPU + * in place of its usual ThreadContext class. It handles updating the + * checker's state any time state is updated externally through the + * ThreadContext. + */ +template <class TC> +class CheckerThreadContext : public ThreadContext +{ + public: + CheckerThreadContext(TC *actual_tc, + CheckerCPU *checker_cpu) + : actualTC(actual_tc), checkerTC(checker_cpu->thread), + checkerCPU(checker_cpu) + { } + + private: + /** The main CPU's ThreadContext, or class that implements the + * ThreadContext interface. */ + TC *actualTC; + /** The checker's own SimpleThread. Will be updated any time + * anything uses this ThreadContext to externally update a + * thread's state. */ + SimpleThread *checkerTC; + /** Pointer to the checker CPU. */ + CheckerCPU *checkerCPU; + + public: + + BaseCPU *getCpuPtr() { return actualTC->getCpuPtr(); } + + void setCpuId(int id) + { + actualTC->setCpuId(id); + checkerTC->setCpuId(id); + } + + int readCpuId() { return actualTC->readCpuId(); } + +#if FULL_SYSTEM + System *getSystemPtr() { return actualTC->getSystemPtr(); } + + PhysicalMemory *getPhysMemPtr() { return actualTC->getPhysMemPtr(); } + + AlphaITB *getITBPtr() { return actualTC->getITBPtr(); } + + AlphaDTB *getDTBPtr() { return actualTC->getDTBPtr(); } + + Kernel::Statistics *getKernelStats() { return actualTC->getKernelStats(); } + + FunctionalPort *getPhysPort() { return actualTC->getPhysPort(); } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) + { return actualTC->getVirtPort(); } + + void delVirtPort(VirtualPort *vp) { actualTC->delVirtPort(vp); } +#else + TranslatingPort *getMemPort() { return actualTC->getMemPort(); } + + Process *getProcessPtr() { return actualTC->getProcessPtr(); } +#endif + + Status status() const { return actualTC->status(); } + + void setStatus(Status new_status) + { + actualTC->setStatus(new_status); + checkerTC->setStatus(new_status); + } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1) { actualTC->activate(delay); } + + /// Set the status to Suspended. + void suspend() { actualTC->suspend(); } + + /// Set the status to Unallocated. + void deallocate() { actualTC->deallocate(); } + + /// Set the status to Halted. + void halt() { actualTC->halt(); } + +#if FULL_SYSTEM + void dumpFuncProfile() { actualTC->dumpFuncProfile(); } +#endif + + void takeOverFrom(ThreadContext *oldContext) + { + actualTC->takeOverFrom(oldContext); + checkerTC->takeOverFrom(oldContext); + } + + void regStats(const std::string &name) { actualTC->regStats(name); } + + void serialize(std::ostream &os) { actualTC->serialize(os); } + void unserialize(Checkpoint *cp, const std::string §ion) + { actualTC->unserialize(cp, section); } + +#if FULL_SYSTEM + EndQuiesceEvent *getQuiesceEvent() { return actualTC->getQuiesceEvent(); } + + Tick readLastActivate() { return actualTC->readLastActivate(); } + Tick readLastSuspend() { return actualTC->readLastSuspend(); } + + void profileClear() { return actualTC->profileClear(); } + void profileSample() { return actualTC->profileSample(); } +#endif + + int getThreadNum() { return actualTC->getThreadNum(); } + + // @todo: Do I need this? + MachInst getInst() { return actualTC->getInst(); } + + // @todo: Do I need this? + void copyArchRegs(ThreadContext *tc) + { + actualTC->copyArchRegs(tc); + checkerTC->copyArchRegs(tc); + } + + void clearArchRegs() + { + actualTC->clearArchRegs(); + checkerTC->clearArchRegs(); + } + + // + // New accessors for new decoder. + // + uint64_t readIntReg(int reg_idx) + { return actualTC->readIntReg(reg_idx); } + + FloatReg readFloatReg(int reg_idx, int width) + { return actualTC->readFloatReg(reg_idx, width); } + + FloatReg readFloatReg(int reg_idx) + { return actualTC->readFloatReg(reg_idx); } + + FloatRegBits readFloatRegBits(int reg_idx, int width) + { return actualTC->readFloatRegBits(reg_idx, width); } + + FloatRegBits readFloatRegBits(int reg_idx) + { return actualTC->readFloatRegBits(reg_idx); } + + void setIntReg(int reg_idx, uint64_t val) + { + actualTC->setIntReg(reg_idx, val); + checkerTC->setIntReg(reg_idx, val); + } + + void setFloatReg(int reg_idx, FloatReg val, int width) + { + actualTC->setFloatReg(reg_idx, val, width); + checkerTC->setFloatReg(reg_idx, val, width); + } + + void setFloatReg(int reg_idx, FloatReg val) + { + actualTC->setFloatReg(reg_idx, val); + checkerTC->setFloatReg(reg_idx, val); + } + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width) + { + actualTC->setFloatRegBits(reg_idx, val, width); + checkerTC->setFloatRegBits(reg_idx, val, width); + } + + void setFloatRegBits(int reg_idx, FloatRegBits val) + { + actualTC->setFloatRegBits(reg_idx, val); + checkerTC->setFloatRegBits(reg_idx, val); + } + + uint64_t readPC() { return actualTC->readPC(); } + + void setPC(uint64_t val) + { + actualTC->setPC(val); + checkerTC->setPC(val); + checkerCPU->recordPCChange(val); + } + + uint64_t readNextPC() { return actualTC->readNextPC(); } + + void setNextPC(uint64_t val) + { + actualTC->setNextPC(val); + checkerTC->setNextPC(val); + checkerCPU->recordNextPCChange(val); + } + + uint64_t readNextNPC() { return actualTC->readNextNPC(); } + + void setNextNPC(uint64_t val) + { + actualTC->setNextNPC(val); + checkerTC->setNextNPC(val); + checkerCPU->recordNextPCChange(val); + } + + MiscReg readMiscReg(int misc_reg) + { return actualTC->readMiscReg(misc_reg); } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return actualTC->readMiscRegWithEffect(misc_reg, fault); } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + checkerTC->setMiscReg(misc_reg, val); + return actualTC->setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + checkerTC->setMiscRegWithEffect(misc_reg, val); + return actualTC->setMiscRegWithEffect(misc_reg, val); + } + + unsigned readStCondFailures() + { return actualTC->readStCondFailures(); } + + void setStCondFailures(unsigned sc_failures) + { + checkerTC->setStCondFailures(sc_failures); + actualTC->setStCondFailures(sc_failures); + } +#if FULL_SYSTEM + bool inPalMode() { return actualTC->inPalMode(); } +#endif + + // @todo: Fix this! + bool misspeculating() { return actualTC->misspeculating(); } + +#if !FULL_SYSTEM + IntReg getSyscallArg(int i) { return actualTC->getSyscallArg(i); } + + // used to shift args for indirect syscall + void setSyscallArg(int i, IntReg val) + { + checkerTC->setSyscallArg(i, val); + actualTC->setSyscallArg(i, val); + } + + void setSyscallReturn(SyscallReturn return_value) + { + checkerTC->setSyscallReturn(return_value); + actualTC->setSyscallReturn(return_value); + } + + Counter readFuncExeInst() { return actualTC->readFuncExeInst(); } +#endif + void changeRegFileContext(RegFile::ContextParam param, + RegFile::ContextVal val) + { + actualTC->changeRegFileContext(param, val); + checkerTC->changeRegFileContext(param, val); + } +}; + +#endif // __CPU_CHECKER_EXEC_CONTEXT_HH__ diff --git a/src/cpu/cpu_models.py b/src/cpu/cpu_models.py index 8d0a15f61..1add32745 100644 --- a/src/cpu/cpu_models.py +++ b/src/cpu/cpu_models.py @@ -23,6 +23,12 @@ # 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. +# +# Authors: Steve Reinhardt + +import os +import os.path +import sys ################ # CpuModel class @@ -45,7 +51,6 @@ class CpuModel: # Add self to dict CpuModel.dict[name] = self - # # Define CPU models. # @@ -65,7 +70,27 @@ CpuModel('TimingSimpleCPU', 'timing_simple_cpu_exec.cc', CpuModel('FullCPU', 'full_cpu_exec.cc', '#include "encumbered/cpu/full/dyn_inst.hh"', { 'CPU_exec_context': 'DynInst' }) -CpuModel('AlphaFullCPU', 'alpha_o3_exec.cc', - '#include "cpu/o3/alpha_dyn_inst.hh"', - { 'CPU_exec_context': 'AlphaDynInst<AlphaSimpleImpl>' }) +CpuModel('OzoneSimpleCPU', 'ozone_simple_exec.cc', + '#include "cpu/ozone/dyn_inst.hh"', + { 'CPU_exec_context': 'OzoneDynInst<SimpleImpl>' }) +CpuModel('OzoneCPU', 'ozone_exec.cc', + '#include "cpu/ozone/dyn_inst.hh"', + { 'CPU_exec_context': 'OzoneDynInst<OzoneImpl>' }) +CpuModel('CheckerCPU', 'checker_cpu_exec.cc', + '#include "cpu/checker/cpu.hh"', + { 'CPU_exec_context': 'CheckerCPU' }) + +# Maybe there is a more clever way to determine ISA +# here but since the environment variable isnt passed through +# here the easiest way is this... +sub_template = 'not found' +for argument in sys.argv: + if 'ALPHA' in argument: + sub_template = 'AlphaDynInst<AlphaSimpleImpl>' + +if sub_template == 'not found': + sys.exit('NO CPU_exec_context substitution defined for this ISA') +CpuModel('O3CPU', 'o3_cpu_exec.cc', + '#include "cpu/o3/isa_specific.hh"', + { 'CPU_exec_context': sub_template }) diff --git a/src/cpu/cpuevent.cc b/src/cpu/cpuevent.cc new file mode 100644 index 000000000..679244a6b --- /dev/null +++ b/src/cpu/cpuevent.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + */ + +#include "cpu/cpuevent.hh" + +/** Static list of all CpuEvent objects so we can modify their thread + * contexts as needed. */ +CpuEvent::CpuEventList CpuEvent::cpuEventList; + +CpuEvent::~CpuEvent() +{ + CpuEventList::iterator i; + + // delete the event from the global list + for (i = cpuEventList.begin(); i != cpuEventList.end(); ) { + if (*i == this) + i = cpuEventList.erase(i); + else + i++; + } +} + +void +CpuEvent::replaceThreadContext(ThreadContext *oldTc, ThreadContext *newTc) +{ + CpuEventList::iterator i; + + // Update any events that have the old thread context with the new thread + // context + for (i = cpuEventList.begin(); i != cpuEventList.end(); i++) { + if ((*i)->tc == oldTc) + (*i)->tc = newTc; + } +} diff --git a/src/cpu/cpuevent.hh b/src/cpu/cpuevent.hh new file mode 100644 index 000000000..9dfae27cf --- /dev/null +++ b/src/cpu/cpuevent.hh @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + */ + +#ifndef __CPU_CPUEVENT_HH__ +#define __CPU_CPUEVENT_HH__ + +#include <vector> +#include "sim/eventq.hh" + +class ThreadContext; + +/** This class creates a global list of events that need a pointer to a + * thread context. When a switchover takes place the events can be migrated + * to the new thread context, otherwise you could have a wake timer interrupt + * go off on a switched out cpu or other unfortunate events. This object MUST be + * dynamically allocated to avoid it being deleted after a cpu switch happens. + * */ +class CpuEvent : public Event +{ + private: + /** type of global list of cpu events. */ + typedef std::vector<CpuEvent *> CpuEventList; + + /** Static list of cpu events that is searched every time a cpu switch + * happens. */ + static CpuEventList cpuEventList; + + /** The thread context that is switched to the new cpus. */ + ThreadContext *tc; + + public: + CpuEvent(EventQueue *q, ThreadContext *_tc, Priority p = Default_Pri) + : Event(q, p), tc(_tc) + { cpuEventList.push_back(this); } + + /** delete the cpu event from the global list. */ + ~CpuEvent(); + + /** Update all events switching old tc to new tc. + * @param oldTc the old thread context we are switching from + * @param newTc the new thread context we are switching to. + */ + static void replaceThreadContext(ThreadContext *oldTc, + ThreadContext *newTc); +}; + +template <class T, void (T::* F)(ThreadContext *tc)> +class CpuEventWrapper : public CpuEvent +{ + private: + T *object; + + public: + CpuEventWrapper(T *obj, ThreadContext *_tc, EventQueue *q = &mainEventQueue, + Priority p = Default_Pri) + : CpuEvent(q, _tc, p), object(obj) + { } + void process() { (object->*F)(tc); } +}; + +#endif // __CPU_CPUEVENT_HH__ + diff --git a/src/cpu/exec_context.hh b/src/cpu/exec_context.hh index 1f26183ab..f6e8d7c25 100644 --- a/src/cpu/exec_context.hh +++ b/src/cpu/exec_context.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 The Regents of The University of Michigan + * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,427 +24,138 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_EXEC_CONTEXT_HH__ -#define __CPU_EXEC_CONTEXT_HH__ - -#include "config/full_system.hh" -#include "mem/request.hh" -#include "sim/faults.hh" -#include "sim/host.hh" -#include "sim/serialize.hh" -#include "sim/byteswap.hh" - -// @todo: Figure out a more architecture independent way to obtain the ITB and -// DTB pointers. -class AlphaDTB; -class AlphaITB; -class BaseCPU; -class Event; -class TranslatingPort; -class FunctionalPort; -class VirtualPort; -class Process; -class System; - -class ExecContext -{ - protected: - typedef TheISA::RegFile RegFile; - typedef TheISA::MachInst MachInst; - typedef TheISA::IntReg IntReg; - typedef TheISA::FloatReg FloatReg; - typedef TheISA::FloatRegBits FloatRegBits; - typedef TheISA::MiscRegFile MiscRegFile; - typedef TheISA::MiscReg MiscReg; - public: - enum Status - { - /// Initialized but not running yet. All CPUs start in - /// this state, but most transition to Active on cycle 1. - /// In MP or SMT systems, non-primary contexts will stay - /// in this state until a thread is assigned to them. - Unallocated, - - /// Running. Instructions should be executed only when - /// the context is in this state. - Active, - - /// Temporarily inactive. Entered while waiting for - /// synchronization, etc. - Suspended, - - /// Permanently shut down. Entered when target executes - /// m5exit pseudo-instruction. When all contexts enter - /// this state, the simulation will terminate. - Halted - }; - - virtual ~ExecContext() { }; - - virtual BaseCPU *getCpuPtr() = 0; - - virtual void setCpuId(int id) = 0; - - virtual int readCpuId() = 0; - -#if FULL_SYSTEM - virtual System *getSystemPtr() = 0; - - virtual AlphaITB *getITBPtr() = 0; - - virtual AlphaDTB * getDTBPtr() = 0; - - virtual FunctionalPort *getPhysPort() = 0; - - virtual VirtualPort *getVirtPort(ExecContext *xc = NULL) = 0; - - virtual void delVirtPort(VirtualPort *vp) = 0; -#else - virtual TranslatingPort *getMemPort() = 0; - - virtual Process *getProcessPtr() = 0; -#endif - - virtual Status status() const = 0; - - virtual void setStatus(Status new_status) = 0; - - /// Set the status to Active. Optional delay indicates number of - /// cycles to wait before beginning execution. - virtual void activate(int delay = 1) = 0; - - /// Set the status to Suspended. - virtual void suspend() = 0; - - /// Set the status to Unallocated. - virtual void deallocate() = 0; - - /// Set the status to Halted. - virtual void halt() = 0; - -#if FULL_SYSTEM - virtual void dumpFuncProfile() = 0; -#endif - - virtual void takeOverFrom(ExecContext *old_context) = 0; - - virtual void regStats(const std::string &name) = 0; - - virtual void serialize(std::ostream &os) = 0; - virtual void unserialize(Checkpoint *cp, const std::string §ion) = 0; - -#if FULL_SYSTEM - virtual Event *getQuiesceEvent() = 0; - - // Not necessarily the best location for these... - // Having an extra function just to read these is obnoxious - virtual Tick readLastActivate() = 0; - virtual Tick readLastSuspend() = 0; - - virtual void profileClear() = 0; - virtual void profileSample() = 0; -#endif - - virtual int getThreadNum() = 0; - - virtual int getInstAsid() = 0; - virtual int getDataAsid() = 0; - - virtual Fault translateInstReq(RequestPtr &req) = 0; - - virtual Fault translateDataReadReq(RequestPtr &req) = 0; - - virtual Fault translateDataWriteReq(RequestPtr &req) = 0; - - // Also somewhat obnoxious. Really only used for the TLB fault. - // However, may be quite useful in SPARC. - virtual TheISA::MachInst getInst() = 0; - - virtual void copyArchRegs(ExecContext *xc) = 0; - - virtual void clearArchRegs() = 0; - - // - // New accessors for new decoder. - // - virtual uint64_t readIntReg(int reg_idx) = 0; - - virtual FloatReg readFloatReg(int reg_idx, int width) = 0; - - virtual FloatReg readFloatReg(int reg_idx) = 0; - - virtual FloatRegBits readFloatRegBits(int reg_idx, int width) = 0; - - virtual FloatRegBits readFloatRegBits(int reg_idx) = 0; - - virtual void setIntReg(int reg_idx, uint64_t val) = 0; - - virtual void setFloatReg(int reg_idx, FloatReg val, int width) = 0; - - virtual void setFloatReg(int reg_idx, FloatReg val) = 0; - - virtual void setFloatRegBits(int reg_idx, FloatRegBits val) = 0; - - virtual void setFloatRegBits(int reg_idx, FloatRegBits val, int width) = 0; - - virtual uint64_t readPC() = 0; - - virtual void setPC(uint64_t val) = 0; - - virtual uint64_t readNextPC() = 0; - - virtual void setNextPC(uint64_t val) = 0; - - virtual uint64_t readNextNPC() = 0; - - virtual void setNextNPC(uint64_t val) = 0; - - virtual MiscReg readMiscReg(int misc_reg) = 0; - - virtual MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) = 0; - - virtual Fault setMiscReg(int misc_reg, const MiscReg &val) = 0; - - virtual Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) = 0; - - // Also not necessarily the best location for these two. Hopefully will go - // away once we decide upon where st cond failures goes. - virtual unsigned readStCondFailures() = 0; - - virtual void setStCondFailures(unsigned sc_failures) = 0; - -#if FULL_SYSTEM - virtual int readIntrFlag() = 0; - virtual void setIntrFlag(int val) = 0; - virtual Fault hwrei() = 0; - virtual bool inPalMode() = 0; - virtual bool simPalCheck(int palFunc) = 0; -#endif - - // Only really makes sense for old CPU model. Still could be useful though. - virtual bool misspeculating() = 0; - -#if !FULL_SYSTEM - virtual IntReg getSyscallArg(int i) = 0; - - // used to shift args for indirect syscall - virtual void setSyscallArg(int i, IntReg val) = 0; - - virtual void setSyscallReturn(SyscallReturn return_value) = 0; - - virtual void syscall(int64_t callnum) = 0; - - // Same with st cond failures. - virtual Counter readFuncExeInst() = 0; - - virtual void setFuncExeInst(Counter new_val) = 0; -#endif - - virtual void changeRegFileContext(RegFile::ContextParam param, - RegFile::ContextVal val) = 0; -}; - -template <class XC> -class ProxyExecContext : public ExecContext -{ - public: - ProxyExecContext(XC *actual_xc) - { actualXC = actual_xc; } - - private: - XC *actualXC; - - public: +#error "Cannot include this file" - BaseCPU *getCpuPtr() { return actualXC->getCpuPtr(); } - - void setCpuId(int id) { actualXC->setCpuId(id); } - - int readCpuId() { return actualXC->readCpuId(); } +/** + * The ExecContext is not a usable class. It is simply here for + * documentation purposes. It shows the interface that is used by the + * ISA to access and change CPU state. + */ +class ExecContext { + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to reduce overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + /** Reads an integer register. */ + uint64_t readIntReg(const StaticInst *si, int idx); + + /** Reads a floating point register of a specific width. */ + FloatReg readFloatReg(const StaticInst *si, int idx, int width); + + /** Reads a floating point register of single register width. */ + FloatReg readFloatReg(const StaticInst *si, int idx); + + /** Reads a floating point register of a specific width in its + * binary format, instead of by value. */ + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width); + + /** Reads a floating point register in its binary format, instead + * of by value. */ + FloatRegBits readFloatRegBits(const StaticInst *si, int idx); + + /** Sets an integer register to a value. */ + void setIntReg(const StaticInst *si, int idx, uint64_t val); + + /** Sets a floating point register of a specific width to a value. */ + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width); + + /** Sets a floating point register of single width to a value. */ + void setFloatReg(const StaticInst *si, int idx, FloatReg val); + + /** Sets the bits of a floating point register of a specific width + * to a binary value. */ + void setFloatRegBits(const StaticInst *si, int idx, + FloatRegBits val, int width); + + /** Sets the bits of a floating point register of single width + * to a binary value. */ + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val); + + /** Reads the PC. */ + uint64_t readPC(); + /** Reads the NextPC. */ + uint64_t readNextPC(); + /** Reads the Next-NextPC. Only for architectures like SPARC or MIPS. */ + uint64_t readNextNPC(); + + /** Sets the PC. */ + void setPC(uint64_t val); + /** Sets the NextPC. */ + void setNextPC(uint64_t val); + /** Sets the Next-NextPC. Only for architectures like SPARC or MIPS. */ + void setNextNPC(uint64_t val); + + /** Reads a miscellaneous register. */ + MiscReg readMiscReg(int misc_reg); + + /** Reads a miscellaneous register, handling any architectural + * side effects due to reading that register. */ + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault); + + /** Sets a miscellaneous register. */ + Fault setMiscReg(int misc_reg, const MiscReg &val); + + /** Sets a miscellaneous register, handling any architectural + * side effects due to writing that register. */ + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + + /** Records the effective address of the instruction. Only valid + * for memory ops. */ + void setEA(Addr EA); + /** Returns the effective address of the instruction. Only valid + * for memory ops. */ + Addr getEA(); + + /** Returns a pointer to the ThreadContext. */ + ThreadContext *tcBase(); + + /** Reads an address, creating a memory request with the given + * flags. Stores result of read in data. */ + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + /** Writes to an address, creating a memory request with the given + * flags. Writes data to memory. For store conditionals, returns + * the result of the store in res. */ + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + /** Prefetches an address, creating a memory request with the + * given flags. */ + void prefetch(Addr addr, unsigned flags); + + /** Hints to the memory system that an address will be written to + * soon, with the given size. Creates a memory request with the + * given flags. */ + void writeHint(Addr addr, int size, unsigned flags); #if FULL_SYSTEM - System *getSystemPtr() { return actualXC->getSystemPtr(); } - - AlphaITB *getITBPtr() { return actualXC->getITBPtr(); } - - AlphaDTB *getDTBPtr() { return actualXC->getDTBPtr(); } - - FunctionalPort *getPhysPort() { return actualXC->getPhysPort(); } - - VirtualPort *getVirtPort(ExecContext *xc = NULL) { return actualXC->getVirtPort(xc); } - - void delVirtPort(VirtualPort *vp) { return actualXC->delVirtPort(vp); } + /** Somewhat Alpha-specific function that handles returning from + * an error or interrupt. */ + Fault hwrei(); + /** Reads the interrupt flags. */ + int readIntrFlag(); + /** Sets the interrupt flags to a value. */ + void setIntrFlag(int val); + + /** + * Check for special simulator handling of specific PAL calls. If + * return value is false, actual PAL call will be suppressed. + */ + bool simPalCheck(int palFunc); #else - TranslatingPort *getMemPort() { return actualXC->getMemPort(); } - - Process *getProcessPtr() { return actualXC->getProcessPtr(); } -#endif - - Status status() const { return actualXC->status(); } - - void setStatus(Status new_status) { actualXC->setStatus(new_status); } - - /// Set the status to Active. Optional delay indicates number of - /// cycles to wait before beginning execution. - void activate(int delay = 1) { actualXC->activate(delay); } - - /// Set the status to Suspended. - void suspend() { actualXC->suspend(); } - - /// Set the status to Unallocated. - void deallocate() { actualXC->deallocate(); } - - /// Set the status to Halted. - void halt() { actualXC->halt(); } - -#if FULL_SYSTEM - void dumpFuncProfile() { actualXC->dumpFuncProfile(); } -#endif - - void takeOverFrom(ExecContext *oldContext) - { actualXC->takeOverFrom(oldContext); } - - void regStats(const std::string &name) { actualXC->regStats(name); } - - void serialize(std::ostream &os) { actualXC->serialize(os); } - void unserialize(Checkpoint *cp, const std::string §ion) - { actualXC->unserialize(cp, section); } - -#if FULL_SYSTEM - Event *getQuiesceEvent() { return actualXC->getQuiesceEvent(); } - - Tick readLastActivate() { return actualXC->readLastActivate(); } - Tick readLastSuspend() { return actualXC->readLastSuspend(); } - - void profileClear() { return actualXC->profileClear(); } - void profileSample() { return actualXC->profileSample(); } + /** Executes a syscall specified by the callnum. */ + void syscall(int64_t callnum); #endif - - int getThreadNum() { return actualXC->getThreadNum(); } - - int getInstAsid() { return actualXC->getInstAsid(); } - int getDataAsid() { return actualXC->getDataAsid(); } - - Fault translateInstReq(RequestPtr &req) - { return actualXC->translateInstReq(req); } - - Fault translateDataReadReq(RequestPtr &req) - { return actualXC->translateDataReadReq(req); } - - Fault translateDataWriteReq(RequestPtr &req) - { return actualXC->translateDataWriteReq(req); } - - // @todo: Do I need this? - MachInst getInst() { return actualXC->getInst(); } - - // @todo: Do I need this? - void copyArchRegs(ExecContext *xc) { actualXC->copyArchRegs(xc); } - - void clearArchRegs() { actualXC->clearArchRegs(); } - - // - // New accessors for new decoder. - // - uint64_t readIntReg(int reg_idx) - { return actualXC->readIntReg(reg_idx); } - - FloatReg readFloatReg(int reg_idx, int width) - { return actualXC->readFloatReg(reg_idx, width); } - - FloatReg readFloatReg(int reg_idx) - { return actualXC->readFloatReg(reg_idx); } - - FloatRegBits readFloatRegBits(int reg_idx, int width) - { return actualXC->readFloatRegBits(reg_idx, width); } - - FloatRegBits readFloatRegBits(int reg_idx) - { return actualXC->readFloatRegBits(reg_idx); } - - void setIntReg(int reg_idx, uint64_t val) - { actualXC->setIntReg(reg_idx, val); } - - void setFloatReg(int reg_idx, FloatReg val, int width) - { actualXC->setFloatReg(reg_idx, val, width); } - - void setFloatReg(int reg_idx, FloatReg val) - { actualXC->setFloatReg(reg_idx, val); } - - void setFloatRegBits(int reg_idx, FloatRegBits val, int width) - { actualXC->setFloatRegBits(reg_idx, val, width); } - - void setFloatRegBits(int reg_idx, FloatRegBits val) - { actualXC->setFloatRegBits(reg_idx, val); } - - uint64_t readPC() { return actualXC->readPC(); } - - void setPC(uint64_t val) { actualXC->setPC(val); } - - uint64_t readNextPC() { return actualXC->readNextPC(); } - - void setNextPC(uint64_t val) { actualXC->setNextPC(val); } - - uint64_t readNextNPC() { return actualXC->readNextNPC(); } - - void setNextNPC(uint64_t val) { actualXC->setNextNPC(val); } - - MiscReg readMiscReg(int misc_reg) - { return actualXC->readMiscReg(misc_reg); } - - MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) - { return actualXC->readMiscRegWithEffect(misc_reg, fault); } - - Fault setMiscReg(int misc_reg, const MiscReg &val) - { return actualXC->setMiscReg(misc_reg, val); } - - Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) - { return actualXC->setMiscRegWithEffect(misc_reg, val); } - - unsigned readStCondFailures() - { return actualXC->readStCondFailures(); } - - void setStCondFailures(unsigned sc_failures) - { actualXC->setStCondFailures(sc_failures); } - -#if FULL_SYSTEM - int readIntrFlag() { return actualXC->readIntrFlag(); } - - void setIntrFlag(int val) { actualXC->setIntrFlag(val); } - - Fault hwrei() { return actualXC->hwrei(); } - - bool inPalMode() { return actualXC->inPalMode(); } - - bool simPalCheck(int palFunc) { return actualXC->simPalCheck(palFunc); } -#endif - - // @todo: Fix this! - bool misspeculating() { return actualXC->misspeculating(); } - -#if !FULL_SYSTEM - IntReg getSyscallArg(int i) { return actualXC->getSyscallArg(i); } - - // used to shift args for indirect syscall - void setSyscallArg(int i, IntReg val) - { actualXC->setSyscallArg(i, val); } - - void setSyscallReturn(SyscallReturn return_value) - { actualXC->setSyscallReturn(return_value); } - - void syscall(int64_t callnum) { actualXC->syscall(callnum); } - - Counter readFuncExeInst() { return actualXC->readFuncExeInst(); } - - void setFuncExeInst(Counter new_val) - { return actualXC->setFuncExeInst(new_val); } -#endif - - void changeRegFileContext(RegFile::ContextParam param, - RegFile::ContextVal val) - { - actualXC->changeRegFileContext(param, val); - } }; - -#endif diff --git a/src/cpu/exetrace.cc b/src/cpu/exetrace.cc index 0ed3b43c4..7fdad5113 100644 --- a/src/cpu/exetrace.cc +++ b/src/cpu/exetrace.cc @@ -24,6 +24,11 @@ * 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu + * Nathan Binkert + * Steve Raasch */ #include <fstream> @@ -82,7 +87,8 @@ Trace::InstRecord::dump(ostream &outs) std::string sym_str; Addr sym_addr; if (debugSymbolTable - && debugSymbolTable->findNearestSymbol(PC, sym_str, sym_addr)) { + && debugSymbolTable->findNearestSymbol(PC, sym_str, sym_addr) + && flags[PC_SYMBOL]) { if (PC != sym_addr) sym_str += csprintf("+%d", PC - sym_addr); outs << "@" << sym_str << " : "; @@ -190,6 +196,8 @@ Param<bool> exe_trace_print_fetchseq(&exeTraceParams, "print_fetchseq", "print fetch sequence number", false); Param<bool> exe_trace_print_cp_seq(&exeTraceParams, "print_cpseq", "print correct-path sequence number", false); +Param<bool> exe_trace_pc_symbol(&exeTraceParams, "pc_symbol", + "Use symbols for the PC if available", true); Param<bool> exe_trace_intel_format(&exeTraceParams, "intel_format", "print trace in intel compatible format", false); Param<string> exe_trace_system(&exeTraceParams, "trace_system", @@ -214,6 +222,7 @@ Trace::InstRecord::setParams() flags[PRINT_INT_REGS] = exe_trace_print_iregs; flags[PRINT_FETCH_SEQ] = exe_trace_print_fetchseq; flags[PRINT_CP_SEQ] = exe_trace_print_cp_seq; + flags[PC_SYMBOL] = exe_trace_pc_symbol; flags[INTEL_FORMAT] = exe_trace_intel_format; trace_system = exe_trace_system; } diff --git a/src/cpu/exetrace.hh b/src/cpu/exetrace.hh index a26cdc517..95f8b449c 100644 --- a/src/cpu/exetrace.hh +++ b/src/cpu/exetrace.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __EXETRACE_HH__ @@ -35,7 +38,7 @@ #include "sim/host.hh" #include "cpu/inst_seq.hh" // for InstSeqNum #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/static_inst.hh" class BaseCPU; @@ -144,6 +147,7 @@ class InstRecord : public Record PRINT_INT_REGS, PRINT_FETCH_SEQ, PRINT_CP_SEQ, + PC_SYMBOL, INTEL_FORMAT, NUM_BITS }; @@ -169,14 +173,14 @@ InstRecord::setRegs(const IntRegFile ®s) inline InstRecord * -getInstRecord(Tick cycle, ExecContext *xc, BaseCPU *cpu, +getInstRecord(Tick cycle, ThreadContext *tc, BaseCPU *cpu, const StaticInstPtr staticInst, Addr pc, int thread = 0) { if (DTRACE(InstExec) && - (InstRecord::traceMisspec() || !xc->misspeculating())) { + (InstRecord::traceMisspec() || !tc->misspeculating())) { return new InstRecord(cycle, cpu, staticInst, pc, - xc->misspeculating(), thread); + tc->misspeculating(), thread); } return NULL; diff --git a/src/cpu/func_unit.cc b/src/cpu/func_unit.cc new file mode 100644 index 000000000..c20578a43 --- /dev/null +++ b/src/cpu/func_unit.cc @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2002-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + */ + +#include <sstream> + +#include "base/misc.hh" +#include "cpu/func_unit.hh" +#include "sim/builder.hh" + +using namespace std; + + +//////////////////////////////////////////////////////////////////////////// +// +// The funciton unit +// +FuncUnit::FuncUnit() +{ + capabilityList.reset(); +} + + +// Copy constructor +FuncUnit::FuncUnit(const FuncUnit &fu) +{ + + for (int i = 0; i < Num_OpClasses; ++i) { + opLatencies[i] = fu.opLatencies[i]; + issueLatencies[i] = fu.issueLatencies[i]; + } + + capabilityList = fu.capabilityList; +} + + +void +FuncUnit::addCapability(OpClass cap, unsigned oplat, unsigned issuelat) +{ + if (issuelat == 0 || oplat == 0) + panic("FuncUnit: you don't really want a zero-cycle latency do you?"); + + capabilityList.set(cap); + + opLatencies[cap] = oplat; + issueLatencies[cap] = issuelat; +} + +bool +FuncUnit::provides(OpClass capability) +{ + return capabilityList[capability]; +} + +bitset<Num_OpClasses> +FuncUnit::capabilities() +{ + return capabilityList; +} + +unsigned & +FuncUnit::opLatency(OpClass cap) +{ + return opLatencies[cap]; +} + +unsigned +FuncUnit::issueLatency(OpClass capability) +{ + return issueLatencies[capability]; +} + +//////////////////////////////////////////////////////////////////////////// +// +// The SimObjects we use to get the FU information into the simulator +// +//////////////////////////////////////////////////////////////////////////// + +// +// We use 2 objects to specify this data in the INI file: +// (1) OpDesc - Describes the operation class & latencies +// (multiple OpDesc objects can refer to the same +// operation classes) +// (2) FUDesc - Describes the operations available in the unit & +// the number of these units +// +// + + +// +// The operation-class description object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(OpDesc) + + SimpleEnumParam<OpClass> opClass; + Param<unsigned> opLat; + Param<unsigned> issueLat; + +END_DECLARE_SIM_OBJECT_PARAMS(OpDesc) + +BEGIN_INIT_SIM_OBJECT_PARAMS(OpDesc) + + INIT_ENUM_PARAM(opClass, "type of operation", opClassStrings), + INIT_PARAM(opLat, "cycles until result is available"), + INIT_PARAM(issueLat, "cycles until another can be issued") + +END_INIT_SIM_OBJECT_PARAMS(OpDesc) + + +CREATE_SIM_OBJECT(OpDesc) +{ + return new OpDesc(getInstanceName(), opClass, opLat, issueLat); +} + +REGISTER_SIM_OBJECT("OpDesc", OpDesc) + + +// +// The FuDesc object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(FUDesc) + + SimObjectVectorParam<OpDesc *> opList; + Param<unsigned> count; + +END_DECLARE_SIM_OBJECT_PARAMS(FUDesc) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(FUDesc) + + INIT_PARAM(opList, "list of operation classes for this FU type"), + INIT_PARAM(count, "number of these FU's available") + +END_INIT_SIM_OBJECT_PARAMS(FUDesc) + + +CREATE_SIM_OBJECT(FUDesc) +{ + return new FUDesc(getInstanceName(), opList, count); +} + +REGISTER_SIM_OBJECT("FUDesc", FUDesc) + diff --git a/src/cpu/func_unit.hh b/src/cpu/func_unit.hh new file mode 100644 index 000000000..780143096 --- /dev/null +++ b/src/cpu/func_unit.hh @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2002-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Raasch + */ + +#ifndef __CPU_FUNC_UNIT_HH__ +#define __CPU_FUNC_UNIT_HH__ + +#include <bitset> +#include <string> +#include <vector> + +#include "cpu/op_class.hh" +#include "sim/sim_object.hh" + +//////////////////////////////////////////////////////////////////////////// +// +// Structures used ONLY during the initialization phase... +// +// +// + +struct OpDesc : public SimObject +{ + OpClass opClass; + unsigned opLat; + unsigned issueLat; + + OpDesc(std::string name, OpClass c, unsigned o, unsigned i) + : SimObject(name), opClass(c), opLat(o), issueLat(i) {}; +}; + +struct FUDesc : public SimObject +{ + std::vector<OpDesc *> opDescList; + unsigned number; + + FUDesc(std::string name, std::vector<OpDesc *> l, unsigned n) + : SimObject(name), opDescList(l), number(n) {}; +}; + +typedef std::vector<OpDesc *>::iterator OPDDiterator; +typedef std::vector<FUDesc *>::iterator FUDDiterator; + + + + +//////////////////////////////////////////////////////////////////////////// +// +// The actual FU object +// +// +// +class FuncUnit +{ + private: + unsigned opLatencies[Num_OpClasses]; + unsigned issueLatencies[Num_OpClasses]; + std::bitset<Num_OpClasses> capabilityList; + + public: + FuncUnit(); + FuncUnit(const FuncUnit &fu); + + std::string name; + + void addCapability(OpClass cap, unsigned oplat, unsigned issuelat); + + bool provides(OpClass capability); + std::bitset<Num_OpClasses> capabilities(); + + unsigned &opLatency(OpClass capability); + unsigned issueLatency(OpClass capability); +}; + +#endif // __FU_POOL_HH__ diff --git a/src/cpu/inst_seq.hh b/src/cpu/inst_seq.hh index 8de047af7..e7acd215f 100644 --- a/src/cpu/inst_seq.hh +++ b/src/cpu/inst_seq.hh @@ -24,11 +24,16 @@ * 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. + * + * Authors: Steve Raasch + * Nathan Binkert */ #ifndef __STD_TYPES_HH__ #define __STD_TYPES_HH__ +#include <stdint.h> + // inst sequence type, used to order instructions in the ready list, // if this rolls over the ready list order temporarily will get messed // up, but execution will continue and complete correctly diff --git a/src/cpu/intr_control.cc b/src/cpu/intr_control.cc index 43e7f654c..4cbc86891 100644 --- a/src/cpu/intr_control.cc +++ b/src/cpu/intr_control.cc @@ -24,13 +24,16 @@ * 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. + * + * Authors: Nathan Binkert + * Ron Dreslinski */ #include <string> #include <vector> #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/intr_control.hh" #include "sim/builder.hh" #include "sim/sim_object.hh" @@ -48,32 +51,32 @@ IntrControl::IntrControl(const string &name, BaseCPU *c) void IntrControl::post(int int_num, int index) { - std::vector<ExecContext *> &xcvec = cpu->system->execContexts; - BaseCPU *temp = xcvec[0]->getCpuPtr(); + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[0]->getCpuPtr(); temp->post_interrupt(int_num, index); } void IntrControl::post(int cpu_id, int int_num, int index) { - std::vector<ExecContext *> &xcvec = cpu->system->execContexts; - BaseCPU *temp = xcvec[cpu_id]->getCpuPtr(); + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[cpu_id]->getCpuPtr(); temp->post_interrupt(int_num, index); } void IntrControl::clear(int int_num, int index) { - std::vector<ExecContext *> &xcvec = cpu->system->execContexts; - BaseCPU *temp = xcvec[0]->getCpuPtr(); + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[0]->getCpuPtr(); temp->clear_interrupt(int_num, index); } void IntrControl::clear(int cpu_id, int int_num, int index) { - std::vector<ExecContext *> &xcvec = cpu->system->execContexts; - BaseCPU *temp = xcvec[cpu_id]->getCpuPtr(); + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[cpu_id]->getCpuPtr(); temp->clear_interrupt(int_num, index); } diff --git a/src/cpu/intr_control.hh b/src/cpu/intr_control.hh index 5ec4e14cb..2e3f9e038 100644 --- a/src/cpu/intr_control.hh +++ b/src/cpu/intr_control.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ron Dreslinski */ #ifndef __INTR_CONTROL_HH__ diff --git a/src/cpu/memtest/memtest.cc b/src/cpu/memtest/memtest.cc index 54def1012..7ea9eaefc 100644 --- a/src/cpu/memtest/memtest.cc +++ b/src/cpu/memtest/memtest.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Erik Hallnor + * Steve Reinhardt */ // FIX ME: make trackBlkAddr use blocksize from actual cache, not hard coded @@ -35,7 +38,7 @@ #include "base/misc.hh" #include "base/statistics.hh" -#include "cpu/cpu_exec_context.hh" +#include "cpu/simple_thread.hh" #include "cpu/memtest/memtest.hh" #include "mem/cache/base_cache.hh" #include "sim/builder.hh" @@ -78,7 +81,7 @@ MemTest::MemTest(const string &name, vector<string> cmd; cmd.push_back("/bin/ls"); vector<string> null_vec; - cpuXC = new CPUExecContext(NULL, 0, mainMem, 0); + thread = new SimpleThread(NULL, 0, mainMem, 0); blockSize = cacheInterface->getBlockSize(); blockAddrMask = blockSize - 1; @@ -268,7 +271,7 @@ MemTest::tick() req->data = new uint8_t[req->size]; req->paddr &= ~(req->size - 1); req->time = curTick; - req->xc = cpuXC->getProxy(); + req->xc = thread->getProxy(); if (cmd < percentReads) { // read diff --git a/src/cpu/memtest/memtest.hh b/src/cpu/memtest/memtest.hh index cdb40a26a..42fb235db 100644 --- a/src/cpu/memtest/memtest.hh +++ b/src/cpu/memtest/memtest.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Erik Hallnor + * Steve Reinhardt */ #ifndef __CPU_MEMTEST_MEMTEST_HH__ @@ -39,7 +42,7 @@ #include "sim/sim_object.hh" #include "sim/stats.hh" -class ExecContext; +class ThreadContext; class MemTest : public SimObject { public: @@ -83,7 +86,7 @@ class MemTest : public SimObject MemInterface *cacheInterface; FunctionalMemory *mainMem; FunctionalMemory *checkMem; - CPUExecContext *cpuXC; + SimpleThread *thread; unsigned size; // size of testing memory region diff --git a/src/cpu/o3/2bit_local_pred.cc b/src/cpu/o3/2bit_local_pred.cc index d9744eec7..77a45ea26 100644 --- a/src/cpu/o3/2bit_local_pred.cc +++ b/src/cpu/o3/2bit_local_pred.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,29 +24,41 @@ * 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. + * + * Authors: Kevin Lim */ +#include "base/intmath.hh" +#include "base/misc.hh" #include "base/trace.hh" #include "cpu/o3/2bit_local_pred.hh" -DefaultBP::DefaultBP(unsigned _localPredictorSize, - unsigned _localCtrBits, - unsigned _instShiftAmt) +LocalBP::LocalBP(unsigned _localPredictorSize, + unsigned _localCtrBits, + unsigned _instShiftAmt) : localPredictorSize(_localPredictorSize), localCtrBits(_localCtrBits), instShiftAmt(_instShiftAmt) { - // Should do checks here to make sure sizes are correct (powers of 2). + if (!isPowerOf2(localPredictorSize)) { + fatal("Invalid local predictor size!\n"); + } + + localPredictorSets = localPredictorSize / localCtrBits; + + if (!isPowerOf2(localPredictorSets)) { + fatal("Invalid number of local predictor sets! Check localCtrBits.\n"); + } // Setup the index mask. - indexMask = localPredictorSize - 1; + indexMask = localPredictorSets - 1; DPRINTF(Fetch, "Branch predictor: index mask: %#x\n", indexMask); // Setup the array of counters for the local predictor. - localCtrs = new SatCounter[localPredictorSize]; + localCtrs.resize(localPredictorSets); - for (int i = 0; i < localPredictorSize; ++i) + for (int i = 0; i < localPredictorSets; ++i) localCtrs[i].setBits(_localCtrBits); DPRINTF(Fetch, "Branch predictor: local predictor size: %i\n", @@ -58,24 +70,30 @@ DefaultBP::DefaultBP(unsigned _localPredictorSize, instShiftAmt); } +void +LocalBP::reset() +{ + for (int i = 0; i < localPredictorSets; ++i) { + localCtrs[i].reset(); + } +} + bool -DefaultBP::lookup(Addr &branch_addr) +LocalBP::lookup(Addr &branch_addr, void * &bp_history) { bool taken; - uint8_t local_prediction; + uint8_t counter_val; unsigned local_predictor_idx = getLocalIndex(branch_addr); DPRINTF(Fetch, "Branch predictor: Looking up index %#x\n", local_predictor_idx); - assert(local_predictor_idx < localPredictorSize); - - local_prediction = localCtrs[local_predictor_idx].read(); + counter_val = localCtrs[local_predictor_idx].read(); DPRINTF(Fetch, "Branch predictor: prediction is %i.\n", - (int)local_prediction); + (int)counter_val); - taken = getPrediction(local_prediction); + taken = getPrediction(counter_val); #if 0 // Speculative update. @@ -92,8 +110,9 @@ DefaultBP::lookup(Addr &branch_addr) } void -DefaultBP::update(Addr &branch_addr, bool taken) +LocalBP::update(Addr &branch_addr, bool taken, void *bp_history) { + assert(bp_history == NULL); unsigned local_predictor_idx; // Update the local predictor. @@ -102,8 +121,6 @@ DefaultBP::update(Addr &branch_addr, bool taken) DPRINTF(Fetch, "Branch predictor: Looking up index %#x\n", local_predictor_idx); - assert(local_predictor_idx < localPredictorSize); - if (taken) { DPRINTF(Fetch, "Branch predictor: Branch updated as taken.\n"); localCtrs[local_predictor_idx].increment(); @@ -115,7 +132,7 @@ DefaultBP::update(Addr &branch_addr, bool taken) inline bool -DefaultBP::getPrediction(uint8_t &count) +LocalBP::getPrediction(uint8_t &count) { // Get the MSB of the count return (count >> (localCtrBits - 1)); @@ -123,7 +140,7 @@ DefaultBP::getPrediction(uint8_t &count) inline unsigned -DefaultBP::getLocalIndex(Addr &branch_addr) +LocalBP::getLocalIndex(Addr &branch_addr) { return (branch_addr >> instShiftAmt) & indexMask; } diff --git a/src/cpu/o3/2bit_local_pred.hh b/src/cpu/o3/2bit_local_pred.hh index 97433e542..0a2a71d3e 100644 --- a/src/cpu/o3/2bit_local_pred.hh +++ b/src/cpu/o3/2bit_local_pred.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,43 +24,65 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__ -#define __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__ +#ifndef __CPU_O3_2BIT_LOCAL_PRED_HH__ +#define __CPU_O3_2BIT_LOCAL_PRED_HH__ // For Addr type. #include "arch/isa_traits.hh" #include "cpu/o3/sat_counter.hh" -class DefaultBP +#include <vector> + +/** + * Implements a local predictor that uses the PC to index into a table of + * counters. Note that any time a pointer to the bp_history is given, it + * should be NULL using this predictor because it does not have any branch + * predictor state that needs to be recorded or updated; the update can be + * determined solely by the branch being taken or not taken. + */ +class LocalBP { public: /** * Default branch predictor constructor. + * @param localPredictorSize Size of the local predictor. + * @param localCtrBits Number of bits per counter. + * @param instShiftAmt Offset amount for instructions to ignore alignment. */ - DefaultBP(unsigned localPredictorSize, unsigned localCtrBits, - unsigned instShiftAmt); + LocalBP(unsigned localPredictorSize, unsigned localCtrBits, + unsigned instShiftAmt); /** * Looks up the given address in the branch predictor and returns * a true/false value as to whether it is taken. * @param branch_addr The address of the branch to look up. + * @param bp_history Pointer to any bp history state. * @return Whether or not the branch is taken. */ - bool lookup(Addr &branch_addr); + bool lookup(Addr &branch_addr, void * &bp_history); /** * Updates the branch predictor with the actual result of a branch. * @param branch_addr The address of the branch to update. * @param taken Whether or not the branch was taken. */ - void update(Addr &branch_addr, bool taken); + void update(Addr &branch_addr, bool taken, void *bp_history); - private: + void squash(void *bp_history) + { assert(bp_history == NULL); } - /** Returns the taken/not taken prediction given the value of the + void reset(); + + private: + /** + * Returns the taken/not taken prediction given the value of the * counter. + * @param count The value of the counter. + * @return The prediction based on the counter value. */ inline bool getPrediction(uint8_t &count); @@ -68,11 +90,14 @@ class DefaultBP inline unsigned getLocalIndex(Addr &PC); /** Array of counters that make up the local predictor. */ - SatCounter *localCtrs; + std::vector<SatCounter> localCtrs; /** Size of the local predictor. */ unsigned localPredictorSize; + /** Number of sets. */ + unsigned localPredictorSets; + /** Number of bits of the local predictor's counters. */ unsigned localCtrBits; @@ -83,4 +108,4 @@ class DefaultBP unsigned indexMask; }; -#endif // __CPU_O3_CPU_2BIT_LOCAL_PRED_HH__ +#endif // __CPU_O3_2BIT_LOCAL_PRED_HH__ diff --git a/src/cpu/o3/SConscript b/src/cpu/o3/SConscript new file mode 100755 index 000000000..e65d41411 --- /dev/null +++ b/src/cpu/o3/SConscript @@ -0,0 +1,79 @@ +# -*- mode:python -*- + +# Copyright (c) 2006 The Regents of The University of Michigan +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Korey Sewell + +import os +import os.path +import sys + +# Import build environment variable from SConstruct. +Import('env') + + +################################################################# +# +# Include ISA-specific files for the O3 CPU-model +# +################################################################# + +sources = [] + +if env['TARGET_ISA'] == 'alpha': + sources += Split(''' + alpha/dyn_inst.cc + alpha/cpu.cc + alpha/thread_context.cc + alpha/cpu_builder.cc + ''') +elif env['TARGET_ISA'] == 'mips': + sys.exit('O3 CPU does not support MIPS') + #sources += Split(''' + # mips/dyn_inst.cc + # mips/cpu.cc + # mips/thread_context.cc + # mips/cpu_builder.cc + # ''') +elif env['TARGET_ISA'] == 'sparc': + sys.exit('O3 CPU does not support MIPS') + #sources += Split(''' + # sparc/dyn_inst.cc + # sparc/cpu.cc + # sparc/thread_context.cc + # sparc/cpu_builder.cc + # ''') +else: + sys.exit('O3 CPU does not support the \'%s\' ISA' % env['TARGET_ISA']) + + +# Convert file names to SCons File objects. This takes care of the +# path relative to the top of the directory tree. +sources = [File(s) for s in sources] + +Return('sources') + diff --git a/src/cpu/o3/alpha_cpu.cc b/src/cpu/o3/alpha/cpu.cc index 7bc90dae6..ed10b2fd1 100644 --- a/src/cpu/o3/alpha_cpu.cc +++ b/src/cpu/o3/alpha/cpu.cc @@ -24,13 +24,15 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_impl.hh" -#include "cpu/o3/alpha_cpu_impl.hh" -#include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/o3/alpha/impl.hh" +#include "cpu/o3/alpha/cpu_impl.hh" +#include "cpu/o3/alpha/dyn_inst.hh" -// Force instantiation of AlphaFullCPU for all the implemntations that are +// Force instantiation of AlphaO3CPU for all the implemntations that are // needed. Consider merging this and alpha_dyn_inst.cc, and maybe all // classes that depend on a certain impl, into one file (alpha_impl.cc?). -template class AlphaFullCPU<AlphaSimpleImpl>; +template class AlphaO3CPU<AlphaSimpleImpl>; diff --git a/src/cpu/o3/alpha/cpu.hh b/src/cpu/o3/alpha/cpu.hh new file mode 100644 index 000000000..b961341d5 --- /dev/null +++ b/src/cpu/o3/alpha/cpu.hh @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_ALPHA_CPU_HH__ +#define __CPU_O3_ALPHA_CPU_HH__ + +#include "arch/isa_traits.hh" +#include "cpu/thread_context.hh" +#include "cpu/o3/cpu.hh" +#include "sim/byteswap.hh" + +class EndQuiesceEvent; +namespace Kernel { + class Statistics; +}; + +class TranslatingPort; + +/** + * AlphaO3CPU class. Derives from the FullO3CPU class, and + * implements all ISA and implementation specific functions of the + * CPU. This is the CPU class that is used for the SimObjects, and is + * what is given to the DynInsts. Most of its state exists in the + * FullO3CPU; the state is has is mainly for ISA specific + * functionality. + */ +template <class Impl> +class AlphaO3CPU : public FullO3CPU<Impl> +{ + protected: + typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + typedef TheISA::RegFile RegFile; + typedef TheISA::MiscRegFile MiscRegFile; + + public: + typedef O3ThreadState<Impl> ImplState; + typedef O3ThreadState<Impl> Thread; + typedef typename Impl::Params Params; + + /** Constructs an AlphaO3CPU with the given parameters. */ + AlphaO3CPU(Params *params); + +#if FULL_SYSTEM + /** ITB pointer. */ + AlphaITB *itb; + /** DTB pointer. */ + AlphaDTB *dtb; +#endif + + /** Registers statistics. */ + void regStats(); + +#if FULL_SYSTEM + /** Translates instruction requestion. */ + Fault translateInstReq(RequestPtr &req, Thread *thread) + { + return itb->translate(req, thread->getTC()); + } + + /** Translates data read request. */ + Fault translateDataReadReq(RequestPtr &req, Thread *thread) + { + return dtb->translate(req, thread->getTC(), false); + } + + /** Translates data write request. */ + Fault translateDataWriteReq(RequestPtr &req, Thread *thread) + { + return dtb->translate(req, thread->getTC(), true); + } + +#else + /** Translates instruction requestion in syscall emulation mode. */ + Fault translateInstReq(RequestPtr &req, Thread *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + + /** Translates data read request in syscall emulation mode. */ + Fault translateDataReadReq(RequestPtr &req, Thread *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + + /** Translates data write request in syscall emulation mode. */ + Fault translateDataWriteReq(RequestPtr &req, Thread *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + +#endif + /** Reads a miscellaneous register. */ + MiscReg readMiscReg(int misc_reg, unsigned tid); + + /** Reads a misc. register, including any side effects the read + * might have as defined by the architecture. + */ + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault, unsigned tid); + + /** Sets a miscellaneous register. */ + Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned tid); + + /** Sets a misc. register, including any side effects the write + * might have as defined by the architecture. + */ + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val, unsigned tid); + + /** Initiates a squash of all in-flight instructions for a given + * thread. The source of the squash is an external update of + * state through the TC. + */ + void squashFromTC(unsigned tid); + +#if FULL_SYSTEM + /** Posts an interrupt. */ + void post_interrupt(int int_num, int index); + /** Reads the interrupt flag. */ + int readIntrFlag(); + /** Sets the interrupt flags. */ + void setIntrFlag(int val); + /** HW return from error interrupt. */ + Fault hwrei(unsigned tid); + /** Returns if a specific PC is a PAL mode PC. */ + bool inPalMode(uint64_t PC) + { return AlphaISA::PcPAL(PC); } + + bool simPalCheck(int palFunc, unsigned tid); + + /** Processes any interrupts. */ + void processInterrupts(); + + /** Halts the CPU. */ + void halt() { panic("Halt not implemented!\n"); } +#endif + + /** Traps to handle given fault. */ + void trap(Fault fault, unsigned tid); + +#if !FULL_SYSTEM + /** Executes a syscall. + * @todo: Determine if this needs to be virtual. + */ + void syscall(int64_t callnum, int tid); + /** Gets a syscall argument. */ + IntReg getSyscallArg(int i, int tid); + + /** Used to shift args for indirect syscall. */ + void setSyscallArg(int i, IntReg val, int tid); + + /** Sets the return value of a syscall. */ + void setSyscallReturn(SyscallReturn return_value, int tid); +#endif + + /** CPU read function, forwards read to LSQ. */ + template <class T> + Fault read(RequestPtr &req, T &data, int load_idx) + { + return this->iew.ldstQueue.read(req, data, load_idx); + } + + /** CPU write function, forwards write to LSQ. */ + template <class T> + Fault write(RequestPtr &req, T &data, int store_idx) + { + return this->iew.ldstQueue.write(req, data, store_idx); + } + + Addr lockAddr; + + /** Temporary fix for the lock flag, works in the UP case. */ + bool lockFlag; +}; + +#endif // __CPU_O3_ALPHA_CPU_HH__ diff --git a/src/cpu/o3/alpha_cpu_builder.cc b/src/cpu/o3/alpha/cpu_builder.cc index 6025b8ef2..490305cbf 100644 --- a/src/cpu/o3/alpha_cpu_builder.cc +++ b/src/cpu/o3/alpha/cpu_builder.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,49 +24,32 @@ * 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. + * + * Authors: Kevin Lim */ -#include "base/inifile.hh" -#include "base/loader/symtab.hh" -#include "base/misc.hh" +#include <string> + #include "cpu/base.hh" -#include "cpu/exetrace.hh" -#include "cpu/o3/alpha_cpu.hh" -#include "cpu/o3/alpha_impl.hh" -#include "mem/base_mem.hh" -#include "mem/cache/base_cache.hh" -#include "mem/mem_interface.hh" +#include "cpu/o3/alpha/cpu.hh" +#include "cpu/o3/alpha/impl.hh" +#include "cpu/o3/alpha/params.hh" +#include "cpu/o3/fu_pool.hh" #include "sim/builder.hh" -#include "sim/debug.hh" -#include "sim/host.hh" -#include "sim/process.hh" -#include "sim/sim_events.hh" -#include "sim/sim_object.hh" -#include "sim/stats.hh" -#if FULL_SYSTEM -#include "base/remote_gdb.hh" -#include "mem/functional/memory_control.hh" -#include "mem/functional/physical.hh" -#include "sim/system.hh" -#include "arch/tlb.hh" -#include "arch/vtophys.hh" -#else // !FULL_SYSTEM -#include "mem/functional/functional.hh" -#endif // FULL_SYSTEM - -class DerivAlphaFullCPU : public AlphaFullCPU<AlphaSimpleImpl> +class DerivO3CPU : public AlphaO3CPU<AlphaSimpleImpl> { public: - DerivAlphaFullCPU(AlphaSimpleParams p) - : AlphaFullCPU<AlphaSimpleImpl>(p) + DerivO3CPU(AlphaSimpleParams *p) + : AlphaO3CPU<AlphaSimpleImpl>(p) { } }; -BEGIN_DECLARE_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) +BEGIN_DECLARE_SIM_OBJECT_PARAMS(DerivO3CPU) Param<int> clock; Param<int> numThreads; +Param<int> activity; #if FULL_SYSTEM SimObjectParam<System *> system; @@ -76,15 +59,17 @@ SimObjectParam<AlphaDTB *> dtb; #else SimObjectVectorParam<Process *> workload; #endif // FULL_SYSTEM -SimObjectParam<FunctionalMemory *> mem; + +SimObjectParam<MemObject *> mem; + +SimObjectParam<BaseCPU *> checker; Param<Counter> max_insts_any_thread; Param<Counter> max_insts_all_threads; Param<Counter> max_loads_any_thread; Param<Counter> max_loads_all_threads; -SimObjectParam<BaseCache *> icache; -SimObjectParam<BaseCache *> dcache; +Param<unsigned> cachePorts; Param<unsigned> decodeToFetchDelay; Param<unsigned> renameToFetchDelay; @@ -107,30 +92,25 @@ Param<unsigned> commitToIEWDelay; Param<unsigned> renameToIEWDelay; Param<unsigned> issueToExecuteDelay; Param<unsigned> issueWidth; -Param<unsigned> executeWidth; -Param<unsigned> executeIntWidth; -Param<unsigned> executeFloatWidth; -Param<unsigned> executeBranchWidth; -Param<unsigned> executeMemoryWidth; +SimObjectParam<FUPool *> fuPool; Param<unsigned> iewToCommitDelay; Param<unsigned> renameToROBDelay; Param<unsigned> commitWidth; Param<unsigned> squashWidth; +Param<Tick> trapLatency; +Param<Tick> fetchTrapLatency; -#if 0 +Param<std::string> predType; Param<unsigned> localPredictorSize; -Param<unsigned> localPredictorCtrBits; -#endif -Param<unsigned> local_predictor_size; -Param<unsigned> local_ctr_bits; -Param<unsigned> local_history_table_size; -Param<unsigned> local_history_bits; -Param<unsigned> global_predictor_size; -Param<unsigned> global_ctr_bits; -Param<unsigned> global_history_bits; -Param<unsigned> choice_predictor_size; -Param<unsigned> choice_ctr_bits; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; Param<unsigned> BTBEntries; Param<unsigned> BTBTagSize; @@ -147,6 +127,16 @@ Param<unsigned> numPhysFloatRegs; Param<unsigned> numIQEntries; Param<unsigned> numROBEntries; +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + Param<unsigned> instShiftAmt; Param<bool> defer_registration; @@ -154,12 +144,13 @@ Param<bool> defer_registration; Param<bool> function_trace; Param<Tick> function_trace_start; -END_DECLARE_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) +END_DECLARE_SIM_OBJECT_PARAMS(DerivO3CPU) -BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) +BEGIN_INIT_SIM_OBJECT_PARAMS(DerivO3CPU) INIT_PARAM(clock, "clock speed"), INIT_PARAM(numThreads, "number of HW thread contexts"), + INIT_PARAM_DFLT(activity, "Initial activity count", 0), #if FULL_SYSTEM INIT_PARAM(system, "System object"), @@ -170,7 +161,9 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM(workload, "Processes to run"), #endif // FULL_SYSTEM - INIT_PARAM_DFLT(mem, "Memory", NULL), + INIT_PARAM(mem, "Memory"), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), INIT_PARAM_DFLT(max_insts_any_thread, "Terminate when any thread reaches this inst count", @@ -187,8 +180,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) "count", 0), - INIT_PARAM_DFLT(icache, "L1 instruction cache", NULL), - INIT_PARAM_DFLT(dcache, "L1 data cache", NULL), + INIT_PARAM_DFLT(cachePorts, "Cache Ports", 200), INIT_PARAM(decodeToFetchDelay, "Decode to fetch delay"), INIT_PARAM(renameToFetchDelay, "Rename to fetch delay"), @@ -196,7 +188,6 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) "delay"), INIT_PARAM(commitToFetchDelay, "Commit to fetch delay"), INIT_PARAM(fetchWidth, "Fetch width"), - INIT_PARAM(renameToDecodeDelay, "Rename to decode delay"), INIT_PARAM(iewToDecodeDelay, "Issue/Execute/Writeback to decode" "delay"), @@ -217,32 +208,26 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM(issueToExecuteDelay, "Issue to execute delay (internal" "to the IEW stage)"), INIT_PARAM(issueWidth, "Issue width"), - INIT_PARAM(executeWidth, "Execute width"), - INIT_PARAM(executeIntWidth, "Integer execute width"), - INIT_PARAM(executeFloatWidth, "Floating point execute width"), - INIT_PARAM(executeBranchWidth, "Branch execute width"), - INIT_PARAM(executeMemoryWidth, "Memory execute width"), + INIT_PARAM_DFLT(fuPool, "Functional unit pool", NULL), INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " "delay"), INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), INIT_PARAM(commitWidth, "Commit width"), INIT_PARAM(squashWidth, "Squash width"), - -#if 0 - INIT_PARAM(localPredictorSize, "Size of the local predictor in entries. " - "Must be a power of 2."), - INIT_PARAM(localPredictorCtrBits, "Number of bits per counter for bpred"), -#endif - INIT_PARAM(local_predictor_size, "Size of local predictor"), - INIT_PARAM(local_ctr_bits, "Bits per counter"), - INIT_PARAM(local_history_table_size, "Size of local history table"), - INIT_PARAM(local_history_bits, "Bits for the local history"), - INIT_PARAM(global_predictor_size, "Size of global predictor"), - INIT_PARAM(global_ctr_bits, "Bits per counter"), - INIT_PARAM(global_history_bits, "Bits of history"), - INIT_PARAM(choice_predictor_size, "Size of choice predictor"), - INIT_PARAM(choice_ctr_bits, "Bits of choice counters"), + INIT_PARAM_DFLT(trapLatency, "Number of cycles before the trap is handled", 6), + INIT_PARAM_DFLT(fetchTrapLatency, "Number of cycles before the fetch trap is handled", 12), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + INIT_PARAM(localPredictorSize, "Size of local predictor"), + INIT_PARAM(localCtrBits, "Bits per counter"), + INIT_PARAM(localHistoryTableSize, "Size of local history table"), + INIT_PARAM(localHistoryBits, "Bits for the local history"), + INIT_PARAM(globalPredictorSize, "Size of global predictor"), + INIT_PARAM(globalCtrBits, "Bits per counter"), + INIT_PARAM(globalHistoryBits, "Bits of history"), + INIT_PARAM(choicePredictorSize, "Size of choice predictor"), + INIT_PARAM(choiceCtrBits, "Bits of choice counters"), INIT_PARAM(BTBEntries, "Number of BTB entries"), INIT_PARAM(BTBTagSize, "Size of the BTB tags, in bits"), @@ -260,17 +245,27 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) INIT_PARAM(numIQEntries, "Number of instruction queue entries"), INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + INIT_PARAM_DFLT(smtNumFetchingThreads, "SMT Number of Fetching Threads", 1), + INIT_PARAM_DFLT(smtFetchPolicy, "SMT Fetch Policy", "SingleThread"), + INIT_PARAM_DFLT(smtLSQPolicy, "SMT LSQ Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtLSQThreshold,"SMT LSQ Threshold", 100), + INIT_PARAM_DFLT(smtIQPolicy, "SMT IQ Policy", "Partitioned"), + INIT_PARAM_DFLT(smtIQThreshold, "SMT IQ Threshold", 100), + INIT_PARAM_DFLT(smtROBPolicy, "SMT ROB Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtROBThreshold,"SMT ROB Threshold", 100), + INIT_PARAM_DFLT(smtCommitPolicy,"SMT Commit Fetch Policy", "RoundRobin"), + INIT_PARAM(instShiftAmt, "Number of bits to shift instructions by"), INIT_PARAM(defer_registration, "defer system registration (for sampling)"), INIT_PARAM(function_trace, "Enable function trace"), INIT_PARAM(function_trace_start, "Cycle to start function trace") -END_INIT_SIM_OBJECT_PARAMS(DerivAlphaFullCPU) +END_INIT_SIM_OBJECT_PARAMS(DerivO3CPU) -CREATE_SIM_OBJECT(DerivAlphaFullCPU) +CREATE_SIM_OBJECT(DerivO3CPU) { - DerivAlphaFullCPU *cpu; + DerivO3CPU *cpu; #if FULL_SYSTEM // Full-system only supports a single thread for the moment. @@ -279,114 +274,131 @@ CREATE_SIM_OBJECT(DerivAlphaFullCPU) // In non-full-system mode, we infer the number of threads from // the workload if it's not explicitly specified. int actual_num_threads = - numThreads.isValid() ? numThreads : workload.size(); + (numThreads.isValid() && numThreads >= workload.size()) ? + numThreads : workload.size(); if (workload.size() == 0) { fatal("Must specify at least one workload!"); } - #endif - AlphaSimpleParams params; + AlphaSimpleParams *params = new AlphaSimpleParams; - params.clock = clock; + params->clock = clock; - params.name = getInstanceName(); - params.numberOfThreads = actual_num_threads; + params->name = getInstanceName(); + params->numberOfThreads = actual_num_threads; + params->activity = activity; #if FULL_SYSTEM - params.system = system; - params.cpu_id = cpu_id; - params.itb = itb; - params.dtb = dtb; + params->system = system; + params->cpu_id = cpu_id; + params->itb = itb; + params->dtb = dtb; #else - params.workload = workload; + params->workload = workload; #endif // FULL_SYSTEM - params.mem = mem; + params->mem = mem; + + params->checker = checker; - params.max_insts_any_thread = max_insts_any_thread; - params.max_insts_all_threads = max_insts_all_threads; - params.max_loads_any_thread = max_loads_any_thread; - params.max_loads_all_threads = max_loads_all_threads; + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; // // Caches // - params.icacheInterface = icache ? icache->getInterface() : NULL; - params.dcacheInterface = dcache ? dcache->getInterface() : NULL; - - params.decodeToFetchDelay = decodeToFetchDelay; - params.renameToFetchDelay = renameToFetchDelay; - params.iewToFetchDelay = iewToFetchDelay; - params.commitToFetchDelay = commitToFetchDelay; - params.fetchWidth = fetchWidth; - - params.renameToDecodeDelay = renameToDecodeDelay; - params.iewToDecodeDelay = iewToDecodeDelay; - params.commitToDecodeDelay = commitToDecodeDelay; - params.fetchToDecodeDelay = fetchToDecodeDelay; - params.decodeWidth = decodeWidth; - - params.iewToRenameDelay = iewToRenameDelay; - params.commitToRenameDelay = commitToRenameDelay; - params.decodeToRenameDelay = decodeToRenameDelay; - params.renameWidth = renameWidth; - - params.commitToIEWDelay = commitToIEWDelay; - params.renameToIEWDelay = renameToIEWDelay; - params.issueToExecuteDelay = issueToExecuteDelay; - params.issueWidth = issueWidth; - params.executeWidth = executeWidth; - params.executeIntWidth = executeIntWidth; - params.executeFloatWidth = executeFloatWidth; - params.executeBranchWidth = executeBranchWidth; - params.executeMemoryWidth = executeMemoryWidth; - - params.iewToCommitDelay = iewToCommitDelay; - params.renameToROBDelay = renameToROBDelay; - params.commitWidth = commitWidth; - params.squashWidth = squashWidth; -#if 0 - params.localPredictorSize = localPredictorSize; - params.localPredictorCtrBits = localPredictorCtrBits; -#endif - params.local_predictor_size = local_predictor_size; - params.local_ctr_bits = local_ctr_bits; - params.local_history_table_size = local_history_table_size; - params.local_history_bits = local_history_bits; - params.global_predictor_size = global_predictor_size; - params.global_ctr_bits = global_ctr_bits; - params.global_history_bits = global_history_bits; - params.choice_predictor_size = choice_predictor_size; - params.choice_ctr_bits = choice_ctr_bits; - - params.BTBEntries = BTBEntries; - params.BTBTagSize = BTBTagSize; - - params.RASSize = RASSize; - - params.LQEntries = LQEntries; - params.SQEntries = SQEntries; - params.SSITSize = SSITSize; - params.LFSTSize = LFSTSize; - - params.numPhysIntRegs = numPhysIntRegs; - params.numPhysFloatRegs = numPhysFloatRegs; - params.numIQEntries = numIQEntries; - params.numROBEntries = numROBEntries; - - params.instShiftAmt = 2; - - params.defReg = defer_registration; - - params.functionTrace = function_trace; - params.functionTraceStart = function_trace_start; - - cpu = new DerivAlphaFullCPU(params); + params->cachePorts = cachePorts; + + params->decodeToFetchDelay = decodeToFetchDelay; + params->renameToFetchDelay = renameToFetchDelay; + params->iewToFetchDelay = iewToFetchDelay; + params->commitToFetchDelay = commitToFetchDelay; + params->fetchWidth = fetchWidth; + + params->renameToDecodeDelay = renameToDecodeDelay; + params->iewToDecodeDelay = iewToDecodeDelay; + params->commitToDecodeDelay = commitToDecodeDelay; + params->fetchToDecodeDelay = fetchToDecodeDelay; + params->decodeWidth = decodeWidth; + + params->iewToRenameDelay = iewToRenameDelay; + params->commitToRenameDelay = commitToRenameDelay; + params->decodeToRenameDelay = decodeToRenameDelay; + params->renameWidth = renameWidth; + + params->commitToIEWDelay = commitToIEWDelay; + params->renameToIEWDelay = renameToIEWDelay; + params->issueToExecuteDelay = issueToExecuteDelay; + params->issueWidth = issueWidth; + params->fuPool = fuPool; + + params->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + params->trapLatency = trapLatency; + params->fetchTrapLatency = fetchTrapLatency; + + params->predType = predType; + params->localPredictorSize = localPredictorSize; + params->localCtrBits = localCtrBits; + params->localHistoryTableSize = localHistoryTableSize; + params->localHistoryBits = localHistoryBits; + params->globalPredictorSize = globalPredictorSize; + params->globalCtrBits = globalCtrBits; + params->globalHistoryBits = globalHistoryBits; + params->choicePredictorSize = choicePredictorSize; + params->choiceCtrBits = choiceCtrBits; + + params->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->smtNumFetchingThreads = smtNumFetchingThreads; + + // Default smtFetchPolicy to "RoundRobin", if necessary. + std::string round_robin_policy = "RoundRobin"; + std::string single_thread = "SingleThread"; + + if (actual_num_threads > 1 && single_thread.compare(smtFetchPolicy) == 0) + params->smtFetchPolicy = round_robin_policy; + else + params->smtFetchPolicy = smtFetchPolicy; + + params->smtIQPolicy = smtIQPolicy; + params->smtLSQPolicy = smtLSQPolicy; + params->smtLSQThreshold = smtLSQThreshold; + params->smtROBPolicy = smtROBPolicy; + params->smtROBThreshold = smtROBThreshold; + params->smtCommitPolicy = smtCommitPolicy; + + params->instShiftAmt = 2; + + params->deferRegistration = defer_registration; + + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + + cpu = new DerivO3CPU(params); return cpu; } -REGISTER_SIM_OBJECT("DerivAlphaFullCPU", DerivAlphaFullCPU) +REGISTER_SIM_OBJECT("DerivO3CPU", DerivO3CPU) diff --git a/src/cpu/o3/alpha/cpu_impl.hh b/src/cpu/o3/alpha/cpu_impl.hh new file mode 100644 index 000000000..0473e60c2 --- /dev/null +++ b/src/cpu/o3/alpha/cpu_impl.hh @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "config/use_checker.hh" + +#include "arch/alpha/faults.hh" +#include "base/cprintf.hh" +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/checker/thread_context.hh" +#include "sim/sim_events.hh" +#include "sim/stats.hh" + +#include "cpu/o3/alpha/cpu.hh" +#include "cpu/o3/alpha/params.hh" +#include "cpu/o3/alpha/thread_context.hh" +#include "cpu/o3/comm.hh" +#include "cpu/o3/thread_state.hh" + +#if FULL_SYSTEM +#include "arch/alpha/osfpal.hh" +#include "arch/isa_traits.hh" +#include "cpu/quiesce_event.hh" +#include "kern/kernel_stats.hh" +#include "sim/sim_exit.hh" +#include "sim/system.hh" +#endif + +using namespace TheISA; + +template <class Impl> +AlphaO3CPU<Impl>::AlphaO3CPU(Params *params) +#if FULL_SYSTEM + : FullO3CPU<Impl>(params), itb(params->itb), dtb(params->dtb) +#else + : FullO3CPU<Impl>(params) +#endif +{ + DPRINTF(O3CPU, "Creating AlphaO3CPU object.\n"); + + // Setup any thread state. + this->thread.resize(this->numThreads); + + for (int i = 0; i < this->numThreads; ++i) { +#if FULL_SYSTEM + // SMT is not supported in FS mode yet. + assert(this->numThreads == 1); + this->thread[i] = new Thread(this, 0); + this->thread[i]->setStatus(ThreadContext::Suspended); +#else + if (i < params->workload.size()) { + DPRINTF(O3CPU, "Workload[%i] process is %#x", + i, this->thread[i]); + this->thread[i] = new Thread(this, i, params->workload[i], + i, params->mem); + + this->thread[i]->setStatus(ThreadContext::Suspended); + +#if !FULL_SYSTEM + /* Use this port to for syscall emulation writes to memory. */ + Port *mem_port; + TranslatingPort *trans_port; + trans_port = new TranslatingPort(csprintf("%s-%d-funcport", + name(), i), + params->workload[i]->pTable, + false); + mem_port = params->mem->getPort("functional"); + mem_port->setPeer(trans_port); + trans_port->setPeer(mem_port); + this->thread[i]->setMemPort(trans_port); +#endif + //usedTids[i] = true; + //threadMap[i] = i; + } else { + //Allocate Empty thread so M5 can use later + //when scheduling threads to CPU + Process* dummy_proc = NULL; + + this->thread[i] = new Thread(this, i, dummy_proc, i, params->mem); + //usedTids[i] = false; + } +#endif // !FULL_SYSTEM + + ThreadContext *tc; + + // Setup the TC that will serve as the interface to the threads/CPU. + AlphaTC<Impl> *alpha_tc = + new AlphaTC<Impl>; + + tc = alpha_tc; + + // If we're using a checker, then the TC should be the + // CheckerThreadContext. +#if USE_CHECKER + if (params->checker) { + tc = new CheckerThreadContext<AlphaTC<Impl> >( + alpha_tc, this->checker); + } +#endif + + alpha_tc->cpu = this; + alpha_tc->thread = this->thread[i]; + +#if FULL_SYSTEM + // Setup quiesce event. + this->thread[i]->quiesceEvent = new EndQuiesceEvent(tc); + + Port *mem_port; + FunctionalPort *phys_port; + VirtualPort *virt_port; + phys_port = new FunctionalPort(csprintf("%s-%d-funcport", + name(), i)); + mem_port = this->system->physmem->getPort("functional"); + mem_port->setPeer(phys_port); + phys_port->setPeer(mem_port); + + virt_port = new VirtualPort(csprintf("%s-%d-vport", + name(), i)); + mem_port = this->system->physmem->getPort("functional"); + mem_port->setPeer(virt_port); + virt_port->setPeer(mem_port); + + this->thread[i]->setPhysPort(phys_port); + this->thread[i]->setVirtPort(virt_port); +#endif + // Give the thread the TC. + this->thread[i]->tc = tc; + + // Add the TC to the CPU's list of TC's. + this->threadContexts.push_back(tc); + } + + for (int i=0; i < this->numThreads; i++) { + this->thread[i]->setFuncExeInst(0); + } + + // Sets CPU pointers. These must be set at this level because the CPU + // pointers are defined to be the highest level of CPU class. + this->fetch.setCPU(this); + this->decode.setCPU(this); + this->rename.setCPU(this); + this->iew.setCPU(this); + this->commit.setCPU(this); + + this->rob.setCPU(this); + this->regFile.setCPU(this); + + lockAddr = 0; + lockFlag = false; +} + +template <class Impl> +void +AlphaO3CPU<Impl>::regStats() +{ + // Register stats for everything that has stats. + this->fullCPURegStats(); + this->fetch.regStats(); + this->decode.regStats(); + this->rename.regStats(); + this->iew.regStats(); + this->commit.regStats(); +} + + +template <class Impl> +MiscReg +AlphaO3CPU<Impl>::readMiscReg(int misc_reg, unsigned tid) +{ + return this->regFile.readMiscReg(misc_reg, tid); +} + +template <class Impl> +MiscReg +AlphaO3CPU<Impl>::readMiscRegWithEffect(int misc_reg, Fault &fault, + unsigned tid) +{ + return this->regFile.readMiscRegWithEffect(misc_reg, fault, tid); +} + +template <class Impl> +Fault +AlphaO3CPU<Impl>::setMiscReg(int misc_reg, const MiscReg &val, unsigned tid) +{ + return this->regFile.setMiscReg(misc_reg, val, tid); +} + +template <class Impl> +Fault +AlphaO3CPU<Impl>::setMiscRegWithEffect(int misc_reg, const MiscReg &val, + unsigned tid) +{ + return this->regFile.setMiscRegWithEffect(misc_reg, val, tid); +} + +template <class Impl> +void +AlphaO3CPU<Impl>::squashFromTC(unsigned tid) +{ + this->thread[tid]->inSyscall = true; + this->commit.generateTCEvent(tid); +} + +#if FULL_SYSTEM + +template <class Impl> +void +AlphaO3CPU<Impl>::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (this->thread[0]->status() == ThreadContext::Suspended) { + DPRINTF(IPI,"Suspended Processor awoke\n"); + this->threadContexts[0]->activate(); + } +} + +template <class Impl> +int +AlphaO3CPU<Impl>::readIntrFlag() +{ + return this->regFile.readIntrFlag(); +} + +template <class Impl> +void +AlphaO3CPU<Impl>::setIntrFlag(int val) +{ + this->regFile.setIntrFlag(val); +} + +template <class Impl> +Fault +AlphaO3CPU<Impl>::hwrei(unsigned tid) +{ + // Need to clear the lock flag upon returning from an interrupt. + this->lockFlag = false; + + this->thread[tid]->kernelStats->hwrei(); + + this->checkInterrupts = true; + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +template <class Impl> +bool +AlphaO3CPU<Impl>::simPalCheck(int palFunc, unsigned tid) +{ + if (this->thread[tid]->kernelStats) + this->thread[tid]->kernelStats->callpal(palFunc, + this->threadContexts[tid]); + + switch (palFunc) { + case PAL::halt: + halt(); + if (--System::numSystemsRunning == 0) + exitSimLoop("all cpus halted"); + break; + + case PAL::bpt: + case PAL::bugchk: + if (this->system->breakpoint()) + return false; + break; + } + + return true; +} + +template <class Impl> +void +AlphaO3CPU<Impl>::processInterrupts() +{ + // Check for interrupts here. For now can copy the code that + // exists within isa_fullsys_traits.hh. Also assume that thread 0 + // is the one that handles the interrupts. + // @todo: Possibly consolidate the interrupt checking code. + // @todo: Allow other threads to handle interrupts. + + // Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + this->checkInterrupts = false; + + if (this->readMiscReg(IPR_ASTRR, 0)) + panic("asynchronous traps not implemented\n"); + + if (this->readMiscReg(IPR_SIRR, 0)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (this->readMiscReg(IPR_SIRR, 0) & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = this->intr_status(); + + if (interrupts) { + for (int i = INTLEVEL_EXTERNAL_MIN; + i < INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + } + + if (ipl && ipl > this->readMiscReg(IPR_IPLR, 0)) { + this->setMiscReg(IPR_ISR, summary, 0); + this->setMiscReg(IPR_INTID, ipl, 0); + // Checker needs to know these two registers were updated. +#if USE_CHECKER + if (this->checker) { + this->checker->threadBase()->setMiscReg(IPR_ISR, summary); + this->checker->threadBase()->setMiscReg(IPR_INTID, ipl); + } +#endif + this->trap(Fault(new InterruptFault), 0); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + this->readMiscReg(IPR_IPLR, 0), ipl, summary); + } +} + +#endif // FULL_SYSTEM + +template <class Impl> +void +AlphaO3CPU<Impl>::trap(Fault fault, unsigned tid) +{ + // Pass the thread's TC into the invoke method. + fault->invoke(this->threadContexts[tid]); +} + +#if !FULL_SYSTEM + +template <class Impl> +void +AlphaO3CPU<Impl>::syscall(int64_t callnum, int tid) +{ + DPRINTF(O3CPU, "[tid:%i] Executing syscall().\n\n", tid); + + DPRINTF(Activity,"Activity: syscall() called.\n"); + + // Temporarily increase this by one to account for the syscall + // instruction. + ++(this->thread[tid]->funcExeInst); + + // Execute the actual syscall. + this->thread[tid]->syscall(callnum); + + // Decrease funcExeInst by one as the normal commit will handle + // incrementing it. + --(this->thread[tid]->funcExeInst); +} + +template <class Impl> +TheISA::IntReg +AlphaO3CPU<Impl>::getSyscallArg(int i, int tid) +{ + return this->readArchIntReg(AlphaISA::ArgumentReg0 + i, tid); +} + +template <class Impl> +void +AlphaO3CPU<Impl>::setSyscallArg(int i, IntReg val, int tid) +{ + this->setArchIntReg(AlphaISA::ArgumentReg0 + i, val, tid); +} + +template <class Impl> +void +AlphaO3CPU<Impl>::setSyscallReturn(SyscallReturn return_value, int tid) +{ + // check for error condition. Alpha syscall convention is to + // indicate success/failure in reg a3 (r19) and put the + // return value itself in the standard return value reg (v0). + if (return_value.successful()) { + // no error + this->setArchIntReg(SyscallSuccessReg, 0, tid); + this->setArchIntReg(ReturnValueReg, return_value.value(), tid); + } else { + // got an error, return details + this->setArchIntReg(SyscallSuccessReg, (IntReg) -1, tid); + this->setArchIntReg(ReturnValueReg, -return_value.value(), tid); + } +} +#endif diff --git a/src/cpu/o3/alpha_dyn_inst.cc b/src/cpu/o3/alpha/dyn_inst.cc index 72ac77d95..97d2f3d08 100644 --- a/src/cpu/o3/alpha_dyn_inst.cc +++ b/src/cpu/o3/alpha/dyn_inst.cc @@ -24,10 +24,12 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_dyn_inst_impl.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/alpha/dyn_inst_impl.hh" +#include "cpu/o3/alpha/impl.hh" // Force instantiation of AlphaDynInst for all the implementations that // are needed. diff --git a/src/cpu/o3/alpha_dyn_inst.hh b/src/cpu/o3/alpha/dyn_inst.hh index 5b8a05e5c..9dee610b6 100644 --- a/src/cpu/o3/alpha_dyn_inst.hh +++ b/src/cpu/o3/alpha/dyn_inst.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,36 +24,45 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_ALPHA_DYN_INST_HH__ -#define __CPU_O3_CPU_ALPHA_DYN_INST_HH__ +#ifndef __CPU_O3_ALPHA_DYN_INST_HH__ +#define __CPU_O3_ALPHA_DYN_INST_HH__ +#include "arch/isa_traits.hh" #include "cpu/base_dyn_inst.hh" -#include "cpu/o3/alpha_cpu.hh" -#include "cpu/o3/alpha_impl.hh" #include "cpu/inst_seq.hh" +#include "cpu/o3/alpha/cpu.hh" +#include "cpu/o3/alpha/impl.hh" + +class Packet; /** - * Mostly implementation specific AlphaDynInst. It is templated in case there - * are other implementations that are similar enough to be able to use this - * class without changes. This is mainly useful if there are multiple similar - * CPU implementations of the same ISA. + * Mostly implementation & ISA specific AlphaDynInst. As with most + * other classes in the new CPU model, it is templated on the Impl to + * allow for passing in of all types, such as the CPU type and the ISA + * type. The AlphaDynInst serves as the primary interface to the CPU + * for instructions that are executing. */ - template <class Impl> class AlphaDynInst : public BaseDynInst<Impl> { public: /** Typedef for the CPU. */ - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::O3CPU O3CPU; /** Binary machine instruction type. */ typedef TheISA::MachInst MachInst; + /** Extended machine instruction type. */ + typedef TheISA::ExtMachInst ExtMachInst; /** Logical register index type. */ typedef TheISA::RegIndex RegIndex; /** Integer register index type. */ typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; /** Misc register index type. */ typedef TheISA::MiscReg MiscReg; @@ -64,60 +73,74 @@ class AlphaDynInst : public BaseDynInst<Impl> public: /** BaseDynInst constructor given a binary instruction. */ - AlphaDynInst(MachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, - FullCPU *cpu); + AlphaDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, + O3CPU *cpu); /** BaseDynInst constructor given a static inst pointer. */ AlphaDynInst(StaticInstPtr &_staticInst); /** Executes the instruction.*/ - Fault execute() - { - return this->fault = this->staticInst->execute(this, this->traceData); - } + Fault execute(); + + /** Initiates the access. Only valid for memory operations. */ + Fault initiateAcc(); + + /** Completes the access. Only valid for memory operations. */ + Fault completeAcc(Packet *pkt); + + private: + /** Initializes variables. */ + void initVars(); public: + /** Reads a miscellaneous register. */ MiscReg readMiscReg(int misc_reg) { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return 0; + return this->cpu->readMiscReg(misc_reg, this->threadNumber); } + /** Reads a misc. register, including any side-effects the read + * might have as defined by the architecture. + */ MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return 0; + return this->cpu->readMiscRegWithEffect(misc_reg, fault, + this->threadNumber); } + /** Sets a misc. register. */ Fault setMiscReg(int misc_reg, const MiscReg &val) { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return NoFault; + this->instResult.integer = val; + return this->cpu->setMiscReg(misc_reg, val, this->threadNumber); } + /** Sets a misc. register, including any side-effects the write + * might have as defined by the architecture. + */ Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return NoFault; + return this->cpu->setMiscRegWithEffect(misc_reg, val, + this->threadNumber); } #if FULL_SYSTEM + /** Calls hardware return from error interrupt. */ Fault hwrei(); + /** Reads interrupt flag. */ int readIntrFlag(); + /** Sets interrupt flag. */ void setIntrFlag(int val); + /** Checks if system is in PAL mode. */ bool inPalMode(); + /** Traps to handle specified fault. */ void trap(Fault fault); bool simPalCheck(int palFunc); #else - void syscall(); + /** Calls a syscall. */ + void syscall(int64_t callnum); #endif - - private: /** Physical register index of the destination registers of this * instruction. @@ -178,32 +201,32 @@ class AlphaDynInst : public BaseDynInst<Impl> void setIntReg(const StaticInst *si, int idx, uint64_t val) { this->cpu->setIntReg(_destRegIdx[idx], val); - this->instResult.integer = val; + BaseDynInst<Impl>::setIntReg(si, idx, val); } void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) { this->cpu->setFloatReg(_destRegIdx[idx], val, width); - this->instResult.fp = val; + BaseDynInst<Impl>::setFloatReg(si, idx, val, width); } void setFloatReg(const StaticInst *si, int idx, FloatReg val) { this->cpu->setFloatReg(_destRegIdx[idx], val); - this->instResult.dbl = val; + BaseDynInst<Impl>::setFloatReg(si, idx, val); } void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val, int width) { this->cpu->setFloatRegBits(_destRegIdx[idx], val, width); - this->instResult.integer = val; + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); } void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) { this->cpu->setFloatRegBits(_destRegIdx[idx], val); - this->instResult.integer = val; + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); } /** Returns the physical register index of the i'th destination @@ -249,16 +272,24 @@ class AlphaDynInst : public BaseDynInst<Impl> } public: + /** Calculates EA part of a memory instruction. Currently unused, + * though it may be useful in the future if we want to split + * memory operations into EA calculation and memory access parts. + */ Fault calcEA() { return this->staticInst->eaCompInst()->execute(this, this->traceData); } + /** Does the memory access part of a memory instruction. Currently unused, + * though it may be useful in the future if we want to split + * memory operations into EA calculation and memory access parts. + */ Fault memAccess() { return this->staticInst->memAccInst()->execute(this, this->traceData); } }; -#endif // __CPU_O3_CPU_ALPHA_DYN_INST_HH__ +#endif // __CPU_O3_ALPHA_DYN_INST_HH__ diff --git a/src/cpu/o3/alpha_dyn_inst_impl.hh b/src/cpu/o3/alpha/dyn_inst_impl.hh index 96b7d3430..2d1b4b309 100644 --- a/src/cpu/o3/alpha_dyn_inst_impl.hh +++ b/src/cpu/o3/alpha/dyn_inst_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,47 +24,87 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/o3/alpha/dyn_inst.hh" template <class Impl> -AlphaDynInst<Impl>::AlphaDynInst(MachInst inst, Addr PC, Addr Pred_PC, - InstSeqNum seq_num, FullCPU *cpu) +AlphaDynInst<Impl>::AlphaDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, + InstSeqNum seq_num, O3CPU *cpu) : BaseDynInst<Impl>(inst, PC, Pred_PC, seq_num, cpu) { + initVars(); +} + +template <class Impl> +AlphaDynInst<Impl>::AlphaDynInst(StaticInstPtr &_staticInst) + : BaseDynInst<Impl>(_staticInst) +{ + initVars(); +} + +template <class Impl> +void +AlphaDynInst<Impl>::initVars() +{ // Make sure to have the renamed register entries set to the same // as the normal register entries. It will allow the IQ to work // without any modifications. - for (int i = 0; i < this->staticInst->numDestRegs(); i++) - { + for (int i = 0; i < this->staticInst->numDestRegs(); i++) { _destRegIdx[i] = this->staticInst->destRegIdx(i); } - for (int i = 0; i < this->staticInst->numSrcRegs(); i++) - { + for (int i = 0; i < this->staticInst->numSrcRegs(); i++) { _srcRegIdx[i] = this->staticInst->srcRegIdx(i); this->_readySrcRegIdx[i] = 0; } +} + +template <class Impl> +Fault +AlphaDynInst<Impl>::execute() +{ + // @todo: Pretty convoluted way to avoid squashing from happening + // when using the TC during an instruction's execution + // (specifically for instructions that have side-effects that use + // the TC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->execute(this, this->traceData); + + this->thread->inSyscall = in_syscall; + return this->fault; } template <class Impl> -AlphaDynInst<Impl>::AlphaDynInst(StaticInstPtr &_staticInst) - : BaseDynInst<Impl>(_staticInst) +Fault +AlphaDynInst<Impl>::initiateAcc() { - // Make sure to have the renamed register entries set to the same - // as the normal register entries. It will allow the IQ to work - // without any modifications. - for (int i = 0; i < _staticInst->numDestRegs(); i++) - { - _destRegIdx[i] = _staticInst->destRegIdx(i); - } + // @todo: Pretty convoluted way to avoid squashing from happening + // when using the TC during an instruction's execution + // (specifically for instructions that have side-effects that use + // the TC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; - for (int i = 0; i < _staticInst->numSrcRegs(); i++) - { - _srcRegIdx[i] = _staticInst->srcRegIdx(i); - } + this->fault = this->staticInst->initiateAcc(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template <class Impl> +Fault +AlphaDynInst<Impl>::completeAcc(Packet *pkt) +{ + this->fault = this->staticInst->completeAcc(pkt, this, this->traceData); + + return this->fault; } #if FULL_SYSTEM @@ -72,14 +112,26 @@ template <class Impl> Fault AlphaDynInst<Impl>::hwrei() { - return this->cpu->hwrei(); + // Can only do a hwrei when in pal mode. + if (!this->cpu->inPalMode(this->readPC())) + return new AlphaISA::UnimplementedOpcodeFault; + + // Set the next PC based on the value of the EXC_ADDR IPR. + this->setNextPC(this->cpu->readMiscReg(AlphaISA::IPR_EXC_ADDR, + this->threadNumber)); + + // Tell CPU to clear any state it needs to if a hwrei is taken. + this->cpu->hwrei(this->threadNumber); + + // FIXME: XXX check for interrupts? XXX + return NoFault; } template <class Impl> int AlphaDynInst<Impl>::readIntrFlag() { -return this->cpu->readIntrFlag(); + return this->cpu->readIntrFlag(); } template <class Impl> @@ -93,28 +145,28 @@ template <class Impl> bool AlphaDynInst<Impl>::inPalMode() { - return this->cpu->inPalMode(); + return this->cpu->inPalMode(this->PC); } template <class Impl> void AlphaDynInst<Impl>::trap(Fault fault) { - this->cpu->trap(fault); + this->cpu->trap(fault, this->threadNumber); } template <class Impl> bool AlphaDynInst<Impl>::simPalCheck(int palFunc) { - return this->cpu->simPalCheck(palFunc); + return this->cpu->simPalCheck(palFunc, this->threadNumber); } #else template <class Impl> void -AlphaDynInst<Impl>::syscall() +AlphaDynInst<Impl>::syscall(int64_t callnum) { - this->cpu->syscall(this->threadNumber); + this->cpu->syscall(callnum, this->threadNumber); } #endif diff --git a/src/cpu/o3/alpha_impl.hh b/src/cpu/o3/alpha/impl.hh index 5e39fcb37..8cd8692c6 100644 --- a/src/cpu/o3/alpha_impl.hh +++ b/src/cpu/o3/alpha/impl.hh @@ -24,14 +24,16 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_ALPHA_IMPL_HH__ -#define __CPU_O3_CPU_ALPHA_IMPL_HH__ +#ifndef __CPU_O3_ALPHA_IMPL_HH__ +#define __CPU_O3_ALPHA_IMPL_HH__ #include "arch/alpha/isa_traits.hh" -#include "cpu/o3/alpha_params.hh" +#include "cpu/o3/alpha/params.hh" #include "cpu/o3/cpu_policy.hh" // Forward declarations. @@ -39,12 +41,12 @@ template <class Impl> class AlphaDynInst; template <class Impl> -class AlphaFullCPU; +class AlphaO3CPU; -/** Implementation specific struct that defines several key things to the +/** Implementation specific struct that defines several key types to the * CPU, the stages within the CPU, the time buffers, and the DynInst. * The struct defines the ISA, the CPU policy, the specific DynInst, the - * specific FullCPU, and all of the structs from the time buffers to do + * specific O3CPU, and all of the structs from the time buffers to do * communication. * This is one of the key things that must be defined for each hardware * specific CPU implementation. @@ -54,10 +56,10 @@ struct AlphaSimpleImpl /** The type of MachInst. */ typedef TheISA::MachInst MachInst; - /** The CPU policy to be used (ie fetch, decode, etc.). */ + /** The CPU policy to be used, which defines all of the CPU stages. */ typedef SimpleCPUPolicy<AlphaSimpleImpl> CPUPol; - /** The DynInst to be used. */ + /** The DynInst type to be used. */ typedef AlphaDynInst<AlphaSimpleImpl> DynInst; /** The refcounted DynInst pointer to be used. In most cases this is @@ -65,15 +67,28 @@ struct AlphaSimpleImpl */ typedef RefCountingPtr<DynInst> DynInstPtr; - /** The FullCPU to be used. */ - typedef AlphaFullCPU<AlphaSimpleImpl> FullCPU; + /** The O3CPU type to be used. */ + typedef AlphaO3CPU<AlphaSimpleImpl> O3CPU; + + /** Same typedef, but for CPUType. BaseDynInst may not always use + * an O3 CPU, so it's clearer to call it CPUType instead in that + * case. + */ + typedef O3CPU CPUType; /** The Params to be passed to each stage. */ typedef AlphaSimpleParams Params; enum { - MaxWidth = 8 + MaxWidth = 8, + MaxThreads = 4 }; }; -#endif // __CPU_O3_CPU_ALPHA_IMPL_HH__ +/** The O3Impl to be used. */ +typedef AlphaSimpleImpl O3CPUImpl; + +/** The O3Impl to be used. */ +typedef DynInst O3DynInst; + +#endif // __CPU_O3_ALPHA_IMPL_HH__ diff --git a/src/cpu/o3/alpha/params.hh b/src/cpu/o3/alpha/params.hh new file mode 100644 index 000000000..b1f2a487d --- /dev/null +++ b/src/cpu/o3/alpha/params.hh @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_ALPHA_PARAMS_HH__ +#define __CPU_O3_ALPHA_PARAMS_HH__ + +#include "cpu/o3/cpu.hh" +#include "cpu/o3/params.hh" + +//Forward declarations +class AlphaDTB; +class AlphaITB; +class MemObject; +class Process; +class System; + +/** + * This file defines the parameters that will be used for the AlphaO3CPU. + * This must be defined externally so that the Impl can have a params class + * defined that it can pass to all of the individual stages. + */ + +class AlphaSimpleParams : public O3Params +{ + public: + +#if FULL_SYSTEM + AlphaITB *itb; + AlphaDTB *dtb; +#else + std::vector<Process *> workload; + Process *process; +#endif // FULL_SYSTEM + + MemObject *mem; + + BaseCPU *checker; + + unsigned decodeToFetchDelay; +}; + +#endif // __CPU_O3_ALPHA_PARAMS_HH__ diff --git a/src/cpu/o3/alpha/thread_context.cc b/src/cpu/o3/alpha/thread_context.cc new file mode 100755 index 000000000..4a02715bc --- /dev/null +++ b/src/cpu/o3/alpha/thread_context.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/thread_context.hh" +#include "cpu/o3/thread_context_impl.hh" + +template class O3ThreadContext<AlphaSimpleImpl>; + diff --git a/src/cpu/o3/alpha/thread_context.hh b/src/cpu/o3/alpha/thread_context.hh new file mode 100644 index 000000000..78b0ee788 --- /dev/null +++ b/src/cpu/o3/alpha/thread_context.hh @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/thread_context.hh" + +template <class Impl> +class AlphaTC : public O3ThreadContext<Impl> +{ + public: +#if FULL_SYSTEM + /** Returns a pointer to the ITB. */ + virtual AlphaITB *getITBPtr() { return this->cpu->itb; } + + /** Returns a pointer to the DTB. */ + virtual AlphaDTB *getDTBPtr() { return this->cpu->dtb; } + + /** Returns pointer to the quiesce event. */ + virtual EndQuiesceEvent *getQuiesceEvent() + { + return this->thread->quiesceEvent; + } + + /** Returns if the thread is currently in PAL mode, based on + * the PC's value. */ + virtual bool inPalMode() + { return TheISA::PcPAL(this->cpu->readPC(this->thread->readTid())); } +#endif + + virtual uint64_t readNextNPC() + { + panic("Alpha has no NextNPC!"); + return 0; + } + + virtual void setNextNPC(uint64_t val) + { + panic("Alpha has no NextNPC!"); + } + + virtual void changeRegFileContext(TheISA::RegFile::ContextParam param, + TheISA::RegFile::ContextVal val) + { panic("Not supported on Alpha!"); } + + + // This function exits the thread context in the CPU and returns + // 1 if the CPU has no more active threads (meaning it's OK to exit); + // Used in syscall-emulation mode when a thread executes the 'exit' + // syscall. + virtual int exit() + { + this->cpu->deallocateContext(this->thread->readTid()); + + // If there are still threads executing in the system + if (this->cpu->numActiveThreads()) + return 0; + else + return 1; + } +}; diff --git a/src/cpu/o3/alpha_cpu.hh b/src/cpu/o3/alpha_cpu.hh deleted file mode 100644 index 8e1e0f42a..000000000 --- a/src/cpu/o3/alpha_cpu.hh +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (c) 2004-2005 The Regents of The University of Michigan - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer; - * redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution; - * neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// Todo: Find all the stuff in ExecContext and ev5 that needs to be -// specifically designed for this CPU. - -#ifndef __CPU_O3_CPU_ALPHA_FULL_CPU_HH__ -#define __CPU_O3_CPU_ALPHA_FULL_CPU_HH__ - -#include "cpu/o3/cpu.hh" -#include "arch/isa_traits.hh" -#include "sim/byteswap.hh" - -template <class Impl> -class AlphaFullCPU : public FullO3CPU<Impl> -{ - protected: - typedef TheISA::IntReg IntReg; - typedef TheISA::MiscReg MiscReg; - typedef TheISA::RegFile RegFile; - typedef TheISA::MiscRegFile MiscRegFile; - - public: - typedef typename Impl::Params Params; - - public: - AlphaFullCPU(Params ¶ms); - -#if FULL_SYSTEM - AlphaITB *itb; - AlphaDTB *dtb; -#endif - - public: - void regStats(); - -#if FULL_SYSTEM - //Note that the interrupt stuff from the base CPU might be somewhat - //ISA specific (ie NumInterruptLevels). These functions might not - //be needed in FullCPU though. -// void post_interrupt(int int_num, int index); -// void clear_interrupt(int int_num, int index); -// void clear_interrupts(); - - Fault translateInstReq(MemReqPtr &req) - { - return itb->translate(req); - } - - Fault translateDataReadReq(MemReqPtr &req) - { - return dtb->translate(req, false); - } - - Fault translateDataWriteReq(MemReqPtr &req) - { - return dtb->translate(req, true); - } - -#else - Fault dummyTranslation(MemReqPtr &req) - { -#if 0 - assert((req->vaddr >> 48 & 0xffff) == 0); -#endif - - // put the asid in the upper 16 bits of the paddr - req->paddr = req->vaddr & ~((Addr)0xffff << sizeof(Addr) * 8 - 16); - req->paddr = req->paddr | (Addr)req->asid << sizeof(Addr) * 8 - 16; - return NoFault; - } - - Fault translateInstReq(MemReqPtr &req) - { - return dummyTranslation(req); - } - - Fault translateDataReadReq(MemReqPtr &req) - { - return dummyTranslation(req); - } - - Fault translateDataWriteReq(MemReqPtr &req) - { - return dummyTranslation(req); - } - -#endif - - // Later on may want to remove this misc stuff from the regfile and - // have it handled at this level. Might prove to be an issue when - // trying to rename source/destination registers... - MiscReg readMiscReg(int misc_reg) - { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return 0; - } - - Fault setMiscReg(int misc_reg, const MiscReg &val) - { - // Dummy function for now. - // @todo: Fix this once reg file gets fixed. - return NoFault; - } - - // Most of the full system code and syscall emulation is not yet - // implemented. These functions do show what the final interface will - // look like. -#if FULL_SYSTEM - int readIntrFlag(); - void setIntrFlag(int val); - Fault hwrei(); - bool inPalMode() { return AlphaISA::PcPAL(this->regFile.readPC()); } - bool inPalMode(uint64_t PC) - { return AlphaISA::PcPAL(PC); } - - void trap(Fault fault); - bool simPalCheck(int palFunc); - - void processInterrupts(); -#endif - - -#if !FULL_SYSTEM - // Need to change these into regfile calls that directly set a certain - // register. Actually, these functions should handle most of this - // functionality by themselves; should look up the rename and then - // set the register. - IntReg getSyscallArg(int i) - { - return this->cpuXC->readIntReg(AlphaISA::ArgumentReg0 + i); - } - - // used to shift args for indirect syscall - void setSyscallArg(int i, IntReg val) - { - this->cpuXC->setIntReg(AlphaISA::ArgumentReg0 + i, val); - } - - void setSyscallReturn(int64_t return_value) - { - // check for error condition. Alpha syscall convention is to - // indicate success/failure in reg a3 (r19) and put the - // return value itself in the standard return value reg (v0). - const int RegA3 = 19; // only place this is used - if (return_value >= 0) { - // no error - this->cpuXC->setIntReg(RegA3, 0); - this->cpuXC->setIntReg(AlphaISA::ReturnValueReg, return_value); - } else { - // got an error, return details - this->cpuXC->setIntReg(RegA3, (IntReg) -1); - this->cpuXC->setIntReg(AlphaISA::ReturnValueReg, -return_value); - } - } - - void syscall(short thread_num); - void squashStages(); - -#endif - - void copyToXC(); - void copyFromXC(); - - public: -#if FULL_SYSTEM - bool palShadowEnabled; - - // Not sure this is used anywhere. - void intr_post(RegFile *regs, Fault fault, Addr pc); - // Actually used within exec files. Implement properly. - void swapPALShadow(bool use_shadow); - // Called by CPU constructor. Can implement as I please. - void initCPU(RegFile *regs); - // Called by initCPU. Implement as I please. - void initIPRs(RegFile *regs); - - void halt() { panic("Halt not implemented!\n"); } -#endif - - - template <class T> - Fault read(MemReqPtr &req, T &data) - { -#if FULL_SYSTEM && THE_ISA == ALPHA_ISA - if (req->flags & LOCKED) { - req->xc->setMiscReg(TheISA::Lock_Addr_DepTag, req->paddr); - req->xc->setMiscReg(TheISA::Lock_Flag_DepTag, true); - } -#endif - - Fault error; - error = this->mem->read(req, data); - data = gtoh(data); - return error; - } - - template <class T> - Fault read(MemReqPtr &req, T &data, int load_idx) - { - return this->iew.ldstQueue.read(req, data, load_idx); - } - - template <class T> - Fault write(MemReqPtr &req, T &data) - { -#if FULL_SYSTEM && THE_ISA == ALPHA_ISA - ExecContext *xc; - - // If this is a store conditional, act appropriately - if (req->flags & LOCKED) { - xc = req->xc; - - if (req->flags & UNCACHEABLE) { - // Don't update result register (see stq_c in isa_desc) - req->result = 2; - xc->setStCondFailures(0);//Needed? [RGD] - } else { - bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag); - Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag); - req->result = lock_flag; - if (!lock_flag || - ((lock_addr & ~0xf) != (req->paddr & ~0xf))) { - xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); - xc->setStCondFailures(xc->readStCondFailures() + 1); - if (((xc->readStCondFailures()) % 100000) == 0) { - std::cerr << "Warning: " - << xc->readStCondFailures() - << " consecutive store conditional failures " - << "on cpu " << req->xc->readCpuId() - << std::endl; - } - return NoFault; - } - else xc->setStCondFailures(0); - } - } - - // Need to clear any locked flags on other proccessors for - // this address. Only do this for succsful Store Conditionals - // and all other stores (WH64?). Unsuccessful Store - // Conditionals would have returned above, and wouldn't fall - // through. - for (int i = 0; i < this->system->execContexts.size(); i++){ - xc = this->system->execContexts[i]; - if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) == - (req->paddr & ~0xf)) { - xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); - } - } - -#endif - - return this->mem->write(req, (T)htog(data)); - } - - template <class T> - Fault write(MemReqPtr &req, T &data, int store_idx) - { - return this->iew.ldstQueue.write(req, data, store_idx); - } - -}; - -#endif // __CPU_O3_CPU_ALPHA_FULL_CPU_HH__ diff --git a/src/cpu/o3/alpha_cpu_impl.hh b/src/cpu/o3/alpha_cpu_impl.hh deleted file mode 100644 index 7c4c2b969..000000000 --- a/src/cpu/o3/alpha_cpu_impl.hh +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2004-2005 The Regents of The University of Michigan - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer; - * redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution; - * neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "arch/alpha/faults.hh" -#include "base/cprintf.hh" -#include "base/statistics.hh" -#include "base/timebuf.hh" -#include "mem/cache/cache.hh" // for dynamic cast -#include "mem/mem_interface.hh" -#include "sim/builder.hh" -#include "sim/sim_events.hh" -#include "sim/stats.hh" - -#include "cpu/o3/alpha_cpu.hh" -#include "cpu/o3/alpha_params.hh" -#include "cpu/o3/comm.hh" - -#if FULL_SYSTEM -#include "arch/alpha/osfpal.hh" -#include "arch/alpha/isa_traits.hh" -#endif - -template <class Impl> -AlphaFullCPU<Impl>::AlphaFullCPU(Params ¶ms) - : FullO3CPU<Impl>(params) -{ - DPRINTF(FullCPU, "AlphaFullCPU: Creating AlphaFullCPU object.\n"); - - this->fetch.setCPU(this); - this->decode.setCPU(this); - this->rename.setCPU(this); - this->iew.setCPU(this); - this->commit.setCPU(this); - - this->rob.setCPU(this); -} - -template <class Impl> -void -AlphaFullCPU<Impl>::regStats() -{ - // Register stats for everything that has stats. - this->fullCPURegStats(); - this->fetch.regStats(); - this->decode.regStats(); - this->rename.regStats(); - this->iew.regStats(); - this->commit.regStats(); -} - -#if !FULL_SYSTEM - -// Will probably need to know which thread is calling syscall -// Will need to pass that information in to the DynInst when it is constructed, -// so that this call can be made with the proper thread number. -template <class Impl> -void -AlphaFullCPU<Impl>::syscall(short thread_num) -{ - DPRINTF(FullCPU, "AlphaFullCPU: Syscall() called.\n\n"); - - // Commit stage needs to run as well. - this->commit.tick(); - - squashStages(); - - // Temporarily increase this by one to account for the syscall - // instruction. - ++(this->funcExeInst); - - // Copy over all important state to xc once all the unrolling is done. - copyToXC(); - - // This is hardcoded to thread 0 while the CPU is only single threaded. - this->thread[0]->syscall(); - - // Copy over all important state back to CPU. - copyFromXC(); - - // Decrease funcExeInst by one as the normal commit will handle - // incrememnting it. - --(this->funcExeInst); -} - -// This is not a pretty function, and should only be used if it is necessary -// to fake having everything squash all at once (ie for non-full system -// syscalls). Maybe put this at the FullCPU level? -template <class Impl> -void -AlphaFullCPU<Impl>::squashStages() -{ - InstSeqNum rob_head = this->rob.readHeadSeqNum(); - - // Now hack the time buffer to put this sequence number in the places - // where the stages might read it. - for (int i = 0; i < 5; ++i) - { - this->timeBuffer.access(-i)->commitInfo.doneSeqNum = rob_head; - } - - this->fetch.squash(this->rob.readHeadNextPC()); - this->fetchQueue.advance(); - - this->decode.squash(); - this->decodeQueue.advance(); - - this->rename.squash(); - this->renameQueue.advance(); - this->renameQueue.advance(); - - // Be sure to advance the IEW queues so that the commit stage doesn't - // try to set an instruction as completed at the same time that it - // might be deleting it. - this->iew.squash(); - this->iewQueue.advance(); - this->iewQueue.advance(); - // Needs to tell the LSQ to write back all of its data - this->iew.lsqWriteback(); - - this->rob.squash(rob_head); - this->commit.setSquashing(); - - // Now hack the time buffer to clear the sequence numbers in the places - // where the stages might read it.? - for (int i = 0; i < 5; ++i) - { - this->timeBuffer.access(-i)->commitInfo.doneSeqNum = 0; - } - -} - -#endif // FULL_SYSTEM - -template <class Impl> -void -AlphaFullCPU<Impl>::copyToXC() -{ - PhysRegIndex renamed_reg; - - // First loop through the integer registers. - for (int i = 0; i < AlphaISA::NumIntRegs; ++i) - { - renamed_reg = this->renameMap.lookup(i); - this->cpuXC->setIntReg(i, this->regFile.readIntReg(renamed_reg)); - DPRINTF(FullCPU, "FullCPU: Copying register %i, has data %lli.\n", - renamed_reg, this->regFile.intRegFile[renamed_reg]); - } - - // Then loop through the floating point registers. - for (int i = 0; i < AlphaISA::NumFloatRegs; ++i) - { - renamed_reg = this->renameMap.lookup(i + AlphaISA::FP_Base_DepTag); - this->cpuXC->setFloatRegBits(i, - this->regFile.readFloatRegBits(renamed_reg)); - } - - this->cpuXC->setMiscReg(AlphaISA::Fpcr_DepTag, - this->regFile.readMiscReg(AlphaISA::Fpcr_DepTag)); - this->cpuXC->setMiscReg(AlphaISA::Uniq_DepTag, - this->regFile.readMiscReg(AlphaISA::Uniq_DepTag)); - this->cpuXC->setMiscReg(AlphaISA::Lock_Flag_DepTag, - this->regFile.readMiscReg(AlphaISA::Lock_Flag_DepTag)); - this->cpuXC->setMiscReg(AlphaISA::Lock_Addr_DepTag, - this->regFile.readMiscReg(AlphaISA::Lock_Addr_DepTag)); - - this->cpuXC->setPC(this->rob.readHeadPC()); - this->cpuXC->setNextPC(this->cpuXC->readPC()+4); - -#if !FULL_SYSTEM - this->cpuXC->setFuncExeInst(this->funcExeInst); -#endif -} - -// This function will probably mess things up unless the ROB is empty and -// there are no instructions in the pipeline. -template <class Impl> -void -AlphaFullCPU<Impl>::copyFromXC() -{ - PhysRegIndex renamed_reg; - - // First loop through the integer registers. - for (int i = 0; i < AlphaISA::NumIntRegs; ++i) - { - renamed_reg = this->renameMap.lookup(i); - - DPRINTF(FullCPU, "FullCPU: Copying over register %i, had data %lli, " - "now has data %lli.\n", - renamed_reg, this->regFile.intRegFile[renamed_reg], - this->cpuXC->readIntReg(i)); - - this->regFile.setIntReg(renamed_reg, this->cpuXC->readIntReg(i)); - } - - // Then loop through the floating point registers. - for (int i = 0; i < AlphaISA::NumFloatRegs; ++i) - { - renamed_reg = this->renameMap.lookup(i + AlphaISA::FP_Base_DepTag); - this->regFile.setFloatRegBits(renamed_reg, - this->cpuXC->readFloatRegBits(i)); - } - - // Then loop through the misc registers. - this->regFile.setMiscReg(AlphaISA::Fpcr_DepTag, - this->cpuXC->readMiscReg(AlphaISA::Fpcr_DepTag)); - this->regFile.setMiscReg(AlphaISA::Uniq_DepTag, - this->cpuXC->readMiscReg(AlphaISA::Uniq_DepTag)); - this->regFile.setMiscReg(AlphaISA::Lock_Flag_DepTag, - this->cpuXC->readMiscReg(AlphaISA::Lock_Flag_DepTag)); - this->regFile.setMiscReg(AlphaISA::Lock_Addr_DepTag, - this->cpuXC->readMiscReg(AlphaISA::Lock_Addr_DepTag)); - - // Then finally set the PC and the next PC. -// regFile.pc = cpuXC->regs.pc; -// regFile.npc = cpuXC->regs.npc; -#if !FULL_SYSTEM - this->funcExeInst = this->cpuXC->readFuncExeInst(); -#endif -} - -#if FULL_SYSTEM - -template <class Impl> -int -AlphaFullCPU<Impl>::readIntrFlag() -{ - return this->regFile.readIntrFlag(); -} - -template <class Impl> -void -AlphaFullCPU<Impl>::setIntrFlag(int val) -{ - this->regFile.setIntrFlag(val); -} - -// Can force commit stage to squash and stuff. -template <class Impl> -Fault -AlphaFullCPU<Impl>::hwrei() -{ - if (!inPalMode()) - return new AlphaISA::UnimplementedOpcodeFault; - - this->setNextPC(this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR)); - -// kernelStats.hwrei(); - - if ((this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR) & 1) == 0) -// AlphaISA::swap_palshadow(®s, false); - - this->checkInterrupts = true; - - // FIXME: XXX check for interrupts? XXX - return NoFault; -} - -template <class Impl> -bool -AlphaFullCPU<Impl>::simPalCheck(int palFunc) -{ -// kernelStats.callpal(palFunc); - - switch (palFunc) { - case PAL::halt: - halt(); - if (--System::numSystemsRunning == 0) - new SimExitEvent("all cpus halted"); - break; - - case PAL::bpt: - case PAL::bugchk: - if (this->system->breakpoint()) - return false; - break; - } - - return true; -} - -// Probably shouldn't be able to switch to the trap handler as quickly as -// this. Also needs to get the exception restart address from the commit -// stage. -template <class Impl> -void -AlphaFullCPU<Impl>::trap(Fault fault) -{ -/* // Keep in mind that a trap may be initiated by fetch if there's a TLB - // miss - uint64_t PC = this->commit.readCommitPC(); - - DPRINTF(Fault, "Fault %s\n", fault->name()); - this->recordEvent(csprintf("Fault %s", fault->name())); - - //kernelStats.fault(fault); - - if (fault->isA<ArithmeticFault>()) - panic("Arithmetic traps are unimplemented!"); - - // exception restart address - Get the commit PC - if (!fault->isA<InterruptFault>() || !inPalMode(PC)) - this->regFile.miscRegs.setReg(AlphaISA::IPR_EXC_ADDR, PC); - - if (fault->isA<PalFault>() || fault->isA<ArithmeticFault>()) - // || fault == InterruptFault && !PC_PAL(regs.pc) - { - // traps... skip faulting instruction - AlphaISA::MiscReg ipr_exc_addr = - this->regFile.miscRegs.readReg(AlphaISA::IPR_EXC_ADDR); - this->regFile.miscRegs.setReg(AlphaISA::IPR_EXC_ADDR, - ipr_exc_addr + 4); - } - - if (!inPalMode(PC)) - swapPALShadow(true); - - this->regFile.setPC(this->regFile.miscRegs.readReg(AlphaISA::IPR_PAL_BASE) + - (dynamic_cast<AlphaFault *>(fault.get()))->vect()); - this->regFile.setNextPC(PC + sizeof(MachInst));*/ -} - -template <class Impl> -void -AlphaFullCPU<Impl>::processInterrupts() -{ - // Check for interrupts here. For now can copy the code that exists - // within isa_fullsys_traits.hh. -} - -// swap_palshadow swaps in the values of the shadow registers and -// swaps them with the values of the physical registers that map to the -// same logical index. -template <class Impl> -void -AlphaFullCPU<Impl>::swapPALShadow(bool use_shadow) -{ - if (palShadowEnabled == use_shadow) - panic("swap_palshadow: wrong PAL shadow state"); - - palShadowEnabled = use_shadow; - - // Will have to lookup in rename map to get physical registers, then - // swap. -} - -#endif // FULL_SYSTEM diff --git a/src/cpu/o3/base_dyn_inst.cc b/src/cpu/o3/base_dyn_inst.cc new file mode 100644 index 000000000..0979c5c8f --- /dev/null +++ b/src/cpu/o3/base_dyn_inst.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/base_dyn_inst_impl.hh" +#include "cpu/o3/isa_specific.hh" + +// Explicit instantiation +template class BaseDynInst<O3CPUImpl>; + +template <> +int +BaseDynInst<O3CPUImpl>::instcount = 0; diff --git a/src/cpu/o3/bpred_unit.cc b/src/cpu/o3/bpred_unit.cc index 85bd6f0a6..08fd4e8ea 100644 --- a/src/cpu/o3/bpred_unit.cc +++ b/src/cpu/o3/bpred_unit.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,10 +24,11 @@ * 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. + * + * Authors: Kevin Lim */ #include "cpu/o3/bpred_unit_impl.hh" -#include "cpu/o3/alpha_impl.hh" -#include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/o3/isa_specific.hh" -template class TwobitBPredUnit<AlphaSimpleImpl>; +template class BPredUnit<O3CPUImpl>; diff --git a/src/cpu/o3/bpred_unit.hh b/src/cpu/o3/bpred_unit.hh index 2725684f7..2c0a39565 100644 --- a/src/cpu/o3/bpred_unit.hh +++ b/src/cpu/o3/bpred_unit.hh @@ -24,10 +24,12 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __BPRED_UNIT_HH__ -#define __BPRED_UNIT_HH__ +#ifndef __CPU_O3_BPRED_UNIT_HH__ +#define __CPU_O3_BPRED_UNIT_HH__ // For Addr type. #include "arch/isa_traits.hh" @@ -35,99 +37,220 @@ #include "cpu/inst_seq.hh" #include "cpu/o3/2bit_local_pred.hh" -#include "cpu/o3/tournament_pred.hh" #include "cpu/o3/btb.hh" #include "cpu/o3/ras.hh" +#include "cpu/o3/tournament_pred.hh" #include <list> /** * Basically a wrapper class to hold both the branch predictor - * and the BTB. Right now I'm unsure of the implementation; it would - * be nicer to have something closer to the CPUPolicy or the Impl where - * this is just typedefs, but it forces the upper level stages to be - * aware of the constructors of the BP and the BTB. The nicer thing - * to do is have this templated on the Impl, accept the usual Params - * object, and be able to call the constructors on the BP and BTB. + * and the BTB. */ template<class Impl> -class TwobitBPredUnit +class BPredUnit { - public: + private: typedef typename Impl::Params Params; typedef typename Impl::DynInstPtr DynInstPtr; - TwobitBPredUnit(Params ¶ms); + enum PredType { + Local, + Tournament + }; + + PredType predictor; + public: + + /** + * @param params The params object, that has the size of the BP and BTB. + */ + BPredUnit(Params *params); + + /** + * Registers statistics. + */ void regStats(); - bool predict(DynInstPtr &inst, Addr &PC); + void switchOut(); + + void takeOverFrom(); + + /** + * Predicts whether or not the instruction is a taken branch, and the + * target of the branch if it is taken. + * @param inst The branch instruction. + * @param PC The predicted PC is passed back through this parameter. + * @param tid The thread id. + * @return Returns if the branch is taken or not. + */ + bool predict(DynInstPtr &inst, Addr &PC, unsigned tid); + + // @todo: Rename this function. + void BPUncond(void * &bp_history); - void update(const InstSeqNum &done_sn); + /** + * Tells the branch predictor to commit any updates until the given + * sequence number. + * @param done_sn The sequence number to commit any older updates up until. + * @param tid The thread id. + */ + void update(const InstSeqNum &done_sn, unsigned tid); - void squash(const InstSeqNum &squashed_sn); + /** + * Squashes all outstanding updates until a given sequence number. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param tid The thread id. + */ + void squash(const InstSeqNum &squashed_sn, unsigned tid); + /** + * Squashes all outstanding updates until a given sequence number, and + * corrects that sn's update with the proper address and taken/not taken. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param corr_target The correct branch target. + * @param actually_taken The correct branch direction. + * @param tid The thread id. + */ void squash(const InstSeqNum &squashed_sn, const Addr &corr_target, - bool actually_taken); + bool actually_taken, unsigned tid); - bool BPLookup(Addr &inst_PC) - { return BP.lookup(inst_PC); } + /** + * @param bp_history Pointer to the history object. The predictor + * will need to update any state and delete the object. + */ + void BPSquash(void *bp_history); + /** + * Looks up a given PC in the BP to see if it is taken or not taken. + * @param inst_PC The PC to look up. + * @param bp_history Pointer that will be set to an object that + * has the branch predictor state associated with the lookup. + * @return Whether the branch is taken or not taken. + */ + bool BPLookup(Addr &inst_PC, void * &bp_history); + + /** + * Looks up a given PC in the BTB to see if a matching entry exists. + * @param inst_PC The PC to look up. + * @return Whether the BTB contains the given PC. + */ bool BTBValid(Addr &inst_PC) - { return BTB.valid(inst_PC); } + { return BTB.valid(inst_PC, 0); } + /** + * Looks up a given PC in the BTB to get the predicted target. + * @param inst_PC The PC to look up. + * @return The address of the target of the branch. + */ Addr BTBLookup(Addr &inst_PC) - { return BTB.lookup(inst_PC); } + { return BTB.lookup(inst_PC, 0); } - // Will want to include global history. - void BPUpdate(Addr &inst_PC, bool taken) - { BP.update(inst_PC, taken); } + /** + * Updates the BP with taken/not taken information. + * @param inst_PC The branch's PC that will be updated. + * @param taken Whether the branch was taken or not taken. + * @param bp_history Pointer to the branch predictor state that is + * associated with the branch lookup that is being updated. + * @todo Make this update flexible enough to handle a global predictor. + */ + void BPUpdate(Addr &inst_PC, bool taken, void *bp_history); + /** + * Updates the BTB with the target of a branch. + * @param inst_PC The branch's PC that will be updated. + * @param target_PC The branch's target that will be added to the BTB. + */ void BTBUpdate(Addr &inst_PC, Addr &target_PC) - { BTB.update(inst_PC, target_PC); } + { BTB.update(inst_PC, target_PC,0); } + + void dump(); private: struct PredictorHistory { + /** + * Makes a predictor history struct that contains any + * information needed to update the predictor, BTB, and RAS. + */ PredictorHistory(const InstSeqNum &seq_num, const Addr &inst_PC, - const bool pred_taken) - : seqNum(seq_num), PC(inst_PC), predTaken(pred_taken), - globalHistory(0), usedRAS(0), wasCall(0), RASIndex(0), - RASTarget(0) + const bool pred_taken, void *bp_history, + const unsigned _tid) + : seqNum(seq_num), PC(inst_PC), RASTarget(0), + RASIndex(0), tid(_tid), predTaken(pred_taken), usedRAS(0), + wasCall(0), bpHistory(bp_history) { } + /** The sequence number for the predictor history entry. */ InstSeqNum seqNum; + /** The PC associated with the sequence number. */ Addr PC; - bool predTaken; + /** The RAS target (only valid if a return). */ + Addr RASTarget; - unsigned globalHistory; + /** The RAS index of the instruction (only valid if a call). */ + unsigned RASIndex; + + /** The thread id. */ + unsigned tid; + /** Whether or not it was predicted taken. */ + bool predTaken; + + /** Whether or not the RAS was used. */ bool usedRAS; + /** Whether or not the instruction was a call. */ bool wasCall; - unsigned RASIndex; - - Addr RASTarget; + /** Pointer to the history object passed back from the branch + * predictor. It is used to update or restore state of the + * branch predictor. + */ + void *bpHistory; }; - std::list<PredictorHistory> predHist; + typedef std::list<PredictorHistory> History; + + /** + * The per-thread predictor history. This is used to update the predictor + * as instructions are committed, or restore it to the proper state after + * a squash. + */ + History predHist[Impl::MaxThreads]; + + /** The local branch predictor. */ + LocalBP *localBP; - DefaultBP BP; + /** The tournament branch predictor. */ + TournamentBP *tournamentBP; + /** The BTB. */ DefaultBTB BTB; - ReturnAddrStack RAS; + /** The per-thread return address stack. */ + ReturnAddrStack RAS[Impl::MaxThreads]; + /** Stat for number of BP lookups. */ Stats::Scalar<> lookups; + /** Stat for number of conditional branches predicted. */ Stats::Scalar<> condPredicted; + /** Stat for number of conditional branches predicted incorrectly. */ Stats::Scalar<> condIncorrect; + /** Stat for number of BTB lookups. */ Stats::Scalar<> BTBLookups; + /** Stat for number of BTB hits. */ Stats::Scalar<> BTBHits; + /** Stat for number of times the BTB is correct. */ Stats::Scalar<> BTBCorrect; + /** Stat for number of times the RAS is used to get a target. */ Stats::Scalar<> usedRAS; + /** Stat for number of times the RAS is incorrect. */ Stats::Scalar<> RASIncorrect; }; -#endif // __BPRED_UNIT_HH__ +#endif // __CPU_O3_BPRED_UNIT_HH__ diff --git a/src/cpu/o3/bpred_unit_impl.hh b/src/cpu/o3/bpred_unit_impl.hh index 8d16a0cdf..0da02145b 100644 --- a/src/cpu/o3/bpred_unit_impl.hh +++ b/src/cpu/o3/bpred_unit_impl.hh @@ -24,27 +24,54 @@ * 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. + * + * Authors: Kevin Lim */ +#include <list> +#include <vector> + #include "base/trace.hh" #include "base/traceflags.hh" #include "cpu/o3/bpred_unit.hh" +using namespace std; + template<class Impl> -TwobitBPredUnit<Impl>::TwobitBPredUnit(Params ¶ms) - : BP(params.local_predictor_size, - params.local_ctr_bits, - params.instShiftAmt), - BTB(params.BTBEntries, - params.BTBTagSize, - params.instShiftAmt), - RAS(params.RASSize) +BPredUnit<Impl>::BPredUnit(Params *params) + : BTB(params->BTBEntries, + params->BTBTagSize, + params->instShiftAmt) { + // Setup the selected predictor. + if (params->predType == "local") { + localBP = new LocalBP(params->localPredictorSize, + params->localCtrBits, + params->instShiftAmt); + predictor = Local; + } else if (params->predType == "tournament") { + tournamentBP = new TournamentBP(params->localPredictorSize, + params->localCtrBits, + params->localHistoryTableSize, + params->localHistoryBits, + params->globalPredictorSize, + params->globalHistoryBits, + params->globalCtrBits, + params->choicePredictorSize, + params->choiceCtrBits, + params->instShiftAmt); + predictor = Tournament; + } else { + fatal("Invalid BP selected!"); + } + + for (int i=0; i < Impl::MaxThreads; i++) + RAS[i].init(params->RASSize); } template <class Impl> void -TwobitBPredUnit<Impl>::regStats() +BPredUnit<Impl>::regStats() { lookups .name(name() + ".BPredUnit.lookups") @@ -79,7 +106,7 @@ TwobitBPredUnit<Impl>::regStats() usedRAS .name(name() + ".BPredUnit.usedRAS") - .desc("Number of times the RAS was used.") + .desc("Number of times the RAS was used to get a target.") ; RASIncorrect @@ -89,12 +116,36 @@ TwobitBPredUnit<Impl>::regStats() } template <class Impl> +void +BPredUnit<Impl>::switchOut() +{ + // Clear any state upon switch out. + for (int i = 0; i < Impl::MaxThreads; ++i) { + squash(0, i); + } +} + +template <class Impl> +void +BPredUnit<Impl>::takeOverFrom() +{ + // Can reset all predictor state, but it's not necessarily better + // than leaving it be. +/* + for (int i = 0; i < Impl::MaxThreads; ++i) + RAS[i].reset(); + + BP.reset(); + BTB.reset(); +*/ +} + +template <class Impl> bool -TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC) +BPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC, unsigned tid) { // See if branch predictor predicts taken. // If so, get its target addr either from the BTB or the RAS. - // Once that's done, speculatively update the predictor? // Save off record of branch stuff so the RAS can be fixed // up once it's done. @@ -105,19 +156,25 @@ TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC) ++lookups; + void *bp_history = NULL; + if (inst->isUncondCtrl()) { - DPRINTF(Fetch, "BranchPred: Unconditional control.\n"); + DPRINTF(Fetch, "BranchPred: [tid:%i] Unconditional control.\n", tid); pred_taken = true; + // Tell the BP there was an unconditional branch. + BPUncond(bp_history); } else { ++condPredicted; - pred_taken = BPLookup(PC); + pred_taken = BPLookup(PC, bp_history); - DPRINTF(Fetch, "BranchPred: Branch predictor predicted %i for PC %#x" - "\n", pred_taken, inst->readPC()); + DPRINTF(Fetch, "BranchPred: [tid:%i]: Branch predictor predicted %i " + "for PC %#x\n", + tid, pred_taken, inst->readPC()); } - PredictorHistory predict_record(inst->seqNum, PC, pred_taken); + PredictorHistory predict_record(inst->seqNum, PC, pred_taken, + bp_history, tid); // Now lookup in the BTB or RAS. if (pred_taken) { @@ -126,45 +183,48 @@ TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC) // If it's a function return call, then look up the address // in the RAS. - target = RAS.top(); + target = RAS[tid].top(); // Record the top entry of the RAS, and its index. predict_record.usedRAS = true; - predict_record.RASIndex = RAS.topIdx(); + predict_record.RASIndex = RAS[tid].topIdx(); predict_record.RASTarget = target; - RAS.pop(); + assert(predict_record.RASIndex < 16); - DPRINTF(Fetch, "BranchPred: Instruction %#x is a return, RAS " - "predicted target: %#x, RAS index: %i.\n", - inst->readPC(), target, predict_record.RASIndex); + RAS[tid].pop(); + + DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x is a return, " + "RAS predicted target: %#x, RAS index: %i.\n", + tid, inst->readPC(), target, predict_record.RASIndex); } else { ++BTBLookups; if (inst->isCall()) { - RAS.push(PC+sizeof(MachInst)); + RAS[tid].push(PC + sizeof(MachInst)); // Record that it was a call so that the top RAS entry can // be popped off if the speculation is incorrect. predict_record.wasCall = true; - DPRINTF(Fetch, "BranchPred: Instruction %#x was a call, " - "adding %#x to the RAS.\n", - inst->readPC(), PC+sizeof(MachInst)); + DPRINTF(Fetch, "BranchPred: [tid:%i] Instruction %#x was a call" + ", adding %#x to the RAS.\n", + tid, inst->readPC(), PC + sizeof(MachInst)); } - if (BTB.valid(PC)) { + if (BTB.valid(PC, tid)) { ++BTBHits; - //If it's anything else, use the BTB to get the target addr. - target = BTB.lookup(PC); + // If it's not a return, use the BTB to get the target addr. + target = BTB.lookup(PC, tid); - DPRINTF(Fetch, "BranchPred: Instruction %#x predicted target " - "is %#x.\n", inst->readPC(), target); + DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x predicted" + " target is %#x.\n", + tid, inst->readPC(), target); } else { - DPRINTF(Fetch, "BranchPred: BTB doesn't have a valid entry." - "\n"); + DPRINTF(Fetch, "BranchPred: [tid:%i]: BTB doesn't have a " + "valid entry.\n",tid); pred_taken = false; } @@ -180,97 +240,173 @@ TwobitBPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC) inst->setPredTarg(PC); } - predHist.push_front(predict_record); + predHist[tid].push_front(predict_record); - assert(!predHist.empty()); + DPRINTF(Fetch, "[tid:%i] predHist.size(): %i\n", tid, predHist[tid].size()); return pred_taken; } template <class Impl> void -TwobitBPredUnit<Impl>::update(const InstSeqNum &done_sn) +BPredUnit<Impl>::update(const InstSeqNum &done_sn, unsigned tid) { - DPRINTF(Fetch, "BranchPred: Commiting branches until sequence number " - "%i.\n", done_sn); - - while (!predHist.empty() && predHist.back().seqNum <= done_sn) { - assert(!predHist.empty()); + DPRINTF(Fetch, "BranchPred: [tid:%i]: Commiting branches until sequence" + "number %lli.\n", tid, done_sn); - // Update the branch predictor with the correct results of branches. - BP.update(predHist.back().PC, predHist.back().predTaken); + while (!predHist[tid].empty() && + predHist[tid].back().seqNum <= done_sn) { + // Update the branch predictor with the correct results. + BPUpdate(predHist[tid].back().PC, + predHist[tid].back().predTaken, + predHist[tid].back().bpHistory); - predHist.pop_back(); + predHist[tid].pop_back(); } } template <class Impl> void -TwobitBPredUnit<Impl>::squash(const InstSeqNum &squashed_sn) +BPredUnit<Impl>::squash(const InstSeqNum &squashed_sn, unsigned tid) { - while (!predHist.empty() && predHist.front().seqNum > squashed_sn) { - if (predHist.front().usedRAS) { - DPRINTF(Fetch, "BranchPred: Restoring top of RAS to: %i, " - "target: %#x.\n", - predHist.front().RASIndex, - predHist.front().RASTarget); - - RAS.restore(predHist.front().RASIndex, - predHist.front().RASTarget); - } else if (predHist.front().wasCall) { - DPRINTF(Fetch, "BranchPred: Removing speculative entry added " - "to the RAS.\n"); - - RAS.pop(); + History &pred_hist = predHist[tid]; + + while (!pred_hist.empty() && + pred_hist.front().seqNum > squashed_sn) { + if (pred_hist.front().usedRAS) { + DPRINTF(Fetch, "BranchPred: [tid:%i]: Restoring top of RAS to: %i," + " target: %#x.\n", + tid, + pred_hist.front().RASIndex, + pred_hist.front().RASTarget); + + RAS[tid].restore(pred_hist.front().RASIndex, + pred_hist.front().RASTarget); + + } else if (pred_hist.front().wasCall) { + DPRINTF(Fetch, "BranchPred: [tid:%i]: Removing speculative entry " + "added to the RAS.\n",tid); + + RAS[tid].pop(); } - predHist.pop_front(); + // This call should delete the bpHistory. + BPSquash(pred_hist.front().bpHistory); + + pred_hist.pop_front(); } + } template <class Impl> void -TwobitBPredUnit<Impl>::squash(const InstSeqNum &squashed_sn, - const Addr &corr_target, - const bool actually_taken) +BPredUnit<Impl>::squash(const InstSeqNum &squashed_sn, + const Addr &corr_target, + const bool actually_taken, + unsigned tid) { // Now that we know that a branch was mispredicted, we need to undo // all the branches that have been seen up until this branch and // fix up everything. + History &pred_hist = predHist[tid]; + ++condIncorrect; - DPRINTF(Fetch, "BranchPred: Squashing from sequence number %i, " + DPRINTF(Fetch, "BranchPred: [tid:%i]: Squashing from sequence number %i, " "setting target to %#x.\n", - squashed_sn, corr_target); + tid, squashed_sn, corr_target); - while (!predHist.empty() && predHist.front().seqNum > squashed_sn) { + squash(squashed_sn, tid); - if (predHist.front().usedRAS) { - DPRINTF(Fetch, "BranchPred: Restoring top of RAS to: %i, " - "target: %#x.\n", - predHist.front().RASIndex, - predHist.front().RASTarget); + // If there's a squash due to a syscall, there may not be an entry + // corresponding to the squash. In that case, don't bother trying to + // fix up the entry. + if (!pred_hist.empty()) { + assert(pred_hist.front().seqNum == squashed_sn); + if (pred_hist.front().usedRAS) { + ++RASIncorrect; + } - RAS.restore(predHist.front().RASIndex, - predHist.front().RASTarget); - } else if (predHist.front().wasCall) { - DPRINTF(Fetch, "BranchPred: Removing speculative entry added " - "to the RAS.\n"); + BPUpdate(pred_hist.front().PC, actually_taken, + pred_hist.front().bpHistory); - RAS.pop(); - } + BTB.update(pred_hist.front().PC, corr_target, tid); + pred_hist.pop_front(); + } +} + +template <class Impl> +void +BPredUnit<Impl>::BPUncond(void * &bp_history) +{ + // Only the tournament predictor cares about unconditional branches. + if (predictor == Tournament) { + tournamentBP->uncondBr(bp_history); + } +} - predHist.pop_front(); +template <class Impl> +void +BPredUnit<Impl>::BPSquash(void *bp_history) +{ + if (predictor == Local) { + localBP->squash(bp_history); + } else if (predictor == Tournament) { + tournamentBP->squash(bp_history); + } else { + panic("Predictor type is unexpected value!"); } +} - predHist.front().predTaken = actually_taken; +template <class Impl> +bool +BPredUnit<Impl>::BPLookup(Addr &inst_PC, void * &bp_history) +{ + if (predictor == Local) { + return localBP->lookup(inst_PC, bp_history); + } else if (predictor == Tournament) { + return tournamentBP->lookup(inst_PC, bp_history); + } else { + panic("Predictor type is unexpected value!"); + } +} - if (predHist.front().usedRAS) { - ++RASIncorrect; +template <class Impl> +void +BPredUnit<Impl>::BPUpdate(Addr &inst_PC, bool taken, void *bp_history) +{ + if (predictor == Local) { + localBP->update(inst_PC, taken, bp_history); + } else if (predictor == Tournament) { + tournamentBP->update(inst_PC, taken, bp_history); + } else { + panic("Predictor type is unexpected value!"); } +} + +template <class Impl> +void +BPredUnit<Impl>::dump() +{ + typename History::iterator pred_hist_it; + + for (int i = 0; i < Impl::MaxThreads; ++i) { + if (!predHist[i].empty()) { + pred_hist_it = predHist[i].begin(); - BP.update(predHist.front().PC, actually_taken); + cprintf("predHist[%i].size(): %i\n", i, predHist[i].size()); - BTB.update(predHist.front().PC, corr_target); + while (pred_hist_it != predHist[i].end()) { + cprintf("[sn:%lli], PC:%#x, tid:%i, predTaken:%i, " + "bpHistory:%#x\n", + (*pred_hist_it).seqNum, (*pred_hist_it).PC, + (*pred_hist_it).tid, (*pred_hist_it).predTaken, + (*pred_hist_it).bpHistory); + pred_hist_it++; + } + + cprintf("\n"); + } + } } diff --git a/src/cpu/o3/btb.cc b/src/cpu/o3/btb.cc index 2d39c3856..01640f4d1 100644 --- a/src/cpu/o3/btb.cc +++ b/src/cpu/o3/btb.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Kevin Lim */ #include "base/intmath.hh" @@ -39,14 +41,15 @@ DefaultBTB::DefaultBTB(unsigned _numEntries, tagBits(_tagBits), instShiftAmt(_instShiftAmt) { - // @todo Check to make sure num_entries is valid (a power of 2) - DPRINTF(Fetch, "BTB: Creating BTB object.\n"); - btb = new BTBEntry[numEntries]; + if (!isPowerOf2(numEntries)) { + fatal("BTB entries is not a power of 2!"); + } - for (int i = 0; i < numEntries; ++i) - { + btb.resize(numEntries); + + for (int i = 0; i < numEntries; ++i) { btb[i].valid = false; } @@ -57,6 +60,14 @@ DefaultBTB::DefaultBTB(unsigned _numEntries, tagShiftAmt = instShiftAmt + floorLog2(numEntries); } +void +DefaultBTB::reset() +{ + for (int i = 0; i < numEntries; ++i) { + btb[i].valid = false; + } +} + inline unsigned DefaultBTB::getIndex(const Addr &inst_PC) @@ -73,7 +84,7 @@ DefaultBTB::getTag(const Addr &inst_PC) } bool -DefaultBTB::valid(const Addr &inst_PC) +DefaultBTB::valid(const Addr &inst_PC, unsigned tid) { unsigned btb_idx = getIndex(inst_PC); @@ -81,7 +92,9 @@ DefaultBTB::valid(const Addr &inst_PC) assert(btb_idx < numEntries); - if (btb[btb_idx].valid && inst_tag == btb[btb_idx].tag) { + if (btb[btb_idx].valid + && inst_tag == btb[btb_idx].tag + && btb[btb_idx].tid == tid) { return true; } else { return false; @@ -92,7 +105,7 @@ DefaultBTB::valid(const Addr &inst_PC) // address is valid, and also the address. For now will just use addr = 0 to // represent invalid entry. Addr -DefaultBTB::lookup(const Addr &inst_PC) +DefaultBTB::lookup(const Addr &inst_PC, unsigned tid) { unsigned btb_idx = getIndex(inst_PC); @@ -100,7 +113,9 @@ DefaultBTB::lookup(const Addr &inst_PC) assert(btb_idx < numEntries); - if (btb[btb_idx].valid && inst_tag == btb[btb_idx].tag) { + if (btb[btb_idx].valid + && inst_tag == btb[btb_idx].tag + && btb[btb_idx].tid == tid) { return btb[btb_idx].target; } else { return 0; @@ -108,12 +123,13 @@ DefaultBTB::lookup(const Addr &inst_PC) } void -DefaultBTB::update(const Addr &inst_PC, const Addr &target) +DefaultBTB::update(const Addr &inst_PC, const Addr &target, unsigned tid) { unsigned btb_idx = getIndex(inst_PC); assert(btb_idx < numEntries); + btb[btb_idx].tid = tid; btb[btb_idx].valid = true; btb[btb_idx].target = target; btb[btb_idx].tag = getTag(inst_PC); diff --git a/src/cpu/o3/btb.hh b/src/cpu/o3/btb.hh index 77bdc32ea..dfa3b7b06 100644 --- a/src/cpu/o3/btb.hh +++ b/src/cpu/o3/btb.hh @@ -24,13 +24,16 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_BTB_HH__ -#define __CPU_O3_CPU_BTB_HH__ +#ifndef __CPU_O3_BTB_HH__ +#define __CPU_O3_BTB_HH__ // For Addr type. #include "arch/isa_traits.hh" +#include "base/misc.hh" class DefaultBTB { @@ -42,39 +45,86 @@ class DefaultBTB { } + /** The entry's tag. */ Addr tag; + + /** The entry's target. */ Addr target; + + /** The entry's thread id. */ + unsigned tid; + + /** Whether or not the entry is valid. */ bool valid; }; public: + /** Creates a BTB with the given number of entries, number of bits per + * tag, and instruction offset amount. + * @param numEntries Number of entries for the BTB. + * @param tagBits Number of bits for each tag in the BTB. + * @param instShiftAmt Offset amount for instructions to ignore alignment. + */ DefaultBTB(unsigned numEntries, unsigned tagBits, unsigned instShiftAmt); - Addr lookup(const Addr &inst_PC); - - bool valid(const Addr &inst_PC); - - void update(const Addr &inst_PC, const Addr &target_PC); + void reset(); + + /** Looks up an address in the BTB. Must call valid() first on the address. + * @param inst_PC The address of the branch to look up. + * @param tid The thread id. + * @return Returns the target of the branch. + */ + Addr lookup(const Addr &inst_PC, unsigned tid); + + /** Checks if a branch is in the BTB. + * @param inst_PC The address of the branch to look up. + * @param tid The thread id. + * @return Whether or not the branch exists in the BTB. + */ + bool valid(const Addr &inst_PC, unsigned tid); + + /** Updates the BTB with the target of a branch. + * @param inst_PC The address of the branch being updated. + * @param target_PC The target address of the branch. + * @param tid The thread id. + */ + void update(const Addr &inst_PC, const Addr &target_PC, + unsigned tid); private: + /** Returns the index into the BTB, based on the branch's PC. + * @param inst_PC The branch to look up. + * @return Returns the index into the BTB. + */ inline unsigned getIndex(const Addr &inst_PC); + /** Returns the tag bits of a given address. + * @param inst_PC The branch's address. + * @return Returns the tag bits. + */ inline Addr getTag(const Addr &inst_PC); - BTBEntry *btb; + /** The actual BTB. */ + std::vector<BTBEntry> btb; + /** The number of entries in the BTB. */ unsigned numEntries; + /** The index mask. */ unsigned idxMask; + /** The number of tag bits per entry. */ unsigned tagBits; + /** The tag mask. */ unsigned tagMask; + /** Number of bits to shift PC when calculating index. */ unsigned instShiftAmt; + /** Number of bits to shift PC when calculating tag. */ unsigned tagShiftAmt; }; -#endif // __CPU_O3_CPU_BTB_HH__ +#endif // __CPU_O3_BTB_HH__ diff --git a/src/cpu/o3/checker_builder.cc b/src/cpu/o3/checker_builder.cc new file mode 100644 index 000000000..782d963b0 --- /dev/null +++ b/src/cpu/o3/checker_builder.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu_impl.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/alpha/dyn_inst.hh" +#include "cpu/o3/alpha/impl.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class MemObject; + +template +class Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >; + +/** + * Specific non-templated derived class used for SimObject configuration. + */ +class O3Checker : public Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > > +{ + public: + O3Checker(Params *p) + : Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// CheckerCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(O3Checker) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + SimObjectParam<System *> system; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + Param<int> clock; + + Param<bool> defer_registration; + Param<bool> exitOnError; + Param<bool> warnOnlyOnLoadError; + Param<bool> function_trace; + Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(O3Checker) + +BEGIN_INIT_SIM_OBJECT_PARAMS(O3Checker) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(system, "system object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(exitOnError, "exit on error"), + INIT_PARAM_DFLT(warnOnlyOnLoadError, "warn, but don't exit, if a load " + "result errors", false), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(O3Checker) + + +CREATE_SIM_OBJECT(O3Checker) +{ + O3Checker::Params *params = new O3Checker::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = 0; + params->max_insts_all_threads = 0; + params->max_loads_any_thread = 0; + params->max_loads_all_threads = 0; + params->exitOnError = exitOnError; + params->warnOnlyOnLoadError = warnOnlyOnLoadError; + params->deferRegistration = defer_registration; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->clock = clock; + // Hack to touch all parameters. Consider not deriving Checker + // from BaseCPU..it's not really a CPU in the end. + Counter temp; + temp = max_insts_any_thread; + temp = max_insts_all_threads; + temp = max_loads_any_thread; + temp = max_loads_all_threads; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->system = system; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + O3Checker *cpu = new O3Checker(params); + return cpu; +} + +REGISTER_SIM_OBJECT("O3Checker", O3Checker) diff --git a/src/cpu/o3/comm.hh b/src/cpu/o3/comm.hh index c74c77ddf..bf1bd08e8 100644 --- a/src/cpu/o3/comm.hh +++ b/src/cpu/o3/comm.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,32 +24,41 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_COMM_HH__ -#define __CPU_O3_CPU_COMM_HH__ +#ifndef __CPU_O3_COMM_HH__ +#define __CPU_O3_COMM_HH__ #include <vector> +#include "arch/faults.hh" #include "arch/isa_traits.hh" #include "cpu/inst_seq.hh" #include "sim/host.hh" -// Find better place to put this typedef. -// The impl might be the best place for this. +// Typedef for physical register index type. Although the Impl would be the +// most likely location for this, there are a few classes that need this +// typedef yet are not templated on the Impl. For now it will be defined here. typedef short int PhysRegIndex; +/** Struct that defines the information passed from fetch to decode. */ template<class Impl> -struct SimpleFetchSimpleDecode { +struct DefaultFetchDefaultDecode { typedef typename Impl::DynInstPtr DynInstPtr; int size; DynInstPtr insts[Impl::MaxWidth]; + Fault fetchFault; + InstSeqNum fetchFaultSN; + bool clearFetchFault; }; +/** Struct that defines the information passed from decode to rename. */ template<class Impl> -struct SimpleDecodeSimpleRename { +struct DefaultDecodeDefaultRename { typedef typename Impl::DynInstPtr DynInstPtr; int size; @@ -57,8 +66,9 @@ struct SimpleDecodeSimpleRename { DynInstPtr insts[Impl::MaxWidth]; }; +/** Struct that defines the information passed from rename to IEW. */ template<class Impl> -struct SimpleRenameSimpleIEW { +struct DefaultRenameDefaultIEW { typedef typename Impl::DynInstPtr DynInstPtr; int size; @@ -66,20 +76,23 @@ struct SimpleRenameSimpleIEW { DynInstPtr insts[Impl::MaxWidth]; }; +/** Struct that defines the information passed from IEW to commit. */ template<class Impl> -struct SimpleIEWSimpleCommit { +struct DefaultIEWDefaultCommit { typedef typename Impl::DynInstPtr DynInstPtr; int size; DynInstPtr insts[Impl::MaxWidth]; - bool squash; - bool branchMispredict; - bool branchTaken; - uint64_t mispredPC; - uint64_t nextPC; - InstSeqNum squashedSeqNum; + bool squash[Impl::MaxThreads]; + bool branchMispredict[Impl::MaxThreads]; + bool branchTaken[Impl::MaxThreads]; + uint64_t mispredPC[Impl::MaxThreads]; + uint64_t nextPC[Impl::MaxThreads]; + InstSeqNum squashedSeqNum[Impl::MaxThreads]; + + bool includeSquashInst[Impl::MaxThreads]; }; template<class Impl> @@ -91,73 +104,95 @@ struct IssueStruct { DynInstPtr insts[Impl::MaxWidth]; }; +/** Struct that defines all backwards communication. */ +template<class Impl> struct TimeBufStruct { struct decodeComm { bool squash; - bool stall; bool predIncorrect; uint64_t branchAddr; InstSeqNum doneSeqNum; - // Might want to package this kind of branch stuff into a single + // @todo: Might want to package this kind of branch stuff into a single // struct as it is used pretty frequently. bool branchMispredict; bool branchTaken; uint64_t mispredPC; uint64_t nextPC; + + unsigned branchCount; }; - decodeComm decodeInfo; + decodeComm decodeInfo[Impl::MaxThreads]; - // Rename can't actually tell anything to squash or send a new PC back - // because it doesn't do anything along those lines. But maybe leave - // these fields in here to keep the stages mostly orthagonal. struct renameComm { - bool squash; - bool stall; - - uint64_t nextPC; }; - renameComm renameInfo; + renameComm renameInfo[Impl::MaxThreads]; struct iewComm { - bool stall; - // Also eventually include skid buffer space. + bool usedIQ; unsigned freeIQEntries; + bool usedLSQ; + unsigned freeLSQEntries; + + unsigned iqCount; + unsigned ldstqCount; + + unsigned dispatched; + unsigned dispatchedToLSQ; }; - iewComm iewInfo; + iewComm iewInfo[Impl::MaxThreads]; struct commitComm { - bool squash; - bool stall; + bool usedROB; unsigned freeROBEntries; + bool emptyROB; + + bool squash; + bool robSquashing; bool branchMispredict; bool branchTaken; uint64_t mispredPC; uint64_t nextPC; - bool robSquashing; - // Represents the instruction that has either been retired or // squashed. Similar to having a single bus that broadcasts the // retired or squashed sequence number. InstSeqNum doneSeqNum; - // Extra bit of information so that the LDSTQ only updates when it - // needs to. - bool commitIsLoad; + //Just in case we want to do a commit/squash on a cycle + //(necessary for multiple ROBs?) + bool commitInsts; + InstSeqNum squashSeqNum; // Communication specifically to the IQ to tell the IQ that it can // schedule a non-speculative instruction. InstSeqNum nonSpecSeqNum; + + // Hack for now to send back an uncached access to the IEW stage. + typedef typename Impl::DynInstPtr DynInstPtr; + bool uncached; + DynInstPtr uncachedLoad; + + bool interruptPending; + bool clearInterrupt; }; - commitComm commitInfo; + commitComm commitInfo[Impl::MaxThreads]; + + bool decodeBlock[Impl::MaxThreads]; + bool decodeUnblock[Impl::MaxThreads]; + bool renameBlock[Impl::MaxThreads]; + bool renameUnblock[Impl::MaxThreads]; + bool iewBlock[Impl::MaxThreads]; + bool iewUnblock[Impl::MaxThreads]; + bool commitBlock[Impl::MaxThreads]; + bool commitUnblock[Impl::MaxThreads]; }; -#endif //__CPU_O3_CPU_COMM_HH__ +#endif //__CPU_O3_COMM_HH__ diff --git a/src/cpu/o3/commit.cc b/src/cpu/o3/commit.cc index cf33d7f8b..637d59f52 100644 --- a/src/cpu/o3/commit.cc +++ b/src/cpu/o3/commit.cc @@ -24,10 +24,11 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/isa_specific.hh" #include "cpu/o3/commit_impl.hh" -template class SimpleCommit<AlphaSimpleImpl>; +template class DefaultCommit<O3CPUImpl>; diff --git a/src/cpu/o3/commit.hh b/src/cpu/o3/commit.hh index 580c1a316..60b555269 100644 --- a/src/cpu/o3/commit.hh +++ b/src/cpu/o3/commit.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,95 +24,276 @@ * 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. + * + * Authors: Kevin Lim + * Korey Sewell */ -// Todo: Maybe have a special method for handling interrupts/traps. -// -// Traps: Have IEW send a signal to commit saying that there's a trap to -// be handled. Have commit send the PC back to the fetch stage, along -// with the current commit PC. Fetch will directly access the IPR and save -// off all the proper stuff. Commit can send out a squash, or something -// close to it. -// Do the same for hwrei(). However, requires that commit be specifically -// built to support that kind of stuff. Probably not horrible to have -// commit support having the CPU tell it to squash the other stages and -// restart at a given address. The IPR register does become an issue. -// Probably not a big deal if the IPR stuff isn't cycle accurate. Can just -// have the original function handle writing to the IPR register. - -#ifndef __CPU_O3_CPU_SIMPLE_COMMIT_HH__ -#define __CPU_O3_CPU_SIMPLE_COMMIT_HH__ +#ifndef __CPU_O3_COMMIT_HH__ +#define __CPU_O3_COMMIT_HH__ +#include "arch/faults.hh" #include "base/statistics.hh" #include "base/timebuf.hh" -#include "mem/memory_interface.hh" - +#include "cpu/exetrace.hh" +#include "cpu/inst_seq.hh" + +template <class> +class O3ThreadState; + +/** + * DefaultCommit handles single threaded and SMT commit. Its width is + * specified by the parameters; each cycle it tries to commit that + * many instructions. The SMT policy decides which thread it tries to + * commit instructions from. Non- speculative instructions must reach + * the head of the ROB before they are ready to execute; once they + * reach the head, commit will broadcast the instruction's sequence + * number to the previous stages so that they can issue/ execute the + * instruction. Only one non-speculative instruction is handled per + * cycle. Commit is responsible for handling all back-end initiated + * redirects. It receives the redirect, and then broadcasts it to all + * stages, indicating the sequence number they should squash until, + * and any necessary branch misprediction information as well. It + * priortizes redirects by instruction's age, only broadcasting a + * redirect if it corresponds to an instruction that should currently + * be in the ROB. This is done by tracking the sequence number of the + * youngest instruction in the ROB, which gets updated to any + * squashing instruction's sequence number, and only broadcasting a + * redirect if it corresponds to an older instruction. Commit also + * supports multiple cycle squashing, to model a ROB that can only + * remove a certain number of instructions per cycle. + */ template<class Impl> -class SimpleCommit +class DefaultCommit { public: // Typedefs from the Impl. - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::O3CPU O3CPU; typedef typename Impl::DynInstPtr DynInstPtr; typedef typename Impl::Params Params; typedef typename Impl::CPUPol CPUPol; + typedef typename CPUPol::RenameMap RenameMap; typedef typename CPUPol::ROB ROB; typedef typename CPUPol::TimeStruct TimeStruct; + typedef typename CPUPol::FetchStruct FetchStruct; typedef typename CPUPol::IEWStruct IEWStruct; typedef typename CPUPol::RenameStruct RenameStruct; - public: - // I don't believe commit can block, so it will only have two - // statuses for now. - // Actually if there's a cache access that needs to block (ie - // uncachable load or just a mem access in commit) then the stage - // may have to wait. - enum Status { + typedef typename CPUPol::Fetch Fetch; + typedef typename CPUPol::IEW IEW; + + typedef O3ThreadState<Impl> Thread; + + /** Event class used to schedule a squash due to a trap (fault or + * interrupt) to happen on a specific cycle. + */ + class TrapEvent : public Event { + private: + DefaultCommit<Impl> *commit; + unsigned tid; + + public: + TrapEvent(DefaultCommit<Impl> *_commit, unsigned _tid); + + void process(); + const char *description(); + }; + + /** Overall commit status. Used to determine if the CPU can deschedule + * itself due to a lack of activity. + */ + enum CommitStatus{ + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { Running, Idle, ROBSquashing, - DcacheMissStall, - DcacheMissComplete + TrapPending, + FetchTrapPending + }; + + /** Commit policy for SMT mode. */ + enum CommitPolicy { + Aggressive, + RoundRobin, + OldestReady }; private: - Status _status; + /** Overall commit status. */ + CommitStatus _status; + /** Next commit status, to be set at the end of the cycle. */ + CommitStatus _nextStatus; + /** Per-thread status. */ + ThreadStatus commitStatus[Impl::MaxThreads]; + /** Commit policy used in SMT mode. */ + CommitPolicy commitPolicy; public: - SimpleCommit(Params ¶ms); + /** Construct a DefaultCommit with the given parameters. */ + DefaultCommit(Params *params); + + /** Returns the name of the DefaultCommit. */ + std::string name() const; + /** Registers statistics. */ void regStats(); - void setCPU(FullCPU *cpu_ptr); + /** Sets the CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets the list of threads. */ + void setThreads(std::vector<Thread *> &threads); + /** Sets the main time buffer pointer, used for backwards communication. */ void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr); + + /** Sets the pointer to the queue coming from rename. */ void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr); + /** Sets the pointer to the queue coming from IEW. */ void setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr); + void setFetchStage(Fetch *fetch_stage); + + Fetch *fetchStage; + + /** Sets the pointer to the IEW stage. */ + void setIEWStage(IEW *iew_stage); + + /** The pointer to the IEW stage. Used solely to ensure that + * various events (traps, interrupts, syscalls) do not occur until + * all stores have written back. + */ + IEW *iewStage; + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to the commited state rename map. */ + void setRenameMap(RenameMap rm_ptr[Impl::MaxThreads]); + + /** Sets pointer to the ROB. */ void setROB(ROB *rob_ptr); + /** Initializes stage by sending back the number of free entries. */ + void initStage(); + + /** Initializes the switching out of commit. */ + void switchOut(); + + /** Completes the switch out of commit. */ + void doSwitchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Ticks the commit stage, which tries to commit instructions. */ void tick(); + /** Handles any squashes that are sent from IEW, and adds instructions + * to the ROB and tries to commit instructions. + */ void commit(); + /** Returns the number of free ROB entries for a specific thread. */ + unsigned numROBFreeEntries(unsigned tid); + + /** Generates an event to schedule a squash due to a trap. */ + void generateTrapEvent(unsigned tid); + + /** Records that commit needs to initiate a squash due to an + * external state update through the TC. + */ + void generateTCEvent(unsigned tid); + private: + /** Updates the overall status of commit with the nextStatus, and + * tell the CPU if commit is active/inactive. + */ + void updateStatus(); + /** Sets the next status based on threads' statuses, which becomes the + * current status at the end of the cycle. + */ + void setNextStatus(); + + /** Checks if the ROB is completed with squashing. This is for the case + * where the ROB can take multiple cycles to complete squashing. + */ + bool robDoneSquashing(); + + /** Returns if any of the threads have the number of ROB entries changed + * on this cycle. Used to determine if the number of free ROB entries needs + * to be sent back to previous stages. + */ + bool changedROBEntries(); + + /** Squashes all in flight instructions. */ + void squashAll(unsigned tid); + + /** Handles squashing due to a trap. */ + void squashFromTrap(unsigned tid); + + /** Handles squashing due to an TC write. */ + void squashFromTC(unsigned tid); + + /** Commits as many instructions as possible. */ void commitInsts(); + /** Tries to commit the head ROB instruction passed in. + * @param head_inst The instruction to be committed. + */ bool commitHead(DynInstPtr &head_inst, unsigned inst_num); + /** Gets instructions from rename and inserts them into the ROB. */ void getInsts(); + /** Marks completed instructions using information sent from IEW. */ void markCompletedInsts(); + /** Gets the thread to commit, based on the SMT policy. */ + int getCommittingThread(); + + /** Returns the thread ID to use based on a round robin policy. */ + int roundRobin(); + + /** Returns the thread ID to use based on an oldest instruction policy. */ + int oldestReady(); + public: - uint64_t readCommitPC(); + /** Returns the PC of the head instruction of the ROB. + * @todo: Probably remove this function as it returns only thread 0. + */ + uint64_t readPC() { return PC[0]; } + + /** Returns the PC of a specific thread. */ + uint64_t readPC(unsigned tid) { return PC[tid]; } + + /** Sets the PC of a specific thread. */ + void setPC(uint64_t val, unsigned tid) { PC[tid] = val; } + + /** Reads the next PC of a specific thread. */ + uint64_t readNextPC(unsigned tid) { return nextPC[tid]; } - void setSquashing() { _status = ROBSquashing; } + /** Sets the next PC of a specific thread. */ + void setNextPC(uint64_t val, unsigned tid) { nextPC[tid] = val; } + +#if THE_ISA != ALPHA_ISA + /** Reads the next NPC of a specific thread. */ + uint64_t readNextPC(unsigned tid) { return nextNPC[tid]; } + + /** Sets the next NPC of a specific thread. */ + void setNextPC(uint64_t val, unsigned tid) { nextNPC[tid] = val; } +#endif private: /** Time buffer interface. */ @@ -124,6 +305,10 @@ class SimpleCommit /** Wire to read information from IEW (for ROB). */ typename TimeBuffer<TimeStruct>::wire robInfoFromIEW; + TimeBuffer<FetchStruct> *fetchQueue; + + typename TimeBuffer<FetchStruct>::wire fromFetch; + /** IEW instruction queue interface. */ TimeBuffer<IEWStruct> *iewQueue; @@ -136,45 +321,143 @@ class SimpleCommit /** Wire to read information from rename queue. */ typename TimeBuffer<RenameStruct>::wire fromRename; + public: /** ROB interface. */ ROB *rob; - /** Pointer to FullCPU. */ - FullCPU *cpu; + private: + /** Pointer to O3CPU. */ + O3CPU *cpu; + + /** Vector of all of the threads. */ + std::vector<Thread *> thread; - /** Memory interface. Used for d-cache accesses. */ - MemInterface *dcacheInterface; + Fault fetchFault; + + int fetchTrapWait; + + /** Records that commit has written to the time buffer this cycle. Used for + * the CPU to determine if it can deschedule itself if there is no activity. + */ + bool wroteToTimeBuffer; + + /** Records if the number of ROB entries has changed this cycle. If it has, + * then the number of free entries must be re-broadcast. + */ + bool changedROBNumEntries[Impl::MaxThreads]; + + /** A counter of how many threads are currently squashing. */ + int squashCounter; + + /** Records if a thread has to squash this cycle due to a trap. */ + bool trapSquash[Impl::MaxThreads]; + + /** Records if a thread has to squash this cycle due to an XC write. */ + bool tcSquash[Impl::MaxThreads]; + + /** Priority List used for Commit Policy */ + std::list<unsigned> priority_list; - private: /** IEW to Commit delay, in ticks. */ unsigned iewToCommitDelay; + /** Commit to IEW delay, in ticks. */ + unsigned commitToIEWDelay; + /** Rename to ROB delay, in ticks. */ unsigned renameToROBDelay; + unsigned fetchToCommitDelay; + /** Rename width, in instructions. Used so ROB knows how many * instructions to get from the rename instruction queue. */ unsigned renameWidth; - /** IEW width, in instructions. Used so ROB knows how many - * instructions to get from the IEW instruction queue. - */ - unsigned iewWidth; - /** Commit width, in instructions. */ unsigned commitWidth; + /** Number of Reorder Buffers */ + unsigned numRobs; + + /** Number of Active Threads */ + unsigned numThreads; + + /** Is a switch out pending. */ + bool switchPending; + + /** Is commit switched out. */ + bool switchedOut; + + /** The latency to handle a trap. Used when scheduling trap + * squash event. + */ + Tick trapLatency; + + Tick fetchTrapLatency; + + Tick fetchFaultTick; + + /** The commit PC of each thread. Refers to the instruction that + * is currently being processed/committed. + */ + Addr PC[Impl::MaxThreads]; + + /** The next PC of each thread. */ + Addr nextPC[Impl::MaxThreads]; + +#if THE_ISA != ALPHA_ISA + /** The next NPC of each thread. */ + Addr nextNPC[Impl::MaxThreads]; +#endif + + /** The sequence number of the youngest valid instruction in the ROB. */ + InstSeqNum youngestSeqNum[Impl::MaxThreads]; + + /** Pointer to the list of active threads. */ + std::list<unsigned> *activeThreads; + + /** Rename map interface. */ + RenameMap *renameMap[Impl::MaxThreads]; + + /** Updates commit stats based on this instruction. */ + void updateComInstStats(DynInstPtr &inst); + + /** Stat for the total number of committed instructions. */ Stats::Scalar<> commitCommittedInsts; + /** Stat for the total number of squashed instructions discarded by commit. + */ Stats::Scalar<> commitSquashedInsts; + /** Stat for the total number of times commit is told to squash. + * @todo: Actually increment this stat. + */ Stats::Scalar<> commitSquashEvents; + /** Stat for the total number of times commit has had to stall due to a non- + * speculative instruction reaching the head of the ROB. + */ Stats::Scalar<> commitNonSpecStalls; - Stats::Scalar<> commitCommittedBranches; - Stats::Scalar<> commitCommittedLoads; - Stats::Scalar<> commitCommittedMemRefs; + /** Stat for the total number of branch mispredicts that caused a squash. */ Stats::Scalar<> branchMispredicts; - - Stats::Distribution<> n_committed_dist; + /** Distribution of the number of committed instructions each cycle. */ + Stats::Distribution<> numCommittedDist; + + /** Total number of instructions committed. */ + Stats::Vector<> statComInst; + /** Total number of software prefetches committed. */ + Stats::Vector<> statComSwp; + /** Stat for the total number of committed memory references. */ + Stats::Vector<> statComRefs; + /** Stat for the total number of committed loads. */ + Stats::Vector<> statComLoads; + /** Total number of committed memory barriers. */ + Stats::Vector<> statComMembars; + /** Total number of committed branches. */ + Stats::Vector<> statComBranches; + + /** Number of cycles where the commit bandwidth limit is reached. */ + Stats::Scalar<> commitEligibleSamples; + /** Number of instructions not committed due to bandwidth limits. */ + Stats::Vector<> commitEligible; }; -#endif // __CPU_O3_CPU_SIMPLE_COMMIT_HH__ +#endif // __CPU_O3_COMMIT_HH__ diff --git a/src/cpu/o3/commit_impl.hh b/src/cpu/o3/commit_impl.hh index e289bc0c0..06b8e8a95 100644 --- a/src/cpu/o3/commit_impl.hh +++ b/src/cpu/o3/commit_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,28 +24,122 @@ * 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. + * + * Authors: Kevin Lim */ +#include "config/full_system.hh" +#include "config/use_checker.hh" + +#include <algorithm> +#include <string> + +#include "base/loader/symtab.hh" #include "base/timebuf.hh" -#include "cpu/o3/commit.hh" #include "cpu/exetrace.hh" +#include "cpu/o3/commit.hh" +#include "cpu/o3/thread_state.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + +using namespace std; template <class Impl> -SimpleCommit<Impl>::SimpleCommit(Params ¶ms) - : dcacheInterface(params.dcacheInterface), - iewToCommitDelay(params.iewToCommitDelay), - renameToROBDelay(params.renameToROBDelay), - renameWidth(params.renameWidth), - iewWidth(params.executeWidth), - commitWidth(params.commitWidth) +DefaultCommit<Impl>::TrapEvent::TrapEvent(DefaultCommit<Impl> *_commit, + unsigned _tid) + : Event(&mainEventQueue, CPU_Tick_Pri), commit(_commit), tid(_tid) { - _status = Idle; + this->setFlags(Event::AutoDelete); } template <class Impl> void -SimpleCommit<Impl>::regStats() +DefaultCommit<Impl>::TrapEvent::process() +{ + // This will get reset by commit if it was switched out at the + // time of this event processing. + commit->trapSquash[tid] = true; +} + +template <class Impl> +const char * +DefaultCommit<Impl>::TrapEvent::description() { + return "Trap event"; +} + +template <class Impl> +DefaultCommit<Impl>::DefaultCommit(Params *params) + : squashCounter(0), + iewToCommitDelay(params->iewToCommitDelay), + commitToIEWDelay(params->commitToIEWDelay), + renameToROBDelay(params->renameToROBDelay), + fetchToCommitDelay(params->commitToFetchDelay), + renameWidth(params->renameWidth), + commitWidth(params->commitWidth), + numThreads(params->numberOfThreads), + switchPending(false), + switchedOut(false), + trapLatency(params->trapLatency), + fetchTrapLatency(params->fetchTrapLatency) +{ + _status = Active; + _nextStatus = Inactive; + string policy = params->smtCommitPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Assign commit policy + if (policy == "aggressive"){ + commitPolicy = Aggressive; + + DPRINTF(Commit,"Commit Policy set to Aggressive."); + } else if (policy == "roundrobin"){ + commitPolicy = RoundRobin; + + //Set-Up Priority List + for (int tid=0; tid < numThreads; tid++) { + priority_list.push_back(tid); + } + + DPRINTF(Commit,"Commit Policy set to Round Robin."); + } else if (policy == "oldestready"){ + commitPolicy = OldestReady; + + DPRINTF(Commit,"Commit Policy set to Oldest Ready."); + } else { + assert(0 && "Invalid SMT Commit Policy. Options Are: {Aggressive," + "RoundRobin,OldestReady}"); + } + + for (int i=0; i < numThreads; i++) { + commitStatus[i] = Idle; + changedROBNumEntries[i] = false; + trapSquash[i] = false; + tcSquash[i] = false; + PC[i] = nextPC[i] = 0; + } + + fetchFaultTick = 0; + fetchTrapWait = 0; +} + +template <class Impl> +std::string +DefaultCommit<Impl>::name() const +{ + return cpu->name() + ".commit"; +} + +template <class Impl> +void +DefaultCommit<Impl>::regStats() +{ + using namespace Stats; commitCommittedInsts .name(name() + ".commitCommittedInsts") .desc("The number of committed instructions") @@ -63,41 +157,97 @@ SimpleCommit<Impl>::regStats() .desc("The number of times commit has been forced to stall to " "communicate backwards") .prereq(commitNonSpecStalls); - commitCommittedBranches - .name(name() + ".commitCommittedBranches") - .desc("The number of committed branches") - .prereq(commitCommittedBranches); - commitCommittedLoads - .name(name() + ".commitCommittedLoads") - .desc("The number of committed loads") - .prereq(commitCommittedLoads); - commitCommittedMemRefs - .name(name() + ".commitCommittedMemRefs") - .desc("The number of committed memory references") - .prereq(commitCommittedMemRefs); branchMispredicts .name(name() + ".branchMispredicts") .desc("The number of times a branch was mispredicted") .prereq(branchMispredicts); - n_committed_dist + numCommittedDist .init(0,commitWidth,1) .name(name() + ".COM:committed_per_cycle") .desc("Number of insts commited each cycle") .flags(Stats::pdf) ; + + statComInst + .init(cpu->number_of_threads) + .name(name() + ".COM:count") + .desc("Number of instructions committed") + .flags(total) + ; + + statComSwp + .init(cpu->number_of_threads) + .name(name() + ".COM:swp_count") + .desc("Number of s/w prefetches committed") + .flags(total) + ; + + statComRefs + .init(cpu->number_of_threads) + .name(name() + ".COM:refs") + .desc("Number of memory references committed") + .flags(total) + ; + + statComLoads + .init(cpu->number_of_threads) + .name(name() + ".COM:loads") + .desc("Number of loads committed") + .flags(total) + ; + + statComMembars + .init(cpu->number_of_threads) + .name(name() + ".COM:membars") + .desc("Number of memory barriers committed") + .flags(total) + ; + + statComBranches + .init(cpu->number_of_threads) + .name(name() + ".COM:branches") + .desc("Number of branches committed") + .flags(total) + ; + + commitEligible + .init(cpu->number_of_threads) + .name(name() + ".COM:bw_limited") + .desc("number of insts not committed due to BW limits") + .flags(total) + ; + + commitEligibleSamples + .name(name() + ".COM:bw_lim_events") + .desc("number cycles where commit BW limit reached") + ; } template <class Impl> void -SimpleCommit<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultCommit<Impl>::setCPU(O3CPU *cpu_ptr) { DPRINTF(Commit, "Commit: Setting CPU pointer.\n"); cpu = cpu_ptr; + + // Commit must broadcast the number of free entries it has at the start of + // the simulation, so it starts as active. + cpu->activateStage(O3CPU::CommitIdx); + + trapLatency = cpu->cycles(trapLatency); + fetchTrapLatency = cpu->cycles(fetchTrapLatency); } template <class Impl> void -SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +DefaultCommit<Impl>::setThreads(vector<Thread *> &threads) +{ + thread = threads; +} + +template <class Impl> +void +DefaultCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) { DPRINTF(Commit, "Commit: Setting time buffer pointer.\n"); timeBuffer = tb_ptr; @@ -111,7 +261,18 @@ SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) template <class Impl> void -SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) +DefaultCommit<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) +{ + DPRINTF(Commit, "Commit: Setting fetch queue pointer.\n"); + fetchQueue = fq_ptr; + + // Setup wire to get instructions from rename (for the ROB). + fromFetch = fetchQueue->getWire(-fetchToCommitDelay); +} + +template <class Impl> +void +DefaultCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) { DPRINTF(Commit, "Commit: Setting rename queue pointer.\n"); renameQueue = rq_ptr; @@ -122,7 +283,7 @@ SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) template <class Impl> void -SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) +DefaultCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) { DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n"); iewQueue = iq_ptr; @@ -133,7 +294,40 @@ SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) template <class Impl> void -SimpleCommit<Impl>::setROB(ROB *rob_ptr) +DefaultCommit<Impl>::setFetchStage(Fetch *fetch_stage) +{ + fetchStage = fetch_stage; +} + +template <class Impl> +void +DefaultCommit<Impl>::setIEWStage(IEW *iew_stage) +{ + iewStage = iew_stage; +} + +template<class Impl> +void +DefaultCommit<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(Commit, "Commit: Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template <class Impl> +void +DefaultCommit<Impl>::setRenameMap(RenameMap rm_ptr[]) +{ + DPRINTF(Commit, "Setting rename map pointers.\n"); + + for (int i=0; i < numThreads; i++) { + renameMap[i] = &rm_ptr[i]; + } +} + +template <class Impl> +void +DefaultCommit<Impl>::setROB(ROB *rob_ptr) { DPRINTF(Commit, "Commit: Setting ROB pointer.\n"); rob = rob_ptr; @@ -141,107 +335,425 @@ SimpleCommit<Impl>::setROB(ROB *rob_ptr) template <class Impl> void -SimpleCommit<Impl>::tick() -{ - // If the ROB is currently in its squash sequence, then continue - // to squash. In this case, commit does not do anything. Otherwise - // run commit. - if (_status == ROBSquashing) { - if (rob->isDoneSquashing()) { - _status = Running; - } else { - rob->doSquash(); - - // Send back sequence number of tail of ROB, so other stages - // can squash younger instructions. Note that really the only - // stage that this is important for is the IEW stage; other - // stages can just clear all their state as long as selective - // replay isn't used. - toIEW->commitInfo.doneSeqNum = rob->readTailSeqNum(); - toIEW->commitInfo.robSquashing = true; +DefaultCommit<Impl>::initStage() +{ + rob->setActiveThreads(activeThreads); + rob->resetEntries(); + + // Broadcast the number of free entries. + for (int i=0; i < numThreads; i++) { + toIEW->commitInfo[i].usedROB = true; + toIEW->commitInfo[i].freeROBEntries = rob->numFreeEntries(i); + } + + cpu->activityThisCycle(); +} + +template <class Impl> +void +DefaultCommit<Impl>::switchOut() +{ + switchPending = true; +} + +template <class Impl> +void +DefaultCommit<Impl>::doSwitchOut() +{ + switchedOut = true; + switchPending = false; + rob->switchOut(); +} + +template <class Impl> +void +DefaultCommit<Impl>::takeOverFrom() +{ + switchedOut = false; + _status = Active; + _nextStatus = Inactive; + for (int i=0; i < numThreads; i++) { + commitStatus[i] = Idle; + changedROBNumEntries[i] = false; + trapSquash[i] = false; + tcSquash[i] = false; + } + squashCounter = 0; + rob->takeOverFrom(); +} + +template <class Impl> +void +DefaultCommit<Impl>::updateStatus() +{ + // reset ROB changed variable + list<unsigned>::iterator threads = (*activeThreads).begin(); + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + changedROBNumEntries[tid] = false; + + // Also check if any of the threads has a trap pending + if (commitStatus[tid] == TrapPending || + commitStatus[tid] == FetchTrapPending) { + _nextStatus = Active; + } + } + + if (_nextStatus == Inactive && _status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + cpu->deactivateStage(O3CPU::CommitIdx); + } else if (_nextStatus == Active && _status == Inactive) { + DPRINTF(Activity, "Activating stage.\n"); + cpu->activateStage(O3CPU::CommitIdx); + } + + _status = _nextStatus; +} + +template <class Impl> +void +DefaultCommit<Impl>::setNextStatus() +{ + int squashes = 0; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (commitStatus[tid] == ROBSquashing) { + squashes++; + } + } + + squashCounter = squashes; + + // If commit is currently squashing, then it will have activity for the + // next cycle. Set its next status as active. + if (squashCounter) { + _nextStatus = Active; + } +} + +template <class Impl> +bool +DefaultCommit<Impl>::changedROBEntries() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (changedROBNumEntries[tid]) { + return true; } - } else { - commit(); } + return false; +} + +template <class Impl> +unsigned +DefaultCommit<Impl>::numROBFreeEntries(unsigned tid) +{ + return rob->numFreeEntries(tid); +} + +template <class Impl> +void +DefaultCommit<Impl>::generateTrapEvent(unsigned tid) +{ + DPRINTF(Commit, "Generating trap event for [tid:%i]\n", tid); + + TrapEvent *trap = new TrapEvent(this, tid); + + trap->schedule(curTick + trapLatency); + + thread[tid]->trapPending = true; +} + +template <class Impl> +void +DefaultCommit<Impl>::generateTCEvent(unsigned tid) +{ + DPRINTF(Commit, "Generating TC squash event for [tid:%i]\n", tid); + + tcSquash[tid] = true; +} + +template <class Impl> +void +DefaultCommit<Impl>::squashAll(unsigned tid) +{ + // If we want to include the squashing instruction in the squash, + // then use one older sequence number. + // Hopefully this doesn't mess things up. Basically I want to squash + // all instructions of this thread. + InstSeqNum squashed_inst = rob->isEmpty() ? + 0 : rob->readHeadInst(tid)->seqNum - 1;; + + // All younger instructions will be squashed. Set the sequence + // number as the youngest instruction in the ROB (0 in this case. + // Hopefully nothing breaks.) + youngestSeqNum[tid] = 0; + + rob->squash(squashed_inst, tid); + changedROBNumEntries[tid] = true; + + // Send back the sequence number of the squashed instruction. + toIEW->commitInfo[tid].doneSeqNum = squashed_inst; + + // Send back the squash signal to tell stages that they should + // squash. + toIEW->commitInfo[tid].squash = true; + + // Send back the rob squashing signal so other stages know that + // the ROB is in the process of squashing. + toIEW->commitInfo[tid].robSquashing = true; + + toIEW->commitInfo[tid].branchMispredict = false; + + toIEW->commitInfo[tid].nextPC = PC[tid]; +} + +template <class Impl> +void +DefaultCommit<Impl>::squashFromTrap(unsigned tid) +{ + squashAll(tid); + + DPRINTF(Commit, "Squashing from trap, restarting at PC %#x\n", PC[tid]); + + thread[tid]->trapPending = false; + thread[tid]->inSyscall = false; + + trapSquash[tid] = false; + + commitStatus[tid] = ROBSquashing; + cpu->activityThisCycle(); +} + +template <class Impl> +void +DefaultCommit<Impl>::squashFromTC(unsigned tid) +{ + squashAll(tid); + + DPRINTF(Commit, "Squashing from TC, restarting at PC %#x\n", PC[tid]); + + thread[tid]->inSyscall = false; + assert(!thread[tid]->trapPending); + + commitStatus[tid] = ROBSquashing; + cpu->activityThisCycle(); + + tcSquash[tid] = false; +} + +template <class Impl> +void +DefaultCommit<Impl>::tick() +{ + wroteToTimeBuffer = false; + _nextStatus = Inactive; + + if (switchPending && rob->isEmpty() && !iewStage->hasStoresToWB()) { + cpu->signalSwitched(); + return; + } + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + // Check if any of the threads are done squashing. Change the + // status if they are done. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (commitStatus[tid] == ROBSquashing) { + + if (rob->isDoneSquashing(tid)) { + commitStatus[tid] = Running; + } else { + DPRINTF(Commit,"[tid:%u]: Still Squashing, cannot commit any" + "insts this cycle.\n", tid); + rob->doSquash(tid); + toIEW->commitInfo[tid].robSquashing = true; + wroteToTimeBuffer = true; + } + } + } + + commit(); + markCompletedInsts(); - // Writeback number of free ROB entries here. - DPRINTF(Commit, "Commit: ROB has %d free entries.\n", - rob->numFreeEntries()); - toIEW->commitInfo.freeROBEntries = rob->numFreeEntries(); + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isEmpty(tid) && rob->readHeadInst(tid)->readyToCommit()) { + // The ROB has more instructions it can commit. Its next status + // will be active. + _nextStatus = Active; + + DynInstPtr inst = rob->readHeadInst(tid); + + DPRINTF(Commit,"[tid:%i]: Instruction [sn:%lli] PC %#x is head of" + " ROB and ready to commit\n", + tid, inst->seqNum, inst->readPC()); + + } else if (!rob->isEmpty(tid)) { + DynInstPtr inst = rob->readHeadInst(tid); + + DPRINTF(Commit,"[tid:%i]: Can't commit, Instruction [sn:%lli] PC " + "%#x is head of ROB and not ready\n", + tid, inst->seqNum, inst->readPC()); + } + + DPRINTF(Commit, "[tid:%i]: ROB has %d insts & %d free entries.\n", + tid, rob->countInsts(tid), rob->numFreeEntries(tid)); + } + + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity This Cycle.\n"); + cpu->activityThisCycle(); + } + + updateStatus(); } template <class Impl> void -SimpleCommit<Impl>::commit() +DefaultCommit<Impl>::commit() { + ////////////////////////////////////// // Check for interrupts ////////////////////////////////////// - // Process interrupts if interrupts are enabled and not in PAL mode. - // Take the PC from commit and write it to the IPR, then squash. The - // interrupt completing will take care of restoring the PC from that value - // in the IPR. Look at IPR[EXC_ADDR]; - // hwrei() is what resets the PC to the place where instruction execution - // beings again. #if FULL_SYSTEM - if (//checkInterrupts && + // Process interrupts if interrupts are enabled, not in PAL mode, + // and no other traps or external squashes are currently pending. + // @todo: Allow other threads to handle interrupts. + if (cpu->checkInterrupts && cpu->check_interrupts() && - !cpu->inPalMode(readCommitPC())) { - // Will need to squash all instructions currently in flight and have - // the interrupt handler restart at the last non-committed inst. - // Most of that can be handled through the trap() function. The - // processInterrupts() function really just checks for interrupts - // and then calls trap() if there is an interrupt present. + !cpu->inPalMode(readPC()) && + !trapSquash[0] && + !tcSquash[0]) { + // Tell fetch that there is an interrupt pending. This will + // make fetch wait until it sees a non PAL-mode PC, at which + // point it stops fetching instructions. + toIEW->commitInfo[0].interruptPending = true; + + // Wait until the ROB is empty and all stores have drained in + // order to enter the interrupt. + if (rob->isEmpty() && !iewStage->hasStoresToWB()) { + // Not sure which thread should be the one to interrupt. For now + // always do thread 0. + assert(!thread[0]->inSyscall); + thread[0]->inSyscall = true; + + // CPU will handle implementation of the interrupt. + cpu->processInterrupts(); - // CPU will handle implementation of the interrupt. - cpu->processInterrupts(); + // Now squash or record that I need to squash this cycle. + commitStatus[0] = TrapPending; + + // Exit state update mode to avoid accidental updating. + thread[0]->inSyscall = false; + + // Generate trap squash event. + generateTrapEvent(0); + + toIEW->commitInfo[0].clearInterrupt = true; + + DPRINTF(Commit, "Interrupt detected.\n"); + } else { + DPRINTF(Commit, "Interrupt pending, waiting for ROB to empty.\n"); + } } #endif // FULL_SYSTEM //////////////////////////////////// - // Check for squash signal, handle that first + // Check for any possible squashes, handle them first //////////////////////////////////// - // Want to mainly check if the IEW stage is telling the ROB to squash. - // Should I also check if the commit stage is telling the ROB to squah? - // This might be necessary to keep the same timing between the IQ and - // the ROB... - if (fromIEW->squash) { - DPRINTF(Commit, "Commit: Squashing instructions in the ROB.\n"); + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + // Not sure which one takes priority. I think if we have + // both, that's a bad sign. + if (trapSquash[tid] == true) { + assert(!tcSquash[tid]); + squashFromTrap(tid); + } else if (tcSquash[tid] == true) { + squashFromTC(tid); + } + + // Squashed sequence number must be older than youngest valid + // instruction in the ROB. This prevents squashes from younger + // instructions overriding squashes from older instructions. + if (fromIEW->squash[tid] && + commitStatus[tid] != TrapPending && + fromIEW->squashedSeqNum[tid] <= youngestSeqNum[tid]) { + + DPRINTF(Commit, "[tid:%i]: Squashing due to PC %#x [sn:%i]\n", + tid, + fromIEW->mispredPC[tid], + fromIEW->squashedSeqNum[tid]); - _status = ROBSquashing; + DPRINTF(Commit, "[tid:%i]: Redirecting to PC %#x\n", + tid, + fromIEW->nextPC[tid]); - InstSeqNum squashed_inst = fromIEW->squashedSeqNum; + commitStatus[tid] = ROBSquashing; - rob->squash(squashed_inst); + // If we want to include the squashing instruction in the squash, + // then use one older sequence number. + InstSeqNum squashed_inst = fromIEW->squashedSeqNum[tid]; - // Send back the sequence number of the squashed instruction. - toIEW->commitInfo.doneSeqNum = squashed_inst; + if (fromIEW->includeSquashInst[tid] == true) + squashed_inst--; - // Send back the squash signal to tell stages that they should squash. - toIEW->commitInfo.squash = true; + // All younger instructions will be squashed. Set the sequence + // number as the youngest instruction in the ROB. + youngestSeqNum[tid] = squashed_inst; - // Send back the rob squashing signal so other stages know that the - // ROB is in the process of squashing. - toIEW->commitInfo.robSquashing = true; + rob->squash(squashed_inst, tid); + changedROBNumEntries[tid] = true; - toIEW->commitInfo.branchMispredict = fromIEW->branchMispredict; + toIEW->commitInfo[tid].doneSeqNum = squashed_inst; - toIEW->commitInfo.branchTaken = fromIEW->branchTaken; + toIEW->commitInfo[tid].squash = true; - toIEW->commitInfo.nextPC = fromIEW->nextPC; + // Send back the rob squashing signal so other stages know that + // the ROB is in the process of squashing. + toIEW->commitInfo[tid].robSquashing = true; - toIEW->commitInfo.mispredPC = fromIEW->mispredPC; + toIEW->commitInfo[tid].branchMispredict = + fromIEW->branchMispredict[tid]; - if (toIEW->commitInfo.branchMispredict) { - ++branchMispredicts; + toIEW->commitInfo[tid].branchTaken = + fromIEW->branchTaken[tid]; + + toIEW->commitInfo[tid].nextPC = fromIEW->nextPC[tid]; + + toIEW->commitInfo[tid].mispredPC = fromIEW->mispredPC[tid]; + + if (toIEW->commitInfo[tid].branchMispredict) { + ++branchMispredicts; + } } + } - if (_status != ROBSquashing) { + setNextStatus(); + + if (squashCounter != numThreads) { // If we're not currently squashing, then get instructions. getInsts(); @@ -249,121 +761,186 @@ SimpleCommit<Impl>::commit() commitInsts(); } - // If the ROB is empty, we can set this stage to idle. Use this - // in the future when the Idle status will actually be utilized. -#if 0 - if (rob->isEmpty()) { - DPRINTF(Commit, "Commit: ROB is empty. Status changed to idle.\n"); - _status = Idle; - // Schedule an event so that commit will actually wake up - // once something gets put in the ROB. + //Check for any activity + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (changedROBNumEntries[tid]) { + toIEW->commitInfo[tid].usedROB = true; + toIEW->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid); + + if (rob->isEmpty(tid)) { + toIEW->commitInfo[tid].emptyROB = true; + } + + wroteToTimeBuffer = true; + changedROBNumEntries[tid] = false; + } } -#endif } -// Loop that goes through as many instructions in the ROB as possible and -// tries to commit them. The actual work for committing is done by the -// commitHead() function. template <class Impl> void -SimpleCommit<Impl>::commitInsts() +DefaultCommit<Impl>::commitInsts() { //////////////////////////////////// // Handle commit - // Note that commit will be handled prior to the ROB so that the ROB - // only tries to commit instructions it has in this current cycle, and - // not instructions it is writing in during this cycle. - // Can't commit and squash things at the same time... + // Note that commit will be handled prior to putting new + // instructions in the ROB so that the ROB only tries to commit + // instructions it has in this current cycle, and not instructions + // it is writing in during this cycle. Can't commit and squash + // things at the same time... //////////////////////////////////// - if (rob->isEmpty()) - return; - - DynInstPtr head_inst = rob->readHeadInst(); + DPRINTF(Commit, "Trying to commit instructions in the ROB.\n"); unsigned num_committed = 0; + DynInstPtr head_inst; + // Commit as many instructions as possible until the commit bandwidth // limit is reached, or it becomes impossible to commit any more. - while (!rob->isEmpty() && - head_inst->readyToCommit() && - num_committed < commitWidth) - { - DPRINTF(Commit, "Commit: Trying to commit head instruction.\n"); + while (num_committed < commitWidth) { + int commit_thread = getCommittingThread(); + + if (commit_thread == -1 || !rob->isHeadReady(commit_thread)) + break; - // If the head instruction is squashed, it is ready to retire at any - // time. However, we need to avoid updating any other state - // incorrectly if it's already been squashed. + head_inst = rob->readHeadInst(commit_thread); + + int tid = head_inst->threadNumber; + + assert(tid == commit_thread); + + DPRINTF(Commit, "Trying to commit head instruction, [sn:%i] [tid:%i]\n", + head_inst->seqNum, tid); + + // If the head instruction is squashed, it is ready to retire + // (be removed from the ROB) at any time. if (head_inst->isSquashed()) { - DPRINTF(Commit, "Commit: Retiring squashed instruction from " + DPRINTF(Commit, "Retiring squashed instruction from " "ROB.\n"); - // Tell ROB to retire head instruction. This retires the head - // inst in the ROB without affecting any other stages. - rob->retireHead(); + rob->retireHead(commit_thread); ++commitSquashedInsts; + // Record that the number of ROB entries has changed. + changedROBNumEntries[tid] = true; } else { + PC[tid] = head_inst->readPC(); + nextPC[tid] = head_inst->readNextPC(); + // Increment the total number of non-speculative instructions // executed. // Hack for now: it really shouldn't happen until after the // commit is deemed to be successful, but this count is needed // for syscalls. - cpu->funcExeInst++; + thread[tid]->funcExeInst++; // Try to commit the head instruction. bool commit_success = commitHead(head_inst, num_committed); - // Update what instruction we are looking at if the commit worked. if (commit_success) { ++num_committed; - // Send back which instruction has been committed. - // @todo: Update this later when a wider pipeline is used. - // Hmm, can't really give a pointer here...perhaps the - // sequence number instead (copy). - toIEW->commitInfo.doneSeqNum = head_inst->seqNum; + changedROBNumEntries[tid] = true; + + // Set the doneSeqNum to the youngest committed instruction. + toIEW->commitInfo[tid].doneSeqNum = head_inst->seqNum; ++commitCommittedInsts; - if (!head_inst->isNop()) { - cpu->instDone(); + // To match the old model, don't count nops and instruction + // prefetches towards the total commit count. + if (!head_inst->isNop() && !head_inst->isInstPrefetch()) { + cpu->instDone(tid); + } + + PC[tid] = nextPC[tid]; + nextPC[tid] = nextPC[tid] + sizeof(TheISA::MachInst); +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + // Debug statement. Checks to make sure we're not + // currently updating state while handling PC events. + if (count == 0) + assert(!thread[tid]->inSyscall && + !thread[tid]->trapPending); + oldpc = PC[tid]; + cpu->system->pcEventQueue.service( + thread[tid]->getTC()); + count++; + } while (oldpc != PC[tid]); + if (count > 1) { + DPRINTF(Commit, "PC skip function event, stopping commit\n"); + break; } +#endif } else { + DPRINTF(Commit, "Unable to commit head instruction PC:%#x " + "[tid:%i] [sn:%i].\n", + head_inst->readPC(), tid ,head_inst->seqNum); break; } } - - // Update the pointer to read the next instruction in the ROB. - head_inst = rob->readHeadInst(); } DPRINTF(CommitRate, "%i\n", num_committed); - n_committed_dist.sample(num_committed); + numCommittedDist.sample(num_committed); + + if (num_committed == commitWidth) { + commitEligibleSamples++; + } } template <class Impl> bool -SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) +DefaultCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) { - // Make sure instruction is valid assert(head_inst); - // If the instruction is not executed yet, then it is a non-speculative - // or store inst. Signal backwards that it should be executed. + int tid = head_inst->threadNumber; + + // If the instruction is not executed yet, then it will need extra + // handling. Signal backwards that it should be executed. if (!head_inst->isExecuted()) { // Keep this number correct. We have not yet actually executed // and committed this instruction. - cpu->funcExeInst--; - - if (head_inst->isNonSpeculative()) { - DPRINTF(Commit, "Commit: Encountered a store or non-speculative " - "instruction at the head of the ROB, PC %#x.\n", - head_inst->readPC()); + thread[tid]->funcExeInst--; + + head_inst->setAtCommit(); + + if (head_inst->isNonSpeculative() || + head_inst->isStoreConditional() || + head_inst->isMemBarrier() || + head_inst->isWriteBarrier()) { + + DPRINTF(Commit, "Encountered a barrier or non-speculative " + "instruction [sn:%lli] at the head of the ROB, PC %#x.\n", + head_inst->seqNum, head_inst->readPC()); + +#if !FULL_SYSTEM + // Hack to make sure syscalls/memory barriers/quiesces + // aren't executed until all stores write back their data. + // This direct communication shouldn't be used for + // anything other than this. + if (inst_num > 0 || iewStage->hasStoresToWB()) +#else + if ((head_inst->isMemBarrier() || head_inst->isWriteBarrier() || + head_inst->isQuiesce()) && + iewStage->hasStoresToWB()) +#endif + { + DPRINTF(Commit, "Waiting for all stores to writeback.\n"); + return false; + } - toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum; + toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum; // Change the instruction so it won't try to commit again until // it is executed. @@ -372,74 +949,110 @@ SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) ++commitNonSpecStalls; return false; + } else if (head_inst->isLoad()) { + DPRINTF(Commit, "[sn:%lli]: Uncached load, PC %#x.\n", + head_inst->seqNum, head_inst->readPC()); + + // Send back the non-speculative instruction's sequence + // number. Tell the lsq to re-execute the load. + toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum; + toIEW->commitInfo[tid].uncached = true; + toIEW->commitInfo[tid].uncachedLoad = head_inst; + + head_inst->clearCanCommit(); + + return false; } else { - panic("Commit: Trying to commit un-executed instruction " + panic("Trying to commit un-executed instruction " "of unknown type!\n"); } } - // Now check if it's one of the special trap or barrier or - // serializing instructions. - if (head_inst->isThreadSync() || - head_inst->isSerializing() || - head_inst->isMemBarrier() || - head_inst->isWriteBarrier() ) - { - // Not handled for now. Mem barriers and write barriers are safe - // to simply let commit as memory accesses only happen once they - // reach the head of commit. Not sure about the other two. - panic("Serializing or barrier instructions" - " are not handled yet.\n"); + if (head_inst->isThreadSync()) { + // Not handled for now. + panic("Thread sync instructions are not handled yet.\n"); } + // Stores mark themselves as completed. + if (!head_inst->isStore()) { + head_inst->setCompleted(); + } + +#if USE_CHECKER + // Use checker prior to updating anything due to traps or PC + // based events. + if (cpu->checker) { + cpu->checker->verify(head_inst); + } +#endif + // Check if the instruction caused a fault. If so, trap. Fault inst_fault = head_inst->getFault(); if (inst_fault != NoFault) { - if (!head_inst->isNop()) { -#if FULL_SYSTEM - cpu->trap(inst_fault); -#else // !FULL_SYSTEM - panic("fault (%d) detected @ PC %08p", inst_fault, - head_inst->PC); -#endif // FULL_SYSTEM + head_inst->setCompleted(); + DPRINTF(Commit, "Inst [sn:%lli] PC %#x has a fault\n", + head_inst->seqNum, head_inst->readPC()); + + if (iewStage->hasStoresToWB() || inst_num > 0) { + DPRINTF(Commit, "Stores outstanding, fault must wait.\n"); + return false; } - } - // Check if we're really ready to commit. If not then return false. - // I'm pretty sure all instructions should be able to commit if they've - // reached this far. For now leave this in as a check. - if (!rob->isHeadReady()) { - panic("Commit: Unable to commit head instruction!\n"); - return false; - } +#if USE_CHECKER + if (cpu->checker && head_inst->isStore()) { + cpu->checker->verify(head_inst); + } +#endif - // If it's a branch, then send back branch prediction update info - // to the fetch stage. - // This should be handled in the iew stage if a mispredict happens... + assert(!thread[tid]->inSyscall); - if (head_inst->isControl()) { + // Mark that we're in state update mode so that the trap's + // execution doesn't generate extra squashes. + thread[tid]->inSyscall = true; -#if 0 - toIEW->nextPC = head_inst->readPC(); - //Maybe switch over to BTB incorrect. - toIEW->btbMissed = head_inst->btbMiss(); - toIEW->target = head_inst->nextPC; - //Maybe also include global history information. - //This simple version will have no branch prediction however. -#endif + // DTB will sometimes need the machine instruction for when + // faults happen. So we will set it here, prior to the DTB + // possibly needing it for its fault. + thread[tid]->setInst( + static_cast<TheISA::MachInst>(head_inst->staticInst->machInst)); + + // Execute the trap. Although it's slightly unrealistic in + // terms of timing (as it doesn't wait for the full timing of + // the trap event to complete before updating state), it's + // needed to update the state as soon as possible. This + // prevents external agents from changing any specific state + // that the trap need. + cpu->trap(inst_fault, tid); + + // Exit state update mode to avoid accidental updating. + thread[tid]->inSyscall = false; - ++commitCommittedBranches; + commitStatus[tid] = TrapPending; + + // Generate trap squash event. + generateTrapEvent(tid); + + return false; } - // Now that the instruction is going to be committed, finalize its - // trace data. + updateComInstStats(head_inst); + if (head_inst->traceData) { + head_inst->traceData->setFetchSeq(head_inst->seqNum); + head_inst->traceData->setCPSeq(thread[tid]->numInst); head_inst->traceData->finalize(); + head_inst->traceData = NULL; + } + + // Update the commit rename map + for (int i = 0; i < head_inst->numDestRegs(); i++) { + renameMap[tid]->setEntry(head_inst->destRegIdx(i), + head_inst->renamedDestRegIdx(i)); } - //Finally clear the head ROB entry. - rob->retireHead(); + // Finally clear the head ROB entry. + rob->retireHead(tid); // Return true to indicate that we have committed an instruction. return true; @@ -447,37 +1060,39 @@ SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) template <class Impl> void -SimpleCommit<Impl>::getInsts() +DefaultCommit<Impl>::getInsts() { - ////////////////////////////////////// - // Handle ROB functions - ////////////////////////////////////// - - // Read any issued instructions and place them into the ROB. Do this - // prior to squashing to avoid having instructions in the ROB that - // don't get squashed properly. + // Read any renamed instructions and place them into the ROB. int insts_to_process = min((int)renameWidth, fromRename->size); - for (int inst_num = 0; - inst_num < insts_to_process; - ++inst_num) + for (int inst_num = 0; inst_num < insts_to_process; ++inst_num) { - if (!fromRename->insts[inst_num]->isSquashed()) { - DPRINTF(Commit, "Commit: Inserting PC %#x into ROB.\n", - fromRename->insts[inst_num]->readPC()); - rob->insertInst(fromRename->insts[inst_num]); + DynInstPtr inst = fromRename->insts[inst_num]; + int tid = inst->threadNumber; + + if (!inst->isSquashed() && + commitStatus[tid] != ROBSquashing) { + changedROBNumEntries[tid] = true; + + DPRINTF(Commit, "Inserting PC %#x [sn:%i] [tid:%i] into ROB.\n", + inst->readPC(), inst->seqNum, tid); + + rob->insertInst(inst); + + assert(rob->getThreadEntries(tid) <= rob->getMaxEntries(tid)); + + youngestSeqNum[tid] = inst->seqNum; } else { - DPRINTF(Commit, "Commit: Instruction %i PC %#x was " + DPRINTF(Commit, "Instruction PC %#x [sn:%i] [tid:%i] was " "squashed, skipping.\n", - fromRename->insts[inst_num]->seqNum, - fromRename->insts[inst_num]->readPC()); + inst->readPC(), inst->seqNum, tid); } } } template <class Impl> void -SimpleCommit<Impl>::markCompletedInsts() +DefaultCommit<Impl>::markCompletedInsts() { // Grab completed insts out of the IEW instruction queue, and mark // instructions completed within the ROB. @@ -485,18 +1100,178 @@ SimpleCommit<Impl>::markCompletedInsts() inst_num < fromIEW->size && fromIEW->insts[inst_num]; ++inst_num) { - DPRINTF(Commit, "Commit: Marking PC %#x, SN %i ready within ROB.\n", - fromIEW->insts[inst_num]->readPC(), - fromIEW->insts[inst_num]->seqNum); + if (!fromIEW->insts[inst_num]->isSquashed()) { + DPRINTF(Commit, "[tid:%i]: Marking PC %#x, [sn:%lli] ready " + "within ROB.\n", + fromIEW->insts[inst_num]->threadNumber, + fromIEW->insts[inst_num]->readPC(), + fromIEW->insts[inst_num]->seqNum); + + // Mark the instruction as ready to commit. + fromIEW->insts[inst_num]->setCanCommit(); + } + } +} + +template <class Impl> +bool +DefaultCommit<Impl>::robDoneSquashing() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isDoneSquashing(tid)) + return false; + } + + return true; +} + +template <class Impl> +void +DefaultCommit<Impl>::updateComInstStats(DynInstPtr &inst) +{ + unsigned thread = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) { + statComSwp[thread]++; + } else { + statComInst[thread]++; + } +#else + statComInst[thread]++; +#endif + + // + // Control Instructions + // + if (inst->isControl()) + statComBranches[thread]++; + + // + // Memory references + // + if (inst->isMemRef()) { + statComRefs[thread]++; + + if (inst->isLoad()) { + statComLoads[thread]++; + } + } - // Mark the instruction as ready to commit. - fromIEW->insts[inst_num]->setCanCommit(); + if (inst->isMemBarrier()) { + statComMembars[thread]++; } } +//////////////////////////////////////// +// // +// SMT COMMIT POLICY MAINTAINED HERE // +// // +//////////////////////////////////////// template <class Impl> -uint64_t -SimpleCommit<Impl>::readCommitPC() +int +DefaultCommit<Impl>::getCommittingThread() +{ + if (numThreads > 1) { + switch (commitPolicy) { + + case Aggressive: + //If Policy is Aggressive, commit will call + //this function multiple times per + //cycle + return oldestReady(); + + case RoundRobin: + return roundRobin(); + + case OldestReady: + return oldestReady(); + + default: + return -1; + } + } else { + int tid = (*activeThreads).front(); + + if (commitStatus[tid] == Running || + commitStatus[tid] == Idle || + commitStatus[tid] == FetchTrapPending) { + return tid; + } else { + return -1; + } + } +} + +template<class Impl> +int +DefaultCommit<Impl>::roundRobin() +{ + list<unsigned>::iterator pri_iter = priority_list.begin(); + list<unsigned>::iterator end = priority_list.end(); + + while (pri_iter != end) { + unsigned tid = *pri_iter; + + if (commitStatus[tid] == Running || + commitStatus[tid] == Idle || + commitStatus[tid] == FetchTrapPending) { + + if (rob->isHeadReady(tid)) { + priority_list.erase(pri_iter); + priority_list.push_back(tid); + + return tid; + } + } + + pri_iter++; + } + + return -1; +} + +template<class Impl> +int +DefaultCommit<Impl>::oldestReady() { - return rob->readHeadPC(); + unsigned oldest = 0; + bool first = true; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isEmpty(tid) && + (commitStatus[tid] == Running || + commitStatus[tid] == Idle || + commitStatus[tid] == FetchTrapPending)) { + + if (rob->isHeadReady(tid)) { + + DynInstPtr head_inst = rob->readHeadInst(tid); + + if (first) { + oldest = tid; + first = false; + } else if (head_inst->seqNum < oldest) { + oldest = tid; + } + } + } + } + + if (!first) { + return oldest; + } else { + return -1; + } } diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc index a268dbc23..feca4cdf2 100644 --- a/src/cpu/o3/cpu.cc +++ b/src/cpu/o3/cpu.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,30 +24,47 @@ * 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. + * + * Authors: Kevin Lim + * Korey Sewell */ #include "config/full_system.hh" +#include "config/use_checker.hh" #if FULL_SYSTEM #include "sim/system.hh" #else #include "sim/process.hh" #endif -#include "sim/root.hh" -#include "cpu/cpu_exec_context.hh" -#include "cpu/exec_context.hh" -#include "cpu/o3/alpha_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/activity.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" +#include "cpu/o3/isa_specific.hh" #include "cpu/o3/cpu.hh" +#include "sim/root.hh" +#include "sim/stat_control.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + using namespace std; +using namespace TheISA; -BaseFullCPU::BaseFullCPU(Params ¶ms) - : BaseCPU(¶ms), cpu_id(0) +BaseO3CPU::BaseO3CPU(Params *params) + : BaseCPU(params), cpu_id(0) { } +void +BaseO3CPU::regStats() +{ + BaseCPU::regStats(); +} + template <class Impl> FullO3CPU<Impl>::TickEvent::TickEvent(FullO3CPU<Impl> *c) : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c) @@ -68,97 +85,115 @@ FullO3CPU<Impl>::TickEvent::description() return "FullO3CPU tick event"; } -//Call constructor to all the pipeline stages here template <class Impl> -FullO3CPU<Impl>::FullO3CPU(Params ¶ms) -#if FULL_SYSTEM - : BaseFullCPU(params), -#else - : BaseFullCPU(params), -#endif // FULL_SYSTEM +FullO3CPU<Impl>::ActivateThreadEvent::ActivateThreadEvent() + : Event(&mainEventQueue, CPU_Tick_Pri) +{ +} + +template <class Impl> +void +FullO3CPU<Impl>::ActivateThreadEvent::init(int thread_num, + FullO3CPU<Impl> *thread_cpu) +{ + tid = thread_num; + cpu = thread_cpu; +} + +template <class Impl> +void +FullO3CPU<Impl>::ActivateThreadEvent::process() +{ + cpu->activateThread(tid); +} + +template <class Impl> +const char * +FullO3CPU<Impl>::ActivateThreadEvent::description() +{ + return "FullO3CPU \"Activate Thread\" event"; +} + +template <class Impl> +FullO3CPU<Impl>::FullO3CPU(Params *params) + : BaseO3CPU(params), tickEvent(this), + removeInstsThisCycle(false), fetch(params), decode(params), rename(params), iew(params), commit(params), - regFile(params.numPhysIntRegs, params.numPhysFloatRegs), + regFile(params->numPhysIntRegs, params->numPhysFloatRegs), - freeList(TheISA::NumIntRegs, params.numPhysIntRegs, - TheISA::NumFloatRegs, params.numPhysFloatRegs), + freeList(params->numberOfThreads, + TheISA::NumIntRegs, params->numPhysIntRegs, + TheISA::NumFloatRegs, params->numPhysFloatRegs), - renameMap(TheISA::NumIntRegs, params.numPhysIntRegs, - TheISA::NumFloatRegs, params.numPhysFloatRegs, - TheISA::NumMiscRegs, - TheISA::ZeroReg, - TheISA::ZeroReg + TheISA::NumIntRegs), + rob(params->numROBEntries, params->squashWidth, + params->smtROBPolicy, params->smtROBThreshold, + params->numberOfThreads), - rob(params.numROBEntries, params.squashWidth), + scoreboard(params->numberOfThreads, + TheISA::NumIntRegs, params->numPhysIntRegs, + TheISA::NumFloatRegs, params->numPhysFloatRegs, + TheISA::NumMiscRegs * number_of_threads, + TheISA::ZeroReg), - // What to pass to these time buffers? // For now just have these time buffers be pretty big. + // @todo: Make these time buffer sizes parameters or derived + // from latencies timeBuffer(5, 5), fetchQueue(5, 5), decodeQueue(5, 5), renameQueue(5, 5), iewQueue(5, 5), - - cpuXC(NULL), + activityRec(NumStages, 10, params->activity), globalSeqNum(1), #if FULL_SYSTEM - system(params.system), - memCtrl(system->memctrl), + system(params->system), physmem(system->physmem), - itb(params.itb), - dtb(params.dtb), - mem(params.mem), -#else - // Hardcoded for a single thread!! - mem(params.workload[0]->getMemory()), #endif // FULL_SYSTEM - - icacheInterface(params.icacheInterface), - dcacheInterface(params.dcacheInterface), - deferRegistration(params.defReg), - numInsts(0), - funcExeInst(0) + mem(params->mem), + switchCount(0), + deferRegistration(params->deferRegistration), + numThreads(number_of_threads) { _status = Idle; -#if !FULL_SYSTEM - thread.resize(this->number_of_threads); -#endif + checker = NULL; - for (int i = 0; i < this->number_of_threads; ++i) { + if (params->checker) { +#if USE_CHECKER + BaseCPU *temp_checker = params->checker; + checker = dynamic_cast<Checker<DynInstPtr> *>(temp_checker); + checker->setMemory(mem); #if FULL_SYSTEM - assert(i == 0); - thread[i] = new CPUExecContext(this, 0, system, itb, dtb, mem); - system->execContexts[i] = thread[i]->getProxy(); - - execContexts.push_back(system->execContexts[i]); + checker->setSystem(params->system); +#endif #else - if (i < params.workload.size()) { - DPRINTF(FullCPU, "FullCPU: Workload[%i]'s starting PC is %#x, " - "process is %#x", - i, params.workload[i]->prog_entry, thread[i]); - thread[i] = new CPUExecContext(this, i, params.workload[i], i); - } - assert(params.workload[i]->getMemory() != NULL); - assert(mem != NULL); - execContexts.push_back(thread[i]->getProxy()); -#endif // !FULL_SYSTEM + panic("Checker enabled but not compiled in!"); +#endif // USE_CHECKER } - // Note that this is a hack so that my code which still uses xc-> will - // still work. I should remove this eventually - cpuXC = thread[0]; +#if !FULL_SYSTEM + thread.resize(number_of_threads); + tids.resize(number_of_threads); +#endif + + // The stages also need their CPU pointer setup. However this + // must be done at the upper level CPU because they have pointers + // to the upper level CPU, and not this FullO3CPU. - // The stages also need their CPU pointer setup. However this must be - // done at the upper level CPU because they have pointers to the upper - // level CPU, and not this FullO3CPU. + // Set up Pointers to the activeThreads list for each stage + fetch.setActiveThreads(&activeThreads); + decode.setActiveThreads(&activeThreads); + rename.setActiveThreads(&activeThreads); + iew.setActiveThreads(&activeThreads); + commit.setActiveThreads(&activeThreads); // Give each of the stages the time buffer they will use. fetch.setTimeBuffer(&timeBuffer); @@ -170,6 +205,7 @@ FullO3CPU<Impl>::FullO3CPU(Params ¶ms) // Also setup each of the stages' queues. fetch.setFetchQueue(&fetchQueue); decode.setFetchQueue(&fetchQueue); + commit.setFetchQueue(&fetchQueue); decode.setDecodeQueue(&decodeQueue); rename.setDecodeQueue(&decodeQueue); rename.setRenameQueue(&renameQueue); @@ -178,16 +214,87 @@ FullO3CPU<Impl>::FullO3CPU(Params ¶ms) commit.setIEWQueue(&iewQueue); commit.setRenameQueue(&renameQueue); + commit.setFetchStage(&fetch); + commit.setIEWStage(&iew); + rename.setIEWStage(&iew); + rename.setCommitStage(&commit); + +#if !FULL_SYSTEM + int active_threads = params->workload.size(); + + if (active_threads > Impl::MaxThreads) { + panic("Workload Size too large. Increase the 'MaxThreads'" + "constant in your O3CPU impl. file (e.g. o3/alpha/impl.hh) or " + "edit your workload size."); + } +#else + int active_threads = 1; +#endif + + //Make Sure That this a Valid Architeture + assert(params->numPhysIntRegs >= numThreads * TheISA::NumIntRegs); + assert(params->numPhysFloatRegs >= numThreads * TheISA::NumFloatRegs); + + rename.setScoreboard(&scoreboard); + iew.setScoreboard(&scoreboard); + // Setup the rename map for whichever stages need it. - rename.setRenameMap(&renameMap); - iew.setRenameMap(&renameMap); + PhysRegIndex lreg_idx = 0; + PhysRegIndex freg_idx = params->numPhysIntRegs; //Index to 1 after int regs + + for (int tid=0; tid < numThreads; tid++) { + bool bindRegs = (tid <= active_threads - 1); + + commitRenameMap[tid].init(TheISA::NumIntRegs, + params->numPhysIntRegs, + lreg_idx, //Index for Logical. Regs + + TheISA::NumFloatRegs, + params->numPhysFloatRegs, + freg_idx, //Index for Float Regs + + TheISA::NumMiscRegs, + + TheISA::ZeroReg, + TheISA::ZeroReg, + + tid, + false); + + renameMap[tid].init(TheISA::NumIntRegs, + params->numPhysIntRegs, + lreg_idx, //Index for Logical. Regs + + TheISA::NumFloatRegs, + params->numPhysFloatRegs, + freg_idx, //Index for Float Regs + + TheISA::NumMiscRegs, + + TheISA::ZeroReg, + TheISA::ZeroReg, + + tid, + bindRegs); + } - // Setup the free list for whichever stages need it. + rename.setRenameMap(renameMap); + commit.setRenameMap(commitRenameMap); + + // Give renameMap & rename stage access to the freeList; + for (int i=0; i < numThreads; i++) { + renameMap[i].setFreeList(&freeList); + } rename.setFreeList(&freeList); - renameMap.setFreeList(&freeList); // Setup the ROB for whichever stages need it. commit.setROB(&rob); + + lastRunningCycle = curTick; + + lastActivatedCycle = -1; + + contextSwitch = false; } template <class Impl> @@ -199,18 +306,71 @@ template <class Impl> void FullO3CPU<Impl>::fullCPURegStats() { - // Register any of the FullCPU's stats here. + BaseO3CPU::regStats(); + + // Register any of the O3CPU's stats here. + timesIdled + .name(name() + ".timesIdled") + .desc("Number of times that the entire CPU went into an idle state and" + " unscheduled itself") + .prereq(timesIdled); + + idleCycles + .name(name() + ".idleCycles") + .desc("Total number of cycles that the CPU has spent unscheduled due " + "to idling") + .prereq(idleCycles); + + // Number of Instructions simulated + // -------------------------------- + // Should probably be in Base CPU but need templated + // MaxThreads so put in here instead + committedInsts + .init(numThreads) + .name(name() + ".committedInsts") + .desc("Number of Instructions Simulated"); + + totalCommittedInsts + .name(name() + ".committedInsts_total") + .desc("Number of Instructions Simulated"); + + cpi + .name(name() + ".cpi") + .desc("CPI: Cycles Per Instruction") + .precision(6); + cpi = simTicks / committedInsts; + + totalCpi + .name(name() + ".cpi_total") + .desc("CPI: Total CPI of All Threads") + .precision(6); + totalCpi = simTicks / totalCommittedInsts; + + ipc + .name(name() + ".ipc") + .desc("IPC: Instructions Per Cycle") + .precision(6); + ipc = committedInsts / simTicks; + + totalIpc + .name(name() + ".ipc_total") + .desc("IPC: Total IPC of All Threads") + .precision(6); + totalIpc = totalCommittedInsts / simTicks; + } template <class Impl> void FullO3CPU<Impl>::tick() { - DPRINTF(FullCPU, "\n\nFullCPU: Ticking main, FullO3CPU.\n"); + DPRINTF(O3CPU, "\n\nFullO3CPU: Ticking main, FullO3CPU.\n"); - //Tick each of the stages if they're actually running. - //Will want to figure out a way to unschedule itself if they're all - //going to be idle for a long time. + ++numCycles; + +// activity = false; + + //Tick each of the stages fetch.tick(); decode.tick(); @@ -221,7 +381,11 @@ FullO3CPU<Impl>::tick() commit.tick(); - // Now advance the time buffers, unless the stage is stalled. +#if !FULL_SYSTEM + doContextSwitch(); +#endif + + // Now advance the time buffers timeBuffer.advance(); fetchQueue.advance(); @@ -229,114 +393,421 @@ FullO3CPU<Impl>::tick() renameQueue.advance(); iewQueue.advance(); - if (_status == Running && !tickEvent.scheduled()) - tickEvent.schedule(curTick + 1); + activityRec.advance(); + + if (removeInstsThisCycle) { + cleanUpRemovedInsts(); + } + + if (!tickEvent.scheduled()) { + if (_status == SwitchedOut) { + // increment stat + lastRunningCycle = curTick; + } else if (!activityRec.active()) { + lastRunningCycle = curTick; + timesIdled++; + } else { + tickEvent.schedule(curTick + cycles(1)); + } + } + +#if !FULL_SYSTEM + updateThreadPriority(); +#endif + } template <class Impl> void FullO3CPU<Impl>::init() { - if(!deferRegistration) - { - this->registerExecContexts(); + if (!deferRegistration) { + registerThreadContexts(); + } + + // Set inSyscall so that the CPU doesn't squash when initially + // setting up registers. + for (int i = 0; i < number_of_threads; ++i) + thread[i]->inSyscall = true; - // Need to do a copy of the xc->regs into the CPU's regfile so - // that it can start properly. + for (int tid=0; tid < number_of_threads; tid++) { #if FULL_SYSTEM - ExecContext *src_xc = system->execContexts[0]; - TheISA::initCPU(src_xc, src_xc->readCpuId()); + ThreadContext *src_tc = threadContexts[tid]; #else - ExecContext *src_xc = thread[0]->getProxy(); + ThreadContext *src_tc = thread[tid]->getTC(); #endif - // First loop through the integer registers. - for (int i = 0; i < TheISA::NumIntRegs; ++i) - { - regFile.intRegFile[i] = src_xc->readIntReg(i); + // Threads start in the Suspended State + if (src_tc->status() != ThreadContext::Suspended) { + continue; } - // Then loop through the floating point registers. - for (int i = 0; i < TheISA::NumFloatRegs; ++i) - { - regFile.floatRegFile.setRegBits(i, src_xc->readRegBits(i)) - } -/* - // Then loop through the misc registers. - regFile.miscRegs.fpcr = src_xc->regs.miscRegs.fpcr; - regFile.miscRegs.uniq = src_xc->regs.miscRegs.uniq; - regFile.miscRegs.lock_flag = src_xc->regs.miscRegs.lock_flag; - regFile.miscRegs.lock_addr = src_xc->regs.miscRegs.lock_addr; -*/ - // Then finally set the PC and the next PC. - regFile.pc = src_xc->readPC(); - regFile.npc = src_xc->readNextPC(); +#if FULL_SYSTEM + TheISA::initCPU(src_tc, src_tc->readCpuId()); +#endif } + + // Clear inSyscall. + for (int i = 0; i < number_of_threads; ++i) + thread[i]->inSyscall = false; + + // Initialize stages. + fetch.initStage(); + iew.initStage(); + rename.initStage(); + commit.initStage(); + + commit.setThreads(thread); } template <class Impl> void -FullO3CPU<Impl>::activateContext(int thread_num, int delay) +FullO3CPU<Impl>::insertThread(unsigned tid) +{ + DPRINTF(O3CPU,"[tid:%i] Initializing thread data"); + // Will change now that the PC and thread state is internal to the CPU + // and not in the ThreadContext. +#if 0 +#if FULL_SYSTEM + ThreadContext *src_tc = system->threadContexts[tid]; +#else + ThreadContext *src_tc = thread[tid]; +#endif + + //Bind Int Regs to Rename Map + for (int ireg = 0; ireg < TheISA::NumIntRegs; ireg++) { + PhysRegIndex phys_reg = freeList.getIntReg(); + + renameMap[tid].setEntry(ireg,phys_reg); + scoreboard.setReg(phys_reg); + } + + //Bind Float Regs to Rename Map + for (int freg = 0; freg < TheISA::NumFloatRegs; freg++) { + PhysRegIndex phys_reg = freeList.getFloatReg(); + + renameMap[tid].setEntry(freg,phys_reg); + scoreboard.setReg(phys_reg); + } + + //Copy Thread Data Into RegFile + this->copyFromTC(tid); + + //Set PC/NPC + regFile.pc[tid] = src_tc->readPC(); + regFile.npc[tid] = src_tc->readNextPC(); + + src_tc->setStatus(ThreadContext::Active); + + activateContext(tid,1); + + //Reset ROB/IQ/LSQ Entries + commit.rob->resetEntries(); + iew.resetEntries(); +#endif +} + +template <class Impl> +void +FullO3CPU<Impl>::removeThread(unsigned tid) +{ + DPRINTF(O3CPU,"[tid:%i] Removing thread data"); +#if 0 + //Unbind Int Regs from Rename Map + for (int ireg = 0; ireg < TheISA::NumIntRegs; ireg++) { + PhysRegIndex phys_reg = renameMap[tid].lookup(ireg); + + scoreboard.unsetReg(phys_reg); + freeList.addReg(phys_reg); + } + + //Unbind Float Regs from Rename Map + for (int freg = 0; freg < TheISA::NumFloatRegs; freg++) { + PhysRegIndex phys_reg = renameMap[tid].lookup(freg); + + scoreboard.unsetReg(phys_reg); + freeList.addReg(phys_reg); + } + + //Copy Thread Data From RegFile + /* Fix Me: + * Do we really need to do this if we are removing a thread + * in the sense that it's finished (exiting)? If the thread is just + * being suspended we might... + */ +// this->copyToTC(tid); + + //Squash Throughout Pipeline + fetch.squash(0,tid); + decode.squash(tid); + rename.squash(tid); + + assert(iew.ldstQueue.getCount(tid) == 0); + + //Reset ROB/IQ/LSQ Entries + if (activeThreads.size() >= 1) { + commit.rob->resetEntries(); + iew.resetEntries(); + } +#endif +} + + +template <class Impl> +void +FullO3CPU<Impl>::activateWhenReady(int tid) +{ + DPRINTF(O3CPU,"[tid:%i]: Checking if resources are available for incoming" + "(e.g. PhysRegs/ROB/IQ/LSQ) \n", + tid); + + bool ready = true; + + if (freeList.numFreeIntRegs() >= TheISA::NumIntRegs) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "Phys. Int. Regs.\n", + tid); + ready = false; + } else if (freeList.numFreeFloatRegs() >= TheISA::NumFloatRegs) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "Phys. Float. Regs.\n", + tid); + ready = false; + } else if (commit.rob->numFreeEntries() >= + commit.rob->entryAmount(activeThreads.size() + 1)) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "ROB entries.\n", + tid); + ready = false; + } else if (iew.instQueue.numFreeEntries() >= + iew.instQueue.entryAmount(activeThreads.size() + 1)) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "IQ entries.\n", + tid); + ready = false; + } else if (iew.ldstQueue.numFreeEntries() >= + iew.ldstQueue.entryAmount(activeThreads.size() + 1)) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "LSQ entries.\n", + tid); + ready = false; + } + + if (ready) { + insertThread(tid); + + contextSwitch = false; + + cpuWaitList.remove(tid); + } else { + suspendContext(tid); + + //blocks fetch + contextSwitch = true; + + //do waitlist + cpuWaitList.push_back(tid); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::activateThread(unsigned int tid) +{ + list<unsigned>::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive == activeThreads.end()) { + DPRINTF(O3CPU, "[tid:%i]: Adding to active threads list\n", + tid); + + activeThreads.push_back(tid); + } +} + + +template <class Impl> +void +FullO3CPU<Impl>::activateContext(int tid, int delay) { // Needs to set each stage to running as well. + if (delay){ + DPRINTF(O3CPU, "[tid:%i]: Scheduling thread context to activate " + "on cycle %d\n", tid, curTick + cycles(delay)); + scheduleActivateThreadEvent(tid, delay); + } else { + activateThread(tid); + } + + if(lastActivatedCycle < curTick) { + scheduleTickEvent(delay); + + // Be sure to signal that there's some activity so the CPU doesn't + // deschedule itself. + activityRec.activity(); + fetch.wakeFromQuiesce(); - scheduleTickEvent(delay); + lastActivatedCycle = curTick; - _status = Running; + _status = Running; + } } template <class Impl> void -FullO3CPU<Impl>::suspendContext(int thread_num) +FullO3CPU<Impl>::suspendContext(int tid) { - panic("suspendContext unimplemented!"); + DPRINTF(O3CPU,"[tid: %i]: Suspended ...\n", tid); + unscheduleTickEvent(); + _status = Idle; +/* + //Remove From Active List, if Active + list<unsigned>::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive != activeThreads.end()) { + DPRINTF(O3CPU,"[tid:%i]: Removing from active threads list\n", + tid); + activeThreads.erase(isActive); + } +*/ } template <class Impl> void -FullO3CPU<Impl>::deallocateContext(int thread_num) +FullO3CPU<Impl>::deallocateContext(int tid) { - panic("deallocateContext unimplemented!"); + DPRINTF(O3CPU,"[tid:%i]: Deallocating ...", tid); +/* + //Remove From Active List, if Active + list<unsigned>::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive != activeThreads.end()) { + DPRINTF(O3CPU,"[tid:%i]: Removing from active threads list\n", + tid); + activeThreads.erase(isActive); + + removeThread(tid); + } +*/ } template <class Impl> void -FullO3CPU<Impl>::haltContext(int thread_num) +FullO3CPU<Impl>::haltContext(int tid) { - panic("haltContext unimplemented!"); + DPRINTF(O3CPU,"[tid:%i]: Halted ...", tid); +/* + //Remove From Active List, if Active + list<unsigned>::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive != activeThreads.end()) { + DPRINTF(O3CPU,"[tid:%i]: Removing from active threads list\n", + tid); + activeThreads.erase(isActive); + + removeThread(tid); + } +*/ } template <class Impl> void -FullO3CPU<Impl>::switchOut() +FullO3CPU<Impl>::switchOut(Sampler *_sampler) { - panic("FullO3CPU does not have a switch out function.\n"); + sampler = _sampler; + switchCount = 0; + fetch.switchOut(); + decode.switchOut(); + rename.switchOut(); + iew.switchOut(); + commit.switchOut(); + + // Wake the CPU and record activity so everything can drain out if + // the CPU is currently idle. + wakeCPU(); + activityRec.activity(); +} + +template <class Impl> +void +FullO3CPU<Impl>::signalSwitched() +{ + if (++switchCount == NumStages) { + fetch.doSwitchOut(); + rename.doSwitchOut(); + commit.doSwitchOut(); + instList.clear(); + while (!removeList.empty()) { + removeList.pop(); + } + +#if USE_CHECKER + if (checker) + checker->switchOut(sampler); +#endif + + if (tickEvent.scheduled()) + tickEvent.squash(); + sampler->signalSwitched(); + _status = SwitchedOut; + } + assert(switchCount <= 5); } template <class Impl> void FullO3CPU<Impl>::takeOverFrom(BaseCPU *oldCPU) { + // Flush out any old data from the time buffers. + for (int i = 0; i < 10; ++i) { + timeBuffer.advance(); + fetchQueue.advance(); + decodeQueue.advance(); + renameQueue.advance(); + iewQueue.advance(); + } + + activityRec.reset(); + BaseCPU::takeOverFrom(oldCPU); + fetch.takeOverFrom(); + decode.takeOverFrom(); + rename.takeOverFrom(); + iew.takeOverFrom(); + commit.takeOverFrom(); + assert(!tickEvent.scheduled()); - // Set all status's to active, schedule the - // CPU's tick event. - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; - if (xc->status() == ExecContext::Active && _status != Running) { + // @todo: Figure out how to properly select the tid to put onto + // the active threads list. + int tid = 0; + + list<unsigned>::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive == activeThreads.end()) { + //May Need to Re-code this if the delay variable is the delay + //needed for thread to activate + DPRINTF(O3CPU, "Adding Thread %i to active threads list\n", + tid); + + activeThreads.push_back(tid); + } + + // Set all statuses to active, schedule the CPU's tick event. + // @todo: Fix up statuses so this is handled properly + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && _status != Running) { _status = Running; tickEvent.schedule(curTick); } } -} - -template <class Impl> -InstSeqNum -FullO3CPU<Impl>::getAndIncrementInstSeq() -{ - // Hopefully this works right. - return globalSeqNum++; + if (!tickEvent.scheduled()) + tickEvent.schedule(curTick); } template <class Impl> @@ -411,156 +882,384 @@ FullO3CPU<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val) template <class Impl> uint64_t -FullO3CPU<Impl>::readPC() +FullO3CPU<Impl>::readArchIntReg(int reg_idx, unsigned tid) { - return regFile.readPC(); + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + return regFile.readIntReg(phys_reg); +} + +template <class Impl> +float +FullO3CPU<Impl>::readArchFloatRegSingle(int reg_idx, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + return regFile.readFloatReg(phys_reg); +} + +template <class Impl> +double +FullO3CPU<Impl>::readArchFloatRegDouble(int reg_idx, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + return regFile.readFloatReg(phys_reg, 64); +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readArchFloatRegInt(int reg_idx, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + return regFile.readFloatRegBits(phys_reg); } template <class Impl> void -FullO3CPU<Impl>::setNextPC(uint64_t val) +FullO3CPU<Impl>::setArchIntReg(int reg_idx, uint64_t val, unsigned tid) { - regFile.setNextPC(val); + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setIntReg(phys_reg, val); } template <class Impl> void -FullO3CPU<Impl>::setPC(Addr new_PC) +FullO3CPU<Impl>::setArchFloatRegSingle(int reg_idx, float val, unsigned tid) { - regFile.setPC(new_PC); + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setFloatReg(phys_reg, val); } template <class Impl> void -FullO3CPU<Impl>::addInst(DynInstPtr &inst) +FullO3CPU<Impl>::setArchFloatRegDouble(int reg_idx, double val, unsigned tid) { - instList.push_back(inst); + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setFloatReg(phys_reg, val, 64); } template <class Impl> void -FullO3CPU<Impl>::instDone() +FullO3CPU<Impl>::setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid) { - // Keep an instruction count. - numInsts++; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); - // Check for instruction-count-based events. - comInstEventQueue[0]->serviceEvents(numInsts); + regFile.setFloatRegBits(phys_reg, val); +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readPC(unsigned tid) +{ + return commit.readPC(tid); } template <class Impl> void -FullO3CPU<Impl>::removeBackInst(DynInstPtr &inst) +FullO3CPU<Impl>::setPC(Addr new_PC,unsigned tid) { - DynInstPtr inst_to_delete; + commit.setPC(new_PC, tid); +} - // Walk through the instruction list, removing any instructions - // that were inserted after the given instruction, inst. - while (instList.back() != inst) - { - assert(!instList.empty()); +template <class Impl> +uint64_t +FullO3CPU<Impl>::readNextPC(unsigned tid) +{ + return commit.readNextPC(tid); +} + +template <class Impl> +void +FullO3CPU<Impl>::setNextPC(uint64_t val,unsigned tid) +{ + commit.setNextPC(val, tid); +} - // Obtain the pointer to the instruction. - inst_to_delete = instList.back(); +#if THE_ISA != ALPHA_ISA +template <class Impl> +uint64_t +FullO3CPU<Impl>::readNextNPC(unsigned tid) +{ + return commit.readNextNPC(tid); +} - DPRINTF(FullCPU, "FullCPU: Removing instruction %i, PC %#x\n", - inst_to_delete->seqNum, inst_to_delete->readPC()); +template <class Impl> +void +FullO3CPU<Impl>::setNextNNPC(uint64_t val,unsigned tid) +{ + commit.setNextNPC(val, tid); +} +#endif - // Remove the instruction from the list. - instList.pop_back(); +template <class Impl> +typename FullO3CPU<Impl>::ListIt +FullO3CPU<Impl>::addInst(DynInstPtr &inst) +{ + instList.push_back(inst); - // Mark it as squashed. - inst_to_delete->setSquashed(); - } + return --(instList.end()); } template <class Impl> void -FullO3CPU<Impl>::removeFrontInst(DynInstPtr &inst) +FullO3CPU<Impl>::instDone(unsigned tid) { - DynInstPtr inst_to_remove; + // Keep an instruction count. + thread[tid]->numInst++; + thread[tid]->numInsts++; + committedInsts[tid]++; + totalCommittedInsts++; - // The front instruction should be the same one being asked to be removed. - assert(instList.front() == inst); + // Check for instruction-count-based events. + comInstEventQueue[tid]->serviceEvents(thread[tid]->numInst); +} - // Remove the front instruction. - inst_to_remove = inst; - instList.pop_front(); +template <class Impl> +void +FullO3CPU<Impl>::addToRemoveList(DynInstPtr &inst) +{ + removeInstsThisCycle = true; - DPRINTF(FullCPU, "FullCPU: Removing committed instruction %#x, PC %#x\n", - inst_to_remove, inst_to_remove->readPC()); + removeList.push(inst->getInstListIt()); } template <class Impl> void -FullO3CPU<Impl>::removeInstsNotInROB() +FullO3CPU<Impl>::removeFrontInst(DynInstPtr &inst) { - DPRINTF(FullCPU, "FullCPU: Deleting instructions from instruction " - "list.\n"); + DPRINTF(O3CPU, "Removing committed instruction [tid:%i] PC %#x " + "[sn:%lli]\n", + inst->threadNumber, inst->readPC(), inst->seqNum); - DynInstPtr rob_tail = rob.readTailInst(); + removeInstsThisCycle = true; - removeBackInst(rob_tail); + // Remove the front instruction. + removeList.push(inst->getInstListIt()); } template <class Impl> void -FullO3CPU<Impl>::removeInstsUntil(const InstSeqNum &seq_num) +FullO3CPU<Impl>::removeInstsNotInROB(unsigned tid) { - DPRINTF(FullCPU, "FullCPU: Deleting instructions from instruction " - "list.\n"); + DPRINTF(O3CPU, "Thread %i: Deleting instructions from instruction" + " list.\n", tid); + + ListIt end_it; + + bool rob_empty = false; + + if (instList.empty()) { + return; + } else if (rob.isEmpty(/*tid*/)) { + DPRINTF(O3CPU, "ROB is empty, squashing all insts.\n"); + end_it = instList.begin(); + rob_empty = true; + } else { + end_it = (rob.readTailInst(tid))->getInstListIt(); + DPRINTF(O3CPU, "ROB is not empty, squashing insts not in ROB.\n"); + } + + removeInstsThisCycle = true; + + ListIt inst_it = instList.end(); - DynInstPtr inst_to_delete; + inst_it--; - while (instList.back()->seqNum > seq_num) { + // Walk through the instruction list, removing any instructions + // that were inserted after the given instruction iterator, end_it. + while (inst_it != end_it) { assert(!instList.empty()); - // Obtain the pointer to the instruction. - inst_to_delete = instList.back(); + squashInstIt(inst_it, tid); - DPRINTF(FullCPU, "FullCPU: Removing instruction %i, PC %#x\n", - inst_to_delete->seqNum, inst_to_delete->readPC()); + inst_it--; + } - // Remove the instruction from the list. - instList.back() = NULL; - instList.pop_back(); + // If the ROB was empty, then we actually need to remove the first + // instruction as well. + if (rob_empty) { + squashInstIt(inst_it, tid); + } +} - // Mark it as squashed. - inst_to_delete->setSquashed(); +template <class Impl> +void +FullO3CPU<Impl>::removeInstsUntil(const InstSeqNum &seq_num, + unsigned tid) +{ + assert(!instList.empty()); + + removeInstsThisCycle = true; + + ListIt inst_iter = instList.end(); + + inst_iter--; + + DPRINTF(O3CPU, "Deleting instructions from instruction " + "list that are from [tid:%i] and above [sn:%lli] (end=%lli).\n", + tid, seq_num, (*inst_iter)->seqNum); + + while ((*inst_iter)->seqNum > seq_num) { + + bool break_loop = (inst_iter == instList.begin()); + + squashInstIt(inst_iter, tid); + + inst_iter--; + + if (break_loop) + break; } +} +template <class Impl> +inline void +FullO3CPU<Impl>::squashInstIt(const ListIt &instIt, const unsigned &tid) +{ + if ((*instIt)->threadNumber == tid) { + DPRINTF(O3CPU, "Squashing instruction, " + "[tid:%i] [sn:%lli] PC %#x\n", + (*instIt)->threadNumber, + (*instIt)->seqNum, + (*instIt)->readPC()); + + // Mark it as squashed. + (*instIt)->setSquashed(); + + // @todo: Formulate a consistent method for deleting + // instructions from the instruction list + // Remove the instruction from the list. + removeList.push(instIt); + } } template <class Impl> void +FullO3CPU<Impl>::cleanUpRemovedInsts() +{ + while (!removeList.empty()) { + DPRINTF(O3CPU, "Removing instruction, " + "[tid:%i] [sn:%lli] PC %#x\n", + (*removeList.front())->threadNumber, + (*removeList.front())->seqNum, + (*removeList.front())->readPC()); + + instList.erase(removeList.front()); + + removeList.pop(); + } + + removeInstsThisCycle = false; +} +/* +template <class Impl> +void FullO3CPU<Impl>::removeAllInsts() { instList.clear(); } - +*/ template <class Impl> void FullO3CPU<Impl>::dumpInsts() { int num = 0; - typename list<DynInstPtr>::iterator inst_list_it = instList.begin(); - while (inst_list_it != instList.end()) - { - cprintf("Instruction:%i\nPC:%#x\nSN:%lli\nIssued:%i\nSquashed:%i\n\n", - num, (*inst_list_it)->readPC(), (*inst_list_it)->seqNum, - (*inst_list_it)->isIssued(), (*inst_list_it)->isSquashed()); + ListIt inst_list_it = instList.begin(); + + cprintf("Dumping Instruction List\n"); + + while (inst_list_it != instList.end()) { + cprintf("Instruction:%i\nPC:%#x\n[tid:%i]\n[sn:%lli]\nIssued:%i\n" + "Squashed:%i\n\n", + num, (*inst_list_it)->readPC(), (*inst_list_it)->threadNumber, + (*inst_list_it)->seqNum, (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); inst_list_it++; ++num; } } - +/* template <class Impl> void FullO3CPU<Impl>::wakeDependents(DynInstPtr &inst) { iew.wakeDependents(inst); } +*/ +template <class Impl> +void +FullO3CPU<Impl>::wakeCPU() +{ + if (activityRec.active() || tickEvent.scheduled()) { + DPRINTF(Activity, "CPU already running.\n"); + return; + } + + DPRINTF(Activity, "Waking up CPU\n"); + + idleCycles += (curTick - 1) - lastRunningCycle; + + tickEvent.schedule(curTick); +} + +template <class Impl> +int +FullO3CPU<Impl>::getFreeTid() +{ + for (int i=0; i < numThreads; i++) { + if (!tids[i]) { + tids[i] = true; + return i; + } + } + + return -1; +} + +template <class Impl> +void +FullO3CPU<Impl>::doContextSwitch() +{ + if (contextSwitch) { + + //ADD CODE TO DEACTIVE THREAD HERE (???) + + for (int tid=0; tid < cpuWaitList.size(); tid++) { + activateWhenReady(tid); + } + + if (cpuWaitList.size() == 0) + contextSwitch = true; + } +} + +template <class Impl> +void +FullO3CPU<Impl>::updateThreadPriority() +{ + if (activeThreads.size() > 1) + { + //DEFAULT TO ROUND ROBIN SCHEME + //e.g. Move highest priority to end of thread list + list<unsigned>::iterator list_begin = activeThreads.begin(); + list<unsigned>::iterator list_end = activeThreads.end(); + + unsigned high_thread = *list_begin; + + activeThreads.erase(list_begin); + + activeThreads.push_back(high_thread); + } +} // Forward declaration of FullO3CPU. -template class FullO3CPU<AlphaSimpleImpl>; +template class FullO3CPU<O3CPUImpl>; diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh index f7c80e8a1..1cff6142d 100644 --- a/src/cpu/o3/cpu.hh +++ b/src/cpu/o3/cpu.hh @@ -24,120 +24,264 @@ * 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. + * + * Authors: Kevin Lim + * Korey Sewell */ -//Todo: Add in a lot of the functions that are ISA specific. Also define -//the functions that currently exist within the base cpu class. Define -//everything for the simobject stuff so it can be serialized and -//instantiated, add in debugging statements everywhere. Have CPU schedule -//itself properly. Threads! -// Avoid running stages and advancing queues if idle/stalled. - -#ifndef __CPU_O3_CPU_FULL_CPU_HH__ -#define __CPU_O3_CPU_FULL_CPU_HH__ +#ifndef __CPU_O3_CPU_HH__ +#define __CPU_O3_CPU_HH__ #include <iostream> #include <list> +#include <queue> +#include <set> #include <vector> +#include "arch/isa_traits.hh" #include "base/statistics.hh" #include "base/timebuf.hh" #include "config/full_system.hh" +#include "cpu/activity.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" +#include "cpu/simple_thread.hh" #include "cpu/o3/comm.hh" #include "cpu/o3/cpu_policy.hh" +#include "cpu/o3/scoreboard.hh" +#include "cpu/o3/thread_state.hh" +//#include "cpu/o3/thread_context.hh" #include "sim/process.hh" -class ExecContext; -class FunctionalMemory; +template <class> +class Checker; +class ThreadContext; +template <class> +class O3ThreadContext; +class MemObject; class Process; -class BaseFullCPU : public BaseCPU +class BaseO3CPU : public BaseCPU { //Stuff that's pretty ISA independent will go here. public: typedef BaseCPU::Params Params; -#if FULL_SYSTEM - BaseFullCPU(Params ¶ms); -#else - BaseFullCPU(Params ¶ms); -#endif // FULL_SYSTEM + BaseO3CPU(Params *params); + + void regStats(); + + /** Sets this CPU's ID. */ + void setCpuId(int id) { cpu_id = id; } + + /** Reads this CPU's ID. */ + int readCpuId() { return cpu_id; } protected: int cpu_id; }; +/** + * FullO3CPU class, has each of the stages (fetch through commit) + * within it, as well as all of the time buffers between stages. The + * tick() function for the CPU is defined here. + */ template <class Impl> -class FullO3CPU : public BaseFullCPU +class FullO3CPU : public BaseO3CPU { public: - //Put typedefs from the Impl here. + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + + // Typedefs from the Impl here. typedef typename Impl::CPUPol CPUPolicy; typedef typename Impl::Params Params; typedef typename Impl::DynInstPtr DynInstPtr; + typedef O3ThreadState<Impl> Thread; + + typedef typename std::list<DynInstPtr>::iterator ListIt; + + friend class O3ThreadContext<Impl>; + public: enum Status { Running, Idle, Halted, - Blocked // ? + Blocked, + SwitchedOut }; + /** Overall CPU status. */ Status _status; + /** Per-thread status in CPU, used for SMT. */ + Status _threadStatus[Impl::MaxThreads]; + private: class TickEvent : public Event { private: + /** Pointer to the CPU. */ FullO3CPU<Impl> *cpu; public: + /** Constructs a tick event. */ TickEvent(FullO3CPU<Impl> *c); + + /** Processes a tick event, calling tick() on the CPU. */ void process(); + /** Returns the description of the tick event. */ const char *description(); }; + /** The tick event used for scheduling CPU ticks. */ TickEvent tickEvent; - /// Schedule tick event, regardless of its current state. + /** Schedule tick event, regardless of its current state. */ void scheduleTickEvent(int delay) { if (tickEvent.squashed()) - tickEvent.reschedule(curTick + delay); + tickEvent.reschedule(curTick + cycles(delay)); else if (!tickEvent.scheduled()) - tickEvent.schedule(curTick + delay); + tickEvent.schedule(curTick + cycles(delay)); } - /// Unschedule tick event, regardless of its current state. + /** Unschedule tick event, regardless of its current state. */ void unscheduleTickEvent() { if (tickEvent.scheduled()) tickEvent.squash(); } + class ActivateThreadEvent : public Event + { + private: + /** Number of Thread to Activate */ + int tid; + + /** Pointer to the CPU. */ + FullO3CPU<Impl> *cpu; + + public: + /** Constructs the event. */ + ActivateThreadEvent(); + + /** Initialize Event */ + void init(int thread_num, FullO3CPU<Impl> *thread_cpu); + + /** Processes the event, calling activateThread() on the CPU. */ + void process(); + + /** Returns the description of the event. */ + const char *description(); + }; + + /** Schedule thread to activate , regardless of its current state. */ + void scheduleActivateThreadEvent(int tid, int delay) + { + // Schedule thread to activate, regardless of its current state. + if (activateThreadEvent[tid].squashed()) + activateThreadEvent[tid].reschedule(curTick + cycles(delay)); + else if (!activateThreadEvent[tid].scheduled()) + activateThreadEvent[tid].schedule(curTick + cycles(delay)); + } + + /** Unschedule actiavte thread event, regardless of its current state. */ + void unscheduleActivateThreadEvent(int tid) + { + if (activateThreadEvent[tid].scheduled()) + activateThreadEvent[tid].squash(); + } + + /** The tick event used for scheduling CPU ticks. */ + ActivateThreadEvent activateThreadEvent[Impl::MaxThreads]; + public: - FullO3CPU(Params ¶ms); + /** Constructs a CPU with the given parameters. */ + FullO3CPU(Params *params); + /** Destructor. */ ~FullO3CPU(); + /** Registers statistics. */ void fullCPURegStats(); + /** Ticks CPU, calling tick() on each stage, and checking the overall + * activity to see if the CPU should deschedule itself. + */ void tick(); + /** Initialize the CPU */ void init(); - void activateContext(int thread_num, int delay); - void suspendContext(int thread_num); - void deallocateContext(int thread_num); - void haltContext(int thread_num); + /** Returns the Number of Active Threads in the CPU */ + int numActiveThreads() + { return activeThreads.size(); } + + /** Add Thread to Active Threads List */ + void activateThread(unsigned int tid); + + /** Setup CPU to insert a thread's context */ + void insertThread(unsigned tid); + + /** Remove all of a thread's context from CPU */ + void removeThread(unsigned tid); + + /** Count the Total Instructions Committed in the CPU. */ + virtual Counter totalInstructions() const + { + Counter total(0); + + for (int i=0; i < thread.size(); i++) + total += thread[i]->numInst; + + return total; + } + + /** Add Thread to Active Threads List. */ + void activateContext(int tid, int delay); + + /** Remove Thread from Active Threads List */ + void suspendContext(int tid); + + /** Remove Thread from Active Threads List && + * Remove Thread Context from CPU. + */ + void deallocateContext(int tid); + + /** Remove Thread from Active Threads List && + * Remove Thread Context from CPU. + */ + void haltContext(int tid); + + /** Activate a Thread When CPU Resources are Available. */ + void activateWhenReady(int tid); + + /** Add or Remove a Thread Context in the CPU. */ + void doContextSwitch(); + + /** Update The Order In Which We Process Threads. */ + void updateThreadPriority(); + + /** Executes a syscall on this cycle. + * --------------------------------------- + * Note: this is a virtual function. CPU-Specific + * functionality defined in derived classes + */ + virtual void syscall(int tid) { panic("Unimplemented!"); } + + /** Switches out this CPU. */ + void switchOut(Sampler *sampler); + + /** Signals to this CPU that a stage has completed switching out. */ + void signalSwitched(); - void switchOut(); + /** Takes over from another CPU. */ void takeOverFrom(BaseCPU *oldCPU); /** Get the current instruction sequence number, and increment it. */ - InstSeqNum getAndIncrementInstSeq(); + InstSeqNum getAndIncrementInstSeq() + { return globalSeqNum++; } #if FULL_SYSTEM /** Check if this address is a valid instruction address. */ @@ -147,27 +291,24 @@ class FullO3CPU : public BaseFullCPU bool validDataAddr(Addr addr) { return true; } /** Get instruction asid. */ - int getInstAsid() - { return regFile.miscRegs.getInstAsid(); } + int getInstAsid(unsigned tid) + { return regFile.miscRegs[tid].getInstAsid(); } /** Get data asid. */ - int getDataAsid() - { return regFile.miscRegs.getDataAsid(); } + int getDataAsid(unsigned tid) + { return regFile.miscRegs[tid].getDataAsid(); } #else - bool validInstAddr(Addr addr) - { return thread[0]->validInstAddr(addr); } - - bool validDataAddr(Addr addr) - { return thread[0]->validDataAddr(addr); } + /** Get instruction asid. */ + int getInstAsid(unsigned tid) + { return thread[tid]->getInstAsid(); } - int getInstAsid() { return thread[0]->getInstAsid(); } - int getDataAsid() { return thread[0]->getDataAsid(); } + /** Get data asid. */ + int getDataAsid(unsigned tid) + { return thread[tid]->getDataAsid(); } #endif - // - // New accessors for new decoder. - // + /** Register accessors. Index refers to the physical register index. */ uint64_t readIntReg(int reg_idx); FloatReg readFloatReg(int reg_idx); @@ -180,103 +321,121 @@ class FullO3CPU : public BaseFullCPU void setIntReg(int reg_idx, uint64_t val); - void setFloatReg(int reg_idx, FloatReg val, int width); + void setFloatReg(int reg_idx, FloatReg val); void setFloatReg(int reg_idx, FloatReg val, int width); void setFloatRegBits(int reg_idx, FloatRegBits val); - void setFloatRegBits(int reg_idx, FloatRegBits val); + void setFloatRegBits(int reg_idx, FloatRegBits val, int width); + + uint64_t readArchIntReg(int reg_idx, unsigned tid); + + float readArchFloatRegSingle(int reg_idx, unsigned tid); + + double readArchFloatRegDouble(int reg_idx, unsigned tid); + + uint64_t readArchFloatRegInt(int reg_idx, unsigned tid); + + /** Architectural register accessors. Looks up in the commit + * rename table to obtain the true physical index of the + * architected register first, then accesses that physical + * register. + */ + void setArchIntReg(int reg_idx, uint64_t val, unsigned tid); + + void setArchFloatRegSingle(int reg_idx, float val, unsigned tid); + + void setArchFloatRegDouble(int reg_idx, double val, unsigned tid); + + void setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid); - uint64_t readPC(); + /** Reads the commit PC of a specific thread. */ + uint64_t readPC(unsigned tid); - void setNextPC(uint64_t val); + /** Sets the commit PC of a specific thread. */ + void setPC(Addr new_PC, unsigned tid); - void setPC(Addr new_PC); + /** Reads the next PC of a specific thread. */ + uint64_t readNextPC(unsigned tid); + + /** Sets the next PC of a specific thread. */ + void setNextPC(uint64_t val, unsigned tid); + + /** Reads the next NPC of a specific thread. */ + uint64_t readNextNPC(unsigned tid); + + /** Sets the next NPC of a specific thread. */ + void setNextNPC(uint64_t val, unsigned tid); /** Function to add instruction onto the head of the list of the * instructions. Used when new instructions are fetched. */ - void addInst(DynInstPtr &inst); + ListIt addInst(DynInstPtr &inst); /** Function to tell the CPU that an instruction has completed. */ - void instDone(); - - /** Remove all instructions in back of the given instruction, but leave - * that instruction in the list. This is useful in a squash, when there - * are instructions in this list that don't exist in structures such as - * the ROB. The instruction doesn't have to be the last instruction in - * the list, but will be once this function completes. - * @todo: Remove only up until that inst? Squashed inst is most likely - * valid. - */ - void removeBackInst(DynInstPtr &inst); - - /** Remove an instruction from the front of the list. It is expected - * that there are no instructions in front of it (that is, none are older - * than the instruction being removed). Used when retiring instructions. - * @todo: Remove the argument to this function, and just have it remove - * last instruction once it's verified that commit has the same ordering - * as the instruction list. + void instDone(unsigned tid); + + /** Add Instructions to the CPU Remove List*/ + void addToRemoveList(DynInstPtr &inst); + + /** Remove an instruction from the front end of the list. There's + * no restriction on location of the instruction. */ void removeFrontInst(DynInstPtr &inst); /** Remove all instructions that are not currently in the ROB. */ - void removeInstsNotInROB(); + void removeInstsNotInROB(unsigned tid); /** Remove all instructions younger than the given sequence number. */ - void removeInstsUntil(const InstSeqNum &seq_num); + void removeInstsUntil(const InstSeqNum &seq_num,unsigned tid); - /** Remove all instructions from the list. */ - void removeAllInsts(); + /** Removes the instruction pointed to by the iterator. */ + inline void squashInstIt(const ListIt &instIt, const unsigned &tid); - void dumpInsts(); + /** Cleans up all instructions on the remove list. */ + void cleanUpRemovedInsts(); - /** Basically a wrapper function so that instructions executed at - * commit can tell the instruction queue that they have completed. - * Eventually this hack should be removed. - */ - void wakeDependents(DynInstPtr &inst); + /** Debug function to print all instructions on the list. */ + void dumpInsts(); public: /** List of all the instructions in flight. */ - list<DynInstPtr> instList; + std::list<DynInstPtr> instList; + + /** List of all the instructions that will be removed at the end of this + * cycle. + */ + std::queue<ListIt> removeList; + +#ifdef DEBUG + /** Debug structure to keep track of the sequence numbers still in + * flight. + */ + std::set<InstSeqNum> snList; +#endif + + /** Records if instructions need to be removed this cycle due to + * being retired or squashed. + */ + bool removeInstsThisCycle; - //not sure these should be private. protected: /** The fetch stage. */ typename CPUPolicy::Fetch fetch; - /** The fetch stage's status. */ - typename CPUPolicy::Fetch::Status fetchStatus; - /** The decode stage. */ typename CPUPolicy::Decode decode; - /** The decode stage's status. */ - typename CPUPolicy::Decode::Status decodeStatus; - /** The dispatch stage. */ typename CPUPolicy::Rename rename; - /** The dispatch stage's status. */ - typename CPUPolicy::Rename::Status renameStatus; - /** The issue/execute/writeback stages. */ typename CPUPolicy::IEW iew; - /** The issue/execute/writeback stage's status. */ - typename CPUPolicy::IEW::Status iewStatus; - /** The commit stage. */ typename CPUPolicy::Commit commit; - /** The fetch stage's status. */ - typename CPUPolicy::Commit::Status commitStatus; - - //Might want to just pass these objects in to the constructors of the - //appropriate stage. regFile is in iew, freeList in dispatch, renameMap - //in dispatch, and the rob in commit. /** The register file. */ typename CPUPolicy::RegFile regFile; @@ -284,12 +443,33 @@ class FullO3CPU : public BaseFullCPU typename CPUPolicy::FreeList freeList; /** The rename map. */ - typename CPUPolicy::RenameMap renameMap; + typename CPUPolicy::RenameMap renameMap[Impl::MaxThreads]; + + /** The commit rename map. */ + typename CPUPolicy::RenameMap commitRenameMap[Impl::MaxThreads]; /** The re-order buffer. */ typename CPUPolicy::ROB rob; + /** Active Threads List */ + std::list<unsigned> activeThreads; + + /** Integer Register Scoreboard */ + Scoreboard scoreboard; + public: + /** Enum to give each stage a specific index, so when calling + * activateStage() or deactivateStage(), they can specify which stage + * is being activated/deactivated. + */ + enum StageIdx { + FetchIdx, + DecodeIdx, + RenameIdx, + IEWIdx, + CommitIdx, + NumStages }; + /** Typedefs from the Impl to get the structs that each of the * time buffers should use. */ @@ -318,46 +498,112 @@ class FullO3CPU : public BaseFullCPU /** The IEW stage's instruction queue. */ TimeBuffer<IEWStruct> iewQueue; + private: + /** The activity recorder; used to tell if the CPU has any + * activity remaining or if it can go to idle and deschedule + * itself. + */ + ActivityRecorder activityRec; + public: - /** The temporary exec context to support older accessors. */ - CPUExecContext *cpuXC; + /** Records that there was time buffer activity this cycle. */ + void activityThisCycle() { activityRec.activity(); } - /** Temporary function to get pointer to exec context. */ - ExecContext *xcBase() - { - return thread[0]->getProxy(); - } + /** Changes a stage's status to active within the activity recorder. */ + void activateStage(const StageIdx idx) + { activityRec.activateStage(idx); } + + /** Changes a stage's status to inactive within the activity recorder. */ + void deactivateStage(const StageIdx idx) + { activityRec.deactivateStage(idx); } - CPUExecContext *cpuXCBase() + /** Wakes the CPU, rescheduling the CPU if it's not already active. */ + void wakeCPU(); + + /** Gets a free thread id. Use if thread ids change across system. */ + int getFreeTid(); + + public: + /** Returns a pointer to a thread context. */ + ThreadContext *tcBase(unsigned tid) { - return thread[0]; + return thread[tid]->getTC(); } + /** The global sequence number counter. */ InstSeqNum globalSeqNum; + /** Pointer to the checker, which can dynamically verify + * instruction results at run time. This can be set to NULL if it + * is not being used. + */ + Checker<DynInstPtr> *checker; + #if FULL_SYSTEM + /** Pointer to the system. */ System *system; - MemoryController *memCtrl; + /** Pointer to physical memory. */ PhysicalMemory *physmem; +#endif - AlphaITB *itb; - AlphaDTB *dtb; + /** Pointer to memory. */ + MemObject *mem; -// SWContext *swCtx; -#endif - std::vector<CPUExecContext *> thread; + /** Pointer to the sampler */ + Sampler *sampler; - FunctionalMemory *mem; + /** Counter of how many stages have completed switching out. */ + int switchCount; + /** Pointers to all of the threads in the CPU. */ + std::vector<Thread *> thread; + + /** Pointer to the icache interface. */ MemInterface *icacheInterface; + /** Pointer to the dcache interface. */ MemInterface *dcacheInterface; + /** Whether or not the CPU should defer its registration. */ bool deferRegistration; - Counter numInsts; - - Counter funcExeInst; + /** Is there a context switch pending? */ + bool contextSwitch; + + /** Threads Scheduled to Enter CPU */ + std::list<int> cpuWaitList; + + /** The cycle that the CPU was last running, used for statistics. */ + Tick lastRunningCycle; + + /** The cycle that the CPU was last activated by a new thread*/ + Tick lastActivatedCycle; + + /** Number of Threads CPU can process */ + unsigned numThreads; + + /** Mapping for system thread id to cpu id */ + std::map<unsigned,unsigned> threadMap; + + /** Available thread ids in the cpu*/ + std::vector<unsigned> tids; + + /** Stat for total number of times the CPU is descheduled. */ + Stats::Scalar<> timesIdled; + /** Stat for total number of cycles the CPU spends descheduled. */ + Stats::Scalar<> idleCycles; + /** Stat for the number of committed instructions per thread. */ + Stats::Vector<> committedInsts; + /** Stat for the total number of committed instructions. */ + Stats::Scalar<> totalCommittedInsts; + /** Stat for the CPI per thread. */ + Stats::Formula cpi; + /** Stat for the total CPI. */ + Stats::Formula totalCpi; + /** Stat for the IPC per thread. */ + Stats::Formula ipc; + /** Stat for the total IPC. */ + Stats::Formula totalIpc; }; -#endif +#endif // __CPU_O3_CPU_HH__ diff --git a/src/cpu/o3/cpu_policy.hh b/src/cpu/o3/cpu_policy.hh index 41f06f81b..32a0adcf1 100644 --- a/src/cpu/o3/cpu_policy.hh +++ b/src/cpu/o3/cpu_policy.hh @@ -24,15 +24,18 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_CPU_POLICY_HH__ -#define __CPU_O3_CPU_CPU_POLICY_HH__ +#ifndef __CPU_O3_CPU_POLICY_HH__ +#define __CPU_O3_CPU_POLICY_HH__ #include "cpu/o3/bpred_unit.hh" #include "cpu/o3/free_list.hh" #include "cpu/o3/inst_queue.hh" -#include "cpu/o3/ldstq.hh" +#include "cpu/o3/lsq.hh" +#include "cpu/o3/lsq_unit.hh" #include "cpu/o3/mem_dep_unit.hh" #include "cpu/o3/regfile.hh" #include "cpu/o3/rename_map.hh" @@ -47,42 +50,70 @@ #include "cpu/o3/comm.hh" +/** + * Struct that defines the key classes to be used by the CPU. All + * classes use the typedefs defined here to determine what are the + * classes of the other stages and communication buffers. In order to + * change a structure such as the IQ, simply change the typedef here + * to use the desired class instead, and recompile. In order to + * create a different CPU to be used simultaneously with this one, see + * the alpha_impl.hh file for instructions. + */ template<class Impl> struct SimpleCPUPolicy { - typedef TwobitBPredUnit<Impl> BPredUnit; + /** Typedef for the branch prediction unit (which includes the BP, + * RAS, and BTB). + */ + typedef BPredUnit<Impl> BPredUnit; + /** Typedef for the register file. Most classes assume a unified + * physical register file. + */ typedef PhysRegFile<Impl> RegFile; + /** Typedef for the freelist of registers. */ typedef SimpleFreeList FreeList; + /** Typedef for the rename map. */ typedef SimpleRenameMap RenameMap; + /** Typedef for the ROB. */ typedef ROB<Impl> ROB; + /** Typedef for the instruction queue/scheduler. */ typedef InstructionQueue<Impl> IQ; + /** Typedef for the memory dependence unit. */ typedef MemDepUnit<StoreSet, Impl> MemDepUnit; - typedef LDSTQ<Impl> LDSTQ; + /** Typedef for the LSQ. */ + typedef LSQ<Impl> LSQ; + /** Typedef for the thread-specific LSQ units. */ + typedef LSQUnit<Impl> LSQUnit; - typedef SimpleFetch<Impl> Fetch; - typedef SimpleDecode<Impl> Decode; - typedef SimpleRename<Impl> Rename; - typedef SimpleIEW<Impl> IEW; - typedef SimpleCommit<Impl> Commit; + /** Typedef for fetch. */ + typedef DefaultFetch<Impl> Fetch; + /** Typedef for decode. */ + typedef DefaultDecode<Impl> Decode; + /** Typedef for rename. */ + typedef DefaultRename<Impl> Rename; + /** Typedef for Issue/Execute/Writeback. */ + typedef DefaultIEW<Impl> IEW; + /** Typedef for commit. */ + typedef DefaultCommit<Impl> Commit; /** The struct for communication between fetch and decode. */ - typedef SimpleFetchSimpleDecode<Impl> FetchStruct; + typedef DefaultFetchDefaultDecode<Impl> FetchStruct; /** The struct for communication between decode and rename. */ - typedef SimpleDecodeSimpleRename<Impl> DecodeStruct; + typedef DefaultDecodeDefaultRename<Impl> DecodeStruct; /** The struct for communication between rename and IEW. */ - typedef SimpleRenameSimpleIEW<Impl> RenameStruct; + typedef DefaultRenameDefaultIEW<Impl> RenameStruct; /** The struct for communication between IEW and commit. */ - typedef SimpleIEWSimpleCommit<Impl> IEWStruct; + typedef DefaultIEWDefaultCommit<Impl> IEWStruct; /** The struct for communication within the IEW stage. */ typedef IssueStruct<Impl> IssueStruct; /** The struct for all backwards communication. */ - typedef TimeBufStruct TimeStruct; + typedef TimeBufStruct<Impl> TimeStruct; }; -#endif //__CPU_O3_CPU_CPU_POLICY_HH__ +#endif //__CPU_O3_CPU_POLICY_HH__ diff --git a/src/cpu/o3/decode.cc b/src/cpu/o3/decode.cc index 290648318..896e38331 100644 --- a/src/cpu/o3/decode.cc +++ b/src/cpu/o3/decode.cc @@ -24,10 +24,11 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/isa_specific.hh" #include "cpu/o3/decode_impl.hh" -template class SimpleDecode<AlphaSimpleImpl>; +template class DefaultDecode<O3CPUImpl>; diff --git a/src/cpu/o3/decode.hh b/src/cpu/o3/decode.hh index 5b9a0f822..1edf3335d 100644 --- a/src/cpu/o3/decode.hh +++ b/src/cpu/o3/decode.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,22 +24,31 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_SIMPLE_DECODE_HH__ -#define __CPU_O3_CPU_SIMPLE_DECODE_HH__ +#ifndef __CPU_O3_DECODE_HH__ +#define __CPU_O3_DECODE_HH__ #include <queue> #include "base/statistics.hh" #include "base/timebuf.hh" +/** + * DefaultDecode class handles both single threaded and SMT + * decode. Its width is specified by the parameters; each cycles it + * tries to decode that many instructions. Because instructions are + * actually decoded when the StaticInst is created, this stage does + * not do much other than check any PC-relative branches. + */ template<class Impl> -class SimpleDecode +class DefaultDecode { private: // Typedefs from the Impl. - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::O3CPU O3CPU; typedef typename Impl::DynInstPtr DynInstPtr; typedef typename Impl::Params Params; typedef typename Impl::CPUPol CPUPol; @@ -50,54 +59,137 @@ class SimpleDecode typedef typename CPUPol::TimeStruct TimeStruct; public: - // The only time decode will become blocked is if dispatch becomes - // blocked, which means IQ or ROB is probably full. - enum Status { + /** Overall decode stage status. Used to determine if the CPU can + * deschedule itself due to a lack of activity. + */ + enum DecodeStatus { + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { Running, Idle, + StartSquash, Squashing, Blocked, Unblocking }; private: - // May eventually need statuses on a per thread basis. - Status _status; + /** Decode status. */ + DecodeStatus _status; + + /** Per-thread status. */ + ThreadStatus decodeStatus[Impl::MaxThreads]; public: - SimpleDecode(Params ¶ms); + /** DefaultDecode constructor. */ + DefaultDecode(Params *params); + + /** Returns the name of decode. */ + std::string name() const; + /** Registers statistics. */ void regStats(); - void setCPU(FullCPU *cpu_ptr); + /** Sets CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + /** Sets the main backwards communication time buffer pointer. */ void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + /** Sets pointer to time buffer used to communicate to the next stage. */ void setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr); + /** Sets pointer to time buffer coming from fetch. */ void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Switches out the decode stage. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Ticks decode, processing all input signals and decoding as many + * instructions as possible. + */ void tick(); - void decode(); + /** Determines what to do based on decode's current status. + * @param status_change decode() sets this variable if there was a status + * change (ie switching from from blocking to unblocking). + * @param tid Thread id to decode instructions from. + */ + void decode(bool &status_change, unsigned tid); + + /** Processes instructions from fetch and passes them on to rename. + * Decoding of instructions actually happens when they are created in + * fetch, so this function mostly checks if PC-relative branches are + * correct. + */ + void decodeInsts(unsigned tid); private: + /** Inserts a thread's instructions into the skid buffer, to be decoded + * once decode unblocks. + */ + void skidInsert(unsigned tid); + + /** Returns if all of the skid buffers are empty. */ + bool skidsEmpty(); + + /** Updates overall decode status based on all of the threads' statuses. */ + void updateStatus(); + + /** Separates instructions from fetch into individual lists of instructions + * sorted by thread. + */ + void sortInsts(); + + /** Reads all stall signals from the backwards communication timebuffer. */ + void readStallSignals(unsigned tid); + + /** Checks all input signals and updates decode's status appropriately. */ + bool checkSignalsAndUpdate(unsigned tid); + + /** Checks all stall signals, and returns if any are true. */ + bool checkStall(unsigned tid) const; + + /** Returns if there any instructions from fetch on this cycle. */ inline bool fetchInstsValid(); - void block(); + /** Switches decode to blocking, and signals back that decode has + * become blocked. + * @return Returns true if there is a status change. + */ + bool block(unsigned tid); - inline void unblock(); + /** Switches decode to unblocking if the skid buffer is empty, and + * signals back that decode has unblocked. + * @return Returns true if there is a status change. + */ + bool unblock(unsigned tid); - void squash(DynInstPtr &inst); + /** Squashes if there is a PC-relative branch that was predicted + * incorrectly. Sends squash information back to fetch. + */ + void squash(DynInstPtr &inst, unsigned tid); public: - // Might want to make squash a friend function. - void squash(); + /** Squashes due to commit signalling a squash. Changes status to + * squashing and clears block/unblock signals as needed. + */ + unsigned squash(unsigned tid); private: // Interfaces to objects outside of decode. /** CPU interface. */ - FullCPU *cpu; + O3CPU *cpu; /** Time buffer interface. */ TimeBuffer<TimeStruct> *timeBuffer; @@ -127,10 +219,27 @@ class SimpleDecode /** Wire to get fetch's output from fetch queue. */ typename TimeBuffer<FetchStruct>::wire fromFetch; + /** Queue of all instructions coming from fetch this cycle. */ + std::queue<DynInstPtr> insts[Impl::MaxThreads]; + /** Skid buffer between fetch and decode. */ - std::queue<FetchStruct> skidBuffer; + std::queue<DynInstPtr> skidBuffer[Impl::MaxThreads]; + + /** Variable that tracks if decode has written to the time buffer this + * cycle. Used to tell CPU if there is activity this cycle. + */ + bool wroteToTimeBuffer; + + /** Source of possible stalls. */ + struct Stalls { + bool rename; + bool iew; + bool commit; + }; + + /** Tracks which stages are telling decode to stall. */ + Stalls stalls[Impl::MaxThreads]; - //Consider making these unsigned to avoid any confusion. /** Rename to decode delay, in ticks. */ unsigned renameToDecodeDelay; @@ -146,20 +255,43 @@ class SimpleDecode /** The width of decode, in instructions. */ unsigned decodeWidth; - /** The instruction that decode is currently on. It needs to have - * persistent state so that when a stall occurs in the middle of a - * group of instructions, it can restart at the proper instruction. - */ - unsigned numInst; + /** Index of instructions being sent to rename. */ + unsigned toRenameIndex; + + /** number of Active Threads*/ + unsigned numThreads; + /** List of active thread ids */ + std::list<unsigned> *activeThreads; + + /** Number of branches in flight. */ + unsigned branchCount[Impl::MaxThreads]; + + /** Maximum size of the skid buffer. */ + unsigned skidBufferMax; + + /** Stat for total number of idle cycles. */ Stats::Scalar<> decodeIdleCycles; + /** Stat for total number of blocked cycles. */ Stats::Scalar<> decodeBlockedCycles; + /** Stat for total number of normal running cycles. */ + Stats::Scalar<> decodeRunCycles; + /** Stat for total number of unblocking cycles. */ Stats::Scalar<> decodeUnblockCycles; + /** Stat for total number of squashing cycles. */ Stats::Scalar<> decodeSquashCycles; + /** Stat for number of times a branch is resolved at decode. */ + Stats::Scalar<> decodeBranchResolved; + /** Stat for number of times a branch mispredict is detected. */ Stats::Scalar<> decodeBranchMispred; + /** Stat for number of times decode detected a non-control instruction + * incorrectly predicted as a branch. + */ Stats::Scalar<> decodeControlMispred; + /** Stat for total number of decoded instructions. */ Stats::Scalar<> decodeDecodedInsts; + /** Stat for total number of squashed instructions. */ Stats::Scalar<> decodeSquashedInsts; }; -#endif // __CPU_O3_CPU_SIMPLE_DECODE_HH__ +#endif // __CPU_O3_DECODE_HH__ diff --git a/src/cpu/o3/decode_impl.hh b/src/cpu/o3/decode_impl.hh index 463f0ddac..16be01784 100644 --- a/src/cpu/o3/decode_impl.hh +++ b/src/cpu/o3/decode_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,75 +24,105 @@ * 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. + * + * Authors: Kevin Lim */ #include "cpu/o3/decode.hh" +using namespace std; + template<class Impl> -SimpleDecode<Impl>::SimpleDecode(Params ¶ms) - : renameToDecodeDelay(params.renameToDecodeDelay), - iewToDecodeDelay(params.iewToDecodeDelay), - commitToDecodeDelay(params.commitToDecodeDelay), - fetchToDecodeDelay(params.fetchToDecodeDelay), - decodeWidth(params.decodeWidth), - numInst(0) +DefaultDecode<Impl>::DefaultDecode(Params *params) + : renameToDecodeDelay(params->renameToDecodeDelay), + iewToDecodeDelay(params->iewToDecodeDelay), + commitToDecodeDelay(params->commitToDecodeDelay), + fetchToDecodeDelay(params->fetchToDecodeDelay), + decodeWidth(params->decodeWidth), + numThreads(params->numberOfThreads) { - DPRINTF(Decode, "Decode: decodeWidth=%i.\n", decodeWidth); - _status = Idle; + _status = Inactive; + + // Setup status, make sure stall signals are clear. + for (int i = 0; i < numThreads; ++i) { + decodeStatus[i] = Idle; + + stalls[i].rename = false; + stalls[i].iew = false; + stalls[i].commit = false; + } + + // @todo: Make into a parameter + skidBufferMax = (fetchToDecodeDelay * params->fetchWidth) + decodeWidth; +} + +template <class Impl> +std::string +DefaultDecode<Impl>::name() const +{ + return cpu->name() + ".decode"; } template <class Impl> void -SimpleDecode<Impl>::regStats() +DefaultDecode<Impl>::regStats() { decodeIdleCycles - .name(name() + ".decodeIdleCycles") + .name(name() + ".DECODE:IdleCycles") .desc("Number of cycles decode is idle") .prereq(decodeIdleCycles); decodeBlockedCycles - .name(name() + ".decodeBlockedCycles") + .name(name() + ".DECODE:BlockedCycles") .desc("Number of cycles decode is blocked") .prereq(decodeBlockedCycles); + decodeRunCycles + .name(name() + ".DECODE:RunCycles") + .desc("Number of cycles decode is running") + .prereq(decodeRunCycles); decodeUnblockCycles - .name(name() + ".decodeUnblockCycles") + .name(name() + ".DECODE:UnblockCycles") .desc("Number of cycles decode is unblocking") .prereq(decodeUnblockCycles); decodeSquashCycles - .name(name() + ".decodeSquashCycles") + .name(name() + ".DECODE:SquashCycles") .desc("Number of cycles decode is squashing") .prereq(decodeSquashCycles); + decodeBranchResolved + .name(name() + ".DECODE:BranchResolved") + .desc("Number of times decode resolved a branch") + .prereq(decodeBranchResolved); decodeBranchMispred - .name(name() + ".decodeBranchMispred") + .name(name() + ".DECODE:BranchMispred") .desc("Number of times decode detected a branch misprediction") .prereq(decodeBranchMispred); decodeControlMispred - .name(name() + ".decodeControlMispred") + .name(name() + ".DECODE:ControlMispred") .desc("Number of times decode detected an instruction incorrectly" " predicted as a control") .prereq(decodeControlMispred); decodeDecodedInsts - .name(name() + ".decodeDecodedInsts") + .name(name() + ".DECODE:DecodedInsts") .desc("Number of instructions handled by decode") .prereq(decodeDecodedInsts); decodeSquashedInsts - .name(name() + ".decodeSquashedInsts") + .name(name() + ".DECODE:SquashedInsts") .desc("Number of squashed instructions handled by decode") .prereq(decodeSquashedInsts); } template<class Impl> void -SimpleDecode<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultDecode<Impl>::setCPU(O3CPU *cpu_ptr) { - DPRINTF(Decode, "Decode: Setting CPU pointer.\n"); + DPRINTF(Decode, "Setting CPU pointer.\n"); cpu = cpu_ptr; } template<class Impl> void -SimpleDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +DefaultDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) { - DPRINTF(Decode, "Decode: Setting time buffer pointer.\n"); + DPRINTF(Decode, "Setting time buffer pointer.\n"); timeBuffer = tb_ptr; // Setup wire to write information back to fetch. @@ -106,9 +136,9 @@ SimpleDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) template<class Impl> void -SimpleDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) +DefaultDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) { - DPRINTF(Decode, "Decode: Setting decode queue pointer.\n"); + DPRINTF(Decode, "Setting decode queue pointer.\n"); decodeQueue = dq_ptr; // Setup wire to write information to proper place in decode queue. @@ -117,9 +147,9 @@ SimpleDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) template<class Impl> void -SimpleDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) +DefaultDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) { - DPRINTF(Decode, "Decode: Setting fetch queue pointer.\n"); + DPRINTF(Decode, "Setting fetch queue pointer.\n"); fetchQueue = fq_ptr; // Setup wire to read information from fetch queue. @@ -127,250 +157,541 @@ SimpleDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) } template<class Impl> +void +DefaultDecode<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(Decode, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template <class Impl> +void +DefaultDecode<Impl>::switchOut() +{ + // Decode can immediately switch out. + cpu->signalSwitched(); +} + +template <class Impl> +void +DefaultDecode<Impl>::takeOverFrom() +{ + _status = Inactive; + + // Be sure to reset state and clear out any old instructions. + for (int i = 0; i < numThreads; ++i) { + decodeStatus[i] = Idle; + + stalls[i].rename = false; + stalls[i].iew = false; + stalls[i].commit = false; + while (!insts[i].empty()) + insts[i].pop(); + while (!skidBuffer[i].empty()) + skidBuffer[i].pop(); + branchCount[i] = 0; + } + wroteToTimeBuffer = false; +} + +template<class Impl> +bool +DefaultDecode<Impl>::checkStall(unsigned tid) const +{ + bool ret_val = false; + + if (stalls[tid].rename) { + DPRINTF(Decode,"[tid:%i]: Stall fom Rename stage detected.\n", tid); + ret_val = true; + } else if (stalls[tid].iew) { + DPRINTF(Decode,"[tid:%i]: Stall fom IEW stage detected.\n", tid); + ret_val = true; + } else if (stalls[tid].commit) { + DPRINTF(Decode,"[tid:%i]: Stall fom Commit stage detected.\n", tid); + ret_val = true; + } + + return ret_val; +} + +template<class Impl> inline bool -SimpleDecode<Impl>::fetchInstsValid() +DefaultDecode<Impl>::fetchInstsValid() { return fromFetch->size > 0; } template<class Impl> -void -SimpleDecode<Impl>::block() +bool +DefaultDecode<Impl>::block(unsigned tid) { - DPRINTF(Decode, "Decode: Blocking.\n"); - - // Set the status to Blocked. - _status = Blocked; + DPRINTF(Decode, "[tid:%u]: Blocking.\n", tid); // Add the current inputs to the skid buffer so they can be // reprocessed when this stage unblocks. - skidBuffer.push(*fromFetch); + skidInsert(tid); + + // If the decode status is blocked or unblocking then decode has not yet + // signalled fetch to unblock. In that case, there is no need to tell + // fetch to block. + if (decodeStatus[tid] != Blocked) { + // Set the status to Blocked. + decodeStatus[tid] = Blocked; + + if (decodeStatus[tid] != Unblocking) { + toFetch->decodeBlock[tid] = true; + wroteToTimeBuffer = true; + } - // Note that this stage only signals previous stages to stall when - // it is the cause of the stall originates at this stage. Otherwise - // the previous stages are expected to check all possible stall signals. + return true; + } + + return false; } template<class Impl> -inline void -SimpleDecode<Impl>::unblock() +bool +DefaultDecode<Impl>::unblock(unsigned tid) { - DPRINTF(Decode, "Decode: Unblocking, going to remove " - "instructions from skid buffer.\n"); - // Remove the now processed instructions from the skid buffer. - skidBuffer.pop(); - - // If there's still information in the skid buffer, then - // continue to tell previous stages to stall. They will be - // able to restart once the skid buffer is empty. - if (!skidBuffer.empty()) { - toFetch->decodeInfo.stall = true; - } else { - DPRINTF(Decode, "Decode: Finished unblocking.\n"); - _status = Running; + // Decode is done unblocking only if the skid buffer is empty. + if (skidBuffer[tid].empty()) { + DPRINTF(Decode, "[tid:%u]: Done unblocking.\n", tid); + toFetch->decodeUnblock[tid] = true; + wroteToTimeBuffer = true; + + decodeStatus[tid] = Running; + return true; } + + DPRINTF(Decode, "[tid:%u]: Currently unblocking.\n", tid); + + return false; } -// This squash is specifically for when Decode detects a PC-relative branch -// was predicted incorrectly. template<class Impl> void -SimpleDecode<Impl>::squash(DynInstPtr &inst) +DefaultDecode<Impl>::squash(DynInstPtr &inst, unsigned tid) { - DPRINTF(Decode, "Decode: Squashing due to incorrect branch prediction " - "detected at decode.\n"); - Addr new_PC = inst->readNextPC(); - - toFetch->decodeInfo.branchMispredict = true; - toFetch->decodeInfo.doneSeqNum = inst->seqNum; - toFetch->decodeInfo.predIncorrect = true; - toFetch->decodeInfo.squash = true; - toFetch->decodeInfo.nextPC = new_PC; - toFetch->decodeInfo.branchTaken = true; + DPRINTF(Decode, "[tid:%i]: Squashing due to incorrect branch prediction " + "detected at decode.\n", tid); + + // Send back mispredict information. + toFetch->decodeInfo[tid].branchMispredict = true; + toFetch->decodeInfo[tid].doneSeqNum = inst->seqNum; + toFetch->decodeInfo[tid].predIncorrect = true; + toFetch->decodeInfo[tid].squash = true; + toFetch->decodeInfo[tid].nextPC = inst->branchTarget(); + toFetch->decodeInfo[tid].branchTaken = + inst->readNextPC() != (inst->readPC() + sizeof(TheISA::MachInst)); + + // Might have to tell fetch to unblock. + if (decodeStatus[tid] == Blocked || + decodeStatus[tid] == Unblocking) { + toFetch->decodeUnblock[tid] = 1; + } // Set status to squashing. - _status = Squashing; + decodeStatus[tid] = Squashing; + + for (int i=0; i<fromFetch->size; i++) { + if (fromFetch->insts[i]->threadNumber == tid && + fromFetch->insts[i]->seqNum > inst->seqNum) { + fromFetch->insts[i]->setSquashed(); + } + } + + // Clear the instruction list and skid buffer in case they have any + // insts in them. + while (!insts[tid].empty()) { + insts[tid].pop(); + } - // Clear the skid buffer in case it has any data in it. - while (!skidBuffer.empty()) { - skidBuffer.pop(); + while (!skidBuffer[tid].empty()) { + skidBuffer[tid].pop(); } // Squash instructions up until this one - // Slightly unrealistic! - cpu->removeInstsUntil(inst->seqNum); + cpu->removeInstsUntil(inst->seqNum, tid); } template<class Impl> -void -SimpleDecode<Impl>::squash() +unsigned +DefaultDecode<Impl>::squash(unsigned tid) { - DPRINTF(Decode, "Decode: Squashing.\n"); + DPRINTF(Decode, "[tid:%i]: Squashing.\n",tid); + + if (decodeStatus[tid] == Blocked || + decodeStatus[tid] == Unblocking) { +#if !FULL_SYSTEM + // In syscall emulation, we can have both a block and a squash due + // to a syscall in the same cycle. This would cause both signals to + // be high. This shouldn't happen in full system. + // @todo: Determine if this still happens. + if (toFetch->decodeBlock[tid]) { + toFetch->decodeBlock[tid] = 0; + } else { + toFetch->decodeUnblock[tid] = 1; + } +#else + toFetch->decodeUnblock[tid] = 1; +#endif + } + // Set status to squashing. - _status = Squashing; + decodeStatus[tid] = Squashing; - // Maybe advance the time buffer? Not sure what to do in the normal - // case. + // Go through incoming instructions from fetch and squash them. + unsigned squash_count = 0; - // Clear the skid buffer in case it has any data in it. - while (!skidBuffer.empty()) - { - skidBuffer.pop(); + for (int i=0; i<fromFetch->size; i++) { + if (fromFetch->insts[i]->threadNumber == tid) { + fromFetch->insts[i]->setSquashed(); + squash_count++; + } } + + // Clear the instruction list and skid buffer in case they have any + // insts in them. + while (!insts[tid].empty()) { + insts[tid].pop(); + } + + while (!skidBuffer[tid].empty()) { + skidBuffer[tid].pop(); + } + + return squash_count; } template<class Impl> void -SimpleDecode<Impl>::tick() +DefaultDecode<Impl>::skidInsert(unsigned tid) { - // Decode should try to execute as many instructions as its bandwidth - // will allow, as long as it is not currently blocked. - if (_status != Blocked && _status != Squashing) { - DPRINTF(Decode, "Decode: Not blocked, so attempting to run " - "stage.\n"); - // Make sure that the skid buffer has something in it if the - // status is unblocking. - assert(_status == Unblocking ? !skidBuffer.empty() : 1); + DynInstPtr inst = NULL; - decode(); + while (!insts[tid].empty()) { + inst = insts[tid].front(); - // If the status was unblocking, then instructions from the skid - // buffer were used. Remove those instructions and handle - // the rest of unblocking. - if (_status == Unblocking) { - ++decodeUnblockCycles; + insts[tid].pop(); - if (fetchInstsValid()) { - // Add the current inputs to the skid buffer so they can be - // reprocessed when this stage unblocks. - skidBuffer.push(*fromFetch); - } + assert(tid == inst->threadNumber); - unblock(); - } - } else if (_status == Blocked) { - ++decodeBlockedCycles; + DPRINTF(Decode,"Inserting [sn:%lli] PC:%#x into decode skidBuffer %i\n", + inst->seqNum, inst->readPC(), inst->threadNumber); - if (fetchInstsValid()) { - block(); - } + skidBuffer[tid].push(inst); + } - if (!fromRename->renameInfo.stall && - !fromIEW->iewInfo.stall && - !fromCommit->commitInfo.stall) { - DPRINTF(Decode, "Decode: Stall signals cleared, going to " - "unblock.\n"); - _status = Unblocking; + // @todo: Eventually need to enforce this by not letting a thread + // fetch past its skidbuffer + assert(skidBuffer[tid].size() <= skidBufferMax); +} - // Continue to tell previous stage to block until this - // stage is done unblocking. - toFetch->decodeInfo.stall = true; - } else { - DPRINTF(Decode, "Decode: Still blocked.\n"); - toFetch->decodeInfo.stall = true; +template<class Impl> +bool +DefaultDecode<Impl>::skidsEmpty() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; +} + +template<class Impl> +void +DefaultDecode<Impl>::updateStatus() +{ + bool any_unblocking = false; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (decodeStatus[tid] == Unblocking) { + any_unblocking = true; + break; } + } + + // Decode will have activity if it's unblocking. + if (any_unblocking) { + if (_status == Inactive) { + _status = Active; + + DPRINTF(Activity, "Activating stage.\n"); - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - squash(); + cpu->activateStage(O3CPU::DecodeIdx); } - } else if (_status == Squashing) { - if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { - _status = Running; - } else if (fromCommit->commitInfo.squash) { - ++decodeSquashCycles; - - squash(); + } else { + // If it's not unblocking, then decode will not have any internal + // activity. Switch it to inactive. + if (_status == Active) { + _status = Inactive; + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(O3CPU::DecodeIdx); } } } +template <class Impl> +void +DefaultDecode<Impl>::sortInsts() +{ + int insts_from_fetch = fromFetch->size; +#ifdef DEBUG + for (int i=0; i < numThreads; i++) + assert(insts[i].empty()); +#endif + for (int i = 0; i < insts_from_fetch; ++i) { + insts[fromFetch->insts[i]->threadNumber].push(fromFetch->insts[i]); + } +} + template<class Impl> void -SimpleDecode<Impl>::decode() +DefaultDecode<Impl>::readStallSignals(unsigned tid) { - // Check time buffer if being told to squash. - if (fromCommit->commitInfo.squash) { - squash(); - return; + if (fromRename->renameBlock[tid]) { + stalls[tid].rename = true; } - // Check time buffer if being told to stall. - if (fromRename->renameInfo.stall || - fromIEW->iewInfo.stall || - fromCommit->commitInfo.stall) { - block(); - return; + if (fromRename->renameUnblock[tid]) { + assert(stalls[tid].rename); + stalls[tid].rename = false; + } + + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } + + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + stalls[tid].iew = false; + } + + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; + } +} + +template <class Impl> +bool +DefaultDecode<Impl>::checkSignalsAndUpdate(unsigned tid) +{ + // Check if there's a squash signal, squash if there is. + // Check stall signals, block if necessary. + // If status was blocked + // Check if stall conditions have passed + // if so then go to unblocking + // If status was Squashing + // check if squashing is not high. Switch to running this cycle. + + // Update the per thread stall statuses. + readStallSignals(tid); + + // Check squash signals from commit. + if (fromCommit->commitInfo[tid].squash) { + + DPRINTF(Decode, "[tid:%u]: Squashing instructions due to squash " + "from commit.\n", tid); + + squash(tid); + + return true; + } + + // Check ROB squash signals from commit. + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Decode, "[tid:%u]: ROB is still squashing.\n", tid); + + // Continue to squash. + decodeStatus[tid] = Squashing; + + return true; + } + + if (checkStall(tid)) { + return block(tid); } - // Check fetch queue to see if instructions are available. - // If no available instructions, do nothing, unless this stage is - // currently unblocking. - if (!fetchInstsValid() && _status != Unblocking) { - DPRINTF(Decode, "Decode: Nothing to do, breaking out early.\n"); + if (decodeStatus[tid] == Blocked) { + DPRINTF(Decode, "[tid:%u]: Done blocking, switching to unblocking.\n", + tid); + + decodeStatus[tid] = Unblocking; + + unblock(tid); + + return true; + } + + if (decodeStatus[tid] == Squashing) { + // Switch status to running if decode isn't being told to block or + // squash this cycle. + DPRINTF(Decode, "[tid:%u]: Done squashing, switching to running.\n", + tid); + + decodeStatus[tid] = Running; + + return false; + } + + // If we've reached this point, we have not gotten any signals that + // cause decode to change its status. Decode remains the same as before. + return false; +} + +template<class Impl> +void +DefaultDecode<Impl>::tick() +{ + wroteToTimeBuffer = false; + + bool status_change = false; + + toRenameIndex = 0; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + sortInsts(); + + //Check stall and squash signals. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + DPRINTF(Decode,"Processing [tid:%i]\n",tid); + status_change = checkSignalsAndUpdate(tid) || status_change; + + decode(status_change, tid); + } + + if (status_change) { + updateStatus(); + } + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity this cycle.\n"); + + cpu->activityThisCycle(); + } +} + +template<class Impl> +void +DefaultDecode<Impl>::decode(bool &status_change, unsigned tid) +{ + // If status is Running or idle, + // call decodeInsts() + // If status is Unblocking, + // buffer any instructions coming from fetch + // continue trying to empty skid buffer + // check if stall conditions have passed + + if (decodeStatus[tid] == Blocked) { + ++decodeBlockedCycles; + } else if (decodeStatus[tid] == Squashing) { + ++decodeSquashCycles; + } + + // Decode should try to decode as many instructions as its bandwidth + // will allow, as long as it is not currently blocked. + if (decodeStatus[tid] == Running || + decodeStatus[tid] == Idle) { + DPRINTF(Decode, "[tid:%u] Not blocked, so attempting to run " + "stage.\n",tid); + + decodeInsts(tid); + } else if (decodeStatus[tid] == Unblocking) { + // Make sure that the skid buffer has something in it if the + // status is unblocking. + assert(!skidsEmpty()); + + // If the status was unblocking, then instructions from the skid + // buffer were used. Remove those instructions and handle + // the rest of unblocking. + decodeInsts(tid); + + if (fetchInstsValid()) { + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + } + + status_change = unblock(tid) || status_change; + } +} + +template <class Impl> +void +DefaultDecode<Impl>::decodeInsts(unsigned tid) +{ + // Instructions can come either from the skid buffer or the list of + // instructions coming from fetch, depending on decode's status. + int insts_available = decodeStatus[tid] == Unblocking ? + skidBuffer[tid].size() : insts[tid].size(); + + if (insts_available == 0) { + DPRINTF(Decode, "[tid:%u] Nothing to do, breaking out" + " early.\n",tid); // Should I change the status to idle? ++decodeIdleCycles; return; + } else if (decodeStatus[tid] == Unblocking) { + DPRINTF(Decode, "[tid:%u] Unblocking, removing insts from skid " + "buffer.\n",tid); + ++decodeUnblockCycles; + } else if (decodeStatus[tid] == Running) { + ++decodeRunCycles; } - // Might be better to use a base DynInst * instead? DynInstPtr inst; - unsigned to_rename_index = 0; + std::queue<DynInstPtr> + &insts_to_decode = decodeStatus[tid] == Unblocking ? + skidBuffer[tid] : insts[tid]; - int insts_available = _status == Unblocking ? - skidBuffer.front().size - numInst : - fromFetch->size; + DPRINTF(Decode, "[tid:%u]: Sending instruction to rename.\n",tid); - // Debug block... -#if 0 - if (insts_available) { - DPRINTF(Decode, "Decode: Instructions available.\n"); - } else { - if (_status == Unblocking && skidBuffer.empty()) { - DPRINTF(Decode, "Decode: No instructions available, skid buffer " - "empty.\n"); - } else if (_status != Unblocking && - !fromFetch->insts[0]) { - DPRINTF(Decode, "Decode: No instructions available, fetch queue " - "empty.\n"); - } else { - panic("Decode: No instructions available, unexpected condition!" - "\n"); - } - } -#endif + while (insts_available > 0 && toRenameIndex < decodeWidth) { + assert(!insts_to_decode.empty()); - while (insts_available > 0) - { - DPRINTF(Decode, "Decode: Sending instruction to rename.\n"); + inst = insts_to_decode.front(); - inst = _status == Unblocking ? skidBuffer.front().insts[numInst] : - fromFetch->insts[numInst]; + insts_to_decode.pop(); - DPRINTF(Decode, "Decode: Processing instruction %i with PC %#x\n", - inst->seqNum, inst->readPC()); + DPRINTF(Decode, "[tid:%u]: Processing instruction [sn:%lli] with " + "PC %#x\n", + tid, inst->seqNum, inst->readPC()); if (inst->isSquashed()) { - DPRINTF(Decode, "Decode: Instruction %i with PC %#x is " + DPRINTF(Decode, "[tid:%u]: Instruction %i with PC %#x is " "squashed, skipping.\n", - inst->seqNum, inst->readPC()); + tid, inst->seqNum, inst->readPC()); ++decodeSquashedInsts; - ++numInst; --insts_available; continue; } - // Also check if instructions have no source registers. Mark // them as ready to issue at any time. Not sure if this check // should exist here or at a later stage; however it doesn't matter // too much for function correctness. - // Isn't this handled by the inst queue? if (inst->numSrcRegs() == 0) { inst->setCanIssue(); } @@ -378,9 +699,12 @@ SimpleDecode<Impl>::decode() // This current instruction is valid, so add it into the decode // queue. The next instruction may not be valid, so check to // see if branches were predicted correctly. - toRename->insts[to_rename_index] = inst; + toRename->insts[toRenameIndex] = inst; ++(toRename->size); + ++toRenameIndex; + ++decodeDecodedInsts; + --insts_available; // Ensure that if it was predicted as a branch, it really is a // branch. @@ -388,38 +712,40 @@ SimpleDecode<Impl>::decode() panic("Instruction predicted as a branch!"); ++decodeControlMispred; + // Might want to set some sort of boolean and just do // a check at the end - squash(inst); + squash(inst, inst->threadNumber); + break; } // Go ahead and compute any PC-relative branches. - if (inst->isDirectCtrl() && inst->isUncondCtrl()) { + ++decodeBranchResolved; - inst->setNextPC(inst->branchTarget()); - - if (inst->mispredicted()) { + if (inst->branchTarget() != inst->readPredTarg()) { ++decodeBranchMispred; + // Might want to set some sort of boolean and just do // a check at the end - squash(inst); + squash(inst, inst->threadNumber); + inst->setPredTarg(inst->branchTarget()); + break; } } + } - // Normally can check if a direct branch has the right target - // addr (either the immediate, or the branch PC + 4) and redirect - // fetch if it's incorrect. - - // Increment which instruction we're looking at. - ++numInst; - ++to_rename_index; - ++decodeDecodedInsts; - - --insts_available; + // If we didn't process all instructions, then we will need to block + // and put all those instructions into the skid buffer. + if (!insts_to_decode.empty()) { + block(tid); } - numInst = 0; + // Record that decode has written to the time buffer for activity + // tracking. + if (toRenameIndex) { + wroteToTimeBuffer = true; + } } diff --git a/src/cpu/o3/dep_graph.hh b/src/cpu/o3/dep_graph.hh new file mode 100644 index 000000000..c19fd0abf --- /dev/null +++ b/src/cpu/o3/dep_graph.hh @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_DEP_GRAPH_HH__ +#define __CPU_O3_DEP_GRAPH_HH__ + +#include "cpu/o3/comm.hh" + +/** Node in a linked list. */ +template <class DynInstPtr> +class DependencyEntry +{ + public: + DependencyEntry() + : inst(NULL), next(NULL) + { } + + DynInstPtr inst; + //Might want to include data about what arch. register the + //dependence is waiting on. + DependencyEntry<DynInstPtr> *next; +}; + +/** Array of linked list that maintains the dependencies between + * producing instructions and consuming instructions. Each linked + * list represents a single physical register, having the future + * producer of the register's value, and all consumers waiting on that + * value on the list. The head node of each linked list represents + * the producing instruction of that register. Instructions are put + * on the list upon reaching the IQ, and are removed from the list + * either when the producer completes, or the instruction is squashed. +*/ +template <class DynInstPtr> +class DependencyGraph +{ + public: + typedef DependencyEntry<DynInstPtr> DepEntry; + + /** Default construction. Must call resize() prior to use. */ + DependencyGraph() + : numEntries(0), memAllocCounter(0), nodesTraversed(0), nodesRemoved(0) + { } + + ~DependencyGraph(); + + /** Resize the dependency graph to have num_entries registers. */ + void resize(int num_entries); + + /** Clears all of the linked lists. */ + void reset(); + + /** Inserts an instruction to be dependent on the given index. */ + void insert(PhysRegIndex idx, DynInstPtr &new_inst); + + /** Sets the producing instruction of a given register. */ + void setInst(PhysRegIndex idx, DynInstPtr &new_inst) + { dependGraph[idx].inst = new_inst; } + + /** Clears the producing instruction. */ + void clearInst(PhysRegIndex idx) + { dependGraph[idx].inst = NULL; } + + /** Removes an instruction from a single linked list. */ + void remove(PhysRegIndex idx, DynInstPtr &inst_to_remove); + + /** Removes and returns the newest dependent of a specific register. */ + DynInstPtr pop(PhysRegIndex idx); + + /** Checks if there are any dependents on a specific register. */ + bool empty(PhysRegIndex idx) { return !dependGraph[idx].next; } + + /** Debugging function to dump out the dependency graph. + */ + void dump(); + + private: + /** Array of linked lists. Each linked list is a list of all the + * instructions that depend upon a given register. The actual + * register's index is used to index into the graph; ie all + * instructions in flight that are dependent upon r34 will be + * in the linked list of dependGraph[34]. + */ + DepEntry *dependGraph; + + /** Number of linked lists; identical to the number of registers. */ + int numEntries; + + // Debug variable, remove when done testing. + unsigned memAllocCounter; + + public: + // Debug variable, remove when done testing. + uint64_t nodesTraversed; + // Debug variable, remove when done testing. + uint64_t nodesRemoved; +}; + +template <class DynInstPtr> +DependencyGraph<DynInstPtr>::~DependencyGraph() +{ + delete [] dependGraph; +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::resize(int num_entries) +{ + numEntries = num_entries; + dependGraph = new DepEntry[numEntries]; +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::reset() +{ + // Clear the dependency graph + DepEntry *curr; + DepEntry *prev; + + for (int i = 0; i < numEntries; ++i) { + curr = dependGraph[i].next; + + while (curr) { + memAllocCounter--; + + prev = curr; + curr = prev->next; + prev->inst = NULL; + + delete prev; + } + + if (dependGraph[i].inst) { + dependGraph[i].inst = NULL; + } + + dependGraph[i].next = NULL; + } +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::insert(PhysRegIndex idx, DynInstPtr &new_inst) +{ + //Add this new, dependent instruction at the head of the dependency + //chain. + + // First create the entry that will be added to the head of the + // dependency chain. + DepEntry *new_entry = new DepEntry; + new_entry->next = dependGraph[idx].next; + new_entry->inst = new_inst; + + // Then actually add it to the chain. + dependGraph[idx].next = new_entry; + + ++memAllocCounter; +} + + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::remove(PhysRegIndex idx, + DynInstPtr &inst_to_remove) +{ + DepEntry *prev = &dependGraph[idx]; + DepEntry *curr = dependGraph[idx].next; + + // Make sure curr isn't NULL. Because this instruction is being + // removed from a dependency list, it must have been placed there at + // an earlier time. The dependency chain should not be empty, + // unless the instruction dependent upon it is already ready. + if (curr == NULL) { + return; + } + + nodesRemoved++; + + // Find the instruction to remove within the dependency linked list. + while (curr->inst != inst_to_remove) { + prev = curr; + curr = curr->next; + nodesTraversed++; + + assert(curr != NULL); + } + + // Now remove this instruction from the list. + prev->next = curr->next; + + --memAllocCounter; + + // Could push this off to the destructor of DependencyEntry + curr->inst = NULL; + + delete curr; +} + +template <class DynInstPtr> +DynInstPtr +DependencyGraph<DynInstPtr>::pop(PhysRegIndex idx) +{ + DepEntry *node; + node = dependGraph[idx].next; + DynInstPtr inst = NULL; + if (node) { + inst = node->inst; + dependGraph[idx].next = node->next; + node->inst = NULL; + memAllocCounter--; + delete node; + } + return inst; +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::dump() +{ + DepEntry *curr; + + for (int i = 0; i < numEntries; ++i) + { + curr = &dependGraph[i]; + + if (curr->inst) { + cprintf("dependGraph[%i]: producer: %#x [sn:%lli] consumer: ", + i, curr->inst->readPC(), curr->inst->seqNum); + } else { + cprintf("dependGraph[%i]: No producer. consumer: ", i); + } + + while (curr->next != NULL) { + curr = curr->next; + + cprintf("%#x [sn:%lli] ", + curr->inst->readPC(), curr->inst->seqNum); + } + + cprintf("\n"); + } + cprintf("memAllocCounter: %i\n", memAllocCounter); +} + +#endif // __CPU_O3_DEP_GRAPH_HH__ diff --git a/src/cpu/o3/fetch.cc b/src/cpu/o3/fetch.cc index 8ad5e6565..d809b07e4 100644 --- a/src/cpu/o3/fetch.cc +++ b/src/cpu/o3/fetch.cc @@ -24,10 +24,11 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/isa_specific.hh" #include "cpu/o3/fetch_impl.hh" -template class SimpleFetch<AlphaSimpleImpl>; +template class DefaultFetch<O3CPUImpl>; diff --git a/src/cpu/o3/fetch.hh b/src/cpu/o3/fetch.hh index cc64800d9..7fcd21b7d 100644 --- a/src/cpu/o3/fetch.hh +++ b/src/cpu/o3/fetch.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,87 +24,190 @@ * 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. + * + * Authors: Kevin Lim + * Korey Sewell */ -// Todo: SMT fetch, -// Add a way to get a stage's current status. - -#ifndef __CPU_O3_CPU_SIMPLE_FETCH_HH__ -#define __CPU_O3_CPU_SIMPLE_FETCH_HH__ +#ifndef __CPU_O3_FETCH_HH__ +#define __CPU_O3_FETCH_HH__ +#include "arch/utility.hh" #include "base/statistics.hh" #include "base/timebuf.hh" #include "cpu/pc_event.hh" -#include "mem/mem_interface.hh" +#include "mem/packet_impl.hh" +#include "mem/port.hh" #include "sim/eventq.hh" +class Sampler; + /** - * SimpleFetch class to fetch a single instruction each cycle. SimpleFetch - * will stall if there's an Icache miss, but otherwise assumes a one cycle - * Icache hit. + * DefaultFetch class handles both single threaded and SMT fetch. Its + * width is specified by the parameters; each cycle it tries to fetch + * that many instructions. It supports using a branch predictor to + * predict direction and targets. + * It supports the idling functionality of the CPU by indicating to + * the CPU when it is active and inactive. */ - template <class Impl> -class SimpleFetch +class DefaultFetch { public: /** Typedefs from Impl. */ typedef typename Impl::CPUPol CPUPol; typedef typename Impl::DynInst DynInst; typedef typename Impl::DynInstPtr DynInstPtr; - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::O3CPU O3CPU; typedef typename Impl::Params Params; + /** Typedefs from the CPU policy. */ typedef typename CPUPol::BPredUnit BPredUnit; typedef typename CPUPol::FetchStruct FetchStruct; typedef typename CPUPol::TimeStruct TimeStruct; /** Typedefs from ISA. */ typedef TheISA::MachInst MachInst; + typedef TheISA::ExtMachInst ExtMachInst; + + /** IcachePort class for DefaultFetch. Handles doing the + * communication with the cache/memory. + */ + class IcachePort : public Port + { + protected: + /** Pointer to fetch. */ + DefaultFetch<Impl> *fetch; + + public: + /** Default constructor. */ + IcachePort(DefaultFetch<Impl> *_fetch) + : Port(_fetch->name() + "-iport"), fetch(_fetch) + { } + + protected: + /** Atomic version of receive. Panics. */ + virtual Tick recvAtomic(PacketPtr pkt); + + /** Functional version of receive. Panics. */ + virtual void recvFunctional(PacketPtr pkt); + + /** Receives status change. Other than range changing, panics. */ + virtual void recvStatusChange(Status status); + + /** Returns the address ranges of this device. */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + /** Timing version of receive. Handles setting fetch to the + * proper status to start fetching. */ + virtual bool recvTiming(PacketPtr pkt); + + /** Handles doing a retry of a failed fetch. */ + virtual void recvRetry(); + }; public: - enum Status { + /** Overall fetch status. Used to determine if the CPU can + * deschedule itsef due to a lack of activity. + */ + enum FetchStatus { + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { Running, Idle, Squashing, Blocked, - IcacheMissStall, - IcacheMissComplete + Fetching, + TrapPending, + QuiescePending, + SwitchOut, + IcacheWaitResponse, + IcacheWaitRetry, + IcacheAccessComplete }; - // May eventually need statuses on a per thread basis. - Status _status; + /** Fetching Policy, Add new policies here.*/ + enum FetchPriority { + SingleThread, + RoundRobin, + Branch, + IQ, + LSQ + }; - bool stalled; + private: + /** Fetch status. */ + FetchStatus _status; - public: - class CacheCompletionEvent : public Event - { - private: - SimpleFetch *fetch; + /** Per-thread status. */ + ThreadStatus fetchStatus[Impl::MaxThreads]; - public: - CacheCompletionEvent(SimpleFetch *_fetch); + /** Fetch policy. */ + FetchPriority fetchPolicy; - virtual void process(); - virtual const char *description(); - }; + /** List that has the threads organized by priority. */ + std::list<unsigned> priorityList; public: - /** SimpleFetch constructor. */ - SimpleFetch(Params ¶ms); + /** DefaultFetch constructor. */ + DefaultFetch(Params *params); + + /** Returns the name of fetch. */ + std::string name() const; + /** Registers statistics. */ void regStats(); - void setCPU(FullCPU *cpu_ptr); + /** Sets CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + /** Sets the main backwards communication time buffer pointer. */ void setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to time buffer used to communicate to the next stage. */ void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr); - void processCacheCompletion(); + /** Initialize stage. */ + void initStage(); + + /** Processes cache completion event. */ + void processCacheCompletion(PacketPtr pkt); + + /** Begins the switch out of the fetch stage. */ + void switchOut(); + + /** Completes the switch out of the fetch stage. */ + void doSwitchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Checks if the fetch stage is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** Tells fetch to wake up from a quiesce instruction. */ + void wakeFromQuiesce(); private: + /** Changes the status of this stage to active, and indicates this + * to the CPU. + */ + inline void switchToActive(); + + /** Changes the status of this stage to inactive, and indicates + * this to the CPU. + */ + inline void switchToInactive(); + /** * Looks up in the branch predictor to see if the next PC should be * either next PC+=MachInst or a branch target. @@ -120,24 +223,54 @@ class SimpleFetch * fault that happened. Puts the data into the class variable * cacheData. * @param fetch_PC The PC address that is being fetched from. + * @param ret_fault The fault reference that will be set to the result of + * the icache access. + * @param tid Thread id. * @return Any fault that occured. */ - Fault fetchCacheLine(Addr fetch_PC); + bool fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid); + + /** Squashes a specific thread and resets the PC. */ + inline void doSquash(const Addr &new_PC, unsigned tid); + + /** Squashes a specific thread and resets the PC. Also tells the CPU to + * remove any instructions between fetch and decode that should be sqaushed. + */ + void squashFromDecode(const Addr &new_PC, const InstSeqNum &seq_num, + unsigned tid); - inline void doSquash(const Addr &new_PC); + /** Checks if a thread is stalled. */ + bool checkStall(unsigned tid) const; - void squashFromDecode(const Addr &new_PC, const InstSeqNum &seq_num); + /** Updates overall fetch stage status; to be called at the end of each + * cycle. */ + FetchStatus updateFetchStatus(); public: - // Figure out PC vs next PC and how it should be updated - void squash(const Addr &new_PC); + /** Squashes a specific thread and resets the PC. Also tells the CPU to + * remove any instructions that are not in the ROB. The source of this + * squash should be the commit stage. + */ + void squash(const Addr &new_PC, unsigned tid); + /** Ticks the fetch stage, processing all inputs signals and fetching + * as many instructions as possible. + */ void tick(); - void fetch(); + /** Checks all input signals and updates the status as necessary. + * @return: Returns if the status has changed due to input signals. + */ + bool checkSignalsAndUpdate(unsigned tid); + + /** Does the actual fetching of instructions and passing them on to the + * next stage. + * @param status_change fetch() sets this variable if there was a status + * change (ie switching to IcacheMissStall). + */ + void fetch(bool &status_change); - // Align an address (typically a PC) to the start of an I-cache block. - // We fold in the PISA 64- to 32-bit conversion here as well. + /** Align a PC to the start of an I-cache block. */ Addr icacheBlockAlignPC(Addr addr) { addr = TheISA::realPCToFetchPC(addr); @@ -145,8 +278,27 @@ class SimpleFetch } private: - /** Pointer to the FullCPU. */ - FullCPU *cpu; + /** Handles retrying the fetch access. */ + void recvRetry(); + + /** Returns the appropriate thread to fetch, given the fetch policy. */ + int getFetchingThread(FetchPriority &fetch_priority); + + /** Returns the appropriate thread to fetch using a round robin policy. */ + int roundRobin(); + + /** Returns the appropriate thread to fetch using the IQ count policy. */ + int iqCount(); + + /** Returns the appropriate thread to fetch using the LSQ count policy. */ + int lsqCount(); + + /** Returns the appropriate thread to fetch using the branch count policy. */ + int branchCount(); + + private: + /** Pointer to the O3CPU. */ + O3CPU *cpu; /** Time buffer interface. */ TimeBuffer<TimeStruct> *timeBuffer; @@ -170,14 +322,50 @@ class SimpleFetch /** Wire used to write any information heading to decode. */ typename TimeBuffer<FetchStruct>::wire toDecode; + MemObject *mem; + /** Icache interface. */ - MemInterface *icacheInterface; + IcachePort *icachePort; /** BPredUnit. */ BPredUnit branchPred; + /** Per-thread fetch PC. */ + Addr PC[Impl::MaxThreads]; + + /** Per-thread next PC. */ + Addr nextPC[Impl::MaxThreads]; + +#if THE_ISA != ALPHA_ISA + /** Per-thread next Next PC. + * This is not a real register but is used for + * architectures that use a branch-delay slot. + * (such as MIPS or Sparc) + */ + Addr nextNPC[Impl::MaxThreads]; +#endif + /** Memory request used to access cache. */ - MemReqPtr memReq; + RequestPtr memReq[Impl::MaxThreads]; + + /** Variable that tracks if fetch has written to the time buffer this + * cycle. Used to tell CPU if there is activity this cycle. + */ + bool wroteToTimeBuffer; + + /** Tracks how many instructions has been fetched this cycle. */ + int numInst; + + /** Source of possible stalls. */ + struct Stalls { + bool decode; + bool rename; + bool iew; + bool commit; + }; + + /** Tracks which stages are telling fetch to stall. */ + Stalls stalls[Impl::MaxThreads]; /** Decode to fetch delay, in ticks. */ unsigned decodeToFetchDelay; @@ -194,6 +382,15 @@ class SimpleFetch /** The width of fetch in instructions. */ unsigned fetchWidth; + /** Is the cache blocked? If so no threads can access it. */ + bool cacheBlocked; + + /** The packet that is waiting to be retried. */ + PacketPtr retryPkt; + + /** The thread that is waiting on the cache to tell fetch to retry. */ + int retryTid; + /** Cache block size. */ int cacheBlkSize; @@ -201,23 +398,69 @@ class SimpleFetch Addr cacheBlkMask; /** The cache line being fetched. */ - uint8_t *cacheData; + uint8_t *cacheData[Impl::MaxThreads]; /** Size of instructions. */ int instSize; /** Icache stall statistics. */ - Counter lastIcacheStall; + Counter lastIcacheStall[Impl::MaxThreads]; + + /** List of Active Threads */ + std::list<unsigned> *activeThreads; + /** Number of threads. */ + unsigned numThreads; + + /** Number of threads that are actively fetching. */ + unsigned numFetchingThreads; + + /** Thread ID being fetched. */ + int threadFetched; + + /** Checks if there is an interrupt pending. If there is, fetch + * must stop once it is not fetching PAL instructions. + */ + bool interruptPending; + + /** Records if fetch is switched out. */ + bool switchedOut; + + // @todo: Consider making these vectors and tracking on a per thread basis. + /** Stat for total number of cycles stalled due to an icache miss. */ Stats::Scalar<> icacheStallCycles; + /** Stat for total number of fetched instructions. */ Stats::Scalar<> fetchedInsts; + /** Total number of fetched branches. */ + Stats::Scalar<> fetchedBranches; + /** Stat for total number of predicted branches. */ Stats::Scalar<> predictedBranches; + /** Stat for total number of cycles spent fetching. */ Stats::Scalar<> fetchCycles; + /** Stat for total number of cycles spent squashing. */ Stats::Scalar<> fetchSquashCycles; + /** Stat for total number of cycles spent blocked due to other stages in + * the pipeline. + */ + Stats::Scalar<> fetchIdleCycles; + /** Total number of cycles spent blocked. */ Stats::Scalar<> fetchBlockedCycles; + /** Total number of cycles spent in any other state. */ + Stats::Scalar<> fetchMiscStallCycles; + /** Stat for total number of fetched cache lines. */ Stats::Scalar<> fetchedCacheLines; - - Stats::Distribution<> fetch_nisn_dist; + /** Total number of outstanding icache accesses that were dropped + * due to a squash. + */ + Stats::Scalar<> fetchIcacheSquashes; + /** Distribution of number of instructions fetched each cycle. */ + Stats::Distribution<> fetchNisnDist; + /** Rate of how often fetch was idle. */ + Stats::Formula idleRate; + /** Number of branch fetches per cycle. */ + Stats::Formula branchRate; + /** Number of instruction fetched per cycle. */ + Stats::Formula fetchRate; }; -#endif //__CPU_O3_CPU_SIMPLE_FETCH_HH__ +#endif //__CPU_O3_FETCH_HH__ diff --git a/src/cpu/o3/fetch_impl.hh b/src/cpu/o3/fetch_impl.hh index 8029fc732..60eb76d17 100644 --- a/src/cpu/o3/fetch_impl.hh +++ b/src/cpu/o3/fetch_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,85 +24,164 @@ * 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. + * + * Authors: Kevin Lim + * Korey Sewell */ -// Remove this later; used only for debugging. -#define OPCODE(X) (X >> 26) & 0x3f +#include "config/use_checker.hh" #include "arch/isa_traits.hh" -#include "sim/byteswap.hh" +#include "arch/utility.hh" +#include "cpu/checker/cpu.hh" #include "cpu/exetrace.hh" -#include "mem/base_mem.hh" -#include "mem/mem_interface.hh" -#include "mem/mem_req.hh" #include "cpu/o3/fetch.hh" - +#include "mem/packet.hh" +#include "mem/request.hh" +#include "sim/byteswap.hh" +#include "sim/host.hh" #include "sim/root.hh" +#if FULL_SYSTEM +#include "arch/tlb.hh" +#include "arch/vtophys.hh" +#include "base/remote_gdb.hh" +#include "sim/system.hh" +#endif // FULL_SYSTEM + +#include <algorithm> + +using namespace std; +using namespace TheISA; + +template<class Impl> +Tick +DefaultFetch<Impl>::IcachePort::recvAtomic(PacketPtr pkt) +{ + panic("DefaultFetch doesn't expect recvAtomic callback!"); + return curTick; +} + template<class Impl> -SimpleFetch<Impl>::CacheCompletionEvent -::CacheCompletionEvent(SimpleFetch *_fetch) - : Event(&mainEventQueue), - fetch(_fetch) +void +DefaultFetch<Impl>::IcachePort::recvFunctional(PacketPtr pkt) { + panic("DefaultFetch doesn't expect recvFunctional callback!"); } template<class Impl> void -SimpleFetch<Impl>::CacheCompletionEvent::process() +DefaultFetch<Impl>::IcachePort::recvStatusChange(Status status) { - fetch->processCacheCompletion(); + if (status == RangeChange) + return; + + panic("DefaultFetch doesn't expect recvStatusChange callback!"); } template<class Impl> -const char * -SimpleFetch<Impl>::CacheCompletionEvent::description() +bool +DefaultFetch<Impl>::IcachePort::recvTiming(Packet *pkt) { - return "SimpleFetch cache completion event"; + fetch->processCacheCompletion(pkt); + return true; } template<class Impl> -SimpleFetch<Impl>::SimpleFetch(Params ¶ms) - : icacheInterface(params.icacheInterface), +void +DefaultFetch<Impl>::IcachePort::recvRetry() +{ + fetch->recvRetry(); +} + +template<class Impl> +DefaultFetch<Impl>::DefaultFetch(Params *params) + : mem(params->mem), branchPred(params), - decodeToFetchDelay(params.decodeToFetchDelay), - renameToFetchDelay(params.renameToFetchDelay), - iewToFetchDelay(params.iewToFetchDelay), - commitToFetchDelay(params.commitToFetchDelay), - fetchWidth(params.fetchWidth) -{ - DPRINTF(Fetch, "Fetch: Fetch constructor called\n"); - - // Set status to idle. - _status = Idle; - - // Create a new memory request. - memReq = new MemReq(); - // Not sure of this parameter. I think it should be based on the - // thread number. -#if !FULL_SYSTEM - memReq->asid = 0; -#else - memReq->asid = 0; -#endif // FULL_SYSTEM - memReq->data = new uint8_t[64]; + decodeToFetchDelay(params->decodeToFetchDelay), + renameToFetchDelay(params->renameToFetchDelay), + iewToFetchDelay(params->iewToFetchDelay), + commitToFetchDelay(params->commitToFetchDelay), + fetchWidth(params->fetchWidth), + cacheBlocked(false), + retryPkt(NULL), + retryTid(-1), + numThreads(params->numberOfThreads), + numFetchingThreads(params->smtNumFetchingThreads), + interruptPending(false), + switchedOut(false) +{ + if (numThreads > Impl::MaxThreads) + fatal("numThreads is not a valid value\n"); + + // Set fetch stage's status to inactive. + _status = Inactive; + + string policy = params->smtFetchPolicy; + + // Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + // Figure out fetch policy + if (policy == "singlethread") { + fetchPolicy = SingleThread; + if (numThreads > 1) + panic("Invalid Fetch Policy for a SMT workload."); + } else if (policy == "roundrobin") { + fetchPolicy = RoundRobin; + DPRINTF(Fetch, "Fetch policy set to Round Robin\n"); + } else if (policy == "branch") { + fetchPolicy = Branch; + DPRINTF(Fetch, "Fetch policy set to Branch Count\n"); + } else if (policy == "iqcount") { + fetchPolicy = IQ; + DPRINTF(Fetch, "Fetch policy set to IQ count\n"); + } else if (policy == "lsqcount") { + fetchPolicy = LSQ; + DPRINTF(Fetch, "Fetch policy set to LSQ count\n"); + } else { + fatal("Invalid Fetch Policy. Options Are: {SingleThread," + " RoundRobin,LSQcount,IQcount}\n"); + } // Size of cache block. - cacheBlkSize = icacheInterface ? icacheInterface->getBlockSize() : 64; + cacheBlkSize = 64; // Create mask to get rid of offset bits. cacheBlkMask = (cacheBlkSize - 1); + for (int tid=0; tid < numThreads; tid++) { + + fetchStatus[tid] = Running; + + priorityList.push_back(tid); + + memReq[tid] = NULL; + + // Create space to store a cache line. + cacheData[tid] = new uint8_t[cacheBlkSize]; + + stalls[tid].decode = 0; + stalls[tid].rename = 0; + stalls[tid].iew = 0; + stalls[tid].commit = 0; + } + // Get the size of an instruction. instSize = sizeof(MachInst); +} - // Create space to store a cache line. - cacheData = new uint8_t[cacheBlkSize]; +template <class Impl> +std::string +DefaultFetch<Impl>::name() const +{ + return cpu->name() + ".fetch"; } template <class Impl> void -SimpleFetch<Impl>::regStats() +DefaultFetch<Impl>::regStats() { icacheStallCycles .name(name() + ".icacheStallCycles") @@ -110,58 +189,116 @@ SimpleFetch<Impl>::regStats() .prereq(icacheStallCycles); fetchedInsts - .name(name() + ".fetchedInsts") + .name(name() + ".Insts") .desc("Number of instructions fetch has processed") .prereq(fetchedInsts); + + fetchedBranches + .name(name() + ".Branches") + .desc("Number of branches that fetch encountered") + .prereq(fetchedBranches); + predictedBranches .name(name() + ".predictedBranches") .desc("Number of branches that fetch has predicted taken") .prereq(predictedBranches); + fetchCycles - .name(name() + ".fetchCycles") + .name(name() + ".Cycles") .desc("Number of cycles fetch has run and was not squashing or" " blocked") .prereq(fetchCycles); + fetchSquashCycles - .name(name() + ".fetchSquashCycles") + .name(name() + ".SquashCycles") .desc("Number of cycles fetch has spent squashing") .prereq(fetchSquashCycles); + + fetchIdleCycles + .name(name() + ".IdleCycles") + .desc("Number of cycles fetch was idle") + .prereq(fetchIdleCycles); + fetchBlockedCycles - .name(name() + ".fetchBlockedCycles") + .name(name() + ".BlockedCycles") .desc("Number of cycles fetch has spent blocked") .prereq(fetchBlockedCycles); + fetchedCacheLines - .name(name() + ".fetchedCacheLines") + .name(name() + ".CacheLines") .desc("Number of cache lines fetched") .prereq(fetchedCacheLines); - fetch_nisn_dist + fetchMiscStallCycles + .name(name() + ".MiscStallCycles") + .desc("Number of cycles fetch has spent waiting on interrupts, or " + "bad addresses, or out of MSHRs") + .prereq(fetchMiscStallCycles); + + fetchIcacheSquashes + .name(name() + ".IcacheSquashes") + .desc("Number of outstanding Icache misses that were squashed") + .prereq(fetchIcacheSquashes); + + fetchNisnDist .init(/* base value */ 0, /* last value */ fetchWidth, /* bucket size */ 1) - .name(name() + ".FETCH:rate_dist") + .name(name() + ".rateDist") .desc("Number of instructions fetched each cycle (Total)") - .flags(Stats::pdf) - ; + .flags(Stats::pdf); + + idleRate + .name(name() + ".idleRate") + .desc("Percent of cycles fetch was idle") + .prereq(idleRate); + idleRate = fetchIdleCycles * 100 / cpu->numCycles; + + branchRate + .name(name() + ".branchRate") + .desc("Number of branch fetches per cycle") + .flags(Stats::total); + branchRate = fetchedBranches / cpu->numCycles; + + fetchRate + .name(name() + ".rate") + .desc("Number of inst fetches per cycle") + .flags(Stats::total); + fetchRate = fetchedInsts / cpu->numCycles; branchPred.regStats(); } template<class Impl> void -SimpleFetch<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultFetch<Impl>::setCPU(O3CPU *cpu_ptr) { - DPRINTF(Fetch, "Fetch: Setting the CPU pointer.\n"); + DPRINTF(Fetch, "Setting the CPU pointer.\n"); cpu = cpu_ptr; - // This line will be removed eventually. - memReq->xc = cpu->xcBase(); + + // Name is finally available, so create the port. + icachePort = new IcachePort(this); + + Port *mem_dport = mem->getPort(""); + icachePort->setPeer(mem_dport); + mem_dport->setPeer(icachePort); + +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->setIcachePort(icachePort); + } +#endif + + // Fetch needs to start fetching instructions at the very beginning, + // so it must start up in active state. + switchToActive(); } template<class Impl> void -SimpleFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer) +DefaultFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer) { - DPRINTF(Fetch, "Fetch: Setting the time buffer pointer.\n"); + DPRINTF(Fetch, "Setting the time buffer pointer.\n"); timeBuffer = time_buffer; // Create wires to get information from proper places in time buffer. @@ -173,9 +310,17 @@ SimpleFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer) template<class Impl> void -SimpleFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) +DefaultFetch<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(Fetch, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template<class Impl> +void +DefaultFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) { - DPRINTF(Fetch, "Fetch: Setting the fetch queue pointer.\n"); + DPRINTF(Fetch, "Setting the fetch queue pointer.\n"); fetchQueue = fq_ptr; // Create wire to write information to proper place in fetch queue. @@ -184,21 +329,139 @@ SimpleFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) template<class Impl> void -SimpleFetch<Impl>::processCacheCompletion() +DefaultFetch<Impl>::initStage() { - DPRINTF(Fetch, "Fetch: Waking up from cache miss.\n"); + // Setup PC and nextPC with initial state. + for (int tid = 0; tid < numThreads; tid++) { + PC[tid] = cpu->readPC(tid); + nextPC[tid] = cpu->readNextPC(tid); +#if THE_ISA != ALPHA_ISA + nextNPC[tid] = cpu->readNextNPC(tid); +#endif + } +} + +template<class Impl> +void +DefaultFetch<Impl>::processCacheCompletion(PacketPtr pkt) +{ + unsigned tid = pkt->req->getThreadNum(); + + DPRINTF(Fetch, "[tid:%u] Waking up from cache miss.\n",tid); // Only change the status if it's still waiting on the icache access // to return. - // Can keep track of how many cache accesses go unused due to - // misspeculation here. - if (_status == IcacheMissStall) - _status = IcacheMissComplete; + if (fetchStatus[tid] != IcacheWaitResponse || + pkt->req != memReq[tid] || + isSwitchedOut()) { + ++fetchIcacheSquashes; + delete pkt->req; + delete pkt; + return; + } + + // Wake up the CPU (if it went to sleep and was waiting on this completion + // event). + cpu->wakeCPU(); + + DPRINTF(Activity, "[tid:%u] Activating fetch due to cache completion\n", + tid); + + switchToActive(); + + // Only switch to IcacheAccessComplete if we're not stalled as well. + if (checkStall(tid)) { + fetchStatus[tid] = Blocked; + } else { + fetchStatus[tid] = IcacheAccessComplete; + } + + // Reset the mem req to NULL. + delete pkt->req; + delete pkt; + memReq[tid] = NULL; +} + +template <class Impl> +void +DefaultFetch<Impl>::switchOut() +{ + // Fetch is ready to switch out at any time. + switchedOut = true; + cpu->signalSwitched(); +} + +template <class Impl> +void +DefaultFetch<Impl>::doSwitchOut() +{ + // Branch predictor needs to have its state cleared. + branchPred.switchOut(); +} + +template <class Impl> +void +DefaultFetch<Impl>::takeOverFrom() +{ + // Reset all state + for (int i = 0; i < Impl::MaxThreads; ++i) { + stalls[i].decode = 0; + stalls[i].rename = 0; + stalls[i].iew = 0; + stalls[i].commit = 0; + PC[i] = cpu->readPC(i); + nextPC[i] = cpu->readNextPC(i); +#if THE_ISA != ALPHA_ISA + nextNPC[i] = cpu->readNextNPC(i); +#endif + fetchStatus[i] = Running; + } + numInst = 0; + wroteToTimeBuffer = false; + _status = Inactive; + switchedOut = false; + branchPred.takeOverFrom(); +} + +template <class Impl> +void +DefaultFetch<Impl>::wakeFromQuiesce() +{ + DPRINTF(Fetch, "Waking up from quiesce\n"); + // Hopefully this is safe + // @todo: Allow other threads to wake from quiesce. + fetchStatus[0] = Running; +} + +template <class Impl> +inline void +DefaultFetch<Impl>::switchToActive() +{ + if (_status == Inactive) { + DPRINTF(Activity, "Activating stage.\n"); + + cpu->activateStage(O3CPU::FetchIdx); + + _status = Active; + } +} + +template <class Impl> +inline void +DefaultFetch<Impl>::switchToInactive() +{ + if (_status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(O3CPU::FetchIdx); + + _status = Inactive; + } } template <class Impl> bool -SimpleFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) +DefaultFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) { // Do branch prediction check here. // A bit of a misnomer...next_PC is actually the current PC until @@ -211,7 +474,9 @@ SimpleFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) return false; } - predict_taken = branchPred.predict(inst, next_PC); + predict_taken = branchPred.predict(inst, next_PC, inst->threadNumber); + + ++fetchedBranches; if (predict_taken) { ++predictedBranches; @@ -221,251 +486,430 @@ SimpleFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC) } template <class Impl> -Fault -SimpleFetch<Impl>::fetchCacheLine(Addr fetch_PC) +bool +DefaultFetch<Impl>::fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid) { - // Check if the instruction exists within the cache. - // If it does, then proceed on to read the instruction and the rest - // of the instructions in the cache line until either the end of the - // cache line or a predicted taken branch is encountered. + Fault fault = NoFault; #if FULL_SYSTEM // Flag to say whether or not address is physical addr. - unsigned flags = cpu->inPalMode() ? PHYSICAL : 0; + unsigned flags = cpu->inPalMode(fetch_PC) ? PHYSICAL : 0; #else unsigned flags = 0; #endif // FULL_SYSTEM - Fault fault = NoFault; + if (cacheBlocked || (interruptPending && flags == 0) || switchedOut) { + // Hold off fetch from getting new instructions when: + // Cache is blocked, or + // while an interrupt is pending and we're not in PAL mode, or + // fetch is switched out. + return false; + } // Align the fetch PC so it's at the start of a cache block. fetch_PC = icacheBlockAlignPC(fetch_PC); - // Setup the memReq to do a read of the first isntruction's address. + // Setup the memReq to do a read of the first instruction's address. // Set the appropriate read size and flags as well. - memReq->cmd = Read; - memReq->reset(fetch_PC, cacheBlkSize, flags); + // Build request here. + RequestPtr mem_req = new Request(tid, fetch_PC, cacheBlkSize, flags, + fetch_PC, cpu->readCpuId(), tid); - // Translate the instruction request. - // Should this function be - // in the CPU class ? Probably...ITB/DTB should exist within the - // CPU. + memReq[tid] = mem_req; - fault = cpu->translateInstReq(memReq); + // Translate the instruction request. + fault = cpu->translateInstReq(mem_req, cpu->thread[tid]); // In the case of faults, the fetch stage may need to stall and wait - // on what caused the fetch (ITB or Icache miss). + // for the ITB miss to be handled. // If translation was successful, attempt to read the first // instruction. if (fault == NoFault) { +#if 0 + if (cpu->system->memctrl->badaddr(memReq[tid]->paddr) || + memReq[tid]->flags & UNCACHEABLE) { + DPRINTF(Fetch, "Fetch: Bad address %#x (hopefully on a " + "misspeculating path)!", + memReq[tid]->paddr); + ret_fault = TheISA::genMachineCheckFault(); + return false; + } +#endif + + // Build packet here. + PacketPtr data_pkt = new Packet(mem_req, + Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(cacheData[tid]); + DPRINTF(Fetch, "Fetch: Doing instruction read.\n"); - fault = cpu->mem->read(memReq, cacheData); - // This read may change when the mem interface changes. fetchedCacheLines++; - } - - // Now do the timing access to see whether or not the instruction - // exists within the cache. - if (icacheInterface && fault == NoFault) { - DPRINTF(Fetch, "Fetch: Doing timing memory access.\n"); - memReq->completionEvent = NULL; - memReq->time = curTick; + // Now do the timing access to see whether or not the instruction + // exists within the cache. + if (!icachePort->sendTiming(data_pkt)) { + assert(retryPkt == NULL); + assert(retryTid == -1); + DPRINTF(Fetch, "[tid:%i] Out of MSHRs!\n", tid); + fetchStatus[tid] = IcacheWaitRetry; + retryPkt = data_pkt; + retryTid = tid; + cacheBlocked = true; + return false; + } - MemAccessResult result = icacheInterface->access(memReq); + DPRINTF(Fetch, "[tid:%i]: Doing cache access.\n", tid); - // If the cache missed (in this model functional and timing - // memories are different), then schedule an event to wake - // up this stage once the cache miss completes. - if (result != MA_HIT && icacheInterface->doEvents()) { - memReq->completionEvent = new CacheCompletionEvent(this); + lastIcacheStall[tid] = curTick; - // How does current model work as far as individual - // stages scheduling/unscheduling? - // Perhaps have only the main CPU scheduled/unscheduled, - // and have it choose what stages to run appropriately. + DPRINTF(Activity, "[tid:%i]: Activity: Waiting on I-cache " + "response.\n", tid); - DPRINTF(Fetch, "Fetch: Stalling due to icache miss.\n"); - _status = IcacheMissStall; - } + fetchStatus[tid] = IcacheWaitResponse; + } else { + delete mem_req; + memReq[tid] = NULL; } - return fault; + ret_fault = fault; + return true; } template <class Impl> inline void -SimpleFetch<Impl>::doSquash(const Addr &new_PC) +DefaultFetch<Impl>::doSquash(const Addr &new_PC, unsigned tid) { - DPRINTF(Fetch, "Fetch: Squashing, setting PC to: %#x.\n", new_PC); + DPRINTF(Fetch, "[tid:%i]: Squashing, setting PC to: %#x.\n", + tid, new_PC); - cpu->setNextPC(new_PC + instSize); - cpu->setPC(new_PC); + PC[tid] = new_PC; + nextPC[tid] = new_PC + instSize; // Clear the icache miss if it's outstanding. - if (_status == IcacheMissStall && icacheInterface) { - DPRINTF(Fetch, "Fetch: Squashing outstanding Icache miss.\n"); - // @todo: Use an actual thread number here. - icacheInterface->squash(0); + if (fetchStatus[tid] == IcacheWaitResponse) { + DPRINTF(Fetch, "[tid:%i]: Squashing outstanding Icache miss.\n", + tid); + memReq[tid] = NULL; + } + + // Get rid of the retrying packet if it was from this thread. + if (retryTid == tid) { + assert(cacheBlocked); + cacheBlocked = false; + retryTid = -1; + retryPkt = NULL; + delete retryPkt->req; + delete retryPkt; } - _status = Squashing; + fetchStatus[tid] = Squashing; ++fetchSquashCycles; } template<class Impl> void -SimpleFetch<Impl>::squashFromDecode(const Addr &new_PC, - const InstSeqNum &seq_num) +DefaultFetch<Impl>::squashFromDecode(const Addr &new_PC, + const InstSeqNum &seq_num, + unsigned tid) { - DPRINTF(Fetch, "Fetch: Squashing from decode.\n"); + DPRINTF(Fetch, "[tid:%i]: Squashing from decode.\n",tid); - doSquash(new_PC); + doSquash(new_PC, tid); // Tell the CPU to remove any instructions that are in flight between // fetch and decode. - cpu->removeInstsUntil(seq_num); + cpu->removeInstsUntil(seq_num, tid); +} + +template<class Impl> +bool +DefaultFetch<Impl>::checkStall(unsigned tid) const +{ + bool ret_val = false; + + if (cpu->contextSwitch) { + DPRINTF(Fetch,"[tid:%i]: Stalling for a context switch.\n",tid); + ret_val = true; + } else if (stalls[tid].decode) { + DPRINTF(Fetch,"[tid:%i]: Stall from Decode stage detected.\n",tid); + ret_val = true; + } else if (stalls[tid].rename) { + DPRINTF(Fetch,"[tid:%i]: Stall from Rename stage detected.\n",tid); + ret_val = true; + } else if (stalls[tid].iew) { + DPRINTF(Fetch,"[tid:%i]: Stall from IEW stage detected.\n",tid); + ret_val = true; + } else if (stalls[tid].commit) { + DPRINTF(Fetch,"[tid:%i]: Stall from Commit stage detected.\n",tid); + ret_val = true; + } + + return ret_val; +} + +template<class Impl> +typename DefaultFetch<Impl>::FetchStatus +DefaultFetch<Impl>::updateFetchStatus() +{ + //Check Running + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + + unsigned tid = *threads++; + + if (fetchStatus[tid] == Running || + fetchStatus[tid] == Squashing || + fetchStatus[tid] == IcacheAccessComplete) { + + if (_status == Inactive) { + DPRINTF(Activity, "[tid:%i]: Activating stage.\n",tid); + + if (fetchStatus[tid] == IcacheAccessComplete) { + DPRINTF(Activity, "[tid:%i]: Activating fetch due to cache" + "completion\n",tid); + } + + cpu->activateStage(O3CPU::FetchIdx); + } + + return Active; + } + } + + // Stage is switching from active to inactive, notify CPU of it. + if (_status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(O3CPU::FetchIdx); + } + + return Inactive; } template <class Impl> void -SimpleFetch<Impl>::squash(const Addr &new_PC) +DefaultFetch<Impl>::squash(const Addr &new_PC, unsigned tid) { - DPRINTF(Fetch, "Fetch: Squash from commit.\n"); + DPRINTF(Fetch, "[tid:%u]: Squash from commit.\n",tid); - doSquash(new_PC); + doSquash(new_PC, tid); // Tell the CPU to remove any instructions that are not in the ROB. - cpu->removeInstsNotInROB(); + cpu->removeInstsNotInROB(tid); } -template<class Impl> +template <class Impl> void -SimpleFetch<Impl>::tick() +DefaultFetch<Impl>::tick() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + bool status_change = false; + + wroteToTimeBuffer = false; + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + // Check the signals for each thread to determine the proper status + // for each thread. + bool updated_status = checkSignalsAndUpdate(tid); + status_change = status_change || updated_status; + } + + DPRINTF(Fetch, "Running stage.\n"); + + // Reset the number of the instruction we're fetching. + numInst = 0; + +#if FULL_SYSTEM + if (fromCommit->commitInfo[0].interruptPending) { + interruptPending = true; + } + + if (fromCommit->commitInfo[0].clearInterrupt) { + interruptPending = false; + } +#endif + + for (threadFetched = 0; threadFetched < numFetchingThreads; + threadFetched++) { + // Fetch each of the actively fetching threads. + fetch(status_change); + } + + // Record number of instructions fetched this cycle for distribution. + fetchNisnDist.sample(numInst); + + if (status_change) { + // Change the fetch stage status if there was a status change. + _status = updateFetchStatus(); + } + + // If there was activity this cycle, inform the CPU of it. + if (wroteToTimeBuffer || cpu->contextSwitch) { + DPRINTF(Activity, "Activity this cycle.\n"); + + cpu->activityThisCycle(); + } +} + +template <class Impl> +bool +DefaultFetch<Impl>::checkSignalsAndUpdate(unsigned tid) { + // Update the per thread stall statuses. + if (fromDecode->decodeBlock[tid]) { + stalls[tid].decode = true; + } + + if (fromDecode->decodeUnblock[tid]) { + assert(stalls[tid].decode); + assert(!fromDecode->decodeBlock[tid]); + stalls[tid].decode = false; + } + + if (fromRename->renameBlock[tid]) { + stalls[tid].rename = true; + } + + if (fromRename->renameUnblock[tid]) { + assert(stalls[tid].rename); + assert(!fromRename->renameBlock[tid]); + stalls[tid].rename = false; + } + + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } + + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + assert(!fromIEW->iewBlock[tid]); + stalls[tid].iew = false; + } + + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + assert(!fromCommit->commitBlock[tid]); + stalls[tid].commit = false; + } + // Check squash signals from commit. - if (fromCommit->commitInfo.squash) { - DPRINTF(Fetch, "Fetch: Squashing instructions due to squash " - "from commit.\n"); + if (fromCommit->commitInfo[tid].squash) { + + DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash " + "from commit.\n",tid); // In any case, squash. - squash(fromCommit->commitInfo.nextPC); + squash(fromCommit->commitInfo[tid].nextPC,tid); // Also check if there's a mispredict that happened. - if (fromCommit->commitInfo.branchMispredict) { - branchPred.squash(fromCommit->commitInfo.doneSeqNum, - fromCommit->commitInfo.nextPC, - fromCommit->commitInfo.branchTaken); + if (fromCommit->commitInfo[tid].branchMispredict) { + branchPred.squash(fromCommit->commitInfo[tid].doneSeqNum, + fromCommit->commitInfo[tid].nextPC, + fromCommit->commitInfo[tid].branchTaken, + tid); } else { - branchPred.squash(fromCommit->commitInfo.doneSeqNum); + branchPred.squash(fromCommit->commitInfo[tid].doneSeqNum, + tid); } - return; - } else if (fromCommit->commitInfo.doneSeqNum) { + return true; + } else if (fromCommit->commitInfo[tid].doneSeqNum) { // Update the branch predictor if it wasn't a squashed instruction - // that was braodcasted. - branchPred.update(fromCommit->commitInfo.doneSeqNum); + // that was broadcasted. + branchPred.update(fromCommit->commitInfo[tid].doneSeqNum, tid); } // Check ROB squash signals from commit. - if (fromCommit->commitInfo.robSquashing) { - DPRINTF(Fetch, "Fetch: ROB is still squashing.\n"); + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Fetch, "[tid:%u]: ROB is still squashing.\n", tid); // Continue to squash. - _status = Squashing; + fetchStatus[tid] = Squashing; - ++fetchSquashCycles; - return; + return true; } // Check squash signals from decode. - if (fromDecode->decodeInfo.squash) { - DPRINTF(Fetch, "Fetch: Squashing instructions due to squash " - "from decode.\n"); + if (fromDecode->decodeInfo[tid].squash) { + DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash " + "from decode.\n",tid); // Update the branch predictor. - if (fromDecode->decodeInfo.branchMispredict) { - branchPred.squash(fromDecode->decodeInfo.doneSeqNum, - fromDecode->decodeInfo.nextPC, - fromDecode->decodeInfo.branchTaken); + if (fromDecode->decodeInfo[tid].branchMispredict) { + branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum, + fromDecode->decodeInfo[tid].nextPC, + fromDecode->decodeInfo[tid].branchTaken, + tid); } else { - branchPred.squash(fromDecode->decodeInfo.doneSeqNum); + branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum, + tid); } - if (_status != Squashing) { - // Squash unless we're already squashing? - squashFromDecode(fromDecode->decodeInfo.nextPC, - fromDecode->decodeInfo.doneSeqNum); - return; + if (fetchStatus[tid] != Squashing) { + // Squash unless we're already squashing + squashFromDecode(fromDecode->decodeInfo[tid].nextPC, + fromDecode->decodeInfo[tid].doneSeqNum, + tid); + + return true; } } - // Check if any of the stall signals are high. - if (fromDecode->decodeInfo.stall || - fromRename->renameInfo.stall || - fromIEW->iewInfo.stall || - fromCommit->commitInfo.stall) - { - // Block stage, regardless of current status. - - DPRINTF(Fetch, "Fetch: Stalling stage.\n"); - DPRINTF(Fetch, "Fetch: Statuses: Decode: %i Rename: %i IEW: %i " - "Commit: %i\n", - fromDecode->decodeInfo.stall, - fromRename->renameInfo.stall, - fromIEW->iewInfo.stall, - fromCommit->commitInfo.stall); - - _status = Blocked; + if (checkStall(tid) && fetchStatus[tid] != IcacheWaitResponse) { + DPRINTF(Fetch, "[tid:%i]: Setting to blocked\n",tid); - ++fetchBlockedCycles; - return; - } else if (_status == Blocked) { - // Unblock stage if status is currently blocked and none of the - // stall signals are being held high. - _status = Running; + fetchStatus[tid] = Blocked; - ++fetchBlockedCycles; - return; + return true; } - // If fetch has reached this point, then there are no squash signals - // still being held high. Check if fetch is in the squashing state; - // if so, fetch can switch to running. - // Similarly, there are no blocked signals still being held high. - // Check if fetch is in the blocked state; if so, fetch can switch to - // running. - if (_status == Squashing) { - DPRINTF(Fetch, "Fetch: Done squashing, switching to running.\n"); - - // Switch status to running - _status = Running; - - ++fetchCycles; - - fetch(); - } else if (_status != IcacheMissStall) { - DPRINTF(Fetch, "Fetch: Running stage.\n"); + if (fetchStatus[tid] == Blocked || + fetchStatus[tid] == Squashing) { + // Switch status to running if fetch isn't being told to block or + // squash this cycle. + DPRINTF(Fetch, "[tid:%i]: Done squashing, switching to running.\n", + tid); - ++fetchCycles; + fetchStatus[tid] = Running; - fetch(); + return true; } + + // If we've reached this point, we have not gotten any signals that + // cause fetch to change its status. Fetch remains the same as before. + return false; } template<class Impl> void -SimpleFetch<Impl>::fetch() +DefaultFetch<Impl>::fetch(bool &status_change) { ////////////////////////////////////////// // Start actual fetch ////////////////////////////////////////// + int tid = getFetchingThread(fetchPolicy); + + if (tid == -1) { + DPRINTF(Fetch,"There are no more threads available to fetch from.\n"); + + // Breaks looping condition in tick() + threadFetched = numFetchingThreads; + return; + } + + DPRINTF(Fetch, "Attempting to fetch from [tid:%i]\n", tid); // The current PC. - Addr fetch_PC = cpu->readPC(); + Addr &fetch_PC = PC[tid]; // Fault code for memory access. Fault fault = NoFault; @@ -473,60 +917,75 @@ SimpleFetch<Impl>::fetch() // If returning from the delay of a cache miss, then update the status // to running, otherwise do the cache access. Possibly move this up // to tick() function. - if (_status == IcacheMissComplete) { - DPRINTF(Fetch, "Fetch: Icache miss is complete.\n"); - - // Reset the completion event to NULL. - memReq->completionEvent = NULL; - - _status = Running; + if (fetchStatus[tid] == IcacheAccessComplete) { + DPRINTF(Fetch, "[tid:%i]: Icache miss is complete.\n", + tid); + + fetchStatus[tid] = Running; + status_change = true; + } else if (fetchStatus[tid] == Running) { + DPRINTF(Fetch, "[tid:%i]: Attempting to translate and read " + "instruction, starting at PC %08p.\n", + tid, fetch_PC); + + bool fetch_success = fetchCacheLine(fetch_PC, fault, tid); + if (!fetch_success) { + if (cacheBlocked) { + ++icacheStallCycles; + } else { + ++fetchMiscStallCycles; + } + return; + } } else { - DPRINTF(Fetch, "Fetch: Attempting to translate and read " - "instruction, starting at PC %08p.\n", - fetch_PC); + if (fetchStatus[tid] == Idle) { + ++fetchIdleCycles; + } else if (fetchStatus[tid] == Blocked) { + ++fetchBlockedCycles; + } else if (fetchStatus[tid] == Squashing) { + ++fetchSquashCycles; + } else if (fetchStatus[tid] == IcacheWaitResponse) { + ++icacheStallCycles; + } - fault = fetchCacheLine(fetch_PC); + // Status is Idle, Squashing, Blocked, or IcacheWaitResponse, so + // fetch should do nothing. + return; } - // If we had a stall due to an icache miss, then return. It'd - // be nicer if this were handled through the kind of fault that - // is returned by the function. - if (_status == IcacheMissStall) { + ++fetchCycles; + + // If we had a stall due to an icache miss, then return. + if (fetchStatus[tid] == IcacheWaitResponse) { + ++icacheStallCycles; + status_change = true; return; } - // As far as timing goes, the CPU will need to send an event through - // the MemReq in order to be woken up once the memory access completes. - // Probably have a status on a per thread basis so each thread can - // block independently and be woken up independently. - Addr next_PC = fetch_PC; InstSeqNum inst_seq; MachInst inst; - unsigned offset = fetch_PC & cacheBlkMask; - unsigned fetched; + ExtMachInst ext_inst; + // @todo: Fix this hack. + unsigned offset = (fetch_PC & cacheBlkMask) & ~3; if (fault == NoFault) { // If the read of the first instruction was successful, then grab the // instructions from the rest of the cache line and put them into the // queue heading to decode. - DPRINTF(Fetch, "Fetch: Adding instructions to queue to decode.\n"); - - ////////////////////////// - // Fetch first instruction - ////////////////////////// + DPRINTF(Fetch, "[tid:%i]: Adding instructions to queue to " + "decode.\n",tid); // Need to keep track of whether or not a predicted branch // ended this fetch block. bool predicted_branch = false; - for (fetched = 0; + for (; offset < cacheBlkSize && - fetched < fetchWidth && + numInst < fetchWidth && !predicted_branch; - ++fetched) - { + ++numInst) { // Get a sequence number. inst_seq = cpu->getAndIncrementInstSeq(); @@ -536,31 +995,40 @@ SimpleFetch<Impl>::fetch() // Get the instruction from the array of the cache line. inst = gtoh(*reinterpret_cast<MachInst *> - (&cacheData[offset])); + (&cacheData[tid][offset])); + + ext_inst = TheISA::makeExtMI(inst, fetch_PC); // Create a new DynInst from the instruction fetched. - DynInstPtr instruction = new DynInst(inst, fetch_PC, next_PC, + DynInstPtr instruction = new DynInst(ext_inst, fetch_PC, + next_PC, inst_seq, cpu); + instruction->setTid(tid); + + instruction->setASID(tid); + + instruction->setThreadState(cpu->thread[tid]); - DPRINTF(Fetch, "Fetch: Instruction %i created, with PC %#x\n", - inst_seq, instruction->readPC()); + DPRINTF(Fetch, "[tid:%i]: Instruction PC %#x created " + "[sn:%lli]\n", + tid, instruction->readPC(), inst_seq); - DPRINTF(Fetch, "Fetch: Instruction opcode is: %03p\n", - OPCODE(inst)); + DPRINTF(Fetch, "[tid:%i]: Instruction is: %s\n", + tid, instruction->staticInst->disassemble(fetch_PC)); instruction->traceData = - Trace::getInstRecord(curTick, cpu->xcBase(), cpu, + Trace::getInstRecord(curTick, cpu->tcBase(tid), cpu, instruction->staticInst, - instruction->readPC(), 0); + instruction->readPC(),tid); predicted_branch = lookupAndUpdateNextPC(instruction, next_PC); // Add instruction to the CPU's list of instructions. - cpu->addInst(instruction); + instruction->setInstListIt(cpu->addInst(instruction)); // Write the instruction to the first slot in the queue // that heads to decode. - toDecode->insts[fetched] = instruction; + toDecode->insts[numInst] = instruction; toDecode->size++; @@ -570,48 +1038,252 @@ SimpleFetch<Impl>::fetch() // Move to the next instruction, unless we have a branch. fetch_PC = next_PC; + if (instruction->isQuiesce()) { + warn("cycle %lli: Quiesce instruction encountered, halting fetch!", + curTick); + fetchStatus[tid] = QuiescePending; + ++numInst; + status_change = true; + break; + } + offset+= instSize; } + } - fetch_nisn_dist.sample(fetched); + if (numInst > 0) { + wroteToTimeBuffer = true; } // Now that fetching is completed, update the PC to signify what the next - // cycle will be. Might want to move this to the beginning of this - // function so that the PC updates at the beginning of everything. - // Or might want to leave setting the PC to the main CPU, with fetch - // only changing the nextPC (will require correct determination of - // next PC). + // cycle will be. if (fault == NoFault) { - DPRINTF(Fetch, "Fetch: Setting PC to %08p.\n", next_PC); - cpu->setPC(next_PC); - cpu->setNextPC(next_PC + instSize); + DPRINTF(Fetch, "[tid:%i]: Setting PC to %08p.\n",tid, next_PC); + +#if THE_ISA == ALPHA_ISA + PC[tid] = next_PC; + nextPC[tid] = next_PC + instSize; +#else + PC[tid] = next_PC; + nextPC[tid] = next_PC + instSize; + nextPC[tid] = next_PC + instSize; + + thread->setNextPC(thread->readNextNPC()); + thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); +#endif } else { - // If the issue was an icache miss, then we can just return and - // wait until it is handled. - if (_status == IcacheMissStall) { - return; + // We shouldn't be in an icache miss and also have a fault (an ITB + // miss) + if (fetchStatus[tid] == IcacheWaitResponse) { + panic("Fetch should have exited prior to this!"); } - // Handle the fault. - // This stage will not be able to continue until all the ROB - // slots are empty, at which point the fault can be handled. - // The only other way it can wake up is if a squash comes along - // and changes the PC. Not sure how to handle that case...perhaps - // have it handled by the upper level CPU class which peeks into the - // time buffer and sees if a squash comes along, in which case it - // changes the status. + // Send the fault to commit. This thread will not do anything + // until commit handles the fault. The only other way it can + // wake up is if a squash comes along and changes the PC. +#if FULL_SYSTEM + assert(numInst != fetchWidth); + // Get a sequence number. + inst_seq = cpu->getAndIncrementInstSeq(); + // We will use a nop in order to carry the fault. + ext_inst = TheISA::NoopMachInst; - DPRINTF(Fetch, "Fetch: Blocked, need to handle the trap.\n"); + // Create a new DynInst from the dummy nop. + DynInstPtr instruction = new DynInst(ext_inst, fetch_PC, + next_PC, + inst_seq, cpu); + instruction->setPredTarg(next_PC + instSize); + instruction->setTid(tid); - _status = Blocked; -#if FULL_SYSTEM -// cpu->trap(fault); - // Send a signal to the ROB indicating that there's a trap from the - // fetch stage that needs to be handled. Need to indicate that - // there's a fault, and the fault type. + instruction->setASID(tid); + + instruction->setThreadState(cpu->thread[tid]); + + instruction->traceData = NULL; + + instruction->setInstListIt(cpu->addInst(instruction)); + + instruction->fault = fault; + + toDecode->insts[numInst] = instruction; + toDecode->size++; + + DPRINTF(Fetch, "[tid:%i]: Blocked, need to handle the trap.\n",tid); + + fetchStatus[tid] = TrapPending; + status_change = true; + + warn("cycle %lli: fault (%d) detected @ PC %08p", curTick, fault, PC[tid]); #else // !FULL_SYSTEM - fatal("fault (%d) detected @ PC %08p", fault, cpu->readPC()); + warn("cycle %lli: fault (%d) detected @ PC %08p", curTick, fault, PC[tid]); #endif // FULL_SYSTEM } } + +template<class Impl> +void +DefaultFetch<Impl>::recvRetry() +{ + assert(cacheBlocked); + if (retryPkt != NULL) { + assert(retryTid != -1); + assert(fetchStatus[retryTid] == IcacheWaitRetry); + + if (icachePort->sendTiming(retryPkt)) { + fetchStatus[retryTid] = IcacheWaitResponse; + retryPkt = NULL; + retryTid = -1; + cacheBlocked = false; + } + } else { + assert(retryTid == -1); + // Access has been squashed since it was sent out. Just clear + // the cache being blocked. + cacheBlocked = false; + } +} + +/////////////////////////////////////// +// // +// SMT FETCH POLICY MAINTAINED HERE // +// // +/////////////////////////////////////// +template<class Impl> +int +DefaultFetch<Impl>::getFetchingThread(FetchPriority &fetch_priority) +{ + if (numThreads > 1) { + switch (fetch_priority) { + + case SingleThread: + return 0; + + case RoundRobin: + return roundRobin(); + + case IQ: + return iqCount(); + + case LSQ: + return lsqCount(); + + case Branch: + return branchCount(); + + default: + return -1; + } + } else { + int tid = *((*activeThreads).begin()); + + if (fetchStatus[tid] == Running || + fetchStatus[tid] == IcacheAccessComplete || + fetchStatus[tid] == Idle) { + return tid; + } else { + return -1; + } + } + +} + + +template<class Impl> +int +DefaultFetch<Impl>::roundRobin() +{ + list<unsigned>::iterator pri_iter = priorityList.begin(); + list<unsigned>::iterator end = priorityList.end(); + + int high_pri; + + while (pri_iter != end) { + high_pri = *pri_iter; + + assert(high_pri <= numThreads); + + if (fetchStatus[high_pri] == Running || + fetchStatus[high_pri] == IcacheAccessComplete || + fetchStatus[high_pri] == Idle) { + + priorityList.erase(pri_iter); + priorityList.push_back(high_pri); + + return high_pri; + } + + pri_iter++; + } + + return -1; +} + +template<class Impl> +int +DefaultFetch<Impl>::iqCount() +{ + priority_queue<unsigned> PQ; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + PQ.push(fromIEW->iewInfo[tid].iqCount); + } + + while (!PQ.empty()) { + + unsigned high_pri = PQ.top(); + + if (fetchStatus[high_pri] == Running || + fetchStatus[high_pri] == IcacheAccessComplete || + fetchStatus[high_pri] == Idle) + return high_pri; + else + PQ.pop(); + + } + + return -1; +} + +template<class Impl> +int +DefaultFetch<Impl>::lsqCount() +{ + priority_queue<unsigned> PQ; + + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + PQ.push(fromIEW->iewInfo[tid].ldstqCount); + } + + while (!PQ.empty()) { + + unsigned high_pri = PQ.top(); + + if (fetchStatus[high_pri] == Running || + fetchStatus[high_pri] == IcacheAccessComplete || + fetchStatus[high_pri] == Idle) + return high_pri; + else + PQ.pop(); + + } + + return -1; +} + +template<class Impl> +int +DefaultFetch<Impl>::branchCount() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + panic("Branch Count Fetch policy unimplemented\n"); + return *threads; +} diff --git a/src/cpu/o3/free_list.cc b/src/cpu/o3/free_list.cc index 6f0b4be1e..ae651398b 100644 --- a/src/cpu/o3/free_list.cc +++ b/src/cpu/o3/free_list.cc @@ -24,13 +24,16 @@ * 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. + * + * Authors: Kevin Lim */ #include "base/trace.hh" #include "cpu/o3/free_list.hh" -SimpleFreeList::SimpleFreeList(unsigned _numLogicalIntRegs, +SimpleFreeList::SimpleFreeList(unsigned activeThreads, + unsigned _numLogicalIntRegs, unsigned _numPhysicalIntRegs, unsigned _numLogicalFloatRegs, unsigned _numPhysicalFloatRegs) @@ -40,43 +43,30 @@ SimpleFreeList::SimpleFreeList(unsigned _numLogicalIntRegs, numPhysicalFloatRegs(_numPhysicalFloatRegs), numPhysicalRegs(numPhysicalIntRegs + numPhysicalFloatRegs) { - DPRINTF(FreeList, "FreeList: Creating new free list object.\n"); - - // DEBUG stuff. - freeIntRegsScoreboard.resize(numPhysicalIntRegs); - - freeFloatRegsScoreboard.resize(numPhysicalRegs); - - for (PhysRegIndex i = 0; i < numLogicalIntRegs; ++i) { - freeIntRegsScoreboard[i] = 0; - } + DPRINTF(FreeList, "Creating new free list object.\n"); // Put all of the extra physical registers onto the free list. This // means excluding all of the base logical registers. - for (PhysRegIndex i = numLogicalIntRegs; + for (PhysRegIndex i = numLogicalIntRegs * activeThreads; i < numPhysicalIntRegs; ++i) { freeIntRegs.push(i); - - freeIntRegsScoreboard[i] = 1; - } - - for (PhysRegIndex i = 0; i < numPhysicalIntRegs + numLogicalFloatRegs; - ++i) - { - freeFloatRegsScoreboard[i] = 0; } // Put all of the extra physical registers onto the free list. This // means excluding all of the base logical registers. Because the // float registers' indices start where the physical registers end, // some math must be done to determine where the free registers start. - for (PhysRegIndex i = numPhysicalIntRegs + numLogicalFloatRegs; - i < numPhysicalRegs; ++i) + PhysRegIndex i = numPhysicalIntRegs + (numLogicalFloatRegs * activeThreads); + + for ( ; i < numPhysicalRegs; ++i) { freeFloatRegs.push(i); - - freeFloatRegsScoreboard[i] = 1; } } +std::string +SimpleFreeList::name() const +{ + return "cpu.freelist"; +} diff --git a/src/cpu/o3/free_list.hh b/src/cpu/o3/free_list.hh index 0b85dba1e..c669b0b34 100644 --- a/src/cpu/o3/free_list.hh +++ b/src/cpu/o3/free_list.hh @@ -24,15 +24,18 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_FREE_LIST_HH__ -#define __CPU_O3_CPU_FREE_LIST_HH__ +#ifndef __CPU_O3_FREE_LIST_HH__ +#define __CPU_O3_FREE_LIST_HH__ #include <iostream> #include <queue> #include "arch/isa_traits.hh" +#include "base/misc.hh" #include "base/trace.hh" #include "base/traceflags.hh" #include "cpu/o3/comm.hh" @@ -45,10 +48,9 @@ * other classes, it assumes that the indices for the floating point * registers starts after the integer registers end. Hence the variable * numPhysicalIntRegs is logically equivalent to the baseFP dependency. - * Note that - * while this most likely should be called FreeList, the name "FreeList" - * is used in a typedef within the CPU Policy, and therefore no class - * can be named simply "FreeList". + * Note that while this most likely should be called FreeList, the name + * "FreeList" is used in a typedef within the CPU Policy, and therefore no + * class can be named simply "FreeList". * @todo: Give a better name to the base FP dependency. */ class SimpleFreeList @@ -75,36 +77,51 @@ class SimpleFreeList /** Total number of physical registers. */ int numPhysicalRegs; - /** DEBUG stuff below. */ - std::vector<int> freeIntRegsScoreboard; - - std::vector<bool> freeFloatRegsScoreboard; - public: - SimpleFreeList(unsigned _numLogicalIntRegs, + /** Constructs a free list. + * @param activeThreads Number of active threads. + * @param _numLogicalIntRegs Number of logical integer registers. + * @param _numPhysicalIntRegs Number of physical integer registers. + * @param _numLogicalFloatRegs Number of logical fp registers. + * @param _numPhysicalFloatRegs Number of physical fp registers. + */ + SimpleFreeList(unsigned activeThreads, + unsigned _numLogicalIntRegs, unsigned _numPhysicalIntRegs, unsigned _numLogicalFloatRegs, unsigned _numPhysicalFloatRegs); + /** Gives the name of the freelist. */ + std::string name() const; + + /** Gets a free integer register. */ inline PhysRegIndex getIntReg(); + /** Gets a free fp register. */ inline PhysRegIndex getFloatReg(); + /** Adds a register back to the free list. */ inline void addReg(PhysRegIndex freed_reg); + /** Adds an integer register back to the free list. */ inline void addIntReg(PhysRegIndex freed_reg); + /** Adds a fp register back to the free list. */ inline void addFloatReg(PhysRegIndex freed_reg); + /** Checks if there are any free integer registers. */ bool hasFreeIntRegs() { return !freeIntRegs.empty(); } + /** Checks if there are any free fp registers. */ bool hasFreeFloatRegs() { return !freeFloatRegs.empty(); } + /** Returns the number of free integer registers. */ int numFreeIntRegs() { return freeIntRegs.size(); } + /** Returns the number of free fp registers. */ int numFreeFloatRegs() { return freeFloatRegs.size(); } }; @@ -112,7 +129,8 @@ class SimpleFreeList inline PhysRegIndex SimpleFreeList::getIntReg() { - DPRINTF(Rename, "FreeList: Trying to get free integer register.\n"); + DPRINTF(FreeList, "Trying to get free integer register.\n"); + if (freeIntRegs.empty()) { panic("No free integer registers!"); } @@ -121,17 +139,14 @@ SimpleFreeList::getIntReg() freeIntRegs.pop(); - // DEBUG - assert(freeIntRegsScoreboard[free_reg]); - freeIntRegsScoreboard[free_reg] = 0; - return(free_reg); } inline PhysRegIndex SimpleFreeList::getFloatReg() { - DPRINTF(Rename, "FreeList: Trying to get free float register.\n"); + DPRINTF(FreeList, "Trying to get free float register.\n"); + if (freeFloatRegs.empty()) { panic("No free integer registers!"); } @@ -140,42 +155,28 @@ SimpleFreeList::getFloatReg() freeFloatRegs.pop(); - // DEBUG - assert(freeFloatRegsScoreboard[free_reg]); - freeFloatRegsScoreboard[free_reg] = 0; - return(free_reg); } inline void SimpleFreeList::addReg(PhysRegIndex freed_reg) { - DPRINTF(Rename, "Freelist: Freeing register %i.\n", freed_reg); + DPRINTF(FreeList,"Freeing register %i.\n", freed_reg); //Might want to add in a check for whether or not this register is //already in there. A bit vector or something similar would be useful. if (freed_reg < numPhysicalIntRegs) { - freeIntRegs.push(freed_reg); - - // DEBUG - assert(freeIntRegsScoreboard[freed_reg] == false); - freeIntRegsScoreboard[freed_reg] = 1; + if (freed_reg != TheISA::ZeroReg) + freeIntRegs.push(freed_reg); } else if (freed_reg < numPhysicalRegs) { - freeFloatRegs.push(freed_reg); - - // DEBUG - assert(freeFloatRegsScoreboard[freed_reg] == false); - freeFloatRegsScoreboard[freed_reg] = 1; + if (freed_reg != (TheISA::ZeroReg + numPhysicalIntRegs)) + freeFloatRegs.push(freed_reg); } } inline void SimpleFreeList::addIntReg(PhysRegIndex freed_reg) { - DPRINTF(Rename, "Freelist: Freeing int register %i.\n", freed_reg); - - // DEBUG - assert(!freeIntRegsScoreboard[freed_reg]); - freeIntRegsScoreboard[freed_reg] = 1; + DPRINTF(FreeList,"Freeing int register %i.\n", freed_reg); freeIntRegs.push(freed_reg); } @@ -183,13 +184,9 @@ SimpleFreeList::addIntReg(PhysRegIndex freed_reg) inline void SimpleFreeList::addFloatReg(PhysRegIndex freed_reg) { - DPRINTF(Rename, "Freelist: Freeing float register %i.\n", freed_reg); - - // DEBUG - assert(!freeFloatRegsScoreboard[freed_reg]); - freeFloatRegsScoreboard[freed_reg] = 1; + DPRINTF(FreeList,"Freeing float register %i.\n", freed_reg); freeFloatRegs.push(freed_reg); } -#endif // __CPU_O3_CPU_FREE_LIST_HH__ +#endif // __CPU_O3_FREE_LIST_HH__ diff --git a/src/cpu/o3/fu_pool.cc b/src/cpu/o3/fu_pool.cc new file mode 100644 index 000000000..42e329aca --- /dev/null +++ b/src/cpu/o3/fu_pool.cc @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include <sstream> + +#include "cpu/o3/fu_pool.hh" +#include "cpu/func_unit.hh" +#include "sim/builder.hh" + +using namespace std; + +//////////////////////////////////////////////////////////////////////////// +// +// A pool of function units +// + +inline void +FUPool::FUIdxQueue::addFU(int fu_idx) +{ + funcUnitsIdx.push_back(fu_idx); + ++size; +} + +inline int +FUPool::FUIdxQueue::getFU() +{ + int retval = funcUnitsIdx[idx++]; + + if (idx == size) + idx = 0; + + return retval; +} + +FUPool::~FUPool() +{ + fuListIterator i = funcUnits.begin(); + fuListIterator end = funcUnits.end(); + for (; i != end; ++i) + delete *i; +} + + +// Constructor +FUPool::FUPool(string name, vector<FUDesc *> paramList) + : SimObject(name) +{ + numFU = 0; + + funcUnits.clear(); + + for (int i = 0; i < Num_OpClasses; ++i) { + maxOpLatencies[i] = 0; + maxIssueLatencies[i] = 0; + } + + // + // Iterate through the list of FUDescData structures + // + for (FUDDiterator i = paramList.begin(); i != paramList.end(); ++i) { + + // + // Don't bother with this if we're not going to create any FU's + // + if ((*i)->number) { + // + // Create the FuncUnit object from this structure + // - add the capabilities listed in the FU's operation + // description + // + // We create the first unit, then duplicate it as needed + // + FuncUnit *fu = new FuncUnit; + + OPDDiterator j = (*i)->opDescList.begin(); + OPDDiterator end = (*i)->opDescList.end(); + for (; j != end; ++j) { + // indicate that this pool has this capability + capabilityList.set((*j)->opClass); + + // Add each of the FU's that will have this capability to the + // appropriate queue. + for (int k = 0; k < (*i)->number; ++k) + fuPerCapList[(*j)->opClass].addFU(numFU + k); + + // indicate that this FU has the capability + fu->addCapability((*j)->opClass, (*j)->opLat, (*j)->issueLat); + + if ((*j)->opLat > maxOpLatencies[(*j)->opClass]) + maxOpLatencies[(*j)->opClass] = (*j)->opLat; + + if ((*j)->issueLat > maxIssueLatencies[(*j)->opClass]) + maxIssueLatencies[(*j)->opClass] = (*j)->issueLat; + } + + numFU++; + + // Add the appropriate number of copies of this FU to the list + ostringstream s; + + s << (*i)->name() << "(0)"; + fu->name = s.str(); + funcUnits.push_back(fu); + + for (int c = 1; c < (*i)->number; ++c) { + ostringstream s; + numFU++; + FuncUnit *fu2 = new FuncUnit(*fu); + + s << (*i)->name() << "(" << c << ")"; + fu2->name = s.str(); + funcUnits.push_back(fu2); + } + } + } + + unitBusy.resize(numFU); + + for (int i = 0; i < numFU; i++) { + unitBusy[i] = false; + } +} + +void +FUPool::annotateMemoryUnits(unsigned hit_latency) +{ + maxOpLatencies[MemReadOp] = hit_latency; + + fuListIterator i = funcUnits.begin(); + fuListIterator iend = funcUnits.end(); + for (; i != iend; ++i) { + if ((*i)->provides(MemReadOp)) + (*i)->opLatency(MemReadOp) = hit_latency; + + if ((*i)->provides(MemWriteOp)) + (*i)->opLatency(MemWriteOp) = hit_latency; + } +} + +int +FUPool::getUnit(OpClass capability) +{ + // If this pool doesn't have the specified capability, + // return this information to the caller + if (!capabilityList[capability]) + return -2; + + int fu_idx = fuPerCapList[capability].getFU(); + int start_idx = fu_idx; + + // Iterate through the circular queue if needed, stopping if we've reached + // the first element again. + while (unitBusy[fu_idx]) { + fu_idx = fuPerCapList[capability].getFU(); + if (fu_idx == start_idx) { + // No FU available + return -1; + } + } + + assert(fu_idx < numFU); + + unitBusy[fu_idx] = true; + + return fu_idx; +} + +void +FUPool::freeUnitNextCycle(int fu_idx) +{ + assert(unitBusy[fu_idx]); + unitsToBeFreed.push_back(fu_idx); +} + +void +FUPool::processFreeUnits() +{ + while (!unitsToBeFreed.empty()) { + int fu_idx = unitsToBeFreed.back(); + unitsToBeFreed.pop_back(); + + assert(unitBusy[fu_idx]); + + unitBusy[fu_idx] = false; + } +} + +void +FUPool::dump() +{ + cout << "Function Unit Pool (" << name() << ")\n"; + cout << "======================================\n"; + cout << "Free List:\n"; + + for (int i = 0; i < numFU; ++i) { + if (unitBusy[i]) { + continue; + } + + cout << " [" << i << "] : "; + + cout << funcUnits[i]->name << " "; + + cout << "\n"; + } + + cout << "======================================\n"; + cout << "Busy List:\n"; + for (int i = 0; i < numFU; ++i) { + if (!unitBusy[i]) { + continue; + } + + cout << " [" << i << "] : "; + + cout << funcUnits[i]->name << " "; + + cout << "\n"; + } +} + +void +FUPool::switchOut() +{ +} + +void +FUPool::takeOverFrom() +{ + for (int i = 0; i < numFU; i++) { + unitBusy[i] = false; + } + unitsToBeFreed.clear(); +} + +// + +//////////////////////////////////////////////////////////////////////////// +// +// The SimObjects we use to get the FU information into the simulator +// +//////////////////////////////////////////////////////////////////////////// + +// +// FUPool - Contails a list of FUDesc objects to make available +// + +// +// The FuPool object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(FUPool) + + SimObjectVectorParam<FUDesc *> FUList; + +END_DECLARE_SIM_OBJECT_PARAMS(FUPool) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(FUPool) + + INIT_PARAM(FUList, "list of FU's for this pool") + +END_INIT_SIM_OBJECT_PARAMS(FUPool) + + +CREATE_SIM_OBJECT(FUPool) +{ + return new FUPool(getInstanceName(), FUList); +} + +REGISTER_SIM_OBJECT("FUPool", FUPool) + diff --git a/src/cpu/o3/fu_pool.hh b/src/cpu/o3/fu_pool.hh new file mode 100644 index 000000000..52d83f056 --- /dev/null +++ b/src/cpu/o3/fu_pool.hh @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_FU_POOL_HH__ +#define __CPU_O3_FU_POOL_HH__ + +#include <bitset> +#include <list> +#include <string> +#include <vector> + +#include "base/sched_list.hh" +#include "cpu/op_class.hh" +#include "sim/sim_object.hh" + +class FUDesc; +class FuncUnit; + +/** + * Pool of FU's, specific to the new CPU model. The old FU pool had lists of + * free units and busy units, and whenever a FU was needed it would iterate + * through the free units to find a FU that provided the capability. This pool + * has lists of units specific to each of the capabilities, and whenever a FU + * is needed, it iterates through that list to find a free unit. The previous + * FU pool would have to be ticked each cycle to update which units became + * free. This FU pool lets the IEW stage handle freeing units, which frees + * them as their scheduled execution events complete. This limits units in this + * model to either have identical issue and op latencies, or 1 cycle issue + * latencies. + */ +class FUPool : public SimObject +{ + private: + /** Maximum op execution latencies, per op class. */ + unsigned maxOpLatencies[Num_OpClasses]; + /** Maximum issue latencies, per op class. */ + unsigned maxIssueLatencies[Num_OpClasses]; + + /** Bitvector listing capabilities of this FU pool. */ + std::bitset<Num_OpClasses> capabilityList; + + /** Bitvector listing which FUs are busy. */ + std::vector<bool> unitBusy; + + /** List of units to be freed at the end of this cycle. */ + std::vector<int> unitsToBeFreed; + + /** + * Class that implements a circular queue to hold FU indices. The hope is + * that FUs that have been just used will be moved to the end of the queue + * by iterating through it, thus leaving free units at the head of the + * queue. + */ + class FUIdxQueue { + public: + /** Constructs a circular queue of FU indices. */ + FUIdxQueue() + : idx(0), size(0) + { } + + /** Adds a FU to the queue. */ + inline void addFU(int fu_idx); + + /** Returns the index of the FU at the head of the queue, and changes + * the index to the next element. + */ + inline int getFU(); + + private: + /** Circular queue index. */ + int idx; + + /** Size of the queue. */ + int size; + + /** Queue of FU indices. */ + std::vector<int> funcUnitsIdx; + }; + + /** Per op class queues of FUs that provide that capability. */ + FUIdxQueue fuPerCapList[Num_OpClasses]; + + /** Number of FUs. */ + int numFU; + + /** Functional units. */ + std::vector<FuncUnit *> funcUnits; + + typedef std::vector<FuncUnit *>::iterator fuListIterator; + + public: + + /** Constructs a FU pool. */ + FUPool(std::string name, std::vector<FUDesc *> l); + ~FUPool(); + + /** Annotates units that provide memory operations. Included only because + * old FU pool provided this function. + */ + void annotateMemoryUnits(unsigned hit_latency); + + /** + * Gets a FU providing the requested capability. Will mark the unit as busy, + * but leaves the freeing of the unit up to the IEW stage. + * @param capability The capability requested. + * @return Returns -2 if the FU pool does not have the capability, -1 if + * there is no free FU, and the FU's index otherwise. + */ + int getUnit(OpClass capability); + + /** Frees a FU at the end of this cycle. */ + void freeUnitNextCycle(int fu_idx); + + /** Frees all FUs on the list. */ + void processFreeUnits(); + + /** Returns the total number of FUs. */ + int size() { return numFU; } + + /** Debugging function used to dump FU information. */ + void dump(); + + /** Returns the operation execution latency of the given capability. */ + unsigned getOpLatency(OpClass capability) { + return maxOpLatencies[capability]; + } + + /** Returns the issue latency of the given capability. */ + unsigned getIssueLatency(OpClass capability) { + return maxIssueLatencies[capability]; + } + + /** Switches out functional unit pool. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); +}; + +#endif // __CPU_O3_FU_POOL_HH__ diff --git a/src/cpu/o3/iew.cc b/src/cpu/o3/iew.cc index 45b5610e7..f99be7fe0 100644 --- a/src/cpu/o3/iew.cc +++ b/src/cpu/o3/iew.cc @@ -24,11 +24,12 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/isa_specific.hh" #include "cpu/o3/iew_impl.hh" #include "cpu/o3/inst_queue.hh" -template class SimpleIEW<AlphaSimpleImpl>; +template class DefaultIEW<O3CPUImpl>; diff --git a/src/cpu/o3/iew.hh b/src/cpu/o3/iew.hh index 1e370d4e6..2af68d8fc 100644 --- a/src/cpu/o3/iew.hh +++ b/src/cpu/o3/iew.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,113 +24,265 @@ * 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. + * + * Authors: Kevin Lim */ -//Todo: Update with statuses. -//Need to handle delaying writes to the writeback bus if it's full at the -//given time. - -#ifndef __CPU_O3_CPU_SIMPLE_IEW_HH__ -#define __CPU_O3_CPU_SIMPLE_IEW_HH__ +#ifndef __CPU_O3_IEW_HH__ +#define __CPU_O3_IEW_HH__ #include <queue> -#include "config/full_system.hh" #include "base/statistics.hh" #include "base/timebuf.hh" +#include "config/full_system.hh" #include "cpu/o3/comm.hh" - +#include "cpu/o3/scoreboard.hh" +#include "cpu/o3/lsq.hh" + +class FUPool; + +/** + * DefaultIEW handles both single threaded and SMT IEW + * (issue/execute/writeback). It handles the dispatching of + * instructions to the LSQ/IQ as part of the issue stage, and has the + * IQ try to issue instructions each cycle. The execute latency is + * actually tied into the issue latency to allow the IQ to be able to + * do back-to-back scheduling without having to speculatively schedule + * instructions. This happens by having the IQ have access to the + * functional units, and the IQ gets the execution latencies from the + * FUs when it issues instructions. Instructions reach the execute + * stage on the last cycle of their execution, which is when the IQ + * knows to wake up any dependent instructions, allowing back to back + * scheduling. The execute portion of IEW separates memory + * instructions from non-memory instructions, either telling the LSQ + * to execute the instruction, or executing the instruction directly. + * The writeback portion of IEW completes the instructions by waking + * up any dependents, and marking the register ready on the + * scoreboard. + */ template<class Impl> -class SimpleIEW +class DefaultIEW { private: //Typedefs from Impl typedef typename Impl::CPUPol CPUPol; typedef typename Impl::DynInstPtr DynInstPtr; - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::O3CPU O3CPU; typedef typename Impl::Params Params; typedef typename CPUPol::IQ IQ; typedef typename CPUPol::RenameMap RenameMap; - typedef typename CPUPol::LDSTQ LDSTQ; + typedef typename CPUPol::LSQ LSQ; typedef typename CPUPol::TimeStruct TimeStruct; typedef typename CPUPol::IEWStruct IEWStruct; typedef typename CPUPol::RenameStruct RenameStruct; typedef typename CPUPol::IssueStruct IssueStruct; - friend class Impl::FullCPU; + friend class Impl::O3CPU; + friend class CPUPol::IQ; + public: + /** Overall IEW stage status. Used to determine if the CPU can + * deschedule itself due to a lack of activity. + */ enum Status { + Active, + Inactive + }; + + /** Status for Issue, Execute, and Writeback stages. */ + enum StageStatus { Running, Blocked, Idle, + StartSquash, Squashing, Unblocking }; private: + /** Overall stage status. */ Status _status; - Status _issueStatus; - Status _exeStatus; - Status _wbStatus; + /** Dispatch status. */ + StageStatus dispatchStatus[Impl::MaxThreads]; + /** Execute status. */ + StageStatus exeStatus; + /** Writeback status. */ + StageStatus wbStatus; public: - class WritebackEvent : public Event { - private: - DynInstPtr inst; - SimpleIEW<Impl> *iewStage; + /** Constructs a DefaultIEW with the given parameters. */ + DefaultIEW(Params *params); - public: - WritebackEvent(DynInstPtr &_inst, SimpleIEW<Impl> *_iew); - - virtual void process(); - virtual const char *description(); - }; - - public: - SimpleIEW(Params ¶ms); + /** Returns the name of the DefaultIEW stage. */ + std::string name() const; + /** Registers statistics. */ void regStats(); - void setCPU(FullCPU *cpu_ptr); + /** Initializes stage; sends back the number of free IQ and LSQ entries. */ + void initStage(); + /** Sets CPU pointer for IEW, IQ, and LSQ. */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets main time buffer used for backwards communication. */ void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + /** Sets time buffer for getting instructions coming from rename. */ void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr); + /** Sets time buffer to pass on instructions to commit. */ void setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr); - void setRenameMap(RenameMap *rm_ptr); + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to the scoreboard. */ + void setScoreboard(Scoreboard *sb_ptr); - void squash(); + /** Starts switch out of IEW stage. */ + void switchOut(); - void squashDueToBranch(DynInstPtr &inst); + /** Completes switch out of IEW stage. */ + void doSwitchOut(); - void squashDueToMem(DynInstPtr &inst); + /** Takes over from another CPU's thread. */ + void takeOverFrom(); - void block(); + /** Returns if IEW is switched out. */ + bool isSwitchedOut() { return switchedOut; } - inline void unblock(); + /** Squashes instructions in IEW for a specific thread. */ + void squash(unsigned tid); + /** Wakes all dependents of a completed instruction. */ void wakeDependents(DynInstPtr &inst); + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst); + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst); + + /** Sends an instruction to commit through the time buffer. */ void instToCommit(DynInstPtr &inst); + /** Inserts unused instructions of a thread into the skid buffer. */ + void skidInsert(unsigned tid); + + /** Returns the max of the number of entries in all of the skid buffers. */ + int skidCount(); + + /** Returns if all of the skid buffers are empty. */ + bool skidsEmpty(); + + /** Updates overall IEW status based on all of the stages' statuses. */ + void updateStatus(); + + /** Resets entries of the IQ and the LSQ. */ + void resetEntries(); + + /** Tells the CPU to wakeup if it has descheduled itself due to no + * activity. Used mainly by the LdWritebackEvent. + */ + void wakeCPU(); + + /** Reports to the CPU that there is activity this cycle. */ + void activityThisCycle(); + + /** Tells CPU that the IEW stage is active and running. */ + inline void activateStage(); + + /** Tells CPU that the IEW stage is inactive and idle. */ + inline void deactivateStage(); + + /** Returns if the LSQ has any stores to writeback. */ + bool hasStoresToWB() { return ldstQueue.hasStoresToWB(); } + private: - void dispatchInsts(); + /** Sends commit proper information for a squash due to a branch + * mispredict. + */ + void squashDueToBranch(DynInstPtr &inst, unsigned thread_id); + /** Sends commit proper information for a squash due to a memory order + * violation. + */ + void squashDueToMemOrder(DynInstPtr &inst, unsigned thread_id); + + /** Sends commit proper information for a squash due to memory becoming + * blocked (younger issued instructions must be retried). + */ + void squashDueToMemBlocked(DynInstPtr &inst, unsigned thread_id); + + /** Sets Dispatch to blocked, and signals back to other stages to block. */ + void block(unsigned thread_id); + + /** Unblocks Dispatch if the skid buffer is empty, and signals back to + * other stages to unblock. + */ + void unblock(unsigned thread_id); + + /** Determines proper actions to take given Dispatch's status. */ + void dispatch(unsigned tid); + + /** Dispatches instructions to IQ and LSQ. */ + void dispatchInsts(unsigned tid); + + /** Executes instructions. In the case of memory operations, it informs the + * LSQ to execute the instructions. Also handles any redirects that occur + * due to the executed instructions. + */ void executeInsts(); + /** Writebacks instructions. In our model, the instruction's execute() + * function atomically reads registers, executes, and writes registers. + * Thus this writeback only wakes up dependent instructions, and informs + * the scoreboard of registers becoming ready. + */ + void writebackInsts(); + + /** Returns the number of valid, non-squashed instructions coming from + * rename to dispatch. + */ + unsigned validInstsFromRename(); + + /** Reads the stall signals. */ + void readStallSignals(unsigned tid); + + /** Checks if any of the stall conditions are currently true. */ + bool checkStall(unsigned tid); + + /** Processes inputs and changes state accordingly. */ + void checkSignalsAndUpdate(unsigned tid); + + /** Removes instructions from rename from a thread's instruction list. */ + void emptyRenameInsts(unsigned tid); + + /** Sorts instructions coming from rename into lists separated by thread. */ + void sortInsts(); + public: + /** Ticks IEW stage, causing Dispatch, the IQ, the LSQ, Execute, and + * Writeback to run for one cycle. + */ void tick(); - void iew(); + private: + /** Updates execution stats based on the instruction. */ + void updateExeInstStats(DynInstPtr &inst); - //Interfaces to objects inside and outside of IEW. - /** Time buffer interface. */ + /** Pointer to main time buffer used for backwards communication. */ TimeBuffer<TimeStruct> *timeBuffer; + /** Wire to write information heading to previous stages. */ + typename TimeBuffer<TimeStruct>::wire toFetch; + /** Wire to get commit's output from backwards time buffer. */ typename TimeBuffer<TimeStruct>::wire fromCommit; @@ -158,32 +310,67 @@ class SimpleIEW /** Wire to write infromation heading to commit. */ typename TimeBuffer<IEWStruct>::wire toCommit; - //Will need internal queue to hold onto instructions coming from - //the rename stage in case of a stall. + /** Queue of all instructions coming from rename this cycle. */ + std::queue<DynInstPtr> insts[Impl::MaxThreads]; + /** Skid buffer between rename and IEW. */ - std::queue<RenameStruct> skidBuffer; + std::queue<DynInstPtr> skidBuffer[Impl::MaxThreads]; + + /** Scoreboard pointer. */ + Scoreboard* scoreboard; - protected: + public: /** Instruction queue. */ IQ instQueue; - LDSTQ ldstQueue; + /** Load / store queue. */ + LSQ ldstQueue; -#if !FULL_SYSTEM - public: - void lsqWriteback(); -#endif + /** Pointer to the functional unit pool. */ + FUPool *fuPool; private: - /** Pointer to rename map. Might not want this stage to directly - * access this though... + /** CPU pointer. */ + O3CPU *cpu; + + /** Records if IEW has written to the time buffer this cycle, so that the + * CPU can deschedule itself if there is no activity. */ - RenameMap *renameMap; + bool wroteToTimeBuffer; + + /** Source of possible stalls. */ + struct Stalls { + bool commit; + }; - /** CPU interface. */ - FullCPU *cpu; + /** Stages that are telling IEW to stall. */ + Stalls stalls[Impl::MaxThreads]; + + /** Debug function to print instructions that are issued this cycle. */ + void printAvailableInsts(); + + public: + /** Records if the LSQ needs to be updated on the next cycle, so that + * IEW knows if there will be activity on the next cycle. + */ + bool updateLSQNextCycle; private: + /** Records if there is a fetch redirect on this cycle for each thread. */ + bool fetchRedirect[Impl::MaxThreads]; + + /** Used to track if all instructions have been dispatched this cycle. + * If they have not, then blocking must have occurred, and the instructions + * would already be added to the skid buffer. + * @todo: Fix this hack. + */ + bool dispatchedAllInsts; + + /** Records if the queues have been changed (inserted or issued insts), + * so that IEW knows to broadcast the updated amount of free entries. + */ + bool updatedQueues; + /** Commit to IEW delay, in ticks. */ unsigned commitToIEWDelay; @@ -206,34 +393,96 @@ class SimpleIEW /** Width of issue, in instructions. */ unsigned issueWidth; - /** Width of execute, in instructions. Might make more sense to break - * down into FP vs int. - */ - unsigned executeWidth; + /** Index into queue of instructions being written back. */ + unsigned wbNumInst; - /** Number of cycles stage has been squashing. Used so that the stage - * knows when it can start unblocking, which is when the previous stage - * has received the stall signal and clears up its outputs. + /** Cycle number within the queue of instructions being written back. + * Used in case there are too many instructions writing back at the current + * cycle and writesbacks need to be scheduled for the future. See comments + * in instToCommit(). */ - unsigned cyclesSquashing; + unsigned wbCycle; + /** Number of active threads. */ + unsigned numThreads; + + /** Pointer to list of active threads. */ + std::list<unsigned> *activeThreads; + + /** Maximum size of the skid buffer. */ + unsigned skidBufferMax; + + /** Is this stage switched out. */ + bool switchedOut; + + /** Stat for total number of idle cycles. */ Stats::Scalar<> iewIdleCycles; + /** Stat for total number of squashing cycles. */ Stats::Scalar<> iewSquashCycles; + /** Stat for total number of blocking cycles. */ Stats::Scalar<> iewBlockCycles; + /** Stat for total number of unblocking cycles. */ Stats::Scalar<> iewUnblockCycles; -// Stats::Scalar<> iewWBInsts; + /** Stat for total number of instructions dispatched. */ Stats::Scalar<> iewDispatchedInsts; + /** Stat for total number of squashed instructions dispatch skips. */ Stats::Scalar<> iewDispSquashedInsts; + /** Stat for total number of dispatched load instructions. */ Stats::Scalar<> iewDispLoadInsts; + /** Stat for total number of dispatched store instructions. */ Stats::Scalar<> iewDispStoreInsts; + /** Stat for total number of dispatched non speculative instructions. */ Stats::Scalar<> iewDispNonSpecInsts; + /** Stat for number of times the IQ becomes full. */ Stats::Scalar<> iewIQFullEvents; - Stats::Scalar<> iewExecutedInsts; - Stats::Scalar<> iewExecLoadInsts; - Stats::Scalar<> iewExecStoreInsts; - Stats::Scalar<> iewExecSquashedInsts; + /** Stat for number of times the LSQ becomes full. */ + Stats::Scalar<> iewLSQFullEvents; + /** Stat for total number of memory ordering violation events. */ Stats::Scalar<> memOrderViolationEvents; + /** Stat for total number of incorrect predicted taken branches. */ Stats::Scalar<> predictedTakenIncorrect; + /** Stat for total number of incorrect predicted not taken branches. */ + Stats::Scalar<> predictedNotTakenIncorrect; + /** Stat for total number of mispredicted branches detected at execute. */ + Stats::Formula branchMispredicts; + + /** Stat for total number of executed instructions. */ + Stats::Scalar<> iewExecutedInsts; + /** Stat for total number of executed load instructions. */ + Stats::Vector<> iewExecLoadInsts; + /** Stat for total number of squashed instructions skipped at execute. */ + Stats::Scalar<> iewExecSquashedInsts; + /** Number of executed software prefetches. */ + Stats::Vector<> iewExecutedSwp; + /** Number of executed nops. */ + Stats::Vector<> iewExecutedNop; + /** Number of executed meomory references. */ + Stats::Vector<> iewExecutedRefs; + /** Number of executed branches. */ + Stats::Vector<> iewExecutedBranches; + /** Number of executed store instructions. */ + Stats::Formula iewExecStoreInsts; + /** Number of instructions executed per cycle. */ + Stats::Formula iewExecRate; + + /** Number of instructions sent to commit. */ + Stats::Vector<> iewInstsToCommit; + /** Number of instructions that writeback. */ + Stats::Vector<> writebackCount; + /** Number of instructions that wake consumers. */ + Stats::Vector<> producerInst; + /** Number of instructions that wake up from producers. */ + Stats::Vector<> consumerInst; + /** Number of instructions that were delayed in writing back due + * to resource contention. + */ + Stats::Vector<> wbPenalized; + /** Number of instructions per cycle written back. */ + Stats::Formula wbRate; + /** Average number of woken instructions per writeback. */ + Stats::Formula wbFanout; + /** Number of instructions per cycle delayed in writing back . */ + Stats::Formula wbPenalizedRate; }; -#endif // __CPU_O3_CPU_IEW_HH__ +#endif // __CPU_O3_IEW_HH__ diff --git a/src/cpu/o3/iew_impl.hh b/src/cpu/o3/iew_impl.hh index 85217dd10..8e6fd46a1 100644 --- a/src/cpu/o3/iew_impl.hh +++ b/src/cpu/o3/iew_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,64 +24,40 @@ * 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. + * + * Authors: Kevin Lim */ // @todo: Fix the instantaneous communication among all the stages within // iew. There's a clear delay between issue and execute, yet backwards // communication happens simultaneously. -// Update the statuses for each stage. #include <queue> #include "base/timebuf.hh" +#include "cpu/o3/fu_pool.hh" #include "cpu/o3/iew.hh" -template<class Impl> -SimpleIEW<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, - SimpleIEW<Impl> *_iew) - : Event(&mainEventQueue, CPU_Tick_Pri), inst(_inst), iewStage(_iew) -{ - this->setFlags(Event::AutoDelete); -} - -template<class Impl> -void -SimpleIEW<Impl>::WritebackEvent::process() -{ - DPRINTF(IEW, "IEW: WRITEBACK EVENT!!!!\n"); - - // Need to insert instruction into queue to commit - iewStage->instToCommit(inst); - // Need to execute second half of the instruction, do actual writing to - // registers and such - inst->execute(); -} - -template<class Impl> -const char * -SimpleIEW<Impl>::WritebackEvent::description() -{ - return "LSQ writeback event"; -} +using namespace std; template<class Impl> -SimpleIEW<Impl>::SimpleIEW(Params ¶ms) - : // Just make this time buffer really big for now +DefaultIEW<Impl>::DefaultIEW(Params *params) + : // @todo: Make this into a parameter. issueToExecQueue(5, 5), instQueue(params), ldstQueue(params), - commitToIEWDelay(params.commitToIEWDelay), - renameToIEWDelay(params.renameToIEWDelay), - issueToExecuteDelay(params.issueToExecuteDelay), - issueReadWidth(params.issueWidth), - issueWidth(params.issueWidth), - executeWidth(params.executeWidth) -{ - DPRINTF(IEW, "IEW: executeIntWidth: %i.\n", params.executeIntWidth); - _status = Idle; - _issueStatus = Idle; - _exeStatus = Idle; - _wbStatus = Idle; + fuPool(params->fuPool), + commitToIEWDelay(params->commitToIEWDelay), + renameToIEWDelay(params->renameToIEWDelay), + issueToExecuteDelay(params->issueToExecuteDelay), + issueReadWidth(params->issueWidth), + issueWidth(params->issueWidth), + numThreads(params->numberOfThreads), + switchedOut(false) +{ + _status = Active; + exeStatus = Running; + wbStatus = Idle; // Setup wire to read instructions coming from issue. fromIssue = issueToExecQueue.getWire(-issueToExecuteDelay); @@ -89,14 +65,35 @@ SimpleIEW<Impl>::SimpleIEW(Params ¶ms) // Instruction queue needs the queue between issue and execute. instQueue.setIssueToExecuteQueue(&issueToExecQueue); + instQueue.setIEW(this); ldstQueue.setIEW(this); + + for (int i=0; i < numThreads; i++) { + dispatchStatus[i] = Running; + stalls[i].commit = false; + fetchRedirect[i] = false; + } + + updateLSQNextCycle = false; + + skidBufferMax = (3 * (renameToIEWDelay * params->renameWidth)) + issueWidth; +} + +template <class Impl> +std::string +DefaultIEW<Impl>::name() const +{ + return cpu->name() + ".iew"; } template <class Impl> void -SimpleIEW<Impl>::regStats() +DefaultIEW<Impl>::regStats() { + using namespace Stats; + instQueue.regStats(); + ldstQueue.regStats(); iewIdleCycles .name(name() + ".iewIdleCycles") @@ -114,8 +111,6 @@ SimpleIEW<Impl>::regStats() .name(name() + ".iewUnblockCycles") .desc("Number of cycles IEW is unblocking"); -// iewWBInsts; - iewDispatchedInsts .name(name() + ".iewDispatchedInsts") .desc("Number of instructions dispatched to IQ"); @@ -140,47 +135,163 @@ SimpleIEW<Impl>::regStats() .name(name() + ".iewIQFullEvents") .desc("Number of times the IQ has become full, causing a stall"); + iewLSQFullEvents + .name(name() + ".iewLSQFullEvents") + .desc("Number of times the LSQ has become full, causing a stall"); + + memOrderViolationEvents + .name(name() + ".memOrderViolationEvents") + .desc("Number of memory order violations"); + + predictedTakenIncorrect + .name(name() + ".predictedTakenIncorrect") + .desc("Number of branches that were predicted taken incorrectly"); + + predictedNotTakenIncorrect + .name(name() + ".predictedNotTakenIncorrect") + .desc("Number of branches that were predicted not taken incorrectly"); + + branchMispredicts + .name(name() + ".branchMispredicts") + .desc("Number of branch mispredicts detected at execute"); + + branchMispredicts = predictedTakenIncorrect + predictedNotTakenIncorrect; + iewExecutedInsts - .name(name() + ".iewExecutedInsts") + .name(name() + ".EXEC:insts") .desc("Number of executed instructions"); iewExecLoadInsts - .name(name() + ".iewExecLoadInsts") - .desc("Number of load instructions executed"); - - iewExecStoreInsts - .name(name() + ".iewExecStoreInsts") - .desc("Number of store instructions executed"); + .init(cpu->number_of_threads) + .name(name() + ".EXEC:loads") + .desc("Number of load instructions executed") + .flags(total); iewExecSquashedInsts - .name(name() + ".iewExecSquashedInsts") + .name(name() + ".EXEC:squashedInsts") .desc("Number of squashed instructions skipped in execute"); - memOrderViolationEvents - .name(name() + ".memOrderViolationEvents") - .desc("Number of memory order violations"); + iewExecutedSwp + .init(cpu->number_of_threads) + .name(name() + ".EXEC:swp") + .desc("number of swp insts executed") + .flags(total); + + iewExecutedNop + .init(cpu->number_of_threads) + .name(name() + ".EXEC:nop") + .desc("number of nop insts executed") + .flags(total); + + iewExecutedRefs + .init(cpu->number_of_threads) + .name(name() + ".EXEC:refs") + .desc("number of memory reference insts executed") + .flags(total); + + iewExecutedBranches + .init(cpu->number_of_threads) + .name(name() + ".EXEC:branches") + .desc("Number of branches executed") + .flags(total); - predictedTakenIncorrect - .name(name() + ".predictedTakenIncorrect") - .desc("Number of branches that were predicted taken incorrectly"); + iewExecStoreInsts + .name(name() + ".EXEC:stores") + .desc("Number of stores executed") + .flags(total); + iewExecStoreInsts = iewExecutedRefs - iewExecLoadInsts; + + iewExecRate + .name(name() + ".EXEC:rate") + .desc("Inst execution rate") + .flags(total); + + iewExecRate = iewExecutedInsts / cpu->numCycles; + + iewInstsToCommit + .init(cpu->number_of_threads) + .name(name() + ".WB:sent") + .desc("cumulative count of insts sent to commit") + .flags(total); + + writebackCount + .init(cpu->number_of_threads) + .name(name() + ".WB:count") + .desc("cumulative count of insts written-back") + .flags(total); + + producerInst + .init(cpu->number_of_threads) + .name(name() + ".WB:producers") + .desc("num instructions producing a value") + .flags(total); + + consumerInst + .init(cpu->number_of_threads) + .name(name() + ".WB:consumers") + .desc("num instructions consuming a value") + .flags(total); + + wbPenalized + .init(cpu->number_of_threads) + .name(name() + ".WB:penalized") + .desc("number of instrctions required to write to 'other' IQ") + .flags(total); + + wbPenalizedRate + .name(name() + ".WB:penalized_rate") + .desc ("fraction of instructions written-back that wrote to 'other' IQ") + .flags(total); + + wbPenalizedRate = wbPenalized / writebackCount; + + wbFanout + .name(name() + ".WB:fanout") + .desc("average fanout of values written-back") + .flags(total); + + wbFanout = producerInst / consumerInst; + + wbRate + .name(name() + ".WB:rate") + .desc("insts written-back per cycle") + .flags(total); + wbRate = writebackCount / cpu->numCycles; } template<class Impl> void -SimpleIEW<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultIEW<Impl>::initStage() { - DPRINTF(IEW, "IEW: Setting CPU pointer.\n"); + for (int tid=0; tid < numThreads; tid++) { + toRename->iewInfo[tid].usedIQ = true; + toRename->iewInfo[tid].freeIQEntries = + instQueue.numFreeEntries(tid); + + toRename->iewInfo[tid].usedLSQ = true; + toRename->iewInfo[tid].freeLSQEntries = + ldstQueue.numFreeEntries(tid); + } +} + +template<class Impl> +void +DefaultIEW<Impl>::setCPU(O3CPU *cpu_ptr) +{ + DPRINTF(IEW, "Setting CPU pointer.\n"); cpu = cpu_ptr; instQueue.setCPU(cpu_ptr); ldstQueue.setCPU(cpu_ptr); + + cpu->activateStage(O3CPU::IEWIdx); } template<class Impl> void -SimpleIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +DefaultIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) { - DPRINTF(IEW, "IEW: Setting time buffer pointer.\n"); + DPRINTF(IEW, "Setting time buffer pointer.\n"); timeBuffer = tb_ptr; // Setup wire to read information from time buffer, from commit. @@ -189,15 +300,17 @@ SimpleIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) // Setup wire to write information back to previous stages. toRename = timeBuffer->getWire(0); + toFetch = timeBuffer->getWire(0); + // Instruction queue also needs main time buffer. instQueue.setTimeBuffer(tb_ptr); } template<class Impl> void -SimpleIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) +DefaultIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) { - DPRINTF(IEW, "IEW: Setting rename queue pointer.\n"); + DPRINTF(IEW, "Setting rename queue pointer.\n"); renameQueue = rq_ptr; // Setup wire to read information from rename queue. @@ -206,9 +319,9 @@ SimpleIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) template<class Impl> void -SimpleIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) +DefaultIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) { - DPRINTF(IEW, "IEW: Setting IEW queue pointer.\n"); + DPRINTF(IEW, "Setting IEW queue pointer.\n"); iewQueue = iq_ptr; // Setup wire to write instructions to commit. @@ -217,355 +330,947 @@ SimpleIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) template<class Impl> void -SimpleIEW<Impl>::setRenameMap(RenameMap *rm_ptr) +DefaultIEW<Impl>::setActiveThreads(list<unsigned> *at_ptr) { - DPRINTF(IEW, "IEW: Setting rename map pointer.\n"); - renameMap = rm_ptr; + DPRINTF(IEW, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; + + ldstQueue.setActiveThreads(at_ptr); + instQueue.setActiveThreads(at_ptr); } template<class Impl> void -SimpleIEW<Impl>::squash() +DefaultIEW<Impl>::setScoreboard(Scoreboard *sb_ptr) +{ + DPRINTF(IEW, "Setting scoreboard pointer.\n"); + scoreboard = sb_ptr; +} + +template <class Impl> +void +DefaultIEW<Impl>::switchOut() +{ + // IEW is ready to switch out at any time. + cpu->signalSwitched(); +} + +template <class Impl> +void +DefaultIEW<Impl>::doSwitchOut() { - DPRINTF(IEW, "IEW: Squashing all instructions.\n"); - _status = Squashing; + // Clear any state. + switchedOut = true; + + instQueue.switchOut(); + ldstQueue.switchOut(); + fuPool->switchOut(); + + for (int i = 0; i < numThreads; i++) { + while (!insts[i].empty()) + insts[i].pop(); + while (!skidBuffer[i].empty()) + skidBuffer[i].pop(); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::takeOverFrom() +{ + // Reset all state. + _status = Active; + exeStatus = Running; + wbStatus = Idle; + switchedOut = false; + + instQueue.takeOverFrom(); + ldstQueue.takeOverFrom(); + fuPool->takeOverFrom(); + + initStage(); + cpu->activityThisCycle(); + + for (int i=0; i < numThreads; i++) { + dispatchStatus[i] = Running; + stalls[i].commit = false; + fetchRedirect[i] = false; + } + + updateLSQNextCycle = false; + + // @todo: Fix hardcoded number + for (int i = 0; i < 6; ++i) { + issueToExecQueue.advance(); + } +} + +template<class Impl> +void +DefaultIEW<Impl>::squash(unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing all instructions.\n", + tid); // Tell the IQ to start squashing. - instQueue.squash(); + instQueue.squash(tid); // Tell the LDSTQ to start squashing. - ldstQueue.squash(fromCommit->commitInfo.doneSeqNum); + ldstQueue.squash(fromCommit->commitInfo[tid].doneSeqNum, tid); + + updatedQueues = true; + + // Clear the skid buffer in case it has any data in it. + while (!skidBuffer[tid].empty()) { + + if (skidBuffer[tid].front()->isLoad() || + skidBuffer[tid].front()->isStore() ) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + + toRename->iewInfo[tid].dispatched++; + + skidBuffer[tid].pop(); + } + + emptyRenameInsts(tid); } template<class Impl> void -SimpleIEW<Impl>::squashDueToBranch(DynInstPtr &inst) -{ - DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n", - inst->PC); - // Perhaps leave the squashing up to the ROB stage to tell it when to - // squash? - _status = Squashing; - - // Tell rename to squash through the time buffer. - toCommit->squash = true; - // Also send PC update information back to prior stages. - toCommit->squashedSeqNum = inst->seqNum; - toCommit->mispredPC = inst->readPC(); - toCommit->nextPC = inst->readNextPC(); - toCommit->branchMispredict = true; - // Prediction was incorrect, so send back inverse. - toCommit->branchTaken = inst->readNextPC() != +DefaultIEW<Impl>::squashDueToBranch(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, PC: %#x " + "[sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->mispredPC[tid] = inst->readPC(); + toCommit->nextPC[tid] = inst->readNextPC(); + toCommit->branchMispredict[tid] = true; + toCommit->branchTaken[tid] = inst->readNextPC() != (inst->readPC() + sizeof(TheISA::MachInst)); + + toCommit->includeSquashInst[tid] = false; + + wroteToTimeBuffer = true; +} + +template<class Impl> +void +DefaultIEW<Impl>::squashDueToMemOrder(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, " + "PC: %#x [sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->nextPC[tid] = inst->readNextPC(); + + toCommit->includeSquashInst[tid] = false; + + wroteToTimeBuffer = true; } template<class Impl> void -SimpleIEW<Impl>::squashDueToMem(DynInstPtr &inst) +DefaultIEW<Impl>::squashDueToMemBlocked(DynInstPtr &inst, unsigned tid) { - DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n", - inst->PC); - // Perhaps leave the squashing up to the ROB stage to tell it when to - // squash? - _status = Squashing; + DPRINTF(IEW, "[tid:%i]: Memory blocked, squashing load and younger insts, " + "PC: %#x [sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->nextPC[tid] = inst->readPC(); + + // Must include the broadcasted SN in the squash. + toCommit->includeSquashInst[tid] = true; - // Tell rename to squash through the time buffer. - toCommit->squash = true; - // Also send PC update information back to prior stages. - toCommit->squashedSeqNum = inst->seqNum; - toCommit->nextPC = inst->readNextPC(); + ldstQueue.setLoadBlockedHandled(tid); + + wroteToTimeBuffer = true; } template<class Impl> void -SimpleIEW<Impl>::block() +DefaultIEW<Impl>::block(unsigned tid) { - DPRINTF(IEW, "IEW: Blocking.\n"); - // Set the status to Blocked. - _status = Blocked; + DPRINTF(IEW, "[tid:%u]: Blocking.\n", tid); + + if (dispatchStatus[tid] != Blocked && + dispatchStatus[tid] != Unblocking) { + toRename->iewBlock[tid] = true; + wroteToTimeBuffer = true; + } // Add the current inputs to the skid buffer so they can be // reprocessed when this stage unblocks. - skidBuffer.push(*fromRename); + skidInsert(tid); - // Note that this stage only signals previous stages to stall when - // it is the cause of the stall originates at this stage. Otherwise - // the previous stages are expected to check all possible stall signals. + dispatchStatus[tid] = Blocked; } template<class Impl> -inline void -SimpleIEW<Impl>::unblock() +void +DefaultIEW<Impl>::unblock(unsigned tid) { - // Check if there's information in the skid buffer. If there is, then - // set status to unblocking, otherwise set it directly to running. - DPRINTF(IEW, "IEW: Reading instructions out of the skid " - "buffer.\n"); - // Remove the now processed instructions from the skid buffer. - skidBuffer.pop(); - - // If there's still information in the skid buffer, then - // continue to tell previous stages to stall. They will be - // able to restart once the skid buffer is empty. - if (!skidBuffer.empty()) { - toRename->iewInfo.stall = true; - } else { - DPRINTF(IEW, "IEW: Stage is done unblocking.\n"); - _status = Running; + DPRINTF(IEW, "[tid:%i]: Reading instructions out of the skid " + "buffer %u.\n",tid, tid); + + // If the skid bufffer is empty, signal back to previous stages to unblock. + // Also switch status to running. + if (skidBuffer[tid].empty()) { + toRename->iewUnblock[tid] = true; + wroteToTimeBuffer = true; + DPRINTF(IEW, "[tid:%i]: Done unblocking.\n",tid); + dispatchStatus[tid] = Running; } } template<class Impl> void -SimpleIEW<Impl>::wakeDependents(DynInstPtr &inst) +DefaultIEW<Impl>::wakeDependents(DynInstPtr &inst) { instQueue.wakeDependents(inst); } +template<class Impl> +void +DefaultIEW<Impl>::rescheduleMemInst(DynInstPtr &inst) +{ + instQueue.rescheduleMemInst(inst); +} + +template<class Impl> +void +DefaultIEW<Impl>::replayMemInst(DynInstPtr &inst) +{ + instQueue.replayMemInst(inst); +} template<class Impl> void -SimpleIEW<Impl>::instToCommit(DynInstPtr &inst) +DefaultIEW<Impl>::instToCommit(DynInstPtr &inst) { + // First check the time slot that this instruction will write + // to. If there are free write ports at the time, then go ahead + // and write the instruction to that time. If there are not, + // keep looking back to see where's the first time there's a + // free slot. + while ((*iewQueue)[wbCycle].insts[wbNumInst]) { + ++wbNumInst; + if (wbNumInst == issueWidth) { + ++wbCycle; + wbNumInst = 0; + } + assert(wbCycle < 5); + } + + // Add finished instruction to queue to commit. + (*iewQueue)[wbCycle].insts[wbNumInst] = inst; + (*iewQueue)[wbCycle].size++; } template <class Impl> +unsigned +DefaultIEW<Impl>::validInstsFromRename() +{ + unsigned inst_count = 0; + + for (int i=0; i<fromRename->size; i++) { + if (!fromRename->insts[i]->isSquashed()) + inst_count++; + } + + return inst_count; +} + +template<class Impl> void -SimpleIEW<Impl>::dispatchInsts() -{ - //////////////////////////////////////// - // DISPATCH/ISSUE stage - //////////////////////////////////////// - - //Put into its own function? - //Add instructions to IQ if there are any instructions there - - // Check if there are any instructions coming from rename, and we're. - // not squashing. - if (fromRename->size > 0) { - int insts_to_add = fromRename->size; - - // Loop through the instructions, putting them in the instruction - // queue. - for (int inst_num = 0; inst_num < insts_to_add; ++inst_num) - { - DynInstPtr inst = fromRename->insts[inst_num]; - - // Make sure there's a valid instruction there. - assert(inst); - - DPRINTF(IEW, "IEW: Issue: Adding PC %#x to IQ.\n", - inst->readPC()); - - // Be sure to mark these instructions as ready so that the - // commit stage can go ahead and execute them, and mark - // them as issued so the IQ doesn't reprocess them. - if (inst->isSquashed()) { - ++iewDispSquashedInsts; - continue; - } else if (instQueue.isFull()) { - DPRINTF(IEW, "IEW: Issue: IQ has become full.\n"); - // Call function to start blocking. - block(); - // Tell previous stage to stall. - toRename->iewInfo.stall = true; - - ++iewIQFullEvents; - break; - } else if (inst->isLoad()) { - DPRINTF(IEW, "IEW: Issue: Memory instruction " - "encountered, adding to LDSTQ.\n"); - - // Reserve a spot in the load store queue for this - // memory access. - ldstQueue.insertLoad(inst); - - ++iewDispLoadInsts; - } else if (inst->isStore()) { - ldstQueue.insertStore(inst); +DefaultIEW<Impl>::skidInsert(unsigned tid) +{ + DynInstPtr inst = NULL; - ++iewDispStoreInsts; - } else if (inst->isNonSpeculative()) { - DPRINTF(IEW, "IEW: Issue: Nonspeculative instruction " - "encountered, skipping.\n"); + while (!insts[tid].empty()) { + inst = insts[tid].front(); - // Same hack as with stores. - inst->setCanCommit(); + insts[tid].pop(); + + DPRINTF(Decode,"[tid:%i]: Inserting [sn:%lli] PC:%#x into " + "dispatch skidBuffer %i\n",tid, inst->seqNum, + inst->readPC(),tid); + + skidBuffer[tid].push(inst); + } + + assert(skidBuffer[tid].size() <= skidBufferMax && + "Skidbuffer Exceeded Max Size"); +} + +template<class Impl> +int +DefaultIEW<Impl>::skidCount() +{ + int max=0; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned thread_count = skidBuffer[*threads++].size(); + if (max < thread_count) + max = thread_count; + } + + return max; +} + +template<class Impl> +bool +DefaultIEW<Impl>::skidsEmpty() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; +} + +template <class Impl> +void +DefaultIEW<Impl>::updateStatus() +{ + bool any_unblocking = false; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (dispatchStatus[tid] == Unblocking) { + any_unblocking = true; + break; + } + } + + // If there are no ready instructions waiting to be scheduled by the IQ, + // and there's no stores waiting to write back, and dispatch is not + // unblocking, then there is no internal activity for the IEW stage. + if (_status == Active && !instQueue.hasReadyInsts() && + !ldstQueue.willWB() && !any_unblocking) { + DPRINTF(IEW, "IEW switching to idle\n"); + + deactivateStage(); + + _status = Inactive; + } else if (_status == Inactive && (instQueue.hasReadyInsts() || + ldstQueue.willWB() || + any_unblocking)) { + // Otherwise there is internal activity. Set to active. + DPRINTF(IEW, "IEW switching to active\n"); + + activateStage(); + + _status = Active; + } +} + +template <class Impl> +void +DefaultIEW<Impl>::resetEntries() +{ + instQueue.resetEntries(); + ldstQueue.resetEntries(); +} + +template <class Impl> +void +DefaultIEW<Impl>::readStallSignals(unsigned tid) +{ + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; + } +} + +template <class Impl> +bool +DefaultIEW<Impl>::checkStall(unsigned tid) +{ + bool ret_val(false); + + if (stalls[tid].commit) { + DPRINTF(IEW,"[tid:%i]: Stall from Commit stage detected.\n",tid); + ret_val = true; + } else if (instQueue.isFull(tid)) { + DPRINTF(IEW,"[tid:%i]: Stall: IQ is full.\n",tid); + ret_val = true; + } else if (ldstQueue.isFull(tid)) { + DPRINTF(IEW,"[tid:%i]: Stall: LSQ is full\n",tid); + + if (ldstQueue.numLoads(tid) > 0 ) { + + DPRINTF(IEW,"[tid:%i]: LSQ oldest load: [sn:%i] \n", + tid,ldstQueue.getLoadHeadSeqNum(tid)); + } + + if (ldstQueue.numStores(tid) > 0) { + + DPRINTF(IEW,"[tid:%i]: LSQ oldest store: [sn:%i] \n", + tid,ldstQueue.getStoreHeadSeqNum(tid)); + } + + ret_val = true; + } else if (ldstQueue.isStalled(tid)) { + DPRINTF(IEW,"[tid:%i]: Stall: LSQ stall detected.\n",tid); + ret_val = true; + } + + return ret_val; +} + +template <class Impl> +void +DefaultIEW<Impl>::checkSignalsAndUpdate(unsigned tid) +{ + // Check if there's a squash signal, squash if there is + // Check stall signals, block if there is. + // If status was Blocked + // if so then go to unblocking + // If status was Squashing + // check if squashing is not high. Switch to running this cycle. + + readStallSignals(tid); + + if (fromCommit->commitInfo[tid].squash) { + squash(tid); + + if (dispatchStatus[tid] == Blocked || + dispatchStatus[tid] == Unblocking) { + toRename->iewUnblock[tid] = true; + wroteToTimeBuffer = true; + } + + dispatchStatus[tid] = Squashing; + + fetchRedirect[tid] = false; + return; + } + + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(IEW, "[tid:%i]: ROB is still squashing.\n", tid); + + dispatchStatus[tid] = Squashing; + + emptyRenameInsts(tid); + wroteToTimeBuffer = true; + return; + } + + if (checkStall(tid)) { + block(tid); + dispatchStatus[tid] = Blocked; + return; + } + + if (dispatchStatus[tid] == Blocked) { + // Status from previous cycle was blocked, but there are no more stall + // conditions. Switch over to unblocking. + DPRINTF(IEW, "[tid:%i]: Done blocking, switching to unblocking.\n", + tid); + + dispatchStatus[tid] = Unblocking; + + unblock(tid); - // Specificall insert it as nonspeculative. + return; + } + + if (dispatchStatus[tid] == Squashing) { + // Switch status to running if rename isn't being told to block or + // squash this cycle. + DPRINTF(IEW, "[tid:%i]: Done squashing, switching to running.\n", + tid); + + dispatchStatus[tid] = Running; + + return; + } +} + +template <class Impl> +void +DefaultIEW<Impl>::sortInsts() +{ + int insts_from_rename = fromRename->size; +#ifdef DEBUG + for (int i = 0; i < numThreads; i++) + assert(insts[i].empty()); +#endif + for (int i = 0; i < insts_from_rename; ++i) { + insts[fromRename->insts[i]->threadNumber].push(fromRename->insts[i]); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::emptyRenameInsts(unsigned tid) +{ + while (!insts[tid].empty()) { + if (insts[tid].front()->isLoad() || + insts[tid].front()->isStore() ) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + + toRename->iewInfo[tid].dispatched++; + + insts[tid].pop(); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::wakeCPU() +{ + cpu->wakeCPU(); +} + +template <class Impl> +void +DefaultIEW<Impl>::activityThisCycle() +{ + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); +} + +template <class Impl> +inline void +DefaultIEW<Impl>::activateStage() +{ + DPRINTF(Activity, "Activating stage.\n"); + cpu->activateStage(O3CPU::IEWIdx); +} + +template <class Impl> +inline void +DefaultIEW<Impl>::deactivateStage() +{ + DPRINTF(Activity, "Deactivating stage.\n"); + cpu->deactivateStage(O3CPU::IEWIdx); +} + +template<class Impl> +void +DefaultIEW<Impl>::dispatch(unsigned tid) +{ + // If status is Running or idle, + // call dispatchInsts() + // If status is Unblocking, + // buffer any instructions coming from rename + // continue trying to empty skid buffer + // check if stall conditions have passed + + if (dispatchStatus[tid] == Blocked) { + ++iewBlockCycles; + + } else if (dispatchStatus[tid] == Squashing) { + ++iewSquashCycles; + } + + // Dispatch should try to dispatch as many instructions as its bandwidth + // will allow, as long as it is not currently blocked. + if (dispatchStatus[tid] == Running || + dispatchStatus[tid] == Idle) { + DPRINTF(IEW, "[tid:%i] Not blocked, so attempting to run " + "dispatch.\n", tid); + + dispatchInsts(tid); + } else if (dispatchStatus[tid] == Unblocking) { + // Make sure that the skid buffer has something in it if the + // status is unblocking. + assert(!skidsEmpty()); + + // If the status was unblocking, then instructions from the skid + // buffer were used. Remove those instructions and handle + // the rest of unblocking. + dispatchInsts(tid); + + ++iewUnblockCycles; + + if (validInstsFromRename() && dispatchedAllInsts) { + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + } + + unblock(tid); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::dispatchInsts(unsigned tid) +{ + dispatchedAllInsts = true; + + // Obtain instructions from skid buffer if unblocking, or queue from rename + // otherwise. + std::queue<DynInstPtr> &insts_to_dispatch = + dispatchStatus[tid] == Unblocking ? + skidBuffer[tid] : insts[tid]; + + int insts_to_add = insts_to_dispatch.size(); + + DynInstPtr inst; + bool add_to_iq = false; + int dis_num_inst = 0; + + // Loop through the instructions, putting them in the instruction + // queue. + for ( ; dis_num_inst < insts_to_add && + dis_num_inst < issueReadWidth; + ++dis_num_inst) + { + inst = insts_to_dispatch.front(); + + if (dispatchStatus[tid] == Unblocking) { + DPRINTF(IEW, "[tid:%i]: Issue: Examining instruction from skid " + "buffer\n", tid); + } + + // Make sure there's a valid instruction there. + assert(inst); + + DPRINTF(IEW, "[tid:%i]: Issue: Adding PC %#x [sn:%lli] [tid:%i] to " + "IQ.\n", + tid, inst->readPC(), inst->seqNum, inst->threadNumber); + + // Be sure to mark these instructions as ready so that the + // commit stage can go ahead and execute them, and mark + // them as issued so the IQ doesn't reprocess them. + + // Check for squashed instructions. + if (inst->isSquashed()) { + DPRINTF(IEW, "[tid:%i]: Issue: Squashed instruction encountered, " + "not adding to IQ.\n", tid); + + ++iewDispSquashedInsts; + + insts_to_dispatch.pop(); + + //Tell Rename That An Instruction has been processed + if (inst->isLoad() || inst->isStore()) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + toRename->iewInfo[tid].dispatched++; + + continue; + } + + // Check for full conditions. + if (instQueue.isFull(tid)) { + DPRINTF(IEW, "[tid:%i]: Issue: IQ has become full.\n", tid); + + // Call function to start blocking. + block(tid); + + // Set unblock to false. Special case where we are using + // skidbuffer (unblocking) instructions but then we still + // get full in the IQ. + toRename->iewUnblock[tid] = false; + + dispatchedAllInsts = false; + + ++iewIQFullEvents; + break; + } else if (ldstQueue.isFull(tid)) { + DPRINTF(IEW, "[tid:%i]: Issue: LSQ has become full.\n",tid); + + // Call function to start blocking. + block(tid); + + // Set unblock to false. Special case where we are using + // skidbuffer (unblocking) instructions but then we still + // get full in the IQ. + toRename->iewUnblock[tid] = false; + + dispatchedAllInsts = false; + + ++iewLSQFullEvents; + break; + } + + // Otherwise issue the instruction just fine. + if (inst->isLoad()) { + DPRINTF(IEW, "[tid:%i]: Issue: Memory instruction " + "encountered, adding to LSQ.\n", tid); + + // Reserve a spot in the load store queue for this + // memory access. + ldstQueue.insertLoad(inst); + + ++iewDispLoadInsts; + + add_to_iq = true; + + toRename->iewInfo[tid].dispatchedToLSQ++; + } else if (inst->isStore()) { + DPRINTF(IEW, "[tid:%i]: Issue: Memory instruction " + "encountered, adding to LSQ.\n", tid); + + ldstQueue.insertStore(inst); + + ++iewDispStoreInsts; + + if (inst->isStoreConditional()) { + // Store conditionals need to be set as "canCommit()" + // so that commit can process them when they reach the + // head of commit. + // @todo: This is somewhat specific to Alpha. + inst->setCanCommit(); instQueue.insertNonSpec(inst); + add_to_iq = false; ++iewDispNonSpecInsts; + } else { + add_to_iq = true; + } - continue; - } else if (inst->isNop()) { - DPRINTF(IEW, "IEW: Issue: Nop instruction encountered " - ", skipping.\n"); + toRename->iewInfo[tid].dispatchedToLSQ++; +#if FULL_SYSTEM + } else if (inst->isMemBarrier() || inst->isWriteBarrier()) { + // Same as non-speculative stores. + inst->setCanCommit(); + instQueue.insertBarrier(inst); + add_to_iq = false; +#endif + } else if (inst->isNonSpeculative()) { + DPRINTF(IEW, "[tid:%i]: Issue: Nonspeculative instruction " + "encountered, skipping.\n", tid); - inst->setIssued(); - inst->setExecuted(); - inst->setCanCommit(); + // Same as non-speculative stores. + inst->setCanCommit(); - instQueue.advanceTail(inst); + // Specifically insert it as nonspeculative. + instQueue.insertNonSpec(inst); - continue; - } else if (inst->isExecuted()) { - assert(0 && "Instruction shouldn't be executed.\n"); - DPRINTF(IEW, "IEW: Issue: Executed branch encountered, " - "skipping.\n"); + ++iewDispNonSpecInsts; - inst->setIssued(); - inst->setCanCommit(); + add_to_iq = false; + } else if (inst->isNop()) { + DPRINTF(IEW, "[tid:%i]: Issue: Nop instruction encountered, " + "skipping.\n", tid); - instQueue.advanceTail(inst); + inst->setIssued(); + inst->setExecuted(); + inst->setCanCommit(); - continue; - } + instQueue.recordProducer(inst); - // If the instruction queue is not full, then add the - // instruction. - instQueue.insert(fromRename->insts[inst_num]); + iewExecutedNop[tid]++; - ++iewDispatchedInsts; + add_to_iq = false; + } else if (inst->isExecuted()) { + assert(0 && "Instruction shouldn't be executed.\n"); + DPRINTF(IEW, "Issue: Executed branch encountered, " + "skipping.\n"); + + inst->setIssued(); + inst->setCanCommit(); + + instQueue.recordProducer(inst); + + add_to_iq = false; + } else { + add_to_iq = true; } + + // If the instruction queue is not full, then add the + // instruction. + if (add_to_iq) { + instQueue.insert(inst); + } + + insts_to_dispatch.pop(); + + toRename->iewInfo[tid].dispatched++; + + ++iewDispatchedInsts; + } + + if (!insts_to_dispatch.empty()) { + DPRINTF(IEW,"[tid:%i]: Issue: Bandwidth Full. Blocking.\n"); + block(tid); + toRename->iewUnblock[tid] = false; + } + + if (dispatchStatus[tid] == Idle && dis_num_inst) { + dispatchStatus[tid] = Running; + + updatedQueues = true; } + + dis_num_inst = 0; } template <class Impl> void -SimpleIEW<Impl>::executeInsts() +DefaultIEW<Impl>::printAvailableInsts() { - //////////////////////////////////////// - //EXECUTE/WRITEBACK stage - //////////////////////////////////////// + int inst = 0; + + cout << "Available Instructions: "; + + while (fromIssue->insts[inst]) { + + if (inst%3==0) cout << "\n\t"; - //Put into its own function? - //Similarly should probably have separate execution for int vs FP. - // Above comment is handled by the issue queue only issuing a valid - // mix of int/fp instructions. - //Actually okay to just have one execution, buuuuuut will need - //somewhere that defines the execution latency of all instructions. - // @todo: Move to the FU pool used in the current full cpu. + cout << "PC: " << fromIssue->insts[inst]->readPC() + << " TN: " << fromIssue->insts[inst]->threadNumber + << " SN: " << fromIssue->insts[inst]->seqNum << " | "; + + inst++; + + } + + cout << "\n"; +} + +template <class Impl> +void +DefaultIEW<Impl>::executeInsts() +{ + wbNumInst = 0; + wbCycle = 0; - int fu_usage = 0; - bool fetch_redirect = false; - int inst_slot = 0; - int time_slot = 0; + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + fetchRedirect[tid] = false; + } + + // Uncomment this if you want to see all available instructions. +// printAvailableInsts(); // Execute/writeback any instructions that are available. - for (int inst_num = 0; - fu_usage < executeWidth && /* Haven't exceeded available FU's. */ - inst_num < issueWidth && - fromIssue->insts[inst_num]; - ++inst_num) { + int insts_to_execute = fromIssue->size; + int inst_num = 0; + for (; inst_num < insts_to_execute; + ++inst_num) { - DPRINTF(IEW, "IEW: Execute: Executing instructions from IQ.\n"); + DPRINTF(IEW, "Execute: Executing instructions from IQ.\n"); - // Get instruction from issue's queue. - DynInstPtr inst = fromIssue->insts[inst_num]; + DynInstPtr inst = instQueue.getInstToExecute(); - DPRINTF(IEW, "IEW: Execute: Processing PC %#x.\n", inst->readPC()); + DPRINTF(IEW, "Execute: Processing PC %#x, [tid:%i] [sn:%i].\n", + inst->readPC(), inst->threadNumber,inst->seqNum); // Check if the instruction is squashed; if so then skip it - // and don't count it towards the FU usage. if (inst->isSquashed()) { - DPRINTF(IEW, "IEW: Execute: Instruction was squashed.\n"); + DPRINTF(IEW, "Execute: Instruction was squashed.\n"); // Consider this instruction executed so that commit can go // ahead and retire the instruction. inst->setExecuted(); - toCommit->insts[inst_num] = inst; + // Not sure if I should set this here or just let commit try to + // commit any squashed instructions. I like the latter a bit more. + inst->setCanCommit(); ++iewExecSquashedInsts; continue; } - inst->setExecuted(); - - // If an instruction is executed, then count it towards FU usage. - ++fu_usage; + Fault fault = NoFault; // Execute instruction. // Note that if the instruction faults, it will be handled // at the commit stage. - if (inst->isMemRef()) { - DPRINTF(IEW, "IEW: Execute: Calculating address for memory " + if (inst->isMemRef() && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(IEW, "Execute: Calculating address for memory " "reference.\n"); // Tell the LDSTQ to execute this instruction (if it is a load). if (inst->isLoad()) { - ldstQueue.executeLoad(inst); - - ++iewExecLoadInsts; + // Loads will mark themselves as executed, and their writeback + // event adds the instruction to the queue to commit + fault = ldstQueue.executeLoad(inst); } else if (inst->isStore()) { ldstQueue.executeStore(inst); - ++iewExecStoreInsts; + // If the store had a fault then it may not have a mem req + if (inst->req && !(inst->req->getFlags() & LOCKED)) { + inst->setExecuted(); + + instToCommit(inst); + } + + // Store conditionals will mark themselves as + // executed, and their writeback event will add the + // instruction to the queue to commit. } else { - panic("IEW: Unexpected memory type!\n"); + panic("Unexpected memory type!\n"); } } else { inst->execute(); - ++iewExecutedInsts; + inst->setExecuted(); + + instToCommit(inst); } - // First check the time slot that this instruction will write - // to. If there are free write ports at the time, then go ahead - // and write the instruction to that time. If there are not, - // keep looking back to see where's the first time there's a - // free slot. What happens if you run out of free spaces? - // For now naively assume that all instructions take one cycle. - // Otherwise would have to look into the time buffer based on the - // latency of the instruction. - (*iewQueue)[time_slot].insts[inst_slot]; - while ((*iewQueue)[time_slot].insts[inst_slot]) { - if (inst_slot < issueWidth) { - ++inst_slot; - } else { - ++time_slot; - inst_slot = 0; - } + updateExeInstStats(inst); - assert(time_slot < 5); - } + // Check if branch prediction was correct, if not then we need + // to tell commit to squash in flight instructions. Only + // handle this if there hasn't already been something that + // redirects fetch in this group of instructions. - // May actually have to work this out, especially with loads and stores + // This probably needs to prioritize the redirects if a different + // scheduler is used. Currently the scheduler schedules the oldest + // instruction first, so the branch resolution order will be correct. + unsigned tid = inst->threadNumber; - // Add finished instruction to queue to commit. - (*iewQueue)[time_slot].insts[inst_slot] = inst; - (*iewQueue)[time_slot].size++; + if (!fetchRedirect[tid]) { - // Check if branch was correct. This check happens after the - // instruction is added to the queue because even if the branch - // is mispredicted, the branch instruction itself is still valid. - // Only handle this if there hasn't already been something that - // redirects fetch in this group of instructions. - if (!fetch_redirect) { if (inst->mispredicted()) { - fetch_redirect = true; + fetchRedirect[tid] = true; - DPRINTF(IEW, "IEW: Execute: Branch mispredict detected.\n"); - DPRINTF(IEW, "IEW: Execute: Redirecting fetch to PC: %#x.\n", + DPRINTF(IEW, "Execute: Branch mispredict detected.\n"); + DPRINTF(IEW, "Execute: Redirecting fetch to PC: %#x.\n", inst->nextPC); // If incorrect, then signal the ROB that it must be squashed. - squashDueToBranch(inst); + squashDueToBranch(inst, tid); if (inst->predTaken()) { predictedTakenIncorrect++; + } else { + predictedNotTakenIncorrect++; } - } else if (ldstQueue.violation()) { - fetch_redirect = true; + } else if (ldstQueue.violation(tid)) { + fetchRedirect[tid] = true; - // Get the DynInst that caused the violation. - DynInstPtr violator = ldstQueue.getMemDepViolator(); + // If there was an ordering violation, then get the + // DynInst that caused the violation. Note that this + // clears the violation signal. + DynInstPtr violator; + violator = ldstQueue.getMemDepViolator(tid); - DPRINTF(IEW, "IEW: LDSTQ detected a violation. Violator PC: " + DPRINTF(IEW, "LDSTQ detected a violation. Violator PC: " "%#x, inst PC: %#x. Addr is: %#x.\n", violator->readPC(), inst->readPC(), inst->physEffAddr); @@ -573,164 +1278,236 @@ SimpleIEW<Impl>::executeInsts() instQueue.violation(inst, violator); // Squash. - squashDueToMem(inst); + squashDueToMemOrder(inst,tid); ++memOrderViolationEvents; + } else if (ldstQueue.loadBlocked(tid) && + !ldstQueue.isLoadBlockedHandled(tid)) { + fetchRedirect[tid] = true; + + DPRINTF(IEW, "Load operation couldn't execute because the " + "memory system is blocked. PC: %#x [sn:%lli]\n", + inst->readPC(), inst->seqNum); + + squashDueToMemBlocked(inst, tid); + } + } + } + + // Update and record activity if we processed any instructions. + if (inst_num) { + if (exeStatus == Idle) { + exeStatus = Running; + } + + updatedQueues = true; + + cpu->activityThisCycle(); + } + + // Need to reset this in case a writeback event needs to write into the + // iew queue. That way the writeback event will write into the correct + // spot in the queue. + wbNumInst = 0; +} + +template <class Impl> +void +DefaultIEW<Impl>::writebackInsts() +{ + // Loop through the head of the time buffer and wake any + // dependents. These instructions are about to write back. Also + // mark scoreboard that this instruction is finally complete. + // Either have IEW have direct access to scoreboard, or have this + // as part of backwards communication. + for (int inst_num = 0; inst_num < issueWidth && + toCommit->insts[inst_num]; inst_num++) { + DynInstPtr inst = toCommit->insts[inst_num]; + int tid = inst->threadNumber; + + DPRINTF(IEW, "Sending instructions to commit, [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + iewInstsToCommit[tid]++; + + // Some instructions will be sent to commit without having + // executed because they need commit to handle them. + // E.g. Uncached loads have not actually executed when they + // are first sent to commit. Instead commit must tell the LSQ + // when it's ready to execute the uncached load. + if (!inst->isSquashed() && inst->isExecuted()) { + int dependents = instQueue.wakeDependents(inst); + + for (int i = 0; i < inst->numDestRegs(); i++) { + //mark as Ready + DPRINTF(IEW,"Setting Destination Register %i\n", + inst->renamedDestRegIdx(i)); + scoreboard->setReg(inst->renamedDestRegIdx(i)); } + + if (dependents) { + producerInst[tid]++; + consumerInst[tid]+= dependents; + } + writebackCount[tid]++; } } } template<class Impl> void -SimpleIEW<Impl>::tick() +DefaultIEW<Impl>::tick() { - // Considering putting all the state-determining stuff in this section. + wbNumInst = 0; + wbCycle = 0; - // Try to fill up issue queue with as many instructions as bandwidth - // allows. - // Decode should try to execute as many instructions as its bandwidth - // will allow, as long as it is not currently blocked. + wroteToTimeBuffer = false; + updatedQueues = false; - // Check if the stage is in a running status. - if (_status != Blocked && _status != Squashing) { - DPRINTF(IEW, "IEW: Status is not blocked, attempting to run " - "stage.\n"); - iew(); + sortInsts(); - // If it's currently unblocking, check to see if it should switch - // to running. - if (_status == Unblocking) { - unblock(); + // Free function units marked as being freed this cycle. + fuPool->processFreeUnits(); - ++iewUnblockCycles; - } - } else if (_status == Squashing) { + list<unsigned>::iterator threads = (*activeThreads).begin(); - DPRINTF(IEW, "IEW: Still squashing.\n"); + // Check stall and squash signals, dispatch any instructions. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; - // Check if stage should remain squashing. Stop squashing if the - // squash signal clears. - if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { - DPRINTF(IEW, "IEW: Done squashing, changing status to " - "running.\n"); + DPRINTF(IEW,"Issue: Processing [tid:%i]\n",tid); - _status = Running; - instQueue.stopSquash(); - } else { - instQueue.doSquash(); - } + checkSignalsAndUpdate(tid); + dispatch(tid); + } - ++iewSquashCycles; - } else if (_status == Blocked) { - // Continue to tell previous stage to stall. - toRename->iewInfo.stall = true; - - // Check if possible stall conditions have cleared. - if (!fromCommit->commitInfo.stall && - !instQueue.isFull()) { - DPRINTF(IEW, "IEW: Stall signals cleared, going to unblock.\n"); - _status = Unblocking; - } + if (exeStatus != Squashing) { + executeInsts(); - // If there's still instructions coming from rename, continue to - // put them on the skid buffer. - if (fromRename->size == 0) { - block(); - } + writebackInsts(); - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - squash(); - } + // Have the instruction queue try to schedule any ready instructions. + // (In actuality, this scheduling is for instructions that will + // be executed next cycle.) + instQueue.scheduleReadyInsts(); - ++iewBlockCycles; + // Also should advance its own time buffers if the stage ran. + // Not the best place for it, but this works (hopefully). + issueToExecQueue.advance(); } - // @todo: Maybe put these at the beginning, so if it's idle it can - // return early. - // Write back number of free IQ entries here. - toRename->iewInfo.freeIQEntries = instQueue.numFreeEntries(); + bool broadcast_free_entries = false; + + if (updatedQueues || exeStatus == Running || updateLSQNextCycle) { + exeStatus = Idle; + updateLSQNextCycle = false; + broadcast_free_entries = true; + } + + // Writeback any stores using any leftover bandwidth. ldstQueue.writebackStores(); // Check the committed load/store signals to see if there's a load // or store to commit. Also check if it's being told to execute a // nonspeculative instruction. // This is pretty inefficient... - if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { - ldstQueue.commitStores(fromCommit->commitInfo.doneSeqNum); - ldstQueue.commitLoads(fromCommit->commitInfo.doneSeqNum); - } - if (fromCommit->commitInfo.nonSpecSeqNum != 0) { - instQueue.scheduleNonSpec(fromCommit->commitInfo.nonSpecSeqNum); - } + threads = (*activeThreads).begin(); + while (threads != (*activeThreads).end()) { + unsigned tid = (*threads++); - DPRINTF(IEW, "IEW: IQ has %i free entries.\n", - instQueue.numFreeEntries()); -} + DPRINTF(IEW,"Processing [tid:%i]\n",tid); -template<class Impl> -void -SimpleIEW<Impl>::iew() -{ - // Might want to put all state checks in the tick() function. - // Check if being told to stall from commit. - if (fromCommit->commitInfo.stall) { - block(); - return; - } else if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - // Also check if commit is telling this stage to squash. - squash(); - return; - } + // Update structures based on instructions committed. + if (fromCommit->commitInfo[tid].doneSeqNum != 0 && + !fromCommit->commitInfo[tid].squash && + !fromCommit->commitInfo[tid].robSquashing) { - dispatchInsts(); + ldstQueue.commitStores(fromCommit->commitInfo[tid].doneSeqNum,tid); - // Have the instruction queue try to schedule any ready instructions. - instQueue.scheduleReadyInsts(); + ldstQueue.commitLoads(fromCommit->commitInfo[tid].doneSeqNum,tid); - executeInsts(); + updateLSQNextCycle = true; + instQueue.commit(fromCommit->commitInfo[tid].doneSeqNum,tid); + } - // Loop through the head of the time buffer and wake any dependents. - // These instructions are about to write back. In the simple model - // this loop can really happen within the previous loop, but when - // instructions have actual latencies, this loop must be separate. - // Also mark scoreboard that this instruction is finally complete. - // Either have IEW have direct access to rename map, or have this as - // part of backwards communication. - for (int inst_num = 0; inst_num < issueWidth && - toCommit->insts[inst_num]; inst_num++) - { - DynInstPtr inst = toCommit->insts[inst_num]; + if (fromCommit->commitInfo[tid].nonSpecSeqNum != 0) { + + //DPRINTF(IEW,"NonspecInst from thread %i",tid); + if (fromCommit->commitInfo[tid].uncached) { + instQueue.replayMemInst(fromCommit->commitInfo[tid].uncachedLoad); + } else { + instQueue.scheduleNonSpec( + fromCommit->commitInfo[tid].nonSpecSeqNum); + } + } - DPRINTF(IEW, "IEW: Sending instructions to commit, PC %#x.\n", - inst->readPC()); + if (broadcast_free_entries) { + toFetch->iewInfo[tid].iqCount = + instQueue.getCount(tid); + toFetch->iewInfo[tid].ldstqCount = + ldstQueue.getCount(tid); - if(!inst->isSquashed()) { - instQueue.wakeDependents(inst); + toRename->iewInfo[tid].usedIQ = true; + toRename->iewInfo[tid].freeIQEntries = + instQueue.numFreeEntries(); + toRename->iewInfo[tid].usedLSQ = true; + toRename->iewInfo[tid].freeLSQEntries = + ldstQueue.numFreeEntries(tid); - for (int i = 0; i < inst->numDestRegs(); i++) - { - renameMap->markAsReady(inst->renamedDestRegIdx(i)); - } + wroteToTimeBuffer = true; } + + DPRINTF(IEW, "[tid:%i], Dispatch dispatched %i instructions.\n", + tid, toRename->iewInfo[tid].dispatched); } - // Also should advance its own time buffers if the stage ran. - // Not the best place for it, but this works (hopefully). - issueToExecQueue.advance(); + DPRINTF(IEW, "IQ has %i free entries (Can schedule: %i). " + "LSQ has %i free entries.\n", + instQueue.numFreeEntries(), instQueue.hasReadyInsts(), + ldstQueue.numFreeEntries()); + + updateStatus(); + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); + } } -#if !FULL_SYSTEM -template<class Impl> +template <class Impl> void -SimpleIEW<Impl>::lsqWriteback() +DefaultIEW<Impl>::updateExeInstStats(DynInstPtr &inst) { - ldstQueue.writebackAllInsts(); -} + int thread_number = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) + iewExecutedSwp[thread_number]++; + else + iewIewExecutedcutedInsts++; +#else + iewExecutedInsts++; #endif + + // + // Control operations + // + if (inst->isControl()) + iewExecutedBranches[thread_number]++; + + // + // Memory operations + // + if (inst->isMemRef()) { + iewExecutedRefs[thread_number]++; + + if (inst->isLoad()) { + iewExecLoadInsts[thread_number]++; + } + } +} diff --git a/src/cpu/o3/inst_queue.cc b/src/cpu/o3/inst_queue.cc index 2ff2282b4..a539066f9 100644 --- a/src/cpu/o3/inst_queue.cc +++ b/src/cpu/o3/inst_queue.cc @@ -24,15 +24,12 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/isa_specific.hh" #include "cpu/o3/inst_queue_impl.hh" // Force instantiation of InstructionQueue. -template class InstructionQueue<AlphaSimpleImpl>; - -template<> -unsigned -InstructionQueue<AlphaSimpleImpl>::DependencyEntry::mem_alloc_counter = 0; +template class InstructionQueue<O3CPUImpl>; diff --git a/src/cpu/o3/inst_queue.hh b/src/cpu/o3/inst_queue.hh index 43fe96c49..4c69ca384 100644 --- a/src/cpu/o3/inst_queue.hh +++ b/src/cpu/o3/inst_queue.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,10 +24,13 @@ * 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. + * + * Authors: Kevin Lim + * Korey Sewell */ -#ifndef __CPU_O3_CPU_INST_QUEUE_HH__ -#define __CPU_O3_CPU_INST_QUEUE_HH__ +#ifndef __CPU_O3_INST_QUEUE_HH__ +#define __CPU_O3_INST_QUEUE_HH__ #include <list> #include <map> @@ -37,8 +40,13 @@ #include "base/statistics.hh" #include "base/timebuf.hh" #include "cpu/inst_seq.hh" +#include "cpu/o3/dep_graph.hh" +#include "cpu/op_class.hh" #include "sim/host.hh" +class FUPool; +class MemInterface; + /** * A standard instruction queue class. It holds ready instructions, in * order, in seperate priority queues to facilitate the scheduling of @@ -47,98 +55,216 @@ * floating point registers have their indices start after the integer * registers (ie with 96 int and 96 fp registers, regs 0-95 are integer * and 96-191 are fp). This remains true even for both logical and - * physical register indices. + * physical register indices. The IQ depends on the memory dependence unit to + * track when memory operations are ready in terms of ordering; register + * dependencies are tracked normally. Right now the IQ also handles the + * execution timing; this is mainly to allow back-to-back scheduling without + * requiring IEW to be able to peek into the IQ. At the end of the execution + * latency, the instruction is put into the queue to execute, where it will + * have the execute() function called on it. + * @todo: Make IQ able to handle multiple FU pools. */ template <class Impl> class InstructionQueue { public: //Typedefs from the Impl. - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::O3CPU O3CPU; typedef typename Impl::DynInstPtr DynInstPtr; typedef typename Impl::Params Params; + typedef typename Impl::CPUPol::IEW IEW; typedef typename Impl::CPUPol::MemDepUnit MemDepUnit; typedef typename Impl::CPUPol::IssueStruct IssueStruct; typedef typename Impl::CPUPol::TimeStruct TimeStruct; - // Typedef of iterator through the list of instructions. Might be - // better to untie this from the FullCPU or pass its information to - // the stages. + // Typedef of iterator through the list of instructions. typedef typename std::list<DynInstPtr>::iterator ListIt; - /** - * Struct for comparing entries to be added to the priority queue. This - * gives reverse ordering to the instructions in terms of sequence - * numbers: the instructions with smaller sequence numbers (and hence - * are older) will be at the top of the priority queue. - */ - struct pqCompare - { - bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const - { - return lhs->seqNum > rhs->seqNum; - } - }; + friend class Impl::O3CPU; - /** - * Struct for comparing entries to be added to the set. This gives - * standard ordering in terms of sequence numbers. - */ - struct setCompare - { - bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const - { - return lhs->seqNum < rhs->seqNum; - } + /** FU completion event class. */ + class FUCompletion : public Event { + private: + /** Executing instruction. */ + DynInstPtr inst; + + /** Index of the FU used for executing. */ + int fuIdx; + + /** Pointer back to the instruction queue. */ + InstructionQueue<Impl> *iqPtr; + + /** Should the FU be added to the list to be freed upon + * completing this event. + */ + bool freeFU; + + public: + /** Construct a FU completion event. */ + FUCompletion(DynInstPtr &_inst, int fu_idx, + InstructionQueue<Impl> *iq_ptr); + + virtual void process(); + virtual const char *description(); + void setFreeFU() { freeFU = true; } }; - typedef std::priority_queue<DynInstPtr, vector<DynInstPtr>, pqCompare> - ReadyInstQueue; + /** Constructs an IQ. */ + InstructionQueue(Params *params); - InstructionQueue(Params ¶ms); + /** Destructs the IQ. */ + ~InstructionQueue(); + /** Returns the name of the IQ. */ + std::string name() const; + + /** Registers statistics. */ void regStats(); - void setCPU(FullCPU *cpu); + /** Resets all instruction queue state. */ + void resetState(); + + /** Sets CPU pointer. */ + void setCPU(O3CPU *_cpu) { cpu = _cpu; } + + /** Sets active threads list. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets the IEW pointer. */ + void setIEW(IEW *iew_ptr) { iewStage = iew_ptr; } + /** Sets the timer buffer between issue and execute. */ void setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2eQueue); + /** Sets the global time buffer. */ void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + /** Switches out the instruction queue. */ + void switchOut(); + + /** Takes over execution from another CPU's thread. */ + void takeOverFrom(); + + /** Returns if the IQ is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** Number of entries needed for given amount of threads. */ + int entryAmount(int num_threads); + + /** Resets max entries for all threads. */ + void resetEntries(); + + /** Returns total number of free entries. */ unsigned numFreeEntries(); + /** Returns number of free entries for a thread. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns whether or not the IQ is full. */ bool isFull(); + /** Returns whether or not the IQ is full for a specific thread. */ + bool isFull(unsigned tid); + + /** Returns if there are any ready instructions in the IQ. */ + bool hasReadyInsts(); + + /** Inserts a new instruction into the IQ. */ void insert(DynInstPtr &new_inst); + /** Inserts a new, non-speculative instruction into the IQ. */ void insertNonSpec(DynInstPtr &new_inst); - void advanceTail(DynInstPtr &inst); + /** Inserts a memory or write barrier into the IQ to make sure + * loads and stores are ordered properly. + */ + void insertBarrier(DynInstPtr &barr_inst); + + /** Returns the oldest scheduled instruction, and removes it from + * the list of instructions waiting to execute. + */ + DynInstPtr getInstToExecute(); + + /** + * Records the instruction as the producer of a register without + * adding it to the rest of the IQ. + */ + void recordProducer(DynInstPtr &inst) + { addToProducers(inst); } + /** Process FU completion event. */ + void processFUCompletion(DynInstPtr &inst, int fu_idx); + + /** + * Schedules ready instructions, adding the ready ones (oldest first) to + * the queue to execute. + */ void scheduleReadyInsts(); + /** Schedules a single specific non-speculative instruction. */ void scheduleNonSpec(const InstSeqNum &inst); - void wakeDependents(DynInstPtr &completed_inst); + /** + * Commits all instructions up to and including the given sequence number, + * for a specific thread. + */ + void commit(const InstSeqNum &inst, unsigned tid = 0); + + /** Wakes all dependents of a completed instruction. */ + int wakeDependents(DynInstPtr &completed_inst); + + /** Adds a ready memory instruction to the ready list. */ + void addReadyMemInst(DynInstPtr &ready_inst); + + /** + * Reschedules a memory instruction. It will be ready to issue once + * replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &resched_inst); + /** Replays a memory instruction. It must be rescheduled first. */ + void replayMemInst(DynInstPtr &replay_inst); + + /** Completes a memory operation. */ + void completeMemInst(DynInstPtr &completed_inst); + + /** Indicates an ordering violation between a store and a load. */ void violation(DynInstPtr &store, DynInstPtr &faulting_load); - // Change this to take in the sequence number - void squash(); + /** + * Squashes instructions for a thread. Squashing information is obtained + * from the time buffer. + */ + void squash(unsigned tid); - void doSquash(); + /** Returns the number of used entries for a thread. */ + unsigned getCount(unsigned tid) { return count[tid]; }; - void stopSquash(); + /** Debug function to print all instructions. */ + void printInsts(); private: + /** Does the actual squashing. */ + void doSquash(unsigned tid); + + ///////////////////////// + // Various pointers + ///////////////////////// + /** Pointer to the CPU. */ - FullCPU *cpu; + O3CPU *cpu; + + /** Cache interface. */ + MemInterface *dcacheInterface; + + /** Pointer to IEW stage. */ + IEW *iewStage; /** The memory dependence unit, which tracks/predicts memory dependences * between instructions. */ - MemDepUnit memDepUnit; + MemDepUnit memDepUnit[Impl::MaxThreads]; /** The queue to the execute stage. Issued instructions will be written * into it. @@ -151,36 +277,40 @@ class InstructionQueue /** Wire to read information from timebuffer. */ typename TimeBuffer<TimeStruct>::wire fromCommit; - enum InstList { - Int, - Float, - Branch, - Memory, - Misc, - Squashed, - None - }; + /** Function unit pool. */ + FUPool *fuPool; - /** List of ready int instructions. Used to keep track of the order in - * which instructions should issue. - */ - ReadyInstQueue readyIntInsts; + ////////////////////////////////////// + // Instruction lists, ready queues, and ordering + ////////////////////////////////////// + + /** List of all the instructions in the IQ (some of which may be issued). */ + std::list<DynInstPtr> instList[Impl::MaxThreads]; - /** List of ready floating point instructions. */ - ReadyInstQueue readyFloatInsts; + /** List of instructions that are ready to be executed. */ + std::list<DynInstPtr> instsToExecute; - /** List of ready branch instructions. */ - ReadyInstQueue readyBranchInsts; + /** + * Struct for comparing entries to be added to the priority queue. + * This gives reverse ordering to the instructions in terms of + * sequence numbers: the instructions with smaller sequence + * numbers (and hence are older) will be at the top of the + * priority queue. + */ + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; - /** List of ready miscellaneous instructions. */ - ReadyInstQueue readyMiscInsts; + typedef std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> + ReadyInstQueue; - /** List of squashed instructions (which are still valid and in IQ). - * Implemented using a priority queue; the entries must contain both - * the IQ index and sequence number of each instruction so that - * ordering based on sequence numbers can be used. + /** List of ready instructions, per op class. They are separated by op + * class to allow for easy mapping to FUs. */ - ReadyInstQueue squashedInsts; + ReadyInstQueue readyInsts[Num_OpClasses]; /** List of non-speculative instructions that will be scheduled * once the IQ gets a signal from commit. While it's redundant to @@ -191,34 +321,80 @@ class InstructionQueue */ std::map<InstSeqNum, DynInstPtr> nonSpecInsts; - typedef typename std::map<InstSeqNum, DynInstPtr>::iterator non_spec_it_t; + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator NonSpecMapIt; - /** Number of free IQ entries left. */ - unsigned freeEntries; + /** Entry for the list age ordering by op class. */ + struct ListOrderEntry { + OpClass queueType; + InstSeqNum oldestInst; + }; - /** The number of entries in the instruction queue. */ - unsigned numEntries; + /** List that contains the age order of the oldest instruction of each + * ready queue. Used to select the oldest instruction available + * among op classes. + * @todo: Might be better to just move these entries around instead + * of creating new ones every time the position changes due to an + * instruction issuing. Not sure std::list supports this. + */ + std::list<ListOrderEntry> listOrder; - /** The number of integer instructions that can be issued in one - * cycle. + typedef typename std::list<ListOrderEntry>::iterator ListOrderIt; + + /** Tracks if each ready queue is on the age order list. */ + bool queueOnList[Num_OpClasses]; + + /** Iterators of each ready queue. Points to their spot in the age order + * list. */ - unsigned intWidth; + ListOrderIt readyIt[Num_OpClasses]; + + /** Add an op class to the age order list. */ + void addToOrderList(OpClass op_class); - /** The number of floating point instructions that can be issued - * in one cycle. + /** + * Called when the oldest instruction has been removed from a ready queue; + * this places that ready queue into the proper spot in the age order list. */ - unsigned floatWidth; + void moveToYoungerInst(ListOrderIt age_order_it); + + DependencyGraph<DynInstPtr> dependGraph; + + ////////////////////////////////////// + // Various parameters + ////////////////////////////////////// + + /** IQ Resource Sharing Policy */ + enum IQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** IQ sharing policy for SMT. */ + IQPolicy iqPolicy; + + /** Number of Total Threads*/ + unsigned numThreads; - /** The number of branches that can be issued in one cycle. */ - unsigned branchWidth; + /** Pointer to list of active threads. */ + std::list<unsigned> *activeThreads; - /** The number of memory instructions that can be issued in one cycle. */ - unsigned memoryWidth; + /** Per Thread IQ count */ + unsigned count[Impl::MaxThreads]; + + /** Max IQ Entries Per Thread */ + unsigned maxEntries[Impl::MaxThreads]; + + /** Number of free IQ entries left. */ + unsigned freeEntries; + + /** The number of entries in the instruction queue. */ + unsigned numEntries; /** The total number of instructions that can be issued in one cycle. */ unsigned totalWidth; - //The number of physical registers in the CPU. + /** The number of physical registers in the CPU. */ unsigned numPhysRegs; /** The number of physical integer registers in the CPU. */ @@ -232,55 +408,11 @@ class InstructionQueue */ unsigned commitToIEWDelay; - ////////////////////////////////// - // Variables needed for squashing - ////////////////////////////////// + /** Is the IQ switched out. */ + bool switchedOut; /** The sequence number of the squashed instruction. */ - InstSeqNum squashedSeqNum; - - /** Iterator that points to the youngest instruction in the IQ. */ - ListIt tail; - - /** Iterator that points to the last instruction that has been squashed. - * This will not be valid unless the IQ is in the process of squashing. - */ - ListIt squashIt; - - /////////////////////////////////// - // Dependency graph stuff - /////////////////////////////////// - - class DependencyEntry - { - public: - DynInstPtr inst; - //Might want to include data about what arch. register the - //dependence is waiting on. - DependencyEntry *next; - - //This function, and perhaps this whole class, stand out a little - //bit as they don't fit a classification well. I want access - //to the underlying structure of the linked list, yet at - //the same time it feels like this should be something abstracted - //away. So for now it will sit here, within the IQ, until - //a better implementation is decided upon. - // This function probably shouldn't be within the entry... - void insert(DynInstPtr &new_inst); - - void remove(DynInstPtr &inst_to_remove); - - // Debug variable, remove when done testing. - static unsigned mem_alloc_counter; - }; - - /** Array of linked lists. Each linked list is a list of all the - * instructions that depend upon a given register. The actual - * register's index is used to index into the graph; ie all - * instructions in flight that are dependent upon r34 will be - * in the linked list of dependGraph[34]. - */ - DependencyEntry *dependGraph; + InstSeqNum squashedSeqNum[Impl::MaxThreads]; /** A cache of the recently woken registers. It is 1 if the register * has been woken up recently, and 0 if the register has been added @@ -288,49 +420,89 @@ class InstructionQueue * is basically a secondary scoreboard, and should pretty much mirror * the scoreboard that exists in the rename map. */ - vector<bool> regScoreboard; + std::vector<bool> regScoreboard; + /** Adds an instruction to the dependency graph, as a consumer. */ bool addToDependents(DynInstPtr &new_inst); - void insertDependency(DynInstPtr &new_inst); - void createDependency(DynInstPtr &new_inst); + /** Adds an instruction to the dependency graph, as a producer. */ + void addToProducers(DynInstPtr &new_inst); + + /** Moves an instruction to the ready queue if it is ready. */ void addIfReady(DynInstPtr &inst); - private: /** Debugging function to count how many entries are in the IQ. It does * a linear walk through the instructions, so do not call this function * during normal execution. */ int countInsts(); - /** Debugging function to dump out the dependency graph. - */ - void dumpDependGraph(); - /** Debugging function to dump all the list sizes, as well as print * out the list of nonspeculative instructions. Should not be used * in any other capacity, but it has no harmful sideaffects. */ void dumpLists(); + /** Debugging function to dump out all instructions that are in the + * IQ. + */ + void dumpInsts(); + + /** Stat for number of instructions added. */ Stats::Scalar<> iqInstsAdded; + /** Stat for number of non-speculative instructions added. */ Stats::Scalar<> iqNonSpecInstsAdded; -// Stats::Scalar<> iqIntInstsAdded; + + Stats::Scalar<> iqInstsIssued; + /** Stat for number of integer instructions issued. */ Stats::Scalar<> iqIntInstsIssued; -// Stats::Scalar<> iqFloatInstsAdded; + /** Stat for number of floating point instructions issued. */ Stats::Scalar<> iqFloatInstsIssued; -// Stats::Scalar<> iqBranchInstsAdded; + /** Stat for number of branch instructions issued. */ Stats::Scalar<> iqBranchInstsIssued; -// Stats::Scalar<> iqMemInstsAdded; + /** Stat for number of memory instructions issued. */ Stats::Scalar<> iqMemInstsIssued; -// Stats::Scalar<> iqMiscInstsAdded; + /** Stat for number of miscellaneous instructions issued. */ Stats::Scalar<> iqMiscInstsIssued; + /** Stat for number of squashed instructions that were ready to issue. */ Stats::Scalar<> iqSquashedInstsIssued; - Stats::Scalar<> iqLoopSquashStalls; + /** Stat for number of squashed instructions examined when squashing. */ Stats::Scalar<> iqSquashedInstsExamined; + /** Stat for number of squashed instruction operands examined when + * squashing. + */ Stats::Scalar<> iqSquashedOperandsExamined; + /** Stat for number of non-speculative instructions removed due to a squash. + */ Stats::Scalar<> iqSquashedNonSpecRemoved; - + // Also include number of instructions rescheduled and replayed. + + /** Distribution of number of instructions in the queue. + * @todo: Need to create struct to track the entry time for each + * instruction. */ + Stats::VectorDistribution<> queueResDist; + /** Distribution of the number of instructions issued. */ + Stats::Distribution<> numIssuedDist; + /** Distribution of the cycles it takes to issue an instruction. + * @todo: Need to create struct to track the ready time for each + * instruction. */ + Stats::VectorDistribution<> issueDelayDist; + + /** Number of times an instruction could not be issued because a + * FU was busy. + */ + Stats::Vector<> statFuBusy; +// Stats::Vector<> dist_unissued; + /** Stat for total number issued for each instruction type. */ + Stats::Vector2d<> statIssuedInstType; + + /** Number of instructions issued per cycle. */ + Stats::Formula issueRate; + + /** Number of times the FU was busy. */ + Stats::Vector<> fuBusy; + /** Number of times the FU was busy per instruction issued. */ + Stats::Formula fuBusyRate; }; -#endif //__CPU_O3_CPU_INST_QUEUE_HH__ +#endif //__CPU_O3_INST_QUEUE_HH__ diff --git a/src/cpu/o3/inst_queue_impl.hh b/src/cpu/o3/inst_queue_impl.hh index 048dc7c00..b99bd0900 100644 --- a/src/cpu/o3/inst_queue_impl.hh +++ b/src/cpu/o3/inst_queue_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,75 +24,153 @@ * 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. + * + * Authors: Kevin Lim + * Korey Sewell */ -// Todo: -// Current ordering allows for 0 cycle added-to-scheduled. Could maybe fake -// it; either do in reverse order, or have added instructions put into a -// different ready queue that, in scheduleRreadyInsts(), gets put onto the -// normal ready queue. This would however give only a one cycle delay, -// but probably is more flexible to actually add in a delay parameter than -// just running it backwards. - #include <limits> #include <vector> #include "sim/root.hh" +#include "cpu/o3/fu_pool.hh" #include "cpu/o3/inst_queue.hh" -// Either compile error or max int due to sign extension. -// Hack to avoid compile warnings. -const InstSeqNum MaxInstSeqNum = std::numeric_limits<InstSeqNum>::max(); +using namespace std; template <class Impl> -InstructionQueue<Impl>::InstructionQueue(Params ¶ms) - : memDepUnit(params), - numEntries(params.numIQEntries), - intWidth(params.executeIntWidth), - floatWidth(params.executeFloatWidth), - branchWidth(params.executeBranchWidth), - memoryWidth(params.executeMemoryWidth), - totalWidth(params.issueWidth), - numPhysIntRegs(params.numPhysIntRegs), - numPhysFloatRegs(params.numPhysFloatRegs), - commitToIEWDelay(params.commitToIEWDelay) +InstructionQueue<Impl>::FUCompletion::FUCompletion(DynInstPtr &_inst, + int fu_idx, + InstructionQueue<Impl> *iq_ptr) + : Event(&mainEventQueue, Stat_Event_Pri), + inst(_inst), fuIdx(fu_idx), iqPtr(iq_ptr), freeFU(false) { - // Initialize the number of free IQ entries. - freeEntries = numEntries; + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +InstructionQueue<Impl>::FUCompletion::process() +{ + iqPtr->processFUCompletion(inst, freeFU ? fuIdx : -1); + inst = NULL; +} + + +template <class Impl> +const char * +InstructionQueue<Impl>::FUCompletion::description() +{ + return "Functional unit completion event"; +} + +template <class Impl> +InstructionQueue<Impl>::InstructionQueue(Params *params) + : fuPool(params->fuPool), + numEntries(params->numIQEntries), + totalWidth(params->issueWidth), + numPhysIntRegs(params->numPhysIntRegs), + numPhysFloatRegs(params->numPhysFloatRegs), + commitToIEWDelay(params->commitToIEWDelay) +{ + assert(fuPool); + + switchedOut = false; + + numThreads = params->numberOfThreads; // Set the number of physical registers as the number of int + float numPhysRegs = numPhysIntRegs + numPhysFloatRegs; - DPRINTF(IQ, "IQ: There are %i physical registers.\n", numPhysRegs); + DPRINTF(IQ, "There are %i physical registers.\n", numPhysRegs); //Create an entry for each physical register within the //dependency graph. - dependGraph = new DependencyEntry[numPhysRegs]; + dependGraph.resize(numPhysRegs); // Resize the register scoreboard. regScoreboard.resize(numPhysRegs); - // Initialize all the head pointers to point to NULL, and all the - // entries as unready. - // Note that in actuality, the registers corresponding to the logical - // registers start off as ready. However this doesn't matter for the - // IQ as the instruction should have been correctly told if those - // registers are ready in rename. Thus it can all be initialized as - // unready. - for (int i = 0; i < numPhysRegs; ++i) - { - dependGraph[i].next = NULL; - dependGraph[i].inst = NULL; - regScoreboard[i] = false; + //Initialize Mem Dependence Units + for (int i = 0; i < numThreads; i++) { + memDepUnit[i].init(params,i); + memDepUnit[i].setIQ(this); } + resetState(); + + string policy = params->smtIQPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out resource sharing policy + if (policy == "dynamic") { + iqPolicy = Dynamic; + + //Set Max Entries to Total ROB Capacity + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = numEntries; + } + + } else if (policy == "partitioned") { + iqPolicy = Partitioned; + + //@todo:make work if part_amt doesnt divide evenly. + int part_amt = numEntries / numThreads; + + //Divide ROB up evenly + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = part_amt; + } + + DPRINTF(IQ, "IQ sharing policy set to Partitioned:" + "%i entries per thread.\n",part_amt); + + } else if (policy == "threshold") { + iqPolicy = Threshold; + + double threshold = (double)params->smtIQThreshold / 100; + + int thresholdIQ = (int)((double)threshold * numEntries); + + //Divide up by threshold amount + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = thresholdIQ; + } + + DPRINTF(IQ, "IQ sharing policy set to Threshold:" + "%i entries per thread.\n",thresholdIQ); + } else { + assert(0 && "Invalid IQ Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } +} + +template <class Impl> +InstructionQueue<Impl>::~InstructionQueue() +{ + dependGraph.reset(); +#ifdef DEBUG + cprintf("Nodes traversed: %i, removed: %i\n", + dependGraph.nodesTraversed, dependGraph.nodesRemoved); +#endif +} + +template <class Impl> +std::string +InstructionQueue<Impl>::name() const +{ + return cpu->name() + ".iq"; } template <class Impl> void InstructionQueue<Impl>::regStats() { + using namespace Stats; iqInstsAdded .name(name() + ".iqInstsAdded") .desc("Number of instructions added to the IQ (excludes non-spec)") @@ -103,36 +181,31 @@ InstructionQueue<Impl>::regStats() .desc("Number of non-speculative instructions added to the IQ") .prereq(iqNonSpecInstsAdded); -// iqIntInstsAdded; + iqInstsIssued + .name(name() + ".iqInstsIssued") + .desc("Number of instructions issued") + .prereq(iqInstsIssued); iqIntInstsIssued .name(name() + ".iqIntInstsIssued") .desc("Number of integer instructions issued") .prereq(iqIntInstsIssued); -// iqFloatInstsAdded; - iqFloatInstsIssued .name(name() + ".iqFloatInstsIssued") .desc("Number of float instructions issued") .prereq(iqFloatInstsIssued); -// iqBranchInstsAdded; - iqBranchInstsIssued .name(name() + ".iqBranchInstsIssued") .desc("Number of branch instructions issued") .prereq(iqBranchInstsIssued); -// iqMemInstsAdded; - iqMemInstsIssued .name(name() + ".iqMemInstsIssued") .desc("Number of memory instructions issued") .prereq(iqMemInstsIssued); -// iqMiscInstsAdded; - iqMiscInstsIssued .name(name() + ".iqMiscInstsIssued") .desc("Number of miscellaneous instructions issued") @@ -143,12 +216,6 @@ InstructionQueue<Impl>::regStats() .desc("Number of squashed instructions issued") .prereq(iqSquashedInstsIssued); - iqLoopSquashStalls - .name(name() + ".iqLoopSquashStalls") - .desc("Number of times issue loop had to restart due to squashed " - "inst; mainly for profiling") - .prereq(iqLoopSquashStalls); - iqSquashedInstsExamined .name(name() + ".iqSquashedInstsExamined") .desc("Number of squashed instructions iterated over during squash;" @@ -166,25 +233,143 @@ InstructionQueue<Impl>::regStats() .desc("Number of squashed non-spec instructions that were removed") .prereq(iqSquashedNonSpecRemoved); - // Tell mem dependence unit to reg stats as well. - memDepUnit.regStats(); + queueResDist + .init(Num_OpClasses, 0, 99, 2) + .name(name() + ".IQ:residence:") + .desc("cycles from dispatch to issue") + .flags(total | pdf | cdf ) + ; + for (int i = 0; i < Num_OpClasses; ++i) { + queueResDist.subname(i, opClassStrings[i]); + } + numIssuedDist + .init(0,totalWidth,1) + .name(name() + ".ISSUE:issued_per_cycle") + .desc("Number of insts issued each cycle") + .flags(pdf) + ; +/* + dist_unissued + .init(Num_OpClasses+2) + .name(name() + ".ISSUE:unissued_cause") + .desc("Reason ready instruction not issued") + .flags(pdf | dist) + ; + for (int i=0; i < (Num_OpClasses + 2); ++i) { + dist_unissued.subname(i, unissued_names[i]); + } +*/ + statIssuedInstType + .init(numThreads,Num_OpClasses) + .name(name() + ".ISSUE:FU_type") + .desc("Type of FU issued") + .flags(total | pdf | dist) + ; + statIssuedInstType.ysubnames(opClassStrings); + + // + // How long did instructions for a particular FU type wait prior to issue + // + + issueDelayDist + .init(Num_OpClasses,0,99,2) + .name(name() + ".ISSUE:") + .desc("cycles from operands ready to issue") + .flags(pdf | cdf) + ; + + for (int i=0; i<Num_OpClasses; ++i) { + stringstream subname; + subname << opClassStrings[i] << "_delay"; + issueDelayDist.subname(i, subname.str()); + } + + issueRate + .name(name() + ".ISSUE:rate") + .desc("Inst issue rate") + .flags(total) + ; + issueRate = iqInstsIssued / cpu->numCycles; + + statFuBusy + .init(Num_OpClasses) + .name(name() + ".ISSUE:fu_full") + .desc("attempts to use FU when none available") + .flags(pdf | dist) + ; + for (int i=0; i < Num_OpClasses; ++i) { + statFuBusy.subname(i, opClassStrings[i]); + } + + fuBusy + .init(numThreads) + .name(name() + ".ISSUE:fu_busy_cnt") + .desc("FU busy when requested") + .flags(total) + ; + + fuBusyRate + .name(name() + ".ISSUE:fu_busy_rate") + .desc("FU busy rate (busy events/executed inst)") + .flags(total) + ; + fuBusyRate = fuBusy / iqInstsIssued; + + for ( int i=0; i < numThreads; i++) { + // Tell mem dependence unit to reg stats as well. + memDepUnit[i].regStats(); + } } template <class Impl> void -InstructionQueue<Impl>::setCPU(FullCPU *cpu_ptr) +InstructionQueue<Impl>::resetState() { - cpu = cpu_ptr; + //Initialize thread IQ counts + for (int i = 0; i <numThreads; i++) { + count[i] = 0; + instList[i].clear(); + } - tail = cpu->instList.begin(); + // Initialize the number of free IQ entries. + freeEntries = numEntries; + + // Note that in actuality, the registers corresponding to the logical + // registers start off as ready. However this doesn't matter for the + // IQ as the instruction should have been correctly told if those + // registers are ready in rename. Thus it can all be initialized as + // unready. + for (int i = 0; i < numPhysRegs; ++i) { + regScoreboard[i] = false; + } + + for (int i = 0; i < numThreads; ++i) { + squashedSeqNum[i] = 0; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + while (!readyInsts[i].empty()) + readyInsts[i].pop(); + queueOnList[i] = false; + readyIt[i] = listOrder.end(); + } + nonSpecInsts.clear(); + listOrder.clear(); } template <class Impl> void -InstructionQueue<Impl>::setIssueToExecuteQueue( - TimeBuffer<IssueStruct> *i2e_ptr) +InstructionQueue<Impl>::setActiveThreads(list<unsigned> *at_ptr) { - DPRINTF(IQ, "IQ: Set the issue to execute queue.\n"); + DPRINTF(IQ, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template <class Impl> +void +InstructionQueue<Impl>::setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2e_ptr) +{ + DPRINTF(IQ, "Set the issue to execute queue.\n"); issueToExecuteQueue = i2e_ptr; } @@ -192,19 +377,77 @@ template <class Impl> void InstructionQueue<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) { - DPRINTF(IQ, "IQ: Set the time buffer.\n"); + DPRINTF(IQ, "Set the time buffer.\n"); timeBuffer = tb_ptr; fromCommit = timeBuffer->getWire(-commitToIEWDelay); } template <class Impl> +void +InstructionQueue<Impl>::switchOut() +{ + resetState(); + dependGraph.reset(); + switchedOut = true; + for (int i = 0; i < numThreads; ++i) { + memDepUnit[i].switchOut(); + } +} + +template <class Impl> +void +InstructionQueue<Impl>::takeOverFrom() +{ + switchedOut = false; +} + +template <class Impl> +int +InstructionQueue<Impl>::entryAmount(int num_threads) +{ + if (iqPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + + +template <class Impl> +void +InstructionQueue<Impl>::resetEntries() +{ + if (iqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + list<unsigned>::iterator list_end = (*activeThreads).end(); + + while (threads != list_end) { + if (iqPolicy == Partitioned) { + maxEntries[*threads++] = numEntries / active_threads; + } else if(iqPolicy == Threshold && active_threads == 1) { + maxEntries[*threads++] = numEntries; + } + } + } +} + +template <class Impl> unsigned InstructionQueue<Impl>::numFreeEntries() { return freeEntries; } +template <class Impl> +unsigned +InstructionQueue<Impl>::numFreeEntries(unsigned tid) +{ + return maxEntries[tid] - count[tid]; +} + // Might want to do something more complex if it knows how many instructions // will be issued this cycle. template <class Impl> @@ -219,447 +462,412 @@ InstructionQueue<Impl>::isFull() } template <class Impl> +bool +InstructionQueue<Impl>::isFull(unsigned tid) +{ + if (numFreeEntries(tid) == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstructionQueue<Impl>::hasReadyInsts() +{ + if (!listOrder.empty()) { + return true; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + return true; + } + } + + return false; +} + +template <class Impl> void InstructionQueue<Impl>::insert(DynInstPtr &new_inst) { // Make sure the instruction is valid assert(new_inst); - DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n", - new_inst->readPC()); + DPRINTF(IQ, "Adding instruction [sn:%lli] PC %#x to the IQ.\n", + new_inst->seqNum, new_inst->readPC()); - // Check if there are any free entries. Panic if there are none. - // Might want to have this return a fault in the future instead of - // panicing. assert(freeEntries != 0); - // If the IQ currently has nothing in it, then there's a possibility - // that the tail iterator is invalid (might have been pointing at an - // instruction that was retired). Reset the tail iterator. - if (freeEntries == numEntries) { - tail = cpu->instList.begin(); - } + instList[new_inst->threadNumber].push_back(new_inst); - // Move the tail iterator. Instructions may not have been issued - // to the IQ, so we may have to increment the iterator more than once. - while ((*tail) != new_inst) { - tail++; - - // Make sure the tail iterator points at something legal. - assert(tail != cpu->instList.end()); - } - - - // Decrease the number of free entries. --freeEntries; + new_inst->setInIQ(); + // Look through its source registers (physical regs), and mark any // dependencies. addToDependents(new_inst); // Have this instruction set itself as the producer of its destination // register(s). - createDependency(new_inst); + addToProducers(new_inst); - // If it's a memory instruction, add it to the memory dependency - // unit. if (new_inst->isMemRef()) { - memDepUnit.insert(new_inst); - // Uh..forgot to look it up and put it on the proper dependency list - // if the instruction should not go yet. + memDepUnit[new_inst->threadNumber].insert(new_inst); } else { - // If the instruction is ready then add it to the ready list. addIfReady(new_inst); } ++iqInstsAdded; + count[new_inst->threadNumber]++; + assert(freeEntries == (numEntries - countInsts())); } template <class Impl> void -InstructionQueue<Impl>::insertNonSpec(DynInstPtr &inst) +InstructionQueue<Impl>::insertNonSpec(DynInstPtr &new_inst) { - nonSpecInsts[inst->seqNum] = inst; - // @todo: Clean up this code; can do it by setting inst as unable // to issue, then calling normal insert on the inst. - // Make sure the instruction is valid - assert(inst); + assert(new_inst); - DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n", - inst->readPC()); + nonSpecInsts[new_inst->seqNum] = new_inst; - // Check if there are any free entries. Panic if there are none. - // Might want to have this return a fault in the future instead of - // panicing. - assert(freeEntries != 0); + DPRINTF(IQ, "Adding non-speculative instruction [sn:%lli] PC %#x " + "to the IQ.\n", + new_inst->seqNum, new_inst->readPC()); - // If the IQ currently has nothing in it, then there's a possibility - // that the tail iterator is invalid (might have been pointing at an - // instruction that was retired). Reset the tail iterator. - if (freeEntries == numEntries) { - tail = cpu->instList.begin(); - } - - // Move the tail iterator. Instructions may not have been issued - // to the IQ, so we may have to increment the iterator more than once. - while ((*tail) != inst) { - tail++; + assert(freeEntries != 0); - // Make sure the tail iterator points at something legal. - assert(tail != cpu->instList.end()); - } + instList[new_inst->threadNumber].push_back(new_inst); - // Decrease the number of free entries. --freeEntries; + new_inst->setInIQ(); + // Have this instruction set itself as the producer of its destination // register(s). - createDependency(inst); + addToProducers(new_inst); // If it's a memory instruction, add it to the memory dependency // unit. - if (inst->isMemRef()) { - memDepUnit.insertNonSpec(inst); + if (new_inst->isMemRef()) { + memDepUnit[new_inst->threadNumber].insertNonSpec(new_inst); } ++iqNonSpecInstsAdded; + + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); } -// Slightly hack function to advance the tail iterator in the case that -// the IEW stage issues an instruction that is not added to the IQ. This -// is needed in case a long chain of such instructions occurs. -// I don't think this is used anymore. template <class Impl> void -InstructionQueue<Impl>::advanceTail(DynInstPtr &inst) +InstructionQueue<Impl>::insertBarrier(DynInstPtr &barr_inst) { - // Make sure the instruction is valid - assert(inst); - - DPRINTF(IQ, "IQ: Adding instruction PC %#x to the IQ.\n", - inst->readPC()); + memDepUnit[barr_inst->threadNumber].insertBarrier(barr_inst); - // Check if there are any free entries. Panic if there are none. - // Might want to have this return a fault in the future instead of - // panicing. - assert(freeEntries != 0); - - // If the IQ currently has nothing in it, then there's a possibility - // that the tail iterator is invalid (might have been pointing at an - // instruction that was retired). Reset the tail iterator. - if (freeEntries == numEntries) { - tail = cpu->instList.begin(); - } - - // Move the tail iterator. Instructions may not have been issued - // to the IQ, so we may have to increment the iterator more than once. - while ((*tail) != inst) { - tail++; - - // Make sure the tail iterator points at something legal. - assert(tail != cpu->instList.end()); - } - - assert(freeEntries <= numEntries); + insertNonSpec(barr_inst); +} - // Have this instruction set itself as the producer of its destination - // register(s). - createDependency(inst); +template <class Impl> +typename Impl::DynInstPtr +InstructionQueue<Impl>::getInstToExecute() +{ + assert(!instsToExecute.empty()); + DynInstPtr inst = instsToExecute.front(); + instsToExecute.pop_front(); + return inst; } -// Need to make sure the number of float and integer instructions -// issued does not exceed the total issue bandwidth. -// @todo: Figure out a better way to remove the squashed items from the -// lists. Checking the top item of each list to see if it's squashed -// wastes time and forces jumps. template <class Impl> void -InstructionQueue<Impl>::scheduleReadyInsts() +InstructionQueue<Impl>::addToOrderList(OpClass op_class) { - DPRINTF(IQ, "IQ: Attempting to schedule ready instructions from " - "the IQ.\n"); - - int int_issued = 0; - int float_issued = 0; - int branch_issued = 0; - int memory_issued = 0; - int squashed_issued = 0; - int total_issued = 0; - - IssueStruct *i2e_info = issueToExecuteQueue->access(0); - - bool insts_available = !readyBranchInsts.empty() || - !readyIntInsts.empty() || - !readyFloatInsts.empty() || - !memDepUnit.empty() || - !readyMiscInsts.empty() || - !squashedInsts.empty(); - - // Note: Requires a globally defined constant. - InstSeqNum oldest_inst = MaxInstSeqNum; - InstList list_with_oldest = None; - - // Temporary values. - DynInstPtr int_head_inst; - DynInstPtr float_head_inst; - DynInstPtr branch_head_inst; - DynInstPtr mem_head_inst; - DynInstPtr misc_head_inst; - DynInstPtr squashed_head_inst; - - // Somewhat nasty code to look at all of the lists where issuable - // instructions are located, and choose the oldest instruction among - // those lists. Consider a rewrite in the future. - while (insts_available && total_issued < totalWidth) - { - // Set this to false. Each if-block is required to set it to true - // if there were instructions available this check. This will cause - // this loop to run once more than necessary, but avoids extra calls. - insts_available = false; - - oldest_inst = MaxInstSeqNum; + assert(!readyInsts[op_class].empty()); - list_with_oldest = None; + ListOrderEntry queue_entry; - if (!readyIntInsts.empty() && - int_issued < intWidth) { + queue_entry.queueType = op_class; - insts_available = true; + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; - int_head_inst = readyIntInsts.top(); + ListOrderIt list_it = listOrder.begin(); + ListOrderIt list_end_it = listOrder.end(); - if (int_head_inst->isSquashed()) { - readyIntInsts.pop(); - - ++iqLoopSquashStalls; - - continue; - } - - oldest_inst = int_head_inst->seqNum; - - list_with_oldest = Int; + while (list_it != list_end_it) { + if ((*list_it).oldestInst > queue_entry.oldestInst) { + break; } - if (!readyFloatInsts.empty() && - float_issued < floatWidth) { - - insts_available = true; - - float_head_inst = readyFloatInsts.top(); - - if (float_head_inst->isSquashed()) { - readyFloatInsts.pop(); - - ++iqLoopSquashStalls; - - continue; - } else if (float_head_inst->seqNum < oldest_inst) { - oldest_inst = float_head_inst->seqNum; + list_it++; + } - list_with_oldest = Float; - } - } + readyIt[op_class] = listOrder.insert(list_it, queue_entry); + queueOnList[op_class] = true; +} - if (!readyBranchInsts.empty() && - branch_issued < branchWidth) { +template <class Impl> +void +InstructionQueue<Impl>::moveToYoungerInst(ListOrderIt list_order_it) +{ + // Get iterator of next item on the list + // Delete the original iterator + // Determine if the next item is either the end of the list or younger + // than the new instruction. If so, then add in a new iterator right here. + // If not, then move along. + ListOrderEntry queue_entry; + OpClass op_class = (*list_order_it).queueType; + ListOrderIt next_it = list_order_it; + + ++next_it; + + queue_entry.queueType = op_class; + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; + + while (next_it != listOrder.end() && + (*next_it).oldestInst < queue_entry.oldestInst) { + ++next_it; + } - insts_available = true; + readyIt[op_class] = listOrder.insert(next_it, queue_entry); +} - branch_head_inst = readyBranchInsts.top(); +template <class Impl> +void +InstructionQueue<Impl>::processFUCompletion(DynInstPtr &inst, int fu_idx) +{ + // The CPU could have been sleeping until this op completed (*extremely* + // long latency op). Wake it if it was. This may be overkill. + if (isSwitchedOut()) { + return; + } - if (branch_head_inst->isSquashed()) { - readyBranchInsts.pop(); + iewStage->wakeCPU(); - ++iqLoopSquashStalls; + if (fu_idx > -1) + fuPool->freeUnitNextCycle(fu_idx); - continue; - } else if (branch_head_inst->seqNum < oldest_inst) { - oldest_inst = branch_head_inst->seqNum; + // @todo: Ensure that these FU Completions happen at the beginning + // of a cycle, otherwise they could add too many instructions to + // the queue. + issueToExecuteQueue->access(0)->size++; + instsToExecute.push_back(inst); +} - list_with_oldest = Branch; - } +// @todo: Figure out a better way to remove the squashed items from the +// lists. Checking the top item of each list to see if it's squashed +// wastes time and forces jumps. +template <class Impl> +void +InstructionQueue<Impl>::scheduleReadyInsts() +{ + DPRINTF(IQ, "Attempting to schedule ready instructions from " + "the IQ.\n"); - } + IssueStruct *i2e_info = issueToExecuteQueue->access(0); - if (!memDepUnit.empty() && - memory_issued < memoryWidth) { + // Have iterator to head of the list + // While I haven't exceeded bandwidth or reached the end of the list, + // Try to get a FU that can do what this op needs. + // If successful, change the oldestInst to the new top of the list, put + // the queue in the proper place in the list. + // Increment the iterator. + // This will avoid trying to schedule a certain op class if there are no + // FUs that handle it. + ListOrderIt order_it = listOrder.begin(); + ListOrderIt order_end_it = listOrder.end(); + int total_issued = 0; - insts_available = true; + while (total_issued < totalWidth && + order_it != order_end_it) { + OpClass op_class = (*order_it).queueType; - mem_head_inst = memDepUnit.top(); + assert(!readyInsts[op_class].empty()); - if (mem_head_inst->isSquashed()) { - memDepUnit.pop(); + DynInstPtr issuing_inst = readyInsts[op_class].top(); - ++iqLoopSquashStalls; + assert(issuing_inst->seqNum == (*order_it).oldestInst); - continue; - } else if (mem_head_inst->seqNum < oldest_inst) { - oldest_inst = mem_head_inst->seqNum; + if (issuing_inst->isSquashed()) { + readyInsts[op_class].pop(); - list_with_oldest = Memory; + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; } - } - - if (!readyMiscInsts.empty()) { - insts_available = true; + listOrder.erase(order_it++); - misc_head_inst = readyMiscInsts.top(); + ++iqSquashedInstsIssued; - if (misc_head_inst->isSquashed()) { - readyMiscInsts.pop(); - - ++iqLoopSquashStalls; - - continue; - } else if (misc_head_inst->seqNum < oldest_inst) { - oldest_inst = misc_head_inst->seqNum; - - list_with_oldest = Misc; - } + continue; } - if (!squashedInsts.empty()) { + int idx = -2; + int op_latency = 1; + int tid = issuing_inst->threadNumber; - insts_available = true; + if (op_class != No_OpClass) { + idx = fuPool->getUnit(op_class); - squashed_head_inst = squashedInsts.top(); - - if (squashed_head_inst->seqNum < oldest_inst) { - list_with_oldest = Squashed; + if (idx > -1) { + op_latency = fuPool->getOpLatency(op_class); } - } - DynInstPtr issuing_inst = NULL; - - switch (list_with_oldest) { - case None: - DPRINTF(IQ, "IQ: Not able to schedule any instructions. Issuing " - "inst is %#x.\n", issuing_inst); - break; - - case Int: - issuing_inst = int_head_inst; - readyIntInsts.pop(); - ++int_issued; - DPRINTF(IQ, "IQ: Issuing integer instruction PC %#x.\n", - issuing_inst->readPC()); - break; - - case Float: - issuing_inst = float_head_inst; - readyFloatInsts.pop(); - ++float_issued; - DPRINTF(IQ, "IQ: Issuing float instruction PC %#x.\n", - issuing_inst->readPC()); - break; - - case Branch: - issuing_inst = branch_head_inst; - readyBranchInsts.pop(); - ++branch_issued; - DPRINTF(IQ, "IQ: Issuing branch instruction PC %#x.\n", - issuing_inst->readPC()); - break; - - case Memory: - issuing_inst = mem_head_inst; + // If we have an instruction that doesn't require a FU, or a + // valid FU, then schedule for execution. + if (idx == -2 || idx != -1) { + if (op_latency == 1) { + i2e_info->size++; + instsToExecute.push_back(issuing_inst); + + // Add the FU onto the list of FU's to be freed next + // cycle if we used one. + if (idx >= 0) + fuPool->freeUnitNextCycle(idx); + } else { + int issue_latency = fuPool->getIssueLatency(op_class); + // Generate completion event for the FU + FUCompletion *execution = new FUCompletion(issuing_inst, + idx, this); + + execution->schedule(curTick + cpu->cycles(issue_latency - 1)); + + // @todo: Enforce that issue_latency == 1 or op_latency + if (issue_latency > 1) { + // If FU isn't pipelined, then it must be freed + // upon the execution completing. + execution->setFreeFU(); + } else { + // Add the FU onto the list of FU's to be freed next cycle. + fuPool->freeUnitNextCycle(idx); + } + } - memDepUnit.pop(); - ++memory_issued; - DPRINTF(IQ, "IQ: Issuing memory instruction PC %#x.\n", - issuing_inst->readPC()); - break; + DPRINTF(IQ, "Thread %i: Issuing instruction PC %#x " + "[sn:%lli]\n", + tid, issuing_inst->readPC(), + issuing_inst->seqNum); - case Misc: - issuing_inst = misc_head_inst; - readyMiscInsts.pop(); + readyInsts[op_class].pop(); - ++iqMiscInstsIssued; + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } - DPRINTF(IQ, "IQ: Issuing a miscellaneous instruction PC %#x.\n", - issuing_inst->readPC()); - break; + issuing_inst->setIssued(); + ++total_issued; - case Squashed: - assert(0 && "Squashed insts should not issue any more!"); - squashedInsts.pop(); - // Set the squashed instruction as able to commit so that commit - // can just drop it from the ROB. This is a bit faked. - ++squashed_issued; - ++freeEntries; + if (!issuing_inst->isMemRef()) { + // Memory instructions can not be freed from the IQ until they + // complete. + ++freeEntries; + count[tid]--; + issuing_inst->clearInIQ(); + } else { + memDepUnit[tid].issue(issuing_inst); + } - DPRINTF(IQ, "IQ: Issuing squashed instruction PC %#x.\n", - squashed_head_inst->readPC()); - break; + listOrder.erase(order_it++); + statIssuedInstType[tid][op_class]++; + } else { + statFuBusy[op_class]++; + fuBusy[tid]++; + ++order_it; } + } - if (list_with_oldest != None && list_with_oldest != Squashed) { - i2e_info->insts[total_issued] = issuing_inst; - i2e_info->size++; - - issuing_inst->setIssued(); + numIssuedDist.sample(total_issued); + iqInstsIssued+= total_issued; - ++freeEntries; - ++total_issued; - } - - assert(freeEntries == (numEntries - countInsts())); + // If we issued any instructions, tell the CPU we had activity. + if (total_issued) { + cpu->activityThisCycle(); + } else { + DPRINTF(IQ, "Not able to schedule any instructions.\n"); } - - iqIntInstsIssued += int_issued; - iqFloatInstsIssued += float_issued; - iqBranchInstsIssued += branch_issued; - iqMemInstsIssued += memory_issued; - iqSquashedInstsIssued += squashed_issued; } template <class Impl> void InstructionQueue<Impl>::scheduleNonSpec(const InstSeqNum &inst) { - DPRINTF(IQ, "IQ: Marking nonspeculative instruction with sequence " - "number %i as ready to execute.\n", inst); + DPRINTF(IQ, "Marking nonspeculative instruction [sn:%lli] as ready " + "to execute.\n", inst); - non_spec_it_t inst_it = nonSpecInsts.find(inst); + NonSpecMapIt inst_it = nonSpecInsts.find(inst); assert(inst_it != nonSpecInsts.end()); - // Mark this instruction as ready to issue. + unsigned tid = (*inst_it).second->threadNumber; + (*inst_it).second->setCanIssue(); - // Now schedule the instruction. if (!(*inst_it).second->isMemRef()) { addIfReady((*inst_it).second); } else { - memDepUnit.nonSpecInstReady((*inst_it).second); + memDepUnit[tid].nonSpecInstReady((*inst_it).second); } + (*inst_it).second = NULL; + nonSpecInsts.erase(inst_it); } template <class Impl> void +InstructionQueue<Impl>::commit(const InstSeqNum &inst, unsigned tid) +{ + DPRINTF(IQ, "[tid:%i]: Committing instructions older than [sn:%i]\n", + tid,inst); + + ListIt iq_it = instList[tid].begin(); + + while (iq_it != instList[tid].end() && + (*iq_it)->seqNum <= inst) { + ++iq_it; + instList[tid].pop_front(); + } + + assert(freeEntries == (numEntries - countInsts())); +} + +template <class Impl> +int InstructionQueue<Impl>::wakeDependents(DynInstPtr &completed_inst) { - DPRINTF(IQ, "IQ: Waking dependents of completed instruction.\n"); - //Look at the physical destination register of the DynInst - //and look it up on the dependency graph. Then mark as ready - //any instructions within the instruction queue. - DependencyEntry *curr; + int dependents = 0; - // Tell the memory dependence unit to wake any dependents on this - // instruction if it is a memory instruction. + DPRINTF(IQ, "Waking dependents of completed instruction.\n"); + + assert(!completed_inst->isSquashed()); + // Tell the memory dependence unit to wake any dependents on this + // instruction if it is a memory instruction. Also complete the memory + // instruction at this point since we know it executed without issues. + // @todo: Might want to rename "completeMemInst" to something that + // indicates that it won't need to be replayed, and call this + // earlier. Might not be a big deal. if (completed_inst->isMemRef()) { - memDepUnit.wakeDependents(completed_inst); + memDepUnit[completed_inst->threadNumber].wakeDependents(completed_inst); + completeMemInst(completed_inst); + } else if (completed_inst->isMemBarrier() || + completed_inst->isWriteBarrier()) { + memDepUnit[completed_inst->threadNumber].completeBarrier(completed_inst); } for (int dest_reg_idx = 0; @@ -676,44 +884,94 @@ InstructionQueue<Impl>::wakeDependents(DynInstPtr &completed_inst) continue; } - DPRINTF(IQ, "IQ: Waking any dependents on register %i.\n", + DPRINTF(IQ, "Waking any dependents on register %i.\n", (int) dest_reg); - //Maybe abstract this part into a function. - //Go through the dependency chain, marking the registers as ready - //within the waiting instructions. - while (dependGraph[dest_reg].next) { + //Go through the dependency chain, marking the registers as + //ready within the waiting instructions. + DynInstPtr dep_inst = dependGraph.pop(dest_reg); - curr = dependGraph[dest_reg].next; - - DPRINTF(IQ, "IQ: Waking up a dependent instruction, PC%#x.\n", - curr->inst->readPC()); + while (dep_inst) { + DPRINTF(IQ, "Waking up a dependent instruction, PC%#x.\n", + dep_inst->readPC()); // Might want to give more information to the instruction - // so that it knows which of its source registers is ready. - // However that would mean that the dependency graph entries - // would need to hold the src_reg_idx. - curr->inst->markSrcRegReady(); - - addIfReady(curr->inst); - - dependGraph[dest_reg].next = curr->next; + // so that it knows which of its source registers is + // ready. However that would mean that the dependency + // graph entries would need to hold the src_reg_idx. + dep_inst->markSrcRegReady(); - DependencyEntry::mem_alloc_counter--; + addIfReady(dep_inst); - curr->inst = NULL; + dep_inst = dependGraph.pop(dest_reg); - delete curr; + ++dependents; } - // Reset the head node now that all of its dependents have been woken - // up. - dependGraph[dest_reg].next = NULL; - dependGraph[dest_reg].inst = NULL; + // Reset the head node now that all of its dependents have + // been woken up. + assert(dependGraph.empty(dest_reg)); + dependGraph.clearInst(dest_reg); // Mark the scoreboard as having that register ready. regScoreboard[dest_reg] = true; } + return dependents; +} + +template <class Impl> +void +InstructionQueue<Impl>::addReadyMemInst(DynInstPtr &ready_inst) +{ + OpClass op_class = ready_inst->opClass(); + + readyInsts[op_class].push(ready_inst); + + // Will need to reorder the list if either a queue is not on the list, + // or it has an older instruction than last time. + if (!queueOnList[op_class]) { + addToOrderList(op_class); + } else if (readyInsts[op_class].top()->seqNum < + (*readyIt[op_class]).oldestInst) { + listOrder.erase(readyIt[op_class]); + addToOrderList(op_class); + } + + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + ready_inst->readPC(), op_class, ready_inst->seqNum); +} + +template <class Impl> +void +InstructionQueue<Impl>::rescheduleMemInst(DynInstPtr &resched_inst) +{ + memDepUnit[resched_inst->threadNumber].reschedule(resched_inst); +} + +template <class Impl> +void +InstructionQueue<Impl>::replayMemInst(DynInstPtr &replay_inst) +{ + memDepUnit[replay_inst->threadNumber].replay(replay_inst); +} + +template <class Impl> +void +InstructionQueue<Impl>::completeMemInst(DynInstPtr &completed_inst) +{ + int tid = completed_inst->threadNumber; + + DPRINTF(IQ, "Completing mem instruction PC:%#x [sn:%lli]\n", + completed_inst->readPC(), completed_inst->seqNum); + + ++freeEntries; + + completed_inst->memOpDone = true; + + memDepUnit[tid].completed(completed_inst); + + count[tid]--; } template <class Impl> @@ -721,58 +979,64 @@ void InstructionQueue<Impl>::violation(DynInstPtr &store, DynInstPtr &faulting_load) { - memDepUnit.violation(store, faulting_load); + memDepUnit[store->threadNumber].violation(store, faulting_load); } template <class Impl> void -InstructionQueue<Impl>::squash() +InstructionQueue<Impl>::squash(unsigned tid) { - DPRINTF(IQ, "IQ: Starting to squash instructions in the IQ.\n"); + DPRINTF(IQ, "[tid:%i]: Starting to squash instructions in " + "the IQ.\n", tid); // Read instruction sequence number of last instruction out of the // time buffer. - squashedSeqNum = fromCommit->commitInfo.doneSeqNum; - - // Setup the squash iterator to point to the tail. - squashIt = tail; + squashedSeqNum[tid] = fromCommit->commitInfo[tid].doneSeqNum; // Call doSquash if there are insts in the IQ - if (freeEntries != numEntries) { - doSquash(); + if (count[tid] > 0) { + doSquash(tid); } // Also tell the memory dependence unit to squash. - memDepUnit.squash(squashedSeqNum); + memDepUnit[tid].squash(squashedSeqNum[tid], tid); } template <class Impl> void -InstructionQueue<Impl>::doSquash() +InstructionQueue<Impl>::doSquash(unsigned tid) { - // Make sure the squash iterator isn't pointing to nothing. - assert(squashIt != cpu->instList.end()); - // Make sure the squashed sequence number is valid. - assert(squashedSeqNum != 0); + // Start at the tail. + ListIt squash_it = instList[tid].end(); + --squash_it; - DPRINTF(IQ, "IQ: Squashing instructions in the IQ.\n"); + DPRINTF(IQ, "[tid:%i]: Squashing until sequence number %i!\n", + tid, squashedSeqNum[tid]); // Squash any instructions younger than the squashed sequence number // given. - while ((*squashIt)->seqNum > squashedSeqNum) { - DynInstPtr squashed_inst = (*squashIt); + while (squash_it != instList[tid].end() && + (*squash_it)->seqNum > squashedSeqNum[tid]) { + + DynInstPtr squashed_inst = (*squash_it); // Only handle the instruction if it actually is in the IQ and // hasn't already been squashed in the IQ. - if (!squashed_inst->isIssued() && - !squashed_inst->isSquashedInIQ()) { + if (squashed_inst->threadNumber != tid || + squashed_inst->isSquashedInIQ()) { + --squash_it; + continue; + } + + if (!squashed_inst->isIssued() || + (squashed_inst->isMemRef() && + !squashed_inst->memOpDone)) { // Remove the instruction from the dependency list. - // Hack for now: These below don't add themselves to the - // dependency list, so don't try to remove them. - if (!squashed_inst->isNonSpeculative()/* && - !squashed_inst->isStore()*/ - ) { + if (!squashed_inst->isNonSpeculative() && + !squashed_inst->isStoreConditional() && + !squashed_inst->isMemBarrier() && + !squashed_inst->isWriteBarrier()) { for (int src_reg_idx = 0; src_reg_idx < squashed_inst->numSrcRegs(); @@ -781,25 +1045,31 @@ InstructionQueue<Impl>::doSquash() PhysRegIndex src_reg = squashed_inst->renamedSrcRegIdx(src_reg_idx); - // Only remove it from the dependency graph if it was - // placed there in the first place. - // HACK: This assumes that instructions woken up from the - // dependency chain aren't informed that a specific src - // register has become ready. This may not always be true - // in the future. + // Only remove it from the dependency graph if it + // was placed there in the first place. + + // Instead of doing a linked list traversal, we + // can just remove these squashed instructions + // either at issue time, or when the register is + // overwritten. The only downside to this is it + // leaves more room for error. + if (!squashed_inst->isReadySrcRegIdx(src_reg_idx) && src_reg < numPhysRegs) { - dependGraph[src_reg].remove(squashed_inst); + dependGraph.remove(src_reg, squashed_inst); } + ++iqSquashedOperandsExamined; } - - // Might want to remove producers as well. } else { - nonSpecInsts[squashed_inst->seqNum] = NULL; + NonSpecMapIt ns_inst_it = + nonSpecInsts.find(squashed_inst->seqNum); + assert(ns_inst_it != nonSpecInsts.end()); - nonSpecInsts.erase(squashed_inst->seqNum); + (*ns_inst_it).second = NULL; + + nonSpecInsts.erase(ns_inst_it); ++iqSquashedNonSpecRemoved; } @@ -809,91 +1079,25 @@ InstructionQueue<Impl>::doSquash() // Mark it as squashed within the IQ. squashed_inst->setSquashedInIQ(); -// squashedInsts.push(squashed_inst); + // @todo: Remove this hack where several statuses are set so the + // inst will flow through the rest of the pipeline. squashed_inst->setIssued(); squashed_inst->setCanCommit(); + squashed_inst->clearInIQ(); + + //Update Thread IQ Count + count[squashed_inst->threadNumber]--; ++freeEntries; - DPRINTF(IQ, "IQ: Instruction PC %#x squashed.\n", - squashed_inst->readPC()); + DPRINTF(IQ, "[tid:%i]: Instruction [sn:%lli] PC %#x " + "squashed.\n", + tid, squashed_inst->seqNum, squashed_inst->readPC()); } - --squashIt; + instList[tid].erase(squash_it--); ++iqSquashedInstsExamined; } - - assert(freeEntries <= numEntries); - - if (freeEntries == numEntries) { - tail = cpu->instList.end(); - } - -} - -template <class Impl> -void -InstructionQueue<Impl>::stopSquash() -{ - // Clear up the squash variables to ensure that squashing doesn't - // get called improperly. - squashedSeqNum = 0; - - squashIt = cpu->instList.end(); -} - -template <class Impl> -void -InstructionQueue<Impl>::DependencyEntry::insert(DynInstPtr &new_inst) -{ - //Add this new, dependent instruction at the head of the dependency - //chain. - - // First create the entry that will be added to the head of the - // dependency chain. - DependencyEntry *new_entry = new DependencyEntry; - new_entry->next = this->next; - new_entry->inst = new_inst; - - // Then actually add it to the chain. - this->next = new_entry; - - ++mem_alloc_counter; -} - -template <class Impl> -void -InstructionQueue<Impl>::DependencyEntry::remove(DynInstPtr &inst_to_remove) -{ - DependencyEntry *prev = this; - DependencyEntry *curr = this->next; - - // Make sure curr isn't NULL. Because this instruction is being - // removed from a dependency list, it must have been placed there at - // an earlier time. The dependency chain should not be empty, - // unless the instruction dependent upon it is already ready. - if (curr == NULL) { - return; - } - - // Find the instruction to remove within the dependency linked list. - while(curr->inst != inst_to_remove) - { - prev = curr; - curr = curr->next; - - assert(curr != NULL); - } - - // Now remove this instruction from the list. - prev->next = curr->next; - - --mem_alloc_counter; - - // Could push this off to the destructor of DependencyEntry - curr->inst = NULL; - - delete curr; } template <class Impl> @@ -920,21 +1124,21 @@ InstructionQueue<Impl>::addToDependents(DynInstPtr &new_inst) if (src_reg >= numPhysRegs) { continue; } else if (regScoreboard[src_reg] == false) { - DPRINTF(IQ, "IQ: Instruction PC %#x has src reg %i that " + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " "is being added to the dependency chain.\n", new_inst->readPC(), src_reg); - dependGraph[src_reg].insert(new_inst); + dependGraph.insert(src_reg, new_inst); // Change the return value to indicate that something // was added to the dependency graph. return_val = true; } else { - DPRINTF(IQ, "IQ: Instruction PC %#x has src reg %i that " + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " "became ready before it reached the IQ.\n", new_inst->readPC(), src_reg); // Mark a register ready within the instruction. - new_inst->markSrcRegReady(); + new_inst->markSrcRegReady(src_reg_idx); } } } @@ -944,12 +1148,12 @@ InstructionQueue<Impl>::addToDependents(DynInstPtr &new_inst) template <class Impl> void -InstructionQueue<Impl>::createDependency(DynInstPtr &new_inst) +InstructionQueue<Impl>::addToProducers(DynInstPtr &new_inst) { - //Actually nothing really needs to be marked when an - //instruction becomes the producer of a register's value, - //but for convenience a ptr to the producing instruction will - //be placed in the head node of the dependency links. + // Nothing really needs to be marked when an instruction becomes + // the producer of a register's value, but for convenience a ptr + // to the producing instruction will be placed in the head node of + // the dependency links. int8_t total_dest_regs = new_inst->numDestRegs(); for (int dest_reg_idx = 0; @@ -966,13 +1170,13 @@ InstructionQueue<Impl>::createDependency(DynInstPtr &new_inst) continue; } - dependGraph[dest_reg].inst = new_inst; - - if (dependGraph[dest_reg].next) { - dumpDependGraph(); - panic("IQ: Dependency graph not empty!"); + if (!dependGraph.empty(dest_reg)) { + dependGraph.dump(); + panic("Dependency graph %i not empty!", dest_reg); } + dependGraph.setInst(dest_reg, new_inst); + // Mark the scoreboard to say it's not yet ready. regScoreboard[dest_reg] = false; } @@ -982,155 +1186,204 @@ template <class Impl> void InstructionQueue<Impl>::addIfReady(DynInstPtr &inst) { - //If the instruction now has all of its source registers + // If the instruction now has all of its source registers // available, then add it to the list of ready instructions. if (inst->readyToIssue()) { //Add the instruction to the proper ready list. - if (inst->isControl()) { + if (inst->isMemRef()) { - DPRINTF(IQ, "IQ: Branch instruction is ready to issue, " - "putting it onto the ready list, PC %#x.\n", - inst->readPC()); - readyBranchInsts.push(inst); - - } else if (inst->isMemRef()) { - - DPRINTF(IQ, "IQ: Checking if memory instruction can issue.\n"); + DPRINTF(IQ, "Checking if memory instruction can issue.\n"); // Message to the mem dependence unit that this instruction has // its registers ready. + memDepUnit[inst->threadNumber].regsReady(inst); - memDepUnit.regsReady(inst); - -#if 0 - if (memDepUnit.readyToIssue(inst)) { - DPRINTF(IQ, "IQ: Memory instruction is ready to issue, " - "putting it onto the ready list, PC %#x.\n", - inst->readPC()); - readyMemInsts.push(inst); - } else { - // Make dependent on the store. - // Will need some way to get the store instruction it should - // be dependent upon; then when the store issues it can - // put the instruction on the ready list. - // Yet another tree? - assert(0 && "Instruction has no way to actually issue"); - } -#endif - - } else if (inst->isInteger()) { - - DPRINTF(IQ, "IQ: Integer instruction is ready to issue, " - "putting it onto the ready list, PC %#x.\n", - inst->readPC()); - readyIntInsts.push(inst); + return; + } - } else if (inst->isFloating()) { + OpClass op_class = inst->opClass(); - DPRINTF(IQ, "IQ: Floating instruction is ready to issue, " - "putting it onto the ready list, PC %#x.\n", - inst->readPC()); - readyFloatInsts.push(inst); + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + inst->readPC(), op_class, inst->seqNum); - } else { - DPRINTF(IQ, "IQ: Miscellaneous instruction is ready to issue, " - "putting it onto the ready list, PC %#x..\n", - inst->readPC()); + readyInsts[op_class].push(inst); - readyMiscInsts.push(inst); + // Will need to reorder the list if either a queue is not on the list, + // or it has an older instruction than last time. + if (!queueOnList[op_class]) { + addToOrderList(op_class); + } else if (readyInsts[op_class].top()->seqNum < + (*readyIt[op_class]).oldestInst) { + listOrder.erase(readyIt[op_class]); + addToOrderList(op_class); } } } -/* - * Caution, this function must not be called prior to tail being updated at - * least once, otherwise it will fail the assertion. This is because - * instList.begin() actually changes upon the insertion of an element into the - * list when the list is empty. - */ template <class Impl> int InstructionQueue<Impl>::countInsts() { - ListIt count_it = cpu->instList.begin(); +#if 0 + //ksewell:This works but definitely could use a cleaner write + //with a more intuitive way of counting. Right now it's + //just brute force .... + // Change the #if if you want to use this method. int total_insts = 0; - if (tail == cpu->instList.end()) - return 0; + for (int i = 0; i < numThreads; ++i) { + ListIt count_it = instList[i].begin(); + + while (count_it != instList[i].end()) { + if (!(*count_it)->isSquashed() && !(*count_it)->isSquashedInIQ()) { + if (!(*count_it)->isIssued()) { + ++total_insts; + } else if ((*count_it)->isMemRef() && + !(*count_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++total_insts; + } + } - while (count_it != tail) { - if (!(*count_it)->isIssued()) { - ++total_insts; + ++count_it; } - - ++count_it; - - assert(count_it != cpu->instList.end()); - } - - // Need to count the tail iterator as well. - if (count_it != cpu->instList.end() && - (*count_it) && - !(*count_it)->isIssued()) { - ++total_insts; } return total_insts; +#else + return numEntries - freeEntries; +#endif } template <class Impl> void -InstructionQueue<Impl>::dumpDependGraph() +InstructionQueue<Impl>::dumpLists() { - DependencyEntry *curr; + for (int i = 0; i < Num_OpClasses; ++i) { + cprintf("Ready list %i size: %i\n", i, readyInsts[i].size()); - for (int i = 0; i < numPhysRegs; ++i) - { - curr = &dependGraph[i]; + cprintf("\n"); + } - if (curr->inst) { - cprintf("dependGraph[%i]: producer: %#x consumer: ", i, - curr->inst->readPC()); - } else { - cprintf("dependGraph[%i]: No producer. consumer: ", i); - } + cprintf("Non speculative list size: %i\n", nonSpecInsts.size()); - while (curr->next != NULL) { - curr = curr->next; + NonSpecMapIt non_spec_it = nonSpecInsts.begin(); + NonSpecMapIt non_spec_end_it = nonSpecInsts.end(); - cprintf("%#x ", curr->inst->readPC()); - } + cprintf("Non speculative list: "); - cprintf("\n"); + while (non_spec_it != non_spec_end_it) { + cprintf("%#x [sn:%lli]", (*non_spec_it).second->readPC(), + (*non_spec_it).second->seqNum); + ++non_spec_it; + } + + cprintf("\n"); + + ListOrderIt list_order_it = listOrder.begin(); + ListOrderIt list_order_end_it = listOrder.end(); + int i = 1; + + cprintf("List order: "); + + while (list_order_it != list_order_end_it) { + cprintf("%i OpClass:%i [sn:%lli] ", i, (*list_order_it).queueType, + (*list_order_it).oldestInst); + + ++list_order_it; + ++i; } + + cprintf("\n"); } + template <class Impl> void -InstructionQueue<Impl>::dumpLists() +InstructionQueue<Impl>::dumpInsts() { - cprintf("Ready integer list size: %i\n", readyIntInsts.size()); + for (int i = 0; i < numThreads; ++i) { + int num = 0; + int valid_num = 0; + ListIt inst_list_it = instList[i].begin(); + + while (inst_list_it != instList[i].end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed + // still count towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); - cprintf("Ready float list size: %i\n", readyFloatInsts.size()); + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } - cprintf("Ready branch list size: %i\n", readyBranchInsts.size()); + cprintf("\n"); - cprintf("Ready misc list size: %i\n", readyMiscInsts.size()); + inst_list_it++; + ++num; + } + } - cprintf("Squashed list size: %i\n", squashedInsts.size()); + cprintf("Insts to Execute list:\n"); - cprintf("Non speculative list size: %i\n", nonSpecInsts.size()); + int num = 0; + int valid_num = 0; + ListIt inst_list_it = instsToExecute.begin(); - non_spec_it_t non_spec_it = nonSpecInsts.begin(); + while (inst_list_it != instsToExecute.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed + // still count towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } - cprintf("Non speculative list: "); + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); - while (non_spec_it != nonSpecInsts.end()) { - cprintf("%#x ", (*non_spec_it).second->readPC()); - ++non_spec_it; - } + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } - cprintf("\n"); + cprintf("\n"); + inst_list_it++; + ++num; + } } diff --git a/src/cpu/o3/isa_specific.hh b/src/cpu/o3/isa_specific.hh new file mode 100755 index 000000000..f8a9dd8cc --- /dev/null +++ b/src/cpu/o3/isa_specific.hh @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell + */ + +#include "cpu/base.hh" + +#if THE_ISA == ALPHA_ISA + #include "cpu/o3/alpha/cpu.hh" + #include "cpu/o3/alpha/impl.hh" + #include "cpu/o3/alpha/params.hh" + #include "cpu/o3/alpha/dyn_inst.hh" +#else + #error "O3CPU doesnt support this ISA" +#endif diff --git a/src/cpu/o3/lsq.cc b/src/cpu/o3/lsq.cc new file mode 100644 index 000000000..527947281 --- /dev/null +++ b/src/cpu/o3/lsq.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/lsq_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class LSQ<O3CPUImpl>; + diff --git a/src/cpu/o3/lsq.hh b/src/cpu/o3/lsq.hh new file mode 100644 index 000000000..89791fec9 --- /dev/null +++ b/src/cpu/o3/lsq.hh @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell + */ + +#ifndef __CPU_O3_LSQ_HH__ +#define __CPU_O3_LSQ_HH__ + +#include <map> +#include <queue> + +#include "config/full_system.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/lsq_unit.hh" +#include "mem/port.hh" +#include "sim/sim_object.hh" + +template <class Impl> +class LSQ { + public: + typedef typename Impl::Params Params; + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::CPUPol::IEW IEW; + typedef typename Impl::CPUPol::LSQUnit LSQUnit; + + /** SMT policy. */ + enum LSQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** Constructs an LSQ with the given parameters. */ + LSQ(Params *params); + + /** Returns the name of the LSQ. */ + std::string name() const; + + /** Registers statistics of each LSQ unit. */ + void regStats(); + + /** Sets the pointer to the list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + /** Sets the CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + /** Sets the IEW stage pointer. */ + void setIEW(IEW *iew_ptr); + /** Switches out the LSQ. */ + void switchOut(); + /** Takes over execution from another CPU's thread. */ + void takeOverFrom(); + + /** Number of entries needed for the given amount of threads.*/ + int entryAmount(int num_threads); + void removeEntries(unsigned tid); + /** Reset the max entries for each thread. */ + void resetEntries(); + /** Resize the max entries for a thread. */ + void resizeEntries(unsigned size, unsigned tid); + + /** Ticks the LSQ. */ + void tick(); + /** Ticks a specific LSQ Unit. */ + void tick(unsigned tid) + { thread[tid].tick(); } + + /** Inserts a load into the LSQ. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store into the LSQ. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load. */ + Fault executeLoad(DynInstPtr &inst); + + /** Executes a store. */ + Fault executeStore(DynInstPtr &inst); + + /** + * Commits loads up until the given sequence number for a specific thread. + */ + void commitLoads(InstSeqNum &youngest_inst, unsigned tid) + { thread[tid].commitLoads(youngest_inst); } + + /** + * Commits stores up until the given sequence number for a specific thread. + */ + void commitStores(InstSeqNum &youngest_inst, unsigned tid) + { thread[tid].commitStores(youngest_inst); } + + /** + * Attempts to write back stores until all cache ports are used or the + * interface becomes blocked. + */ + void writebackStores(); + /** Same as above, but only for one thread. */ + void writebackStores(unsigned tid); + + /** + * Squash instructions from a thread until the specified sequence number. + */ + void squash(const InstSeqNum &squashed_num, unsigned tid) + { thread[tid].squash(squashed_num); } + + /** Returns whether or not there was a memory ordering violation. */ + bool violation(); + /** + * Returns whether or not there was a memory ordering violation for a + * specific thread. + */ + bool violation(unsigned tid) + { return thread[tid].violation(); } + + /** Returns if a load is blocked due to the memory system for a specific + * thread. + */ + bool loadBlocked(unsigned tid) + { return thread[tid].loadBlocked(); } + + bool isLoadBlockedHandled(unsigned tid) + { return thread[tid].isLoadBlockedHandled(); } + + void setLoadBlockedHandled(unsigned tid) + { thread[tid].setLoadBlockedHandled(); } + + /** Gets the instruction that caused the memory ordering violation. */ + DynInstPtr getMemDepViolator(unsigned tid) + { return thread[tid].getMemDepViolator(); } + + /** Returns the head index of the load queue for a specific thread. */ + int getLoadHead(unsigned tid) + { return thread[tid].getLoadHead(); } + + /** Returns the sequence number of the head of the load queue. */ + InstSeqNum getLoadHeadSeqNum(unsigned tid) + { + return thread[tid].getLoadHeadSeqNum(); + } + + /** Returns the head index of the store queue. */ + int getStoreHead(unsigned tid) + { return thread[tid].getStoreHead(); } + + /** Returns the sequence number of the head of the store queue. */ + InstSeqNum getStoreHeadSeqNum(unsigned tid) + { + return thread[tid].getStoreHeadSeqNum(); + } + + /** Returns the number of instructions in all of the queues. */ + int getCount(); + /** Returns the number of instructions in the queues of one thread. */ + int getCount(unsigned tid) + { return thread[tid].getCount(); } + + /** Returns the total number of loads in the load queue. */ + int numLoads(); + /** Returns the total number of loads for a single thread. */ + int numLoads(unsigned tid) + { return thread[tid].numLoads(); } + + /** Returns the total number of stores in the store queue. */ + int numStores(); + /** Returns the total number of stores for a single thread. */ + int numStores(unsigned tid) + { return thread[tid].numStores(); } + + /** Returns the total number of loads that are ready. */ + int numLoadsReady(); + /** Returns the number of loads that are ready for a single thread. */ + int numLoadsReady(unsigned tid) + { return thread[tid].numLoadsReady(); } + + /** Returns the number of free entries. */ + unsigned numFreeEntries(); + /** Returns the number of free entries for a specific thread. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns if the LSQ is full (either LQ or SQ is full). */ + bool isFull(); + /** + * Returns if the LSQ is full for a specific thread (either LQ or SQ is + * full). + */ + bool isFull(unsigned tid); + + /** Returns if any of the LQs are full. */ + bool lqFull(); + /** Returns if the LQ of a given thread is full. */ + bool lqFull(unsigned tid); + + /** Returns if any of the SQs are full. */ + bool sqFull(); + /** Returns if the SQ of a given thread is full. */ + bool sqFull(unsigned tid); + + /** + * Returns if the LSQ is stalled due to a memory operation that must be + * replayed. + */ + bool isStalled(); + /** + * Returns if the LSQ of a specific thread is stalled due to a memory + * operation that must be replayed. + */ + bool isStalled(unsigned tid); + + /** Returns whether or not there are any stores to write back to memory. */ + bool hasStoresToWB(); + + /** Returns whether or not a specific thread has any stores to write back + * to memory. + */ + bool hasStoresToWB(unsigned tid) + { return thread[tid].hasStoresToWB(); } + + /** Returns the number of stores a specific thread has to write back. */ + int numStoresToWB(unsigned tid) + { return thread[tid].numStoresToWB(); } + + /** Returns if the LSQ will write back to memory this cycle. */ + bool willWB(); + /** Returns if the LSQ of a specific thread will write back to memory this + * cycle. + */ + bool willWB(unsigned tid) + { return thread[tid].willWB(); } + + /** Debugging function to print out all instructions. */ + void dumpInsts(); + /** Debugging function to print out instructions from a specific thread. */ + void dumpInsts(unsigned tid) + { thread[tid].dumpInsts(); } + + /** Executes a read operation, using the load specified at the load index. */ + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + /** Executes a store operation, using the store specified at the store + * index. + */ + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + private: + /** The LSQ policy for SMT mode. */ + LSQPolicy lsqPolicy; + + /** The LSQ units for individual threads. */ + LSQUnit thread[Impl::MaxThreads]; + + /** The CPU pointer. */ + O3CPU *cpu; + + /** The IEW stage pointer. */ + IEW *iewStage; + + /** List of Active Threads in System. */ + std::list<unsigned> *activeThreads; + + /** Total Size of LQ Entries. */ + unsigned LQEntries; + /** Total Size of SQ Entries. */ + unsigned SQEntries; + + /** Max LQ Size - Used to Enforce Sharing Policies. */ + unsigned maxLQEntries; + + /** Max SQ Size - Used to Enforce Sharing Policies. */ + unsigned maxSQEntries; + + /** Number of Threads. */ + unsigned numThreads; +}; + +template <class Impl> +template <class T> +Fault +LSQ<Impl>::read(RequestPtr req, T &data, int load_idx) +{ + unsigned tid = req->getThreadNum(); + + return thread[tid].read(req, data, load_idx); +} + +template <class Impl> +template <class T> +Fault +LSQ<Impl>::write(RequestPtr req, T &data, int store_idx) +{ + unsigned tid = req->getThreadNum(); + + return thread[tid].write(req, data, store_idx); +} + +#endif // __CPU_O3_LSQ_HH__ diff --git a/src/cpu/o3/lsq_impl.hh b/src/cpu/o3/lsq_impl.hh new file mode 100644 index 000000000..5173f8be1 --- /dev/null +++ b/src/cpu/o3/lsq_impl.hh @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell + */ + +#include <algorithm> +#include <string> + +#include "cpu/o3/lsq.hh" + +using namespace std; + +template <class Impl> +LSQ<Impl>::LSQ(Params *params) + : LQEntries(params->LQEntries), SQEntries(params->SQEntries), + numThreads(params->numberOfThreads) +{ + DPRINTF(LSQ, "Creating LSQ object.\n"); + + //**********************************************/ + //************ Handle SMT Parameters ***********/ + //**********************************************/ + string policy = params->smtLSQPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out fetch policy + if (policy == "dynamic") { + lsqPolicy = Dynamic; + + maxLQEntries = LQEntries; + maxSQEntries = SQEntries; + + DPRINTF(LSQ, "LSQ sharing policy set to Dynamic\n"); + + } else if (policy == "partitioned") { + lsqPolicy = Partitioned; + + //@todo:make work if part_amt doesnt divide evenly. + maxLQEntries = LQEntries / numThreads; + maxSQEntries = SQEntries / numThreads; + + DPRINTF(Fetch, "LSQ sharing policy set to Partitioned: " + "%i entries per LQ | %i entries per SQ", + maxLQEntries,maxSQEntries); + + } else if (policy == "threshold") { + lsqPolicy = Threshold; + + assert(params->smtLSQThreshold > LQEntries); + assert(params->smtLSQThreshold > SQEntries); + + //Divide up by threshold amount + //@todo: Should threads check the max and the total + //amount of the LSQ + maxLQEntries = params->smtLSQThreshold; + maxSQEntries = params->smtLSQThreshold; + + DPRINTF(LSQ, "LSQ sharing policy set to Threshold: " + "%i entries per LQ | %i entries per SQ", + maxLQEntries,maxSQEntries); + + } else { + assert(0 && "Invalid LSQ Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } + + //Initialize LSQs + for (int tid=0; tid < numThreads; tid++) { + thread[tid].init(params, maxLQEntries, maxSQEntries, tid); + } +} + + +template<class Impl> +std::string +LSQ<Impl>::name() const +{ + return iewStage->name() + ".lsq"; +} + +template<class Impl> +void +LSQ<Impl>::regStats() +{ + //Initialize LSQs + for (int tid=0; tid < numThreads; tid++) { + thread[tid].regStats(); + } +} + +template<class Impl> +void +LSQ<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + activeThreads = at_ptr; + assert(activeThreads != 0); +} + +template<class Impl> +void +LSQ<Impl>::setCPU(O3CPU *cpu_ptr) +{ + cpu = cpu_ptr; + + for (int tid=0; tid < numThreads; tid++) { + thread[tid].setCPU(cpu_ptr); + } +} + +template<class Impl> +void +LSQ<Impl>::setIEW(IEW *iew_ptr) +{ + iewStage = iew_ptr; + + for (int tid=0; tid < numThreads; tid++) { + thread[tid].setIEW(iew_ptr); + } +} + +template <class Impl> +void +LSQ<Impl>::switchOut() +{ + for (int tid = 0; tid < numThreads; tid++) { + thread[tid].switchOut(); + } +} + +template <class Impl> +void +LSQ<Impl>::takeOverFrom() +{ + for (int tid = 0; tid < numThreads; tid++) { + thread[tid].takeOverFrom(); + } +} + +template <class Impl> +int +LSQ<Impl>::entryAmount(int num_threads) +{ + if (lsqPolicy == Partitioned) { + return LQEntries / num_threads; + } else { + return 0; + } +} + +template <class Impl> +void +LSQ<Impl>::resetEntries() +{ + if (lsqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + list<unsigned>::iterator list_end = (*activeThreads).end(); + + int maxEntries; + + if (lsqPolicy == Partitioned) { + maxEntries = LQEntries / active_threads; + } else if (lsqPolicy == Threshold && active_threads == 1) { + maxEntries = LQEntries; + } else { + maxEntries = LQEntries; + } + + while (threads != list_end) { + resizeEntries(maxEntries,*threads++); + } + } +} + +template<class Impl> +void +LSQ<Impl>::removeEntries(unsigned tid) +{ + thread[tid].clearLQ(); + thread[tid].clearSQ(); +} + +template<class Impl> +void +LSQ<Impl>::resizeEntries(unsigned size,unsigned tid) +{ + thread[tid].resizeLQ(size); + thread[tid].resizeSQ(size); +} + +template<class Impl> +void +LSQ<Impl>::tick() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + + thread[tid].tick(); + } +} + +template<class Impl> +void +LSQ<Impl>::insertLoad(DynInstPtr &load_inst) +{ + unsigned tid = load_inst->threadNumber; + + thread[tid].insertLoad(load_inst); +} + +template<class Impl> +void +LSQ<Impl>::insertStore(DynInstPtr &store_inst) +{ + unsigned tid = store_inst->threadNumber; + + thread[tid].insertStore(store_inst); +} + +template<class Impl> +Fault +LSQ<Impl>::executeLoad(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + return thread[tid].executeLoad(inst); +} + +template<class Impl> +Fault +LSQ<Impl>::executeStore(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + return thread[tid].executeStore(inst); +} + +template<class Impl> +void +LSQ<Impl>::writebackStores() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + + if (numStoresToWB(tid) > 0) { + DPRINTF(Writeback,"[tid:%i] Writing back stores. %i stores " + "available for Writeback.\n", tid, numStoresToWB(tid)); + } + + thread[tid].writebackStores(); + } +} + +template<class Impl> +bool +LSQ<Impl>::violation() +{ + /* Answers: Does Anybody Have a Violation?*/ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (thread[tid].violation()) + return true; + } + + return false; +} + +template<class Impl> +int +LSQ<Impl>::getCount() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += getCount(tid); + } + + return total; +} + +template<class Impl> +int +LSQ<Impl>::numLoads() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += numLoads(tid); + } + + return total; +} + +template<class Impl> +int +LSQ<Impl>::numStores() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numStores(); + } + + return total; +} + +template<class Impl> +int +LSQ<Impl>::numLoadsReady() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numLoadsReady(); + } + + return total; +} + +template<class Impl> +unsigned +LSQ<Impl>::numFreeEntries() +{ + unsigned total = 0; + + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numFreeEntries(); + } + + return total; +} + +template<class Impl> +unsigned +LSQ<Impl>::numFreeEntries(unsigned tid) +{ + //if( lsqPolicy == Dynamic ) + //return numFreeEntries(); + //else + return thread[tid].numFreeEntries(); +} + +template<class Impl> +bool +LSQ<Impl>::isFull() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (! (thread[tid].lqFull() || thread[tid].sqFull()) ) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::isFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return isFull(); + else + return thread[tid].lqFull() || thread[tid].sqFull(); +} + +template<class Impl> +bool +LSQ<Impl>::lqFull() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!thread[tid].lqFull()) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::lqFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return lqFull(); + else + return thread[tid].lqFull(); +} + +template<class Impl> +bool +LSQ<Impl>::sqFull() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!sqFull(tid)) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::sqFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return sqFull(); + else + return thread[tid].sqFull(); +} + +template<class Impl> +bool +LSQ<Impl>::isStalled() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!thread[tid].isStalled()) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::isStalled(unsigned tid) +{ + if( lsqPolicy == Dynamic ) + return isStalled(); + else + return thread[tid].isStalled(); +} + +template<class Impl> +bool +LSQ<Impl>::hasStoresToWB() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!hasStoresToWB(tid)) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::willWB() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!willWB(tid)) + return false; + } + + return true; +} + +template<class Impl> +void +LSQ<Impl>::dumpInsts() +{ + list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + thread[tid].dumpInsts(); + } +} diff --git a/src/cpu/o3/lsq_unit.cc b/src/cpu/o3/lsq_unit.cc new file mode 100644 index 000000000..3ca3fa667 --- /dev/null +++ b/src/cpu/o3/lsq_unit.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/lsq_unit_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class LSQUnit<O3CPUImpl>; + diff --git a/src/cpu/o3/lsq_unit.hh b/src/cpu/o3/lsq_unit.hh new file mode 100644 index 000000000..74b8fe5bb --- /dev/null +++ b/src/cpu/o3/lsq_unit.hh @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_LSQ_UNIT_HH__ +#define __CPU_O3_LSQ_UNIT_HH__ + +#include <algorithm> +#include <map> +#include <queue> + +#include "arch/faults.hh" +#include "config/full_system.hh" +#include "base/hashmap.hh" +#include "cpu/inst_seq.hh" +#include "mem/packet_impl.hh" +#include "mem/port.hh" + +/** + * Class that implements the actual LQ and SQ for each specific + * thread. Both are circular queues; load entries are freed upon + * committing, while store entries are freed once they writeback. The + * LSQUnit tracks if there are memory ordering violations, and also + * detects partial load to store forwarding cases (a store only has + * part of a load's data) that requires the load to wait until the + * store writes back. In the former case it holds onto the instruction + * until the dependence unit looks at it, and in the latter it stalls + * the LSQ until the store writes back. At that point the load is + * replayed. + */ +template <class Impl> +class LSQUnit { + protected: + typedef TheISA::IntReg IntReg; + public: + typedef typename Impl::Params Params; + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::CPUPol::IEW IEW; + typedef typename Impl::CPUPol::IssueStruct IssueStruct; + + public: + /** Constructs an LSQ unit. init() must be called prior to use. */ + LSQUnit(); + + /** Initializes the LSQ unit with the specified number of entries. */ + void init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id); + + /** Returns the name of the LSQ unit. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Sets the CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets the IEW stage pointer. */ + void setIEW(IEW *iew_ptr) + { iewStage = iew_ptr; } + + /** Switches out LSQ unit. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Returns if the LSQ is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** Ticks the LSQ unit, which in this case only resets the number of + * used cache ports. + * @todo: Move the number of used ports up to the LSQ level so it can + * be shared by all LSQ units. + */ + void tick() { usedPorts = 0; } + + /** Inserts an instruction. */ + void insert(DynInstPtr &inst); + /** Inserts a load instruction. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store instruction. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load instruction. */ + Fault executeLoad(DynInstPtr &inst); + + Fault executeLoad(int lq_idx) { panic("Not implemented"); return NoFault; } + /** Executes a store instruction. */ + Fault executeStore(DynInstPtr &inst); + + /** Commits the head load. */ + void commitLoad(); + /** Commits loads older than a specific sequence number. */ + void commitLoads(InstSeqNum &youngest_inst); + + /** Commits stores older than a specific sequence number. */ + void commitStores(InstSeqNum &youngest_inst); + + /** Writes back stores. */ + void writebackStores(); + + /** Completes the data access that has been returned from the + * memory system. */ + void completeDataAccess(PacketPtr pkt); + + /** Clears all the entries in the LQ. */ + void clearLQ(); + + /** Clears all the entries in the SQ. */ + void clearSQ(); + + /** Resizes the LQ to a given size. */ + void resizeLQ(unsigned size); + + /** Resizes the SQ to a given size. */ + void resizeSQ(unsigned size); + + /** Squashes all instructions younger than a specific sequence number. */ + void squash(const InstSeqNum &squashed_num); + + /** Returns if there is a memory ordering violation. Value is reset upon + * call to getMemDepViolator(). + */ + bool violation() { return memDepViolator; } + + /** Returns the memory ordering violator. */ + DynInstPtr getMemDepViolator(); + + /** Returns if a load became blocked due to the memory system. */ + bool loadBlocked() + { return isLoadBlocked; } + + /** Clears the signal that a load became blocked. */ + void clearLoadBlocked() + { isLoadBlocked = false; } + + /** Returns if the blocked load was handled. */ + bool isLoadBlockedHandled() + { return loadBlockedHandled; } + + /** Records the blocked load as being handled. */ + void setLoadBlockedHandled() + { loadBlockedHandled = true; } + + /** Returns the number of free entries (min of free LQ and SQ entries). */ + unsigned numFreeEntries(); + + /** Returns the number of loads ready to execute. */ + int numLoadsReady(); + + /** Returns the number of loads in the LQ. */ + int numLoads() { return loads; } + + /** Returns the number of stores in the SQ. */ + int numStores() { return stores; } + + /** Returns if either the LQ or SQ is full. */ + bool isFull() { return lqFull() || sqFull(); } + + /** Returns if the LQ is full. */ + bool lqFull() { return loads >= (LQEntries - 1); } + + /** Returns if the SQ is full. */ + bool sqFull() { return stores >= (SQEntries - 1); } + + /** Returns the number of instructions in the LSQ. */ + unsigned getCount() { return loads + stores; } + + /** Returns if there are any stores to writeback. */ + bool hasStoresToWB() { return storesToWB; } + + /** Returns the number of stores to writeback. */ + int numStoresToWB() { return storesToWB; } + + /** Returns if the LSQ unit will writeback on this cycle. */ + bool willWB() { return storeQueue[storeWBIdx].canWB && + !storeQueue[storeWBIdx].completed && + !isStoreBlocked; } + + private: + /** Writes back the instruction, sending it to IEW. */ + void writeback(DynInstPtr &inst, PacketPtr pkt); + + /** Handles completing the send of a store to memory. */ + void storePostSend(Packet *pkt); + + /** Completes the store at the specified index. */ + void completeStore(int store_idx); + + /** Handles doing the retry. */ + void recvRetry(); + + /** Increments the given store index (circular queue). */ + inline void incrStIdx(int &store_idx); + /** Decrements the given store index (circular queue). */ + inline void decrStIdx(int &store_idx); + /** Increments the given load index (circular queue). */ + inline void incrLdIdx(int &load_idx); + /** Decrements the given load index (circular queue). */ + inline void decrLdIdx(int &load_idx); + + public: + /** Debugging function to dump instructions in the LSQ. */ + void dumpInsts(); + + private: + /** Pointer to the CPU. */ + O3CPU *cpu; + + /** Pointer to the IEW stage. */ + IEW *iewStage; + + /** Pointer to memory object. */ + MemObject *mem; + + /** DcachePort class for this LSQ Unit. Handles doing the + * communication with the cache/memory. + * @todo: Needs to be moved to the LSQ level and have some sort + * of arbitration. + */ + class DcachePort : public Port + { + protected: + /** Pointer to CPU. */ + O3CPU *cpu; + /** Pointer to LSQ. */ + LSQUnit *lsq; + + public: + /** Default constructor. */ + DcachePort(O3CPU *_cpu, LSQUnit *_lsq) + : Port(_lsq->name() + "-dport"), cpu(_cpu), lsq(_lsq) + { } + + protected: + /** Atomic version of receive. Panics. */ + virtual Tick recvAtomic(PacketPtr pkt); + + /** Functional version of receive. Panics. */ + virtual void recvFunctional(PacketPtr pkt); + + /** Receives status change. Other than range changing, panics. */ + virtual void recvStatusChange(Status status); + + /** Returns the address ranges of this device. */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + /** Timing version of receive. Handles writing back and + * completing the load or store that has returned from + * memory. */ + virtual bool recvTiming(PacketPtr pkt); + + /** Handles doing a retry of the previous send. */ + virtual void recvRetry(); + }; + + /** Pointer to the D-cache. */ + DcachePort *dcachePort; + + /** Derived class to hold any sender state the LSQ needs. */ + class LSQSenderState : public Packet::SenderState + { + public: + /** Default constructor. */ + LSQSenderState() + : noWB(false) + { } + + /** Instruction who initiated the access to memory. */ + DynInstPtr inst; + /** Whether or not it is a load. */ + bool isLoad; + /** The LQ/SQ index of the instruction. */ + int idx; + /** Whether or not the instruction will need to writeback. */ + bool noWB; + }; + + /** Writeback event, specifically for when stores forward data to loads. */ + class WritebackEvent : public Event { + public: + /** Constructs a writeback event. */ + WritebackEvent(DynInstPtr &_inst, PacketPtr pkt, LSQUnit *lsq_ptr); + + /** Processes the writeback event. */ + void process(); + + /** Returns the description of this event. */ + const char *description(); + + private: + /** Instruction whose results are being written back. */ + DynInstPtr inst; + + /** The packet that would have been sent to memory. */ + PacketPtr pkt; + + /** The pointer to the LSQ unit that issued the store. */ + LSQUnit<Impl> *lsqPtr; + }; + + public: + struct SQEntry { + /** Constructs an empty store queue entry. */ + SQEntry() + : inst(NULL), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** Constructs a store queue entry for a given instruction. */ + SQEntry(DynInstPtr &_inst) + : inst(_inst), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** The store instruction. */ + DynInstPtr inst; + /** The request for the store. */ + RequestPtr req; + /** The size of the store. */ + int size; + /** The store data. */ + IntReg data; + /** Whether or not the store can writeback. */ + bool canWB; + /** Whether or not the store is committed. */ + bool committed; + /** Whether or not the store is completed. */ + bool completed; + }; + + private: + /** The LSQUnit thread id. */ + unsigned lsqID; + + /** The store queue. */ + std::vector<SQEntry> storeQueue; + + /** The load queue. */ + std::vector<DynInstPtr> loadQueue; + + /** The number of LQ entries, plus a sentinel entry (circular queue). + * @todo: Consider having var that records the true number of LQ entries. + */ + unsigned LQEntries; + /** The number of SQ entries, plus a sentinel entry (circular queue). + * @todo: Consider having var that records the true number of SQ entries. + */ + unsigned SQEntries; + + /** The number of load instructions in the LQ. */ + int loads; + /** The number of store instructions in the SQ. */ + int stores; + /** The number of store instructions in the SQ waiting to writeback. */ + int storesToWB; + + /** The index of the head instruction in the LQ. */ + int loadHead; + /** The index of the tail instruction in the LQ. */ + int loadTail; + + /** The index of the head instruction in the SQ. */ + int storeHead; + /** The index of the first instruction that may be ready to be + * written back, and has not yet been written back. + */ + int storeWBIdx; + /** The index of the tail instruction in the SQ. */ + int storeTail; + + /// @todo Consider moving to a more advanced model with write vs read ports + /** The number of cache ports available each cycle. */ + int cachePorts; + + /** The number of used cache ports in this cycle. */ + int usedPorts; + + /** Is the LSQ switched out. */ + bool switchedOut; + + //list<InstSeqNum> mshrSeqNums; + + /** Wire to read information from the issue stage time queue. */ + typename TimeBuffer<IssueStruct>::wire fromIssue; + + /** Whether or not the LSQ is stalled. */ + bool stalled; + /** The store that causes the stall due to partial store to load + * forwarding. + */ + InstSeqNum stallingStoreIsn; + /** The index of the above store. */ + int stallingLoadIdx; + + /** The packet that needs to be retried. */ + PacketPtr retryPkt; + + /** Whehter or not a store is blocked due to the memory system. */ + bool isStoreBlocked; + + /** Whether or not a load is blocked due to the memory system. */ + bool isLoadBlocked; + + /** Has the blocked load been handled. */ + bool loadBlockedHandled; + + /** The sequence number of the blocked load. */ + InstSeqNum blockedLoadSeqNum; + + /** The oldest load that caused a memory ordering violation. */ + DynInstPtr memDepViolator; + + // Will also need how many read/write ports the Dcache has. Or keep track + // of that in stage that is one level up, and only call executeLoad/Store + // the appropriate number of times. + + /** Total number of loads forwaded from LSQ stores. */ + Stats::Scalar<> lsqForwLoads; + + /** Total number of loads ignored due to invalid addresses. */ + Stats::Scalar<> invAddrLoads; + + /** Total number of squashed loads. */ + Stats::Scalar<> lsqSquashedLoads; + + /** Total number of responses from the memory system that are + * ignored due to the instruction already being squashed. */ + Stats::Scalar<> lsqIgnoredResponses; + + /** Total number of squashed stores. */ + Stats::Scalar<> lsqSquashedStores; + + /** Total number of software prefetches ignored due to invalid addresses. */ + Stats::Scalar<> invAddrSwpfs; + + /** Ready loads blocked due to partial store-forwarding. */ + Stats::Scalar<> lsqBlockedLoads; + + /** Number of loads that were rescheduled. */ + Stats::Scalar<> lsqRescheduledLoads; + + /** Number of times the LSQ is blocked due to the cache. */ + Stats::Scalar<> lsqCacheBlocked; + + public: + /** Executes the load at the given index. */ + template <class T> + Fault read(Request *req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template <class T> + Fault write(Request *req, T &data, int store_idx); + + /** Returns the index of the head load instruction. */ + int getLoadHead() { return loadHead; } + /** Returns the sequence number of the head load instruction. */ + InstSeqNum getLoadHeadSeqNum() + { + if (loadQueue[loadHead]) { + return loadQueue[loadHead]->seqNum; + } else { + return 0; + } + + } + + /** Returns the index of the head store instruction. */ + int getStoreHead() { return storeHead; } + /** Returns the sequence number of the head store instruction. */ + InstSeqNum getStoreHeadSeqNum() + { + if (storeQueue[storeHead].inst) { + return storeQueue[storeHead].inst->seqNum; + } else { + return 0; + } + + } + + /** Returns whether or not the LSQ unit is stalled. */ + bool isStalled() { return stalled; } +}; + +template <class Impl> +template <class T> +Fault +LSQUnit<Impl>::read(Request *req, T &data, int load_idx) +{ + DynInstPtr load_inst = loadQueue[load_idx]; + + assert(load_inst); + + assert(!load_inst->isExecuted()); + + // Make sure this isn't an uncacheable access + // A bit of a hackish way to get uncached accesses to work only if they're + // at the head of the LSQ and are ready to commit (at the head of the ROB + // too). + if (req->getFlags() & UNCACHEABLE && + (load_idx != loadHead || !load_inst->isAtCommit())) { + iewStage->rescheduleMemInst(load_inst); + ++lsqRescheduledLoads; + return TheISA::genMachineCheckFault(); + } + + // Check the SQ for any previous stores that might lead to forwarding + int store_idx = load_inst->sqIdx; + + int store_size = 0; + + DPRINTF(LSQUnit, "Read called, load idx: %i, store idx: %i, " + "storeHead: %i addr: %#x\n", + load_idx, store_idx, storeHead, req->getPaddr()); + +#if FULL_SYSTEM + if (req->getFlags() & LOCKED) { + cpu->lockAddr = req->getPaddr(); + cpu->lockFlag = true; + } +#endif + + while (store_idx != -1) { + // End once we've reached the top of the LSQ + if (store_idx == storeWBIdx) { + break; + } + + // Move the index to one younger + if (--store_idx < 0) + store_idx += SQEntries; + + assert(storeQueue[store_idx].inst); + + store_size = storeQueue[store_idx].size; + + if (store_size == 0) + continue; + + // Check if the store data is within the lower and upper bounds of + // addresses that the request needs. + bool store_has_lower_limit = + req->getVaddr() >= storeQueue[store_idx].inst->effAddr; + bool store_has_upper_limit = + (req->getVaddr() + req->getSize()) <= + (storeQueue[store_idx].inst->effAddr + store_size); + bool lower_load_has_store_part = + req->getVaddr() < (storeQueue[store_idx].inst->effAddr + + store_size); + bool upper_load_has_store_part = + (req->getVaddr() + req->getSize()) > + storeQueue[store_idx].inst->effAddr; + + // If the store's data has all of the data needed, we can forward. + if (store_has_lower_limit && store_has_upper_limit) { + // Get shift amount for offset into the store's data. + int shift_amt = req->getVaddr() & (store_size - 1); + // @todo: Magic number, assumes byte addressing + shift_amt = shift_amt << 3; + + // Cast this to type T? + data = storeQueue[store_idx].data >> shift_amt; + + assert(!load_inst->memData); + load_inst->memData = new uint8_t[64]; + + memcpy(load_inst->memData, &data, req->getSize()); + + DPRINTF(LSQUnit, "Forwarding from store idx %i to load to " + "addr %#x, data %#x\n", + store_idx, req->getVaddr(), data); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(load_inst->memData); + + WritebackEvent *wb = new WritebackEvent(load_inst, data_pkt, this); + + // We'll say this has a 1 cycle load-store forwarding latency + // for now. + // @todo: Need to make this a parameter. + wb->schedule(curTick); + + ++lsqForwLoads; + return NoFault; + } else if ((store_has_lower_limit && lower_load_has_store_part) || + (store_has_upper_limit && upper_load_has_store_part) || + (lower_load_has_store_part && upper_load_has_store_part)) { + // This is the partial store-load forwarding case where a store + // has only part of the load's data. + + // If it's already been written back, then don't worry about + // stalling on it. + if (storeQueue[store_idx].completed) { + continue; + } + + // Must stall load and force it to retry, so long as it's the oldest + // load that needs to do so. + if (!stalled || + (stalled && + load_inst->seqNum < + loadQueue[stallingLoadIdx]->seqNum)) { + stalled = true; + stallingStoreIsn = storeQueue[store_idx].inst->seqNum; + stallingLoadIdx = load_idx; + } + + // Tell IQ/mem dep unit that this instruction will need to be + // rescheduled eventually + iewStage->rescheduleMemInst(load_inst); + ++lsqRescheduledLoads; + + // Do not generate a writeback event as this instruction is not + // complete. + DPRINTF(LSQUnit, "Load-store forwarding mis-match. " + "Store idx %i to load addr %#x\n", + store_idx, req->getVaddr()); + + ++lsqBlockedLoads; + return NoFault; + } + } + + // If there's no forwarding case, then go access memory + DPRINTF(LSQUnit, "Doing functional access for inst [sn:%lli] PC %#x\n", + load_inst->seqNum, load_inst->readPC()); + + assert(!load_inst->memData); + load_inst->memData = new uint8_t[64]; + + ++usedPorts; + + DPRINTF(LSQUnit, "Doing timing access for inst PC %#x\n", + load_inst->readPC()); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(load_inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = true; + state->idx = load_idx; + state->inst = load_inst; + data_pkt->senderState = state; + + // if we have a cache, do cache access too + if (!dcachePort->sendTiming(data_pkt)) { + ++lsqCacheBlocked; + // There's an older load that's already going to squash. + if (isLoadBlocked && blockedLoadSeqNum < load_inst->seqNum) + return NoFault; + + // Record that the load was blocked due to memory. This + // load will squash all instructions after it, be + // refetched, and re-executed. + isLoadBlocked = true; + loadBlockedHandled = false; + blockedLoadSeqNum = load_inst->seqNum; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + + if (data_pkt->result != Packet::Success) { + DPRINTF(LSQUnit, "LSQUnit: D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + load_inst->seqNum); + } else { + DPRINTF(LSQUnit, "LSQUnit: D-cache hit!\n"); + DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", + load_inst->seqNum); + } + + return NoFault; +} + +template <class Impl> +template <class T> +Fault +LSQUnit<Impl>::write(Request *req, T &data, int store_idx) +{ + assert(storeQueue[store_idx].inst); + + DPRINTF(LSQUnit, "Doing write to store idx %i, addr %#x data %#x" + " | storeHead:%i [sn:%i]\n", + store_idx, req->getPaddr(), data, storeHead, + storeQueue[store_idx].inst->seqNum); + + storeQueue[store_idx].req = req; + storeQueue[store_idx].size = sizeof(T); + storeQueue[store_idx].data = data; + + // This function only writes the data to the store queue, so no fault + // can happen here. + return NoFault; +} + +#endif // __CPU_O3_LSQ_UNIT_HH__ diff --git a/src/cpu/o3/lsq_unit_impl.hh b/src/cpu/o3/lsq_unit_impl.hh new file mode 100644 index 000000000..714acb2ef --- /dev/null +++ b/src/cpu/o3/lsq_unit_impl.hh @@ -0,0 +1,986 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "config/use_checker.hh" + +#include "cpu/o3/lsq_unit.hh" +#include "base/str.hh" +#include "mem/packet.hh" +#include "mem/request.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + +template<class Impl> +LSQUnit<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, PacketPtr _pkt, + LSQUnit *lsq_ptr) + : Event(&mainEventQueue), inst(_inst), pkt(_pkt), lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template<class Impl> +void +LSQUnit<Impl>::WritebackEvent::process() +{ + if (!lsqPtr->isSwitchedOut()) { + lsqPtr->writeback(inst, pkt); + } + delete pkt; +} + +template<class Impl> +const char * +LSQUnit<Impl>::WritebackEvent::description() +{ + return "Store writeback event"; +} + +template<class Impl> +void +LSQUnit<Impl>::completeDataAccess(PacketPtr pkt) +{ + LSQSenderState *state = dynamic_cast<LSQSenderState *>(pkt->senderState); + DynInstPtr inst = state->inst; + DPRINTF(IEW, "Writeback event [sn:%lli]\n", inst->seqNum); + DPRINTF(Activity, "Activity: Writeback event [sn:%lli]\n", inst->seqNum); + + //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); + + if (isSwitchedOut() || inst->isSquashed()) { + delete state; + delete pkt; + return; + } else { + if (!state->noWB) { + writeback(inst, pkt); + } + + if (inst->isStore()) { + completeStore(state->idx); + } + } + + delete state; + delete pkt; +} + +template <class Impl> +Tick +LSQUnit<Impl>::DcachePort::recvAtomic(PacketPtr pkt) +{ + panic("O3CPU model does not work with atomic mode!"); + return curTick; +} + +template <class Impl> +void +LSQUnit<Impl>::DcachePort::recvFunctional(PacketPtr pkt) +{ + panic("O3CPU doesn't expect recvFunctional callback!"); +} + +template <class Impl> +void +LSQUnit<Impl>::DcachePort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("O3CPU doesn't expect recvStatusChange callback!"); +} + +template <class Impl> +bool +LSQUnit<Impl>::DcachePort::recvTiming(PacketPtr pkt) +{ + lsq->completeDataAccess(pkt); + return true; +} + +template <class Impl> +void +LSQUnit<Impl>::DcachePort::recvRetry() +{ + lsq->recvRetry(); +} + +template <class Impl> +LSQUnit<Impl>::LSQUnit() + : loads(0), stores(0), storesToWB(0), stalled(false), + isStoreBlocked(false), isLoadBlocked(false), + loadBlockedHandled(false) +{ +} + +template<class Impl> +void +LSQUnit<Impl>::init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) +{ + DPRINTF(LSQUnit, "Creating LSQUnit%i object.\n",id); + + switchedOut = false; + + lsqID = id; + + // Add 1 for the sentinel entry (they are circular queues). + LQEntries = maxLQEntries + 1; + SQEntries = maxSQEntries + 1; + + loadQueue.resize(LQEntries); + storeQueue.resize(SQEntries); + + loadHead = loadTail = 0; + + storeHead = storeWBIdx = storeTail = 0; + + usedPorts = 0; + cachePorts = params->cachePorts; + + mem = params->mem; + + memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} + +template<class Impl> +void +LSQUnit<Impl>::setCPU(O3CPU *cpu_ptr) +{ + cpu = cpu_ptr; + dcachePort = new DcachePort(cpu, this); + + Port *mem_dport = mem->getPort(""); + dcachePort->setPeer(mem_dport); + mem_dport->setPeer(dcachePort); + +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->setDcachePort(dcachePort); + } +#endif +} + +template<class Impl> +std::string +LSQUnit<Impl>::name() const +{ + if (Impl::MaxThreads == 1) { + return iewStage->name() + ".lsq"; + } else { + return iewStage->name() + ".lsq.thread." + to_string(lsqID); + } +} + +template<class Impl> +void +LSQUnit<Impl>::regStats() +{ + lsqForwLoads + .name(name() + ".forwLoads") + .desc("Number of loads that had data forwarded from stores"); + + invAddrLoads + .name(name() + ".invAddrLoads") + .desc("Number of loads ignored due to an invalid address"); + + lsqSquashedLoads + .name(name() + ".squashedLoads") + .desc("Number of loads squashed"); + + lsqIgnoredResponses + .name(name() + ".ignoredResponses") + .desc("Number of memory responses ignored because the instruction is squashed"); + + lsqSquashedStores + .name(name() + ".squashedStores") + .desc("Number of stores squashed"); + + invAddrSwpfs + .name(name() + ".invAddrSwpfs") + .desc("Number of software prefetches ignored due to an invalid address"); + + lsqBlockedLoads + .name(name() + ".blockedLoads") + .desc("Number of blocked loads due to partial load-store forwarding"); + + lsqRescheduledLoads + .name(name() + ".rescheduledLoads") + .desc("Number of loads that were rescheduled"); + + lsqCacheBlocked + .name(name() + ".cacheBlocked") + .desc("Number of times an access to memory failed due to the cache being blocked"); +} + +template<class Impl> +void +LSQUnit<Impl>::clearLQ() +{ + loadQueue.clear(); +} + +template<class Impl> +void +LSQUnit<Impl>::clearSQ() +{ + storeQueue.clear(); +} + +template<class Impl> +void +LSQUnit<Impl>::switchOut() +{ + switchedOut = true; + for (int i = 0; i < loadQueue.size(); ++i) + loadQueue[i] = NULL; + + assert(storesToWB == 0); +} + +template<class Impl> +void +LSQUnit<Impl>::takeOverFrom() +{ + switchedOut = false; + loads = stores = storesToWB = 0; + + loadHead = loadTail = 0; + + storeHead = storeWBIdx = storeTail = 0; + + usedPorts = 0; + + memDepViolator = NULL; + + blockedLoadSeqNum = 0; + + stalled = false; + isLoadBlocked = false; + loadBlockedHandled = false; +} + +template<class Impl> +void +LSQUnit<Impl>::resizeLQ(unsigned size) +{ + unsigned size_plus_sentinel = size + 1; + assert(size_plus_sentinel >= LQEntries); + + if (size_plus_sentinel > LQEntries) { + while (size_plus_sentinel > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size_plus_sentinel; + } + +} + +template<class Impl> +void +LSQUnit<Impl>::resizeSQ(unsigned size) +{ + unsigned size_plus_sentinel = size + 1; + if (size_plus_sentinel > SQEntries) { + while (size_plus_sentinel > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size_plus_sentinel; + } +} + +template <class Impl> +void +LSQUnit<Impl>::insert(DynInstPtr &inst) +{ + assert(inst->isMemRef()); + + assert(inst->isLoad() || inst->isStore()); + + if (inst->isLoad()) { + insertLoad(inst); + } else { + insertStore(inst); + } + + inst->setInLSQ(); +} + +template <class Impl> +void +LSQUnit<Impl>::insertLoad(DynInstPtr &load_inst) +{ + assert((loadTail + 1) % LQEntries != loadHead); + assert(loads < LQEntries); + + DPRINTF(LSQUnit, "Inserting load PC %#x, idx:%i [sn:%lli]\n", + load_inst->readPC(), loadTail, load_inst->seqNum); + + load_inst->lqIdx = loadTail; + + if (stores == 0) { + load_inst->sqIdx = -1; + } else { + load_inst->sqIdx = storeTail; + } + + loadQueue[loadTail] = load_inst; + + incrLdIdx(loadTail); + + ++loads; +} + +template <class Impl> +void +LSQUnit<Impl>::insertStore(DynInstPtr &store_inst) +{ + // Make sure it is not full before inserting an instruction. + assert((storeTail + 1) % SQEntries != storeHead); + assert(stores < SQEntries); + + DPRINTF(LSQUnit, "Inserting store PC %#x, idx:%i [sn:%lli]\n", + store_inst->readPC(), storeTail, store_inst->seqNum); + + store_inst->sqIdx = storeTail; + store_inst->lqIdx = loadTail; + + storeQueue[storeTail] = SQEntry(store_inst); + + incrStIdx(storeTail); + + ++stores; +} + +template <class Impl> +typename Impl::DynInstPtr +LSQUnit<Impl>::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template <class Impl> +unsigned +LSQUnit<Impl>::numFreeEntries() +{ + unsigned free_lq_entries = LQEntries - loads; + unsigned free_sq_entries = SQEntries - stores; + + // Both the LQ and SQ entries have an extra dummy entry to differentiate + // empty/full conditions. Subtract 1 from the free entries. + if (free_lq_entries < free_sq_entries) { + return free_lq_entries - 1; + } else { + return free_sq_entries - 1; + } +} + +template <class Impl> +int +LSQUnit<Impl>::numLoadsReady() +{ + int load_idx = loadHead; + int retval = 0; + + while (load_idx != loadTail) { + assert(loadQueue[load_idx]); + + if (loadQueue[load_idx]->readyToIssue()) { + ++retval; + } + } + + return retval; +} + +template <class Impl> +Fault +LSQUnit<Impl>::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(LSQUnit, "Executing load PC %#x, [sn:%lli]\n", + inst->readPC(),inst->seqNum); + + load_fault = inst->initiateAcc(); + + // If the instruction faulted, then we need to send it along to commit + // without the instruction completing. + if (load_fault != NoFault) { + // Send this instruction to commit, also make sure iew stage + // realizes there is activity. + iewStage->instToCommit(inst); + iewStage->activityThisCycle(); + } + + return load_fault; +} + +template <class Impl> +Fault +LSQUnit<Impl>::executeStore(DynInstPtr &store_inst) +{ + using namespace TheISA; + // Make sure that a store exists. + assert(stores != 0); + + int store_idx = store_inst->sqIdx; + + DPRINTF(LSQUnit, "Executing store PC %#x [sn:%lli]\n", + store_inst->readPC(), store_inst->seqNum); + + // Check the recently completed loads to see if any match this store's + // address. If so, then we have a memory ordering violation. + int load_idx = store_inst->lqIdx; + + Fault store_fault = store_inst->initiateAcc(); + + if (storeQueue[store_idx].size == 0) { + DPRINTF(LSQUnit,"Fault on Store PC %#x, [sn:%lli],Size = 0\n", + store_inst->readPC(),store_inst->seqNum); + + return store_fault; + } + + assert(store_fault == NoFault); + + if (store_inst->isStoreConditional()) { + // Store conditionals need to set themselves as able to + // writeback if we haven't had a fault by here. + storeQueue[store_idx].canWB = true; + + ++storesToWB; + } + + if (!memDepViolator) { + while (load_idx != loadTail) { + // Really only need to check loads that have actually executed + // It's safe to check all loads because effAddr is set to + // InvalAddr when the dyn inst is created. + + // @todo: For now this is extra conservative, detecting a + // violation if the addresses match assuming all accesses + // are quad word accesses. + + // @todo: Fix this, magic number being used here + if ((loadQueue[load_idx]->effAddr >> 8) == + (store_inst->effAddr >> 8)) { + // A load incorrectly passed this store. Squash and refetch. + // For now return a fault to show that it was unsuccessful. + memDepViolator = loadQueue[load_idx]; + + return genMachineCheckFault(); + } + + incrLdIdx(load_idx); + } + + // If we've reached this point, there was no violation. + memDepViolator = NULL; + } + + return store_fault; +} + +template <class Impl> +void +LSQUnit<Impl>::commitLoad() +{ + assert(loadQueue[loadHead]); + + DPRINTF(LSQUnit, "Committing head load instruction, PC %#x\n", + loadQueue[loadHead]->readPC()); + + loadQueue[loadHead] = NULL; + + incrLdIdx(loadHead); + + --loads; +} + +template <class Impl> +void +LSQUnit<Impl>::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || loadQueue[loadHead]); + + while (loads != 0 && loadQueue[loadHead]->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template <class Impl> +void +LSQUnit<Impl>::commitStores(InstSeqNum &youngest_inst) +{ + assert(stores == 0 || storeQueue[storeHead].inst); + + int store_idx = storeHead; + + while (store_idx != storeTail) { + assert(storeQueue[store_idx].inst); + // Mark any stores that are now committed and have not yet + // been marked as able to write back. + if (!storeQueue[store_idx].canWB) { + if (storeQueue[store_idx].inst->seqNum > youngest_inst) { + break; + } + DPRINTF(LSQUnit, "Marking store as able to write back, PC " + "%#x [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + storeQueue[store_idx].inst->seqNum); + + storeQueue[store_idx].canWB = true; + + ++storesToWB; + } + + incrStIdx(store_idx); + } +} + +template <class Impl> +void +LSQUnit<Impl>::writebackStores() +{ + while (storesToWB > 0 && + storeWBIdx != storeTail && + storeQueue[storeWBIdx].inst && + storeQueue[storeWBIdx].canWB && + usedPorts < cachePorts) { + + if (isStoreBlocked) { + DPRINTF(LSQUnit, "Unable to write back any more stores, cache" + " is blocked!\n"); + break; + } + + // Store didn't write any data so no need to write it back to + // memory. + if (storeQueue[storeWBIdx].size == 0) { + completeStore(storeWBIdx); + + incrStIdx(storeWBIdx); + + continue; + } + + ++usedPorts; + + if (storeQueue[storeWBIdx].inst->isDataPrefetch()) { + incrStIdx(storeWBIdx); + + continue; + } + + assert(storeQueue[storeWBIdx].req); + assert(!storeQueue[storeWBIdx].committed); + + DynInstPtr inst = storeQueue[storeWBIdx].inst; + + Request *req = storeQueue[storeWBIdx].req; + storeQueue[storeWBIdx].committed = true; + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + memcpy(inst->memData, (uint8_t *)&storeQueue[storeWBIdx].data, + req->getSize()); + + PacketPtr data_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = false; + state->idx = storeWBIdx; + state->inst = inst; + data_pkt->senderState = state; + + DPRINTF(LSQUnit, "D-Cache: Writing back store idx:%i PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + storeWBIdx, storeQueue[storeWBIdx].inst->readPC(), + req->getPaddr(), *(inst->memData), + storeQueue[storeWBIdx].inst->seqNum); + + // @todo: Remove this SC hack once the memory system handles it. + if (req->getFlags() & LOCKED) { + if (req->getFlags() & UNCACHEABLE) { + req->setScResult(2); + } else { + if (cpu->lockFlag) { + req->setScResult(1); + } else { + req->setScResult(0); + // Hack: Instantly complete this store. + completeDataAccess(data_pkt); + incrStIdx(storeWBIdx); + continue; + } + } + } else { + // Non-store conditionals do not need a writeback. + state->noWB = true; + } + + if (!dcachePort->sendTiming(data_pkt)) { + // Need to handle becoming blocked on a store. + isStoreBlocked = true; + ++lsqCacheBlocked; + assert(retryPkt == NULL); + retryPkt = data_pkt; + } else { + storePostSend(data_pkt); + } + } + + // Not sure this should set it to 0. + usedPorts = 0; + + assert(stores >= 0 && storesToWB >= 0); +} + +/*template <class Impl> +void +LSQUnit<Impl>::removeMSHR(InstSeqNum seqNum) +{ + list<InstSeqNum>::iterator mshr_it = find(mshrSeqNums.begin(), + mshrSeqNums.end(), + seqNum); + + if (mshr_it != mshrSeqNums.end()) { + mshrSeqNums.erase(mshr_it); + DPRINTF(LSQUnit, "Removing MSHR. count = %i\n",mshrSeqNums.size()); + } +}*/ + +template <class Impl> +void +LSQUnit<Impl>::squash(const InstSeqNum &squashed_num) +{ + DPRINTF(LSQUnit, "Squashing until [sn:%lli]!" + "(Loads:%i Stores:%i)\n", squashed_num, loads, stores); + + int load_idx = loadTail; + decrLdIdx(load_idx); + + while (loads != 0 && loadQueue[load_idx]->seqNum > squashed_num) { + DPRINTF(LSQUnit,"Load Instruction PC %#x squashed, " + "[sn:%lli]\n", + loadQueue[load_idx]->readPC(), + loadQueue[load_idx]->seqNum); + + if (isStalled() && load_idx == stallingLoadIdx) { + stalled = false; + stallingStoreIsn = 0; + stallingLoadIdx = 0; + } + + // Clear the smart pointer to make sure it is decremented. + loadQueue[load_idx]->setSquashed(); + loadQueue[load_idx] = NULL; + --loads; + + // Inefficient! + loadTail = load_idx; + + decrLdIdx(load_idx); + ++lsqSquashedLoads; + } + + if (isLoadBlocked) { + if (squashed_num < blockedLoadSeqNum) { + isLoadBlocked = false; + loadBlockedHandled = false; + blockedLoadSeqNum = 0; + } + } + + int store_idx = storeTail; + decrStIdx(store_idx); + + while (stores != 0 && + storeQueue[store_idx].inst->seqNum > squashed_num) { + // Instructions marked as can WB are already committed. + if (storeQueue[store_idx].canWB) { + break; + } + + DPRINTF(LSQUnit,"Store Instruction PC %#x squashed, " + "idx:%i [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + store_idx, storeQueue[store_idx].inst->seqNum); + + // I don't think this can happen. It should have been cleared + // by the stalling load. + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + panic("Is stalled should have been cleared by stalling load!\n"); + stalled = false; + stallingStoreIsn = 0; + } + + // Clear the smart pointer to make sure it is decremented. + storeQueue[store_idx].inst->setSquashed(); + storeQueue[store_idx].inst = NULL; + storeQueue[store_idx].canWB = 0; + + storeQueue[store_idx].req = NULL; + --stores; + + // Inefficient! + storeTail = store_idx; + + decrStIdx(store_idx); + ++lsqSquashedStores; + } +} + +template <class Impl> +void +LSQUnit<Impl>::storePostSend(Packet *pkt) +{ + if (isStalled() && + storeQueue[storeWBIdx].inst->seqNum == stallingStoreIsn) { + DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + iewStage->replayMemInst(loadQueue[stallingLoadIdx]); + } + + if (!storeQueue[storeWBIdx].inst->isStoreConditional()) { + // The store is basically completed at this time. This + // only works so long as the checker doesn't try to + // verify the value in memory for stores. + storeQueue[storeWBIdx].inst->setCompleted(); +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->verify(storeQueue[storeWBIdx].inst); + } +#endif + } + + if (pkt->result != Packet::Success) { + DPRINTF(LSQUnit,"D-Cache Write Miss on idx:%i!\n", + storeWBIdx); + + DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", + storeQueue[storeWBIdx].inst->seqNum); + + //mshrSeqNums.push_back(storeQueue[storeWBIdx].inst->seqNum); + + //DPRINTF(LSQUnit, "Added MSHR. count = %i\n",mshrSeqNums.size()); + + // @todo: Increment stat here. + } else { + DPRINTF(LSQUnit,"D-Cache: Write Hit on idx:%i !\n", + storeWBIdx); + + DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", + storeQueue[storeWBIdx].inst->seqNum); + } + + incrStIdx(storeWBIdx); +} + +template <class Impl> +void +LSQUnit<Impl>::writeback(DynInstPtr &inst, PacketPtr pkt) +{ + iewStage->wakeCPU(); + + // Squashed instructions do not need to complete their access. + if (inst->isSquashed()) { + assert(!inst->isStore()); + ++lsqIgnoredResponses; + return; + } + + if (!inst->isExecuted()) { + inst->setExecuted(); + + // Complete access to copy data to proper place. + inst->completeAcc(pkt); + } + + // Need to insert instruction into queue to commit + iewStage->instToCommit(inst); + + iewStage->activityThisCycle(); +} + +template <class Impl> +void +LSQUnit<Impl>::completeStore(int store_idx) +{ + assert(storeQueue[store_idx].inst); + storeQueue[store_idx].completed = true; + --storesToWB; + // A bit conservative because a store completion may not free up entries, + // but hopefully avoids two store completions in one cycle from making + // the CPU tick twice. + cpu->activityThisCycle(); + + if (store_idx == storeHead) { + do { + incrStIdx(storeHead); + + --stores; + } while (storeQueue[storeHead].completed && + storeHead != storeTail); + + iewStage->updateLSQNextCycle = true; + } + + DPRINTF(LSQUnit, "Completing store [sn:%lli], idx:%i, store head " + "idx:%i\n", + storeQueue[store_idx].inst->seqNum, store_idx, storeHead); + + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + iewStage->replayMemInst(loadQueue[stallingLoadIdx]); + } + + storeQueue[store_idx].inst->setCompleted(); + + // Tell the checker we've completed this instruction. Some stores + // may get reported twice to the checker, but the checker can + // handle that case. +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->verify(storeQueue[store_idx].inst); + } +#endif +} + +template <class Impl> +void +LSQUnit<Impl>::recvRetry() +{ + if (isStoreBlocked) { + assert(retryPkt != NULL); + + if (dcachePort->sendTiming(retryPkt)) { + storePostSend(retryPkt); + retryPkt = NULL; + isStoreBlocked = false; + } else { + // Still blocked! + ++lsqCacheBlocked; + } + } else if (isLoadBlocked) { + DPRINTF(LSQUnit, "Loads squash themselves and all younger insts, " + "no need to resend packet.\n"); + } else { + DPRINTF(LSQUnit, "Retry received but LSQ is no longer blocked.\n"); + } +} + +template <class Impl> +inline void +LSQUnit<Impl>::incrStIdx(int &store_idx) +{ + if (++store_idx >= SQEntries) + store_idx = 0; +} + +template <class Impl> +inline void +LSQUnit<Impl>::decrStIdx(int &store_idx) +{ + if (--store_idx < 0) + store_idx += SQEntries; +} + +template <class Impl> +inline void +LSQUnit<Impl>::incrLdIdx(int &load_idx) +{ + if (++load_idx >= LQEntries) + load_idx = 0; +} + +template <class Impl> +inline void +LSQUnit<Impl>::decrLdIdx(int &load_idx) +{ + if (--load_idx < 0) + load_idx += LQEntries; +} + +template <class Impl> +void +LSQUnit<Impl>::dumpInsts() +{ + cprintf("Load store queue: Dumping instructions.\n"); + cprintf("Load queue size: %i\n", loads); + cprintf("Load queue: "); + + int load_idx = loadHead; + + while (load_idx != loadTail && loadQueue[load_idx]) { + cprintf("%#x ", loadQueue[load_idx]->readPC()); + + incrLdIdx(load_idx); + } + + cprintf("Store queue size: %i\n", stores); + cprintf("Store queue: "); + + int store_idx = storeHead; + + while (store_idx != storeTail && storeQueue[store_idx].inst) { + cprintf("%#x ", storeQueue[store_idx].inst->readPC()); + + incrStIdx(store_idx); + } + + cprintf("\n"); +} diff --git a/src/cpu/o3/mem_dep_unit.cc b/src/cpu/o3/mem_dep_unit.cc index 9c1e7f9d8..6a14dcbff 100644 --- a/src/cpu/o3/mem_dep_unit.cc +++ b/src/cpu/o3/mem_dep_unit.cc @@ -24,13 +24,26 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/isa_specific.hh" #include "cpu/o3/store_set.hh" #include "cpu/o3/mem_dep_unit_impl.hh" // Force instantation of memory dependency unit using store sets and -// AlphaSimpleImpl. -template class MemDepUnit<StoreSet, AlphaSimpleImpl>; +// O3CPUImpl. +template class MemDepUnit<StoreSet, O3CPUImpl>; + +#ifdef DEBUG +template <> +int +MemDepUnit<StoreSet, O3CPUImpl>::MemDepEntry::memdep_count = 0; +template <> +int +MemDepUnit<StoreSet, O3CPUImpl>::MemDepEntry::memdep_insert = 0; +template <> +int +MemDepUnit<StoreSet, O3CPUImpl>::MemDepEntry::memdep_erase = 0; +#endif diff --git a/src/cpu/o3/mem_dep_unit.hh b/src/cpu/o3/mem_dep_unit.hh index ca63577a1..e399f0133 100644 --- a/src/cpu/o3/mem_dep_unit.hh +++ b/src/cpu/o3/mem_dep_unit.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,17 +24,33 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_MEM_DEP_UNIT_HH__ -#define __CPU_O3_CPU_MEM_DEP_UNIT_HH__ +#ifndef __CPU_O3_MEM_DEP_UNIT_HH__ +#define __CPU_O3_MEM_DEP_UNIT_HH__ -#include <map> +#include <list> #include <set> +#include "base/hashmap.hh" +#include "base/refcnt.hh" #include "base/statistics.hh" #include "cpu/inst_seq.hh" +struct SNHash { + size_t operator() (const InstSeqNum &seq_num) const { + unsigned a = (unsigned)seq_num; + unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; + + return hash; + } +}; + +template <class Impl> +class InstructionQueue; + /** * Memory dependency unit class. This holds the memory dependence predictor. * As memory operations are issued to the IQ, they are also issued to this @@ -52,101 +68,166 @@ class MemDepUnit { typedef typename Impl::Params Params; typedef typename Impl::DynInstPtr DynInstPtr; - public: - MemDepUnit(Params ¶ms); + /** Empty constructor. Must call init() prior to using in this case. */ + MemDepUnit() {} + + /** Constructs a MemDepUnit with given parameters. */ + MemDepUnit(Params *params); + + /** Frees up any memory allocated. */ + ~MemDepUnit(); + + /** Returns the name of the memory dependence unit. */ + std::string name() const; + + /** Initializes the unit with parameters and a thread id. */ + void init(Params *params, int tid); + /** Registers statistics. */ void regStats(); + /** Switches out the memory dependence predictor. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Sets the pointer to the IQ. */ + void setIQ(InstructionQueue<Impl> *iq_ptr); + + /** Inserts a memory instruction. */ void insert(DynInstPtr &inst); + /** Inserts a non-speculative memory instruction. */ void insertNonSpec(DynInstPtr &inst); - // Will want to make this operation relatively fast. Right now it - // is somewhat slow. - DynInstPtr &top(); - - void pop(); + /** Inserts a barrier instruction. */ + void insertBarrier(DynInstPtr &barr_inst); + /** Indicate that an instruction has its registers ready. */ void regsReady(DynInstPtr &inst); + /** Indicate that a non-speculative instruction is ready. */ void nonSpecInstReady(DynInstPtr &inst); - void issue(DynInstPtr &inst); + /** Reschedules an instruction to be re-executed. */ + void reschedule(DynInstPtr &inst); + + /** Replays all instructions that have been rescheduled by moving them to + * the ready list. + */ + void replay(DynInstPtr &inst); + + /** Completes a memory instruction. */ + void completed(DynInstPtr &inst); + + /** Completes a barrier instruction. */ + void completeBarrier(DynInstPtr &inst); + /** Wakes any dependents of a memory instruction. */ void wakeDependents(DynInstPtr &inst); - void squash(const InstSeqNum &squashed_num); + /** Squashes all instructions up until a given sequence number for a + * specific thread. + */ + void squash(const InstSeqNum &squashed_num, unsigned tid); + /** Indicates an ordering violation between a store and a younger load. */ void violation(DynInstPtr &store_inst, DynInstPtr &violating_load); - inline bool empty() - { return readyInsts.empty(); } + /** Issues the given instruction */ + void issue(DynInstPtr &inst); + + /** Debugging function to dump the lists of instructions. */ + void dumpLists(); private: - typedef typename std::set<InstSeqNum>::iterator sn_it_t; - typedef typename std::map<InstSeqNum, DynInstPtr>::iterator dyn_it_t; - - // Forward declarations so that the following two typedefs work. - class Dependency; - class ltDependency; - - typedef typename std::set<Dependency, ltDependency>::iterator dep_it_t; - typedef typename std::map<InstSeqNum, vector<dep_it_t> >::iterator - sd_it_t; - - struct Dependency { - Dependency(const InstSeqNum &_seqNum) - : seqNum(_seqNum), regsReady(0), memDepReady(0) - { } - - Dependency(const InstSeqNum &_seqNum, bool _regsReady, - bool _memDepReady) - : seqNum(_seqNum), regsReady(_regsReady), - memDepReady(_memDepReady) - { } - - InstSeqNum seqNum; - mutable bool regsReady; - mutable bool memDepReady; - mutable sd_it_t storeDep; - }; + typedef typename std::list<DynInstPtr>::iterator ListIt; + + class MemDepEntry; - struct ltDependency { - bool operator() (const Dependency &lhs, const Dependency &rhs) + typedef RefCountingPtr<MemDepEntry> MemDepEntryPtr; + + /** Memory dependence entries that track memory operations, marking + * when the instruction is ready to execute and what instructions depend + * upon it. + */ + class MemDepEntry : public RefCounted { + public: + /** Constructs a memory dependence entry. */ + MemDepEntry(DynInstPtr &new_inst) + : inst(new_inst), regsReady(false), memDepReady(false), + completed(false), squashed(false) { - return lhs.seqNum < rhs.seqNum; +#ifdef DEBUG + ++memdep_count; + + DPRINTF(MemDepUnit, "Memory dependency entry created. " + "memdep_count=%i\n", memdep_count); +#endif } + + /** Frees any pointers. */ + ~MemDepEntry() + { + for (int i = 0; i < dependInsts.size(); ++i) { + dependInsts[i] = NULL; + } +#ifdef DEBUG + --memdep_count; + + DPRINTF(MemDepUnit, "Memory dependency entry deleted. " + "memdep_count=%i\n", memdep_count); +#endif + } + + /** Returns the name of the memory dependence entry. */ + std::string name() const { return "memdepentry"; } + + /** The instruction being tracked. */ + DynInstPtr inst; + + /** The iterator to the instruction's location inside the list. */ + ListIt listIt; + + /** A vector of any dependent instructions. */ + std::vector<MemDepEntryPtr> dependInsts; + + /** If the registers are ready or not. */ + bool regsReady; + /** If all memory dependencies have been satisfied. */ + bool memDepReady; + /** If the instruction is completed. */ + bool completed; + /** If the instruction is squashed. */ + bool squashed; + + /** For debugging. */ +#ifdef DEBUG + static int memdep_count; + static int memdep_insert; + static int memdep_erase; +#endif }; - inline void moveToReady(dep_it_t &woken_inst); + /** Finds the memory dependence entry in the hash map. */ + inline MemDepEntryPtr &findInHash(const DynInstPtr &inst); - /** List of instructions that have passed through rename, yet are still - * waiting on either a memory dependence to resolve or source registers to - * become available before they can issue. - */ - std::set<Dependency, ltDependency> waitingInsts; + /** Moves an entry to the ready list. */ + inline void moveToReady(MemDepEntryPtr &ready_inst_entry); - /** List of instructions that have all their predicted memory dependences - * resolved and their source registers ready. - */ - std::set<InstSeqNum> readyInsts; + typedef m5::hash_map<InstSeqNum, MemDepEntryPtr, SNHash> MemDepHash; - // Change this to hold a vector of iterators, which will point to the - // entry of the waiting instructions. - /** List of stores' sequence numbers, each of which has a vector of - * iterators. The iterators point to the appropriate node within - * waitingInsts that has the depenendent instruction. - */ - std::map<InstSeqNum, vector<dep_it_t> > storeDependents; + typedef typename MemDepHash::iterator MemDepHashIt; - // For now will implement this as a map...hash table might not be too - // bad, or could move to something that mimics the current dependency - // graph. - std::map<InstSeqNum, DynInstPtr> memInsts; + /** A hash map of all memory dependence entries. */ + MemDepHash memDepHash; - // Iterator pointer to the top instruction which has is ready. - // Is set by the top() call. - dyn_it_t topInst; + /** A list of all instructions in the memory dependence unit. */ + std::list<DynInstPtr> instList[Impl::MaxThreads]; + + /** A list of all instructions that are going to be replayed. */ + std::list<DynInstPtr> instsToReplay; /** The memory dependence predictor. It is accessed upon new * instructions being added to the IQ, and responds by telling @@ -155,10 +236,29 @@ class MemDepUnit { */ MemDepPred depPred; + /** Is there an outstanding load barrier that loads must wait on. */ + bool loadBarrier; + /** The sequence number of the load barrier. */ + InstSeqNum loadBarrierSN; + /** Is there an outstanding store barrier that loads must wait on. */ + bool storeBarrier; + /** The sequence number of the store barrier. */ + InstSeqNum storeBarrierSN; + + /** Pointer to the IQ. */ + InstructionQueue<Impl> *iqPtr; + + /** The thread id of this memory dependence unit. */ + int id; + + /** Stat for number of inserted loads. */ Stats::Scalar<> insertedLoads; + /** Stat for number of inserted stores. */ Stats::Scalar<> insertedStores; + /** Stat for number of conflicting loads that had to wait for a store. */ Stats::Scalar<> conflictingLoads; + /** Stat for number of conflicting stores that had to wait for a store. */ Stats::Scalar<> conflictingStores; }; -#endif // __CPU_O3_CPU_MEM_DEP_UNIT_HH__ +#endif // __CPU_O3_MEM_DEP_UNIT_HH__ diff --git a/src/cpu/o3/mem_dep_unit_impl.hh b/src/cpu/o3/mem_dep_unit_impl.hh index 296db4c4e..16f67a4e0 100644 --- a/src/cpu/o3/mem_dep_unit_impl.hh +++ b/src/cpu/o3/mem_dep_unit_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,17 +24,64 @@ * 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. + * + * Authors: Kevin Lim */ #include <map> +#include "cpu/o3/inst_queue.hh" #include "cpu/o3/mem_dep_unit.hh" template <class MemDepPred, class Impl> -MemDepUnit<MemDepPred, Impl>::MemDepUnit(Params ¶ms) - : depPred(params.SSITSize, params.LFSTSize) +MemDepUnit<MemDepPred, Impl>::MemDepUnit(Params *params) + : depPred(params->SSITSize, params->LFSTSize), loadBarrier(false), + loadBarrierSN(0), storeBarrier(false), storeBarrierSN(0), iqPtr(NULL) +{ + DPRINTF(MemDepUnit, "Creating MemDepUnit object.\n"); +} + +template <class MemDepPred, class Impl> +MemDepUnit<MemDepPred, Impl>::~MemDepUnit() +{ + for (int tid=0; tid < Impl::MaxThreads; tid++) { + + ListIt inst_list_it = instList[tid].begin(); + + MemDepHashIt hash_it; + + while (!instList[tid].empty()) { + hash_it = memDepHash.find((*inst_list_it)->seqNum); + + assert(hash_it != memDepHash.end()); + + memDepHash.erase(hash_it); + + instList[tid].erase(inst_list_it++); + } + } + +#ifdef DEBUG + assert(MemDepEntry::memdep_count == 0); +#endif +} + +template <class MemDepPred, class Impl> +std::string +MemDepUnit<MemDepPred, Impl>::name() const { - DPRINTF(MemDepUnit, "MemDepUnit: Creating MemDepUnit object.\n"); + return "memdepunit"; +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::init(Params *params, int tid) +{ + DPRINTF(MemDepUnit, "Creating MemDepUnit %i object.\n",tid); + + id = tid; + + depPred.init(params->SSITSize, params->LFSTSize); } template <class MemDepPred, class Impl> @@ -60,56 +107,99 @@ MemDepUnit<MemDepPred, Impl>::regStats() template <class MemDepPred, class Impl> void +MemDepUnit<MemDepPred, Impl>::switchOut() +{ + // Clear any state. + for (int i = 0; i < Impl::MaxThreads; ++i) { + instList[i].clear(); + } + instsToReplay.clear(); + memDepHash.clear(); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::takeOverFrom() +{ + // Be sure to reset all state. + loadBarrier = storeBarrier = false; + loadBarrierSN = storeBarrierSN = 0; + depPred.clear(); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::setIQ(InstructionQueue<Impl> *iq_ptr) +{ + iqPtr = iq_ptr; +} + +template <class MemDepPred, class Impl> +void MemDepUnit<MemDepPred, Impl>::insert(DynInstPtr &inst) { - InstSeqNum inst_seq_num = inst->seqNum; + unsigned tid = inst->threadNumber; + + MemDepEntryPtr inst_entry = new MemDepEntry(inst); + + // Add the MemDepEntry to the hash. + memDepHash.insert( + std::pair<InstSeqNum, MemDepEntryPtr>(inst->seqNum, inst_entry)); +#ifdef DEBUG + MemDepEntry::memdep_insert++; +#endif - Dependency unresolved_dependencies(inst_seq_num); + instList[tid].push_back(inst); - InstSeqNum producing_store = depPred.checkInst(inst->readPC()); + inst_entry->listIt = --(instList[tid].end()); - if (producing_store == 0 || - storeDependents.find(producing_store) == storeDependents.end()) { + // Check any barriers and the dependence predictor for any + // producing memrefs/stores. + InstSeqNum producing_store; + if (inst->isLoad() && loadBarrier) { + producing_store = loadBarrierSN; + } else if (inst->isStore() && storeBarrier) { + producing_store = storeBarrierSN; + } else { + producing_store = depPred.checkInst(inst->readPC()); + } - DPRINTF(MemDepUnit, "MemDepUnit: No dependency for inst PC " - "%#x.\n", inst->readPC()); + MemDepEntryPtr store_entry = NULL; - unresolved_dependencies.storeDep = storeDependents.end(); + // If there is a producing store, try to find the entry. + if (producing_store != 0) { + MemDepHashIt hash_it = memDepHash.find(producing_store); + + if (hash_it != memDepHash.end()) { + store_entry = (*hash_it).second; + } + } + + // If no store entry, then instruction can issue as soon as the registers + // are ready. + if (!store_entry) { + DPRINTF(MemDepUnit, "No dependency for inst PC " + "%#x [sn:%lli].\n", inst->readPC(), inst->seqNum); + + inst_entry->memDepReady = true; if (inst->readyToIssue()) { - readyInsts.insert(inst_seq_num); - } else { - unresolved_dependencies.memDepReady = true; + inst_entry->regsReady = true; - waitingInsts.insert(unresolved_dependencies); + moveToReady(inst_entry); } } else { - DPRINTF(MemDepUnit, "MemDepUnit: Adding to dependency list; " - "inst PC %#x is dependent on seq num %i.\n", + // Otherwise make the instruction dependent on the store/barrier. + DPRINTF(MemDepUnit, "Adding to dependency list; " + "inst PC %#x is dependent on [sn:%lli].\n", inst->readPC(), producing_store); if (inst->readyToIssue()) { - unresolved_dependencies.regsReady = true; + inst_entry->regsReady = true; } - // Find the store that this instruction is dependent on. - sd_it_t store_loc = storeDependents.find(producing_store); - - assert(store_loc != storeDependents.end()); - - // Record the location of the store that this instruction is - // dependent on. - unresolved_dependencies.storeDep = store_loc; - - // If it's not already ready, then add it to the renamed - // list and the dependencies. - dep_it_t inst_loc = - (waitingInsts.insert(unresolved_dependencies)).first; - // Add this instruction to the list of dependents. - (*store_loc).second.push_back(inst_loc); - - assert(!(*store_loc).second.empty()); + store_entry->dependInsts.push_back(inst_entry); if (inst->isLoad()) { ++conflictingLoads; @@ -119,277 +209,288 @@ MemDepUnit<MemDepPred, Impl>::insert(DynInstPtr &inst) } if (inst->isStore()) { - DPRINTF(MemDepUnit, "MemDepUnit: Inserting store PC %#x.\n", - inst->readPC()); - - depPred.insertStore(inst->readPC(), inst_seq_num); - - // Make sure this store isn't already in this list. - assert(storeDependents.find(inst_seq_num) == storeDependents.end()); - - // Put a dependency entry in at the store's sequence number. - // Uh, not sure how this works...I want to create an entry but - // I don't have anything to put into the value yet. - storeDependents[inst_seq_num]; + DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - assert(storeDependents.size() != 0); + depPred.insertStore(inst->readPC(), inst->seqNum, inst->threadNumber); ++insertedStores; - } else if (inst->isLoad()) { ++insertedLoads; } else { - panic("MemDepUnit: Unknown type! (most likely a barrier)."); + panic("Unknown type! (most likely a barrier)."); } - - memInsts[inst_seq_num] = inst; } template <class MemDepPred, class Impl> void MemDepUnit<MemDepPred, Impl>::insertNonSpec(DynInstPtr &inst) { - InstSeqNum inst_seq_num = inst->seqNum; + unsigned tid = inst->threadNumber; + + MemDepEntryPtr inst_entry = new MemDepEntry(inst); - Dependency non_spec_inst(inst_seq_num); + // Insert the MemDepEntry into the hash. + memDepHash.insert( + std::pair<InstSeqNum, MemDepEntryPtr>(inst->seqNum, inst_entry)); +#ifdef DEBUG + MemDepEntry::memdep_insert++; +#endif - non_spec_inst.storeDep = storeDependents.end(); + // Add the instruction to the list. + instList[tid].push_back(inst); - waitingInsts.insert(non_spec_inst); + inst_entry->listIt = --(instList[tid].end()); // Might want to turn this part into an inline function or something. // It's shared between both insert functions. if (inst->isStore()) { - DPRINTF(MemDepUnit, "MemDepUnit: Inserting store PC %#x.\n", - inst->readPC()); - - depPred.insertStore(inst->readPC(), inst_seq_num); - - // Make sure this store isn't already in this list. - assert(storeDependents.find(inst_seq_num) == storeDependents.end()); - - // Put a dependency entry in at the store's sequence number. - // Uh, not sure how this works...I want to create an entry but - // I don't have anything to put into the value yet. - storeDependents[inst_seq_num]; + DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - assert(storeDependents.size() != 0); + depPred.insertStore(inst->readPC(), inst->seqNum, inst->threadNumber); ++insertedStores; - } else if (inst->isLoad()) { ++insertedLoads; } else { - panic("MemDepUnit: Unknown type! (most likely a barrier)."); + panic("Unknown type! (most likely a barrier)."); } - - memInsts[inst_seq_num] = inst; } template <class MemDepPred, class Impl> -typename Impl::DynInstPtr & -MemDepUnit<MemDepPred, Impl>::top() +void +MemDepUnit<MemDepPred, Impl>::insertBarrier(DynInstPtr &barr_inst) { - topInst = memInsts.find( (*readyInsts.begin()) ); + InstSeqNum barr_sn = barr_inst->seqNum; + // Memory barriers block loads and stores, write barriers only stores. + if (barr_inst->isMemBarrier()) { + loadBarrier = true; + loadBarrierSN = barr_sn; + storeBarrier = true; + storeBarrierSN = barr_sn; + DPRINTF(MemDepUnit, "Inserted a memory barrier\n"); + } else if (barr_inst->isWriteBarrier()) { + storeBarrier = true; + storeBarrierSN = barr_sn; + DPRINTF(MemDepUnit, "Inserted a write barrier\n"); + } + + unsigned tid = barr_inst->threadNumber; + + MemDepEntryPtr inst_entry = new MemDepEntry(barr_inst); + + // Add the MemDepEntry to the hash. + memDepHash.insert( + std::pair<InstSeqNum, MemDepEntryPtr>(barr_sn, inst_entry)); +#ifdef DEBUG + MemDepEntry::memdep_insert++; +#endif - DPRINTF(MemDepUnit, "MemDepUnit: Top instruction is PC %#x.\n", - (*topInst).second->readPC()); + // Add the instruction to the instruction list. + instList[tid].push_back(barr_inst); - return (*topInst).second; + inst_entry->listIt = --(instList[tid].end()); } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::pop() +MemDepUnit<MemDepPred, Impl>::regsReady(DynInstPtr &inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Removing instruction PC %#x.\n", - (*topInst).second->readPC()); + DPRINTF(MemDepUnit, "Marking registers as ready for " + "instruction PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); - wakeDependents((*topInst).second); + MemDepEntryPtr inst_entry = findInHash(inst); - issue((*topInst).second); + inst_entry->regsReady = true; - memInsts.erase(topInst); + if (inst_entry->memDepReady) { + DPRINTF(MemDepUnit, "Instruction has its memory " + "dependencies resolved, adding it to the ready list.\n"); - topInst = memInsts.end(); + moveToReady(inst_entry); + } else { + DPRINTF(MemDepUnit, "Instruction still waiting on " + "memory dependency.\n"); + } } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::regsReady(DynInstPtr &inst) +MemDepUnit<MemDepPred, Impl>::nonSpecInstReady(DynInstPtr &inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Marking registers as ready for " - "instruction PC %#x.\n", - inst->readPC()); + DPRINTF(MemDepUnit, "Marking non speculative " + "instruction PC %#x as ready [sn:%lli].\n", + inst->readPC(), inst->seqNum); - InstSeqNum inst_seq_num = inst->seqNum; + MemDepEntryPtr inst_entry = findInHash(inst); + + moveToReady(inst_entry); +} - Dependency inst_to_find(inst_seq_num); +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::reschedule(DynInstPtr &inst) +{ + instsToReplay.push_back(inst); +} - dep_it_t waiting_inst = waitingInsts.find(inst_to_find); +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::replay(DynInstPtr &inst) +{ + DynInstPtr temp_inst; + bool found_inst = false; - assert(waiting_inst != waitingInsts.end()); + // For now this replay function replays all waiting memory ops. + while (!instsToReplay.empty()) { + temp_inst = instsToReplay.front(); - if ((*waiting_inst).memDepReady) { - DPRINTF(MemDepUnit, "MemDepUnit: Instruction has its memory " - "dependencies resolved, adding it to the ready list.\n"); + MemDepEntryPtr inst_entry = findInHash(temp_inst); - moveToReady(waiting_inst); - } else { - DPRINTF(MemDepUnit, "MemDepUnit: Instruction still waiting on " - "memory dependency.\n"); + DPRINTF(MemDepUnit, "Replaying mem instruction PC %#x " + "[sn:%lli].\n", + temp_inst->readPC(), temp_inst->seqNum); - (*waiting_inst).regsReady = true; + moveToReady(inst_entry); + + if (temp_inst == inst) { + found_inst = true; + } + + instsToReplay.pop_front(); } + + assert(found_inst); } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::nonSpecInstReady(DynInstPtr &inst) +MemDepUnit<MemDepPred, Impl>::completed(DynInstPtr &inst) { - DPRINTF(MemDepUnit, "MemDepUnit: Marking non speculative " - "instruction PC %#x as ready.\n", - inst->readPC()); + DPRINTF(MemDepUnit, "Completed mem instruction PC %#x " + "[sn:%lli].\n", + inst->readPC(), inst->seqNum); - InstSeqNum inst_seq_num = inst->seqNum; + unsigned tid = inst->threadNumber; - Dependency inst_to_find(inst_seq_num); + // Remove the instruction from the hash and the list. + MemDepHashIt hash_it = memDepHash.find(inst->seqNum); - dep_it_t waiting_inst = waitingInsts.find(inst_to_find); + assert(hash_it != memDepHash.end()); - assert(waiting_inst != waitingInsts.end()); + instList[tid].erase((*hash_it).second->listIt); - moveToReady(waiting_inst); + (*hash_it).second = NULL; + + memDepHash.erase(hash_it); +#ifdef DEBUG + MemDepEntry::memdep_erase++; +#endif } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::issue(DynInstPtr &inst) +MemDepUnit<MemDepPred, Impl>::completeBarrier(DynInstPtr &inst) { - assert(readyInsts.find(inst->seqNum) != readyInsts.end()); - - DPRINTF(MemDepUnit, "MemDepUnit: Issuing instruction PC %#x.\n", - inst->readPC()); - - // Remove the instruction from the ready list. - readyInsts.erase(inst->seqNum); - - depPred.issued(inst->readPC(), inst->seqNum, inst->isStore()); + wakeDependents(inst); + completed(inst); + + InstSeqNum barr_sn = inst->seqNum; + + if (inst->isMemBarrier()) { + assert(loadBarrier && storeBarrier); + if (loadBarrierSN == barr_sn) + loadBarrier = false; + if (storeBarrierSN == barr_sn) + storeBarrier = false; + } else if (inst->isWriteBarrier()) { + assert(storeBarrier); + if (storeBarrierSN == barr_sn) + storeBarrier = false; + } } template <class MemDepPred, class Impl> void MemDepUnit<MemDepPred, Impl>::wakeDependents(DynInstPtr &inst) { - // Only stores have dependents. - if (!inst->isStore()) { + // Only stores and barriers have dependents. + if (!inst->isStore() && !inst->isMemBarrier() && !inst->isWriteBarrier()) { return; } - // Wake any dependencies. - sd_it_t sd_it = storeDependents.find(inst->seqNum); + MemDepEntryPtr inst_entry = findInHash(inst); - // If there's no entry, then return. Really there should only be - // no entry if the instruction is a load. - if (sd_it == storeDependents.end()) { - DPRINTF(MemDepUnit, "MemDepUnit: Instruction PC %#x, sequence " - "number %i has no dependents.\n", - inst->readPC(), inst->seqNum); + for (int i = 0; i < inst_entry->dependInsts.size(); ++i ) { + MemDepEntryPtr woken_inst = inst_entry->dependInsts[i]; - return; - } - - for (int i = 0; i < (*sd_it).second.size(); ++i ) { - dep_it_t woken_inst = (*sd_it).second[i]; - - DPRINTF(MemDepUnit, "MemDepUnit: Waking up a dependent inst, " - "sequence number %i.\n", - (*woken_inst).seqNum); -#if 0 - // Should we have reached instructions that are actually squashed, - // there will be no more useful instructions in this dependency - // list. Break out early. - if (waitingInsts.find(woken_inst) == waitingInsts.end()) { - DPRINTF(MemDepUnit, "MemDepUnit: Dependents on inst PC %#x " - "are squashed, starting at SN %i. Breaking early.\n", - inst->readPC(), woken_inst); - break; + if (!woken_inst->inst) { + // Potentially removed mem dep entries could be on this list + continue; } -#endif - if ((*woken_inst).regsReady) { + DPRINTF(MemDepUnit, "Waking up a dependent inst, " + "[sn:%lli].\n", + woken_inst->inst->seqNum); + + if (woken_inst->regsReady && !woken_inst->squashed) { moveToReady(woken_inst); } else { - (*woken_inst).memDepReady = true; + woken_inst->memDepReady = true; } } - storeDependents.erase(sd_it); + inst_entry->dependInsts.clear(); } template <class MemDepPred, class Impl> void -MemDepUnit<MemDepPred, Impl>::squash(const InstSeqNum &squashed_num) +MemDepUnit<MemDepPred, Impl>::squash(const InstSeqNum &squashed_num, + unsigned tid) { - - if (!waitingInsts.empty()) { - dep_it_t waiting_it = waitingInsts.end(); - - --waiting_it; - - // Remove entries from the renamed list as long as we haven't reached - // the end and the entries continue to be younger than the squashed. - while (!waitingInsts.empty() && - (*waiting_it).seqNum > squashed_num) - { - if (!(*waiting_it).memDepReady && - (*waiting_it).storeDep != storeDependents.end()) { - sd_it_t sd_it = (*waiting_it).storeDep; - - // Make sure the iterator that the store has pointing - // back is actually to this instruction. - assert((*sd_it).second.back() == waiting_it); - - // Now remove this from the store's list of dependent - // instructions. - (*sd_it).second.pop_back(); + if (!instsToReplay.empty()) { + ListIt replay_it = instsToReplay.begin(); + while (replay_it != instsToReplay.end()) { + if ((*replay_it)->threadNumber == tid && + (*replay_it)->seqNum > squashed_num) { + instsToReplay.erase(replay_it++); + } else { + ++replay_it; } - - waitingInsts.erase(waiting_it--); } } - if (!readyInsts.empty()) { - sn_it_t ready_it = readyInsts.end(); + ListIt squash_it = instList[tid].end(); + --squash_it; - --ready_it; + MemDepHashIt hash_it; - // Same for the ready list. - while (!readyInsts.empty() && - (*ready_it) > squashed_num) - { - readyInsts.erase(ready_it--); - } - } + while (!instList[tid].empty() && + (*squash_it)->seqNum > squashed_num) { - if (!storeDependents.empty()) { - sd_it_t dep_it = storeDependents.end(); + DPRINTF(MemDepUnit, "Squashing inst [sn:%lli]\n", + (*squash_it)->seqNum); - --dep_it; + hash_it = memDepHash.find((*squash_it)->seqNum); - // Same for the dependencies list. - while (!storeDependents.empty() && - (*dep_it).first > squashed_num) - { - // This store's list of dependent instructions should be empty. - assert((*dep_it).second.empty()); + assert(hash_it != memDepHash.end()); - storeDependents.erase(dep_it--); - } + (*hash_it).second->squashed = true; + + (*hash_it).second = NULL; + + memDepHash.erase(hash_it); +#ifdef DEBUG + MemDepEntry::memdep_erase++; +#endif + + instList[tid].erase(squash_it--); } // Tell the dependency predictor to squash as well. - depPred.squash(squashed_num); + depPred.squash(squashed_num, tid); } template <class MemDepPred, class Impl> @@ -397,7 +498,7 @@ void MemDepUnit<MemDepPred, Impl>::violation(DynInstPtr &store_inst, DynInstPtr &violating_load) { - DPRINTF(MemDepUnit, "MemDepUnit: Passing violating PCs to store sets," + DPRINTF(MemDepUnit, "Passing violating PCs to store sets," " load: %#x, store: %#x\n", violating_load->readPC(), store_inst->readPC()); // Tell the memory dependence unit of the violation. @@ -405,15 +506,66 @@ MemDepUnit<MemDepPred, Impl>::violation(DynInstPtr &store_inst, } template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::issue(DynInstPtr &inst) +{ + DPRINTF(MemDepUnit, "Issuing instruction PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); + + depPred.issued(inst->readPC(), inst->seqNum, inst->isStore()); +} + +template <class MemDepPred, class Impl> +inline typename MemDepUnit<MemDepPred,Impl>::MemDepEntryPtr & +MemDepUnit<MemDepPred, Impl>::findInHash(const DynInstPtr &inst) +{ + MemDepHashIt hash_it = memDepHash.find(inst->seqNum); + + assert(hash_it != memDepHash.end()); + + return (*hash_it).second; +} + +template <class MemDepPred, class Impl> inline void -MemDepUnit<MemDepPred, Impl>::moveToReady(dep_it_t &woken_inst) +MemDepUnit<MemDepPred, Impl>::moveToReady(MemDepEntryPtr &woken_inst_entry) { - DPRINTF(MemDepUnit, "MemDepUnit: Adding instruction sequence number %i " - "to the ready list.\n", (*woken_inst).seqNum); + DPRINTF(MemDepUnit, "Adding instruction [sn:%lli] " + "to the ready list.\n", woken_inst_entry->inst->seqNum); - // Add it to the ready list. - readyInsts.insert((*woken_inst).seqNum); + assert(!woken_inst_entry->squashed); + + iqPtr->addReadyMemInst(woken_inst_entry->inst); +} - // Remove it from the waiting instructions. - waitingInsts.erase(woken_inst); + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::dumpLists() +{ + for (unsigned tid=0; tid < Impl::MaxThreads; tid++) { + cprintf("Instruction list %i size: %i\n", + tid, instList[tid].size()); + + ListIt inst_list_it = instList[tid].begin(); + int num = 0; + + while (inst_list_it != instList[tid].end()) { + cprintf("Instruction:%i\nPC:%#x\n[sn:%i]\n[tid:%i]\nIssued:%i\n" + "Squashed:%i\n\n", + num, (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + inst_list_it++; + ++num; + } + } + + cprintf("Memory dependence hash size: %i\n", memDepHash.size()); + +#ifdef DEBUG + cprintf("Memory dependence entries: %i\n", MemDepEntry::memdep_count); +#endif } diff --git a/src/cpu/o3/alpha_params.hh b/src/cpu/o3/params.hh index 79b0937e3..69a1bb937 100644..100755 --- a/src/cpu/o3/alpha_params.hh +++ b/src/cpu/o3/params.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,45 +24,35 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_ALPHA_SIMPLE_PARAMS_HH__ -#define __CPU_O3_CPU_ALPHA_SIMPLE_PARAMS_HH__ +#ifndef __CPU_O3_PARAMS_HH__ +#define __CPU_O3_PARAMS_HH__ #include "cpu/o3/cpu.hh" //Forward declarations -class System; -class AlphaITB; -class AlphaDTB; -class FunctionalMemory; -class Process; -class MemInterface; +class FUPool; /** - * This file defines the parameters that will be used for the AlphaFullCPU. + * This file defines the parameters that will be used for the O3CPU. * This must be defined externally so that the Impl can have a params class * defined that it can pass to all of the individual stages. */ - -class AlphaSimpleParams : public BaseFullCPU::Params +class O3Params : public BaseO3CPU::Params { public: - -#if FULL_SYSTEM - AlphaITB *itb; AlphaDTB *dtb; -#else - std::vector<Process *> workload; - Process *process; -#endif // FULL_SYSTEM - - FunctionalMemory *mem; + unsigned activity; // // Caches // - MemInterface *icacheInterface; - MemInterface *dcacheInterface; + // MemInterface *icacheInterface; + // MemInterface *dcacheInterface; + + unsigned cachePorts; // // Fetch @@ -97,11 +87,7 @@ class AlphaSimpleParams : public BaseFullCPU::Params unsigned renameToIEWDelay; unsigned issueToExecuteDelay; unsigned issueWidth; - unsigned executeWidth; - unsigned executeIntWidth; - unsigned executeFloatWidth; - unsigned executeBranchWidth; - unsigned executeMemoryWidth; + FUPool *fuPool; // // Commit @@ -110,24 +96,22 @@ class AlphaSimpleParams : public BaseFullCPU::Params unsigned renameToROBDelay; unsigned commitWidth; unsigned squashWidth; + Tick trapLatency; + Tick fetchTrapLatency; // - // Branch predictor (BP & BTB) + // Branch predictor (BP, BTB, RAS) // -/* + std::string predType; unsigned localPredictorSize; - unsigned localPredictorCtrBits; -*/ - - unsigned local_predictor_size; - unsigned local_ctr_bits; - unsigned local_history_table_size; - unsigned local_history_bits; - unsigned global_predictor_size; - unsigned global_ctr_bits; - unsigned global_history_bits; - unsigned choice_predictor_size; - unsigned choice_ctr_bits; + unsigned localCtrBits; + unsigned localHistoryTableSize; + unsigned localHistoryBits; + unsigned globalPredictorSize; + unsigned globalCtrBits; + unsigned globalHistoryBits; + unsigned choicePredictorSize; + unsigned choiceCtrBits; unsigned BTBEntries; unsigned BTBTagSize; @@ -154,10 +138,24 @@ class AlphaSimpleParams : public BaseFullCPU::Params unsigned numIQEntries; unsigned numROBEntries; + //SMT Parameters + unsigned smtNumFetchingThreads; + + std::string smtFetchPolicy; + + std::string smtIQPolicy; + unsigned smtIQThreshold; + + std::string smtLSQPolicy; + unsigned smtLSQThreshold; + + std::string smtCommitPolicy; + + std::string smtROBPolicy; + unsigned smtROBThreshold; + // Probably can get this from somewhere. unsigned instShiftAmt; - - bool defReg; }; -#endif // __CPU_O3_CPU_ALPHA_PARAMS_HH__ +#endif // __CPU_O3_ALPHA_PARAMS_HH__ diff --git a/src/cpu/o3/ras.cc b/src/cpu/o3/ras.cc index 0a7d6ca63..f9939259a 100644 --- a/src/cpu/o3/ras.cc +++ b/src/cpu/o3/ras.cc @@ -24,16 +24,30 @@ * 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. + * + * Authors: Kevin Lim */ #include "cpu/o3/ras.hh" -ReturnAddrStack::ReturnAddrStack(unsigned _numEntries) - : numEntries(_numEntries), usedEntries(0), - tos(0) +void +ReturnAddrStack::init(unsigned _numEntries) { - addrStack = new Addr[numEntries]; + numEntries = _numEntries; + usedEntries = 0; + tos = 0; + + addrStack.resize(numEntries); + + for (int i = 0; i < numEntries; ++i) + addrStack[i] = 0; +} +void +ReturnAddrStack::reset() +{ + usedEntries = 0; + tos = 0; for (int i = 0; i < numEntries; ++i) addrStack[i] = 0; } @@ -53,9 +67,6 @@ ReturnAddrStack::push(const Addr &return_addr) void ReturnAddrStack::pop() { - // Not sure it's possible to really track usedEntries properly. -// assert(usedEntries > 0); - if (usedEntries > 0) { --usedEntries; } diff --git a/src/cpu/o3/ras.hh b/src/cpu/o3/ras.hh index 46d98181e..5c8a93285 100644 --- a/src/cpu/o3/ras.hh +++ b/src/cpu/o3/ras.hh @@ -24,45 +24,74 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_RAS_HH__ -#define __CPU_O3_CPU_RAS_HH__ +#ifndef __CPU_O3_RAS_HH__ +#define __CPU_O3_RAS_HH__ // For Addr type. #include "arch/isa_traits.hh" +#include <vector> +/** Return address stack class, implements a simple RAS. */ class ReturnAddrStack { public: - ReturnAddrStack(unsigned numEntries); + /** Creates a return address stack, but init() must be called prior to + * use. + */ + ReturnAddrStack() {} + + /** Initializes RAS with a specified number of entries. + * @param numEntries Number of entries in the RAS. + */ + void init(unsigned numEntries); + + void reset(); + /** Returns the top address on the RAS. */ Addr top() { return addrStack[tos]; } + /** Returns the index of the top of the RAS. */ unsigned topIdx() { return tos; } + /** Pushes an address onto the RAS. */ void push(const Addr &return_addr); + /** Pops the top address from the RAS. */ void pop(); + /** Changes index to the top of the RAS, and replaces the top address with + * a new target. + * @param top_entry_idx The index of the RAS that will now be the top. + * @param restored_target The new target address of the new top of the RAS. + */ void restore(unsigned top_entry_idx, const Addr &restored_target); private: + /** Increments the top of stack index. */ inline void incrTos() { if (++tos == numEntries) tos = 0; } + /** Decrements the top of stack index. */ inline void decrTos() { tos = (tos == 0 ? numEntries - 1 : tos - 1); } - Addr *addrStack; + /** The RAS itself. */ + std::vector<Addr> addrStack; + /** The number of entries in the RAS. */ unsigned numEntries; + /** The number of used entries in the RAS. */ unsigned usedEntries; + /** The top of stack index. */ unsigned tos; }; -#endif // __CPU_O3_CPU_RAS_HH__ +#endif // __CPU_O3_RAS_HH__ diff --git a/src/cpu/o3/regfile.hh b/src/cpu/o3/regfile.hh index a5cfa8f3c..6972f055f 100644 --- a/src/cpu/o3/regfile.hh +++ b/src/cpu/o3/regfile.hh @@ -24,15 +24,17 @@ * 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. + * + * Authors: Kevin Lim + * Gabe Black */ -#ifndef __CPU_O3_CPU_REGFILE_HH__ -#define __CPU_O3_CPU_REGFILE_HH__ - -// @todo: Destructor +#ifndef __CPU_O3_REGFILE_HH__ +#define __CPU_O3_REGFILE_HH__ #include "arch/isa_traits.hh" #include "arch/faults.hh" +#include "arch/types.hh" #include "base/trace.hh" #include "config/full_system.hh" #include "cpu/o3/comm.hh" @@ -42,32 +44,40 @@ #endif -// This really only depends on the ISA, and not the Impl. It might be nicer -// to see if I can make it depend on nothing... -// Things that are in the ifdef FULL_SYSTEM are pretty dependent on the ISA, -// and should go in the AlphaFullCPU. +#include <vector> +/** + * Simple physical register file class. + * Right now this is specific to Alpha until we decide if/how to make things + * generic enough to support other ISAs. + */ template <class Impl> class PhysRegFile { protected: typedef TheISA::IntReg IntReg; typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; typedef TheISA::MiscRegFile MiscRegFile; typedef TheISA::MiscReg MiscReg; - //Note that most of the definitions of the IntReg, FloatReg, etc. exist - //within the Impl/ISA class and not within this PhysRegFile class. + typedef union { + FloatReg d; + FloatRegBits q; + } PhysFloatReg; - //Will need some way to allow stuff like swap_palshadow to access the - //correct registers. Might require code changes to swap_palshadow and - //other execution contexts. + // Note that most of the definitions of the IntReg, FloatReg, etc. exist + // within the Impl/ISA class and not within this PhysRegFile class. - //Will make these registers public for now, but they probably should - //be private eventually with some accessor functions. + // Will make these registers public for now, but they probably should + // be private eventually with some accessor functions. public: - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::O3CPU O3CPU; + /** + * Constructs a physical register file with the specified amount of + * integer and floating point registers. + */ PhysRegFile(unsigned _numPhysicalIntRegs, unsigned _numPhysicalFloatRegs); @@ -80,12 +90,13 @@ class PhysRegFile // void serialize(std::ostream &os); // void unserialize(Checkpoint *cp, const std::string §ion); + /** Reads an integer register. */ uint64_t readIntReg(PhysRegIndex reg_idx) { assert(reg_idx < numPhysicalIntRegs); DPRINTF(IEW, "RegFile: Access to int register %i, has data " - "%i\n", int(reg_idx), intRegFile[reg_idx]); + "%#x\n", int(reg_idx), intRegFile[reg_idx]); return intRegFile[reg_idx]; } @@ -96,14 +107,15 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - FloatReg floatReg = floatRegFile.readReg(reg_idx, width); + FloatReg floatReg = floatRegFile[reg_idx].d; DPRINTF(IEW, "RegFile: Access to %d byte float register %i, has " - "data %8.8d\n", int(reg_idx), (double)floatReg); + "data %#x\n", int(reg_idx), floatRegFile[reg_idx].q); return floatReg; } + /** Reads a floating point register (double precision). */ FloatReg readFloatReg(PhysRegIndex reg_idx) { // Remove the base Float reg dependency. @@ -111,14 +123,15 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - FloatReg floatReg = floatRegFile.readReg(reg_idx); + FloatReg floatReg = floatRegFile[reg_idx].d; DPRINTF(IEW, "RegFile: Access to float register %i, has " - "data %8.8d\n", int(reg_idx), (double)floatReg); + "data %#x\n", int(reg_idx), floatRegFile[reg_idx].q); return floatReg; } + /** Reads a floating point register as an integer. */ FloatRegBits readFloatRegBits(PhysRegIndex reg_idx, int width) { // Remove the base Float reg dependency. @@ -126,10 +139,10 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - FloatRegBits floatRegBits = floatRegFile.readRegBits(reg_idx, width); + FloatRegBits floatRegBits = floatRegFile[reg_idx].q; - DPRINTF(IEW, "RegFile: Access to %d byte float register %i as int, " - "has data %lli\n", int(reg_idx), (uint64_t)floatRegBits); + DPRINTF(IEW, "RegFile: Access to float register %i as int, " + "has data %#x\n", int(reg_idx), (uint64_t)floatRegBits); return floatRegBits; } @@ -141,24 +154,27 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - FloatRegBits floatRegBits = floatRegFile.readRegBits(reg_idx); + FloatRegBits floatRegBits = floatRegFile[reg_idx].q; DPRINTF(IEW, "RegFile: Access to float register %i as int, " - "has data %lli\n", int(reg_idx), (uint64_t)floatRegBits); + "has data %#x\n", int(reg_idx), (uint64_t)floatRegBits); return floatRegBits; } + /** Sets an integer register to the given value. */ void setIntReg(PhysRegIndex reg_idx, uint64_t val) { assert(reg_idx < numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting int register %i to %lli\n", + DPRINTF(IEW, "RegFile: Setting int register %i to %#x\n", int(reg_idx), val); - intRegFile[reg_idx] = val; + if (reg_idx != TheISA::ZeroReg) + intRegFile[reg_idx] = val; } + /** Sets a single precision floating point register to the given value. */ void setFloatReg(PhysRegIndex reg_idx, FloatReg val, int width) { // Remove the base Float reg dependency. @@ -166,12 +182,14 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting float register %i to %8.8d\n", - int(reg_idx), (double)val); + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", + int(reg_idx), (uint64_t)val); - floatRegFile.setReg(reg_idx, val, width); + if (reg_idx != TheISA::ZeroReg) + floatRegFile[reg_idx].d = val; } + /** Sets a double precision floating point register to the given value. */ void setFloatReg(PhysRegIndex reg_idx, FloatReg val) { // Remove the base Float reg dependency. @@ -179,12 +197,14 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting float register %i to %8.8d\n", - int(reg_idx), (double)val); + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", + int(reg_idx), (uint64_t)val); - floatRegFile.setReg(reg_idx, val); + if (reg_idx != TheISA::ZeroReg) + floatRegFile[reg_idx].d = val; } + /** Sets a floating point register to the given integer value. */ void setFloatRegBits(PhysRegIndex reg_idx, FloatRegBits val, int width) { // Remove the base Float reg dependency. @@ -192,10 +212,10 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting float register %i to %lli\n", + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", int(reg_idx), (uint64_t)val); - floatRegFile.setRegBits(reg_idx, val, width); + floatRegFile[reg_idx].q = val; } void setFloatRegBits(PhysRegIndex reg_idx, FloatRegBits val) @@ -205,81 +225,68 @@ class PhysRegFile assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); - DPRINTF(IEW, "RegFile: Setting float register %i to %lli\n", + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", int(reg_idx), (uint64_t)val); - floatRegFile.setRegBits(reg_idx, val); - } - - uint64_t readPC() - { - return pc; + floatRegFile[reg_idx].q = val; } - void setPC(uint64_t val) + MiscReg readMiscReg(int misc_reg, unsigned thread_id) { - pc = val; + return miscRegs[thread_id].readReg(misc_reg); } - void setNextPC(uint64_t val) + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault, + unsigned thread_id) { - npc = val; + return miscRegs[thread_id].readRegWithEffect(misc_reg, fault, + cpu->tcBase(thread_id)); } - //Consider leaving this stuff and below in some implementation specific - //file as opposed to the general register file. Or have a derived class. - MiscReg readMiscReg(int misc_reg) + Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned thread_id) { - // Dummy function for now. - // @todo: Fix this once proxy XC is used. - return 0; + return miscRegs[thread_id].setReg(misc_reg, val); } - Fault setMiscReg(int misc_reg, const MiscReg &val) + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val, + unsigned thread_id) { - // Dummy function for now. - // @todo: Fix this once proxy XC is used. - return NoFault; + return miscRegs[thread_id].setRegWithEffect(misc_reg, val, + cpu->tcBase(thread_id)); } #if FULL_SYSTEM int readIntrFlag() { return intrflag; } + /** Sets an interrupt flag. */ void setIntrFlag(int val) { intrflag = val; } #endif - // These should be private eventually, but will be public for now - // so that I can hack around the initregs issue. public: /** (signed) integer register file. */ IntReg *intRegFile; /** Floating point register file. */ - FloatReg *floatRegFile; + PhysFloatReg *floatRegFile; /** Miscellaneous register file. */ - MiscRegFile miscRegs; - - /** Program counter. */ - Addr pc; - - /** Next-cycle program counter. */ - Addr npc; + MiscRegFile miscRegs[Impl::MaxThreads]; #if FULL_SYSTEM private: - // This is ISA specifc stuff; remove it eventually once ISAImpl is used -// IntReg palregs[NumIntRegs]; // PAL shadow registers int intrflag; // interrupt flag - bool pal_shadow; // using pal_shadow registers #endif private: - FullCPU *cpu; + /** CPU pointer. */ + O3CPU *cpu; public: - void setCPU(FullCPU *cpu_ptr) { cpu = cpu_ptr; } + /** Sets the CPU pointer. */ + void setCPU(O3CPU *cpu_ptr) { cpu = cpu_ptr; } + /** Number of physical integer registers. */ unsigned numPhysicalIntRegs; + /** Number of physical floating point registers. */ unsigned numPhysicalFloatRegs; }; @@ -290,10 +297,14 @@ PhysRegFile<Impl>::PhysRegFile(unsigned _numPhysicalIntRegs, numPhysicalFloatRegs(_numPhysicalFloatRegs) { intRegFile = new IntReg[numPhysicalIntRegs]; - floatRegFile = new FloatReg[numPhysicalFloatRegs]; + floatRegFile = new PhysFloatReg[numPhysicalFloatRegs]; - memset(intRegFile, 0, sizeof(*intRegFile)); - memset(floatRegFile, 0, sizeof(*floatRegFile)); + for (int i = 0; i < Impl::MaxThreads; ++i) { + miscRegs[i].clear(); + } + + memset(intRegFile, 0, sizeof(IntReg) * numPhysicalIntRegs); + memset(floatRegFile, 0, sizeof(PhysFloatReg) * numPhysicalFloatRegs); } -#endif // __CPU_O3_CPU_REGFILE_HH__ +#endif diff --git a/src/cpu/o3/rename.cc b/src/cpu/o3/rename.cc index 6e9ee23da..443ada0cb 100644 --- a/src/cpu/o3/rename.cc +++ b/src/cpu/o3/rename.cc @@ -24,10 +24,11 @@ * 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. + * + * Authors: Kevin Lim */ -#include "cpu/o3/alpha_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/isa_specific.hh" #include "cpu/o3/rename_impl.hh" -template class SimpleRename<AlphaSimpleImpl>; +template class DefaultRename<O3CPUImpl>; diff --git a/src/cpu/o3/rename.hh b/src/cpu/o3/rename.hh index 07b442964..581fc8f81 100644 --- a/src/cpu/o3/rename.hh +++ b/src/cpu/o3/rename.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,139 +24,276 @@ * 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. + * + * Authors: Kevin Lim */ -// Todo: -// Fix up trap and barrier handling. -// May want to have different statuses to differentiate the different stall -// conditions. - -#ifndef __CPU_O3_CPU_SIMPLE_RENAME_HH__ -#define __CPU_O3_CPU_SIMPLE_RENAME_HH__ +#ifndef __CPU_O3_RENAME_HH__ +#define __CPU_O3_RENAME_HH__ #include <list> #include "base/statistics.hh" #include "base/timebuf.hh" -// Will need rename maps for both the int reg file and fp reg file. -// Or change rename map class to handle both. (RegFile handles both.) +/** + * DefaultRename handles both single threaded and SMT rename. Its + * width is specified by the parameters; each cycle it tries to rename + * that many instructions. It holds onto the rename history of all + * instructions with destination registers, storing the + * arch. register, the new physical register, and the old physical + * register, to allow for undoing of mappings if squashing happens, or + * freeing up registers upon commit. Rename handles blocking if the + * ROB, IQ, or LSQ is going to be full. Rename also handles barriers, + * and does so by stalling on the instruction until the ROB is empty + * and there are no instructions in flight to the ROB. + */ template<class Impl> -class SimpleRename +class DefaultRename { public: // Typedefs from the Impl. typedef typename Impl::CPUPol CPUPol; typedef typename Impl::DynInstPtr DynInstPtr; - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::O3CPU O3CPU; typedef typename Impl::Params Params; - typedef typename CPUPol::FetchStruct FetchStruct; + // Typedefs from the CPUPol typedef typename CPUPol::DecodeStruct DecodeStruct; typedef typename CPUPol::RenameStruct RenameStruct; typedef typename CPUPol::TimeStruct TimeStruct; - - // Typedefs from the CPUPol typedef typename CPUPol::FreeList FreeList; typedef typename CPUPol::RenameMap RenameMap; + // These are used only for initialization. + typedef typename CPUPol::IEW IEW; + typedef typename CPUPol::Commit Commit; // Typedefs from the ISA. typedef TheISA::RegIndex RegIndex; + // A list is used to queue the instructions. Barrier insts must + // be added to the front of the list, which is the only reason for + // using a list instead of a queue. (Most other stages use a + // queue) + typedef std::list<DynInstPtr> InstQueue; + public: - // Rename will block if ROB becomes full or issue queue becomes full, - // or there are no free registers to rename to. - // Only case where rename squashes is if IEW squashes. - enum Status { + /** Overall rename status. Used to determine if the CPU can + * deschedule itself due to a lack of activity. + */ + enum RenameStatus { + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { Running, Idle, + StartSquash, Squashing, Blocked, Unblocking, - BarrierStall + SerializeStall }; private: - Status _status; + /** Rename status. */ + RenameStatus _status; + + /** Per-thread status. */ + ThreadStatus renameStatus[Impl::MaxThreads]; public: - SimpleRename(Params ¶ms); + /** DefaultRename constructor. */ + DefaultRename(Params *params); + + /** Returns the name of rename. */ + std::string name() const; + /** Registers statistics. */ void regStats(); - void setCPU(FullCPU *cpu_ptr); + /** Sets CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + /** Sets the main backwards communication time buffer pointer. */ void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + /** Sets pointer to time buffer used to communicate to the next stage. */ void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr); + /** Sets pointer to time buffer coming from decode. */ void setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr); - void setRenameMap(RenameMap *rm_ptr); + /** Sets pointer to IEW stage. Used only for initialization. */ + void setIEWStage(IEW *iew_stage) + { iew_ptr = iew_stage; } + + /** Sets pointer to commit stage. Used only for initialization. */ + void setCommitStage(Commit *commit_stage) + { commit_ptr = commit_stage; } + + private: + /** Pointer to IEW stage. Used only for initialization. */ + IEW *iew_ptr; + + /** Pointer to commit stage. Used only for initialization. */ + Commit *commit_ptr; + + public: + /** Initializes variables for the stage. */ + void initStage(); + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to rename maps (per-thread structures). */ + void setRenameMap(RenameMap rm_ptr[Impl::MaxThreads]); + /** Sets pointer to the free list. */ void setFreeList(FreeList *fl_ptr); - void dumpHistory(); + /** Sets pointer to the scoreboard. */ + void setScoreboard(Scoreboard *_scoreboard); - void tick(); + /** Switches out the rename stage. */ + void switchOut(); + + /** Completes the switch out. */ + void doSwitchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Squashes all instructions in a thread. */ + void squash(unsigned tid); - void rename(); + /** Ticks rename, which processes all input signals and attempts to rename + * as many instructions as possible. + */ + void tick(); - void squash(); + /** Debugging function used to dump history buffer of renamings. */ + void dumpHistory(); private: - void block(); + /** Determines what to do based on rename's current status. + * @param status_change rename() sets this variable if there was a status + * change (ie switching from blocking to unblocking). + * @param tid Thread id to rename instructions from. + */ + void rename(bool &status_change, unsigned tid); + + /** Renames instructions for the given thread. Also handles serializing + * instructions. + */ + void renameInsts(unsigned tid); + + /** Inserts unused instructions from a given thread into the skid buffer, + * to be renamed once rename unblocks. + */ + void skidInsert(unsigned tid); + + /** Separates instructions from decode into individual lists of instructions + * sorted by thread. + */ + void sortInsts(); + + /** Returns if all of the skid buffers are empty. */ + bool skidsEmpty(); + + /** Updates overall rename status based on all of the threads' statuses. */ + void updateStatus(); + + /** Switches rename to blocking, and signals back that rename has become + * blocked. + * @return Returns true if there is a status change. + */ + bool block(unsigned tid); + + /** Switches rename to unblocking if the skid buffer is empty, and signals + * back that rename has unblocked. + * @return Returns true if there is a status change. + */ + bool unblock(unsigned tid); + + /** Executes actual squash, removing squashed instructions. */ + void doSquash(unsigned tid); + + /** Removes a committed instruction's rename history. */ + void removeFromHistory(InstSeqNum inst_seq_num, unsigned tid); + + /** Renames the source registers of an instruction. */ + inline void renameSrcRegs(DynInstPtr &inst, unsigned tid); + + /** Renames the destination registers of an instruction. */ + inline void renameDestRegs(DynInstPtr &inst, unsigned tid); - inline void unblock(); + /** Calculates the number of free ROB entries for a specific thread. */ + inline int calcFreeROBEntries(unsigned tid); - void doSquash(); + /** Calculates the number of free IQ entries for a specific thread. */ + inline int calcFreeIQEntries(unsigned tid); - void removeFromHistory(InstSeqNum inst_seq_num); + /** Calculates the number of free LSQ entries for a specific thread. */ + inline int calcFreeLSQEntries(unsigned tid); - inline void renameSrcRegs(DynInstPtr &inst); + /** Returns the number of valid instructions coming from decode. */ + unsigned validInsts(); - inline void renameDestRegs(DynInstPtr &inst); + /** Reads signals telling rename to block/unblock. */ + void readStallSignals(unsigned tid); - inline int calcFreeROBEntries(); + /** Checks if any stages are telling rename to block. */ + bool checkStall(unsigned tid); - inline int calcFreeIQEntries(); + /** Gets the number of free entries for a specific thread. */ + void readFreeEntries(unsigned tid); - /** Holds the previous information for each rename. - * Note that often times the inst may have been deleted, so only access - * the pointer for the address and do not dereference it. + /** Checks the signals and updates the status. */ + bool checkSignalsAndUpdate(unsigned tid); + + /** Either serializes on the next instruction available in the InstQueue, + * or records that it must serialize on the next instruction to enter + * rename. + * @param inst_list The list of younger, unprocessed instructions for the + * thread that has the serializeAfter instruction. + * @param tid The thread id. + */ + void serializeAfter(InstQueue &inst_list, unsigned tid); + + /** Holds the information for each destination register rename. It holds + * the instruction's sequence number, the arch register, the old physical + * register for that arch. register, and the new physical register. */ struct RenameHistory { RenameHistory(InstSeqNum _instSeqNum, RegIndex _archReg, PhysRegIndex _newPhysReg, PhysRegIndex _prevPhysReg) : instSeqNum(_instSeqNum), archReg(_archReg), - newPhysReg(_newPhysReg), prevPhysReg(_prevPhysReg), - placeHolder(false) - { - } - - /** Constructor used specifically for cases where a place holder - * rename history entry is being made. - */ - RenameHistory(InstSeqNum _instSeqNum) - : instSeqNum(_instSeqNum), archReg(0), newPhysReg(0), - prevPhysReg(0), placeHolder(true) + newPhysReg(_newPhysReg), prevPhysReg(_prevPhysReg) { } + /** The sequence number of the instruction that renamed. */ InstSeqNum instSeqNum; + /** The architectural register index that was renamed. */ RegIndex archReg; + /** The new physical register that the arch. register is renamed to. */ PhysRegIndex newPhysReg; + /** The old physical register that the arch. register was renamed to. */ PhysRegIndex prevPhysReg; - bool placeHolder; }; - std::list<RenameHistory> historyBuffer; + /** A per-thread list of all destination register renames, used to either + * undo rename mappings or free old physical registers. + */ + std::list<RenameHistory> historyBuffer[Impl::MaxThreads]; - /** CPU interface. */ - FullCPU *cpu; + /** Pointer to CPU. */ + O3CPU *cpu; - // Interfaces to objects outside of rename. - /** Time buffer interface. */ + /** Pointer to main time buffer used for backwards communication. */ TimeBuffer<TimeStruct> *timeBuffer; /** Wire to get IEW's output from backwards time buffer. */ @@ -166,7 +303,6 @@ class SimpleRename typename TimeBuffer<TimeStruct>::wire fromCommit; /** Wire to write infromation heading to previous stages. */ - // Might not be the best name as not only decode will read it. typename TimeBuffer<TimeStruct>::wire toDecode; /** Rename instruction queue. */ @@ -181,15 +317,71 @@ class SimpleRename /** Wire to get decode's output from decode queue. */ typename TimeBuffer<DecodeStruct>::wire fromDecode; + /** Queue of all instructions coming from decode this cycle. */ + InstQueue insts[Impl::MaxThreads]; + /** Skid buffer between rename and decode. */ - std::queue<DecodeStruct> skidBuffer; + InstQueue skidBuffer[Impl::MaxThreads]; /** Rename map interface. */ - SimpleRenameMap *renameMap; + RenameMap *renameMap[Impl::MaxThreads]; /** Free list interface. */ FreeList *freeList; + /** Pointer to the list of active threads. */ + std::list<unsigned> *activeThreads; + + /** Pointer to the scoreboard. */ + Scoreboard *scoreboard; + + /** Count of instructions in progress that have been sent off to the IQ + * and ROB, but are not yet included in their occupancy counts. + */ + int instsInProgress[Impl::MaxThreads]; + + /** Variable that tracks if decode has written to the time buffer this + * cycle. Used to tell CPU if there is activity this cycle. + */ + bool wroteToTimeBuffer; + + /** Structures whose free entries impact the amount of instructions that + * can be renamed. + */ + struct FreeEntries { + unsigned iqEntries; + unsigned lsqEntries; + unsigned robEntries; + }; + + /** Per-thread tracking of the number of free entries of back-end + * structures. + */ + FreeEntries freeEntries[Impl::MaxThreads]; + + /** Records if the ROB is empty. In SMT mode the ROB may be dynamically + * partitioned between threads, so the ROB must tell rename when it is + * empty. + */ + bool emptyROB[Impl::MaxThreads]; + + /** Source of possible stalls. */ + struct Stalls { + bool iew; + bool commit; + }; + + /** Tracks which stages are telling decode to stall. */ + Stalls stalls[Impl::MaxThreads]; + + /** The serialize instruction that rename has stalled on. */ + DynInstPtr serializeInst[Impl::MaxThreads]; + + /** Records if rename needs to serialize on the next instruction for any + * thread. + */ + bool serializeOnNextInst[Impl::MaxThreads]; + /** Delay between iew and rename, in ticks. */ int iewToRenameDelay; @@ -207,27 +399,74 @@ class SimpleRename */ unsigned commitWidth; - /** The instruction that rename is currently on. It needs to have - * persistent state so that when a stall occurs in the middle of a - * group of instructions, it can restart at the proper instruction. + /** The index of the instruction in the time buffer to IEW that rename is + * currently using. + */ + unsigned toIEWIndex; + + /** Whether or not rename needs to block this cycle. */ + bool blockThisCycle; + + /** The number of threads active in rename. */ + unsigned numThreads; + + /** The maximum skid buffer size. */ + unsigned skidBufferMax; + + /** Enum to record the source of a structure full stall. Can come from + * either ROB, IQ, LSQ, and it is priortized in that order. + */ + enum FullSource { + ROB, + IQ, + LSQ, + NONE + }; + + /** Function used to increment the stat that corresponds to the source of + * the stall. */ - unsigned numInst; + inline void incrFullStat(const FullSource &source); + /** Stat for total number of cycles spent squashing. */ Stats::Scalar<> renameSquashCycles; + /** Stat for total number of cycles spent idle. */ Stats::Scalar<> renameIdleCycles; + /** Stat for total number of cycles spent blocking. */ Stats::Scalar<> renameBlockCycles; + /** Stat for total number of cycles spent stalling for a serializing inst. */ + Stats::Scalar<> renameSerializeStallCycles; + /** Stat for total number of cycles spent running normally. */ + Stats::Scalar<> renameRunCycles; + /** Stat for total number of cycles spent unblocking. */ Stats::Scalar<> renameUnblockCycles; + /** Stat for total number of renamed instructions. */ Stats::Scalar<> renameRenamedInsts; + /** Stat for total number of squashed instructions that rename discards. */ Stats::Scalar<> renameSquashedInsts; + /** Stat for total number of times that the ROB starts a stall in rename. */ Stats::Scalar<> renameROBFullEvents; + /** Stat for total number of times that the IQ starts a stall in rename. */ Stats::Scalar<> renameIQFullEvents; + /** Stat for total number of times that the LSQ starts a stall in rename. */ + Stats::Scalar<> renameLSQFullEvents; + /** Stat for total number of times that rename runs out of free registers + * to use to rename. */ Stats::Scalar<> renameFullRegistersEvents; + /** Stat for total number of renamed destination registers. */ Stats::Scalar<> renameRenamedOperands; + /** Stat for total number of source register rename lookups. */ Stats::Scalar<> renameRenameLookups; - Stats::Scalar<> renameHBPlaceHolders; + /** Stat for total number of committed renaming mappings. */ Stats::Scalar<> renameCommittedMaps; + /** Stat for total number of mappings that were undone due to a squash. */ Stats::Scalar<> renameUndoneMaps; - Stats::Scalar<> renameValidUndoneMaps; + /** Number of serialize instructions handled. */ + Stats::Scalar<> renamedSerializing; + /** Number of instructions marked as temporarily serializing. */ + Stats::Scalar<> renamedTempSerializing; + /** Number of instructions inserted into skid buffers. */ + Stats::Scalar<> renameSkidInsts; }; -#endif // __CPU_O3_CPU_SIMPLE_RENAME_HH__ +#endif // __CPU_O3_RENAME_HH__ diff --git a/src/cpu/o3/rename_impl.hh b/src/cpu/o3/rename_impl.hh index 2068b36ab..df8b7f9da 100644 --- a/src/cpu/o3/rename_impl.hh +++ b/src/cpu/o3/rename_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +24,8 @@ * 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. + * + * Authors: Kevin Lim */ #include <list> @@ -31,97 +33,146 @@ #include "config/full_system.hh" #include "cpu/o3/rename.hh" +using namespace std; + +template <class Impl> +DefaultRename<Impl>::DefaultRename(Params *params) + : iewToRenameDelay(params->iewToRenameDelay), + decodeToRenameDelay(params->decodeToRenameDelay), + commitToRenameDelay(params->commitToRenameDelay), + renameWidth(params->renameWidth), + commitWidth(params->commitWidth), + numThreads(params->numberOfThreads) +{ + _status = Inactive; + + for (int i=0; i< numThreads; i++) { + renameStatus[i] = Idle; + + freeEntries[i].iqEntries = 0; + freeEntries[i].lsqEntries = 0; + freeEntries[i].robEntries = 0; + + stalls[i].iew = false; + stalls[i].commit = false; + serializeInst[i] = NULL; + + instsInProgress[i] = 0; + + emptyROB[i] = true; + + serializeOnNextInst[i] = false; + } + + // @todo: Make into a parameter. + skidBufferMax = (2 * (iewToRenameDelay * params->decodeWidth)) + renameWidth; +} + template <class Impl> -SimpleRename<Impl>::SimpleRename(Params ¶ms) - : iewToRenameDelay(params.iewToRenameDelay), - decodeToRenameDelay(params.decodeToRenameDelay), - commitToRenameDelay(params.commitToRenameDelay), - renameWidth(params.renameWidth), - commitWidth(params.commitWidth), - numInst(0) +std::string +DefaultRename<Impl>::name() const { - _status = Idle; + return cpu->name() + ".rename"; } template <class Impl> void -SimpleRename<Impl>::regStats() +DefaultRename<Impl>::regStats() { renameSquashCycles - .name(name() + ".renameSquashCycles") + .name(name() + ".RENAME:SquashCycles") .desc("Number of cycles rename is squashing") .prereq(renameSquashCycles); renameIdleCycles - .name(name() + ".renameIdleCycles") + .name(name() + ".RENAME:IdleCycles") .desc("Number of cycles rename is idle") .prereq(renameIdleCycles); renameBlockCycles - .name(name() + ".renameBlockCycles") + .name(name() + ".RENAME:BlockCycles") .desc("Number of cycles rename is blocking") .prereq(renameBlockCycles); + renameSerializeStallCycles + .name(name() + ".RENAME:serializeStallCycles") + .desc("count of cycles rename stalled for serializing inst") + .flags(Stats::total); + renameRunCycles + .name(name() + ".RENAME:RunCycles") + .desc("Number of cycles rename is running") + .prereq(renameIdleCycles); renameUnblockCycles - .name(name() + ".renameUnblockCycles") + .name(name() + ".RENAME:UnblockCycles") .desc("Number of cycles rename is unblocking") .prereq(renameUnblockCycles); renameRenamedInsts - .name(name() + ".renameRenamedInsts") + .name(name() + ".RENAME:RenamedInsts") .desc("Number of instructions processed by rename") .prereq(renameRenamedInsts); renameSquashedInsts - .name(name() + ".renameSquashedInsts") + .name(name() + ".RENAME:SquashedInsts") .desc("Number of squashed instructions processed by rename") .prereq(renameSquashedInsts); renameROBFullEvents - .name(name() + ".renameROBFullEvents") - .desc("Number of times rename has considered the ROB 'full'") + .name(name() + ".RENAME:ROBFullEvents") + .desc("Number of times rename has blocked due to ROB full") .prereq(renameROBFullEvents); renameIQFullEvents - .name(name() + ".renameIQFullEvents") - .desc("Number of times rename has considered the IQ 'full'") + .name(name() + ".RENAME:IQFullEvents") + .desc("Number of times rename has blocked due to IQ full") .prereq(renameIQFullEvents); + renameLSQFullEvents + .name(name() + ".RENAME:LSQFullEvents") + .desc("Number of times rename has blocked due to LSQ full") + .prereq(renameLSQFullEvents); renameFullRegistersEvents - .name(name() + ".renameFullRegisterEvents") + .name(name() + ".RENAME:FullRegisterEvents") .desc("Number of times there has been no free registers") .prereq(renameFullRegistersEvents); renameRenamedOperands - .name(name() + ".renameRenamedOperands") + .name(name() + ".RENAME:RenamedOperands") .desc("Number of destination operands rename has renamed") .prereq(renameRenamedOperands); renameRenameLookups - .name(name() + ".renameRenameLookups") + .name(name() + ".RENAME:RenameLookups") .desc("Number of register rename lookups that rename has made") .prereq(renameRenameLookups); - renameHBPlaceHolders - .name(name() + ".renameHBPlaceHolders") - .desc("Number of place holders added to the history buffer") - .prereq(renameHBPlaceHolders); renameCommittedMaps - .name(name() + ".renameCommittedMaps") + .name(name() + ".RENAME:CommittedMaps") .desc("Number of HB maps that are committed") .prereq(renameCommittedMaps); renameUndoneMaps - .name(name() + ".renameUndoneMaps") + .name(name() + ".RENAME:UndoneMaps") .desc("Number of HB maps that are undone due to squashing") .prereq(renameUndoneMaps); - renameValidUndoneMaps - .name(name() + ".renameValidUndoneMaps") - .desc("Number of HB maps that are undone, and are not place holders") - .prereq(renameValidUndoneMaps); + renamedSerializing + .name(name() + ".RENAME:serializingInsts") + .desc("count of serializing insts renamed") + .flags(Stats::total) + ; + renamedTempSerializing + .name(name() + ".RENAME:tempSerializingInsts") + .desc("count of temporary serializing insts renamed") + .flags(Stats::total) + ; + renameSkidInsts + .name(name() + ".RENAME:skidInsts") + .desc("count of insts added to the skid buffer") + .flags(Stats::total) + ; } template <class Impl> void -SimpleRename<Impl>::setCPU(FullCPU *cpu_ptr) +DefaultRename<Impl>::setCPU(O3CPU *cpu_ptr) { - DPRINTF(Rename, "Rename: Setting CPU pointer.\n"); + DPRINTF(Rename, "Setting CPU pointer.\n"); cpu = cpu_ptr; } template <class Impl> void -SimpleRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +DefaultRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) { - DPRINTF(Rename, "Rename: Setting time buffer pointer.\n"); + DPRINTF(Rename, "Setting time buffer pointer.\n"); timeBuffer = tb_ptr; // Setup wire to read information from time buffer, from IEW stage. @@ -136,9 +187,9 @@ SimpleRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) template <class Impl> void -SimpleRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) +DefaultRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) { - DPRINTF(Rename, "Rename: Setting rename queue pointer.\n"); + DPRINTF(Rename, "Setting rename queue pointer.\n"); renameQueue = rq_ptr; // Setup wire to write information to future stages. @@ -147,9 +198,9 @@ SimpleRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) template <class Impl> void -SimpleRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) +DefaultRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) { - DPRINTF(Rename, "Rename: Setting decode queue pointer.\n"); + DPRINTF(Rename, "Setting decode queue pointer.\n"); decodeQueue = dq_ptr; // Setup wire to get information from decode. @@ -158,214 +209,721 @@ SimpleRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) template <class Impl> void -SimpleRename<Impl>::setRenameMap(RenameMap *rm_ptr) +DefaultRename<Impl>::initStage() { - DPRINTF(Rename, "Rename: Setting rename map pointer.\n"); - renameMap = rm_ptr; + // Grab the number of free entries directly from the stages. + for (int tid=0; tid < numThreads; tid++) { + freeEntries[tid].iqEntries = iew_ptr->instQueue.numFreeEntries(tid); + freeEntries[tid].lsqEntries = iew_ptr->ldstQueue.numFreeEntries(tid); + freeEntries[tid].robEntries = commit_ptr->numROBFreeEntries(tid); + emptyROB[tid] = true; + } } +template<class Impl> +void +DefaultRename<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(Rename, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + + template <class Impl> void -SimpleRename<Impl>::setFreeList(FreeList *fl_ptr) +DefaultRename<Impl>::setRenameMap(RenameMap rm_ptr[]) { - DPRINTF(Rename, "Rename: Setting free list pointer.\n"); + DPRINTF(Rename, "Setting rename map pointers.\n"); + + for (int i=0; i<numThreads; i++) { + renameMap[i] = &rm_ptr[i]; + } +} + +template <class Impl> +void +DefaultRename<Impl>::setFreeList(FreeList *fl_ptr) +{ + DPRINTF(Rename, "Setting free list pointer.\n"); freeList = fl_ptr; } +template<class Impl> +void +DefaultRename<Impl>::setScoreboard(Scoreboard *_scoreboard) +{ + DPRINTF(Rename, "Setting scoreboard pointer.\n"); + scoreboard = _scoreboard; +} + +template <class Impl> +void +DefaultRename<Impl>::switchOut() +{ + // Rename is ready to switch out at any time. + cpu->signalSwitched(); +} + template <class Impl> void -SimpleRename<Impl>::dumpHistory() +DefaultRename<Impl>::doSwitchOut() { - typename list<RenameHistory>::iterator buf_it = historyBuffer.begin(); + // Clear any state, fix up the rename map. + for (int i = 0; i < numThreads; i++) { + typename list<RenameHistory>::iterator hb_it = historyBuffer[i].begin(); - while (buf_it != historyBuffer.end()) - { - cprintf("Seq num: %i\nArch reg: %i New phys reg: %i Old phys " - "reg: %i\n", (*buf_it).instSeqNum, (int)(*buf_it).archReg, - (int)(*buf_it).newPhysReg, (int)(*buf_it).prevPhysReg); + while (!historyBuffer[i].empty()) { + assert(hb_it != historyBuffer[i].end()); - buf_it++; + DPRINTF(Rename, "[tid:%u]: Removing history entry with sequence " + "number %i.\n", i, (*hb_it).instSeqNum); + + // Tell the rename map to set the architected register to the + // previous physical register that it was renamed to. + renameMap[i]->setEntry(hb_it->archReg, hb_it->prevPhysReg); + + // Put the renamed physical register back on the free list. + freeList->addReg(hb_it->newPhysReg); + + historyBuffer[i].erase(hb_it++); + } + insts[i].clear(); + skidBuffer[i].clear(); } } template <class Impl> void -SimpleRename<Impl>::block() +DefaultRename<Impl>::takeOverFrom() { - DPRINTF(Rename, "Rename: Blocking.\n"); - // Set status to Blocked. - _status = Blocked; + _status = Inactive; + initStage(); - // Add the current inputs onto the skid buffer, so they can be - // reprocessed when this stage unblocks. - skidBuffer.push(*fromDecode); + // Reset all state prior to taking over from the other CPU. + for (int i=0; i< numThreads; i++) { + renameStatus[i] = Idle; + + stalls[i].iew = false; + stalls[i].commit = false; + serializeInst[i] = NULL; + + instsInProgress[i] = 0; - // Note that this stage only signals previous stages to stall when - // it is the cause of the stall originates at this stage. Otherwise - // the previous stages are expected to check all possible stall signals. + emptyROB[i] = true; + + serializeOnNextInst[i] = false; + } } template <class Impl> -inline void -SimpleRename<Impl>::unblock() -{ - DPRINTF(Rename, "Rename: Read instructions out of skid buffer this " - "cycle.\n"); - // Remove the now processed instructions from the skid buffer. - skidBuffer.pop(); - - // If there's still information in the skid buffer, then - // continue to tell previous stages to stall. They will be - // able to restart once the skid buffer is empty. - if (!skidBuffer.empty()) { - toDecode->renameInfo.stall = true; - } else { - DPRINTF(Rename, "Rename: Done unblocking.\n"); - _status = Running; +void +DefaultRename<Impl>::squash(unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Squashing instructions.\n",tid); + + // Clear the stall signal if rename was blocked or unblocking before. + // If it still needs to block, the blocking should happen the next + // cycle and there should be space to hold everything due to the squash. + if (renameStatus[tid] == Blocked || + renameStatus[tid] == Unblocking || + renameStatus[tid] == SerializeStall) { + + toDecode->renameUnblock[tid] = 1; + + serializeInst[tid] = NULL; } + + // Set the status to Squashing. + renameStatus[tid] = Squashing; + + // Squash any instructions from decode. + unsigned squashCount = 0; + + for (int i=0; i<fromDecode->size; i++) { + if (fromDecode->insts[i]->threadNumber == tid) { + fromDecode->insts[i]->setSquashed(); + wroteToTimeBuffer = true; + squashCount++; + } + } + + insts[tid].clear(); + + // Clear the skid buffer in case it has any data in it. + skidBuffer[tid].clear(); + + doSquash(tid); } template <class Impl> void -SimpleRename<Impl>::doSquash() +DefaultRename<Impl>::tick() { - typename list<RenameHistory>::iterator hb_it = historyBuffer.begin(); + wroteToTimeBuffer = false; - InstSeqNum squashed_seq_num = fromCommit->commitInfo.doneSeqNum; + blockThisCycle = false; -#if FULL_SYSTEM - assert(!historyBuffer.empty()); -#else - // After a syscall squashes everything, the history buffer may be empty - // but the ROB may still be squashing instructions. - if (historyBuffer.empty()) { + bool status_change = false; + + toIEWIndex = 0; + + sortInsts(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + // Check stall and squash signals. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + DPRINTF(Rename, "Processing [tid:%i]\n", tid); + + status_change = checkSignalsAndUpdate(tid) || status_change; + + rename(status_change, tid); + } + + if (status_change) { + updateStatus(); + } + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); + } + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + // If we committed this cycle then doneSeqNum will be > 0 + if (fromCommit->commitInfo[tid].doneSeqNum != 0 && + !fromCommit->commitInfo[tid].squash && + renameStatus[tid] != Squashing) { + + removeFromHistory(fromCommit->commitInfo[tid].doneSeqNum, + tid); + } + } + + // @todo: make into updateProgress function + for (int tid=0; tid < numThreads; tid++) { + instsInProgress[tid] -= fromIEW->iewInfo[tid].dispatched; + + assert(instsInProgress[tid] >=0); + } + +} + +template<class Impl> +void +DefaultRename<Impl>::rename(bool &status_change, unsigned tid) +{ + // If status is Running or idle, + // call renameInsts() + // If status is Unblocking, + // buffer any instructions coming from decode + // continue trying to empty skid buffer + // check if stall conditions have passed + + if (renameStatus[tid] == Blocked) { + ++renameBlockCycles; + } else if (renameStatus[tid] == Squashing) { + ++renameSquashCycles; + } else if (renameStatus[tid] == SerializeStall) { + ++renameSerializeStallCycles; + } + + if (renameStatus[tid] == Running || + renameStatus[tid] == Idle) { + DPRINTF(Rename, "[tid:%u]: Not blocked, so attempting to run " + "stage.\n", tid); + + renameInsts(tid); + } else if (renameStatus[tid] == Unblocking) { + renameInsts(tid); + + if (validInsts()) { + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + } + + // If we switched over to blocking, then there's a potential for + // an overall status change. + status_change = unblock(tid) || status_change || blockThisCycle; + } +} + +template <class Impl> +void +DefaultRename<Impl>::renameInsts(unsigned tid) +{ + // Instructions can be either in the skid buffer or the queue of + // instructions coming from decode, depending on the status. + int insts_available = renameStatus[tid] == Unblocking ? + skidBuffer[tid].size() : insts[tid].size(); + + // Check the decode queue to see if instructions are available. + // If there are no available instructions to rename, then do nothing. + if (insts_available == 0) { + DPRINTF(Rename, "[tid:%u]: Nothing to do, breaking out early.\n", + tid); + // Should I change status to idle? + ++renameIdleCycles; return; + } else if (renameStatus[tid] == Unblocking) { + ++renameUnblockCycles; + } else if (renameStatus[tid] == Running) { + ++renameRunCycles; } -#endif // FULL_SYSTEM - // Go through the most recent instructions, undoing the mappings - // they did and freeing up the registers. - while ((*hb_it).instSeqNum > squashed_seq_num) - { - assert(hb_it != historyBuffer.end()); + DynInstPtr inst; - DPRINTF(Rename, "Rename: Removing history entry with sequence " - "number %i.\n", (*hb_it).instSeqNum); + // Will have to do a different calculation for the number of free + // entries. + int free_rob_entries = calcFreeROBEntries(tid); + int free_iq_entries = calcFreeIQEntries(tid); + int free_lsq_entries = calcFreeLSQEntries(tid); + int min_free_entries = free_rob_entries; - // If it's not simply a place holder, then add the registers. - if (!(*hb_it).placeHolder) { - // Tell the rename map to set the architected register to the - // previous physical register that it was renamed to. - renameMap->setEntry(hb_it->archReg, hb_it->prevPhysReg); + FullSource source = ROB; - // Put the renamed physical register back on the free list. - freeList->addReg(hb_it->newPhysReg); + if (free_iq_entries < min_free_entries) { + min_free_entries = free_iq_entries; + source = IQ; + } - ++renameValidUndoneMaps; + if (free_lsq_entries < min_free_entries) { + min_free_entries = free_lsq_entries; + source = LSQ; + } + + // Check if there's any space left. + if (min_free_entries <= 0) { + DPRINTF(Rename, "[tid:%u]: Blocking due to no free ROB/IQ/LSQ " + "entries.\n" + "ROB has %i free entries.\n" + "IQ has %i free entries.\n" + "LSQ has %i free entries.\n", + tid, + free_rob_entries, + free_iq_entries, + free_lsq_entries); + + blockThisCycle = true; + + block(tid); + + incrFullStat(source); + + return; + } else if (min_free_entries < insts_available) { + DPRINTF(Rename, "[tid:%u]: Will have to block this cycle." + "%i insts available, but only %i insts can be " + "renamed due to ROB/IQ/LSQ limits.\n", + tid, insts_available, min_free_entries); + + insts_available = min_free_entries; + + blockThisCycle = true; + + incrFullStat(source); + } + + InstQueue &insts_to_rename = renameStatus[tid] == Unblocking ? + skidBuffer[tid] : insts[tid]; + + DPRINTF(Rename, "[tid:%u]: %i available instructions to " + "send iew.\n", tid, insts_available); + + DPRINTF(Rename, "[tid:%u]: %i insts pipelining from Rename | %i insts " + "dispatched to IQ last cycle.\n", + tid, instsInProgress[tid], fromIEW->iewInfo[tid].dispatched); + + // Handle serializing the next instruction if necessary. + if (serializeOnNextInst[tid]) { + if (emptyROB[tid] && instsInProgress[tid] == 0) { + // ROB already empty; no need to serialize. + serializeOnNextInst[tid] = false; + } else if (!insts_to_rename.empty()) { + insts_to_rename.front()->setSerializeBefore(); } + } - historyBuffer.erase(hb_it++); + int renamed_insts = 0; - ++renameUndoneMaps; + while (insts_available > 0 && toIEWIndex < renameWidth) { + DPRINTF(Rename, "[tid:%u]: Sending instructions to IEW.\n", tid); + + assert(!insts_to_rename.empty()); + + inst = insts_to_rename.front(); + + insts_to_rename.pop_front(); + + if (renameStatus[tid] == Unblocking) { + DPRINTF(Rename,"[tid:%u]: Removing [sn:%lli] PC:%#x from rename " + "skidBuffer\n", + tid, inst->seqNum, inst->readPC()); + } + + if (inst->isSquashed()) { + DPRINTF(Rename, "[tid:%u]: instruction %i with PC %#x is " + "squashed, skipping.\n", + tid, inst->seqNum, inst->threadNumber,inst->readPC()); + + ++renameSquashedInsts; + + // Decrement how many instructions are available. + --insts_available; + + continue; + } + + DPRINTF(Rename, "[tid:%u]: Processing instruction [sn:%lli] with " + "PC %#x.\n", + tid, inst->seqNum, inst->readPC()); + + // Handle serializeAfter/serializeBefore instructions. + // serializeAfter marks the next instruction as serializeBefore. + // serializeBefore makes the instruction wait in rename until the ROB + // is empty. + + // In this model, IPR accesses are serialize before + // instructions, and store conditionals are serialize after + // instructions. This is mainly due to lack of support for + // out-of-order operations of either of those classes of + // instructions. + if ((inst->isIprAccess() || inst->isSerializeBefore()) && + !inst->isSerializeHandled()) { + DPRINTF(Rename, "Serialize before instruction encountered.\n"); + + if (!inst->isTempSerializeBefore()) { + renamedSerializing++; + inst->setSerializeHandled(); + } else { + renamedTempSerializing++; + } + + // Change status over to SerializeStall so that other stages know + // what this is blocked on. + renameStatus[tid] = SerializeStall; + + serializeInst[tid] = inst; + + blockThisCycle = true; + + break; + } else if ((inst->isStoreConditional() || inst->isSerializeAfter()) && + !inst->isSerializeHandled()) { + DPRINTF(Rename, "Serialize after instruction encountered.\n"); + + renamedSerializing++; + + inst->setSerializeHandled(); + + serializeAfter(insts_to_rename, tid); + } + + // Check here to make sure there are enough destination registers + // to rename to. Otherwise block. + if (renameMap[tid]->numFreeEntries() < inst->numDestRegs()) { + DPRINTF(Rename, "Blocking due to lack of free " + "physical registers to rename to.\n"); + blockThisCycle = true; + + ++renameFullRegistersEvents; + + break; + } + + renameSrcRegs(inst, inst->threadNumber); + + renameDestRegs(inst, inst->threadNumber); + + ++renamed_insts; + + // Put instruction in rename queue. + toIEW->insts[toIEWIndex] = inst; + ++(toIEW->size); + + // Increment which instruction we're on. + ++toIEWIndex; + + // Decrement how many instructions are available. + --insts_available; + } + + instsInProgress[tid] += renamed_insts; + renameRenamedInsts += renamed_insts; + + // If we wrote to the time buffer, record this. + if (toIEWIndex) { + wroteToTimeBuffer = true; + } + + // Check if there's any instructions left that haven't yet been renamed. + // If so then block. + if (insts_available) { + blockThisCycle = true; + } + + if (blockThisCycle) { + block(tid); + toDecode->renameUnblock[tid] = false; } } +template<class Impl> +void +DefaultRename<Impl>::skidInsert(unsigned tid) +{ + DynInstPtr inst = NULL; + + while (!insts[tid].empty()) { + inst = insts[tid].front(); + + insts[tid].pop_front(); + + assert(tid == inst->threadNumber); + + DPRINTF(Rename, "[tid:%u]: Inserting [sn:%lli] PC:%#x into Rename " + "skidBuffer\n", tid, inst->seqNum, inst->readPC()); + + ++renameSkidInsts; + + skidBuffer[tid].push_back(inst); + } + + if (skidBuffer[tid].size() > skidBufferMax) + panic("Skidbuffer Exceeded Max Size"); +} + template <class Impl> void -SimpleRename<Impl>::squash() +DefaultRename<Impl>::sortInsts() { - DPRINTF(Rename, "Rename: Squashing instructions.\n"); - // Set the status to Squashing. - _status = Squashing; + int insts_from_decode = fromDecode->size; +#ifdef DEBUG + for (int i=0; i < numThreads; i++) + assert(insts[i].empty()); +#endif + for (int i = 0; i < insts_from_decode; ++i) { + DynInstPtr inst = fromDecode->insts[i]; + insts[inst->threadNumber].push_back(inst); + } +} - numInst = 0; +template<class Impl> +bool +DefaultRename<Impl>::skidsEmpty() +{ + list<unsigned>::iterator threads = (*activeThreads).begin(); - // Clear the skid buffer in case it has any data in it. - while (!skidBuffer.empty()) - { - skidBuffer.pop(); + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; } - doSquash(); + return true; } template<class Impl> void -SimpleRename<Impl>::removeFromHistory(InstSeqNum inst_seq_num) +DefaultRename<Impl>::updateStatus() { - DPRINTF(Rename, "Rename: Removing a committed instruction from the " - "history buffer, until sequence number %lli.\n", inst_seq_num); - typename list<RenameHistory>::iterator hb_it = historyBuffer.end(); + bool any_unblocking = false; - --hb_it; + list<unsigned>::iterator threads = (*activeThreads).begin(); - if (hb_it->instSeqNum > inst_seq_num) { - DPRINTF(Rename, "Rename: Old sequence number encountered. Ensure " - "that a syscall happened recently.\n"); - return; + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (renameStatus[tid] == Unblocking) { + any_unblocking = true; + break; + } } - while ((*hb_it).instSeqNum != inst_seq_num) - { - // Make sure we haven't gone off the end of the list. - assert(hb_it != historyBuffer.end()); - - // In theory instructions at the end of the history buffer - // should be older than the instruction being removed, which - // means they will have a lower sequence number. Also the - // instruction being removed from the history really should - // be the last instruction in the list, as it is the instruction - // that was just committed that is being removed. - assert(hb_it->instSeqNum < inst_seq_num); - DPRINTF(Rename, "Rename: Freeing up older rename of reg %i, sequence" - " number %i.\n", - (*hb_it).prevPhysReg, (*hb_it).instSeqNum); - - if (!(*hb_it).placeHolder) { - freeList->addReg((*hb_it).prevPhysReg); - ++renameCommittedMaps; + // Rename will have activity if it's unblocking. + if (any_unblocking) { + if (_status == Inactive) { + _status = Active; + + DPRINTF(Activity, "Activating stage.\n"); + + cpu->activateStage(O3CPU::RenameIdx); } + } else { + // If it's not unblocking, then rename will not have any internal + // activity. Switch it to inactive. + if (_status == Active) { + _status = Inactive; + DPRINTF(Activity, "Deactivating stage.\n"); - historyBuffer.erase(hb_it--); + cpu->deactivateStage(O3CPU::RenameIdx); + } } +} - // Finally free up the previous register of the finished instruction - // itself. - if (!(*hb_it).placeHolder) { - freeList->addReg(hb_it->prevPhysReg); - ++renameCommittedMaps; +template <class Impl> +bool +DefaultRename<Impl>::block(unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Blocking.\n", tid); + + // Add the current inputs onto the skid buffer, so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + + // Only signal backwards to block if the previous stages do not think + // rename is already blocked. + if (renameStatus[tid] != Blocked) { + if (renameStatus[tid] != Unblocking) { + toDecode->renameBlock[tid] = true; + toDecode->renameUnblock[tid] = false; + wroteToTimeBuffer = true; + } + + // Rename can not go from SerializeStall to Blocked, otherwise + // it would not know to complete the serialize stall. + if (renameStatus[tid] != SerializeStall) { + // Set status to Blocked. + renameStatus[tid] = Blocked; + return true; + } + } + + return false; +} + +template <class Impl> +bool +DefaultRename<Impl>::unblock(unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Trying to unblock.\n", tid); + + // Rename is done unblocking if the skid buffer is empty. + if (skidBuffer[tid].empty() && renameStatus[tid] != SerializeStall) { + + DPRINTF(Rename, "[tid:%u]: Done unblocking.\n", tid); + + toDecode->renameUnblock[tid] = true; + wroteToTimeBuffer = true; + + renameStatus[tid] = Running; + return true; } - historyBuffer.erase(hb_it); + return false; +} + +template <class Impl> +void +DefaultRename<Impl>::doSquash(unsigned tid) +{ + typename list<RenameHistory>::iterator hb_it = historyBuffer[tid].begin(); + + InstSeqNum squashed_seq_num = fromCommit->commitInfo[tid].doneSeqNum; + + // After a syscall squashes everything, the history buffer may be empty + // but the ROB may still be squashing instructions. + if (historyBuffer[tid].empty()) { + return; + } + + // Go through the most recent instructions, undoing the mappings + // they did and freeing up the registers. + while (!historyBuffer[tid].empty() && + (*hb_it).instSeqNum > squashed_seq_num) { + assert(hb_it != historyBuffer[tid].end()); + + DPRINTF(Rename, "[tid:%u]: Removing history entry with sequence " + "number %i.\n", tid, (*hb_it).instSeqNum); + + // Tell the rename map to set the architected register to the + // previous physical register that it was renamed to. + renameMap[tid]->setEntry(hb_it->archReg, hb_it->prevPhysReg); + + // Put the renamed physical register back on the free list. + freeList->addReg(hb_it->newPhysReg); + + historyBuffer[tid].erase(hb_it++); + + ++renameUndoneMaps; + } +} + +template<class Impl> +void +DefaultRename<Impl>::removeFromHistory(InstSeqNum inst_seq_num, unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Removing a committed instruction from the " + "history buffer %u (size=%i), until [sn:%lli].\n", + tid, tid, historyBuffer[tid].size(), inst_seq_num); + + typename list<RenameHistory>::iterator hb_it = historyBuffer[tid].end(); + + --hb_it; + + if (historyBuffer[tid].empty()) { + DPRINTF(Rename, "[tid:%u]: History buffer is empty.\n", tid); + return; + } else if (hb_it->instSeqNum > inst_seq_num) { + DPRINTF(Rename, "[tid:%u]: Old sequence number encountered. Ensure " + "that a syscall happened recently.\n", tid); + return; + } + + // Commit all the renames up until (and including) the committed sequence + // number. Some or even all of the committed instructions may not have + // rename histories if they did not have destination registers that were + // renamed. + while (!historyBuffer[tid].empty() && + hb_it != historyBuffer[tid].end() && + (*hb_it).instSeqNum <= inst_seq_num) { + + DPRINTF(Rename, "[tid:%u]: Freeing up older rename of reg %i, " + "[sn:%lli].\n", + tid, (*hb_it).prevPhysReg, (*hb_it).instSeqNum); + + freeList->addReg((*hb_it).prevPhysReg); + ++renameCommittedMaps; + + historyBuffer[tid].erase(hb_it--); + } } template <class Impl> inline void -SimpleRename<Impl>::renameSrcRegs(DynInstPtr &inst) +DefaultRename<Impl>::renameSrcRegs(DynInstPtr &inst,unsigned tid) { + assert(renameMap[tid] != 0); + unsigned num_src_regs = inst->numSrcRegs(); // Get the architectual register numbers from the source and // destination operands, and redirect them to the right register. // Will need to mark dependencies though. - for (int src_idx = 0; src_idx < num_src_regs; src_idx++) - { + for (int src_idx = 0; src_idx < num_src_regs; src_idx++) { RegIndex src_reg = inst->srcRegIdx(src_idx); // Look up the source registers to get the phys. register they've // been renamed to, and set the sources to those registers. - PhysRegIndex renamed_reg = renameMap->lookup(src_reg); + PhysRegIndex renamed_reg = renameMap[tid]->lookup(src_reg); - DPRINTF(Rename, "Rename: Looking up arch reg %i, got " - "physical reg %i.\n", (int)src_reg, (int)renamed_reg); + DPRINTF(Rename, "[tid:%u]: Looking up arch reg %i, got " + "physical reg %i.\n", tid, (int)src_reg, + (int)renamed_reg); inst->renameSrcReg(src_idx, renamed_reg); - // Either incorporate it into the info passed back, - // or make another function call to see if that register is - // ready or not. - if (renameMap->isReady(renamed_reg)) { - DPRINTF(Rename, "Rename: Register is ready.\n"); + // See if the register is ready or not. + if (scoreboard->getReg(renamed_reg) == true) { + DPRINTF(Rename, "[tid:%u]: Register is ready.\n", tid); inst->markSrcRegReady(src_idx); } @@ -376,379 +934,341 @@ SimpleRename<Impl>::renameSrcRegs(DynInstPtr &inst) template <class Impl> inline void -SimpleRename<Impl>::renameDestRegs(DynInstPtr &inst) +DefaultRename<Impl>::renameDestRegs(DynInstPtr &inst,unsigned tid) { - typename SimpleRenameMap::RenameInfo rename_result; + typename RenameMap::RenameInfo rename_result; unsigned num_dest_regs = inst->numDestRegs(); - // If it's an instruction with no destination registers, then put - // a placeholder within the history buffer. It might be better - // to not put it in the history buffer at all (other than branches, - // which always need at least a place holder), and differentiate - // between instructions with and without destination registers - // when getting from commit the instructions that committed. - if (num_dest_regs == 0) { - RenameHistory hb_entry(inst->seqNum); + // Rename the destination registers. + for (int dest_idx = 0; dest_idx < num_dest_regs; dest_idx++) { + RegIndex dest_reg = inst->destRegIdx(dest_idx); - historyBuffer.push_front(hb_entry); + // Get the physical register that the destination will be + // renamed to. + rename_result = renameMap[tid]->rename(dest_reg); - DPRINTF(Rename, "Rename: Adding placeholder instruction to " - "history buffer, sequence number %lli.\n", - inst->seqNum); + //Mark Scoreboard entry as not ready + scoreboard->unsetReg(rename_result.first); - ++renameHBPlaceHolders; - } else { + DPRINTF(Rename, "[tid:%u]: Renaming arch reg %i to physical " + "reg %i.\n", tid, (int)dest_reg, + (int)rename_result.first); - // Rename the destination registers. - for (int dest_idx = 0; dest_idx < num_dest_regs; dest_idx++) - { - RegIndex dest_reg = inst->destRegIdx(dest_idx); + // Record the rename information so that a history can be kept. + RenameHistory hb_entry(inst->seqNum, dest_reg, + rename_result.first, + rename_result.second); - // Get the physical register that the destination will be - // renamed to. - rename_result = renameMap->rename(dest_reg); + historyBuffer[tid].push_front(hb_entry); - DPRINTF(Rename, "Rename: Renaming arch reg %i to physical " - "reg %i.\n", (int)dest_reg, - (int)rename_result.first); + DPRINTF(Rename, "[tid:%u]: Adding instruction to history buffer, " + "[sn:%lli].\n",tid, + (*historyBuffer[tid].begin()).instSeqNum); - // Record the rename information so that a history can be kept. - RenameHistory hb_entry(inst->seqNum, dest_reg, - rename_result.first, - rename_result.second); + // Tell the instruction to rename the appropriate destination + // register (dest_idx) to the new physical register + // (rename_result.first), and record the previous physical + // register that the same logical register was renamed to + // (rename_result.second). + inst->renameDestReg(dest_idx, + rename_result.first, + rename_result.second); - historyBuffer.push_front(hb_entry); + ++renameRenamedOperands; + } +} - DPRINTF(Rename, "Rename: Adding instruction to history buffer, " - "sequence number %lli.\n", - (*historyBuffer.begin()).instSeqNum); +template <class Impl> +inline int +DefaultRename<Impl>::calcFreeROBEntries(unsigned tid) +{ + int num_free = freeEntries[tid].robEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatched); - // Tell the instruction to rename the appropriate destination - // register (dest_idx) to the new physical register - // (rename_result.first), and record the previous physical - // register that the same logical register was renamed to - // (rename_result.second). - inst->renameDestReg(dest_idx, - rename_result.first, - rename_result.second); + //DPRINTF(Rename,"[tid:%i]: %i rob free\n",tid,num_free); - ++renameRenamedOperands; - } - } + return num_free; } template <class Impl> inline int -SimpleRename<Impl>::calcFreeROBEntries() +DefaultRename<Impl>::calcFreeIQEntries(unsigned tid) { - return fromCommit->commitInfo.freeROBEntries - - renameWidth * iewToRenameDelay; + int num_free = freeEntries[tid].iqEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatched); + + //DPRINTF(Rename,"[tid:%i]: %i iq free\n",tid,num_free); + + return num_free; } template <class Impl> inline int -SimpleRename<Impl>::calcFreeIQEntries() +DefaultRename<Impl>::calcFreeLSQEntries(unsigned tid) { - return fromIEW->iewInfo.freeIQEntries - renameWidth * iewToRenameDelay; -} + int num_free = freeEntries[tid].lsqEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatchedToLSQ); -template<class Impl> -void -SimpleRename<Impl>::tick() -{ - // Rename will need to try to rename as many instructions as it - // has bandwidth, unless it is blocked. - - // Check if _status is BarrierStall. If so, then check if the number - // of free ROB entries is equal to the number of total ROB entries. - // Once equal then wake this stage up. Set status to unblocking maybe. - - if (_status != Blocked && _status != Squashing) { - DPRINTF(Rename, "Rename: Status is not blocked, will attempt to " - "run stage.\n"); - // Make sure that the skid buffer has something in it if the - // status is unblocking. - assert(_status == Unblocking ? !skidBuffer.empty() : 1); - - rename(); - - // If the status was unblocking, then instructions from the skid - // buffer were used. Remove those instructions and handle - // the rest of unblocking. - if (_status == Unblocking) { - ++renameUnblockCycles; - - if (fromDecode->size > 0) { - // Add the current inputs onto the skid buffer, so they can be - // reprocessed when this stage unblocks. - skidBuffer.push(*fromDecode); - } + //DPRINTF(Rename,"[tid:%i]: %i lsq free\n",tid,num_free); - unblock(); - } - } else if (_status == Blocked) { - ++renameBlockCycles; + return num_free; +} - // If stage is blocked and still receiving valid instructions, - // make sure to store them in the skid buffer. - if (fromDecode->size > 0) { +template <class Impl> +unsigned +DefaultRename<Impl>::validInsts() +{ + unsigned inst_count = 0; - block(); + for (int i=0; i<fromDecode->size; i++) { + if (!fromDecode->insts[i]->isSquashed()) + inst_count++; + } - // Continue to tell previous stage to stall. - toDecode->renameInfo.stall = true; - } + return inst_count; +} - if (!fromIEW->iewInfo.stall && - !fromCommit->commitInfo.stall && - calcFreeROBEntries() > 0 && - calcFreeIQEntries() > 0 && - renameMap->numFreeEntries() > 0) { - - // Need to be sure to check all blocking conditions above. - // If they have cleared, then start unblocking. - DPRINTF(Rename, "Rename: Stall signals cleared, going to " - "unblock.\n"); - _status = Unblocking; - - // Continue to tell previous stage to block until this stage - // is done unblocking. - toDecode->renameInfo.stall = true; - } else { - // Otherwise no conditions have changed. Tell previous - // stage to continue blocking. - toDecode->renameInfo.stall = true; - } +template <class Impl> +void +DefaultRename<Impl>::readStallSignals(unsigned tid) +{ + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - squash(); - return; - } - } else if (_status == Squashing) { - ++renameSquashCycles; + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + stalls[tid].iew = false; + } - if (fromCommit->commitInfo.squash) { - squash(); - } else if (!fromCommit->commitInfo.squash && - !fromCommit->commitInfo.robSquashing) { + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } - DPRINTF(Rename, "Rename: Done squashing, going to running.\n"); - _status = Running; - rename(); - } else { - doSquash(); - } + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; } +} - // Ugly code, revamp all of the tick() functions eventually. - if (fromCommit->commitInfo.doneSeqNum != 0 && _status != Squashing) { -#if !FULL_SYSTEM - if (!fromCommit->commitInfo.squash) { - removeFromHistory(fromCommit->commitInfo.doneSeqNum); - } -#else - removeFromHistory(fromCommit->commitInfo.doneSeqNum); -#endif +template <class Impl> +bool +DefaultRename<Impl>::checkStall(unsigned tid) +{ + bool ret_val = false; + + if (stalls[tid].iew) { + DPRINTF(Rename,"[tid:%i]: Stall from IEW stage detected.\n", tid); + ret_val = true; + } else if (stalls[tid].commit) { + DPRINTF(Rename,"[tid:%i]: Stall from Commit stage detected.\n", tid); + ret_val = true; + } else if (calcFreeROBEntries(tid) <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: ROB has 0 free entries.\n", tid); + ret_val = true; + } else if (calcFreeIQEntries(tid) <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: IQ has 0 free entries.\n", tid); + ret_val = true; + } else if (calcFreeLSQEntries(tid) <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: LSQ has 0 free entries.\n", tid); + ret_val = true; + } else if (renameMap[tid]->numFreeEntries() <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: RenameMap has 0 free entries.\n", tid); + ret_val = true; + } else if (renameStatus[tid] == SerializeStall && + (!emptyROB[tid] || instsInProgress[tid])) { + DPRINTF(Rename,"[tid:%i]: Stall: Serialize stall and ROB is not " + "empty.\n", + tid); + ret_val = true; } + return ret_val; } -template<class Impl> +template <class Impl> void -SimpleRename<Impl>::rename() -{ - // Check if any of the stages ahead of rename are telling rename - // to squash. The squash() function will also take care of fixing up - // the rename map and the free list. - if (fromCommit->commitInfo.squash || - fromCommit->commitInfo.robSquashing) { - DPRINTF(Rename, "Rename: Receiving signal from Commit to squash.\n"); - squash(); - return; +DefaultRename<Impl>::readFreeEntries(unsigned tid) +{ + bool updated = false; + if (fromIEW->iewInfo[tid].usedIQ) { + freeEntries[tid].iqEntries = + fromIEW->iewInfo[tid].freeIQEntries; + updated = true; } - // Check if time buffer is telling this stage to stall. - if (fromIEW->iewInfo.stall || - fromCommit->commitInfo.stall) { - DPRINTF(Rename, "Rename: Receiving signal from IEW/Commit to " - "stall.\n"); - block(); - return; + if (fromIEW->iewInfo[tid].usedLSQ) { + freeEntries[tid].lsqEntries = + fromIEW->iewInfo[tid].freeLSQEntries; + updated = true; } - // Check if the current status is squashing. If so, set its status - // to running and resume execution the next cycle. - if (_status == Squashing) { - DPRINTF(Rename, "Rename: Done squashing.\n"); - _status = Running; - return; + if (fromCommit->commitInfo[tid].usedROB) { + freeEntries[tid].robEntries = + fromCommit->commitInfo[tid].freeROBEntries; + emptyROB[tid] = fromCommit->commitInfo[tid].emptyROB; + updated = true; } - // Check the decode queue to see if instructions are available. - // If there are no available instructions to rename, then do nothing. - // Or, if the stage is currently unblocking, then go ahead and run it. - if (fromDecode->size == 0 && _status != Unblocking) { - DPRINTF(Rename, "Rename: Nothing to do, breaking out early.\n"); - // Should I change status to idle? - return; - } - - //////////////////////////////////// - // Actual rename part. - //////////////////////////////////// - - DynInstPtr inst; + DPRINTF(Rename, "[tid:%i]: Free IQ: %i, Free ROB: %i, Free LSQ: %i\n", + tid, + freeEntries[tid].iqEntries, + freeEntries[tid].robEntries, + freeEntries[tid].lsqEntries); - // If we're unblocking, then we may be in the middle of an instruction - // group. Subtract off numInst to get the proper number of instructions - // left. - int insts_available = _status == Unblocking ? - skidBuffer.front().size - numInst : - fromDecode->size; + DPRINTF(Rename, "[tid:%i]: %i instructions not yet in ROB\n", + tid, instsInProgress[tid]); +} - bool block_this_cycle = false; +template <class Impl> +bool +DefaultRename<Impl>::checkSignalsAndUpdate(unsigned tid) +{ + // Check if there's a squash signal, squash if there is + // Check stall signals, block if necessary. + // If status was blocked + // check if stall conditions have passed + // if so then go to unblocking + // If status was Squashing + // check if squashing is not high. Switch to running this cycle. + // If status was serialize stall + // check if ROB is empty and no insts are in flight to the ROB + + readFreeEntries(tid); + readStallSignals(tid); + + if (fromCommit->commitInfo[tid].squash) { + DPRINTF(Rename, "[tid:%u]: Squashing instructions due to squash from " + "commit.\n", tid); + + squash(tid); + + return true; + } - // Will have to do a different calculation for the number of free - // entries. Number of free entries recorded on this cycle - - // renameWidth * renameToDecodeDelay - int free_rob_entries = calcFreeROBEntries(); - int free_iq_entries = calcFreeIQEntries(); - int min_iq_rob = min(free_rob_entries, free_iq_entries); + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Rename, "[tid:%u]: ROB is still squashing.\n", tid); - unsigned to_iew_index = 0; + renameStatus[tid] = Squashing; - // Check if there's any space left. - if (min_iq_rob <= 0) { - DPRINTF(Rename, "Rename: Blocking due to no free ROB or IQ " - "entries.\n" - "Rename: ROB has %d free entries.\n" - "Rename: IQ has %d free entries.\n", - free_rob_entries, - free_iq_entries); - block(); - // Tell previous stage to stall. - toDecode->renameInfo.stall = true; + return true; + } - if (free_rob_entries <= 0) { - ++renameROBFullEvents; - } else { - ++renameIQFullEvents; - } + if (checkStall(tid)) { + return block(tid); + } - return; - } else if (min_iq_rob < insts_available) { - DPRINTF(Rename, "Rename: Will have to block this cycle. Only " - "%i insts can be renamed due to IQ/ROB limits.\n", - min_iq_rob); + if (renameStatus[tid] == Blocked) { + DPRINTF(Rename, "[tid:%u]: Done blocking, switching to unblocking.\n", + tid); - insts_available = min_iq_rob; + renameStatus[tid] = Unblocking; - block_this_cycle = true; + unblock(tid); - if (free_rob_entries < free_iq_entries) { - ++renameROBFullEvents; - } else { - ++renameIQFullEvents; - } + return true; } - while (insts_available > 0) { - DPRINTF(Rename, "Rename: Sending instructions to iew.\n"); + if (renameStatus[tid] == Squashing) { + // Switch status to running if rename isn't being told to block or + // squash this cycle. + DPRINTF(Rename, "[tid:%u]: Done squashing, switching to running.\n", + tid); - // Get the next instruction either from the skid buffer or the - // decode queue. - inst = _status == Unblocking ? skidBuffer.front().insts[numInst] : - fromDecode->insts[numInst]; + renameStatus[tid] = Running; - if (inst->isSquashed()) { - DPRINTF(Rename, "Rename: instruction %i with PC %#x is " - "squashed, skipping.\n", - inst->seqNum, inst->readPC()); + return false; + } - // Go to the next instruction. - ++numInst; + if (renameStatus[tid] == SerializeStall) { + // Stall ends once the ROB is free. + DPRINTF(Rename, "[tid:%u]: Done with serialize stall, switching to " + "unblocking.\n", tid); - ++renameSquashedInsts; + DynInstPtr serial_inst = serializeInst[tid]; - // Decrement how many instructions are available. - --insts_available; + renameStatus[tid] = Unblocking; - continue; - } + unblock(tid); - DPRINTF(Rename, "Rename: Processing instruction %i with PC %#x.\n", - inst->seqNum, inst->readPC()); - - // If it's a trap instruction, then it needs to wait here within - // rename until the ROB is empty. Needs a way to detect that the - // ROB is empty. Maybe an event? - // Would be nice if it could be avoided putting this into a - // specific stage and instead just put it into the AlphaFullCPU. - // Might not really be feasible though... - // (EXCB, TRAPB) - if (inst->isSerializing()) { - panic("Rename: Serializing instruction encountered.\n"); - DPRINTF(Rename, "Rename: Serializing instruction " - "encountered.\n"); - - // Change status over to BarrierStall so that other stages know - // what this is blocked on. - _status = BarrierStall; + DPRINTF(Rename, "[tid:%u]: Processing instruction [%lli] with " + "PC %#x.\n", + tid, serial_inst->seqNum, serial_inst->readPC()); - block_this_cycle = true; + // Put instruction into queue here. + serial_inst->clearSerializeBefore(); - break; + if (!skidBuffer[tid].empty()) { + skidBuffer[tid].push_front(serial_inst); + } else { + insts[tid].push_front(serial_inst); } - // Check here to make sure there are enough destination registers - // to rename to. Otherwise block. - if (renameMap->numFreeEntries() < inst->numDestRegs()) - { - DPRINTF(Rename, "Rename: Blocking due to lack of free " - "physical registers to rename to.\n"); - // Need some sort of event based on a register being freed. - - block_this_cycle = true; + DPRINTF(Rename, "[tid:%u]: Instruction must be processed by rename." + " Adding to front of list.\n", tid); - ++renameFullRegistersEvents; + serializeInst[tid] = NULL; - break; - } + return true; + } - renameSrcRegs(inst); + // If we've reached this point, we have not gotten any signals that + // cause rename to change its status. Rename remains the same as before. + return false; +} - renameDestRegs(inst); +template<class Impl> +void +DefaultRename<Impl>::serializeAfter(InstQueue &inst_list, + unsigned tid) +{ + if (inst_list.empty()) { + // Mark a bit to say that I must serialize on the next instruction. + serializeOnNextInst[tid] = true; + return; + } - // Put instruction in rename queue. - toIEW->insts[to_iew_index] = inst; - ++(toIEW->size); + // Set the next instruction as serializing. + inst_list.front()->setSerializeBefore(); +} - // Decrease the number of free ROB and IQ entries. - --free_rob_entries; - --free_iq_entries; +template <class Impl> +inline void +DefaultRename<Impl>::incrFullStat(const FullSource &source) +{ + switch (source) { + case ROB: + ++renameROBFullEvents; + break; + case IQ: + ++renameIQFullEvents; + break; + case LSQ: + ++renameLSQFullEvents; + break; + default: + panic("Rename full stall stat should be incremented for a reason!"); + break; + } +} - // Increment which instruction we're on. - ++to_iew_index; - ++numInst; +template <class Impl> +void +DefaultRename<Impl>::dumpHistory() +{ + typename list<RenameHistory>::iterator buf_it; - ++renameRenamedInsts; + for (int i = 0; i < numThreads; i++) { - // Decrement how many instructions are available. - --insts_available; - } + buf_it = historyBuffer[i].begin(); - // Check if there's any instructions left that haven't yet been renamed. - // If so then block. - if (block_this_cycle) { - block(); + while (buf_it != historyBuffer[i].end()) { + cprintf("Seq num: %i\nArch reg: %i New phys reg: %i Old phys " + "reg: %i\n", (*buf_it).instSeqNum, (int)(*buf_it).archReg, + (int)(*buf_it).newPhysReg, (int)(*buf_it).prevPhysReg); - toDecode->renameInfo.stall = true; - } else { - // If we had a successful rename and didn't have to exit early, then - // reset numInst so it will refer to the correct instruction on next - // run. - numInst = 0; + buf_it++; + } } } diff --git a/src/cpu/o3/rename_map.cc b/src/cpu/o3/rename_map.cc index 10963f7de..befbc3e8a 100644 --- a/src/cpu/o3/rename_map.cc +++ b/src/cpu/o3/rename_map.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Kevin Lim */ #include <vector> @@ -32,118 +34,105 @@ using namespace std; -// Todo: Consider making functions inline. Avoid having things that are -// using the zero register or misc registers from adding on the registers -// to the free list. Possibly remove the direct communication between -// this and the freelist. Considering making inline bool functions that -// determine if the register is a logical int, logical fp, physical int, -// physical fp, etc. - -SimpleRenameMap::SimpleRenameMap(unsigned _numLogicalIntRegs, - unsigned _numPhysicalIntRegs, - unsigned _numLogicalFloatRegs, - unsigned _numPhysicalFloatRegs, - unsigned _numMiscRegs, - RegIndex _intZeroReg, - RegIndex _floatZeroReg) - : numLogicalIntRegs(_numLogicalIntRegs), - numPhysicalIntRegs(_numPhysicalIntRegs), - numLogicalFloatRegs(_numLogicalFloatRegs), - numPhysicalFloatRegs(_numPhysicalFloatRegs), - numMiscRegs(_numMiscRegs), - intZeroReg(_intZeroReg), - floatZeroReg(_floatZeroReg) +// @todo: Consider making inline bool functions that determine if the +// register is a logical int, logical fp, physical int, physical fp, +// etc. + +SimpleRenameMap::~SimpleRenameMap() +{ +} + +void +SimpleRenameMap::init(unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + PhysRegIndex &ireg_idx, + + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + PhysRegIndex &freg_idx, + + unsigned _numMiscRegs, + + RegIndex _intZeroReg, + RegIndex _floatZeroReg, + + int map_id, + bool bindRegs) { - DPRINTF(Rename, "Rename: Creating rename map. Phys: %i / %i, Float: " - "%i / %i.\n", numLogicalIntRegs, numPhysicalIntRegs, + id = map_id; + + numLogicalIntRegs = _numLogicalIntRegs; + + numLogicalFloatRegs = _numLogicalFloatRegs; + + numPhysicalIntRegs = _numPhysicalIntRegs; + + numPhysicalFloatRegs = _numPhysicalFloatRegs; + + numMiscRegs = _numMiscRegs; + + intZeroReg = _intZeroReg; + floatZeroReg = _floatZeroReg; + + DPRINTF(Rename, "Creating rename map %i. Phys: %i / %i, Float: " + "%i / %i.\n", id, numLogicalIntRegs, numPhysicalIntRegs, numLogicalFloatRegs, numPhysicalFloatRegs); numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs; numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs; - //Create the rename maps, and their scoreboards. - intRenameMap = new RenameEntry[numLogicalIntRegs]; - floatRenameMap = new RenameEntry[numLogicalRegs]; - - // Should combine this into one scoreboard. - intScoreboard.resize(numPhysicalIntRegs); - floatScoreboard.resize(numPhysicalRegs); - miscScoreboard.resize(numPhysicalRegs + numMiscRegs); - - // Initialize the entries in the integer rename map to point to the - // physical registers of the same index, and consider each register - // ready until the first rename occurs. - for (RegIndex index = 0; index < numLogicalIntRegs; ++index) - { - intRenameMap[index].physical_reg = index; - intScoreboard[index] = 1; - } + //Create the rename maps + intRenameMap.resize(numLogicalIntRegs); + floatRenameMap.resize(numLogicalRegs); - // Initialize the rest of the physical registers (the ones that don't - // directly map to a logical register) as unready. - for (PhysRegIndex index = numLogicalIntRegs; - index < numPhysicalIntRegs; - ++index) - { - intScoreboard[index] = 0; - } + if (bindRegs) { + DPRINTF(Rename, "Binding registers into rename map %i",id); - int float_reg_idx = numPhysicalIntRegs; - - // Initialize the entries in the floating point rename map to point to - // the physical registers of the same index, and consider each register - // ready until the first rename occurs. - // Although the index refers purely to architected registers, because - // the floating reg indices come after the integer reg indices, they - // may exceed the size of a normal RegIndex (short). - for (PhysRegIndex index = numLogicalIntRegs; - index < numLogicalRegs; ++index) - { - floatRenameMap[index].physical_reg = float_reg_idx++; - } + // Initialize the entries in the integer rename map to point to the + // physical registers of the same index + for (RegIndex index = 0; index < numLogicalIntRegs; ++index) + { + intRenameMap[index].physical_reg = ireg_idx++; + } - for (PhysRegIndex index = numPhysicalIntRegs; - index < numPhysicalIntRegs + numLogicalFloatRegs; ++index) - { - floatScoreboard[index] = 1; - } + // Initialize the entries in the floating point rename map to point to + // the physical registers of the same index + // Although the index refers purely to architected registers, because + // the floating reg indices come after the integer reg indices, they + // may exceed the size of a normal RegIndex (short). + for (PhysRegIndex index = numLogicalIntRegs; + index < numLogicalRegs; ++index) + { + floatRenameMap[index].physical_reg = freg_idx++; + } + } else { + DPRINTF(Rename, "Binding registers into rename map %i",id); - // Initialize the rest of the physical registers (the ones that don't - // directly map to a logical register) as unready. - for (PhysRegIndex index = numPhysicalIntRegs + numLogicalFloatRegs; - index < numPhysicalRegs; - ++index) - { - floatScoreboard[index] = 0; - } + PhysRegIndex temp_ireg = ireg_idx; - // Initialize the entries in the misc register scoreboard to be ready. - for (PhysRegIndex index = numPhysicalRegs; - index < numPhysicalRegs + numMiscRegs; ++index) - { - miscScoreboard[index] = 1; - } -} + for (RegIndex index = 0; index < numLogicalIntRegs; ++index) + { + intRenameMap[index].physical_reg = temp_ireg++; + } -SimpleRenameMap::~SimpleRenameMap() -{ - // Delete the rename maps as they were allocated with new. - delete [] intRenameMap; - delete [] floatRenameMap; + PhysRegIndex temp_freg = freg_idx; + + for (PhysRegIndex index = numLogicalIntRegs; + index < numLogicalRegs; ++index) + { + floatRenameMap[index].physical_reg = temp_freg++; + } + } } void SimpleRenameMap::setFreeList(SimpleFreeList *fl_ptr) { - //Setup the interface to the freelist. freeList = fl_ptr; } -// Don't allow this stage to fault; force that check to the rename stage. -// Simply ask to rename a logical register and get back a new physical -// register index. SimpleRenameMap::RenameInfo SimpleRenameMap::rename(RegIndex arch_reg) { @@ -156,45 +145,33 @@ SimpleRenameMap::rename(RegIndex arch_reg) // requested architected register. prev_reg = intRenameMap[arch_reg].physical_reg; - // If it's not referencing the zero register, then mark the register - // as not ready. + // If it's not referencing the zero register, then rename the + // register. if (arch_reg != intZeroReg) { - // Get a free physical register to rename to. renamed_reg = freeList->getIntReg(); - // Update the integer rename map. intRenameMap[arch_reg].physical_reg = renamed_reg; assert(renamed_reg >= 0 && renamed_reg < numPhysicalIntRegs); - // Mark register as not ready. - intScoreboard[renamed_reg] = false; } else { // Otherwise return the zero register so nothing bad happens. renamed_reg = intZeroReg; } } else if (arch_reg < numLogicalRegs) { - // Subtract off the base offset for floating point registers. -// arch_reg = arch_reg - numLogicalIntRegs; - // Record the current physical register that is renamed to the // requested architected register. prev_reg = floatRenameMap[arch_reg].physical_reg; - // If it's not referencing the zero register, then mark the register - // as not ready. + // If it's not referencing the zero register, then rename the + // register. if (arch_reg != floatZeroReg) { - // Get a free floating point register to rename to. renamed_reg = freeList->getFloatReg(); - // Update the floating point rename map. floatRenameMap[arch_reg].physical_reg = renamed_reg; assert(renamed_reg < numPhysicalRegs && renamed_reg >= numPhysicalIntRegs); - - // Mark register as not ready. - floatScoreboard[renamed_reg] = false; } else { // Otherwise return the zero register so nothing bad happens. renamed_reg = floatZeroReg; @@ -203,10 +180,10 @@ SimpleRenameMap::rename(RegIndex arch_reg) // Subtract off the base offset for miscellaneous registers. arch_reg = arch_reg - numLogicalRegs; - // No renaming happens to the misc. registers. They are simply the - // registers that come after all the physical registers; thus - // take the base architected register and add the physical registers - // to it. + // No renaming happens to the misc. registers. They are + // simply the registers that come after all the physical + // registers; thus take the base architected register and add + // the physical registers to it. renamed_reg = arch_reg + numPhysicalRegs; // Set the previous register to the same register; mainly it must be @@ -215,24 +192,17 @@ SimpleRenameMap::rename(RegIndex arch_reg) prev_reg = renamed_reg; assert(renamed_reg < numPhysicalRegs + numMiscRegs); - - miscScoreboard[renamed_reg] = false; } return RenameInfo(renamed_reg, prev_reg); } -//Perhaps give this a pair as a return value, of the physical register -//and whether or not it's ready. PhysRegIndex SimpleRenameMap::lookup(RegIndex arch_reg) { if (arch_reg < numLogicalIntRegs) { return intRenameMap[arch_reg].physical_reg; } else if (arch_reg < numLogicalRegs) { - // Subtract off the base FP offset. -// arch_reg = arch_reg - numLogicalIntRegs; - return floatRenameMap[arch_reg].physical_reg; } else { // Subtract off the misc registers offset. @@ -244,38 +214,18 @@ SimpleRenameMap::lookup(RegIndex arch_reg) } } -bool -SimpleRenameMap::isReady(PhysRegIndex phys_reg) -{ - if (phys_reg < numPhysicalIntRegs) { - return intScoreboard[phys_reg]; - } else if (phys_reg < numPhysicalRegs) { - - // Subtract off the base FP offset. -// phys_reg = phys_reg - numPhysicalIntRegs; - - return floatScoreboard[phys_reg]; - } else { - // Subtract off the misc registers offset. -// phys_reg = phys_reg - numPhysicalRegs; - - return miscScoreboard[phys_reg]; - } -} - -// In this implementation the miscellaneous registers do not actually rename, -// so this function does not allow you to try to change their mappings. void SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg) { + // In this implementation the miscellaneous registers do not + // actually rename, so this function does not allow you to try to + // change their mappings. if (arch_reg < numLogicalIntRegs) { DPRINTF(Rename, "Rename Map: Integer register %i being set to %i.\n", (int)arch_reg, renamed_reg); intRenameMap[arch_reg].physical_reg = renamed_reg; - } else { - assert(arch_reg < (numLogicalIntRegs + numLogicalFloatRegs)); - + } else if (arch_reg < numLogicalIntRegs + numLogicalFloatRegs) { DPRINTF(Rename, "Rename Map: Float register %i being set to %i.\n", (int)arch_reg - numLogicalIntRegs, renamed_reg); @@ -283,55 +233,6 @@ SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg) } } -void -SimpleRenameMap::squash(vector<RegIndex> freed_regs, - vector<UnmapInfo> unmaps) -{ - panic("Not sure this function should be called."); - - // Not sure the rename map should be able to access the free list - // like this. - while (!freed_regs.empty()) { - RegIndex free_register = freed_regs.back(); - - if (free_register < numPhysicalIntRegs) { - freeList->addIntReg(free_register); - } else { - // Subtract off the base FP dependence tag. - free_register = free_register - numPhysicalIntRegs; - freeList->addFloatReg(free_register); - } - - freed_regs.pop_back(); - } - - // Take unmap info and roll back the rename map. -} - -void -SimpleRenameMap::markAsReady(PhysRegIndex ready_reg) -{ - DPRINTF(Rename, "Rename map: Marking register %i as ready.\n", - (int)ready_reg); - - if (ready_reg < numPhysicalIntRegs) { - assert(ready_reg >= 0); - - intScoreboard[ready_reg] = 1; - } else if (ready_reg < numPhysicalRegs) { - - // Subtract off the base FP offset. -// ready_reg = ready_reg - numPhysicalIntRegs; - - floatScoreboard[ready_reg] = 1; - } else { - //Subtract off the misc registers offset. -// ready_reg = ready_reg - numPhysicalRegs; - - miscScoreboard[ready_reg] = 1; - } -} - int SimpleRenameMap::numFreeEntries() { diff --git a/src/cpu/o3/rename_map.hh b/src/cpu/o3/rename_map.hh index 57be4a64a..c4c90c99a 100644 --- a/src/cpu/o3/rename_map.hh +++ b/src/cpu/o3/rename_map.hh @@ -24,14 +24,16 @@ * 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. + * + * Authors: Kevin Lim */ // Todo: Create destructor. // Have it so that there's a more meaningful name given to the variable // that marks the beginning of the FP registers. -#ifndef __CPU_O3_CPU_RENAME_MAP_HH__ -#define __CPU_O3_CPU_RENAME_MAP_HH__ +#ifndef __CPU_O3_RENAME_MAP_HH__ +#define __CPU_O3_RENAME_MAP_HH__ #include <iostream> #include <utility> @@ -62,18 +64,30 @@ class SimpleRenameMap typedef std::pair<PhysRegIndex, PhysRegIndex> RenameInfo; public: - //Constructor - SimpleRenameMap(unsigned _numLogicalIntRegs, - unsigned _numPhysicalIntRegs, - unsigned _numLogicalFloatRegs, - unsigned _numPhysicalFloatRegs, - unsigned _numMiscRegs, - RegIndex _intZeroReg, - RegIndex _floatZeroReg); + /** Default constructor. init() must be called prior to use. */ + SimpleRenameMap() {}; /** Destructor. */ ~SimpleRenameMap(); + /** Initializes rename map with given parameters. */ + void init(unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + PhysRegIndex &_int_reg_start, + + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + PhysRegIndex &_float_reg_start, + + unsigned _numMiscRegs, + + RegIndex _intZeroReg, + RegIndex _floatZeroReg, + + int id, + bool bindRegs); + + /** Sets the free list used with this rename map. */ void setFreeList(SimpleFreeList *fl_ptr); //Tell rename map to get a free physical register for a given @@ -84,23 +98,19 @@ class SimpleRenameMap PhysRegIndex lookup(RegIndex phys_reg); - bool isReady(PhysRegIndex arch_reg); - /** * Marks the given register as ready, meaning that its value has been * calculated and written to the register file. * @param ready_reg The index of the physical register that is now ready. */ - void markAsReady(PhysRegIndex ready_reg); - void setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg); - void squash(std::vector<RegIndex> freed_regs, - std::vector<UnmapInfo> unmaps); - int numFreeEntries(); private: + /** Rename Map ID */ + int id; + /** Number of logical integer registers. */ int numLogicalIntRegs; @@ -143,31 +153,16 @@ class SimpleRenameMap { } }; + private: /** Integer rename map. */ - RenameEntry *intRenameMap; + std::vector<RenameEntry> intRenameMap; /** Floating point rename map. */ - RenameEntry *floatRenameMap; + std::vector<RenameEntry> floatRenameMap; + private: /** Free list interface. */ SimpleFreeList *freeList; - - // Might want to make all these scoreboards into one large scoreboard. - - /** Scoreboard of physical integer registers, saying whether or not they - * are ready. - */ - std::vector<bool> intScoreboard; - - /** Scoreboard of physical floating registers, saying whether or not they - * are ready. - */ - std::vector<bool> floatScoreboard; - - /** Scoreboard of miscellaneous registers, saying whether or not they - * are ready. - */ - std::vector<bool> miscScoreboard; }; -#endif //__CPU_O3_CPU_RENAME_MAP_HH__ +#endif //__CPU_O3_RENAME_MAP_HH__ diff --git a/src/cpu/o3/rob.cc b/src/cpu/o3/rob.cc index c10f782fd..9976049cd 100644 --- a/src/cpu/o3/rob.cc +++ b/src/cpu/o3/rob.cc @@ -24,11 +24,13 @@ * 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. + * + * Authors: Kevin Lim + * Nathan Binkert */ -#include "cpu/o3/alpha_dyn_inst.hh" -#include "cpu/o3/alpha_impl.hh" +#include "cpu/o3/isa_specific.hh" #include "cpu/o3/rob_impl.hh" // Force instantiation of InstructionQueue. -template class ROB<AlphaSimpleImpl>; +template class ROB<O3CPUImpl>; diff --git a/src/cpu/o3/rob.hh b/src/cpu/o3/rob.hh index 1185564ad..6f8080ef4 100644 --- a/src/cpu/o3/rob.hh +++ b/src/cpu/o3/rob.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,25 +24,20 @@ * 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. + * + * Authors: Kevin Lim + * Korey Sewell */ -// Todo: Probably add in support for scheduling events (more than one as -// well) on the case of the ROB being empty or full. Considering tracking -// free entries instead of insts in ROB. Differentiate between squashing -// all instructions after the instruction, and all instructions after *and* -// including that instruction. - -#ifndef __CPU_O3_CPU_ROB_HH__ -#define __CPU_O3_CPU_ROB_HH__ +#ifndef __CPU_O3_ROB_HH__ +#define __CPU_O3_ROB_HH__ +#include <string> #include <utility> #include <vector> /** - * ROB class. Uses the instruction list that exists within the CPU to - * represent the ROB. This class doesn't contain that list, but instead - * a pointer to the CPU to get access to the list. The ROB, in this first - * implementation, is largely what drives squashing. + * ROB class. The ROB is largely what drives squashing. */ template <class Impl> class ROB @@ -51,32 +46,68 @@ class ROB typedef TheISA::RegIndex RegIndex; public: //Typedefs from the Impl. - typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::O3CPU O3CPU; typedef typename Impl::DynInstPtr DynInstPtr; - typedef std::pair<RegIndex, PhysRegIndex> UnmapInfo_t; - typedef typename list<DynInstPtr>::iterator InstIt_t; + typedef std::pair<RegIndex, PhysRegIndex> UnmapInfo; + typedef typename std::list<DynInstPtr>::iterator InstIt; + + /** Possible ROB statuses. */ + enum Status { + Running, + Idle, + ROBSquashing + }; + + /** SMT ROB Sharing Policy */ + enum ROBPolicy{ + Dynamic, + Partitioned, + Threshold + }; + + private: + /** Per-thread ROB status. */ + Status robStatus[Impl::MaxThreads]; + + /** ROB resource sharing policy for SMT mode. */ + ROBPolicy robPolicy; public: /** ROB constructor. - * @param _numEntries Number of entries in ROB. - * @param _squashWidth Number of instructions that can be squashed in a - * single cycle. + * @param _numEntries Number of entries in ROB. + * @param _squashWidth Number of instructions that can be squashed in a + * single cycle. + * @param _smtROBPolicy ROB Partitioning Scheme for SMT. + * @param _smtROBThreshold Max Resources(by %) a thread can have in the ROB. + * @param _numThreads The number of active threads. */ - ROB(unsigned _numEntries, unsigned _squashWidth); + ROB(unsigned _numEntries, unsigned _squashWidth, std::string smtROBPolicy, + unsigned _smtROBThreshold, unsigned _numThreads); + + std::string name() const; /** Function to set the CPU pointer, necessary due to which object the ROB * is created within. * @param cpu_ptr Pointer to the implementation specific full CPU object. */ - void setCPU(FullCPU *cpu_ptr); + void setCPU(O3CPU *cpu_ptr); + + /** Sets pointer to the list of active threads. + * @param at_ptr Pointer to the list of active threads. + */ + void setActiveThreads(std::list<unsigned>* at_ptr); + + /** Switches out the ROB. */ + void switchOut(); - /** Function to insert an instruction into the ROB. The parameter inst is - * not truly required, but is useful for checking correctness. Note - * that whatever calls this function must ensure that there is enough - * space within the ROB for the new instruction. + /** Takes over another CPU's thread. */ + void takeOverFrom(); + + /** Function to insert an instruction into the ROB. Note that whatever + * calls this function must ensure that there is enough space within the + * ROB for the new instruction. * @param inst The instruction being inserted into the ROB. - * @todo Remove the parameter once correctness is ensured. */ void insertInst(DynInstPtr &inst); @@ -84,40 +115,134 @@ class ROB * no guarantee as to the return value if the ROB is empty. * @retval Pointer to the DynInst that is at the head of the ROB. */ - DynInstPtr readHeadInst() { return cpu->instList.front(); } +// DynInstPtr readHeadInst(); + + /** Returns a pointer to the head instruction of a specific thread within + * the ROB. + * @return Pointer to the DynInst that is at the head of the ROB. + */ + DynInstPtr readHeadInst(unsigned tid); - DynInstPtr readTailInst() { return (*tail); } + /** Returns pointer to the tail instruction within the ROB. There is + * no guarantee as to the return value if the ROB is empty. + * @retval Pointer to the DynInst that is at the tail of the ROB. + */ +// DynInstPtr readTailInst(); + + /** Returns a pointer to the tail instruction of a specific thread within + * the ROB. + * @return Pointer to the DynInst that is at the tail of the ROB. + */ + DynInstPtr readTailInst(unsigned tid); + + /** Retires the head instruction, removing it from the ROB. */ +// void retireHead(); + + /** Retires the head instruction of a specific thread, removing it from the + * ROB. + */ + void retireHead(unsigned tid); - void retireHead(); + /** Is the oldest instruction across all threads ready. */ +// bool isHeadReady(); - bool isHeadReady(); + /** Is the oldest instruction across a particular thread ready. */ + bool isHeadReady(unsigned tid); + /** Is there any commitable head instruction across all threads ready. */ + bool canCommit(); + + /** Re-adjust ROB partitioning. */ + void resetEntries(); + + /** Number of entries needed For 'num_threads' amount of threads. */ + int entryAmount(int num_threads); + + /** Returns the number of total free entries in the ROB. */ unsigned numFreeEntries(); + /** Returns the number of free entries in a specific ROB paritition. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns the maximum number of entries for a specific thread. */ + unsigned getMaxEntries(unsigned tid) + { return maxEntries[tid]; } + + /** Returns the number of entries being used by a specific thread. */ + unsigned getThreadEntries(unsigned tid) + { return threadEntries[tid]; } + + /** Returns if the ROB is full. */ bool isFull() { return numInstsInROB == numEntries; } + /** Returns if a specific thread's partition is full. */ + bool isFull(unsigned tid) + { return threadEntries[tid] == numEntries; } + + /** Returns if the ROB is empty. */ bool isEmpty() { return numInstsInROB == 0; } - void doSquash(); + /** Returns if a specific thread's partition is empty. */ + bool isEmpty(unsigned tid) + { return threadEntries[tid] == 0; } - void squash(InstSeqNum squash_num); + /** Executes the squash, marking squashed instructions. */ + void doSquash(unsigned tid); - uint64_t readHeadPC(); + /** Squashes all instructions younger than the given sequence number for + * the specific thread. + */ + void squash(InstSeqNum squash_num, unsigned tid); + + /** Updates the head instruction with the new oldest instruction. */ + void updateHead(); + + /** Updates the tail instruction with the new youngest instruction. */ + void updateTail(); + + /** Reads the PC of the oldest head instruction. */ +// uint64_t readHeadPC(); + + /** Reads the PC of the head instruction of a specific thread. */ +// uint64_t readHeadPC(unsigned tid); + + /** Reads the next PC of the oldest head instruction. */ +// uint64_t readHeadNextPC(); + + /** Reads the next PC of the head instruction of a specific thread. */ +// uint64_t readHeadNextPC(unsigned tid); - uint64_t readHeadNextPC(); + /** Reads the sequence number of the oldest head instruction. */ +// InstSeqNum readHeadSeqNum(); - InstSeqNum readHeadSeqNum(); + /** Reads the sequence number of the head instruction of a specific thread. + */ +// InstSeqNum readHeadSeqNum(unsigned tid); + + /** Reads the PC of the youngest tail instruction. */ +// uint64_t readTailPC(); - uint64_t readTailPC(); + /** Reads the PC of the tail instruction of a specific thread. */ +// uint64_t readTailPC(unsigned tid); - InstSeqNum readTailSeqNum(); + /** Reads the sequence number of the youngest tail instruction. */ +// InstSeqNum readTailSeqNum(); + + /** Reads the sequence number of tail instruction of a specific thread. */ +// InstSeqNum readTailSeqNum(unsigned tid); /** Checks if the ROB is still in the process of squashing instructions. * @retval Whether or not the ROB is done squashing. */ - bool isDoneSquashing() const { return doneSquashing; } + bool isDoneSquashing(unsigned tid) const + { return doneSquashing[tid]; } + + /** Checks if the ROB is still in the process of squashing instructions for + * any thread. + */ + bool isDoneSquashing(); /** This is more of a debugging function than anything. Use * numInstsInROB to get the instructions in the ROB unless you are @@ -125,23 +250,46 @@ class ROB */ int countInsts(); - private: + /** This is more of a debugging function than anything. Use + * threadEntries to get the instructions in the ROB unless you are + * double checking that variable. + */ + int countInsts(unsigned tid); + private: /** Pointer to the CPU. */ - FullCPU *cpu; + O3CPU *cpu; + + /** Active Threads in CPU */ + std::list<unsigned>* activeThreads; /** Number of instructions in the ROB. */ unsigned numEntries; + /** Entries Per Thread */ + unsigned threadEntries[Impl::MaxThreads]; + + /** Max Insts a Thread Can Have in the ROB */ + unsigned maxEntries[Impl::MaxThreads]; + + /** ROB List of Instructions */ + std::list<DynInstPtr> instList[Impl::MaxThreads]; + /** Number of instructions that can be squashed in a single cycle. */ unsigned squashWidth; + public: /** Iterator pointing to the instruction which is the last instruction * in the ROB. This may at times be invalid (ie when the ROB is empty), * however it should never be incorrect. */ - InstIt_t tail; + InstIt tail; + + /** Iterator pointing to the instruction which is the first instruction in + * in the ROB*/ + InstIt head; + private: /** Iterator used for walking through the list of instructions when * squashing. Used so that there is persistent state between cycles; * when squashing, the instructions are marked as squashed but not @@ -149,16 +297,24 @@ class ROB * and after a squash. * This will always be set to cpu->instList.end() if it is invalid. */ - InstIt_t squashIt; + InstIt squashIt[Impl::MaxThreads]; + public: /** Number of instructions in the ROB. */ int numInstsInROB; + /** Dummy instruction returned if there are no insts left. */ + DynInstPtr dummyInst; + + private: /** The sequence number of the squashed instruction. */ InstSeqNum squashedSeqNum; /** Is the ROB done squashing. */ - bool doneSquashing; + bool doneSquashing[Impl::MaxThreads]; + + /** Number of active threads. */ + unsigned numThreads; }; -#endif //__CPU_O3_CPU_ROB_HH__ +#endif //__CPU_O3_ROB_HH__ diff --git a/src/cpu/o3/rob_impl.hh b/src/cpu/o3/rob_impl.hh index e7a5671d9..d9978b17f 100644 --- a/src/cpu/o3/rob_impl.hh +++ b/src/cpu/o3/rob_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,148 +24,308 @@ * 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. + * + * Authors: Kevin Lim + * Korey Sewell */ -#ifndef __CPU_O3_CPU_ROB_IMPL_HH__ -#define __CPU_O3_CPU_ROB_IMPL_HH__ - #include "config/full_system.hh" #include "cpu/o3/rob.hh" +using namespace std; + template <class Impl> -ROB<Impl>::ROB(unsigned _numEntries, unsigned _squashWidth) +ROB<Impl>::ROB(unsigned _numEntries, unsigned _squashWidth, + string _smtROBPolicy, unsigned _smtROBThreshold, + unsigned _numThreads) : numEntries(_numEntries), squashWidth(_squashWidth), numInstsInROB(0), - squashedSeqNum(0) + squashedSeqNum(0), + numThreads(_numThreads) +{ + for (int tid=0; tid < numThreads; tid++) { + doneSquashing[tid] = true; + threadEntries[tid] = 0; + } + + string policy = _smtROBPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out rob policy + if (policy == "dynamic") { + robPolicy = Dynamic; + + //Set Max Entries to Total ROB Capacity + for (int i = 0; i < numThreads; i++) { + maxEntries[i]=numEntries; + } + + } else if (policy == "partitioned") { + robPolicy = Partitioned; + DPRINTF(Fetch, "ROB sharing policy set to Partitioned\n"); + + //@todo:make work if part_amt doesnt divide evenly. + int part_amt = numEntries / numThreads; + + //Divide ROB up evenly + for (int i = 0; i < numThreads; i++) { + maxEntries[i]=part_amt; + } + + } else if (policy == "threshold") { + robPolicy = Threshold; + DPRINTF(Fetch, "ROB sharing policy set to Threshold\n"); + + int threshold = _smtROBThreshold;; + + //Divide up by threshold amount + for (int i = 0; i < numThreads; i++) { + maxEntries[i]=threshold; + } + } else { + assert(0 && "Invalid ROB Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } +} + +template <class Impl> +std::string +ROB<Impl>::name() const { - doneSquashing = true; + return cpu->name() + ".rob"; } template <class Impl> void -ROB<Impl>::setCPU(FullCPU *cpu_ptr) +ROB<Impl>::setCPU(O3CPU *cpu_ptr) { cpu = cpu_ptr; - // Set the tail to the beginning of the CPU instruction list so that - // upon the first instruction being inserted into the ROB, the tail - // iterator can simply be incremented. - tail = cpu->instList.begin(); + // Set the per-thread iterators to the end of the instruction list. + for (int i=0; i < numThreads;i++) { + squashIt[i] = instList[i].end(); + } - // Set the squash iterator to the end of the instruction list. - squashIt = cpu->instList.end(); + // Initialize the "universal" ROB head & tail point to invalid + // pointers + head = instList[0].end(); + tail = instList[0].end(); } template <class Impl> -int -ROB<Impl>::countInsts() +void +ROB<Impl>::setActiveThreads(list<unsigned> *at_ptr) { - // Start at 1; if the tail matches cpu->instList.begin(), then there is - // one inst in the ROB. - int return_val = 1; + DPRINTF(ROB, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} - // There are quite a few special cases. Do not use this function other - // than for debugging purposes. - if (cpu->instList.begin() == cpu->instList.end()) { - // In this case there are no instructions in the list. The ROB - // must be empty. - return 0; - } else if (tail == cpu->instList.end()) { - // In this case, the tail is not yet pointing to anything valid. - // The ROB must be empty. - return 0; +template <class Impl> +void +ROB<Impl>::switchOut() +{ + for (int tid = 0; tid < numThreads; tid++) { + instList[tid].clear(); } +} - // Iterate through the ROB from the head to the tail, counting the - // entries. - for (InstIt_t i = cpu->instList.begin(); i != tail; ++i) - { - assert(i != cpu->instList.end()); - ++return_val; +template <class Impl> +void +ROB<Impl>::takeOverFrom() +{ + for (int tid=0; tid < numThreads; tid++) { + doneSquashing[tid] = true; + threadEntries[tid] = 0; + squashIt[tid] = instList[tid].end(); } + numInstsInROB = 0; - return return_val; + // Initialize the "universal" ROB head & tail point to invalid + // pointers + head = instList[0].end(); + tail = instList[0].end(); +} - // Because the head won't be tracked properly until the ROB gets the - // first instruction, and any time that the ROB is empty and has not - // yet gotten the instruction, this function doesn't work. -// return numInstsInROB; +template <class Impl> +void +ROB<Impl>::resetEntries() +{ + if (robPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + list<unsigned>::iterator list_end = (*activeThreads).end(); + + while (threads != list_end) { + if (robPolicy == Partitioned) { + maxEntries[*threads++] = numEntries / active_threads; + } else if (robPolicy == Threshold && active_threads == 1) { + maxEntries[*threads++] = numEntries; + } + } + } +} + +template <class Impl> +int +ROB<Impl>::entryAmount(int num_threads) +{ + if (robPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + +template <class Impl> +int +ROB<Impl>::countInsts() +{ + int total=0; + + for (int i=0;i < numThreads;i++) + total += countInsts(i); + + return total; +} + +template <class Impl> +int +ROB<Impl>::countInsts(unsigned tid) +{ + return instList[tid].size(); } template <class Impl> void ROB<Impl>::insertInst(DynInstPtr &inst) { - // Make sure we have the right number of instructions. - assert(numInstsInROB == countInsts()); - // Make sure the instruction is valid. + //assert(numInstsInROB == countInsts()); assert(inst); - DPRINTF(ROB, "ROB: Adding inst PC %#x to the ROB.\n", inst->readPC()); + DPRINTF(ROB, "Adding inst PC %#x to the ROB.\n", inst->readPC()); - // If the ROB is full then exit. assert(numInstsInROB != numEntries); - ++numInstsInROB; + int tid = inst->threadNumber; - // Increment the tail iterator, moving it one instruction back. - // There is a special case if the ROB was empty prior to this insertion, - // in which case the tail will be pointing at instList.end(). If that - // happens, then reset the tail to the beginning of the list. - if (tail != cpu->instList.end()) { - ++tail; - } else { - tail = cpu->instList.begin(); + instList[tid].push_back(inst); + + //Set Up head iterator if this is the 1st instruction in the ROB + if (numInstsInROB == 0) { + head = instList[tid].begin(); + assert((*head) == inst); } - // Make sure the tail iterator is actually pointing at the instruction - // added. - assert((*tail) == inst); + //Must Decrement for iterator to actually be valid since __.end() + //actually points to 1 after the last inst + tail = instList[tid].end(); + tail--; + + inst->setInROB(); - DPRINTF(ROB, "ROB: Now has %d instructions.\n", numInstsInROB); + ++numInstsInROB; + ++threadEntries[tid]; + + assert((*tail) == inst); + DPRINTF(ROB, "[tid:%i] Now has %d instructions.\n", tid, threadEntries[tid]); } // Whatever calls this function needs to ensure that it properly frees up // registers prior to this function. +/* template <class Impl> void ROB<Impl>::retireHead() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); + assert(numInstsInROB > 0); + + int tid = (*head)->threadNumber; + + retireHead(tid); + + if (numInstsInROB == 0) { + tail = instList[tid].end(); + } +} +*/ + +template <class Impl> +void +ROB<Impl>::retireHead(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); assert(numInstsInROB > 0); // Get the head ROB instruction. - DynInstPtr head_inst = cpu->instList.front(); + InstIt head_it = instList[tid].begin(); + + DynInstPtr head_inst = (*head_it); - // Make certain this can retire. assert(head_inst->readyToCommit()); - DPRINTF(ROB, "ROB: Retiring head instruction of the ROB, " - "instruction PC %#x, seq num %i\n", head_inst->readPC(), + DPRINTF(ROB, "[tid:%u]: Retiring head instruction, " + "instruction PC %#x,[sn:%lli]\n", tid, head_inst->readPC(), head_inst->seqNum); - // Keep track of how many instructions are in the ROB. --numInstsInROB; + --threadEntries[tid]; + + head_inst->clearInROB(); + head_inst->setCommitted(); - // Tell CPU to remove the instruction from the list of instructions. - // A special case is needed if the instruction being retired is the - // only instruction in the ROB; otherwise the tail iterator will become - // invalidated. + instList[tid].erase(head_it); + + //Update "Global" Head of ROB + updateHead(); + + // @todo: A special case is needed if the instruction being + // retired is the only instruction in the ROB; otherwise the tail + // iterator will become invalidated. cpu->removeFrontInst(head_inst); +} +/* +template <class Impl> +bool +ROB<Impl>::isHeadReady() +{ + if (numInstsInROB != 0) { + return (*head)->readyToCommit(); + } - if (numInstsInROB == 0) { - tail = cpu->instList.end(); + return false; +} +*/ +template <class Impl> +bool +ROB<Impl>::isHeadReady(unsigned tid) +{ + if (threadEntries[tid] != 0) { + return instList[tid].front()->readyToCommit(); } + + return false; } template <class Impl> bool -ROB<Impl>::isHeadReady() +ROB<Impl>::canCommit() { - if (numInstsInROB != 0) { - return cpu->instList.front()->readyToCommit(); + //@todo: set ActiveThreads through ROB or CPU + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (isHeadReady(tid)) { + return true; + } } return false; @@ -175,131 +335,339 @@ template <class Impl> unsigned ROB<Impl>::numFreeEntries() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); return numEntries - numInstsInROB; } template <class Impl> +unsigned +ROB<Impl>::numFreeEntries(unsigned tid) +{ + return maxEntries[tid] - threadEntries[tid]; +} + +template <class Impl> void -ROB<Impl>::doSquash() +ROB<Impl>::doSquash(unsigned tid) { - DPRINTF(ROB, "ROB: Squashing instructions.\n"); + DPRINTF(ROB, "[tid:%u]: Squashing instructions until [sn:%i].\n", + tid, squashedSeqNum); + + assert(squashIt[tid] != instList[tid].end()); - assert(squashIt != cpu->instList.end()); + if ((*squashIt[tid])->seqNum < squashedSeqNum) { + DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n", + tid); + + squashIt[tid] = instList[tid].end(); + + doneSquashing[tid] = true; + return; + } + + bool robTailUpdate = false; for (int numSquashed = 0; - numSquashed < squashWidth && (*squashIt)->seqNum != squashedSeqNum; + numSquashed < squashWidth && + squashIt[tid] != instList[tid].end() && + (*squashIt[tid])->seqNum > squashedSeqNum; ++numSquashed) { - // Ensure that the instruction is younger. - assert((*squashIt)->seqNum > squashedSeqNum); - - DPRINTF(ROB, "ROB: Squashing instruction PC %#x, seq num %i.\n", - (*squashIt)->readPC(), (*squashIt)->seqNum); + DPRINTF(ROB, "[tid:%u]: Squashing instruction PC %#x, seq num %i.\n", + (*squashIt[tid])->threadNumber, + (*squashIt[tid])->readPC(), + (*squashIt[tid])->seqNum); // Mark the instruction as squashed, and ready to commit so that // it can drain out of the pipeline. - (*squashIt)->setSquashed(); - - (*squashIt)->setCanCommit(); - - // Special case for when squashing due to a syscall. It's possible - // that the squash happened after the head instruction was already - // committed, meaning that (*squashIt)->seqNum != squashedSeqNum - // will never be false. Normally the squash would never be able - // to go past the head of the ROB; in this case it might, so it - // must be handled otherwise it will segfault. -#if !FULL_SYSTEM - if (squashIt == cpu->instList.begin()) { - DPRINTF(ROB, "ROB: Reached head of instruction list while " + (*squashIt[tid])->setSquashed(); + + (*squashIt[tid])->setCanCommit(); + + + if (squashIt[tid] == instList[tid].begin()) { + DPRINTF(ROB, "Reached head of instruction list while " "squashing.\n"); - squashIt = cpu->instList.end(); + squashIt[tid] = instList[tid].end(); - doneSquashing = true; + doneSquashing[tid] = true; return; } -#endif - // Move the tail iterator to the next instruction. - squashIt--; + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + if ((*squashIt[tid]) == (*tail_thread)) + robTailUpdate = true; + + squashIt[tid]--; } // Check if ROB is done squashing. - if ((*squashIt)->seqNum == squashedSeqNum) { - DPRINTF(ROB, "ROB: Done squashing instructions.\n"); + if ((*squashIt[tid])->seqNum <= squashedSeqNum) { + DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n", + tid); - squashIt = cpu->instList.end(); + squashIt[tid] = instList[tid].end(); + + doneSquashing[tid] = true; + } - doneSquashing = true; + if (robTailUpdate) { + updateTail(); } } + template <class Impl> void -ROB<Impl>::squash(InstSeqNum squash_num) +ROB<Impl>::updateHead() { - DPRINTF(ROB, "ROB: Starting to squash within the ROB.\n"); - doneSquashing = false; + DynInstPtr head_inst; + InstSeqNum lowest_num = 0; + bool first_valid = true; + + // @todo: set ActiveThreads through ROB or CPU + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned thread_num = *threads++; + + if (instList[thread_num].empty()) + continue; + + if (first_valid) { + head = instList[thread_num].begin(); + lowest_num = (*head)->seqNum; + first_valid = false; + continue; + } + + InstIt head_thread = instList[thread_num].begin(); + + DynInstPtr head_inst = (*head_thread); + + assert(head_inst != 0); + + if (head_inst->seqNum < lowest_num) { + head = head_thread; + lowest_num = head_inst->seqNum; + } + } + + if (first_valid) { + head = instList[0].end(); + } + +} + +template <class Impl> +void +ROB<Impl>::updateTail() +{ + tail = instList[0].end(); + bool first_valid = true; + + list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (instList[tid].empty()) { + continue; + } + + // If this is the first valid then assign w/out + // comparison + if (first_valid) { + tail = instList[tid].end(); + tail--; + first_valid = false; + continue; + } + + // Assign new tail if this thread's tail is younger + // than our current "tail high" + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + if ((*tail_thread)->seqNum > (*tail)->seqNum) { + tail = tail_thread; + } + } +} + + +template <class Impl> +void +ROB<Impl>::squash(InstSeqNum squash_num,unsigned tid) +{ + if (isEmpty()) { + DPRINTF(ROB, "Does not need to squash due to being empty " + "[sn:%i]\n", + squash_num); + + return; + } + + DPRINTF(ROB, "Starting to squash within the ROB.\n"); + + robStatus[tid] = ROBSquashing; + + doneSquashing[tid] = false; squashedSeqNum = squash_num; - assert(tail != cpu->instList.end()); + if (!instList[tid].empty()) { + InstIt tail_thread = instList[tid].end(); + tail_thread--; - squashIt = tail; + squashIt[tid] = tail_thread; - doSquash(); + doSquash(tid); + } } +/* +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readHeadInst() +{ + if (numInstsInROB != 0) { + assert((*head)->isInROB()==true); + return *head; + } else { + return dummyInst; + } +} +*/ +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readHeadInst(unsigned tid) +{ + if (threadEntries[tid] != 0) { + InstIt head_thread = instList[tid].begin(); + assert((*head_thread)->isInROB()==true); + + return *head_thread; + } else { + return dummyInst; + } +} +/* template <class Impl> uint64_t ROB<Impl>::readHeadPC() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); - DynInstPtr head_inst = cpu->instList.front(); + DynInstPtr head_inst = *head; return head_inst->readPC(); } template <class Impl> uint64_t +ROB<Impl>::readHeadPC(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); + InstIt head_thread = instList[tid].begin(); + + return (*head_thread)->readPC(); +} + + +template <class Impl> +uint64_t ROB<Impl>::readHeadNextPC() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); - DynInstPtr head_inst = cpu->instList.front(); + DynInstPtr head_inst = *head; return head_inst->readNextPC(); } template <class Impl> +uint64_t +ROB<Impl>::readHeadNextPC(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); + InstIt head_thread = instList[tid].begin(); + + return (*head_thread)->readNextPC(); +} + +template <class Impl> InstSeqNum ROB<Impl>::readHeadSeqNum() { - // Return the last sequence number that has not been squashed. Other - // stages can use it to squash any instructions younger than the current - // tail. - DynInstPtr head_inst = cpu->instList.front(); + //assert(numInstsInROB == countInsts()); + DynInstPtr head_inst = *head; return head_inst->seqNum; } template <class Impl> +InstSeqNum +ROB<Impl>::readHeadSeqNum(unsigned tid) +{ + InstIt head_thread = instList[tid].begin(); + + return ((*head_thread)->seqNum); +} + +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readTailInst() +{ + //assert(numInstsInROB == countInsts()); + //assert(tail != instList[0].end()); + + return (*tail); +} +*/ +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readTailInst(unsigned tid) +{ + //assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return *tail_thread; +} + +/* +template <class Impl> uint64_t ROB<Impl>::readTailPC() { - assert(numInstsInROB == countInsts()); + //assert(numInstsInROB == countInsts()); - assert(tail != cpu->instList.end()); + //assert(tail != instList[0].end()); return (*tail)->readPC(); } template <class Impl> +uint64_t +ROB<Impl>::readTailPC(unsigned tid) +{ + //assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return (*tail_thread)->readPC(); +} + +template <class Impl> InstSeqNum ROB<Impl>::readTailSeqNum() { @@ -309,4 +677,18 @@ ROB<Impl>::readTailSeqNum() return (*tail)->seqNum; } -#endif // __CPU_O3_CPU_ROB_IMPL_HH__ +template <class Impl> +InstSeqNum +ROB<Impl>::readTailSeqNum(unsigned tid) +{ + // Return the last sequence number that has not been squashed. Other + // stages can use it to squash any instructions younger than the current + // tail. + // assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return (*tail_thread)->seqNum; +} +*/ diff --git a/src/cpu/o3/sat_counter.cc b/src/cpu/o3/sat_counter.cc index d20fff650..68d3ef627 100644 --- a/src/cpu/o3/sat_counter.cc +++ b/src/cpu/o3/sat_counter.cc @@ -24,27 +24,29 @@ * 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. + * + * Authors: Kevin Lim */ #include "base/misc.hh" #include "cpu/o3/sat_counter.hh" SatCounter::SatCounter() - : maxVal(0), counter(0) + : initialVal(0), counter(0) { } SatCounter::SatCounter(unsigned bits) - : maxVal((1 << bits) - 1), counter(0) + : initialVal(0), maxVal((1 << bits) - 1), counter(0) { } -SatCounter::SatCounter(unsigned bits, unsigned initial_val) - : maxVal((1 << bits) - 1), counter(initial_val) +SatCounter::SatCounter(unsigned bits, uint8_t initial_val) + : initialVal(initialVal), maxVal((1 << bits) - 1), counter(initial_val) { // Check to make sure initial value doesn't exceed the max counter value. if (initial_val > maxVal) { - panic("BP: Initial counter value exceeds max size."); + fatal("BP: Initial counter value exceeds max size."); } } @@ -53,19 +55,3 @@ SatCounter::setBits(unsigned bits) { maxVal = (1 << bits) - 1; } - -void -SatCounter::increment() -{ - if(counter < maxVal) { - ++counter; - } -} - -void -SatCounter::decrement() -{ - if(counter > 0) { - --counter; - } -} diff --git a/src/cpu/o3/sat_counter.hh b/src/cpu/o3/sat_counter.hh index b7cfe6423..7e15119b0 100644 --- a/src/cpu/o3/sat_counter.hh +++ b/src/cpu/o3/sat_counter.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 The Regents of The University of Michigan + * Copyright (c) 2005-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,11 +24,14 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_SAT_COUNTER_HH__ -#define __CPU_O3_CPU_SAT_COUNTER_HH__ +#ifndef __CPU_O3_SAT_COUNTER_HH__ +#define __CPU_O3_SAT_COUNTER_HH__ +#include "base/misc.hh" #include "sim/host.hh" /** @@ -44,47 +47,70 @@ class SatCounter /** * Constructor for the counter. */ - SatCounter(); + SatCounter() + : initialVal(0), counter(0) + { } /** * Constructor for the counter. * @param bits How many bits the counter will have. */ - SatCounter(unsigned bits); + SatCounter(unsigned bits) + : initialVal(0), maxVal((1 << bits) - 1), counter(0) + { } /** * Constructor for the counter. * @param bits How many bits the counter will have. * @param initial_val Starting value for each counter. */ - SatCounter(unsigned bits, unsigned initial_val); + SatCounter(unsigned bits, uint8_t initial_val) + : initialVal(initialVal), maxVal((1 << bits) - 1), counter(initial_val) + { + // Check to make sure initial value doesn't exceed the max + // counter value. + if (initial_val > maxVal) { + fatal("BP: Initial counter value exceeds max size."); + } + } /** * Sets the number of bits. */ - void setBits(unsigned bits); + void setBits(unsigned bits) { maxVal = (1 << bits) - 1; } + + void reset() { counter = initialVal; } /** * Increments the counter's current value. */ - void increment(); + void increment() + { + if (counter < maxVal) { + ++counter; + } + } /** * Decrements the counter's current value. */ - void decrement(); + void decrement() + { + if (counter > 0) { + --counter; + } + } /** * Read the counter's value. */ const uint8_t read() const - { - return counter; - } + { return counter; } private: + uint8_t initialVal; uint8_t maxVal; uint8_t counter; }; -#endif // __CPU_O3_CPU_SAT_COUNTER_HH__ +#endif // __CPU_O3_SAT_COUNTER_HH__ diff --git a/src/cpu/o3/scoreboard.cc b/src/cpu/o3/scoreboard.cc new file mode 100644 index 000000000..1859b35a4 --- /dev/null +++ b/src/cpu/o3/scoreboard.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell + * Kevin Lim + */ + +#include "cpu/o3/scoreboard.hh" + +Scoreboard::Scoreboard(unsigned activeThreads, + unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + unsigned _numMiscRegs, + unsigned _zeroRegIdx) + : numLogicalIntRegs(_numLogicalIntRegs), + numPhysicalIntRegs(_numPhysicalIntRegs), + numLogicalFloatRegs(_numLogicalFloatRegs), + numPhysicalFloatRegs(_numPhysicalFloatRegs), + numMiscRegs(_numMiscRegs), + zeroRegIdx(_zeroRegIdx) +{ + //Get Register Sizes + numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs; + numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs; + + //Resize scoreboard appropriately + regScoreBoard.resize(numPhysicalRegs + (numMiscRegs * activeThreads)); + + //Initialize values + for (int i=0; i < numLogicalIntRegs * activeThreads; i++) { + regScoreBoard[i] = 1; + } + + for (int i= numPhysicalIntRegs; + i < numPhysicalIntRegs + (numLogicalFloatRegs * activeThreads); + i++) { + regScoreBoard[i] = 1; + } + + for (int i = numPhysicalRegs; + i < numPhysicalRegs + (numMiscRegs * activeThreads); + i++) { + regScoreBoard[i] = 1; + } +} + +std::string +Scoreboard::name() const +{ + return "cpu.scoreboard"; +} + +bool +Scoreboard::getReg(PhysRegIndex phys_reg) +{ + // Always ready if int or fp zero reg. + if (phys_reg == zeroRegIdx || + phys_reg == (zeroRegIdx + numPhysicalIntRegs)) { + return 1; + } + + return regScoreBoard[phys_reg]; +} + +void +Scoreboard::setReg(PhysRegIndex phys_reg) +{ + DPRINTF(Scoreboard, "Setting reg %i as ready\n", phys_reg); + + regScoreBoard[phys_reg] = 1; +} + +void +Scoreboard::unsetReg(PhysRegIndex ready_reg) +{ + if (ready_reg == zeroRegIdx || + ready_reg == (zeroRegIdx + numPhysicalIntRegs)) { + // Don't do anything if int or fp zero reg. + return; + } + + regScoreBoard[ready_reg] = 0; +} diff --git a/src/cpu/o3/scoreboard.hh b/src/cpu/o3/scoreboard.hh new file mode 100644 index 000000000..f8e4df3b7 --- /dev/null +++ b/src/cpu/o3/scoreboard.hh @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Korey Sewell + * Kevin Lim + */ + +#ifndef __CPU_O3_SCOREBOARD_HH__ +#define __CPU_O3_SCOREBOARD_HH__ + +#include <iostream> +#include <utility> +#include <vector> +#include "arch/alpha/isa_traits.hh" +#include "base/trace.hh" +#include "base/traceflags.hh" +#include "cpu/o3/comm.hh" + +/** + * Implements a simple scoreboard to track which registers are ready. + * This class assumes that the fp registers start, index wise, right after + * the integer registers. The misc. registers start, index wise, right after + * the fp registers. + * @todo: Fix up handling of the zero register in case the decoder does not + * automatically make insts that write the zero register into nops. + */ +class Scoreboard +{ + public: + /** Constructs a scoreboard. + * @param activeThreads The number of active threads. + * @param _numLogicalIntRegs Number of logical integer registers. + * @param _numPhysicalIntRegs Number of physical integer registers. + * @param _numLogicalFloatRegs Number of logical fp registers. + * @param _numPhysicalFloatRegs Number of physical fp registers. + * @param _numMiscRegs Number of miscellaneous registers. + * @param _zeroRegIdx Index of the zero register. + */ + Scoreboard(unsigned activeThreads, + unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + unsigned _numMiscRegs, + unsigned _zeroRegIdx); + + /** Destructor. */ + ~Scoreboard() {} + + /** Returns the name of the scoreboard. */ + std::string name() const; + + /** Checks if the register is ready. */ + bool getReg(PhysRegIndex ready_reg); + + /** Sets the register as ready. */ + void setReg(PhysRegIndex phys_reg); + + /** Sets the register as not ready. */ + void unsetReg(PhysRegIndex ready_reg); + + private: + /** Scoreboard of physical integer registers, saying whether or not they + * are ready. + */ + std::vector<bool> regScoreBoard; + + /** Number of logical integer registers. */ + int numLogicalIntRegs; + + /** Number of physical integer registers. */ + int numPhysicalIntRegs; + + /** Number of logical floating point registers. */ + int numLogicalFloatRegs; + + /** Number of physical floating point registers. */ + int numPhysicalFloatRegs; + + /** Number of miscellaneous registers. */ + int numMiscRegs; + + /** Number of logical integer + float registers. */ + int numLogicalRegs; + + /** Number of physical integer + float registers. */ + int numPhysicalRegs; + + /** The logical index of the zero register. */ + int zeroRegIdx; +}; + +#endif diff --git a/src/cpu/o3/store_set.cc b/src/cpu/o3/store_set.cc index 11023f4a8..2d28b617f 100644 --- a/src/cpu/o3/store_set.cc +++ b/src/cpu/o3/store_set.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,49 +24,94 @@ * 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. + * + * Authors: Kevin Lim */ +#include "base/intmath.hh" +#include "base/misc.hh" #include "base/trace.hh" #include "cpu/o3/store_set.hh" StoreSet::StoreSet(int _SSIT_size, int _LFST_size) - : SSIT_size(_SSIT_size), LFST_size(_LFST_size) + : SSITSize(_SSIT_size), LFSTSize(_LFST_size) { DPRINTF(StoreSet, "StoreSet: Creating store set object.\n"); DPRINTF(StoreSet, "StoreSet: SSIT size: %i, LFST size: %i.\n", - SSIT_size, LFST_size); + SSITSize, LFSTSize); + + if (!isPowerOf2(SSITSize)) { + fatal("Invalid SSIT size!\n"); + } - SSIT = new SSID[SSIT_size]; + SSIT.resize(SSITSize); - validSSIT.resize(SSIT_size); + validSSIT.resize(SSITSize); - for (int i = 0; i < SSIT_size; ++i) + for (int i = 0; i < SSITSize; ++i) validSSIT[i] = false; - LFST = new InstSeqNum[LFST_size]; + if (!isPowerOf2(LFSTSize)) { + fatal("Invalid LFST size!\n"); + } + + LFST.resize(LFSTSize); + + validLFST.resize(LFSTSize); + + for (int i = 0; i < LFSTSize; ++i) { + validLFST[i] = false; + LFST[i] = 0; + } + + indexMask = SSITSize - 1; + + offsetBits = 2; +} + +StoreSet::~StoreSet() +{ +} - validLFST.resize(LFST_size); +void +StoreSet::init(int _SSIT_size, int _LFST_size) +{ + SSITSize = _SSIT_size; + LFSTSize = _LFST_size; + + DPRINTF(StoreSet, "StoreSet: Creating store set object.\n"); + DPRINTF(StoreSet, "StoreSet: SSIT size: %i, LFST size: %i.\n", + SSITSize, LFSTSize); - SSCounters = new int[LFST_size]; + SSIT.resize(SSITSize); - for (int i = 0; i < LFST_size; ++i) - { + validSSIT.resize(SSITSize); + + for (int i = 0; i < SSITSize; ++i) + validSSIT[i] = false; + + LFST.resize(LFSTSize); + + validLFST.resize(LFSTSize); + + for (int i = 0; i < LFSTSize; ++i) { validLFST[i] = false; - SSCounters[i] = 0; + LFST[i] = 0; } - index_mask = SSIT_size - 1; + indexMask = SSITSize - 1; - offset_bits = 2; + offsetBits = 2; } + void StoreSet::violation(Addr store_PC, Addr load_PC) { int load_index = calcIndex(load_PC); int store_index = calcIndex(store_PC); - assert(load_index < SSIT_size && store_index < SSIT_size); + assert(load_index < SSITSize && store_index < SSITSize); bool valid_load_SSID = validSSIT[load_index]; bool valid_store_SSID = validSSIT[store_index]; @@ -83,10 +128,7 @@ StoreSet::violation(Addr store_PC, Addr load_PC) SSIT[store_index] = new_set; - assert(new_set < LFST_size); - - SSCounters[new_set]++; - + assert(new_set < LFSTSize); DPRINTF(StoreSet, "StoreSet: Neither load nor store had a valid " "storeset, creating a new one: %i for load %#x, store %#x\n", @@ -98,9 +140,7 @@ StoreSet::violation(Addr store_PC, Addr load_PC) SSIT[store_index] = load_SSID; - assert(load_SSID < LFST_size); - - SSCounters[load_SSID]++; + assert(load_SSID < LFSTSize); DPRINTF(StoreSet, "StoreSet: Load had a valid store set. Adding " "store to that set: %i for load %#x, store %#x\n", @@ -112,9 +152,6 @@ StoreSet::violation(Addr store_PC, Addr load_PC) SSIT[load_index] = store_SSID; - // Because we are having a load point to an already existing set, - // the size of the store set is not incremented. - DPRINTF(StoreSet, "StoreSet: Store had a valid store set: %i for " "load %#x, store %#x\n", store_SSID, load_PC, store_PC); @@ -122,29 +159,19 @@ StoreSet::violation(Addr store_PC, Addr load_PC) SSID load_SSID = SSIT[load_index]; SSID store_SSID = SSIT[store_index]; - assert(load_SSID < LFST_size && store_SSID < LFST_size); + assert(load_SSID < LFSTSize && store_SSID < LFSTSize); - int load_SS_size = SSCounters[load_SSID]; - int store_SS_size = SSCounters[store_SSID]; - - // If the load has the bigger store set, then assign the store - // to the same store set as the load. Otherwise vice-versa. - if (load_SS_size > store_SS_size) { + // The store set with the lower number wins + if (store_SSID > load_SSID) { SSIT[store_index] = load_SSID; - SSCounters[load_SSID]++; - SSCounters[store_SSID]--; - - DPRINTF(StoreSet, "StoreSet: Load had bigger store set: %i; " + DPRINTF(StoreSet, "StoreSet: Load had smaller store set: %i; " "for load %#x, store %#x\n", load_SSID, load_PC, store_PC); } else { SSIT[load_index] = store_SSID; - SSCounters[store_SSID]++; - SSCounters[load_SSID]--; - - DPRINTF(StoreSet, "StoreSet: Store had bigger store set: %i; " + DPRINTF(StoreSet, "StoreSet: Store had smaller store set: %i; " "for load %#x, store %#x\n", store_SSID, load_PC, store_PC); } @@ -159,13 +186,14 @@ StoreSet::insertLoad(Addr load_PC, InstSeqNum load_seq_num) } void -StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num) +StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num, + unsigned tid) { int index = calcIndex(store_PC); int store_SSID; - assert(index < SSIT_size); + assert(index < SSITSize); if (!validSSIT[index]) { // Do nothing if there's no valid entry. @@ -173,13 +201,15 @@ StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num) } else { store_SSID = SSIT[index]; - assert(store_SSID < LFST_size); + assert(store_SSID < LFSTSize); // Update the last store that was fetched with the current one. LFST[store_SSID] = store_seq_num; validLFST[store_SSID] = 1; + storeList[store_seq_num] = store_SSID; + DPRINTF(StoreSet, "Store %#x updated the LFST, SSID: %i\n", store_PC, store_SSID); } @@ -192,7 +222,7 @@ StoreSet::checkInst(Addr PC) int inst_SSID; - assert(index < SSIT_size); + assert(index < SSITSize); if (!validSSIT[index]) { DPRINTF(StoreSet, "Inst %#x with index %i had no SSID\n", @@ -203,7 +233,7 @@ StoreSet::checkInst(Addr PC) } else { inst_SSID = SSIT[index]; - assert(inst_SSID < LFST_size); + assert(inst_SSID < LFSTSize); if (!validLFST[inst_SSID]) { @@ -232,7 +262,13 @@ StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store) int store_SSID; - assert(index < SSIT_size); + assert(index < SSITSize); + + SeqNumMapIt store_list_it = storeList.find(issued_seq_num); + + if (store_list_it != storeList.end()) { + storeList.erase(store_list_it); + } // Make sure the SSIT still has a valid entry for the issued store. if (!validSSIT[index]) { @@ -241,7 +277,7 @@ StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store) store_SSID = SSIT[index]; - assert(store_SSID < LFST_size); + assert(store_SSID < LFSTSize); // If the last fetched store in the store set refers to the store that // was just issued, then invalidate the entry. @@ -252,18 +288,31 @@ StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store) } void -StoreSet::squash(InstSeqNum squashed_num) +StoreSet::squash(InstSeqNum squashed_num, unsigned tid) { - // Not really sure how to do this well. - // Generally this is small enough that it should be okay; short circuit - // evaluation should take care of invalid entries. - DPRINTF(StoreSet, "StoreSet: Squashing until inum %i\n", squashed_num); - for (int i = 0; i < LFST_size; ++i) { - if (validLFST[i] && LFST[i] < squashed_num) { - validLFST[i] = false; + int idx; + SeqNumMapIt store_list_it = storeList.begin(); + + //@todo:Fix to only delete from correct thread + while (!storeList.empty()) { + idx = (*store_list_it).second; + + if ((*store_list_it).first <= squashed_num) { + break; + } + + bool younger = LFST[idx] > squashed_num; + + if (validLFST[idx] && younger) { + DPRINTF(StoreSet, "Squashed [sn:%lli]\n", LFST[idx]); + validLFST[idx] = false; + + storeList.erase(store_list_it++); + } else if (!validLFST[idx] && younger) { + storeList.erase(store_list_it++); } } } @@ -271,12 +320,29 @@ StoreSet::squash(InstSeqNum squashed_num) void StoreSet::clear() { - for (int i = 0; i < SSIT_size; ++i) { + for (int i = 0; i < SSITSize; ++i) { validSSIT[i] = false; } - for (int i = 0; i < LFST_size; ++i) { + for (int i = 0; i < LFSTSize; ++i) { validLFST[i] = false; } + + storeList.clear(); } +void +StoreSet::dump() +{ + cprintf("storeList.size(): %i\n", storeList.size()); + SeqNumMapIt store_list_it = storeList.begin(); + + int num = 0; + + while (store_list_it != storeList.end()) { + cprintf("%i: [sn:%lli] SSID:%i\n", + num, (*store_list_it).first, (*store_list_it).second); + num++; + store_list_it++; + } +} diff --git a/src/cpu/o3/store_set.hh b/src/cpu/o3/store_set.hh index 5a885d838..f5a44a1ac 100644 --- a/src/cpu/o3/store_set.hh +++ b/src/cpu/o3/store_set.hh @@ -24,63 +24,124 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_STORE_SET_HH__ -#define __CPU_O3_CPU_STORE_SET_HH__ +#ifndef __CPU_O3_STORE_SET_HH__ +#define __CPU_O3_STORE_SET_HH__ +#include <list> +#include <map> +#include <utility> #include <vector> #include "arch/isa_traits.hh" #include "cpu/inst_seq.hh" +struct ltseqnum { + bool operator()(const InstSeqNum &lhs, const InstSeqNum &rhs) const + { + return lhs > rhs; + } +}; + +/** + * Implements a store set predictor for determining if memory + * instructions are dependent upon each other. See paper "Memory + * Dependence Prediction using Store Sets" by Chrysos and Emer. SSID + * stands for Store Set ID, SSIT stands for Store Set ID Table, and + * LFST is Last Fetched Store Table. + */ class StoreSet { public: typedef unsigned SSID; public: + /** Default constructor. init() must be called prior to use. */ + StoreSet() { }; + + /** Creates store set predictor with given table sizes. */ StoreSet(int SSIT_size, int LFST_size); + /** Default destructor. */ + ~StoreSet(); + + /** Initializes the store set predictor with the given table sizes. */ + void init(int SSIT_size, int LFST_size); + + /** Records a memory ordering violation between the younger load + * and the older store. */ void violation(Addr store_PC, Addr load_PC); + /** Inserts a load into the store set predictor. This does nothing but + * is included in case other predictors require a similar function. + */ void insertLoad(Addr load_PC, InstSeqNum load_seq_num); - void insertStore(Addr store_PC, InstSeqNum store_seq_num); + /** Inserts a store into the store set predictor. Updates the + * LFST if the store has a valid SSID. */ + void insertStore(Addr store_PC, InstSeqNum store_seq_num, + unsigned tid); + /** Checks if the instruction with the given PC is dependent upon + * any store. @return Returns the sequence number of the store + * instruction this PC is dependent upon. Returns 0 if none. + */ InstSeqNum checkInst(Addr PC); + /** Records this PC/sequence number as issued. */ void issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store); - void squash(InstSeqNum squashed_num); + /** Squashes for a specific thread until the given sequence number. */ + void squash(InstSeqNum squashed_num, unsigned tid); + /** Resets all tables. */ void clear(); + /** Debug function to dump the contents of the store list. */ + void dump(); + private: + /** Calculates the index into the SSIT based on the PC. */ inline int calcIndex(Addr PC) - { return (PC >> offset_bits) & index_mask; } + { return (PC >> offsetBits) & indexMask; } + /** Calculates a Store Set ID based on the PC. */ inline SSID calcSSID(Addr PC) - { return ((PC ^ (PC >> 10)) % LFST_size); } + { return ((PC ^ (PC >> 10)) % LFSTSize); } - SSID *SSIT; + /** The Store Set ID Table. */ + std::vector<SSID> SSIT; + /** Bit vector to tell if the SSIT has a valid entry. */ std::vector<bool> validSSIT; - InstSeqNum *LFST; + /** Last Fetched Store Table. */ + std::vector<InstSeqNum> LFST; + /** Bit vector to tell if the LFST has a valid entry. */ std::vector<bool> validLFST; - int *SSCounters; + /** Map of stores that have been inserted into the store set, but + * not yet issued or squashed. + */ + std::map<InstSeqNum, int, ltseqnum> storeList; + + typedef std::map<InstSeqNum, int, ltseqnum>::iterator SeqNumMapIt; - int SSIT_size; + /** Store Set ID Table size, in entries. */ + int SSITSize; - int LFST_size; + /** Last Fetched Store Table size, in entries. */ + int LFSTSize; - int index_mask; + /** Mask to obtain the index. */ + int indexMask; // HACK: Hardcoded for now. - int offset_bits; + int offsetBits; }; -#endif // __CPU_O3_CPU_STORE_SET_HH__ +#endif // __CPU_O3_STORE_SET_HH__ diff --git a/src/cpu/o3/thread_context.hh b/src/cpu/o3/thread_context.hh new file mode 100755 index 000000000..d097ee63e --- /dev/null +++ b/src/cpu/o3/thread_context.hh @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_THREAD_CONTEXT_HH__ +#define __CPU_O3_THREAD_CONTEXT_HH__ + +#include "cpu/o3/isa_specific.hh" + +class EndQuiesceEvent; +namespace Kernel { + class Statistics; +}; + +class TranslatingPort; + +/** + * Derived ThreadContext class for use with the O3CPU. It + * provides the interface for any external objects to access a + * single thread's state and some general CPU state. Any time + * external objects try to update state through this interface, + * the CPU will create an event to squash all in-flight + * instructions in order to ensure state is maintained correctly. + * It must be defined specifically for the O3CPU because + * not all architectural state is located within the O3ThreadState + * (such as the commit PC, and registers), and specific actions + * must be taken when using this interface (such as squashing all + * in-flight instructions when doing a write to this interface). + */ +template <class Impl> +class O3ThreadContext : public ThreadContext +{ + public: + typedef typename Impl::O3CPU O3CPU; + + /** Pointer to the CPU. */ + O3CPU *cpu; + + /** Pointer to the thread state that this TC corrseponds to. */ + O3ThreadState<Impl> *thread; + + /** Returns a pointer to this CPU. */ + virtual BaseCPU *getCpuPtr() { return cpu; } + + /** Sets this CPU's ID. */ + virtual void setCpuId(int id) { cpu->setCpuId(id); } + + /** Reads this CPU's ID. */ + virtual int readCpuId() { return cpu->readCpuId(); } + +#if FULL_SYSTEM + /** Returns a pointer to the system. */ + virtual System *getSystemPtr() { return cpu->system; } + + /** Returns a pointer to physical memory. */ + virtual PhysicalMemory *getPhysMemPtr() { return cpu->physmem; } + + /** Returns a pointer to this thread's kernel statistics. */ + virtual Kernel::Statistics *getKernelStats() + { return thread->kernelStats; } + + virtual FunctionalPort *getPhysPort() { return thread->getPhysPort(); } + + virtual VirtualPort *getVirtPort(ThreadContext *src_tc = NULL); + + void delVirtPort(VirtualPort *vp); +#else + virtual TranslatingPort *getMemPort() { return thread->getMemPort(); } + + /** Returns a pointer to this thread's process. */ + virtual Process *getProcessPtr() { return thread->getProcessPtr(); } +#endif + /** Returns this thread's status. */ + virtual Status status() const { return thread->status(); } + + /** Sets this thread's status. */ + virtual void setStatus(Status new_status) + { thread->setStatus(new_status); } + + /** Set the status to Active. Optional delay indicates number of + * cycles to wait before beginning execution. */ + virtual void activate(int delay = 1); + + /** Set the status to Suspended. */ + virtual void suspend(); + + /** Set the status to Unallocated. */ + virtual void deallocate(); + + /** Set the status to Halted. */ + virtual void halt(); + +#if FULL_SYSTEM + /** Dumps the function profiling information. + * @todo: Implement. + */ + virtual void dumpFuncProfile(); +#endif + /** Takes over execution of a thread from another CPU. */ + virtual void takeOverFrom(ThreadContext *old_context); + + /** Registers statistics associated with this TC. */ + virtual void regStats(const std::string &name); + + /** Serializes state. */ + virtual void serialize(std::ostream &os); + /** Unserializes state. */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +#if FULL_SYSTEM + /** Reads the last tick that this thread was activated on. */ + virtual Tick readLastActivate(); + /** Reads the last tick that this thread was suspended on. */ + virtual Tick readLastSuspend(); + + /** Clears the function profiling information. */ + virtual void profileClear(); + /** Samples the function profiling information. */ + virtual void profileSample(); +#endif + /** Returns this thread's ID number. */ + virtual int getThreadNum() { return thread->readTid(); } + + /** Returns the instruction this thread is currently committing. + * Only used when an instruction faults. + */ + virtual TheISA::MachInst getInst(); + + /** Copies the architectural registers from another TC into this TC. */ + virtual void copyArchRegs(ThreadContext *tc); + + /** Resets all architectural registers to 0. */ + virtual void clearArchRegs(); + + /** Reads an integer register. */ + virtual uint64_t readIntReg(int reg_idx); + + virtual FloatReg readFloatReg(int reg_idx, int width); + + virtual FloatReg readFloatReg(int reg_idx); + + virtual FloatRegBits readFloatRegBits(int reg_idx, int width); + + virtual FloatRegBits readFloatRegBits(int reg_idx); + + /** Sets an integer register to a value. */ + virtual void setIntReg(int reg_idx, uint64_t val); + + virtual void setFloatReg(int reg_idx, FloatReg val, int width); + + virtual void setFloatReg(int reg_idx, FloatReg val); + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val, int width); + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val); + + /** Reads this thread's PC. */ + virtual uint64_t readPC() + { return cpu->readPC(thread->readTid()); } + + /** Sets this thread's PC. */ + virtual void setPC(uint64_t val); + + /** Reads this thread's next PC. */ + virtual uint64_t readNextPC() + { return cpu->readNextPC(thread->readTid()); } + + /** Sets this thread's next PC. */ + virtual void setNextPC(uint64_t val); + + /** Reads a miscellaneous register. */ + virtual MiscReg readMiscReg(int misc_reg) + { return cpu->readMiscReg(misc_reg, thread->readTid()); } + + /** Reads a misc. register, including any side-effects the + * read might have as defined by the architecture. */ + virtual MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return cpu->readMiscRegWithEffect(misc_reg, fault, thread->readTid()); } + + /** Sets a misc. register. */ + virtual Fault setMiscReg(int misc_reg, const MiscReg &val); + + /** Sets a misc. register, including any side-effects the + * write might have as defined by the architecture. */ + virtual Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + + /** Returns the number of consecutive store conditional failures. */ + // @todo: Figure out where these store cond failures should go. + virtual unsigned readStCondFailures() + { return thread->storeCondFailures; } + + /** Sets the number of consecutive store conditional failures. */ + virtual void setStCondFailures(unsigned sc_failures) + { thread->storeCondFailures = sc_failures; } + + // Only really makes sense for old CPU model. Lots of code + // outside the CPU still checks this function, so it will + // always return false to keep everything working. + /** Checks if the thread is misspeculating. Because it is + * very difficult to determine if the thread is + * misspeculating, this is set as false. */ + virtual bool misspeculating() { return false; } + +#if !FULL_SYSTEM + /** Gets a syscall argument by index. */ + virtual IntReg getSyscallArg(int i); + + /** Sets a syscall argument. */ + virtual void setSyscallArg(int i, IntReg val); + + /** Sets the syscall return value. */ + virtual void setSyscallReturn(SyscallReturn return_value); + + /** Executes a syscall in SE mode. */ + virtual void syscall(int64_t callnum) + { return cpu->syscall(callnum, thread->readTid()); } + + /** Reads the funcExeInst counter. */ + virtual Counter readFuncExeInst() { return thread->funcExeInst; } +#endif +}; + +#endif diff --git a/src/cpu/o3/thread_context_impl.hh b/src/cpu/o3/thread_context_impl.hh new file mode 100755 index 000000000..cfb71f623 --- /dev/null +++ b/src/cpu/o3/thread_context_impl.hh @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/thread_context.hh" +#include "cpu/quiesce_event.hh" + +using namespace TheISA; + +#if FULL_SYSTEM +template <class Impl> +VirtualPort * +O3ThreadContext<Impl>::getVirtPort(ThreadContext *src_tc) +{ + if (!src_tc) + return thread->getVirtPort(); + + VirtualPort *vp; + Port *mem_port; + + vp = new VirtualPort("tc-vport", src_tc); + mem_port = cpu->system->physmem->getPort("functional"); + mem_port->setPeer(vp); + vp->setPeer(mem_port); + return vp; +} + +template <class Impl> +void +O3ThreadContext<Impl>::dumpFuncProfile() +{ + // Currently not supported +} +#endif + +template <class Impl> +void +O3ThreadContext<Impl>::takeOverFrom(ThreadContext *old_context) +{ + // some things should already be set up +#if FULL_SYSTEM + assert(getSystemPtr() == old_context->getSystemPtr()); +#else + assert(getProcessPtr() == old_context->getProcessPtr()); +#endif + + // copy over functional state + setStatus(old_context->status()); + copyArchRegs(old_context); + setCpuId(old_context->readCpuId()); + +#if !FULL_SYSTEM + thread->funcExeInst = old_context->readFuncExeInst(); +#else + EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent(); + if (other_quiesce) { + // Point the quiesce event's TC at this TC so that it wakes up + // the proper CPU. + other_quiesce->tc = this; + } + if (thread->quiesceEvent) { + thread->quiesceEvent->tc = this; + } + + // Transfer kernel stats from one CPU to the other. + thread->kernelStats = old_context->getKernelStats(); +// storeCondFailures = 0; + cpu->lockFlag = false; +#endif + + old_context->setStatus(ThreadContext::Unallocated); + + thread->inSyscall = false; + thread->trapPending = false; +} + +#if FULL_SYSTEM +template <class Impl> +void +O3ThreadContext<Impl>::delVirtPort(VirtualPort *vp) +{ + delete vp->getPeer(); + delete vp; +} +#endif + +template <class Impl> +void +O3ThreadContext<Impl>::activate(int delay) +{ + DPRINTF(O3CPU, "Calling activate on AlphaTC\n"); + + if (thread->status() == ThreadContext::Active) + return; + +#if FULL_SYSTEM + thread->lastActivate = curTick; +#endif + + if (thread->status() == ThreadContext::Unallocated) { + cpu->activateWhenReady(thread->readTid()); + return; + } + + thread->setStatus(ThreadContext::Active); + + // status() == Suspended + cpu->activateContext(thread->readTid(), delay); +} + +template <class Impl> +void +O3ThreadContext<Impl>::suspend() +{ + DPRINTF(O3CPU, "Calling suspend on AlphaTC\n"); + + if (thread->status() == ThreadContext::Suspended) + return; + +#if FULL_SYSTEM + thread->lastActivate = curTick; + thread->lastSuspend = curTick; +#endif +/* +#if FULL_SYSTEM + // Don't change the status from active if there are pending interrupts + if (cpu->check_interrupts()) { + assert(status() == ThreadContext::Active); + return; + } +#endif +*/ + thread->setStatus(ThreadContext::Suspended); + cpu->suspendContext(thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::deallocate() +{ + DPRINTF(O3CPU, "Calling deallocate on AlphaTC\n"); + + if (thread->status() == ThreadContext::Unallocated) + return; + + thread->setStatus(ThreadContext::Unallocated); + cpu->deallocateContext(thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::halt() +{ + DPRINTF(O3CPU, "Calling halt on AlphaTC\n"); + + if (thread->status() == ThreadContext::Halted) + return; + + thread->setStatus(ThreadContext::Halted); + cpu->haltContext(thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::regStats(const std::string &name) +{ +#if FULL_SYSTEM + thread->kernelStats = new Kernel::Statistics(cpu->system); + thread->kernelStats->regStats(name + ".kern"); +#endif +} + +template <class Impl> +void +O3ThreadContext<Impl>::serialize(std::ostream &os) +{ +#if FULL_SYSTEM + if (thread->kernelStats) + thread->kernelStats->serialize(os); +#endif + +} + +template <class Impl> +void +O3ThreadContext<Impl>::unserialize(Checkpoint *cp, const std::string §ion) +{ +#if FULL_SYSTEM + if (thread->kernelStats) + thread->kernelStats->unserialize(cp, section); +#endif + +} + +#if FULL_SYSTEM +template <class Impl> +Tick +O3ThreadContext<Impl>::readLastActivate() +{ + return thread->lastActivate; +} + +template <class Impl> +Tick +O3ThreadContext<Impl>::readLastSuspend() +{ + return thread->lastSuspend; +} + +template <class Impl> +void +O3ThreadContext<Impl>::profileClear() +{} + +template <class Impl> +void +O3ThreadContext<Impl>::profileSample() +{} +#endif + +template <class Impl> +TheISA::MachInst +O3ThreadContext<Impl>:: getInst() +{ + return thread->getInst(); +} + +template <class Impl> +void +O3ThreadContext<Impl>::copyArchRegs(ThreadContext *tc) +{ + // This function will mess things up unless the ROB is empty and + // there are no instructions in the pipeline. + unsigned tid = thread->readTid(); + PhysRegIndex renamed_reg; + + // First loop through the integer registers. + for (int i = 0; i < TheISA::NumIntRegs; ++i) { + renamed_reg = cpu->renameMap[tid].lookup(i); + + DPRINTF(O3CPU, "Copying over register %i, had data %lli, " + "now has data %lli.\n", + renamed_reg, cpu->readIntReg(renamed_reg), + tc->readIntReg(i)); + + cpu->setIntReg(renamed_reg, tc->readIntReg(i)); + } + + // Then loop through the floating point registers. + for (int i = 0; i < TheISA::NumFloatRegs; ++i) { + renamed_reg = cpu->renameMap[tid].lookup(i + TheISA::FP_Base_DepTag); + cpu->setFloatRegBits(renamed_reg, + tc->readFloatRegBits(i)); + } + + // Copy the misc regs. + copyMiscRegs(tc, this); + + // Then finally set the PC and the next PC. + cpu->setPC(tc->readPC(), tid); + cpu->setNextPC(tc->readNextPC(), tid); +#if !FULL_SYSTEM + this->thread->funcExeInst = tc->readFuncExeInst(); +#endif +} + +template <class Impl> +void +O3ThreadContext<Impl>::clearArchRegs() +{} + +template <class Impl> +uint64_t +O3ThreadContext<Impl>::readIntReg(int reg_idx) +{ + return cpu->readArchIntReg(reg_idx, thread->readTid()); +} + +template <class Impl> +FloatReg +O3ThreadContext<Impl>::readFloatReg(int reg_idx, int width) +{ + switch(width) { + case 32: + return cpu->readArchFloatRegSingle(reg_idx, thread->readTid()); + case 64: + return cpu->readArchFloatRegDouble(reg_idx, thread->readTid()); + default: + panic("Unsupported width!"); + return 0; + } +} + +template <class Impl> +FloatReg +O3ThreadContext<Impl>::readFloatReg(int reg_idx) +{ + return cpu->readArchFloatRegSingle(reg_idx, thread->readTid()); +} + +template <class Impl> +FloatRegBits +O3ThreadContext<Impl>::readFloatRegBits(int reg_idx, int width) +{ + DPRINTF(Fault, "Reading floatint register through the TC!\n"); + return cpu->readArchFloatRegInt(reg_idx, thread->readTid()); +} + +template <class Impl> +FloatRegBits +O3ThreadContext<Impl>::readFloatRegBits(int reg_idx) +{ + return cpu->readArchFloatRegInt(reg_idx, thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::setIntReg(int reg_idx, uint64_t val) +{ + cpu->setArchIntReg(reg_idx, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setFloatReg(int reg_idx, FloatReg val, int width) +{ + switch(width) { + case 32: + cpu->setArchFloatRegSingle(reg_idx, val, thread->readTid()); + break; + case 64: + cpu->setArchFloatRegDouble(reg_idx, val, thread->readTid()); + break; + } + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setFloatReg(int reg_idx, FloatReg val) +{ + cpu->setArchFloatRegSingle(reg_idx, val, thread->readTid()); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val, + int width) +{ + DPRINTF(Fault, "Setting floatint register through the TC!\n"); + cpu->setArchFloatRegInt(reg_idx, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val) +{ + cpu->setArchFloatRegInt(reg_idx, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setPC(uint64_t val) +{ + cpu->setPC(val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setNextPC(uint64_t val) +{ + cpu->setNextPC(val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +Fault +O3ThreadContext<Impl>::setMiscReg(int misc_reg, const MiscReg &val) +{ + Fault ret_fault = cpu->setMiscReg(misc_reg, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } + + return ret_fault; +} + +template <class Impl> +Fault +O3ThreadContext<Impl>::setMiscRegWithEffect(int misc_reg, + const MiscReg &val) +{ + Fault ret_fault = cpu->setMiscRegWithEffect(misc_reg, val, + thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } + + return ret_fault; +} + +#if !FULL_SYSTEM + +template <class Impl> +TheISA::IntReg +O3ThreadContext<Impl>::getSyscallArg(int i) +{ + return cpu->getSyscallArg(i, thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::setSyscallArg(int i, IntReg val) +{ + cpu->setSyscallArg(i, val, thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::setSyscallReturn(SyscallReturn return_value) +{ + cpu->setSyscallReturn(return_value, thread->readTid()); +} + +#endif // FULL_SYSTEM + diff --git a/src/cpu/o3/thread_state.hh b/src/cpu/o3/thread_state.hh new file mode 100644 index 000000000..1c8105204 --- /dev/null +++ b/src/cpu/o3/thread_state.hh @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_THREAD_STATE_HH__ +#define __CPU_O3_THREAD_STATE_HH__ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "cpu/thread_context.hh" +#include "cpu/thread_state.hh" + +class Event; +class Process; + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +#else +class FunctionalMemory; +class Process; +#endif + +/** + * Class that has various thread state, such as the status, the + * current instruction being processed, whether or not the thread has + * a trap pending or is being externally updated, the ThreadContext + * pointer, etc. It also handles anything related to a specific + * thread's process, such as syscalls and checking valid addresses. + */ +template <class Impl> +struct O3ThreadState : public ThreadState { + typedef ThreadContext::Status Status; + typedef typename Impl::O3CPU O3CPU; + + private: + /** Pointer to the CPU. */ + O3CPU *cpu; + public: + /** Whether or not the thread is currently in syscall mode, and + * thus able to be externally updated without squashing. + */ + bool inSyscall; + + /** Whether or not the thread is currently waiting on a trap, and + * thus able to be externally updated without squashing. + */ + bool trapPending; + +#if FULL_SYSTEM + O3ThreadState(O3CPU *_cpu, int _thread_num) + : ThreadState(-1, _thread_num), + inSyscall(0), trapPending(0) + { } +#else + O3ThreadState(O3CPU *_cpu, int _thread_num, Process *_process, int _asid, + MemObject *mem) + : ThreadState(-1, _thread_num, _process, _asid, mem), + cpu(_cpu), inSyscall(0), trapPending(0) + { } +#endif + + /** Pointer to the ThreadContext of this thread. */ + ThreadContext *tc; + + /** Returns a pointer to the TC of this thread. */ + ThreadContext *getTC() { return tc; } + +#if !FULL_SYSTEM + /** Handles the syscall. */ + void syscall(int64_t callnum) { process->syscall(callnum, tc); } +#endif +}; + +#endif // __CPU_O3_THREAD_STATE_HH__ diff --git a/src/cpu/o3/tournament_pred.cc b/src/cpu/o3/tournament_pred.cc index 3fb580510..7cf78dcb1 100644 --- a/src/cpu/o3/tournament_pred.cc +++ b/src/cpu/o3/tournament_pred.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,41 +24,50 @@ * 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. + * + * Authors: Kevin Lim */ +#include "base/intmath.hh" #include "cpu/o3/tournament_pred.hh" -TournamentBP::TournamentBP(unsigned _local_predictor_size, - unsigned _local_ctr_bits, - unsigned _local_history_table_size, - unsigned _local_history_bits, - unsigned _global_predictor_size, - unsigned _global_ctr_bits, - unsigned _global_history_bits, - unsigned _choice_predictor_size, - unsigned _choice_ctr_bits, +TournamentBP::TournamentBP(unsigned _localPredictorSize, + unsigned _localCtrBits, + unsigned _localHistoryTableSize, + unsigned _localHistoryBits, + unsigned _globalPredictorSize, + unsigned _globalCtrBits, + unsigned _globalHistoryBits, + unsigned _choicePredictorSize, + unsigned _choiceCtrBits, unsigned _instShiftAmt) - : localPredictorSize(_local_predictor_size), - localCtrBits(_local_ctr_bits), - localHistoryTableSize(_local_history_table_size), - localHistoryBits(_local_history_bits), - globalPredictorSize(_global_predictor_size), - globalCtrBits(_global_ctr_bits), - globalHistoryBits(_global_history_bits), - choicePredictorSize(_global_predictor_size), - choiceCtrBits(_choice_ctr_bits), + : localPredictorSize(_localPredictorSize), + localCtrBits(_localCtrBits), + localHistoryTableSize(_localHistoryTableSize), + localHistoryBits(_localHistoryBits), + globalPredictorSize(_globalPredictorSize), + globalCtrBits(_globalCtrBits), + globalHistoryBits(_globalHistoryBits), + choicePredictorSize(_globalPredictorSize), + choiceCtrBits(_choiceCtrBits), instShiftAmt(_instShiftAmt) { - //Should do checks here to make sure sizes are correct (powers of 2) + if (!isPowerOf2(localPredictorSize)) { + fatal("Invalid local predictor size!\n"); + } //Setup the array of counters for the local predictor - localCtrs = new SatCounter[localPredictorSize]; + localCtrs.resize(localPredictorSize); for (int i = 0; i < localPredictorSize; ++i) localCtrs[i].setBits(localCtrBits); + if (!isPowerOf2(localHistoryTableSize)) { + fatal("Invalid local history table size!\n"); + } + //Setup the history table for the local table - localHistoryTable = new unsigned[localHistoryTableSize]; + localHistoryTable.resize(localHistoryTableSize); for (int i = 0; i < localHistoryTableSize; ++i) localHistoryTable[i] = 0; @@ -66,8 +75,12 @@ TournamentBP::TournamentBP(unsigned _local_predictor_size, // Setup the local history mask localHistoryMask = (1 << localHistoryBits) - 1; + if (!isPowerOf2(globalPredictorSize)) { + fatal("Invalid global predictor size!\n"); + } + //Setup the array of counters for the global predictor - globalCtrs = new SatCounter[globalPredictorSize]; + globalCtrs.resize(globalPredictorSize); for (int i = 0; i < globalPredictorSize; ++i) globalCtrs[i].setBits(globalCtrBits); @@ -77,12 +90,17 @@ TournamentBP::TournamentBP(unsigned _local_predictor_size, // Setup the global history mask globalHistoryMask = (1 << globalHistoryBits) - 1; + if (!isPowerOf2(choicePredictorSize)) { + fatal("Invalid choice predictor size!\n"); + } + //Setup the array of counters for the choice predictor - choiceCtrs = new SatCounter[choicePredictorSize]; + choiceCtrs.resize(choicePredictorSize); for (int i = 0; i < choicePredictorSize; ++i) choiceCtrs[i].setBits(choiceCtrBits); + // @todo: Allow for different thresholds between the predictors. threshold = (1 << (localCtrBits - 1)) - 1; threshold = threshold / 2; } @@ -91,166 +109,185 @@ inline unsigned TournamentBP::calcLocHistIdx(Addr &branch_addr) { + // Get low order bits after removing instruction offset. return (branch_addr >> instShiftAmt) & (localHistoryTableSize - 1); } inline void -TournamentBP::updateHistoriesTaken(unsigned local_history_idx) +TournamentBP::updateGlobalHistTaken() { globalHistory = (globalHistory << 1) | 1; globalHistory = globalHistory & globalHistoryMask; - - localHistoryTable[local_history_idx] = - (localHistoryTable[local_history_idx] << 1) | 1; } inline void -TournamentBP::updateHistoriesNotTaken(unsigned local_history_idx) +TournamentBP::updateGlobalHistNotTaken() { globalHistory = (globalHistory << 1); globalHistory = globalHistory & globalHistoryMask; +} +inline +void +TournamentBP::updateLocalHistTaken(unsigned local_history_idx) +{ + localHistoryTable[local_history_idx] = + (localHistoryTable[local_history_idx] << 1) | 1; +} + +inline +void +TournamentBP::updateLocalHistNotTaken(unsigned local_history_idx) +{ localHistoryTable[local_history_idx] = (localHistoryTable[local_history_idx] << 1); } bool -TournamentBP::lookup(Addr &branch_addr) +TournamentBP::lookup(Addr &branch_addr, void * &bp_history) { - uint8_t local_prediction; + bool local_prediction; unsigned local_history_idx; unsigned local_predictor_idx; - uint8_t global_prediction; - uint8_t choice_prediction; + bool global_prediction; + bool choice_prediction; //Lookup in the local predictor to get its branch prediction local_history_idx = calcLocHistIdx(branch_addr); local_predictor_idx = localHistoryTable[local_history_idx] & localHistoryMask; - local_prediction = localCtrs[local_predictor_idx].read(); + local_prediction = localCtrs[local_predictor_idx].read() > threshold; //Lookup in the global predictor to get its branch prediction - global_prediction = globalCtrs[globalHistory].read(); + global_prediction = globalCtrs[globalHistory].read() > threshold; //Lookup in the choice predictor to see which one to use - choice_prediction = choiceCtrs[globalHistory].read(); - - //@todo Put a threshold value in for the three predictors that can - // be set through the constructor (so this isn't hard coded). - //Also should put some of this code into functions. - if (choice_prediction > threshold) { - if (global_prediction > threshold) { - updateHistoriesTaken(local_history_idx); - - assert(globalHistory < globalPredictorSize && - local_history_idx < localPredictorSize); - - globalCtrs[globalHistory].increment(); - localCtrs[local_history_idx].increment(); - + choice_prediction = choiceCtrs[globalHistory].read() > threshold; + + // Create BPHistory and pass it back to be recorded. + BPHistory *history = new BPHistory; + history->globalHistory = globalHistory; + history->localPredTaken = local_prediction; + history->globalPredTaken = global_prediction; + history->globalUsed = choice_prediction; + bp_history = (void *)history; + + assert(globalHistory < globalPredictorSize && + local_history_idx < localPredictorSize); + + // Commented code is for doing speculative update of counters and + // all histories. + if (choice_prediction) { + if (global_prediction) { +// updateHistoriesTaken(local_history_idx); +// globalCtrs[globalHistory].increment(); +// localCtrs[local_history_idx].increment(); + updateGlobalHistTaken(); return true; } else { - updateHistoriesNotTaken(local_history_idx); - - assert(globalHistory < globalPredictorSize && - local_history_idx < localPredictorSize); - - globalCtrs[globalHistory].decrement(); - localCtrs[local_history_idx].decrement(); - +// updateHistoriesNotTaken(local_history_idx); +// globalCtrs[globalHistory].decrement(); +// localCtrs[local_history_idx].decrement(); + updateGlobalHistNotTaken(); return false; } } else { - if (local_prediction > threshold) { - updateHistoriesTaken(local_history_idx); - - assert(globalHistory < globalPredictorSize && - local_history_idx < localPredictorSize); - - globalCtrs[globalHistory].increment(); - localCtrs[local_history_idx].increment(); - + if (local_prediction) { +// updateHistoriesTaken(local_history_idx); +// globalCtrs[globalHistory].increment(); +// localCtrs[local_history_idx].increment(); + updateGlobalHistTaken(); return true; } else { - updateHistoriesNotTaken(local_history_idx); - - assert(globalHistory < globalPredictorSize && - local_history_idx < localPredictorSize); - - globalCtrs[globalHistory].decrement(); - localCtrs[local_history_idx].decrement(); - +// updateHistoriesNotTaken(local_history_idx); +// globalCtrs[globalHistory].decrement(); +// localCtrs[local_history_idx].decrement(); + updateGlobalHistNotTaken(); return false; } } } -// Update the branch predictor if it predicted a branch wrong. void -TournamentBP::update(Addr &branch_addr, unsigned correct_gh, bool taken) +TournamentBP::uncondBr(void * &bp_history) { + // Create BPHistory and pass it back to be recorded. + BPHistory *history = new BPHistory; + history->globalHistory = globalHistory; + history->localPredTaken = true; + history->globalPredTaken = true; + bp_history = static_cast<void *>(history); + + updateGlobalHistTaken(); +} - uint8_t local_prediction; +void +TournamentBP::update(Addr &branch_addr, bool taken, void *bp_history) +{ unsigned local_history_idx; unsigned local_predictor_idx; - bool local_pred_taken; + unsigned local_predictor_hist; - uint8_t global_prediction; - bool global_pred_taken; - - // Load the correct global history into the register. - globalHistory = correct_gh; - - // Get the local predictor's current prediction, remove the incorrect - // update, and update the local predictor + // Get the local predictor's current prediction local_history_idx = calcLocHistIdx(branch_addr); - local_predictor_idx = localHistoryTable[local_history_idx]; - local_predictor_idx = (local_predictor_idx >> 1) & localHistoryMask; - - local_prediction = localCtrs[local_predictor_idx].read(); - local_pred_taken = local_prediction > threshold; - - //Get the global predictor's current prediction, and update the - //global predictor - global_prediction = globalCtrs[globalHistory].read(); - global_pred_taken = global_prediction > threshold; - - //Update the choice predictor to tell it which one was correct - if (local_pred_taken != global_pred_taken) { - //If the local prediction matches the actual outcome, decerement - //the counter. Otherwise increment the counter. - if (local_pred_taken == taken) { - choiceCtrs[globalHistory].decrement(); - } else { - choiceCtrs[globalHistory].increment(); + local_predictor_hist = localHistoryTable[local_history_idx]; + local_predictor_idx = local_predictor_hist & localHistoryMask; + + // Update the choice predictor to tell it which one was correct if + // there was a prediction. + if (bp_history) { + BPHistory *history = static_cast<BPHistory *>(bp_history); + if (history->localPredTaken != history->globalPredTaken) { + // If the local prediction matches the actual outcome, + // decerement the counter. Otherwise increment the + // counter. + if (history->localPredTaken == taken) { + choiceCtrs[globalHistory].decrement(); + } else if (history->globalPredTaken == taken){ + choiceCtrs[globalHistory].increment(); + } } + + // We're done with this history, now delete it. + delete history; } - if (taken) { - assert(globalHistory < globalPredictorSize && - local_predictor_idx < localPredictorSize); + assert(globalHistory < globalPredictorSize && + local_predictor_idx < localPredictorSize); + // Update the counters and local history with the proper + // resolution of the branch. Global history is updated + // speculatively and restored upon squash() calls, so it does not + // need to be updated. + if (taken) { localCtrs[local_predictor_idx].increment(); globalCtrs[globalHistory].increment(); - globalHistory = (globalHistory << 1) | 1; - globalHistory = globalHistory & globalHistoryMask; + updateLocalHistTaken(local_history_idx); + } else { + localCtrs[local_predictor_idx].decrement(); + globalCtrs[globalHistory].decrement(); - localHistoryTable[local_history_idx] |= 1; + updateLocalHistNotTaken(local_history_idx); } - else { - assert(globalHistory < globalPredictorSize && - local_predictor_idx < localPredictorSize); +} - localCtrs[local_predictor_idx].decrement(); - globalCtrs[globalHistory].decrement(); +void +TournamentBP::squash(void *bp_history) +{ + BPHistory *history = static_cast<BPHistory *>(bp_history); - globalHistory = (globalHistory << 1); - globalHistory = globalHistory & globalHistoryMask; + // Restore global history to state prior to this branch. + globalHistory = history->globalHistory; - localHistoryTable[local_history_idx] &= ~1; - } + // Delete this BPHistory now that we're done with it. + delete history; } + +#ifdef DEBUG +int +TournamentBP::BPHistory::newCount = 0; +#endif diff --git a/src/cpu/o3/tournament_pred.hh b/src/cpu/o3/tournament_pred.hh index cb93c2f67..92402adc6 100644 --- a/src/cpu/o3/tournament_pred.hh +++ b/src/cpu/o3/tournament_pred.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 The Regents of The University of Michigan + * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,61 +24,138 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_O3_CPU_TOURNAMENT_PRED_HH__ -#define __CPU_O3_CPU_TOURNAMENT_PRED_HH__ +#ifndef __CPU_O3_TOURNAMENT_PRED_HH__ +#define __CPU_O3_TOURNAMENT_PRED_HH__ // For Addr type. #include "arch/isa_traits.hh" #include "cpu/o3/sat_counter.hh" - +#include <vector> + +/** + * Implements a tournament branch predictor, hopefully identical to the one + * used in the 21264. It has a local predictor, which uses a local history + * table to index into a table of counters, and a global predictor, which + * uses a global history to index into a table of counters. A choice + * predictor chooses between the two. Only the global history register + * is speculatively updated, the rest are updated upon branches committing + * or misspeculating. + */ class TournamentBP { public: /** * Default branch predictor constructor. */ - TournamentBP(unsigned local_predictor_size, - unsigned local_ctr_bits, - unsigned local_history_table_size, - unsigned local_history_bits, - unsigned global_predictor_size, - unsigned global_history_bits, - unsigned global_ctr_bits, - unsigned choice_predictor_size, - unsigned choice_ctr_bits, + TournamentBP(unsigned localPredictorSize, + unsigned localCtrBits, + unsigned localHistoryTableSize, + unsigned localHistoryBits, + unsigned globalPredictorSize, + unsigned globalHistoryBits, + unsigned globalCtrBits, + unsigned choicePredictorSize, + unsigned choiceCtrBits, unsigned instShiftAmt); /** * Looks up the given address in the branch predictor and returns - * a true/false value as to whether it is taken. + * a true/false value as to whether it is taken. Also creates a + * BPHistory object to store any state it will need on squash/update. * @param branch_addr The address of the branch to look up. + * @param bp_history Pointer that will be set to the BPHistory object. * @return Whether or not the branch is taken. */ - bool lookup(Addr &branch_addr); + bool lookup(Addr &branch_addr, void * &bp_history); + + /** + * Records that there was an unconditional branch, and modifies + * the bp history to point to an object that has the previous + * global history stored in it. + * @param bp_history Pointer that will be set to the BPHistory object. + */ + void uncondBr(void * &bp_history); /** * Updates the branch predictor with the actual result of a branch. * @param branch_addr The address of the branch to update. * @param taken Whether or not the branch was taken. + * @param bp_history Pointer to the BPHistory object that was created + * when the branch was predicted. */ - void update(Addr &branch_addr, unsigned global_history, bool taken); + void update(Addr &branch_addr, bool taken, void *bp_history); + /** + * Restores the global branch history on a squash. + * @param bp_history Pointer to the BPHistory object that has the + * previous global branch history in it. + */ + void squash(void *bp_history); + + /** Returns the global history. */ inline unsigned readGlobalHist() { return globalHistory; } private: - + /** + * Returns if the branch should be taken or not, given a counter + * value. + * @param count The counter value. + */ inline bool getPrediction(uint8_t &count); + /** + * Returns the local history index, given a branch address. + * @param branch_addr The branch's PC address. + */ inline unsigned calcLocHistIdx(Addr &branch_addr); - inline void updateHistoriesTaken(unsigned local_history_idx); + /** Updates global history as taken. */ + inline void updateGlobalHistTaken(); + + /** Updates global history as not taken. */ + inline void updateGlobalHistNotTaken(); + + /** + * Updates local histories as taken. + * @param local_history_idx The local history table entry that + * will be updated. + */ + inline void updateLocalHistTaken(unsigned local_history_idx); + + /** + * Updates local histories as not taken. + * @param local_history_idx The local history table entry that + * will be updated. + */ + inline void updateLocalHistNotTaken(unsigned local_history_idx); - inline void updateHistoriesNotTaken(unsigned local_history_idx); + /** + * The branch history information that is created upon predicting + * a branch. It will be passed back upon updating and squashing, + * when the BP can use this information to update/restore its + * state properly. + */ + struct BPHistory { +#ifdef DEBUG + BPHistory() + { newCount++; } + ~BPHistory() + { newCount--; } + + static int newCount; +#endif + unsigned globalHistory; + bool localPredTaken; + bool globalPredTaken; + bool globalUsed; + }; /** Local counters. */ - SatCounter *localCtrs; + std::vector<SatCounter> localCtrs; /** Size of the local predictor. */ unsigned localPredictorSize; @@ -87,7 +164,7 @@ class TournamentBP unsigned localCtrBits; /** Array of local history table entries. */ - unsigned *localHistoryTable; + std::vector<unsigned> localHistoryTable; /** Size of the local history table. */ unsigned localHistoryTableSize; @@ -100,9 +177,8 @@ class TournamentBP /** Mask to get the proper local history. */ unsigned localHistoryMask; - /** Array of counters that make up the global predictor. */ - SatCounter *globalCtrs; + std::vector<SatCounter> globalCtrs; /** Size of the global predictor. */ unsigned globalPredictorSize; @@ -119,9 +195,8 @@ class TournamentBP /** Mask to get the proper global history. */ unsigned globalHistoryMask; - /** Array of counters that make up the choice predictor. */ - SatCounter *choiceCtrs; + std::vector<SatCounter> choiceCtrs; /** Size of the choice predictor (identical to the global predictor). */ unsigned choicePredictorSize; @@ -140,4 +215,4 @@ class TournamentBP unsigned threshold; }; -#endif // __CPU_O3_CPU_TOURNAMENT_PRED_HH__ +#endif // __CPU_O3_TOURNAMENT_PRED_HH__ diff --git a/src/cpu/op_class.cc b/src/cpu/op_class.cc index 00136ded5..f7ef49c0f 100644 --- a/src/cpu/op_class.cc +++ b/src/cpu/op_class.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #include "cpu/op_class.hh" diff --git a/src/cpu/op_class.hh b/src/cpu/op_class.hh index cdb40a0fb..71819c904 100644 --- a/src/cpu/op_class.hh +++ b/src/cpu/op_class.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #ifndef __CPU__OP_CLASS_HH__ @@ -59,6 +62,6 @@ enum OpClass { /** * Array mapping OpClass enum values to strings. Defined in op_class.cc. */ -extern const char *opClassStrings[]; +extern const char *opClassStrings[Num_OpClasses]; #endif // __CPU__OP_CLASS_HH__ diff --git a/src/cpu/ozone/back_end.cc b/src/cpu/ozone/back_end.cc new file mode 100644 index 000000000..a61a00c84 --- /dev/null +++ b/src/cpu/ozone/back_end.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/back_end_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +//template class BackEnd<OzoneImpl>; diff --git a/src/cpu/ozone/back_end.hh b/src/cpu/ozone/back_end.hh new file mode 100644 index 000000000..9bab6a964 --- /dev/null +++ b/src/cpu/ozone/back_end.hh @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_BACK_END_HH__ +#define __CPU_OZONE_BACK_END_HH__ + +#include <list> +#include <queue> +#include <string> + +#include "arch/faults.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +class ThreadContext; + +template <class Impl> +class OzoneThreadState; + +template <class Impl> +class BackEnd +{ + public: + typedef OzoneThreadState<Impl> Thread; + + typedef typename Impl::Params Params; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::FrontEnd FrontEnd; + typedef typename Impl::FullCPU::CommStruct CommStruct; + + struct SizeStruct { + int size; + }; + + typedef SizeStruct DispatchToIssue; + typedef SizeStruct IssueToExec; + typedef SizeStruct ExecToCommit; + typedef SizeStruct Writeback; + + TimeBuffer<DispatchToIssue> d2i; + typename TimeBuffer<DispatchToIssue>::wire instsToDispatch; + TimeBuffer<IssueToExec> i2e; + typename TimeBuffer<IssueToExec>::wire instsToExecute; + TimeBuffer<ExecToCommit> e2c; + TimeBuffer<Writeback> numInstsToWB; + + TimeBuffer<CommStruct> *comm; + typename TimeBuffer<CommStruct>::wire toIEW; + typename TimeBuffer<CommStruct>::wire fromCommit; + + class InstQueue { + enum queue { + NonSpec, + IQ, + ToBeScheduled, + ReadyList, + ReplayList + }; + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; + public: + InstQueue(Params *params); + + std::string name() const; + + void regStats(); + + void setIssueExecQueue(TimeBuffer<IssueToExec> *i2e_queue); + + void setBE(BackEnd *_be) { be = _be; } + + void insert(DynInstPtr &inst); + + void scheduleReadyInsts(); + + void scheduleNonSpec(const InstSeqNum &sn); + + DynInstPtr getReadyInst(); + + void commit(const InstSeqNum &sn) {} + + void squash(const InstSeqNum &sn); + + int wakeDependents(DynInstPtr &inst); + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst); + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst); + + /** Completes memory instruction. */ + void completeMemInst(DynInstPtr &inst); + + void violation(DynInstPtr &inst, DynInstPtr &violation) { } + + bool isFull() { return numInsts >= size; } + + void dumpInsts(); + + private: + bool find(queue q, typename std::list<DynInstPtr>::iterator it); + BackEnd *be; + TimeBuffer<IssueToExec> *i2e; + typename TimeBuffer<IssueToExec>::wire numIssued; + typedef typename std::list<DynInstPtr> InstList; + typedef typename std::list<DynInstPtr>::iterator InstListIt; + typedef typename std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> ReadyInstQueue; + // Not sure I need the IQ list; it just needs to be a count. + InstList iq; + InstList toBeScheduled; + InstList readyList; + InstList nonSpec; + InstList replayList; + ReadyInstQueue readyQueue; + public: + int size; + int numInsts; + int width; + + Stats::VectorDistribution<> occ_dist; + + Stats::Vector<> inst_count; + Stats::Vector<> peak_inst_count; + Stats::Scalar<> empty_count; + Stats::Scalar<> current_count; + Stats::Scalar<> fullCount; + + Stats::Formula occ_rate; + Stats::Formula avg_residency; + Stats::Formula empty_rate; + Stats::Formula full_rate; + }; + + /** LdWriteback event for a load completion. */ + class LdWritebackEvent : public Event { + private: + /** Instruction that is writing back data to the register file. */ + DynInstPtr inst; + /** Pointer to IEW stage. */ + BackEnd *be; + + public: + /** Constructs a load writeback event. */ + LdWritebackEvent(DynInstPtr &_inst, BackEnd *be); + + /** Processes writeback event. */ + virtual void process(); + /** Returns the description of the writeback event. */ + virtual const char *description(); + }; + + BackEnd(Params *params); + + std::string name() const; + + void regStats(); + + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + void setFrontEnd(FrontEnd *front_end_ptr) + { frontEnd = front_end_ptr; } + + void setTC(ThreadContext *tc_ptr) + { tc = tc_ptr; } + + void setThreadState(Thread *thread_ptr) + { thread = thread_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm); + + void tick(); + void squash(); + void squashFromTC(); + bool tcSquash; + + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + Addr readCommitPC() { return commitPC; } + + Addr commitPC; + + bool robEmpty() { return instList.empty(); } + + bool isFull() { return numInsts >= numROBEntries; } + bool isBlocked() { return status == Blocked || dispatchStatus == Blocked; } + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst) + { IQ.rescheduleMemInst(inst); } + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst) + { IQ.replayMemInst(inst); } + + /** Completes memory instruction. */ + void completeMemInst(DynInstPtr &inst) + { IQ.completeMemInst(inst); } + + void fetchFault(Fault &fault); + + private: + void updateStructures(); + void dispatchInsts(); + void dispatchStall(); + void checkDispatchStatus(); + void scheduleReadyInsts(); + void executeInsts(); + void commitInsts(); + void addToIQ(DynInstPtr &inst); + void addToLSQ(DynInstPtr &inst); + void instToCommit(DynInstPtr &inst); + void writebackInsts(); + bool commitInst(int inst_num); + void squash(const InstSeqNum &sn); + void squashDueToBranch(DynInstPtr &inst); + void squashDueToMemBlocked(DynInstPtr &inst); + void updateExeInstStats(DynInstPtr &inst); + void updateComInstStats(DynInstPtr &inst); + + public: + FullCPU *cpu; + + FrontEnd *frontEnd; + + ThreadContext *tc; + + Thread *thread; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissComplete, + Blocked + }; + + Status status; + + Status dispatchStatus; + + Counter funcExeInst; + + private: +// typedef typename Impl::InstQueue InstQueue; + + InstQueue IQ; + + typedef typename Impl::LdstQueue LdstQueue; + + LdstQueue LSQ; + public: + RenameTable<Impl> commitRenameTable; + + RenameTable<Impl> renameTable; + private: + class DCacheCompletionEvent : public Event + { + private: + BackEnd *be; + + public: + DCacheCompletionEvent(BackEnd *_be); + + virtual void process(); + virtual const char *description(); + }; + + friend class DCacheCompletionEvent; + + DCacheCompletionEvent cacheCompletionEvent; + + MemInterface *dcacheInterface; + + Request *memReq; + + // General back end width. Used if the more specific isn't given. + int width; + + // Dispatch width. + int dispatchWidth; + int numDispatchEntries; + int dispatchSize; + + int issueWidth; + + // Writeback width + int wbWidth; + + // Commit width + int commitWidth; + + /** Index into queue of instructions being written back. */ + unsigned wbNumInst; + + /** Cycle number within the queue of instructions being written + * back. Used in case there are too many instructions writing + * back at the current cycle and writesbacks need to be scheduled + * for the future. See comments in instToCommit(). + */ + unsigned wbCycle; + + int numROBEntries; + int numInsts; + + bool squashPending; + InstSeqNum squashSeqNum; + Addr squashNextPC; + + Fault faultFromFetch; + + private: + typedef typename std::list<DynInstPtr>::iterator InstListIt; + + std::list<DynInstPtr> instList; + std::list<DynInstPtr> dispatch; + std::list<DynInstPtr> writeback; + + int latency; + + int squashLatency; + + bool exactFullStall; + + bool fetchRedirect[Impl::MaxThreads]; + + // number of cycles stalled for D-cache misses +/* Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; +*/ + Stats::Vector<> rob_cap_events; + Stats::Vector<> rob_cap_inst_count; + Stats::Vector<> iq_cap_events; + Stats::Vector<> iq_cap_inst_count; + // total number of instructions executed + Stats::Vector<> exe_inst; + Stats::Vector<> exe_swp; + Stats::Vector<> exe_nop; + Stats::Vector<> exe_refs; + Stats::Vector<> exe_loads; + Stats::Vector<> exe_branches; + + Stats::Vector<> issued_ops; + + // total number of loads forwaded from LSQ stores + Stats::Vector<> lsq_forw_loads; + + // total number of loads ignored due to invalid addresses + Stats::Vector<> inv_addr_loads; + + // total number of software prefetches ignored due to invalid addresses + Stats::Vector<> inv_addr_swpfs; + // ready loads blocked due to memory disambiguation + Stats::Vector<> lsq_blocked_loads; + + Stats::Scalar<> lsqInversion; + + Stats::Vector<> n_issued_dist; + Stats::VectorDistribution<> issue_delay_dist; + + Stats::VectorDistribution<> queue_res_dist; +/* + Stats::Vector<> stat_fu_busy; + Stats::Vector2d<> stat_fuBusy; + Stats::Vector<> dist_unissued; + Stats::Vector2d<> stat_issued_inst_type; + + Stats::Formula misspec_cnt; + Stats::Formula misspec_ipc; + Stats::Formula issue_rate; + Stats::Formula issue_stores; + Stats::Formula issue_op_rate; + Stats::Formula fu_busy_rate; + Stats::Formula commit_stores; + Stats::Formula commit_ipc; + Stats::Formula commit_ipb; + Stats::Formula lsq_inv_rate; +*/ + Stats::Vector<> writeback_count; + Stats::Vector<> producer_inst; + Stats::Vector<> consumer_inst; + Stats::Vector<> wb_penalized; + + Stats::Formula wb_rate; + Stats::Formula wb_fanout; + Stats::Formula wb_penalized_rate; + + // total number of instructions committed + Stats::Vector<> stat_com_inst; + Stats::Vector<> stat_com_swp; + Stats::Vector<> stat_com_refs; + Stats::Vector<> stat_com_loads; + Stats::Vector<> stat_com_membars; + Stats::Vector<> stat_com_branches; + + Stats::Distribution<> n_committed_dist; + + Stats::Scalar<> commit_eligible_samples; + Stats::Vector<> commit_eligible; + + Stats::Scalar<> ROB_fcount; + Stats::Formula ROB_full_rate; + + Stats::Vector<> ROB_count; // cumulative ROB occupancy + Stats::Formula ROB_occ_rate; + Stats::VectorDistribution<> ROB_occ_dist; + public: + void dumpInsts(); +}; + +template <class Impl> +template <class T> +Fault +BackEnd<Impl>::read(RequestPtr req, T &data, int load_idx) +{ +/* memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataReadReq(memReq); + + // if we have a cache, do cache access too + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Read; + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT && dcacheInterface->doEvents()) { + // Fix this hack for keeping funcExeInst correct with loads that + // are executed twice. + --funcExeInst; + + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); +// status = DcacheMissStall; + DPRINTF(OzoneCPU, "Dcache miss stall!\n"); + } else { + // do functional access + fault = thread->mem->read(memReq, data); + + } + } +*/ +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); +*/ + return LSQ.read(req, data, load_idx); +} + +template <class Impl> +template <class T> +Fault +BackEnd<Impl>::write(RequestPtr req, T &data, int store_idx) +{ +/* + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataWriteReq(memReq); + + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Write; + memcpy(memReq->data,(uint8_t *)&data,memReq->size); + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT && dcacheInterface->doEvents()) { + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); +// status = DcacheMissStall; + DPRINTF(OzoneCPU, "Dcache miss stall!\n"); + } + } + + if (res && (fault == NoFault)) + *res = memReq->result; + */ +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); +*/ + return LSQ.write(req, data, store_idx); +} + +#endif // __CPU_OZONE_BACK_END_HH__ diff --git a/src/cpu/ozone/back_end_impl.hh b/src/cpu/ozone/back_end_impl.hh new file mode 100644 index 000000000..ac3218c02 --- /dev/null +++ b/src/cpu/ozone/back_end_impl.hh @@ -0,0 +1,1933 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "encumbered/cpu/full/op_class.hh" +#include "cpu/ozone/back_end.hh" + +template <class Impl> +BackEnd<Impl>::InstQueue::InstQueue(Params *params) + : size(params->numIQEntries), numInsts(0), width(params->issueWidth) +{ +} + +template <class Impl> +std::string +BackEnd<Impl>::InstQueue::name() const +{ + return be->name() + ".iq"; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::regStats() +{ + using namespace Stats; + + occ_dist + .init(1, 0, size, 2) + .name(name() + "occ_dist") + .desc("IQ Occupancy per cycle") + .flags(total | cdf) + ; + + inst_count + .init(1) + .name(name() + "cum_num_insts") + .desc("Total occupancy") + .flags(total) + ; + + peak_inst_count + .init(1) + .name(name() + "peak_occupancy") + .desc("Peak IQ occupancy") + .flags(total) + ; + + current_count + .name(name() + "current_count") + .desc("Occupancy this cycle") + ; + + empty_count + .name(name() + "empty_count") + .desc("Number of empty cycles") + ; + + fullCount + .name(name() + "full_count") + .desc("Number of full cycles") + ; + + + occ_rate + .name(name() + "occ_rate") + .desc("Average occupancy") + .flags(total) + ; + occ_rate = inst_count / be->cpu->numCycles; + + avg_residency + .name(name() + "avg_residency") + .desc("Average IQ residency") + .flags(total) + ; + avg_residency = occ_rate / be->cpu->numCycles; + + empty_rate + .name(name() + "empty_rate") + .desc("Fraction of cycles empty") + ; + empty_rate = 100 * empty_count / be->cpu->numCycles; + + full_rate + .name(name() + "full_rate") + .desc("Fraction of cycles full") + ; + full_rate = 100 * fullCount / be->cpu->numCycles; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::setIssueExecQueue(TimeBuffer<IssueToExec> *i2e_queue) +{ + i2e = i2e_queue; + numIssued = i2e->getWire(0); +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::insert(DynInstPtr &inst) +{ + numInsts++; + inst_count[0]++; + if (!inst->isNonSpeculative()) { + DPRINTF(BE, "Instruction [sn:%lli] added to IQ\n", inst->seqNum); + if (inst->readyToIssue()) { + toBeScheduled.push_front(inst); + inst->iqIt = toBeScheduled.begin(); + inst->iqItValid = true; + } else { + iq.push_front(inst); + inst->iqIt = iq.begin(); + inst->iqItValid = true; + } + } else { + DPRINTF(BE, "Nonspeculative instruction [sn:%lli] added to IQ\n", inst->seqNum); + nonSpec.push_front(inst); + inst->iqIt = nonSpec.begin(); + inst->iqItValid = true; + } +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::scheduleReadyInsts() +{ + int scheduled = numIssued->size; + InstListIt iq_it = --toBeScheduled.end(); + InstListIt iq_end_it = toBeScheduled.end(); + + while (iq_it != iq_end_it && scheduled < width) { +// if ((*iq_it)->readyToIssue()) { + DPRINTF(BE, "Instruction [sn:%lli] PC:%#x is ready\n", + (*iq_it)->seqNum, (*iq_it)->readPC()); + readyQueue.push(*iq_it); + readyList.push_front(*iq_it); + + (*iq_it)->iqIt = readyList.begin(); + + toBeScheduled.erase(iq_it--); + + ++scheduled; +// } else { +// iq_it++; +// } + } + + numIssued->size+= scheduled; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::scheduleNonSpec(const InstSeqNum &sn) +{ +/* + InstListIt non_spec_it = nonSpec.begin(); + InstListIt non_spec_end_it = nonSpec.end(); + + while ((*non_spec_it)->seqNum != sn) { + non_spec_it++; + assert(non_spec_it != non_spec_end_it); + } +*/ + DynInstPtr inst = nonSpec.back(); + + DPRINTF(BE, "Nonspeculative instruction [sn:%lli] scheduled\n", inst->seqNum); + + assert(inst->seqNum == sn); + + assert(find(NonSpec, inst->iqIt)); + nonSpec.erase(inst->iqIt); + readyList.push_front(inst); + inst->iqIt = readyList.begin(); + readyQueue.push(inst); + numIssued->size++; +} + +template <class Impl> +typename Impl::DynInstPtr +BackEnd<Impl>::InstQueue::getReadyInst() +{ + assert(!readyList.empty()); + + DynInstPtr inst = readyQueue.top(); + readyQueue.pop(); + assert(find(ReadyList, inst->iqIt)); + readyList.erase(inst->iqIt); + inst->iqItValid = false; +// if (!inst->isMemRef()) + --numInsts; + return inst; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::squash(const InstSeqNum &sn) +{ + InstListIt iq_it = iq.begin(); + InstListIt iq_end_it = iq.end(); + + while (iq_it != iq_end_it && (*iq_it)->seqNum > sn) { + DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); + (*iq_it)->iqItValid = false; + iq.erase(iq_it++); + --numInsts; + } + + iq_it = nonSpec.begin(); + iq_end_it = nonSpec.end(); + + while (iq_it != iq_end_it && (*iq_it)->seqNum > sn) { + DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); + (*iq_it)->iqItValid = false; + nonSpec.erase(iq_it++); + --numInsts; + } + + iq_it = replayList.begin(); + iq_end_it = replayList.end(); + + while (iq_it != iq_end_it) { + if ((*iq_it)->seqNum > sn) { + DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); + (*iq_it)->iqItValid = false; + replayList.erase(iq_it++); + --numInsts; + } else { + iq_it++; + } + } + + assert(numInsts >= 0); +/* + InstListIt ready_it = readyList.begin(); + InstListIt ready_end_it = readyList.end(); + + while (ready_it != ready_end_it) { + if ((*ready_it)->seqNum > sn) { + readyList.erase(ready_it++); + } else { + ready_it++; + } + } +*/ +} + +template <class Impl> +int +BackEnd<Impl>::InstQueue::wakeDependents(DynInstPtr &inst) +{ + assert(!inst->isSquashed()); + std::vector<DynInstPtr> &dependents = inst->getDependents(); + int num_outputs = dependents.size(); + + DPRINTF(BE, "Waking instruction [sn:%lli] dependents in IQ\n", inst->seqNum); + + for (int i = 0; i < num_outputs; i++) { + DynInstPtr dep_inst = dependents[i]; + dep_inst->markSrcRegReady(); + DPRINTF(BE, "Marking source reg ready [sn:%lli] in IQ\n", dep_inst->seqNum); + + if (dep_inst->readyToIssue() && dep_inst->iqItValid) { + if (dep_inst->isNonSpeculative()) { + assert(find(NonSpec, dep_inst->iqIt)); + nonSpec.erase(dep_inst->iqIt); + } else { + assert(find(IQ, dep_inst->iqIt)); + iq.erase(dep_inst->iqIt); + } + + toBeScheduled.push_front(dep_inst); + dep_inst->iqIt = toBeScheduled.begin(); + } + } + return num_outputs; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::rescheduleMemInst(DynInstPtr &inst) +{ + DPRINTF(BE, "Rescheduling memory instruction [sn:%lli]\n", inst->seqNum); + assert(!inst->iqItValid); + replayList.push_front(inst); + inst->iqIt = replayList.begin(); + inst->iqItValid = true; + ++numInsts; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::replayMemInst(DynInstPtr &inst) +{ + DPRINTF(BE, "Replaying memory instruction [sn:%lli]\n", inst->seqNum); + assert(find(ReplayList, inst->iqIt)); + InstListIt iq_it = --replayList.end(); + InstListIt iq_end_it = replayList.end(); + while (iq_it != iq_end_it) { + DynInstPtr rescheduled_inst = (*iq_it); + + DPRINTF(BE, "Memory instruction [sn:%lli] also replayed\n", inst->seqNum); + replayList.erase(iq_it--); + toBeScheduled.push_front(rescheduled_inst); + rescheduled_inst->iqIt = toBeScheduled.begin(); + } +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::completeMemInst(DynInstPtr &inst) +{ + panic("Not implemented."); +} + +template <class Impl> +bool +BackEnd<Impl>::InstQueue::find(queue q, InstListIt it) +{ + InstListIt iq_it, iq_end_it; + switch(q) { + case NonSpec: + iq_it = nonSpec.begin(); + iq_end_it = nonSpec.end(); + break; + case IQ: + iq_it = iq.begin(); + iq_end_it = iq.end(); + break; + case ToBeScheduled: + iq_it = toBeScheduled.begin(); + iq_end_it = toBeScheduled.end(); + break; + case ReadyList: + iq_it = readyList.begin(); + iq_end_it = readyList.end(); + break; + case ReplayList: + iq_it = replayList.begin(); + iq_end_it = replayList.end(); + } + + while (iq_it != it && iq_it != iq_end_it) { + iq_it++; + } + if (iq_it == it) { + return true; + } else { + return false; + } +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::dumpInsts() +{ + cprintf("IQ size: %i\n", iq.size()); + + InstListIt inst_list_it = --iq.end(); + + int num = 0; + int valid_num = 0; + while (inst_list_it != iq.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("nonSpec size: %i\n", nonSpec.size()); + + inst_list_it = --nonSpec.end(); + + while (inst_list_it != nonSpec.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("toBeScheduled size: %i\n", toBeScheduled.size()); + + inst_list_it = --toBeScheduled.end(); + + while (inst_list_it != toBeScheduled.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("readyList size: %i\n", readyList.size()); + + inst_list_it = --readyList.end(); + + while (inst_list_it != readyList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } +} + +template<class Impl> +BackEnd<Impl>::LdWritebackEvent::LdWritebackEvent(DynInstPtr &_inst, + BackEnd<Impl> *_be) + : Event(&mainEventQueue), inst(_inst), be(_be) +{ + this->setFlags(Event::AutoDelete); +} + +template<class Impl> +void +BackEnd<Impl>::LdWritebackEvent::process() +{ + DPRINTF(BE, "Load writeback event [sn:%lli]\n", inst->seqNum); +// DPRINTF(Activity, "Activity: Ld Writeback event [sn:%lli]\n", inst->seqNum); + + //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); + +// iewStage->wakeCPU(); + + if (inst->isSquashed()) { + inst = NULL; + return; + } + + if (!inst->isExecuted()) { + inst->setExecuted(); + + // Execute again to copy data to proper place. + inst->completeAcc(); + } + + // Need to insert instruction into queue to commit + be->instToCommit(inst); + + //wroteToTimeBuffer = true; +// iewStage->activityThisCycle(); + + inst = NULL; +} + +template<class Impl> +const char * +BackEnd<Impl>::LdWritebackEvent::description() +{ + return "Load writeback event"; +} + + +template <class Impl> +BackEnd<Impl>::DCacheCompletionEvent::DCacheCompletionEvent(BackEnd *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ +} + +template <class Impl> +void +BackEnd<Impl>::DCacheCompletionEvent::process() +{ +} + +template <class Impl> +const char * +BackEnd<Impl>::DCacheCompletionEvent::description() +{ + return "Cache completion event"; +} + +template <class Impl> +BackEnd<Impl>::BackEnd(Params *params) + : d2i(5, 5), i2e(5, 5), e2c(5, 5), numInstsToWB(5, 5), + xcSquash(false), IQ(params), + cacheCompletionEvent(this), width(params->backEndWidth), + exactFullStall(true) +{ + numROBEntries = params->numROBEntries; + numInsts = 0; + numDispatchEntries = 32; + IQ.setBE(this); + LSQ.setBE(this); + + // Setup IQ and LSQ with their parameters here. + instsToDispatch = d2i.getWire(-1); + + instsToExecute = i2e.getWire(-1); + + IQ.setIssueExecQueue(&i2e); + + dispatchWidth = params->dispatchWidth ? params->dispatchWidth : width; + issueWidth = params->issueWidth ? params->issueWidth : width; + wbWidth = params->wbWidth ? params->wbWidth : width; + commitWidth = params->commitWidth ? params->commitWidth : width; + + LSQ.init(params, params->LQEntries, params->SQEntries, 0); + + dispatchStatus = Running; +} + +template <class Impl> +std::string +BackEnd<Impl>::name() const +{ + return cpu->name() + ".backend"; +} + +template <class Impl> +void +BackEnd<Impl>::regStats() +{ + using namespace Stats; + rob_cap_events + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_events") + .desc("number of cycles where ROB cap was active") + .flags(total) + ; + + rob_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_inst") + .desc("number of instructions held up by ROB cap") + .flags(total) + ; + + iq_cap_events + .init(cpu->number_of_threads) + .name(name() +".IQ:cap_events" ) + .desc("number of cycles where IQ cap was active") + .flags(total) + ; + + iq_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".IQ:cap_inst") + .desc("number of instructions held up by IQ cap") + .flags(total) + ; + + + exe_inst + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:count") + .desc("number of insts issued") + .flags(total) + ; + + exe_swp + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:swp") + .desc("number of swp insts issued") + .flags(total) + ; + + exe_nop + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:nop") + .desc("number of nop insts issued") + .flags(total) + ; + + exe_refs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:refs") + .desc("number of memory reference insts issued") + .flags(total) + ; + + exe_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:loads") + .desc("number of load insts issued") + .flags(total) + ; + + exe_branches + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:branches") + .desc("Number of branches issued") + .flags(total) + ; + + issued_ops + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:op_count") + .desc("number of insts issued") + .flags(total) + ; + +/* + for (int i=0; i<Num_OpClasses; ++i) { + stringstream subname; + subname << opClassStrings[i] << "_delay"; + issue_delay_dist.subname(i, subname.str()); + } +*/ + // + // Other stats + // + lsq_forw_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:forw_loads") + .desc("number of loads forwarded via LSQ") + .flags(total) + ; + + inv_addr_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_loads") + .desc("number of invalid-address loads") + .flags(total) + ; + + inv_addr_swpfs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_swpfs") + .desc("number of invalid-address SW prefetches") + .flags(total) + ; + + lsq_blocked_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:blocked_loads") + .desc("number of ready loads not issued due to memory disambiguation") + .flags(total) + ; + + lsqInversion + .name(name() + ".ISSUE:lsq_invert") + .desc("Number of times LSQ instruction issued early") + ; + + n_issued_dist + .init(issueWidth + 1) + .name(name() + ".ISSUE:issued_per_cycle") + .desc("Number of insts issued each cycle") + .flags(total | pdf | dist) + ; + issue_delay_dist + .init(Num_OpClasses,0,99,2) + .name(name() + ".ISSUE:") + .desc("cycles from operands ready to issue") + .flags(pdf | cdf) + ; + + queue_res_dist + .init(Num_OpClasses, 0, 99, 2) + .name(name() + ".IQ:residence:") + .desc("cycles from dispatch to issue") + .flags(total | pdf | cdf ) + ; + for (int i = 0; i < Num_OpClasses; ++i) { + queue_res_dist.subname(i, opClassStrings[i]); + } + + writeback_count + .init(cpu->number_of_threads) + .name(name() + ".WB:count") + .desc("cumulative count of insts written-back") + .flags(total) + ; + + producer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:producers") + .desc("num instructions producing a value") + .flags(total) + ; + + consumer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:consumers") + .desc("num instructions consuming a value") + .flags(total) + ; + + wb_penalized + .init(cpu->number_of_threads) + .name(name() + ".WB:penalized") + .desc("number of instrctions required to write to 'other' IQ") + .flags(total) + ; + + + wb_penalized_rate + .name(name() + ".WB:penalized_rate") + .desc ("fraction of instructions written-back that wrote to 'other' IQ") + .flags(total) + ; + + wb_penalized_rate = wb_penalized / writeback_count; + + wb_fanout + .name(name() + ".WB:fanout") + .desc("average fanout of values written-back") + .flags(total) + ; + + wb_fanout = producer_inst / consumer_inst; + + wb_rate + .name(name() + ".WB:rate") + .desc("insts written-back per cycle") + .flags(total) + ; + wb_rate = writeback_count / cpu->numCycles; + + stat_com_inst + .init(cpu->number_of_threads) + .name(name() + ".COM:count") + .desc("Number of instructions committed") + .flags(total) + ; + + stat_com_swp + .init(cpu->number_of_threads) + .name(name() + ".COM:swp_count") + .desc("Number of s/w prefetches committed") + .flags(total) + ; + + stat_com_refs + .init(cpu->number_of_threads) + .name(name() + ".COM:refs") + .desc("Number of memory references committed") + .flags(total) + ; + + stat_com_loads + .init(cpu->number_of_threads) + .name(name() + ".COM:loads") + .desc("Number of loads committed") + .flags(total) + ; + + stat_com_membars + .init(cpu->number_of_threads) + .name(name() + ".COM:membars") + .desc("Number of memory barriers committed") + .flags(total) + ; + + stat_com_branches + .init(cpu->number_of_threads) + .name(name() + ".COM:branches") + .desc("Number of branches committed") + .flags(total) + ; + n_committed_dist + .init(0,commitWidth,1) + .name(name() + ".COM:committed_per_cycle") + .desc("Number of insts commited each cycle") + .flags(pdf) + ; + + // + // Commit-Eligible instructions... + // + // -> The number of instructions eligible to commit in those + // cycles where we reached our commit BW limit (less the number + // actually committed) + // + // -> The average value is computed over ALL CYCLES... not just + // the BW limited cycles + // + // -> The standard deviation is computed only over cycles where + // we reached the BW limit + // + commit_eligible + .init(cpu->number_of_threads) + .name(name() + ".COM:bw_limited") + .desc("number of insts not committed due to BW limits") + .flags(total) + ; + + commit_eligible_samples + .name(name() + ".COM:bw_lim_events") + .desc("number cycles where commit BW limit reached") + ; + + ROB_fcount + .name(name() + ".ROB:full_count") + .desc("number of cycles where ROB was full") + ; + + ROB_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:occupancy") + .desc(name() + ".ROB occupancy (cumulative)") + .flags(total) + ; + + ROB_full_rate + .name(name() + ".ROB:full_rate") + .desc("ROB full per cycle") + ; + ROB_full_rate = ROB_fcount / cpu->numCycles; + + ROB_occ_rate + .name(name() + ".ROB:occ_rate") + .desc("ROB occupancy rate") + .flags(total) + ; + ROB_occ_rate = ROB_count / cpu->numCycles; + + ROB_occ_dist + .init(cpu->number_of_threads,0,numROBEntries,2) + .name(name() + ".ROB:occ_dist") + .desc("ROB Occupancy per cycle") + .flags(total | cdf) + ; + + IQ.regStats(); +} + +template <class Impl> +void +BackEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) +{ + comm = _comm; + toIEW = comm->getWire(0); + fromCommit = comm->getWire(-1); +} + +template <class Impl> +void +BackEnd<Impl>::tick() +{ + DPRINTF(BE, "Ticking back end\n"); + + ROB_count[0]+= numInsts; + + wbCycle = 0; + + if (xcSquash) { + squashFromXC(); + } + + // Read in any done instruction information and update the IQ or LSQ. + updateStructures(); + + if (dispatchStatus != Blocked) { + d2i.advance(); + dispatchInsts(); + } else { + checkDispatchStatus(); + } + + i2e.advance(); + scheduleReadyInsts(); + + e2c.advance(); + executeInsts(); + + numInstsToWB.advance(); + writebackInsts(); + + commitInsts(); + + DPRINTF(BE, "IQ entries in use: %i, ROB entries in use: %i, LSQ loads: %i, LSQ stores: %i\n", + IQ.numInsts, numInsts, LSQ.numLoads(), LSQ.numStores()); + + assert(numInsts == instList.size()); +} + +template <class Impl> +void +BackEnd<Impl>::updateStructures() +{ + if (fromCommit->doneSeqNum) { + IQ.commit(fromCommit->doneSeqNum); + LSQ.commitLoads(fromCommit->doneSeqNum); + LSQ.commitStores(fromCommit->doneSeqNum); + } + + if (fromCommit->nonSpecSeqNum) { + if (fromCommit->uncached) { + LSQ.executeLoad(fromCommit->lqIdx); + } else { + IQ.scheduleNonSpec( + fromCommit->nonSpecSeqNum); + } + } +} + +template <class Impl> +void +BackEnd<Impl>::addToIQ(DynInstPtr &inst) +{ + // Do anything IQ specific here? + IQ.insert(inst); +} + +template <class Impl> +void +BackEnd<Impl>::addToLSQ(DynInstPtr &inst) +{ + // Do anything LSQ specific here? + LSQ.insert(inst); +} + +template <class Impl> +void +BackEnd<Impl>::dispatchInsts() +{ + DPRINTF(BE, "Trying to dispatch instructions.\n"); + + // Pull instructions out of the front end. + int disp_width = dispatchWidth ? dispatchWidth : width; + + // Could model dispatching time, but in general 1 cycle is probably + // good enough. + + if (dispatchSize < numDispatchEntries) { + for (int i = 0; i < disp_width; i++) { + // Get instructions + DynInstPtr inst = frontEnd->getInst(); + + if (!inst) { + // No more instructions to get + break; + } + + DPRINTF(BE, "Processing instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + for (int i = 0; i < inst->numDestRegs(); ++i) + renameTable[inst->destRegIdx(i)] = inst; + + // Add to queue to be dispatched. + dispatch.push_back(inst); + + d2i[0].size++; + ++dispatchSize; + } + } + + assert(dispatch.size() < 64); + + for (int i = 0; i < instsToDispatch->size; ++i) { + assert(!dispatch.empty()); + // Get instruction from front of time buffer + DynInstPtr inst = dispatch.front(); + dispatch.pop_front(); + --dispatchSize; + + if (inst->isSquashed()) + continue; + + ++numInsts; + instList.push_back(inst); + + DPRINTF(BE, "Dispatching instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + addToIQ(inst); + + if (inst->isMemRef()) { + addToLSQ(inst); + } + + if (inst->isNonSpeculative()) { + inst->setCanCommit(); + } + + // Check if IQ or LSQ is full. If so we'll need to break and stop + // removing instructions. Also update the number of insts to remove + // from the queue. + if (exactFullStall) { + bool stall = false; + if (IQ.isFull()) { + DPRINTF(BE, "IQ is full!\n"); + stall = true; + } else if (LSQ.isFull()) { + DPRINTF(BE, "LSQ is full!\n"); + stall = true; + } else if (isFull()) { + DPRINTF(BE, "ROB is full!\n"); + stall = true; + ROB_fcount++; + } + if (stall) { + instsToDispatch->size-= i+1; + dispatchStall(); + return; + } + } + } + + // Check if IQ or LSQ is full. If so we'll need to break and stop + // removing instructions. Also update the number of insts to remove + // from the queue. Check here if we don't care about exact stall + // conditions. + + bool stall = false; + if (IQ.isFull()) { + DPRINTF(BE, "IQ is full!\n"); + stall = true; + } else if (LSQ.isFull()) { + DPRINTF(BE, "LSQ is full!\n"); + stall = true; + } else if (isFull()) { + DPRINTF(BE, "ROB is full!\n"); + stall = true; + ROB_fcount++; + } + if (stall) { + d2i.advance(); + dispatchStall(); + return; + } +} + +template <class Impl> +void +BackEnd<Impl>::dispatchStall() +{ + dispatchStatus = Blocked; + if (!cpu->decoupledFrontEnd) { + // Tell front end to stall here through a timebuffer, or just tell + // it directly. + } +} + +template <class Impl> +void +BackEnd<Impl>::checkDispatchStatus() +{ + DPRINTF(BE, "Checking dispatch status\n"); + assert(dispatchStatus == Blocked); + if (!IQ.isFull() && !LSQ.isFull() && !isFull()) { + DPRINTF(BE, "Dispatch no longer blocked\n"); + dispatchStatus = Running; + dispatchInsts(); + } +} + +template <class Impl> +void +BackEnd<Impl>::scheduleReadyInsts() +{ + // Tell IQ to put any ready instructions into the instruction list. + // Probably want to have a list of DynInstPtrs returned here. Then I + // can choose to either put them into a time buffer to simulate + // IQ scheduling time, or hand them directly off to the next stage. + // Do you ever want to directly hand it off to the next stage? + DPRINTF(BE, "Trying to schedule ready instructions\n"); + IQ.scheduleReadyInsts(); +} + +template <class Impl> +void +BackEnd<Impl>::executeInsts() +{ + int insts_to_execute = instsToExecute->size; + + issued_ops[0]+= insts_to_execute; + n_issued_dist[insts_to_execute]++; + + DPRINTF(BE, "Trying to execute %i instructions\n", insts_to_execute); + + fetchRedirect[0] = false; + + while (insts_to_execute > 0) { + // Get ready instruction from the IQ (or queue coming out of IQ) + // Execute the ready instruction. + // Wakeup any dependents if it's done. + DynInstPtr inst = IQ.getReadyInst(); + + DPRINTF(BE, "Executing inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + + ++funcExeInst; + + // Check if the instruction is squashed; if so then skip it + // and don't count it towards the FU usage. + if (inst->isSquashed()) { + DPRINTF(BE, "Execute: Instruction was squashed.\n"); + + // Not sure how to handle this plus the method of sending # of + // instructions to use. Probably will just have to count it + // towards the bandwidth usage, but not the FU usage. + --insts_to_execute; + + // Consider this instruction executed so that commit can go + // ahead and retire the instruction. + inst->setExecuted(); + + // Not sure if I should set this here or just let commit try to + // commit any squashed instructions. I like the latter a bit more. + inst->setCanCommit(); + +// ++iewExecSquashedInsts; + + continue; + } + + Fault fault = NoFault; + + // Execute instruction. + // Note that if the instruction faults, it will be handled + // at the commit stage. + if (inst->isMemRef() && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(BE, "Execute: Initiating access for memory " + "reference.\n"); + + // Tell the LDSTQ to execute this instruction (if it is a load). + if (inst->isLoad()) { + // Loads will mark themselves as executed, and their writeback + // event adds the instruction to the queue to commit + fault = LSQ.executeLoad(inst); + +// ++iewExecLoadInsts; + } else if (inst->isStore()) { + LSQ.executeStore(inst); + +// ++iewExecStoreInsts; + + if (!(inst->req->flags & LOCKED)) { + inst->setExecuted(); + + instToCommit(inst); + } + // Store conditionals will mark themselves as executed, and + // their writeback event will add the instruction to the queue + // to commit. + } else { + panic("Unexpected memory type!\n"); + } + + } else { + inst->execute(); + +// ++iewExecutedInsts; + + inst->setExecuted(); + + instToCommit(inst); + } + + updateExeInstStats(inst); + + // Probably should have some sort of function for this. + // More general question of how to handle squashes? Have some sort of + // squash unit that controls it? Probably... + // Check if branch was correct. This check happens after the + // instruction is added to the queue because even if the branch + // is mispredicted, the branch instruction itself is still valid. + // Only handle this if there hasn't already been something that + // redirects fetch in this group of instructions. + + // This probably needs to prioritize the redirects if a different + // scheduler is used. Currently the scheduler schedules the oldest + // instruction first, so the branch resolution order will be correct. + unsigned tid = inst->threadNumber; + + if (!fetchRedirect[tid]) { + + if (inst->mispredicted()) { + fetchRedirect[tid] = true; + + DPRINTF(BE, "Execute: Branch mispredict detected.\n"); + DPRINTF(BE, "Execute: Redirecting fetch to PC: %#x.\n", + inst->nextPC); + + // If incorrect, then signal the ROB that it must be squashed. + squashDueToBranch(inst); + + if (inst->predTaken()) { +// predictedTakenIncorrect++; + } else { +// predictedNotTakenIncorrect++; + } + } else if (LSQ.violation()) { + fetchRedirect[tid] = true; + + // Get the DynInst that caused the violation. Note that this + // clears the violation signal. + DynInstPtr violator; + violator = LSQ.getMemDepViolator(); + + DPRINTF(BE, "LDSTQ detected a violation. Violator PC: " + "%#x, inst PC: %#x. Addr is: %#x.\n", + violator->readPC(), inst->readPC(), inst->physEffAddr); + + // Tell the instruction queue that a violation has occured. +// IQ.violation(inst, violator); + + // Squash. +// squashDueToMemOrder(inst,tid); + squashDueToBranch(inst); + +// ++memOrderViolationEvents; + } else if (LSQ.loadBlocked()) { + fetchRedirect[tid] = true; + + DPRINTF(BE, "Load operation couldn't execute because the " + "memory system is blocked. PC: %#x [sn:%lli]\n", + inst->readPC(), inst->seqNum); + + squashDueToMemBlocked(inst); + } + } + +// instList.pop_front(); + + --insts_to_execute; + + // keep an instruction count + thread->numInst++; + thread->numInsts++; + } + + assert(insts_to_execute >= 0); +} + +template<class Impl> +void +BackEnd<Impl>::instToCommit(DynInstPtr &inst) +{ + int wb_width = wbWidth; + // First check the time slot that this instruction will write + // to. If there are free write ports at the time, then go ahead + // and write the instruction to that time. If there are not, + // keep looking back to see where's the first time there's a + // free slot. What happens if you run out of free spaces? + // For now naively assume that all instructions take one cycle. + // Otherwise would have to look into the time buffer based on the + // latency of the instruction. + + DPRINTF(BE, "Sending instructions to commit [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + while (numInstsToWB[wbCycle].size >= wb_width) { + ++wbCycle; + + assert(wbCycle < 5); + } + + // Add finished instruction to queue to commit. + writeback.push_back(inst); + numInstsToWB[wbCycle].size++; + + if (wbCycle) + wb_penalized[0]++; +} + +template <class Impl> +void +BackEnd<Impl>::writebackInsts() +{ + int wb_width = wbWidth; + // Using this method I'm not quite sure how to prevent an + // instruction from waking its own dependents multiple times, + // without the guarantee that commit always has enough bandwidth + // to accept all instructions being written back. This guarantee + // might not be too unrealistic. + InstListIt wb_inst_it = writeback.begin(); + InstListIt wb_end_it = writeback.end(); + int inst_num = 0; + int consumer_insts = 0; + + for (; inst_num < wb_width && + wb_inst_it != wb_end_it; inst_num++) { + DynInstPtr inst = (*wb_inst_it); + + // Some instructions will be sent to commit without having + // executed because they need commit to handle them. + // E.g. Uncached loads have not actually executed when they + // are first sent to commit. Instead commit must tell the LSQ + // when it's ready to execute the uncached load. + if (!inst->isSquashed()) { + DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + inst->setCanCommit(); + inst->setResultReady(); + + if (inst->isExecuted()) { + int dependents = IQ.wakeDependents(inst); + if (dependents) { + producer_inst[0]++; + consumer_insts+= dependents; + } + } + } + + writeback.erase(wb_inst_it++); + } + LSQ.writebackStores(); + consumer_inst[0]+= consumer_insts; + writeback_count[0]+= inst_num; +} + +template <class Impl> +bool +BackEnd<Impl>::commitInst(int inst_num) +{ + // Read instruction from the head of the ROB + DynInstPtr inst = instList.front(); + + // Make sure instruction is valid + assert(inst); + + if (!inst->readyToCommit()) + return false; + + DPRINTF(BE, "Trying to commit instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + // If the instruction is not executed yet, then it is a non-speculative + // or store inst. Signal backwards that it should be executed. + if (!inst->isExecuted()) { + // Keep this number correct. We have not yet actually executed + // and committed this instruction. +// thread->funcExeInst--; + + if (inst->isNonSpeculative()) { +#if !FULL_SYSTEM + // Hack to make sure syscalls aren't executed until all stores + // write back their data. This direct communication shouldn't + // be used for anything other than this. + if (inst_num > 0 || LSQ.hasStoresToWB()) { + DPRINTF(BE, "Waiting for all stores to writeback.\n"); + return false; + } +#endif + + DPRINTF(BE, "Encountered a store or non-speculative " + "instruction at the head of the ROB, PC %#x.\n", + inst->readPC()); + + // Send back the non-speculative instruction's sequence number. + toIEW->nonSpecSeqNum = inst->seqNum; + + // Change the instruction so it won't try to commit again until + // it is executed. + inst->clearCanCommit(); + +// ++commitNonSpecStalls; + + return false; + } else if (inst->isLoad()) { + DPRINTF(BE, "[sn:%lli]: Uncached load, PC %#x.\n", + inst->seqNum, inst->readPC()); + + // Send back the non-speculative instruction's sequence + // number. Maybe just tell the lsq to re-execute the load. + toIEW->nonSpecSeqNum = inst->seqNum; + toIEW->uncached = true; + toIEW->lqIdx = inst->lqIdx; + + inst->clearCanCommit(); + + return false; + } else { + panic("Trying to commit un-executed instruction " + "of unknown type!\n"); + } + } + + // Now check if it's one of the special trap or barrier or + // serializing instructions. + if (inst->isThreadSync()) + { + // Not handled for now. + panic("Barrier instructions are not handled yet.\n"); + } + + // Check if the instruction caused a fault. If so, trap. + Fault inst_fault = inst->getFault(); + + if (inst_fault != NoFault) { + if (!inst->isNop()) { +#if FULL_SYSTEM + DPRINTF(BE, "Inst [sn:%lli] PC %#x has a fault\n", + inst->seqNum, inst->readPC()); + +// assert(!thread->inSyscall); + +// thread->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + inst_fault->invoke(thread->getXCProxy()); + + // Exit state update mode to avoid accidental updating. +// thread->inSyscall = false; + +// commitStatus = TrapPending; + + // Generate trap squash event. +// generateTrapEvent(); + + return false; +#else // !FULL_SYSTEM + panic("fault (%d) detected @ PC %08p", inst_fault, + inst->PC); +#endif // FULL_SYSTEM + } + } + + if (inst->isControl()) { +// ++commitCommittedBranches; + } + + int freed_regs = 0; + + for (int i = 0; i < inst->numDestRegs(); ++i) { + DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", + (int)inst->destRegIdx(i), inst->seqNum); + thread->renameTable[inst->destRegIdx(i)] = inst; + ++freed_regs; + } + + if (inst->traceData) { + inst->traceData->finalize(); + inst->traceData = NULL; + } + + inst->clearDependents(); + + frontEnd->addFreeRegs(freed_regs); + + instList.pop_front(); + + --numInsts; + cpu->numInst++; + thread->numInsts++; + ++thread->funcExeInst; + thread->PC = inst->readNextPC(); + updateComInstStats(inst); + + // Write the done sequence number here. + toIEW->doneSeqNum = inst->seqNum; + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread->inSyscall && !thread->trapPending); + oldpc = thread->readPC(); + cpu->system->pcEventQueue.service( + thread->getXCProxy()); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + DPRINTF(BE, "PC skip function event, stopping commit\n"); +// completed_last_inst = false; +// squashPending = true; + return false; + } +#endif + return true; +} + +template <class Impl> +void +BackEnd<Impl>::commitInsts() +{ + int commit_width = commitWidth ? commitWidth : width; + + // Not sure this should be a loop or not. + int inst_num = 0; + while (!instList.empty() && inst_num < commit_width) { + if (instList.front()->isSquashed()) { + panic("No squashed insts should still be on the list!"); + instList.front()->clearDependents(); + instList.pop_front(); + continue; + } + + if (!commitInst(inst_num++)) { + break; + } + } + n_committed_dist.sample(inst_num); +} + +template <class Impl> +void +BackEnd<Impl>::squash(const InstSeqNum &sn) +{ + IQ.squash(sn); + LSQ.squash(sn); + + int freed_regs = 0; + InstListIt dispatch_end = dispatch.end(); + InstListIt insts_it = dispatch.end(); + insts_it--; + + while (insts_it != dispatch_end && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + --insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on dispatch list PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*insts_it)->setSquashed(); + + (*insts_it)->setCanCommit(); + + // Be careful with IPRs and such here + for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { + DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); + DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", + (int)(*insts_it)->destRegIdx(i), prev_dest); + renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; + ++freed_regs; + } + + (*insts_it)->clearDependents(); + + --insts_it; + } + + insts_it = instList.end(); + insts_it--; + + while (!instList.empty() && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + --insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on inst list PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*insts_it)->setSquashed(); + + (*insts_it)->setCanCommit(); + + for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { + DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); + DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", + (int)(*insts_it)->destRegIdx(i), prev_dest); + renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; + ++freed_regs; + } + + (*insts_it)->clearDependents(); + + instList.erase(insts_it--); + --numInsts; + } + + frontEnd->addFreeRegs(freed_regs); +} + +template <class Impl> +void +BackEnd<Impl>::squashFromXC() +{ + xcSquash = true; +} + +template <class Impl> +void +BackEnd<Impl>::squashDueToBranch(DynInstPtr &inst) +{ + // Update the branch predictor state I guess + squash(inst->seqNum); + frontEnd->squash(inst->seqNum, inst->readNextPC(), + true, inst->mispredicted()); +} + +template <class Impl> +void +BackEnd<Impl>::squashDueToMemBlocked(DynInstPtr &inst) +{ + DPRINTF(IEW, "Memory blocked, squashing load and younger insts, " + "PC: %#x [sn:%i].\n", inst->readPC(), inst->seqNum); + + squash(inst->seqNum - 1); + frontEnd->squash(inst->seqNum - 1, inst->readPC()); +} + +template <class Impl> +void +BackEnd<Impl>::fetchFault(Fault &fault) +{ + faultFromFetch = fault; +} + +template <class Impl> +void +BackEnd<Impl>::updateExeInstStats(DynInstPtr &inst) +{ + int thread_number = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) + exe_swp[thread_number]++; + else + exe_inst[thread_number]++; +#else + exe_inst[thread_number]++; +#endif + + // + // Control operations + // + if (inst->isControl()) + exe_branches[thread_number]++; + + // + // Memory operations + // + if (inst->isMemRef()) { + exe_refs[thread_number]++; + + if (inst->isLoad()) + exe_loads[thread_number]++; + } +} + +template <class Impl> +void +BackEnd<Impl>::updateComInstStats(DynInstPtr &inst) +{ + unsigned thread = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) { + stat_com_swp[thread]++; + } else { + stat_com_inst[thread]++; + } +#else + stat_com_inst[thread]++; +#endif + + // + // Control Instructions + // + if (inst->isControl()) + stat_com_branches[thread]++; + + // + // Memory references + // + if (inst->isMemRef()) { + stat_com_refs[thread]++; + + if (inst->isLoad()) { + stat_com_loads[thread]++; + } + } + + if (inst->isMemBarrier()) { + stat_com_membars[thread]++; + } +} + +template <class Impl> +void +BackEnd<Impl>::dumpInsts() +{ + int num = 0; + int valid_num = 0; + + InstListIt inst_list_it = instList.begin(); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } + + cprintf("Dispatch list size: %i\n", dispatch.size()); + + inst_list_it = dispatch.begin(); + + while (inst_list_it != dispatch.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } + + cprintf("Writeback list size: %i\n", writeback.size()); + + inst_list_it = writeback.begin(); + + while (inst_list_it != writeback.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } +} diff --git a/src/cpu/ozone/base_dyn_inst.cc b/src/cpu/ozone/base_dyn_inst.cc new file mode 100644 index 000000000..5a3a69dff --- /dev/null +++ b/src/cpu/ozone/base_dyn_inst.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/base_dyn_inst_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +// Explicit instantiation +template class BaseDynInst<OzoneImpl>; + +template <> +int +BaseDynInst<OzoneImpl>::instcount = 0; diff --git a/src/cpu/ozone/bpred_unit.cc b/src/cpu/ozone/bpred_unit.cc new file mode 100644 index 000000000..c823f5e80 --- /dev/null +++ b/src/cpu/ozone/bpred_unit.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/bpred_unit_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +template class BPredUnit<OzoneImpl>; +//template class BPredUnit<SimpleImpl>; diff --git a/src/cpu/ozone/checker_builder.cc b/src/cpu/ozone/checker_builder.cc new file mode 100644 index 000000000..c372e51d6 --- /dev/null +++ b/src/cpu/ozone/checker_builder.cc @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu_impl.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class MemObject; + +template +class Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > >; + +/** + * Specific non-templated derived class used for SimObject configuration. + */ +class OzoneChecker : + public Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > > +{ + public: + OzoneChecker(Params *p) + : Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > >(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// CheckerCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(OzoneChecker) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + SimObjectParam<System *> system; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + Param<int> clock; + + Param<bool> defer_registration; + Param<bool> exitOnError; + Param<bool> warnOnlyOnLoadError; + Param<bool> function_trace; + Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(OzoneChecker) + +BEGIN_INIT_SIM_OBJECT_PARAMS(OzoneChecker) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(system, "system object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(exitOnError, "exit on error"), + INIT_PARAM_DFLT(warnOnlyOnLoadError, "warn, but don't exit, if a load " + "result errors", false), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(OzoneChecker) + + +CREATE_SIM_OBJECT(OzoneChecker) +{ + OzoneChecker::Params *params = new OzoneChecker::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = 0; + params->max_insts_all_threads = 0; + params->max_loads_any_thread = 0; + params->max_loads_all_threads = 0; + params->exitOnError = exitOnError; + params->warnOnlyOnLoadError = warnOnlyOnLoadError; + params->deferRegistration = defer_registration; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->clock = clock; + // Hack to touch all parameters. Consider not deriving Checker + // from BaseCPU..it's not really a CPU in the end. + Counter temp; + temp = max_insts_any_thread; + temp = max_insts_all_threads; + temp = max_loads_any_thread; + temp = max_loads_all_threads; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->system = system; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + OzoneChecker *cpu = new OzoneChecker(params); + return cpu; +} + +REGISTER_SIM_OBJECT("OzoneChecker", OzoneChecker) diff --git a/src/cpu/ozone/cpu.cc b/src/cpu/ozone/cpu.cc index cbeca9d3b..eb6ac37bd 100644 --- a/src/cpu/ozone/cpu.cc +++ b/src/cpu/ozone/cpu.cc @@ -24,10 +24,14 @@ * 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. + * + * Authors: Kevin Lim + * Nathan Binkert */ -#include "cpu/ooo_cpu/ooo_cpu_impl.hh" -#include "cpu/ooo_cpu/ooo_dyn_inst.hh" -#include "cpu/ooo_cpu/ooo_impl.hh" +#include "cpu/ozone/cpu_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" -template class OoOCPU<OoOImpl>; +//template class OzoneCPU<SimpleImpl>; +template class OzoneCPU<OzoneImpl>; diff --git a/src/cpu/ozone/cpu.hh b/src/cpu/ozone/cpu.hh index fa849bb09..f726ac99b 100644 --- a/src/cpu/ozone/cpu.hh +++ b/src/cpu/ozone/cpu.hh @@ -24,32 +24,45 @@ * 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. + * + * Authors: Kevin Lim */ -#ifndef __CPU_OOO_CPU_OOO_CPU_HH__ -#define __CPU_OOO_CPU_OOO_CPU_HH__ +#ifndef __CPU_OZONE_CPU_HH__ +#define __CPU_OZONE_CPU_HH__ + +#include <set> #include "base/statistics.hh" +#include "base/timebuf.hh" #include "config/full_system.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" -#include "encumbered/cpu/full/fu_pool.hh" -#include "cpu/ooo_cpu/ea_list.hh" +#include "cpu/thread_context.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" #include "cpu/pc_event.hh" #include "cpu/static_inst.hh" -#include "mem/mem_interface.hh" +#include "mem/page_table.hh" #include "sim/eventq.hh" // forward declarations #if FULL_SYSTEM -class Processor; +#include "arch/alpha/tlb.hh" + class AlphaITB; class AlphaDTB; class PhysicalMemory; +class MemoryController; +class Sampler; class RemoteGDB; class GDBListener; +namespace Kernel { + class Statistics; +}; + #else class Process; @@ -57,12 +70,17 @@ class Process; #endif // FULL_SYSTEM class Checkpoint; -class MemInterface; +class EndQuiesceEvent; +class MemObject; +class Request; namespace Trace { class InstRecord; } +template <class> +class Checker; + /** * Declaration of Out-of-Order CPU class. Basically it is a SimpleCPU with * simple out-of-order capabilities added to it. It is still a 1 CPI machine @@ -72,23 +90,205 @@ namespace Trace { */ template <class Impl> -class OoOCPU : public BaseCPU +class OzoneCPU : public BaseCPU { private: + typedef typename Impl::FrontEnd FrontEnd; + typedef typename Impl::BackEnd BackEnd; typedef typename Impl::DynInst DynInst; typedef typename Impl::DynInstPtr DynInstPtr; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + + public: + class OzoneTC : public ThreadContext { + public: + OzoneCPU<Impl> *cpu; + + OzoneThreadState<Impl> *thread; + + BaseCPU *getCpuPtr(); + + void setCpuId(int id); + + int readCpuId() { return thread->readCpuId(); } + +#if FULL_SYSTEM + System *getSystemPtr() { return cpu->system; } + + PhysicalMemory *getPhysMemPtr() { return cpu->physmem; } + + AlphaITB *getITBPtr() { return cpu->itb; } + + AlphaDTB * getDTBPtr() { return cpu->dtb; } + + Kernel::Statistics *getKernelStats() + { return thread->getKernelStats(); } + + FunctionalPort *getPhysPort() { return thread->getPhysPort(); } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) + { return thread->getVirtPort(tc); } + + void delVirtPort(VirtualPort *vp); +#else + TranslatingPort *getMemPort() { return thread->getMemPort(); } + + Process *getProcessPtr() { return thread->getProcessPtr(); } +#endif + + Status status() const { return thread->status(); } + + void setStatus(Status new_status); + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1); + + /// Set the status to Suspended. + void suspend(); + + /// Set the status to Unallocated. + void deallocate(); + + /// Set the status to Halted. + void halt(); + +#if FULL_SYSTEM + void dumpFuncProfile(); +#endif + + void takeOverFrom(ThreadContext *old_context); + + void regStats(const std::string &name); + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + +#if FULL_SYSTEM + EndQuiesceEvent *getQuiesceEvent(); + + Tick readLastActivate(); + Tick readLastSuspend(); + + void profileClear(); + void profileSample(); +#endif + + int getThreadNum(); + + // Also somewhat obnoxious. Really only used for the TLB fault. + TheISA::MachInst getInst(); + + void copyArchRegs(ThreadContext *tc); + + void clearArchRegs(); + + uint64_t readIntReg(int reg_idx); + + FloatReg readFloatReg(int reg_idx, int width); + + FloatReg readFloatReg(int reg_idx); + + FloatRegBits readFloatRegBits(int reg_idx, int width); + + FloatRegBits readFloatRegBits(int reg_idx); + + void setIntReg(int reg_idx, uint64_t val); + + void setFloatReg(int reg_idx, FloatReg val, int width); + + void setFloatReg(int reg_idx, FloatReg val); + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width); + + void setFloatRegBits(int reg_idx, FloatRegBits val); + + uint64_t readPC() { return thread->PC; } + void setPC(Addr val); + + uint64_t readNextPC() { return thread->nextPC; } + void setNextPC(Addr val); + + uint64_t readNextNPC() + { + return 0; + } + + void setNextNPC(uint64_t val) + { } + + public: + // ISA stuff: + MiscReg readMiscReg(int misc_reg); + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault); + + Fault setMiscReg(int misc_reg, const MiscReg &val); + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + + unsigned readStCondFailures() + { return thread->storeCondFailures; } + + void setStCondFailures(unsigned sc_failures) + { thread->storeCondFailures = sc_failures; } + +#if FULL_SYSTEM + bool inPalMode() { return cpu->inPalMode(); } +#endif + + bool misspeculating() { return false; } + +#if !FULL_SYSTEM + TheISA::IntReg getSyscallArg(int i) + { return thread->renameTable[TheISA::ArgumentReg0 + i]->readIntResult(); } + + // used to shift args for indirect syscall + void setSyscallArg(int i, TheISA::IntReg val) + { thread->renameTable[TheISA::ArgumentReg0 + i]->setIntResult(i); } + + void setSyscallReturn(SyscallReturn return_value) + { cpu->setSyscallReturn(return_value, thread->readTid()); } + + Counter readFuncExeInst() { return thread->funcExeInst; } + + void setFuncExeInst(Counter new_val) + { thread->funcExeInst = new_val; } +#endif + void changeRegFileContext(TheISA::RegFile::ContextParam param, + TheISA::RegFile::ContextVal val) + { panic("Not supported on Alpha!"); } + }; + + // Ozone specific thread context + OzoneTC ozoneTC; + // Thread context to be used + ThreadContext *tc; + // Checker thread context; will wrap the OzoneTC if a checker is + // being used. + ThreadContext *checkerTC; + + typedef OzoneThreadState<Impl> ImplState; + + private: + OzoneThreadState<Impl> thread; + public: // main simulation loop (one cycle) void tick(); + std::set<InstSeqNum> snList; + std::set<Addr> lockAddrList; private: struct TickEvent : public Event { - OoOCPU *cpu; + OzoneCPU *cpu; int width; - TickEvent(OoOCPU *c, int w); + TickEvent(OzoneCPU *c, int w); void process(); const char *description(); }; @@ -99,9 +299,9 @@ class OoOCPU : public BaseCPU void scheduleTickEvent(int delay) { if (tickEvent.squashed()) - tickEvent.reschedule(curTick + delay); + tickEvent.reschedule(curTick + cycles(delay)); else if (!tickEvent.scheduled()) - tickEvent.schedule(curTick + delay); + tickEvent.schedule(curTick + cycles(delay)); } /// Unschedule tick event, regardless of its current state. @@ -118,20 +318,17 @@ class OoOCPU : public BaseCPU void trace_data(T data); public: - // enum Status { Running, Idle, - IcacheMiss, - IcacheMissComplete, - DcacheMissStall, SwitchedOut }; - private: Status _status; public: + bool checkInterrupts; + void post_interrupt(int int_num, int index); void zero_fill_64(Addr addr) { @@ -142,97 +339,50 @@ class OoOCPU : public BaseCPU } }; - struct Params : public BaseCPU::Params - { - MemInterface *icache_interface; - MemInterface *dcache_interface; - int width; -#if FULL_SYSTEM - AlphaITB *itb; - AlphaDTB *dtb; - FunctionalMemory *mem; -#else - Process *process; -#endif - int issueWidth; - }; + typedef typename Impl::Params Params; - OoOCPU(Params *params); + OzoneCPU(Params *params); - virtual ~OoOCPU(); + virtual ~OzoneCPU(); void init(); - private: - void copyFromXC(); - public: - // execution context - ExecContext *xc; + BaseCPU *getCpuPtr() { return this; } - void switchOut(); - void takeOverFrom(BaseCPU *oldCPU); - -#if FULL_SYSTEM - Addr dbg_vtophys(Addr addr); - - bool interval_stats; -#endif - - // L1 instruction cache - MemInterface *icacheInterface; + void setCpuId(int id) { cpuId = id; } - // L1 data cache - MemInterface *dcacheInterface; + int readCpuId() { return cpuId; } - FuncUnitPool *fuPool; + int cpuId; - // Refcounted pointer to the one memory request. - MemReqPtr cacheMemReq; - - class ICacheCompletionEvent : public Event - { - private: - OoOCPU *cpu; - - public: - ICacheCompletionEvent(OoOCPU *_cpu); - - virtual void process(); - virtual const char *description(); - }; - - // Will need to create a cache completion event upon any memory miss. - ICacheCompletionEvent iCacheCompletionEvent; + void switchOut(Sampler *sampler); + void signalSwitched(); + void takeOverFrom(BaseCPU *oldCPU); - class DCacheCompletionEvent; + Sampler *sampler; - typedef typename - std::list<DCacheCompletionEvent>::iterator DCacheCompEventIt; + int switchCount; - class DCacheCompletionEvent : public Event - { - private: - OoOCPU *cpu; - DynInstPtr inst; - DCacheCompEventIt dcceIt; +#if FULL_SYSTEM + Addr dbg_vtophys(Addr addr); - public: - DCacheCompletionEvent(OoOCPU *_cpu, DynInstPtr &_inst, - DCacheCompEventIt &_dcceIt); + bool interval_stats; - virtual void process(); - virtual const char *description(); - }; + AlphaITB *itb; + AlphaDTB *dtb; + System *system; + PhysicalMemory *physmem; +#endif - friend class DCacheCompletionEvent; + MemObject *mem; - protected: - std::list<DCacheCompletionEvent> dCacheCompList; - DCacheCompEventIt dcceIt; + FrontEnd *frontEnd; + BackEnd *backEnd; private: Status status() const { return _status; } + void setStatus(Status new_status) { _status = new_status; } virtual void activateContext(int thread_num, int delay); virtual void suspendContext(int thread_num); @@ -244,18 +394,16 @@ class OoOCPU : public BaseCPU virtual void resetStats(); // number of simulated instructions + public: Counter numInst; Counter startNumInst; - Stats::Scalar<> numInsts; virtual Counter totalInstructions() const { return numInst - startNumInst; } - // number of simulated memory references - Stats::Scalar<> numMemRefs; - + private: // number of simulated loads Counter numLoad; Counter startNumLoad; @@ -263,376 +411,217 @@ class OoOCPU : public BaseCPU // number of idle cycles Stats::Average<> notIdleFraction; Stats::Formula idleFraction; - - // number of cycles stalled for I-cache misses - Stats::Scalar<> icacheStallCycles; - Counter lastIcacheStall; - - // number of cycles stalled for D-cache misses - Stats::Scalar<> dcacheStallCycles; - Counter lastDcacheStall; - - void processICacheCompletion(); - public: virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); -#if FULL_SYSTEM - bool validInstAddr(Addr addr) { return true; } - bool validDataAddr(Addr addr) { return true; } - int getInstAsid() { return xc->regs.instAsid(); } - int getDataAsid() { return xc->regs.dataAsid(); } - Fault translateInstReq(MemReqPtr &req) +#if FULL_SYSTEM + /** Translates instruction requestion. */ + Fault translateInstReq(RequestPtr &req, OzoneThreadState<Impl> *thread) { - return itb->translate(req); + return itb->translate(req, thread->getTC()); } - Fault translateDataReadReq(MemReqPtr &req) + /** Translates data read request. */ + Fault translateDataReadReq(RequestPtr &req, OzoneThreadState<Impl> *thread) { - return dtb->translate(req, false); + return dtb->translate(req, thread->getTC(), false); } - Fault translateDataWriteReq(MemReqPtr &req) + /** Translates data write request. */ + Fault translateDataWriteReq(RequestPtr &req, OzoneThreadState<Impl> *thread) { - return dtb->translate(req, true); + return dtb->translate(req, thread->getTC(), true); } #else - bool validInstAddr(Addr addr) - { return xc->validInstAddr(addr); } - - bool validDataAddr(Addr addr) - { return xc->validDataAddr(addr); } - - int getInstAsid() { return xc->asid; } - int getDataAsid() { return xc->asid; } - - Fault dummyTranslation(MemReqPtr &req) - { -#if 0 - assert((req->vaddr >> 48 & 0xffff) == 0); -#endif - - // put the asid in the upper 16 bits of the paddr - req->paddr = req->vaddr & ~((Addr)0xffff << sizeof(Addr) * 8 - 16); - req->paddr = req->paddr | (Addr)req->asid << sizeof(Addr) * 8 - 16; - return NoFault; - } - Fault translateInstReq(MemReqPtr &req) + /** Translates instruction requestion in syscall emulation mode. */ + Fault translateInstReq(RequestPtr &req, OzoneThreadState<Impl> *thread) { - return dummyTranslation(req); + return thread->getProcessPtr()->pTable->translate(req); } - Fault translateDataReadReq(MemReqPtr &req) + + /** Translates data read request in syscall emulation mode. */ + Fault translateDataReadReq(RequestPtr &req, OzoneThreadState<Impl> *thread) { - return dummyTranslation(req); + return thread->getProcessPtr()->pTable->translate(req); } - Fault translateDataWriteReq(MemReqPtr &req) + + /** Translates data write request in syscall emulation mode. */ + Fault translateDataWriteReq(RequestPtr &req, OzoneThreadState<Impl> *thread) { - return dummyTranslation(req); + return thread->getProcessPtr()->pTable->translate(req); } - #endif + /** Old CPU read from memory function. No longer used. */ template <class T> - Fault read(Addr addr, T &data, unsigned flags, DynInstPtr inst); - - template <class T> - Fault write(T data, Addr addr, unsigned flags, - uint64_t *res, DynInstPtr inst); - - void prefetch(Addr addr, unsigned flags) + Fault read(Request *req, T &data) { - // need to do this... - } - - void writeHint(Addr addr, int size, unsigned flags) - { - // need to do this... - } - - Fault copySrcTranslate(Addr src); - - Fault copy(Addr dest); - - private: - bool executeInst(DynInstPtr &inst); - - void renameInst(DynInstPtr &inst); - - void addInst(DynInstPtr &inst); - - void commitHeadInst(); - - bool getOneInst(); - - Fault fetchCacheLine(); - - InstSeqNum getAndIncrementInstSeq(); - - bool ambigMemAddr; - - private: - InstSeqNum globalSeqNum; - - DynInstPtr renameTable[TheISA::TotalNumRegs]; - DynInstPtr commitTable[TheISA::TotalNumRegs]; - - // Might need a table of the shadow registers as well. -#if FULL_SYSTEM - DynInstPtr palShadowTable[TheISA::NumIntRegs]; +#if 0 +#if FULL_SYSTEM && defined(TARGET_ALPHA) + if (req->flags & LOCKED) { + req->xc->setMiscReg(TheISA::Lock_Addr_DepTag, req->paddr); + req->xc->setMiscReg(TheISA::Lock_Flag_DepTag, true); + } +#endif + if (req->flags & LOCKED) { + lockAddrList.insert(req->paddr); + lockFlag = true; + } #endif + Fault error; - public: - // The register accessor methods provide the index of the - // instruction's operand (e.g., 0 or 1), not the architectural - // register index, to simplify the implementation of register - // renaming. We find the architectural register index by indexing - // into the instruction's own operand index table. Note that a - // raw pointer to the StaticInst is provided instead of a - // ref-counted StaticInstPtr to redice overhead. This is fine as - // long as these methods don't copy the pointer into any long-term - // storage (which is pretty hard to imagine they would have reason - // to do). - - // In the OoO case these shouldn't read from the XC but rather from the - // rename table of DynInsts. Also these likely shouldn't be called very - // often, other than when adding things into the xc during say a syscall. - - uint64_t readIntReg(StaticInst *si, int idx) - { - return xc->readIntReg(si->srcRegIdx(idx)); + error = this->mem->read(req, data); + data = gtoh(data); + return error; } - FloatReg readFloatReg(StaticInst *si, int idx, width) - { - int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return xc->readFloatReg(reg_idx, width); - } - FloatReg readFloatReg(StaticInst *si, int idx) + /** CPU read function, forwards read to LSQ. */ + template <class T> + Fault read(Request *req, T &data, int load_idx) { - int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return xc->readFloatReg(reg_idx); + return backEnd->read(req, data, load_idx); } - FloatRegBits readFloatRegBits(StaticInst *si, int idx, int width) + /** Old CPU write to memory function. No longer used. */ + template <class T> + Fault write(Request *req, T &data) { - int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return xc->readFloatRegBits(reg_idx, width); - } +#if 0 +#if FULL_SYSTEM && defined(TARGET_ALPHA) + ExecContext *xc; + + // If this is a store conditional, act appropriately + if (req->flags & LOCKED) { + xc = req->xc; + + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + xc->setStCondFailures(0);//Needed? [RGD] + } else { + bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag); + Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag); + req->result = lock_flag; + if (!lock_flag || + ((lock_addr & ~0xf) != (req->paddr & ~0xf))) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + xc->setStCondFailures(xc->readStCondFailures() + 1); + if (((xc->readStCondFailures()) % 100000) == 0) { + std::cerr << "Warning: " + << xc->readStCondFailures() + << " consecutive store conditional failures " + << "on cpu " << req->xc->readCpuId() + << std::endl; + } + return NoFault; + } + else xc->setStCondFailures(0); + } + } - FloatRegBits readFloatRegBits(StaticInst *si, int idx) - { - int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return xc->readFloatRegBits(reg_idx); - } + // Need to clear any locked flags on other proccessors for + // this address. Only do this for succsful Store Conditionals + // and all other stores (WH64?). Unsuccessful Store + // Conditionals would have returned above, and wouldn't fall + // through. + for (int i = 0; i < this->system->threadContexts.size(); i++){ + xc = this->system->threadContexts[i]; + if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) == + (req->paddr & ~0xf)) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + } + } - void setIntReg(StaticInst *si, int idx, uint64_t val) - { - xc->setIntReg(si->destRegIdx(idx), val); - } +#endif - void setFloatReg(StaticInst *si, int idx, FloatReg val, int width) - { - int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - xc->setFloatReg(reg_idx, val, width); - } + if (req->flags & LOCKED) { + if (req->flags & UNCACHEABLE) { + req->result = 2; + } else { + if (this->lockFlag) { + if (lockAddrList.find(req->paddr) != + lockAddrList.end()) { + req->result = 1; + } else { + req->result = 0; + return NoFault; + } + } else { + req->result = 0; + return NoFault; + } + } + } +#endif - void setFloatReg(StaticInst *si, int idx, FloatReg val) - { - int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - xc->setFloatReg(reg_idx, val); + return this->mem->write(req, (T)htog(data)); } - void setFloatRegBits(StaticInst *si, int idx, FloatRegBits val, int width) + /** CPU write function, forwards write to LSQ. */ + template <class T> + Fault write(Request *req, T &data, int store_idx) { - int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - xc->setFloatRegBits(reg_idx, val, width); + return backEnd->write(req, data, store_idx); } - void setFloatRegBits(StaticInst *si, int idx, FloatRegBits val) + void prefetch(Addr addr, unsigned flags) { - int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - xc->setFloatRegBits(reg_idx, val); + // need to do this... } - uint64_t readPC() { return PC; } - void setNextPC(Addr val) { nextPC = val; } - - private: - Addr PC; - Addr nextPC; - - unsigned issueWidth; - - bool fetchRedirExcp; - bool fetchRedirBranch; - - /** Mask to get a cache block's address. */ - Addr cacheBlkMask; - - unsigned cacheBlkSize; - - Addr cacheBlkPC; - - /** The cache line being fetched. */ - uint8_t *cacheData; - - protected: - bool cacheBlkValid; - - private: - - // Align an address (typically a PC) to the start of an I-cache block. - // We fold in the PISA 64- to 32-bit conversion here as well. - Addr icacheBlockAlignPC(Addr addr) + void writeHint(Addr addr, int size, unsigned flags) { - addr = TheISA::realPCToFetchPC(addr); - return (addr & ~(cacheBlkMask)); + // need to do this... } - unsigned instSize; + Fault copySrcTranslate(Addr src); - // ROB tracking stuff. - DynInstPtr robHeadPtr; - DynInstPtr robTailPtr; - unsigned robSize; - unsigned robInsts; + Fault copy(Addr dest); - // List of outstanding EA instructions. - protected: - EAList eaList; + InstSeqNum globalSeqNum; public: - void branchToTarget(Addr val) - { - if (!fetchRedirExcp) { - fetchRedirBranch = true; - PC = val; - } - } - - // ISA stuff: - uint64_t readUniq() { return xc->readUniq(); } - void setUniq(uint64_t val) { xc->setUniq(val); } + void squashFromTC(); - uint64_t readFpcr() { return xc->readFpcr(); } - void setFpcr(uint64_t val) { xc->setFpcr(val); } + // @todo: This can be a useful debug function. Implement it. + void dumpInsts() { frontEnd->dumpInsts(); } #if FULL_SYSTEM - uint64_t readIpr(int idx, Fault &fault) { return xc->readIpr(idx, fault); } - Fault setIpr(int idx, uint64_t val) { return xc->setIpr(idx, val); } - Fault hwrei() { return xc->hwrei(); } - int readIntrFlag() { return xc->readIntrFlag(); } - void setIntrFlag(int val) { xc->setIntrFlag(val); } - bool inPalMode() { return xc->inPalMode(); } - void trap(Fault fault) { fault->invoke(xc); } - bool simPalCheck(int palFunc) { return xc->simPalCheck(palFunc); } + Fault hwrei(); + int readIntrFlag() { return thread.intrflag; } + void setIntrFlag(int val) { thread.intrflag = val; } + bool inPalMode() { return AlphaISA::PcPAL(thread.PC); } + bool inPalMode(Addr pc) { return AlphaISA::PcPAL(pc); } + bool simPalCheck(int palFunc); + void processInterrupts(); #else - void syscall() { xc->syscall(); } -#endif - - ExecContext *xcBase() { return xc; } -}; - - -// precise architected memory state accessor macros -template <class Impl> -template <class T> -Fault -OoOCPU<Impl>::read(Addr addr, T &data, unsigned flags, DynInstPtr inst) -{ - MemReqPtr readReq = new MemReq(); - readReq->xc = xc; - readReq->asid = 0; - readReq->data = new uint8_t[64]; - - readReq->reset(addr, sizeof(T), flags); - - // translate to physical address - This might be an ISA impl call - Fault fault = translateDataReadReq(readReq); - - // do functional access - if (fault == NoFault) - fault = xc->mem->read(readReq, data); -#if 0 - if (traceData) { - traceData->setAddr(addr); - if (fault == NoFault) - traceData->setData(data); - } + void syscall(uint64_t &callnum); + void setSyscallReturn(SyscallReturn return_value, int tid); #endif - // if we have a cache, do cache access too - if (fault == NoFault && dcacheInterface) { - readReq->cmd = Read; - readReq->completionEvent = NULL; - readReq->time = curTick; - /*MemAccessResult result = */dcacheInterface->access(readReq); - - if (dcacheInterface->doEvents()) { - readReq->completionEvent = new DCacheCompletionEvent(this, inst, - dcceIt); - } - } - - if (!dcacheInterface && (readReq->flags & UNCACHEABLE)) - recordEvent("Uncached Read"); + ThreadContext *tcBase() { return tc; } - return fault; -} + bool decoupledFrontEnd; + struct CommStruct { + InstSeqNum doneSeqNum; + InstSeqNum nonSpecSeqNum; + bool uncached; + unsigned lqIdx; -template <class Impl> -template <class T> -Fault -OoOCPU<Impl>::write(T data, Addr addr, unsigned flags, - uint64_t *res, DynInstPtr inst) -{ - MemReqPtr writeReq = new MemReq(); - writeReq->xc = xc; - writeReq->asid = 0; - writeReq->data = new uint8_t[64]; - -#if 0 - if (traceData) { - traceData->setAddr(addr); - traceData->setData(data); - } -#endif - - writeReq->reset(addr, sizeof(T), flags); - - // translate to physical address - Fault fault = translateDataWriteReq(writeReq); - - // do functional access - if (fault == NoFault) - fault = xc->write(writeReq, data); - - if (fault == NoFault && dcacheInterface) { - writeReq->cmd = Write; - memcpy(writeReq->data,(uint8_t *)&data,writeReq->size); - writeReq->completionEvent = NULL; - writeReq->time = curTick; - /*MemAccessResult result = */dcacheInterface->access(writeReq); - - if (dcacheInterface->doEvents()) { - writeReq->completionEvent = new DCacheCompletionEvent(this, inst, - dcceIt); - } - } - - if (res && (fault == NoFault)) - *res = writeReq->result; + bool stall; + }; + TimeBuffer<CommStruct> comm; - if (!dcacheInterface && (writeReq->flags & UNCACHEABLE)) - recordEvent("Uncached Write"); + bool lockFlag; - return fault; -} + Stats::Scalar<> quiesceCycles; + Checker<DynInstPtr> *checker; +}; -#endif // __CPU_OOO_CPU_OOO_CPU_HH__ +#endif // __CPU_OZONE_CPU_HH__ diff --git a/src/cpu/ozone/cpu_builder.cc b/src/cpu/ozone/cpu_builder.cc new file mode 100644 index 000000000..e239b7a94 --- /dev/null +++ b/src/cpu/ozone/cpu_builder.cc @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/cpu.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_params.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class DerivOzoneCPU : public OzoneCPU<OzoneImpl> +{ + public: + DerivOzoneCPU(SimpleParams *p) + : OzoneCPU<OzoneImpl>(p) + { } +}; + + +//////////////////////////////////////////////////////////////////////// +// +// OzoneCPU Simulation Object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(DerivOzoneCPU) + + Param<int> clock; + Param<int> numThreads; + +#if FULL_SYSTEM +SimObjectParam<System *> system; +Param<int> cpu_id; +SimObjectParam<AlphaITB *> itb; +SimObjectParam<AlphaDTB *> dtb; +#else +SimObjectVectorParam<Process *> workload; +//SimObjectParam<PageTable *> page_table; +#endif // FULL_SYSTEM + +SimObjectParam<MemObject *> mem; + +SimObjectParam<BaseCPU *> checker; + +Param<Counter> max_insts_any_thread; +Param<Counter> max_insts_all_threads; +Param<Counter> max_loads_any_thread; +Param<Counter> max_loads_all_threads; + +//SimObjectParam<BaseCache *> icache; +//SimObjectParam<BaseCache *> dcache; + +Param<unsigned> cachePorts; +Param<unsigned> width; +Param<unsigned> frontEndWidth; +Param<unsigned> backEndWidth; +Param<unsigned> backEndSquashLatency; +Param<unsigned> backEndLatency; +Param<unsigned> maxInstBufferSize; +Param<unsigned> numPhysicalRegs; +Param<unsigned> maxOutstandingMemOps; + +Param<unsigned> decodeToFetchDelay; +Param<unsigned> renameToFetchDelay; +Param<unsigned> iewToFetchDelay; +Param<unsigned> commitToFetchDelay; +Param<unsigned> fetchWidth; + +Param<unsigned> renameToDecodeDelay; +Param<unsigned> iewToDecodeDelay; +Param<unsigned> commitToDecodeDelay; +Param<unsigned> fetchToDecodeDelay; +Param<unsigned> decodeWidth; + +Param<unsigned> iewToRenameDelay; +Param<unsigned> commitToRenameDelay; +Param<unsigned> decodeToRenameDelay; +Param<unsigned> renameWidth; + +Param<unsigned> commitToIEWDelay; +Param<unsigned> renameToIEWDelay; +Param<unsigned> issueToExecuteDelay; +Param<unsigned> issueWidth; +Param<unsigned> executeWidth; +Param<unsigned> executeIntWidth; +Param<unsigned> executeFloatWidth; +Param<unsigned> executeBranchWidth; +Param<unsigned> executeMemoryWidth; + +Param<unsigned> iewToCommitDelay; +Param<unsigned> renameToROBDelay; +Param<unsigned> commitWidth; +Param<unsigned> squashWidth; + +Param<std::string> predType; +Param<unsigned> localPredictorSize; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; + +Param<unsigned> BTBEntries; +Param<unsigned> BTBTagSize; + +Param<unsigned> RASSize; + +Param<unsigned> LQEntries; +Param<unsigned> SQEntries; +Param<unsigned> LFSTSize; +Param<unsigned> SSITSize; + +Param<unsigned> numPhysIntRegs; +Param<unsigned> numPhysFloatRegs; +Param<unsigned> numIQEntries; +Param<unsigned> numROBEntries; + +Param<bool> decoupledFrontEnd; +Param<int> dispatchWidth; +Param<int> wbWidth; + +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + +Param<unsigned> instShiftAmt; + +Param<bool> defer_registration; + +Param<bool> function_trace; +Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(DerivOzoneCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(DerivOzoneCPU) + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(numThreads, "number of HW thread contexts"), + +#if FULL_SYSTEM + INIT_PARAM(system, "System object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(itb, "Instruction translation buffer"), + INIT_PARAM(dtb, "Data translation buffer"), +#else + INIT_PARAM(workload, "Processes to run"), +// INIT_PARAM(page_table, "Page table"), +#endif // FULL_SYSTEM + + INIT_PARAM_DFLT(mem, "Memory", NULL), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), + + INIT_PARAM_DFLT(max_insts_any_thread, + "Terminate when any thread reaches this inst count", + 0), + INIT_PARAM_DFLT(max_insts_all_threads, + "Terminate when all threads have reached" + "this inst count", + 0), + INIT_PARAM_DFLT(max_loads_any_thread, + "Terminate when any thread reaches this load count", + 0), + INIT_PARAM_DFLT(max_loads_all_threads, + "Terminate when all threads have reached this load" + "count", + 0), + +// INIT_PARAM_DFLT(icache, "L1 instruction cache", NULL), +// INIT_PARAM_DFLT(dcache, "L1 data cache", NULL), + + INIT_PARAM_DFLT(cachePorts, "Cache Ports", 200), + INIT_PARAM_DFLT(width, "Width", 1), + INIT_PARAM_DFLT(frontEndWidth, "Front end width", 1), + INIT_PARAM_DFLT(backEndWidth, "Back end width", 1), + INIT_PARAM_DFLT(backEndSquashLatency, "Back end squash latency", 1), + INIT_PARAM_DFLT(backEndLatency, "Back end latency", 1), + INIT_PARAM_DFLT(maxInstBufferSize, "Maximum instruction buffer size", 16), + INIT_PARAM(numPhysicalRegs, "Number of physical registers"), + INIT_PARAM_DFLT(maxOutstandingMemOps, "Maximum outstanding memory operations", 4), + + INIT_PARAM(decodeToFetchDelay, "Decode to fetch delay"), + INIT_PARAM(renameToFetchDelay, "Rename to fetch delay"), + INIT_PARAM(iewToFetchDelay, "Issue/Execute/Writeback to fetch" + "delay"), + INIT_PARAM(commitToFetchDelay, "Commit to fetch delay"), + INIT_PARAM(fetchWidth, "Fetch width"), + INIT_PARAM(renameToDecodeDelay, "Rename to decode delay"), + INIT_PARAM(iewToDecodeDelay, "Issue/Execute/Writeback to decode" + "delay"), + INIT_PARAM(commitToDecodeDelay, "Commit to decode delay"), + INIT_PARAM(fetchToDecodeDelay, "Fetch to decode delay"), + INIT_PARAM(decodeWidth, "Decode width"), + + INIT_PARAM(iewToRenameDelay, "Issue/Execute/Writeback to rename" + "delay"), + INIT_PARAM(commitToRenameDelay, "Commit to rename delay"), + INIT_PARAM(decodeToRenameDelay, "Decode to rename delay"), + INIT_PARAM(renameWidth, "Rename width"), + + INIT_PARAM(commitToIEWDelay, "Commit to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(renameToIEWDelay, "Rename to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(issueToExecuteDelay, "Issue to execute delay (internal" + "to the IEW stage)"), + INIT_PARAM(issueWidth, "Issue width"), + INIT_PARAM(executeWidth, "Execute width"), + INIT_PARAM(executeIntWidth, "Integer execute width"), + INIT_PARAM(executeFloatWidth, "Floating point execute width"), + INIT_PARAM(executeBranchWidth, "Branch execute width"), + INIT_PARAM(executeMemoryWidth, "Memory execute width"), + + INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " + "delay"), + INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), + INIT_PARAM(commitWidth, "Commit width"), + INIT_PARAM(squashWidth, "Squash width"), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + INIT_PARAM(localPredictorSize, "Size of local predictor"), + INIT_PARAM(localCtrBits, "Bits per counter"), + INIT_PARAM(localHistoryTableSize, "Size of local history table"), + INIT_PARAM(localHistoryBits, "Bits for the local history"), + INIT_PARAM(globalPredictorSize, "Size of global predictor"), + INIT_PARAM(globalCtrBits, "Bits per counter"), + INIT_PARAM(globalHistoryBits, "Bits of history"), + INIT_PARAM(choicePredictorSize, "Size of choice predictor"), + INIT_PARAM(choiceCtrBits, "Bits of choice counters"), + + INIT_PARAM(BTBEntries, "Number of BTB entries"), + INIT_PARAM(BTBTagSize, "Size of the BTB tags, in bits"), + + INIT_PARAM(RASSize, "RAS size"), + + INIT_PARAM(LQEntries, "Number of load queue entries"), + INIT_PARAM(SQEntries, "Number of store queue entries"), + INIT_PARAM(LFSTSize, "Last fetched store table size"), + INIT_PARAM(SSITSize, "Store set ID table size"), + + INIT_PARAM(numPhysIntRegs, "Number of physical integer registers"), + INIT_PARAM(numPhysFloatRegs, "Number of physical floating point " + "registers"), + INIT_PARAM(numIQEntries, "Number of instruction queue entries"), + INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + + INIT_PARAM_DFLT(decoupledFrontEnd, "Decoupled front end", true), + INIT_PARAM_DFLT(dispatchWidth, "Dispatch width", 0), + INIT_PARAM_DFLT(wbWidth, "Writeback width", 0), + + INIT_PARAM_DFLT(smtNumFetchingThreads, "SMT Number of Fetching Threads", 1), + INIT_PARAM_DFLT(smtFetchPolicy, "SMT Fetch Policy", "SingleThread"), + INIT_PARAM_DFLT(smtLSQPolicy, "SMT LSQ Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtLSQThreshold,"SMT LSQ Threshold", 100), + INIT_PARAM_DFLT(smtIQPolicy, "SMT IQ Policy", "Partitioned"), + INIT_PARAM_DFLT(smtIQThreshold, "SMT IQ Threshold", 100), + INIT_PARAM_DFLT(smtROBPolicy, "SMT ROB Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtROBThreshold,"SMT ROB Threshold", 100), + INIT_PARAM_DFLT(smtCommitPolicy,"SMT Commit Fetch Policy", "RoundRobin"), + + INIT_PARAM(instShiftAmt, "Number of bits to shift instructions by"), + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(DerivOzoneCPU) + +CREATE_SIM_OBJECT(DerivOzoneCPU) +{ + DerivOzoneCPU *cpu; + +#if FULL_SYSTEM + // Full-system only supports a single thread for the moment. + int actual_num_threads = 1; +#else + // In non-full-system mode, we infer the number of threads from + // the workload if it's not explicitly specified. + int actual_num_threads = + numThreads.isValid() ? numThreads : workload.size(); + + if (workload.size() == 0) { + fatal("Must specify at least one workload!"); + } + +#endif + + SimpleParams *params = new SimpleParams; + + params->clock = clock; + + params->name = getInstanceName(); + params->numberOfThreads = actual_num_threads; + +#if FULL_SYSTEM + params->system = system; + params->cpu_id = cpu_id; + params->itb = itb; + params->dtb = dtb; +#else + params->workload = workload; +// params->pTable = page_table; +#endif // FULL_SYSTEM + + params->mem = mem; + params->checker = checker; + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; + + // + // Caches + // +// params->icacheInterface = icache ? icache->getInterface() : NULL; +// params->dcacheInterface = dcache ? dcache->getInterface() : NULL; + params->cachePorts = cachePorts; + + params->width = width; + params->frontEndWidth = frontEndWidth; + params->backEndWidth = backEndWidth; + params->backEndSquashLatency = backEndSquashLatency; + params->backEndLatency = backEndLatency; + params->maxInstBufferSize = maxInstBufferSize; + params->numPhysicalRegs = numPhysIntRegs + numPhysFloatRegs; + params->maxOutstandingMemOps = maxOutstandingMemOps; + + params->decodeToFetchDelay = decodeToFetchDelay; + params->renameToFetchDelay = renameToFetchDelay; + params->iewToFetchDelay = iewToFetchDelay; + params->commitToFetchDelay = commitToFetchDelay; + params->fetchWidth = fetchWidth; + + params->renameToDecodeDelay = renameToDecodeDelay; + params->iewToDecodeDelay = iewToDecodeDelay; + params->commitToDecodeDelay = commitToDecodeDelay; + params->fetchToDecodeDelay = fetchToDecodeDelay; + params->decodeWidth = decodeWidth; + + params->iewToRenameDelay = iewToRenameDelay; + params->commitToRenameDelay = commitToRenameDelay; + params->decodeToRenameDelay = decodeToRenameDelay; + params->renameWidth = renameWidth; + + params->commitToIEWDelay = commitToIEWDelay; + params->renameToIEWDelay = renameToIEWDelay; + params->issueToExecuteDelay = issueToExecuteDelay; + params->issueWidth = issueWidth; + params->executeWidth = executeWidth; + params->executeIntWidth = executeIntWidth; + params->executeFloatWidth = executeFloatWidth; + params->executeBranchWidth = executeBranchWidth; + params->executeMemoryWidth = executeMemoryWidth; + + params->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + + params->predType = predType; + params->localPredictorSize = localPredictorSize; + params->localCtrBits = localCtrBits; + params->localHistoryTableSize = localHistoryTableSize; + params->localHistoryBits = localHistoryBits; + params->globalPredictorSize = globalPredictorSize; + params->globalCtrBits = globalCtrBits; + params->globalHistoryBits = globalHistoryBits; + params->choicePredictorSize = choicePredictorSize; + params->choiceCtrBits = choiceCtrBits; + + params->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->decoupledFrontEnd = decoupledFrontEnd; + params->dispatchWidth = dispatchWidth; + params->wbWidth = wbWidth; + + params->smtNumFetchingThreads = smtNumFetchingThreads; + params->smtFetchPolicy = smtFetchPolicy; + params->smtIQPolicy = smtIQPolicy; + params->smtLSQPolicy = smtLSQPolicy; + params->smtLSQThreshold = smtLSQThreshold; + params->smtROBPolicy = smtROBPolicy; + params->smtROBThreshold = smtROBThreshold; + params->smtCommitPolicy = smtCommitPolicy; + + params->instShiftAmt = 2; + + params->deferRegistration = defer_registration; + + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + + cpu = new DerivOzoneCPU(params); + + return cpu; +} + +REGISTER_SIM_OBJECT("DerivOzoneCPU", DerivOzoneCPU) diff --git a/src/cpu/ozone/cpu_impl.hh b/src/cpu/ozone/cpu_impl.hh index e7ed3cfe0..2cdc8a3da 100644 --- a/src/cpu/ozone/cpu_impl.hh +++ b/src/cpu/ozone/cpu_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 The Regents of The University of Michigan + * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,25 +24,1102 @@ * 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. + * + * Authors: Kevin Lim + * Nathan Binkert */ -#ifndef __CPU_OOO_CPU_OOO_IMPL_HH__ -#define __CPU_OOO_CPU_OOO_IMPL_HH__ +#include "config/full_system.hh" +#include "config/use_checker.hh" + +#include "arch/isa_traits.hh" // For MachInst +#include "base/trace.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "cpu/exetrace.hh" +#include "cpu/ozone/cpu.hh" +#include "cpu/quiesce_event.hh" +#include "cpu/static_inst.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +#if FULL_SYSTEM +#include "arch/faults.hh" +#include "arch/alpha/osfpal.hh" +#include "arch/alpha/tlb.hh" +#include "arch/vtophys.hh" +#include "base/callback.hh" +//#include "base/remote_gdb.hh" +#include "cpu/profile.hh" +#include "kern/kernel_stats.hh" +#include "sim/faults.hh" +#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" +#include "sim/system.hh" +#else // !FULL_SYSTEM +#include "sim/process.hh" +#endif // FULL_SYSTEM + +#if USE_CHECKER +#include "cpu/checker/thread_context.hh" +#endif + +using namespace TheISA; + +template <class Impl> +template<typename T> +void +OzoneCPU<Impl>::trace_data(T data) { + if (traceData) { + traceData->setData(data); + } +} + +template <class Impl> +OzoneCPU<Impl>::TickEvent::TickEvent(OzoneCPU *c, int w) + : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c), width(w) +{ +} + +template <class Impl> +void +OzoneCPU<Impl>::TickEvent::process() +{ + cpu->tick(); +} + +template <class Impl> +const char * +OzoneCPU<Impl>::TickEvent::description() +{ + return "OzoneCPU tick event"; +} + +template <class Impl> +OzoneCPU<Impl>::OzoneCPU(Params *p) +#if FULL_SYSTEM + : BaseCPU(p), thread(this, 0), tickEvent(this, p->width), +#else + : BaseCPU(p), thread(this, 0, p->workload[0], 0, p->mem), + tickEvent(this, p->width), +#endif + mem(p->mem), comm(5, 5) +{ + frontEnd = new FrontEnd(p); + backEnd = new BackEnd(p); + + _status = Idle; + + if (p->checker) { +#if USE_CHECKER + BaseCPU *temp_checker = p->checker; + checker = dynamic_cast<Checker<DynInstPtr> *>(temp_checker); + checker->setMemory(mem); +#if FULL_SYSTEM + checker->setSystem(p->system); +#endif + checkerTC = new CheckerThreadContext<OzoneTC>(&ozoneTC, checker); + thread.tc = checkerTC; + tc = checkerTC; +#else + panic("Checker enabled but not compiled in!"); +#endif + } else { + checker = NULL; + thread.tc = &ozoneTC; + tc = &ozoneTC; + } + + ozoneTC.cpu = this; + ozoneTC.thread = &thread; + + thread.inSyscall = false; + + thread.setStatus(ThreadContext::Suspended); +#if FULL_SYSTEM + /***** All thread state stuff *****/ + thread.cpu = this; + thread.setTid(0); + + thread.quiesceEvent = new EndQuiesceEvent(tc); + + system = p->system; + itb = p->itb; + dtb = p->dtb; + physmem = p->system->physmem; + + if (p->profile) { + thread.profile = new FunctionProfile(p->system->kernelSymtab); + // @todo: This might be better as an ThreadContext instead of OzoneTC + Callback *cb = + new MakeCallback<OzoneTC, + &OzoneTC::dumpFuncProfile>(&ozoneTC); + registerExitCallback(cb); + } + + // let's fill with a dummy node for now so we don't get a segfault + // on the first cycle when there's no node available. + static ProfileNode dummyNode; + thread.profileNode = &dummyNode; + thread.profilePC = 3; +#else + thread.cpu = this; +#endif // !FULL_SYSTEM + + numInst = 0; + startNumInst = 0; + + threadContexts.push_back(tc); + + frontEnd->setCPU(this); + backEnd->setCPU(this); + + frontEnd->setTC(tc); + backEnd->setTC(tc); + + frontEnd->setThreadState(&thread); + backEnd->setThreadState(&thread); -#include "arch/isa_traits.hh" + frontEnd->setCommBuffer(&comm); + backEnd->setCommBuffer(&comm); + + frontEnd->setBackEnd(backEnd); + backEnd->setFrontEnd(frontEnd); + + decoupledFrontEnd = p->decoupledFrontEnd; + + globalSeqNum = 1; + + checkInterrupts = false; + + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + thread.renameTable[i] = new DynInst(this); + thread.renameTable[i]->setResultReady(); + } + + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); + +#if !FULL_SYSTEM + /* Use this port to for syscall emulation writes to memory. */ + Port *mem_port; + TranslatingPort *trans_port; + trans_port = new TranslatingPort(csprintf("%s-%d-funcport", + name(), 0), + p->workload[0]->pTable, + false); + mem_port = p->mem->getPort("functional"); + mem_port->setPeer(trans_port); + trans_port->setPeer(mem_port); + thread.setMemPort(trans_port); +#else + Port *mem_port; + FunctionalPort *phys_port; + VirtualPort *virt_port; + phys_port = new FunctionalPort(csprintf("%s-%d-funcport", + name(), 0)); + mem_port = system->physmem->getPort("functional"); + mem_port->setPeer(phys_port); + phys_port->setPeer(mem_port); + + virt_port = new VirtualPort(csprintf("%s-%d-vport", + name(), 0)); + mem_port = system->physmem->getPort("functional"); + mem_port->setPeer(virt_port); + virt_port->setPeer(mem_port); + + thread.setPhysPort(phys_port); + thread.setVirtPort(virt_port); +#endif + + lockFlag = 0; + + DPRINTF(OzoneCPU, "OzoneCPU: Created Ozone cpu object.\n"); +} + +template <class Impl> +OzoneCPU<Impl>::~OzoneCPU() +{ +} + +template <class Impl> +void +OzoneCPU<Impl>::switchOut(Sampler *_sampler) +{ + sampler = _sampler; + switchCount = 0; + // Front end needs state from back end, so switch out the back end first. + backEnd->switchOut(); + frontEnd->switchOut(); +} + +template <class Impl> +void +OzoneCPU<Impl>::signalSwitched() +{ + if (++switchCount == 2) { + backEnd->doSwitchOut(); + frontEnd->doSwitchOut(); +#if USE_CHECKER + if (checker) + checker->switchOut(sampler); +#endif + + _status = SwitchedOut; + if (tickEvent.scheduled()) + tickEvent.squash(); + sampler->signalSwitched(); + } + assert(switchCount <= 2); +} + +template <class Impl> +void +OzoneCPU<Impl>::takeOverFrom(BaseCPU *oldCPU) +{ + BaseCPU::takeOverFrom(oldCPU); + + backEnd->takeOverFrom(); + frontEnd->takeOverFrom(); + assert(!tickEvent.scheduled()); + + // @todo: Fix hardcoded number + // Clear out any old information in time buffer. + for (int i = 0; i < 6; ++i) { + comm.advance(); + } + + // if any of this CPU's ThreadContexts are active, mark the CPU as + // running and schedule its tick event. + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && + _status != Running) { + _status = Running; + tickEvent.schedule(curTick); + } + } + // Nothing running, change status to reflect that we're no longer + // switched out. + if (_status == SwitchedOut) { + _status = Idle; + } +} + +template <class Impl> +void +OzoneCPU<Impl>::activateContext(int thread_num, int delay) +{ + // Eventually change this in SMT. + assert(thread_num == 0); + + assert(_status == Idle); + notIdleFraction++; + scheduleTickEvent(delay); + _status = Running; + thread.setStatus(ThreadContext::Active); + frontEnd->wakeFromQuiesce(); +} + +template <class Impl> +void +OzoneCPU<Impl>::suspendContext(int thread_num) +{ + // Eventually change this in SMT. + assert(thread_num == 0); + // @todo: Figure out how to initially set the status properly so + // this is running. +// assert(_status == Running); + notIdleFraction--; + unscheduleTickEvent(); + _status = Idle; +} template <class Impl> -class OoOCPU; +void +OzoneCPU<Impl>::deallocateContext(int thread_num) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + +template <class Impl> +void +OzoneCPU<Impl>::haltContext(int thread_num) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + +template <class Impl> +void +OzoneCPU<Impl>::regStats() +{ + using namespace Stats; + + BaseCPU::regStats(); + + thread.numInsts + .name(name() + ".num_insts") + .desc("Number of instructions executed") + ; + + thread.numMemRefs + .name(name() + ".num_refs") + .desc("Number of memory references") + ; + + notIdleFraction + .name(name() + ".not_idle_fraction") + .desc("Percentage of non-idle cycles") + ; + + idleFraction + .name(name() + ".idle_fraction") + .desc("Percentage of idle cycles") + ; + + quiesceCycles + .name(name() + ".quiesce_cycles") + .desc("Number of cycles spent in quiesce") + ; + + idleFraction = constant(1.0) - notIdleFraction; + + frontEnd->regStats(); + backEnd->regStats(); +} + +template <class Impl> +void +OzoneCPU<Impl>::resetStats() +{ + startNumInst = numInst; + notIdleFraction = (_status != Idle); +} + +template <class Impl> +void +OzoneCPU<Impl>::init() +{ + BaseCPU::init(); + + // Mark this as in syscall so it won't need to squash + thread.inSyscall = true; +#if FULL_SYSTEM + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + + // initialize CPU, including PC + TheISA::initCPU(tc, tc->readCpuId()); + } +#endif + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); + + thread.inSyscall = false; +} + +template <class Impl> +void +OzoneCPU<Impl>::serialize(std::ostream &os) +{ + BaseCPU::serialize(os); + SERIALIZE_ENUM(_status); + nameOut(os, csprintf("%s.tc", name())); + ozoneTC.serialize(os); + nameOut(os, csprintf("%s.tickEvent", name())); + tickEvent.serialize(os); +} + +template <class Impl> +void +OzoneCPU<Impl>::unserialize(Checkpoint *cp, const std::string §ion) +{ + BaseCPU::unserialize(cp, section); + UNSERIALIZE_ENUM(_status); + ozoneTC.unserialize(cp, csprintf("%s.tc", section)); + tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); +} + +template <class Impl> +Fault +OzoneCPU<Impl>::copySrcTranslate(Addr src) +{ + panic("Copy not implemented!\n"); + return NoFault; +#if 0 + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + int offset = src & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (src & TheISA::PageMask) != ((src + blk_size) & TheISA::PageMask) && + (src >> 40) != 0xfffffc) { + warn("Copied block source spans pages %x.", src); + no_warn = false; + } + + memReq->reset(src & ~(blk_size - 1), blk_size); + + // translate to physical address + Fault fault = tc->translateDataReadReq(memReq); + + assert(fault != Alignment_Fault); + + if (fault == NoFault) { + tc->copySrcAddr = src; + tc->copySrcPhysAddr = memReq->paddr + offset; + } else { + tc->copySrcAddr = 0; + tc->copySrcPhysAddr = 0; + } + return fault; +#endif +} + +template <class Impl> +Fault +OzoneCPU<Impl>::copy(Addr dest) +{ + panic("Copy not implemented!\n"); + return NoFault; +#if 0 + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + uint8_t data[blk_size]; + //assert(tc->copySrcAddr); + int offset = dest & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (dest & TheISA::PageMask) != ((dest + blk_size) & TheISA::PageMask) && + (dest >> 40) != 0xfffffc) { + no_warn = false; + warn("Copied block destination spans pages %x. ", dest); + } + + memReq->reset(dest & ~(blk_size -1), blk_size); + // translate to physical address + Fault fault = tc->translateDataWriteReq(memReq); + + assert(fault != Alignment_Fault); + + if (fault == NoFault) { + Addr dest_addr = memReq->paddr + offset; + // Need to read straight from memory since we have more than 8 bytes. + memReq->paddr = tc->copySrcPhysAddr; + tc->mem->read(memReq, data); + memReq->paddr = dest_addr; + tc->mem->write(memReq, data); + if (dcacheInterface) { + memReq->cmd = Copy; + memReq->completionEvent = NULL; + memReq->paddr = tc->copySrcPhysAddr; + memReq->dest = dest_addr; + memReq->size = 64; + memReq->time = curTick; + dcacheInterface->access(memReq); + } + } + return fault; +#endif +} + +#if FULL_SYSTEM +template <class Impl> +Addr +OzoneCPU<Impl>::dbg_vtophys(Addr addr) +{ + return vtophys(tc, addr); +} +#endif // FULL_SYSTEM + +#if FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (_status == Idle) { + DPRINTF(IPI,"Suspended Processor awoke\n"); +// thread.activate(); + // Hack for now. Otherwise might have to go through the tc, or + // I need to figure out what's the right thing to call. + activateContext(thread.readTid(), 1); + } +} +#endif // FULL_SYSTEM + +/* start simulation, program loaded, processor precise state initialized */ +template <class Impl> +void +OzoneCPU<Impl>::tick() +{ + DPRINTF(OzoneCPU, "\n\nOzoneCPU: Ticking cpu.\n"); + + _status = Running; + thread.renameTable[ZeroReg]->setIntResult(0); + thread.renameTable[ZeroReg+TheISA::FP_Base_DepTag]-> + setDoubleResult(0.0); + + comm.advance(); + frontEnd->tick(); + backEnd->tick(); + + // check for instruction-count-based events + comInstEventQueue[0]->serviceEvents(numInst); + + if (!tickEvent.scheduled() && _status == Running) + tickEvent.schedule(curTick + cycles(1)); +} + +template <class Impl> +void +OzoneCPU<Impl>::squashFromTC() +{ + thread.inSyscall = true; + backEnd->generateTCEvent(); +} + +#if !FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::syscall(uint64_t &callnum) +{ + // Not sure this copy is needed, depending on how the TC proxy is made. + thread.renameTable.copyFrom(backEnd->renameTable); + + thread.inSyscall = true; + + thread.funcExeInst++; + + DPRINTF(OzoneCPU, "FuncExeInst: %i\n", thread.funcExeInst); + + thread.process->syscall(callnum, tc); + + thread.funcExeInst--; + + thread.inSyscall = false; + + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); +} + +template <class Impl> +void +OzoneCPU<Impl>::setSyscallReturn(SyscallReturn return_value, int tid) +{ + // check for error condition. Alpha syscall convention is to + // indicate success/failure in reg a3 (r19) and put the + // return value itself in the standard return value reg (v0). + if (return_value.successful()) { + // no error + thread.renameTable[SyscallSuccessReg]->setIntResult(0); + thread.renameTable[ReturnValueReg]->setIntResult( + return_value.value()); + } else { + // got an error, return details + thread.renameTable[SyscallSuccessReg]->setIntResult((IntReg) -1); + thread.renameTable[ReturnValueReg]->setIntResult( + -return_value.value()); + } +} +#else +template <class Impl> +Fault +OzoneCPU<Impl>::hwrei() +{ + // Need to move this to ISA code + // May also need to make this per thread + + lockFlag = false; + lockAddrList.clear(); + thread.kernelStats->hwrei(); + + checkInterrupts = true; + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +template <class Impl> +void +OzoneCPU<Impl>::processInterrupts() +{ + // Check for interrupts here. For now can copy the code that + // exists within isa_fullsys_traits.hh. Also assume that thread 0 + // is the one that handles the interrupts. + + // Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + checkInterrupts = false; + + if (thread.readMiscReg(IPR_ASTRR)) + panic("asynchronous traps not implemented\n"); + + if (thread.readMiscReg(IPR_SIRR)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (thread.readMiscReg(IPR_SIRR) & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = intr_status(); + + if (interrupts) { + for (int i = INTLEVEL_EXTERNAL_MIN; + i < INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + } + + if (ipl && ipl > thread.readMiscReg(IPR_IPLR)) { + thread.setMiscReg(IPR_ISR, summary); + thread.setMiscReg(IPR_INTID, ipl); + // @todo: Make this more transparent + if (checker) { + checker->threadBase()->setMiscReg(IPR_ISR, summary); + checker->threadBase()->setMiscReg(IPR_INTID, ipl); + } + Fault fault = new InterruptFault; + fault->invoke(thread.getTC()); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + thread.readMiscReg(IPR_IPLR), ipl, summary); + } +} + +template <class Impl> +bool +OzoneCPU<Impl>::simPalCheck(int palFunc) +{ + // Need to move this to ISA code + // May also need to make this per thread + thread.kernelStats->callpal(palFunc, tc); + + switch (palFunc) { + case PAL::halt: + haltContext(thread.readTid()); + if (--System::numSystemsRunning == 0) + exitSimLoop("all cpus halted"); + break; + + case PAL::bpt: + case PAL::bugchk: + if (system->breakpoint()) + return false; + break; + } + + return true; +} +#endif + +template <class Impl> +BaseCPU * +OzoneCPU<Impl>::OzoneTC::getCpuPtr() +{ + return cpu; +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setCpuId(int id) +{ + cpu->cpuId = id; + thread->setCpuId(id); +} + +#if FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::delVirtPort(VirtualPort *vp) +{ + delete vp->getPeer(); + delete vp; +} +#endif + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setStatus(Status new_status) +{ + thread->setStatus(new_status); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::activate(int delay) +{ + cpu->activateContext(thread->readTid(), delay); +} + +/// Set the status to Suspended. +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::suspend() +{ + cpu->suspendContext(thread->readTid()); +} + +/// Set the status to Unallocated. +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::deallocate() +{ + cpu->deallocateContext(thread->readTid()); +} + +/// Set the status to Halted. +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::halt() +{ + cpu->haltContext(thread->readTid()); +} + +#if FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::dumpFuncProfile() +{ } +#endif + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::takeOverFrom(ThreadContext *old_context) +{ + // some things should already be set up +#if FULL_SYSTEM + assert(getSystemPtr() == old_context->getSystemPtr()); +#else + assert(getProcessPtr() == old_context->getProcessPtr()); +#endif + + // copy over functional state + setStatus(old_context->status()); + copyArchRegs(old_context); + setCpuId(old_context->readCpuId()); + +#if !FULL_SYSTEM + setFuncExeInst(old_context->readFuncExeInst()); +#else + EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent(); + if (other_quiesce) { + // Point the quiesce event's TC at this TC so that it wakes up + // the proper CPU. + other_quiesce->tc = this; + } + if (thread->quiesceEvent) { + thread->quiesceEvent->tc = this; + } + + thread->kernelStats = old_context->getKernelStats(); +// storeCondFailures = 0; + cpu->lockFlag = false; +#endif + + old_context->setStatus(ThreadContext::Unallocated); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::regStats(const std::string &name) +{ +#if FULL_SYSTEM + thread->kernelStats = new Kernel::Statistics(cpu->system); + thread->kernelStats->regStats(name + ".kern"); +#endif +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::serialize(std::ostream &os) +{ } + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::unserialize(Checkpoint *cp, const std::string §ion) +{ } + +#if FULL_SYSTEM +template <class Impl> +EndQuiesceEvent * +OzoneCPU<Impl>::OzoneTC::getQuiesceEvent() +{ + return thread->quiesceEvent; +} + +template <class Impl> +Tick +OzoneCPU<Impl>::OzoneTC::readLastActivate() +{ + return thread->lastActivate; +} + +template <class Impl> +Tick +OzoneCPU<Impl>::OzoneTC::readLastSuspend() +{ + return thread->lastSuspend; +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::profileClear() +{ + if (thread->profile) + thread->profile->clear(); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::profileSample() +{ + if (thread->profile) + thread->profile->sample(thread->profileNode, thread->profilePC); +} +#endif + +template <class Impl> +int +OzoneCPU<Impl>::OzoneTC::getThreadNum() +{ + return thread->readTid(); +} + +// Also somewhat obnoxious. Really only used for the TLB fault. +template <class Impl> +TheISA::MachInst +OzoneCPU<Impl>::OzoneTC::getInst() +{ + return thread->getInst(); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::copyArchRegs(ThreadContext *tc) +{ + thread->PC = tc->readPC(); + thread->nextPC = tc->readNextPC(); + + cpu->frontEnd->setPC(thread->PC); + cpu->frontEnd->setNextPC(thread->nextPC); + + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + if (i < TheISA::FP_Base_DepTag) { + thread->renameTable[i]->setIntResult(tc->readIntReg(i)); + } else if (i < (TheISA::FP_Base_DepTag + TheISA::NumFloatRegs)) { + int fp_idx = i - TheISA::FP_Base_DepTag; + thread->renameTable[i]->setDoubleResult( + tc->readFloatReg(fp_idx, 64)); + } + } + +#if !FULL_SYSTEM + thread->funcExeInst = tc->readFuncExeInst(); +#endif + + // Need to copy the TC values into the current rename table, + // copy the misc regs. + copyMiscRegs(tc, this); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::clearArchRegs() +{ + panic("Unimplemented!"); +} + +template <class Impl> +uint64_t +OzoneCPU<Impl>::OzoneTC::readIntReg(int reg_idx) +{ + return thread->renameTable[reg_idx]->readIntResult(); +} + +template <class Impl> +TheISA::FloatReg +OzoneCPU<Impl>::OzoneTC::readFloatReg(int reg_idx, int width) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + switch(width) { + case 32: + return thread->renameTable[idx]->readFloatResult(); + case 64: + return thread->renameTable[idx]->readDoubleResult(); + default: + panic("Unsupported width!"); + return 0; + } +} + +template <class Impl> +double +OzoneCPU<Impl>::OzoneTC::readFloatReg(int reg_idx) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + return thread->renameTable[idx]->readFloatResult(); +} + +template <class Impl> +uint64_t +OzoneCPU<Impl>::OzoneTC::readFloatRegBits(int reg_idx, int width) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + return thread->renameTable[idx]->readIntResult(); +} + +template <class Impl> +uint64_t +OzoneCPU<Impl>::OzoneTC::readFloatRegBits(int reg_idx) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + return thread->renameTable[idx]->readIntResult(); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setIntReg(int reg_idx, uint64_t val) +{ + thread->renameTable[reg_idx]->setIntResult(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatReg(int reg_idx, FloatReg val, int width) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + switch(width) { + case 32: + panic("Unimplemented!"); + break; + case 64: + thread->renameTable[idx]->setDoubleResult(val); + break; + default: + panic("Unsupported width!"); + } + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatReg(int reg_idx, FloatReg val) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + + thread->renameTable[idx]->setDoubleResult(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatRegBits(int reg_idx, FloatRegBits val, + int width) +{ + panic("Unimplemented!"); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatRegBits(int reg_idx, FloatRegBits val) +{ + panic("Unimplemented!"); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setPC(Addr val) +{ + thread->PC = val; + cpu->frontEnd->setPC(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setNextPC(Addr val) +{ + thread->nextPC = val; + cpu->frontEnd->setNextPC(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +TheISA::MiscReg +OzoneCPU<Impl>::OzoneTC::readMiscReg(int misc_reg) +{ + return thread->miscRegFile.readReg(misc_reg); +} + +template <class Impl> +TheISA::MiscReg +OzoneCPU<Impl>::OzoneTC::readMiscRegWithEffect(int misc_reg, Fault &fault) +{ + return thread->miscRegFile.readRegWithEffect(misc_reg, + fault, this); +} + +template <class Impl> +Fault +OzoneCPU<Impl>::OzoneTC::setMiscReg(int misc_reg, const MiscReg &val) +{ + // Needs to setup a squash event unless we're in syscall mode + Fault ret_fault = thread->miscRegFile.setReg(misc_reg, val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } + + return ret_fault; +} template <class Impl> -class OoODynInst; +Fault +OzoneCPU<Impl>::OzoneTC::setMiscRegWithEffect(int misc_reg, const MiscReg &val) +{ + // Needs to setup a squash event unless we're in syscall mode + Fault ret_fault = thread->miscRegFile.setRegWithEffect(misc_reg, val, + this); -struct OoOImpl { - typedef AlphaISA ISA; - typedef OoOCPU<OoOImpl> OoOCPU; - typedef OoOCPU FullCPU; - typedef OoODynInst<OoOImpl> DynInst; - typedef RefCountingPtr<DynInst> DynInstPtr; -}; + if (!thread->inSyscall) { + cpu->squashFromTC(); + } -#endif // __CPU_OOO_CPU_OOO_IMPL_HH__ + return ret_fault; +} diff --git a/src/cpu/ozone/dyn_inst.cc b/src/cpu/ozone/dyn_inst.cc new file mode 100644 index 000000000..732fb96b8 --- /dev/null +++ b/src/cpu/ozone/dyn_inst.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/dyn_inst_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +template class OzoneDynInst<OzoneImpl>; +//template class OzoneDynInst<SimpleImpl>; + diff --git a/src/cpu/ozone/dyn_inst.hh b/src/cpu/ozone/dyn_inst.hh new file mode 100644 index 000000000..67691d416 --- /dev/null +++ b/src/cpu/ozone/dyn_inst.hh @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_DYN_INST_HH__ +#define __CPU_OZONE_DYN_INST_HH__ + +#include "arch/isa_traits.hh" +#include "config/full_system.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/cpu.hh" // MUST include this +#include "cpu/ozone/ozone_impl.hh" + +#include <list> +#include <vector> + +template <class Impl> +class OzoneDynInst : public BaseDynInst<Impl> +{ + public: + // Typedefs + typedef typename Impl::OzoneCPU OzoneCPU; + + typedef typename OzoneCPU::ImplState ImplState; + + // Typedef for DynInstPtr. This is really just a RefCountingPtr<OoODynInst>. + typedef typename Impl::DynInstPtr DynInstPtr; + + typedef TheISA::ExtMachInst ExtMachInst; + typedef TheISA::MachInst MachInst; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + typedef typename std::list<DynInstPtr>::iterator ListIt; + + // Note that this is duplicated from the BaseDynInst class; I'm + // simply not sure the enum would carry through so I could use it + // in array declarations in this class. + enum { + MaxInstSrcRegs = TheISA::MaxInstSrcRegs, + MaxInstDestRegs = TheISA::MaxInstDestRegs + }; + + OzoneDynInst(OzoneCPU *cpu); + + OzoneDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, + InstSeqNum seq_num, OzoneCPU *cpu); + + OzoneDynInst(StaticInstPtr inst); + + ~OzoneDynInst(); + + void setSrcInst(DynInstPtr &newSrcInst, int regIdx) + { srcInsts[regIdx] = newSrcInst; } + + bool srcInstReady(int regIdx); + + void setPrevDestInst(DynInstPtr &oldDestInst, int regIdx) + { prevDestInst[regIdx] = oldDestInst; } + + DynInstPtr &getPrevDestInst(int regIdx) + { return prevDestInst[regIdx]; } + + void addDependent(DynInstPtr &dependent_inst); + + std::vector<DynInstPtr> &getDependents() { return dependents; } + std::vector<DynInstPtr> &getMemDeps() { return memDependents; } + std::list<DynInstPtr> &getMemSrcs() { return srcMemInsts; } + + void wakeDependents(); + + void wakeMemDependents(); + + void addMemDependent(DynInstPtr &inst) { memDependents.push_back(inst); } + + void addSrcMemInst(DynInstPtr &inst) { srcMemInsts.push_back(inst); } + + void markMemInstReady(OzoneDynInst<Impl> *inst); + + // For now I will remove instructions from the list when they wake + // up. In the future, you only really need a counter. + bool memDepReady() { return srcMemInsts.empty(); } + + private: + void initInstPtrs(); + + std::vector<DynInstPtr> dependents; + + std::vector<DynInstPtr> memDependents; + + std::list<DynInstPtr> srcMemInsts; + + /** The instruction that produces the value of the source + * registers. These may be NULL if the value has already been + * read from the source instruction. + */ + DynInstPtr srcInsts[MaxInstSrcRegs]; + + /** + * Previous rename instruction for this destination. + */ + DynInstPtr prevDestInst[MaxInstSrcRegs]; + + public: + + Fault initiateAcc(); + + Fault completeAcc(Packet *pkt); + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return srcInsts[idx]->readIntResult(); + } + + FloatReg readFloatReg(const StaticInst *si, int idx, int width) + { + switch(width) { + case 32: + return srcInsts[idx]->readFloatResult(); + case 64: + return srcInsts[idx]->readDoubleResult(); + default: + panic("Width not supported"); + return 0; + } + } + + FloatReg readFloatReg(const StaticInst *si, int idx) + { + return srcInsts[idx]->readFloatResult(); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) + { + return srcInsts[idx]->readIntResult(); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx) + { + return srcInsts[idx]->readIntResult(); + } + + /** @todo: Make results into arrays so they can handle multiple dest + * registers. + */ + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + BaseDynInst<Impl>::setIntReg(si, idx, val); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) + { + BaseDynInst<Impl>::setFloatReg(si, idx, val, width); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + BaseDynInst<Impl>::setFloatReg(si, idx, val); + } + + void setFloatRegBits(const StaticInst *si, int idx, + FloatRegBits val, int width) + { + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) + { + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); + } + + void setIntResult(uint64_t result) { this->instResult.integer = result; } + void setDoubleResult(double result) { this->instResult.dbl = result; } + + bool srcsReady(); + bool eaSrcsReady(); + + Fault execute(); + + Fault executeEAComp() + { return NoFault; } + + Fault executeMemAcc() + { return this->staticInst->memAccInst()->execute(this, this->traceData); } + + void clearDependents(); + + void clearMemDependents(); + + public: + // ISA stuff + MiscReg readMiscReg(int misc_reg); + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault); + + Fault setMiscReg(int misc_reg, const MiscReg &val); + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + +#if FULL_SYSTEM + Fault hwrei(); + int readIntrFlag(); + void setIntrFlag(int val); + bool inPalMode(); + void trap(Fault fault); + bool simPalCheck(int palFunc); +#else + void syscall(uint64_t &callnum); +#endif + + ListIt iqIt; + bool iqItValid; +}; + +#endif // __CPU_OZONE_DYN_INST_HH__ diff --git a/src/cpu/ozone/dyn_inst_impl.hh b/src/cpu/ozone/dyn_inst_impl.hh new file mode 100644 index 000000000..bad902c2a --- /dev/null +++ b/src/cpu/ozone/dyn_inst_impl.hh @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "config/full_system.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "kern/kernel_stats.hh" + +using namespace TheISA; + +template <class Impl> +OzoneDynInst<Impl>::OzoneDynInst(OzoneCPU *cpu) + : BaseDynInst<Impl>(0, 0, 0, 0, cpu) +{ + this->setResultReady(); + + initInstPtrs(); +} + +template <class Impl> +OzoneDynInst<Impl>::OzoneDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, + InstSeqNum seq_num, OzoneCPU *cpu) + : BaseDynInst<Impl>(inst, PC, Pred_PC, seq_num, cpu) +{ + initInstPtrs(); +} + +template <class Impl> +OzoneDynInst<Impl>::OzoneDynInst(StaticInstPtr _staticInst) + : BaseDynInst<Impl>(_staticInst) +{ + initInstPtrs(); +} + +template <class Impl> +OzoneDynInst<Impl>::~OzoneDynInst() +{ + DPRINTF(BE, "[sn:%lli] destructor called\n", this->seqNum); + for (int i = 0; i < this->numSrcRegs(); ++i) { + srcInsts[i] = NULL; + } + + for (int i = 0; i < this->numDestRegs(); ++i) { + prevDestInst[i] = NULL; + } + + dependents.clear(); +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::execute() +{ + // @todo: Pretty convoluted way to avoid squashing from happening when using + // the XC during an instruction's execution (specifically for instructions + // that have sideeffects that use the XC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->execute(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::initiateAcc() +{ + // @todo: Pretty convoluted way to avoid squashing from happening when using + // the XC during an instruction's execution (specifically for instructions + // that have sideeffects that use the XC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->initiateAcc(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::completeAcc(Packet *pkt) +{ + this->fault = this->staticInst->completeAcc(pkt, this, this->traceData); + + return this->fault; +} + +template <class Impl> +bool +OzoneDynInst<Impl>::srcInstReady(int regIdx) +{ + return srcInsts[regIdx]->isResultReady(); +} + +template <class Impl> +void +OzoneDynInst<Impl>::addDependent(DynInstPtr &dependent_inst) +{ + dependents.push_back(dependent_inst); +} + +template <class Impl> +void +OzoneDynInst<Impl>::wakeDependents() +{ + for (int i = 0; i < dependents.size(); ++i) { + dependents[i]->markSrcRegReady(); + } +} + +template <class Impl> +void +OzoneDynInst<Impl>::wakeMemDependents() +{ + for (int i = 0; i < memDependents.size(); ++i) { + memDependents[i]->markMemInstReady(this); + } +} + +template <class Impl> +void +OzoneDynInst<Impl>::markMemInstReady(OzoneDynInst<Impl> *inst) +{ + ListIt mem_it = srcMemInsts.begin(); + while ((*mem_it) != inst && mem_it != srcMemInsts.end()) { + mem_it++; + } + assert(mem_it != srcMemInsts.end()); + + srcMemInsts.erase(mem_it); +} + +template <class Impl> +void +OzoneDynInst<Impl>::initInstPtrs() +{ + for (int i = 0; i < MaxInstSrcRegs; ++i) { + srcInsts[i] = NULL; + } + iqItValid = false; +} + +template <class Impl> +bool +OzoneDynInst<Impl>::srcsReady() +{ + for (int i = 0; i < this->numSrcRegs(); ++i) { + if (!srcInsts[i]->isResultReady()) + return false; + } + + return true; +} + +template <class Impl> +bool +OzoneDynInst<Impl>::eaSrcsReady() +{ + for (int i = 1; i < this->numSrcRegs(); ++i) { + if (!srcInsts[i]->isResultReady()) + return false; + } + + return true; +} + +template <class Impl> +void +OzoneDynInst<Impl>::clearDependents() +{ + dependents.clear(); + for (int i = 0; i < this->numSrcRegs(); ++i) { + srcInsts[i] = NULL; + } + for (int i = 0; i < this->numDestRegs(); ++i) { + prevDestInst[i] = NULL; + } +} + +template <class Impl> +void +OzoneDynInst<Impl>::clearMemDependents() +{ + memDependents.clear(); +} + +template <class Impl> +MiscReg +OzoneDynInst<Impl>::readMiscReg(int misc_reg) +{ + return this->thread->readMiscReg(misc_reg); +} + +template <class Impl> +MiscReg +OzoneDynInst<Impl>::readMiscRegWithEffect(int misc_reg, Fault &fault) +{ + return this->thread->readMiscRegWithEffect(misc_reg, fault); +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::setMiscReg(int misc_reg, const MiscReg &val) +{ + this->setIntResult(val); + return this->thread->setMiscReg(misc_reg, val); +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::setMiscRegWithEffect(int misc_reg, const MiscReg &val) +{ + return this->thread->setMiscRegWithEffect(misc_reg, val); +} + +#if FULL_SYSTEM + +template <class Impl> +Fault +OzoneDynInst<Impl>::hwrei() +{ + if (!this->cpu->inPalMode(this->readPC())) + return new AlphaISA::UnimplementedOpcodeFault; + + this->setNextPC(this->thread->readMiscReg(AlphaISA::IPR_EXC_ADDR)); + + this->cpu->hwrei(); + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +template <class Impl> +int +OzoneDynInst<Impl>::readIntrFlag() +{ +return this->cpu->readIntrFlag(); +} + +template <class Impl> +void +OzoneDynInst<Impl>::setIntrFlag(int val) +{ + this->cpu->setIntrFlag(val); +} + +template <class Impl> +bool +OzoneDynInst<Impl>::inPalMode() +{ + return this->cpu->inPalMode(); +} + +template <class Impl> +void +OzoneDynInst<Impl>::trap(Fault fault) +{ + fault->invoke(this->thread->getTC()); +} + +template <class Impl> +bool +OzoneDynInst<Impl>::simPalCheck(int palFunc) +{ + return this->cpu->simPalCheck(palFunc); +} +#else +template <class Impl> +void +OzoneDynInst<Impl>::syscall(uint64_t &callnum) +{ + this->cpu->syscall(callnum); +} +#endif diff --git a/src/cpu/ozone/ea_list.cc b/src/cpu/ozone/ea_list.cc index 6114a0ca1..5ef240700 100644 --- a/src/cpu/ozone/ea_list.cc +++ b/src/cpu/ozone/ea_list.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Kevin Lim + * Nathan Binkert */ #include "arch/isa_traits.hh" diff --git a/src/cpu/ozone/ea_list.hh b/src/cpu/ozone/ea_list.hh index c0eee4bb8..64882632c 100644 --- a/src/cpu/ozone/ea_list.hh +++ b/src/cpu/ozone/ea_list.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Kevin Lim + * Nathan Binkert */ #ifndef __CPU_EA_LIST_HH__ diff --git a/src/cpu/ozone/front_end.cc b/src/cpu/ozone/front_end.cc new file mode 100644 index 000000000..cfd033564 --- /dev/null +++ b/src/cpu/ozone/front_end.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/front_end_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +template class FrontEnd<OzoneImpl>; +//template class FrontEnd<SimpleImpl>; diff --git a/src/cpu/ozone/front_end.hh b/src/cpu/ozone/front_end.hh new file mode 100644 index 000000000..181609098 --- /dev/null +++ b/src/cpu/ozone/front_end.hh @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_FRONT_END_HH__ +#define __CPU_OZONE_FRONT_END_HH__ + +#include <deque> + +#include "arch/utility.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/bpred_unit.hh" +#include "cpu/ozone/rename_table.hh" +#include "mem/port.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" +#include "sim/stats.hh" + +class ThreadContext; +class MemObject; +template <class> +class OzoneThreadState; +class PageTable; +template <class> +class TimeBuffer; + +template <class Impl> +class FrontEnd +{ + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::CPUType CPUType; + typedef typename Impl::BackEnd BackEnd; + + typedef typename Impl::CPUType::OzoneTC OzoneTC; + typedef typename Impl::CPUType::CommStruct CommStruct; + + /** IcachePort class. Handles doing the communication with the + * cache/memory. + */ + class IcachePort : public Port + { + protected: + /** Pointer to FE. */ + FrontEnd<Impl> *fe; + + public: + /** Default constructor. */ + IcachePort(FrontEnd<Impl> *_fe) + : fe(_fe) + { } + + protected: + /** Atomic version of receive. Panics. */ + virtual Tick recvAtomic(PacketPtr pkt); + + /** Functional version of receive. Panics. */ + virtual void recvFunctional(PacketPtr pkt); + + /** Receives status change. Other than range changing, panics. */ + virtual void recvStatusChange(Status status); + + /** Returns the address ranges of this device. */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + /** Timing version of receive. Handles setting fetch to the + * proper status to start fetching. */ + virtual bool recvTiming(PacketPtr pkt); + + /** Handles doing a retry of a failed fetch. */ + virtual void recvRetry(); + }; + + FrontEnd(Params *params); + + std::string name() const; + + void setCPU(CPUType *cpu_ptr); + + void setBackEnd(BackEnd *back_end_ptr) + { backEnd = back_end_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm); + + void setTC(ThreadContext *tc_ptr); + + void setThreadState(OzoneThreadState<Impl> *thread_ptr) + { thread = thread_ptr; } + + void regStats(); + + void tick(); + Fault fetchCacheLine(); + void processInst(DynInstPtr &inst); + void squash(const InstSeqNum &squash_num, const Addr &next_PC, + const bool is_branch = false, const bool branch_taken = false); + DynInstPtr getInst(); + + void processCacheCompletion(Packet *pkt); + + void addFreeRegs(int num_freed); + + bool isEmpty() { return instBuffer.empty(); } + + void switchOut(); + + void doSwitchOut(); + + void takeOverFrom(ThreadContext *old_tc = NULL); + + bool isSwitchedOut() { return switchedOut; } + + bool switchedOut; + + private: + void recvRetry(); + + bool updateStatus(); + + void checkBE(); + DynInstPtr getInstFromCacheline(); + void renameInst(DynInstPtr &inst); + // Returns true if we need to stop the front end this cycle + bool processBarriers(DynInstPtr &inst); + + void handleFault(Fault &fault); + public: + Fault getFault() { return fetchFault; } + private: + Fault fetchFault; + + // Align an address (typically a PC) to the start of an I-cache block. + // We fold in the PISA 64- to 32-bit conversion here as well. + Addr icacheBlockAlignPC(Addr addr) + { + addr = TheISA::realPCToFetchPC(addr); + return (addr & ~(cacheBlkMask)); + } + + InstSeqNum getAndIncrementInstSeq() + { return cpu->globalSeqNum++; } + + public: + CPUType *cpu; + + BackEnd *backEnd; + + ThreadContext *tc; + + OzoneThreadState<Impl> *thread; + + enum Status { + Running, + Idle, + IcacheWaitResponse, + IcacheWaitRetry, + IcacheAccessComplete, + SerializeBlocked, + SerializeComplete, + RenameBlocked, + QuiescePending, + TrapPending, + BEBlocked + }; + + Status status; + + private: + TimeBuffer<CommStruct> *comm; + typename TimeBuffer<CommStruct>::wire fromCommit; + + typedef typename Impl::BranchPred BranchPred; + + BranchPred branchPred; + + IcachePort icachePort; + + MemObject *mem; + + RequestPtr memReq; + + /** Mask to get a cache block's address. */ + Addr cacheBlkMask; + + unsigned cacheBlkSize; + + Addr cacheBlkPC; + + /** The cache line being fetched. */ + uint8_t *cacheData; + + bool fetchCacheLineNextCycle; + + bool cacheBlkValid; + + bool cacheBlocked; + + /** The packet that is waiting to be retried. */ + PacketPtr retryPkt; + + public: + RenameTable<Impl> renameTable; + + private: + Addr PC; + Addr nextPC; + + public: + void setPC(Addr val) { PC = val; } + void setNextPC(Addr val) { nextPC = val; } + + void wakeFromQuiesce(); + + void dumpInsts(); + + private: + typedef typename std::deque<DynInstPtr> InstBuff; + typedef typename InstBuff::iterator InstBuffIt; + + InstBuff instBuffer; + + int instBufferSize; + + int maxInstBufferSize; + + int width; + + int freeRegs; + + int numPhysRegs; + + bool serializeNext; + + DynInstPtr barrierInst; + + public: + bool interruptPending; + private: + // number of idle cycles +/* + Stats::Average<> notIdleFraction; + Stats::Formula idleFraction; +*/ + // @todo: Consider making these vectors and tracking on a per thread basis. + /** Stat for total number of cycles stalled due to an icache miss. */ + Stats::Scalar<> icacheStallCycles; + /** Stat for total number of fetched instructions. */ + Stats::Scalar<> fetchedInsts; + Stats::Scalar<> fetchedBranches; + /** Stat for total number of predicted branches. */ + Stats::Scalar<> predictedBranches; + /** Stat for total number of cycles spent fetching. */ + Stats::Scalar<> fetchCycles; + + Stats::Scalar<> fetchIdleCycles; + /** Stat for total number of cycles spent squashing. */ + Stats::Scalar<> fetchSquashCycles; + /** Stat for total number of cycles spent blocked due to other stages in + * the pipeline. + */ + Stats::Scalar<> fetchBlockedCycles; + /** Stat for total number of fetched cache lines. */ + Stats::Scalar<> fetchedCacheLines; + + Stats::Scalar<> fetchIcacheSquashes; + /** Distribution of number of instructions fetched each cycle. */ + Stats::Distribution<> fetchNisnDist; +// Stats::Vector<> qfull_iq_occupancy; +// Stats::VectorDistribution<> qfull_iq_occ_dist_; + Stats::Formula idleRate; + Stats::Formula branchRate; + Stats::Formula fetchRate; + Stats::Scalar<> IFQCount; // cumulative IFQ occupancy + Stats::Formula IFQOccupancy; + Stats::Formula IFQLatency; + Stats::Scalar<> IFQFcount; // cumulative IFQ full count + Stats::Formula IFQFullRate; + + Stats::Scalar<> dispatchCountStat; + Stats::Scalar<> dispatchedSerializing; + Stats::Scalar<> dispatchedTempSerializing; + Stats::Scalar<> dispatchSerializeStallCycles; + Stats::Formula dispatchRate; + Stats::Formula regIntFull; + Stats::Formula regFpFull; +}; + +#endif // __CPU_OZONE_FRONT_END_HH__ diff --git a/src/cpu/ozone/front_end_impl.hh b/src/cpu/ozone/front_end_impl.hh new file mode 100644 index 000000000..40042489d --- /dev/null +++ b/src/cpu/ozone/front_end_impl.hh @@ -0,0 +1,976 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "config/use_checker.hh" + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "base/statistics.hh" +#include "cpu/thread_context.hh" +#include "cpu/exetrace.hh" +#include "cpu/ozone/front_end.hh" +#include "mem/packet.hh" +#include "mem/request.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + +using namespace TheISA; + +template<class Impl> +Tick +FrontEnd<Impl>::IcachePort::recvAtomic(PacketPtr pkt) +{ + panic("FrontEnd doesn't expect recvAtomic callback!"); + return curTick; +} + +template<class Impl> +void +FrontEnd<Impl>::IcachePort::recvFunctional(PacketPtr pkt) +{ + panic("FrontEnd doesn't expect recvFunctional callback!"); +} + +template<class Impl> +void +FrontEnd<Impl>::IcachePort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("FrontEnd doesn't expect recvStatusChange callback!"); +} + +template<class Impl> +bool +FrontEnd<Impl>::IcachePort::recvTiming(Packet *pkt) +{ + fe->processCacheCompletion(pkt); + return true; +} + +template<class Impl> +void +FrontEnd<Impl>::IcachePort::recvRetry() +{ + fe->recvRetry(); +} + +template <class Impl> +FrontEnd<Impl>::FrontEnd(Params *params) + : branchPred(params), + icachePort(this), + mem(params->mem), + instBufferSize(0), + maxInstBufferSize(params->maxInstBufferSize), + width(params->frontEndWidth), + freeRegs(params->numPhysicalRegs), + numPhysRegs(params->numPhysicalRegs), + serializeNext(false), + interruptPending(false) +{ + switchedOut = false; + + status = Idle; + + memReq = NULL; + // Size of cache block. + cacheBlkSize = 64; + + assert(isPowerOf2(cacheBlkSize)); + + // Create mask to get rid of offset bits. + cacheBlkMask = (cacheBlkSize - 1); + + // Create space to store a cache line. + cacheData = new uint8_t[cacheBlkSize]; + + fetchCacheLineNextCycle = true; + + cacheBlkValid = cacheBlocked = false; + + retryPkt = NULL; + + fetchFault = NoFault; +} + +template <class Impl> +std::string +FrontEnd<Impl>::name() const +{ + return cpu->name() + ".frontend"; +} + +template <class Impl> +void +FrontEnd<Impl>::setCPU(CPUType *cpu_ptr) +{ + cpu = cpu_ptr; + + icachePort.setName(this->name() + "-iport"); + + Port *mem_dport = mem->getPort(""); + icachePort.setPeer(mem_dport); + mem_dport->setPeer(&icachePort); + +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->setIcachePort(&icachePort); + } +#endif +} + +template <class Impl> +void +FrontEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) +{ + comm = _comm; + // @todo: Hardcoded for now. Allow this to be set by a latency. + fromCommit = comm->getWire(-1); +} + +template <class Impl> +void +FrontEnd<Impl>::setTC(ThreadContext *tc_ptr) +{ + tc = tc_ptr; +} + +template <class Impl> +void +FrontEnd<Impl>::regStats() +{ + icacheStallCycles + .name(name() + ".icacheStallCycles") + .desc("Number of cycles fetch is stalled on an Icache miss") + .prereq(icacheStallCycles); + + fetchedInsts + .name(name() + ".fetchedInsts") + .desc("Number of instructions fetch has processed") + .prereq(fetchedInsts); + + fetchedBranches + .name(name() + ".fetchedBranches") + .desc("Number of fetched branches") + .prereq(fetchedBranches); + + predictedBranches + .name(name() + ".predictedBranches") + .desc("Number of branches that fetch has predicted taken") + .prereq(predictedBranches); + + fetchCycles + .name(name() + ".fetchCycles") + .desc("Number of cycles fetch has run and was not squashing or" + " blocked") + .prereq(fetchCycles); + + fetchIdleCycles + .name(name() + ".fetchIdleCycles") + .desc("Number of cycles fetch was idle") + .prereq(fetchIdleCycles); + + fetchSquashCycles + .name(name() + ".fetchSquashCycles") + .desc("Number of cycles fetch has spent squashing") + .prereq(fetchSquashCycles); + + fetchBlockedCycles + .name(name() + ".fetchBlockedCycles") + .desc("Number of cycles fetch has spent blocked") + .prereq(fetchBlockedCycles); + + fetchedCacheLines + .name(name() + ".fetchedCacheLines") + .desc("Number of cache lines fetched") + .prereq(fetchedCacheLines); + + fetchIcacheSquashes + .name(name() + ".fetchIcacheSquashes") + .desc("Number of outstanding Icache misses that were squashed") + .prereq(fetchIcacheSquashes); + + fetchNisnDist + .init(/* base value */ 0, + /* last value */ width, + /* bucket size */ 1) + .name(name() + ".rateDist") + .desc("Number of instructions fetched each cycle (Total)") + .flags(Stats::pdf); + + idleRate + .name(name() + ".idleRate") + .desc("Percent of cycles fetch was idle") + .prereq(idleRate); + idleRate = fetchIdleCycles * 100 / cpu->numCycles; + + branchRate + .name(name() + ".branchRate") + .desc("Number of branch fetches per cycle") + .flags(Stats::total); + branchRate = fetchedBranches / cpu->numCycles; + + fetchRate + .name(name() + ".rate") + .desc("Number of inst fetches per cycle") + .flags(Stats::total); + fetchRate = fetchedInsts / cpu->numCycles; + + IFQCount + .name(name() + ".IFQ:count") + .desc("cumulative IFQ occupancy") + ; + + IFQFcount + .name(name() + ".IFQ:fullCount") + .desc("cumulative IFQ full count") + .flags(Stats::total) + ; + + IFQOccupancy + .name(name() + ".IFQ:occupancy") + .desc("avg IFQ occupancy (inst's)") + ; + IFQOccupancy = IFQCount / cpu->numCycles; + + IFQLatency + .name(name() + ".IFQ:latency") + .desc("avg IFQ occupant latency (cycle's)") + .flags(Stats::total) + ; + + IFQFullRate + .name(name() + ".IFQ:fullRate") + .desc("fraction of time (cycles) IFQ was full") + .flags(Stats::total); + ; + IFQFullRate = IFQFcount * Stats::constant(100) / cpu->numCycles; + + dispatchCountStat + .name(name() + ".DIS:count") + .desc("cumulative count of dispatched insts") + .flags(Stats::total) + ; + + dispatchedSerializing + .name(name() + ".DIS:serializingInsts") + .desc("count of serializing insts dispatched") + .flags(Stats::total) + ; + + dispatchedTempSerializing + .name(name() + ".DIS:tempSerializingInsts") + .desc("count of temporary serializing insts dispatched") + .flags(Stats::total) + ; + + dispatchSerializeStallCycles + .name(name() + ".DIS:serializeStallCycles") + .desc("count of cycles dispatch stalled for serializing inst") + .flags(Stats::total) + ; + + dispatchRate + .name(name() + ".DIS:rate") + .desc("dispatched insts per cycle") + .flags(Stats::total) + ; + dispatchRate = dispatchCountStat / cpu->numCycles; + + regIntFull + .name(name() + ".REG:int:full") + .desc("number of cycles where there were no INT registers") + ; + + regFpFull + .name(name() + ".REG:fp:full") + .desc("number of cycles where there were no FP registers") + ; + IFQLatency = IFQOccupancy / dispatchRate; + + branchPred.regStats(); +} + +template <class Impl> +void +FrontEnd<Impl>::tick() +{ + if (switchedOut) + return; + + // @todo: Maybe I want to just have direct communication... + if (fromCommit->doneSeqNum) { + branchPred.update(fromCommit->doneSeqNum, 0); + } + + IFQCount += instBufferSize; + IFQFcount += instBufferSize == maxInstBufferSize; + + // Fetch cache line + if (status == IcacheAccessComplete) { + cacheBlkValid = true; + + status = Running; + if (barrierInst) + status = SerializeBlocked; + if (freeRegs <= 0) + status = RenameBlocked; + checkBE(); + } else if (status == IcacheWaitResponse || status == IcacheWaitRetry) { + DPRINTF(FE, "Still in Icache wait.\n"); + icacheStallCycles++; + return; + } + + if (status == RenameBlocked || status == SerializeBlocked || + status == TrapPending || status == BEBlocked) { + // Will cause a one cycle bubble between changing state and + // restarting. + DPRINTF(FE, "In blocked status.\n"); + + fetchBlockedCycles++; + + if (status == SerializeBlocked) { + dispatchSerializeStallCycles++; + } + updateStatus(); + return; + } else if (status == QuiescePending) { + DPRINTF(FE, "Waiting for quiesce to execute or get squashed.\n"); + return; + } else if (status != IcacheAccessComplete) { + if (fetchCacheLineNextCycle) { + Fault fault = fetchCacheLine(); + if (fault != NoFault) { + handleFault(fault); + fetchFault = fault; + return; + } + fetchCacheLineNextCycle = false; + } + // If miss, stall until it returns. + if (status == IcacheWaitResponse || status == IcacheWaitRetry) { + // Tell CPU to not tick me for now. + return; + } + } + + fetchCycles++; + + int num_inst = 0; + + // Otherwise loop and process instructions. + // One way to hack infinite width is to set width and maxInstBufferSize + // both really high. Inelegant, but probably will work. + while (num_inst < width && + instBufferSize < maxInstBufferSize) { + // Get instruction from cache line. + DynInstPtr inst = getInstFromCacheline(); + + if (!inst) { + // PC is no longer in the cache line, end fetch. + // Might want to check this at the end of the cycle so that + // there's no cycle lost to checking for a new cache line. + DPRINTF(FE, "Need to get new cache line\n"); + fetchCacheLineNextCycle = true; + break; + } + + processInst(inst); + + if (status == SerializeBlocked) { + break; + } + + // Possibly push into a time buffer that estimates the front end + // latency + instBuffer.push_back(inst); + ++instBufferSize; + ++num_inst; + +#if FULL_SYSTEM + if (inst->isQuiesce()) { + warn("%lli: Quiesce instruction encountered, halting fetch!", curTick); + status = QuiescePending; + break; + } +#endif + + if (inst->predTaken()) { + // Start over with tick? + break; + } else if (freeRegs <= 0) { + DPRINTF(FE, "Ran out of free registers to rename to!\n"); + status = RenameBlocked; + break; + } else if (serializeNext) { + break; + } + } + + fetchNisnDist.sample(num_inst); + checkBE(); + + DPRINTF(FE, "Num insts processed: %i, Inst Buffer size: %i, Free " + "Regs %i\n", num_inst, instBufferSize, freeRegs); +} + +template <class Impl> +Fault +FrontEnd<Impl>::fetchCacheLine() +{ + // Read a cache line, based on the current PC. +#if FULL_SYSTEM + // Flag to say whether or not address is physical addr. + unsigned flags = cpu->inPalMode(PC) ? PHYSICAL : 0; +#else + unsigned flags = 0; +#endif // FULL_SYSTEM + Fault fault = NoFault; + + if (interruptPending && flags == 0) { + return fault; + } + + // Align the fetch PC so it's at the start of a cache block. + Addr fetch_PC = icacheBlockAlignPC(PC); + + DPRINTF(FE, "Fetching cache line starting at %#x.\n", fetch_PC); + + // Setup the memReq to do a read of the first isntruction's address. + // Set the appropriate read size and flags as well. + memReq = new Request(0, fetch_PC, cacheBlkSize, flags, + fetch_PC, cpu->readCpuId(), 0); + + // Translate the instruction request. + fault = cpu->translateInstReq(memReq, thread); + + // Now do the timing access to see whether or not the instruction + // exists within the cache. + if (fault == NoFault) { +#if 0 + if (cpu->system->memctrl->badaddr(memReq->paddr) || + memReq->flags & UNCACHEABLE) { + DPRINTF(FE, "Fetch: Bad address %#x (hopefully on a " + "misspeculating path!", + memReq->paddr); + return TheISA::genMachineCheckFault(); + } +#endif + + // Build packet here. + PacketPtr data_pkt = new Packet(memReq, + Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(cacheData); + + if (!icachePort.sendTiming(data_pkt)) { + assert(retryPkt == NULL); + DPRINTF(Fetch, "Out of MSHRs!\n"); + status = IcacheWaitRetry; + retryPkt = data_pkt; + cacheBlocked = true; + return NoFault; + } + + status = IcacheWaitResponse; + } + + // Note that this will set the cache block PC a bit earlier than it should + // be set. + cacheBlkPC = fetch_PC; + + ++fetchedCacheLines; + + DPRINTF(FE, "Done fetching cache line.\n"); + + return fault; +} + +template <class Impl> +void +FrontEnd<Impl>::processInst(DynInstPtr &inst) +{ + if (processBarriers(inst)) { + return; + } + + Addr inst_PC = inst->readPC(); + + if (!inst->isControl()) { + inst->setPredTarg(inst->readNextPC()); + } else { + fetchedBranches++; + if (branchPred.predict(inst, inst_PC, inst->threadNumber)) { + predictedBranches++; + } + } + + Addr next_PC = inst->readPredTarg(); + + DPRINTF(FE, "[sn:%lli] Predicted and processed inst PC %#x, next PC " + "%#x\n", inst->seqNum, inst_PC, next_PC); + +// inst->setNextPC(next_PC); + + // Not sure where I should set this + PC = next_PC; + + renameInst(inst); +} + +template <class Impl> +bool +FrontEnd<Impl>::processBarriers(DynInstPtr &inst) +{ + if (serializeNext) { + inst->setSerializeBefore(); + serializeNext = false; + } else if (!inst->isSerializing() && + !inst->isIprAccess() && + !inst->isStoreConditional()) { + return false; + } + + if ((inst->isIprAccess() || inst->isSerializeBefore()) && + !inst->isSerializeHandled()) { + DPRINTF(FE, "Serialize before instruction encountered.\n"); + + if (!inst->isTempSerializeBefore()) { + dispatchedSerializing++; + inst->setSerializeHandled(); + } else { + dispatchedTempSerializing++; + } + + // Change status over to SerializeBlocked so that other stages know + // what this is blocked on. + status = SerializeBlocked; + + barrierInst = inst; + return true; + } else if ((inst->isStoreConditional() || inst->isSerializeAfter()) + && !inst->isSerializeHandled()) { + DPRINTF(FE, "Serialize after instruction encountered.\n"); + + inst->setSerializeHandled(); + + dispatchedSerializing++; + + serializeNext = true; + return false; + } + return false; +} + +template <class Impl> +void +FrontEnd<Impl>::handleFault(Fault &fault) +{ + DPRINTF(FE, "Fault at fetch, telling commit\n"); + + // We're blocked on the back end until it handles this fault. + status = TrapPending; + + // Get a sequence number. + InstSeqNum inst_seq = getAndIncrementInstSeq(); + // We will use a nop in order to carry the fault. + ExtMachInst ext_inst = TheISA::NoopMachInst; + + // Create a new DynInst from the dummy nop. + DynInstPtr instruction = new DynInst(ext_inst, PC, + PC+sizeof(MachInst), + inst_seq, cpu); + instruction->setPredTarg(instruction->readNextPC()); +// instruction->setThread(tid); + +// instruction->setASID(tid); + + instruction->setThreadState(thread); + + instruction->traceData = NULL; + + instruction->fault = fault; + instruction->setCanIssue(); + instBuffer.push_back(instruction); + ++instBufferSize; +} + +template <class Impl> +void +FrontEnd<Impl>::squash(const InstSeqNum &squash_num, const Addr &next_PC, + const bool is_branch, const bool branch_taken) +{ + DPRINTF(FE, "Squashing from [sn:%lli], setting PC to %#x\n", + squash_num, next_PC); + + if (fetchFault != NoFault) + fetchFault = NoFault; + + while (!instBuffer.empty() && + instBuffer.back()->seqNum > squash_num) { + DynInstPtr inst = instBuffer.back(); + + DPRINTF(FE, "Squashing instruction [sn:%lli] PC %#x\n", + inst->seqNum, inst->readPC()); + + inst->clearDependents(); + + instBuffer.pop_back(); + --instBufferSize; + + freeRegs+= inst->numDestRegs(); + } + + // Copy over rename table from the back end. + renameTable.copyFrom(backEnd->renameTable); + + PC = next_PC; + + // Update BP with proper information. + if (is_branch) { + branchPred.squash(squash_num, next_PC, branch_taken, 0); + } else { + branchPred.squash(squash_num, 0); + } + + // Clear the icache miss if it's outstanding. + if (status == IcacheWaitResponse) { + DPRINTF(FE, "Squashing outstanding Icache access.\n"); + memReq = NULL; + } + + if (status == SerializeBlocked) { + assert(barrierInst->seqNum > squash_num); + barrierInst = NULL; + } + + // Unless this squash originated from the front end, we're probably + // in running mode now. + // Actually might want to make this latency dependent. + status = Running; + fetchCacheLineNextCycle = true; +} + +template <class Impl> +typename Impl::DynInstPtr +FrontEnd<Impl>::getInst() +{ + if (instBufferSize == 0) { + return NULL; + } + + DynInstPtr inst = instBuffer.front(); + + instBuffer.pop_front(); + + --instBufferSize; + + dispatchCountStat++; + + return inst; +} + +template <class Impl> +void +FrontEnd<Impl>::processCacheCompletion(PacketPtr pkt) +{ + DPRINTF(FE, "Processing cache completion\n"); + + // Do something here. + if (status != IcacheWaitResponse || + pkt->req != memReq || + switchedOut) { + DPRINTF(FE, "Previous fetch was squashed.\n"); + fetchIcacheSquashes++; + delete pkt->req; + delete pkt; + return; + } + + status = IcacheAccessComplete; + +/* if (checkStall(tid)) { + fetchStatus[tid] = Blocked; + } else { + fetchStatus[tid] = IcacheMissComplete; + } +*/ +// memcpy(cacheData, memReq->data, memReq->size); + + // Reset the completion event to NULL. +// memReq->completionEvent = NULL; + delete pkt->req; + delete pkt; + memReq = NULL; +} + +template <class Impl> +void +FrontEnd<Impl>::addFreeRegs(int num_freed) +{ + if (status == RenameBlocked && freeRegs + num_freed > 0) { + status = Running; + } + + DPRINTF(FE, "Adding %i freed registers\n", num_freed); + + freeRegs+= num_freed; + +// assert(freeRegs <= numPhysRegs); + if (freeRegs > numPhysRegs) + freeRegs = numPhysRegs; +} + +template <class Impl> +void +FrontEnd<Impl>::recvRetry() +{ + assert(cacheBlocked); + if (retryPkt != NULL) { + assert(status == IcacheWaitRetry); + + if (icachePort.sendTiming(retryPkt)) { + status = IcacheWaitResponse; + retryPkt = NULL; + cacheBlocked = false; + } + } else { + // Access has been squashed since it was sent out. Just clear + // the cache being blocked. + cacheBlocked = false; + } + +} + +template <class Impl> +bool +FrontEnd<Impl>::updateStatus() +{ + bool serialize_block = !backEnd->robEmpty() || instBufferSize; + bool be_block = cpu->decoupledFrontEnd ? false : backEnd->isBlocked(); + bool ret_val = false; + + if (status == SerializeBlocked && !serialize_block) { + status = SerializeComplete; + ret_val = true; + } + + if (status == BEBlocked && !be_block) { + if (barrierInst) { + status = SerializeBlocked; + } else { + status = Running; + } + ret_val = true; + } + return ret_val; +} + +template <class Impl> +void +FrontEnd<Impl>::checkBE() +{ + bool be_block = cpu->decoupledFrontEnd ? false : backEnd->isBlocked(); + if (be_block) { + if (status == Running || status == Idle) { + status = BEBlocked; + } + } +} + +template <class Impl> +typename Impl::DynInstPtr +FrontEnd<Impl>::getInstFromCacheline() +{ + if (status == SerializeComplete) { + DynInstPtr inst = barrierInst; + status = Running; + barrierInst = NULL; + inst->clearSerializeBefore(); + return inst; + } + + InstSeqNum inst_seq; + MachInst inst; + // @todo: Fix this magic number used here to handle word offset (and + // getting rid of PAL bit) + unsigned offset = (PC & cacheBlkMask) & ~3; + + // PC of inst is not in this cache block + if (PC >= (cacheBlkPC + cacheBlkSize) || PC < cacheBlkPC || !cacheBlkValid) { + return NULL; + } + + ////////////////////////// + // Fetch one instruction + ////////////////////////// + + // Get a sequence number. + inst_seq = getAndIncrementInstSeq(); + + // Make sure this is a valid index. + assert(offset <= cacheBlkSize - sizeof(MachInst)); + + // Get the instruction from the array of the cache line. + inst = htog(*reinterpret_cast<MachInst *>(&cacheData[offset])); + + ExtMachInst decode_inst = TheISA::makeExtMI(inst, PC); + + // Create a new DynInst from the instruction fetched. + DynInstPtr instruction = new DynInst(decode_inst, PC, PC+sizeof(MachInst), + inst_seq, cpu); + + instruction->setThreadState(thread); + + DPRINTF(FE, "Instruction [sn:%lli] created, with PC %#x\n%s\n", + inst_seq, instruction->readPC(), + instruction->staticInst->disassemble(PC)); + + instruction->traceData = + Trace::getInstRecord(curTick, tc, cpu, + instruction->staticInst, + instruction->readPC(), 0); + + // Increment stat of fetched instructions. + ++fetchedInsts; + + return instruction; +} + +template <class Impl> +void +FrontEnd<Impl>::renameInst(DynInstPtr &inst) +{ + DynInstPtr src_inst = NULL; + int num_src_regs = inst->numSrcRegs(); + if (num_src_regs == 0) { + inst->setCanIssue(); + } else { + for (int i = 0; i < num_src_regs; ++i) { + src_inst = renameTable[inst->srcRegIdx(i)]; + + inst->setSrcInst(src_inst, i); + + DPRINTF(FE, "[sn:%lli]: Src reg %i is inst [sn:%lli]\n", + inst->seqNum, (int)inst->srcRegIdx(i), src_inst->seqNum); + + if (src_inst->isResultReady()) { + DPRINTF(FE, "Reg ready.\n"); + inst->markSrcRegReady(i); + } else { + DPRINTF(FE, "Adding to dependent list.\n"); + src_inst->addDependent(inst); + } + } + } + + for (int i = 0; i < inst->numDestRegs(); ++i) { + RegIndex idx = inst->destRegIdx(i); + + DPRINTF(FE, "Dest reg %i is now inst [sn:%lli], was previously " + "[sn:%lli]\n", + (int)inst->destRegIdx(i), inst->seqNum, + renameTable[idx]->seqNum); + + inst->setPrevDestInst(renameTable[idx], i); + + renameTable[idx] = inst; + --freeRegs; + } +} + +template <class Impl> +void +FrontEnd<Impl>::wakeFromQuiesce() +{ + DPRINTF(FE, "Waking up from quiesce\n"); + // Hopefully this is safe + status = Running; +} + +template <class Impl> +void +FrontEnd<Impl>::switchOut() +{ + switchedOut = true; + cpu->signalSwitched(); +} + +template <class Impl> +void +FrontEnd<Impl>::doSwitchOut() +{ + memReq = NULL; + squash(0, 0); + instBuffer.clear(); + instBufferSize = 0; + status = Idle; +} + +template <class Impl> +void +FrontEnd<Impl>::takeOverFrom(ThreadContext *old_tc) +{ + assert(freeRegs == numPhysRegs); + fetchCacheLineNextCycle = true; + + cacheBlkValid = false; + +#if !FULL_SYSTEM +// pTable = params->pTable; +#endif + fetchFault = NoFault; + serializeNext = false; + barrierInst = NULL; + status = Running; + switchedOut = false; + interruptPending = false; +} + +template <class Impl> +void +FrontEnd<Impl>::dumpInsts() +{ + cprintf("instBuffer size: %i\n", instBuffer.size()); + + InstBuffIt buff_it = instBuffer.begin(); + + for (int num = 0; buff_it != instBuffer.end(); num++) { + cprintf("Instruction:%i\nPC:%#x\n[tid:%i]\n[sn:%lli]\nIssued:%i\n" + "Squashed:%i\n\n", + num, (*buff_it)->readPC(), (*buff_it)->threadNumber, + (*buff_it)->seqNum, (*buff_it)->isIssued(), + (*buff_it)->isSquashed()); + buff_it++; + } +} diff --git a/src/cpu/ozone/inorder_back_end.cc b/src/cpu/ozone/inorder_back_end.cc new file mode 100644 index 000000000..bc6618e4a --- /dev/null +++ b/src/cpu/ozone/inorder_back_end.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/inorder_back_end_impl.hh" +#include "cpu/ozone/simple_impl.hh" + +template class InorderBackEnd<SimpleImpl>; diff --git a/src/cpu/ozone/inorder_back_end.hh b/src/cpu/ozone/inorder_back_end.hh new file mode 100644 index 000000000..ffdba2f6c --- /dev/null +++ b/src/cpu/ozone/inorder_back_end.hh @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_INORDER_BACK_END_HH__ +#define __CPU_OZONE_INORDER_BACK_END_HH__ + +#include <list> + +#include "arch/faults.hh" +#include "base/timebuf.hh" +#include "cpu/thread_context.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +template <class Impl> +class InorderBackEnd +{ + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::FrontEnd FrontEnd; + + typedef typename FullCPU::OzoneTC OzoneTC; + typedef typename Impl::FullCPU::CommStruct CommStruct; + + InorderBackEnd(Params *params); + + std::string name() const; + + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + void setFrontEnd(FrontEnd *front_end_ptr) + { frontEnd = front_end_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm) + { comm = _comm; } + + void setTC(ThreadContext *tc_ptr); + + void setThreadState(OzoneThreadState<Impl> *thread_ptr); + + void regStats() { } + +#if FULL_SYSTEM + void checkInterrupts(); +#endif + + void tick(); + void executeInsts(); + void squash(const InstSeqNum &squash_num, const Addr &next_PC); + + void squashFromXC(); + void generateXCEvent() { } + + bool robEmpty() { return instList.empty(); } + + bool isFull() { return false; } + bool isBlocked() { return status == DcacheMissStoreStall || + status == DcacheMissLoadStall || + interruptBlocked; } + + void fetchFault(Fault &fault); + + void dumpInsts(); + + private: + void handleFault(); + + void setSquashInfoFromTC(); + + bool squashPending; + InstSeqNum squashSeqNum; + Addr squashNextPC; + + Fault faultFromFetch; + + bool interruptBlocked; + + public: + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + Addr readCommitPC() { return commitPC; } + + Addr commitPC; + + void switchOut() { panic("Not implemented!"); } + void doSwitchOut() { panic("Not implemented!"); } + void takeOverFrom(ThreadContext *old_tc = NULL) { panic("Not implemented!"); } + + public: + FullCPU *cpu; + + FrontEnd *frontEnd; + + ThreadContext *tc; + + OzoneThreadState<Impl> *thread; + + RenameTable<Impl> renameTable; + + protected: + enum Status { + Running, + Idle, + DcacheMissLoadStall, + DcacheMissStoreStall, + DcacheMissComplete, + Blocked + }; + + Status status; + + class DCacheCompletionEvent : public Event + { + private: + InorderBackEnd *be; + + public: + DCacheCompletionEvent(InorderBackEnd *_be); + + virtual void process(); + virtual const char *description(); + + DynInstPtr inst; + }; + + friend class DCacheCompletionEvent; + + DCacheCompletionEvent cacheCompletionEvent; + +// MemInterface *dcacheInterface; + + RequestPtr memReq; + + private: + typedef typename std::list<DynInstPtr>::iterator InstListIt; + + std::list<DynInstPtr> instList; + + // General back end width. Used if the more specific isn't given. + int width; + + int latency; + + int squashLatency; + + TimeBuffer<int> numInstsToWB; + TimeBuffer<int>::wire instsAdded; + TimeBuffer<int>::wire instsToExecute; + + TimeBuffer<CommStruct> *comm; + // number of cycles stalled for D-cache misses + Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; +}; + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::read(Addr addr, T &data, unsigned flags) +{ + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataReadReq(memReq); + + // if we have a cache, do cache access too + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Read; + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + // Fix this hack for keeping funcExeInst correct with loads that + // are executed twice. + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissLoadStall; + DPRINTF(IBE, "Dcache miss stall!\n"); + } else { + // do functional access + DPRINTF(IBE, "Dcache hit!\n"); + } + } +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); +*/ + return fault; +} +#if 0 +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::read(MemReqPtr &req, T &data) +{ +#if FULL_SYSTEM && defined(TARGET_ALPHA) + if (req->flags & LOCKED) { + req->xc->setMiscReg(TheISA::Lock_Addr_DepTag, req->paddr); + req->xc->setMiscReg(TheISA::Lock_Flag_DepTag, true); + } +#endif + + Fault error; + error = thread->mem->read(req, data); + data = LittleEndianGuest::gtoh(data); + return error; +} +#endif + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataWriteReq(memReq); + + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Write; +// memcpy(memReq->data,(uint8_t *)&data,memReq->size); + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissStoreStall; + DPRINTF(IBE, "Dcache miss stall!\n"); + } else { + DPRINTF(IBE, "Dcache hit!\n"); + } + } + + if (res && (fault == NoFault)) + *res = memReq->result; +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); +*/ + return fault; +} +#if 0 +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::write(MemReqPtr &req, T &data) +{ +#if FULL_SYSTEM && defined(TARGET_ALPHA) + ExecContext *xc; + + // If this is a store conditional, act appropriately + if (req->flags & LOCKED) { + xc = req->xc; + + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + xc->setStCondFailures(0);//Needed? [RGD] + } else { + bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag); + Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag); + req->result = lock_flag; + if (!lock_flag || + ((lock_addr & ~0xf) != (req->paddr & ~0xf))) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + xc->setStCondFailures(xc->readStCondFailures() + 1); + if (((xc->readStCondFailures()) % 100000) == 0) { + std::cerr << "Warning: " + << xc->readStCondFailures() + << " consecutive store conditional failures " + << "on cpu " << req->xc->readCpuId() + << std::endl; + } + return NoFault; + } + else xc->setStCondFailures(0); + } + } + + // Need to clear any locked flags on other proccessors for + // this address. Only do this for succsful Store Conditionals + // and all other stores (WH64?). Unsuccessful Store + // Conditionals would have returned above, and wouldn't fall + // through. + for (int i = 0; i < cpu->system->execContexts.size(); i++){ + xc = cpu->system->execContexts[i]; + if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) == + (req->paddr & ~0xf)) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + } + } + +#endif + return thread->mem->write(req, (T)LittleEndianGuest::htog(data)); +} +#endif + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::read(MemReqPtr &req, T &data, int load_idx) +{ +// panic("Unimplemented!"); +// memReq->reset(addr, sizeof(T), flags); + + // translate to physical address +// Fault fault = cpu->translateDataReadReq(req); + req->cmd = Read; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + req->flags &= ~INST_READ; + Fault fault = cpu->read(req, data); + memcpy(req->data, &data, sizeof(T)); + + // if we have a cache, do cache access too + if (dcacheInterface) { + MemAccessResult result = dcacheInterface->access(req); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + req->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissLoadStall; + DPRINTF(IBE, "Dcache miss load stall!\n"); + } else { + DPRINTF(IBE, "Dcache hit!\n"); + + } + } + +/* + if (!dcacheInterface && (req->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); +*/ + return NoFault; +} + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::write(MemReqPtr &req, T &data, int store_idx) +{ +// req->reset(addr, sizeof(T), flags); + + // translate to physical address +// Fault fault = cpu->translateDataWriteReq(req); + + req->cmd = Write; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + memcpy(req->data, (uint8_t *)&data, req->size); + + switch(req->size) { + case 1: + cpu->write(req, (uint8_t &)data); + break; + case 2: + cpu->write(req, (uint16_t &)data); + break; + case 4: + cpu->write(req, (uint32_t &)data); + break; + case 8: + cpu->write(req, (uint64_t &)data); + break; + default: + panic("Unexpected store size!\n"); + } + + if (dcacheInterface) { + req->cmd = Write; + req->data = new uint8_t[64]; + memcpy(req->data,(uint8_t *)&data,req->size); + req->completionEvent = NULL; + req->time = curTick; + req->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(req); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + req->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissStoreStall; + DPRINTF(IBE, "Dcache miss store stall!\n"); + } else { + DPRINTF(IBE, "Dcache hit!\n"); + + } + } +/* + if (req->flags & LOCKED) { + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + } else { + req->result = 1; + } + } +*/ +/* + if (res && (fault == NoFault)) + *res = req->result; + */ +/* + if (!dcacheInterface && (req->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); +*/ + return NoFault; +} + +#endif // __CPU_OZONE_INORDER_BACK_END_HH__ diff --git a/src/cpu/ozone/inorder_back_end_impl.hh b/src/cpu/ozone/inorder_back_end_impl.hh new file mode 100644 index 000000000..cbb73364e --- /dev/null +++ b/src/cpu/ozone/inorder_back_end_impl.hh @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "cpu/ozone/inorder_back_end.hh" +#include "cpu/ozone/thread_state.hh" + +using namespace TheISA; + +template <class Impl> +InorderBackEnd<Impl>::InorderBackEnd(Params *params) + : squashPending(false), + squashSeqNum(0), + squashNextPC(0), + faultFromFetch(NoFault), + interruptBlocked(false), + cacheCompletionEvent(this), + dcacheInterface(params->dcacheInterface), + width(params->backEndWidth), + latency(params->backEndLatency), + squashLatency(params->backEndSquashLatency), + numInstsToWB(0, latency + 1) +{ + instsAdded = numInstsToWB.getWire(latency); + instsToExecute = numInstsToWB.getWire(0); + + memReq = new MemReq; + memReq->data = new uint8_t[64]; + status = Running; +} + +template <class Impl> +std::string +InorderBackEnd<Impl>::name() const +{ + return cpu->name() + ".inorderbackend"; +} + +template <class Impl> +void +InorderBackEnd<Impl>::setXC(ExecContext *xc_ptr) +{ + xc = xc_ptr; + memReq->xc = xc; +} + +template <class Impl> +void +InorderBackEnd<Impl>::setThreadState(OzoneThreadState<Impl> *thread_ptr) +{ + thread = thread_ptr; + thread->setFuncExeInst(0); +} + +#if FULL_SYSTEM +template <class Impl> +void +InorderBackEnd<Impl>::checkInterrupts() +{ + //Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + cpu->checkInterrupts = false; + + if (thread->readMiscReg(IPR_ASTRR)) + panic("asynchronous traps not implemented\n"); + + if (thread->readMiscReg(IPR_SIRR)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (thread->readMiscReg(IPR_SIRR) & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = cpu->intr_status(); + + if (interrupts) { + for (int i = INTLEVEL_EXTERNAL_MIN; + i < INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + } + + if (ipl && ipl > thread->readMiscReg(IPR_IPLR)) { + thread->inSyscall = true; + + thread->setMiscReg(IPR_ISR, summary); + thread->setMiscReg(IPR_INTID, ipl); + Fault(new InterruptFault)->invoke(xc); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + thread->readMiscReg(IPR_IPLR), ipl, summary); + + // May need to go 1 inst prior + squashPending = true; + + thread->inSyscall = false; + + setSquashInfoFromXC(); + } +} +#endif + +template <class Impl> +void +InorderBackEnd<Impl>::tick() +{ + // Squash due to an external source + // Not sure if this or an interrupt has higher priority + if (squashPending) { + squash(squashSeqNum, squashNextPC); + return; + } + + // if (interrupt) then set thread PC, stall front end, record that + // I'm waiting for it to drain. (for now just squash) +#if FULL_SYSTEM + if (interruptBlocked || + (cpu->checkInterrupts && + cpu->check_interrupts() && + !cpu->inPalMode())) { + if (!robEmpty()) { + interruptBlocked = true; + } else if (robEmpty() && cpu->inPalMode()) { + // Will need to let the front end continue a bit until + // we're out of pal mode. Hopefully we never get into an + // infinite loop... + interruptBlocked = false; + } else { + interruptBlocked = false; + checkInterrupts(); + return; + } + } +#endif + + if (status != DcacheMissLoadStall && + status != DcacheMissStoreStall) { + for (int i = 0; i < width && (*instsAdded) < width; ++i) { + DynInstPtr inst = frontEnd->getInst(); + + if (!inst) + break; + + instList.push_back(inst); + + (*instsAdded)++; + } + +#if FULL_SYSTEM + if (faultFromFetch && robEmpty() && frontEnd->isEmpty()) { + handleFault(); + } else { + executeInsts(); + } +#else + executeInsts(); +#endif + } +} + +template <class Impl> +void +InorderBackEnd<Impl>::executeInsts() +{ + bool completed_last_inst = true; + int insts_to_execute = *instsToExecute; + int freed_regs = 0; + + while (insts_to_execute > 0) { + assert(!instList.empty()); + DynInstPtr inst = instList.front(); + + commitPC = inst->readPC(); + + thread->setPC(commitPC); + thread->setNextPC(inst->readNextPC()); + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread->inSyscall && !thread->trapPending); + oldpc = thread->readPC(); + cpu->system->pcEventQueue.service( + thread->getXCProxy()); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + DPRINTF(IBE, "PC skip function event, stopping commit\n"); + completed_last_inst = false; + squashPending = true; + break; + } +#endif + + Fault inst_fault = NoFault; + + if (status == DcacheMissComplete) { + DPRINTF(IBE, "Completing inst [sn:%lli]\n", inst->seqNum); + status = Running; + } else if (inst->isMemRef() && status != DcacheMissComplete && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(IBE, "Initiating mem op inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + + cacheCompletionEvent.inst = inst; + inst_fault = inst->initiateAcc(); + if (inst_fault == NoFault && + status != DcacheMissLoadStall && + status != DcacheMissStoreStall) { + inst_fault = inst->completeAcc(); + } + ++thread->funcExeInst; + } else { + DPRINTF(IBE, "Executing inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + inst_fault = inst->execute(); + ++thread->funcExeInst; + } + + // Will need to be able to break this loop in case the load + // misses. Split access/complete ops would be useful here + // with writeback events. + if (status == DcacheMissLoadStall) { + *instsToExecute = insts_to_execute; + + completed_last_inst = false; + break; + } else if (status == DcacheMissStoreStall) { + // Figure out how to fix this hack. Probably have DcacheMissLoad + // vs DcacheMissStore. + *instsToExecute = insts_to_execute; + completed_last_inst = false; +/* + instList.pop_front(); + --insts_to_execute; + if (inst->traceData) { + inst->traceData->finalize(); + } +*/ + + // Don't really need to stop for a store stall as long as + // the memory system is able to handle store forwarding + // and such. Breaking out might help avoid the cache + // interface becoming blocked. + break; + } + + inst->setExecuted(); + inst->setCompleted(); + inst->setCanCommit(); + + instList.pop_front(); + + --insts_to_execute; + --(*instsToExecute); + + if (inst->traceData) { + inst->traceData->finalize(); + inst->traceData = NULL; + } + + if (inst_fault != NoFault) { +#if FULL_SYSTEM + DPRINTF(IBE, "Inst [sn:%lli] PC %#x has a fault\n", + inst->seqNum, inst->readPC()); + + assert(!thread->inSyscall); + + thread->inSyscall = true; + + // Hack for now; DTB will sometimes need the machine instruction + // for when faults happen. So we will set it here, prior to the + // DTB possibly needing it for this translation. + thread->setInst( + static_cast<TheISA::MachInst>(inst->staticInst->machInst)); + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + inst_fault->invoke(xc); + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + squashPending = true; + + // Generate trap squash event. +// generateTrapEvent(tid); + completed_last_inst = false; + break; +#else // !FULL_SYSTEM + panic("fault (%d) detected @ PC %08p", inst_fault, + inst->PC); +#endif // FULL_SYSTEM + } + + for (int i = 0; i < inst->numDestRegs(); ++i) { + renameTable[inst->destRegIdx(i)] = inst; + thread->renameTable[inst->destRegIdx(i)] = inst; + ++freed_regs; + } + + inst->clearDependents(); + + comm->access(0)->doneSeqNum = inst->seqNum; + + if (inst->mispredicted()) { + squash(inst->seqNum, inst->readNextPC()); + + thread->setNextPC(inst->readNextPC()); + + break; + } else if (squashPending) { + // Something external happened that caused the CPU to squash. + // Break out of commit and handle the squash next cycle. + break; + } + // If it didn't mispredict, then it executed fine. Send back its + // registers and BP info? What about insts that may still have + // latency, like loads? Probably can send back the information after + // it is completed. + + // keep an instruction count + cpu->numInst++; + thread->numInsts++; + } + + frontEnd->addFreeRegs(freed_regs); + + assert(insts_to_execute >= 0); + + // Should only advance this if I have executed all instructions. + if (insts_to_execute == 0) { + numInstsToWB.advance(); + } + + // Should I set the PC to the next PC here? What do I set next PC to? + if (completed_last_inst) { + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readPC() + sizeof(MachInst)); + } + + if (squashPending) { + setSquashInfoFromXC(); + } +} + +template <class Impl> +void +InorderBackEnd<Impl>::handleFault() +{ + DPRINTF(Commit, "Handling fault from fetch\n"); + + assert(!thread->inSyscall); + + thread->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + faultFromFetch->invoke(xc); + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + squashPending = true; + + setSquashInfoFromXC(); +} + +template <class Impl> +void +InorderBackEnd<Impl>::squash(const InstSeqNum &squash_num, const Addr &next_PC) +{ + DPRINTF(IBE, "Squashing from [sn:%lli], setting PC to %#x\n", + squash_num, next_PC); + + InstListIt squash_it = --(instList.end()); + + int freed_regs = 0; + + while (!instList.empty() && (*squash_it)->seqNum > squash_num) { + DynInstPtr inst = *squash_it; + + DPRINTF(IBE, "Squashing instruction PC %#x, [sn:%lli].\n", + inst->readPC(), + inst->seqNum); + + // May cause problems with misc regs + freed_regs+= inst->numDestRegs(); + inst->clearDependents(); + squash_it--; + instList.pop_back(); + } + + frontEnd->addFreeRegs(freed_regs); + + for (int i = 0; i < latency+1; ++i) { + numInstsToWB.advance(); + } + + squashPending = false; + + // Probably want to make sure that this squash is the one that set the + // thread into inSyscall mode. + thread->inSyscall = false; + + // Tell front end to squash, reset PC to new one. + frontEnd->squash(squash_num, next_PC); + + faultFromFetch = NULL; +} + +template <class Impl> +void +InorderBackEnd<Impl>::squashFromXC() +{ + // Record that I need to squash + squashPending = true; + + thread->inSyscall = true; +} + +template <class Impl> +void +InorderBackEnd<Impl>::setSquashInfoFromXC() +{ + // Need to handle the case of the instList being empty. In that case + // probably any number works, except maybe with stores in the store buffer. + squashSeqNum = instList.empty() ? 0 : instList.front()->seqNum - 1; + + squashNextPC = thread->PC; +} + +template <class Impl> +void +InorderBackEnd<Impl>::fetchFault(Fault &fault) +{ + faultFromFetch = fault; +} + +template <class Impl> +void +InorderBackEnd<Impl>::dumpInsts() +{ + int num = 0; + int valid_num = 0; + + InstListIt inst_list_it = instList.begin(); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } +} + +template <class Impl> +InorderBackEnd<Impl>::DCacheCompletionEvent::DCacheCompletionEvent( + InorderBackEnd *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ +// this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +InorderBackEnd<Impl>::DCacheCompletionEvent::process() +{ + inst->completeAcc(); + be->status = DcacheMissComplete; +} + +template <class Impl> +const char * +InorderBackEnd<Impl>::DCacheCompletionEvent::description() +{ + return "DCache completion event"; +} diff --git a/src/cpu/ozone/inst_queue.cc b/src/cpu/ozone/inst_queue.cc new file mode 100644 index 000000000..7ce5d67ad --- /dev/null +++ b/src/cpu/ozone/inst_queue.cc @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_impl.hh" +#include "cpu/ozone/inst_queue_impl.hh" + +// Force instantiation of InstructionQueue. +template class InstQueue<SimpleImpl>; +template class InstQueue<OzoneImpl>; diff --git a/src/cpu/ozone/inst_queue.hh b/src/cpu/ozone/inst_queue.hh new file mode 100644 index 000000000..0158fd2d2 --- /dev/null +++ b/src/cpu/ozone/inst_queue.hh @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_INST_QUEUE_HH__ +#define __CPU_OZONE_INST_QUEUE_HH__ + +#include <list> +#include <map> +#include <queue> +#include <vector> + +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "sim/host.hh" + +class FUPool; +class MemInterface; + +/** + * A standard instruction queue class. It holds ready instructions, in + * order, in seperate priority queues to facilitate the scheduling of + * instructions. The IQ uses a separate linked list to track dependencies. + * Similar to the rename map and the free list, it expects that + * floating point registers have their indices start after the integer + * registers (ie with 96 int and 96 fp registers, regs 0-95 are integer + * and 96-191 are fp). This remains true even for both logical and + * physical register indices. The IQ depends on the memory dependence unit to + * track when memory operations are ready in terms of ordering; register + * dependencies are tracked normally. Right now the IQ also handles the + * execution timing; this is mainly to allow back-to-back scheduling without + * requiring IEW to be able to peek into the IQ. At the end of the execution + * latency, the instruction is put into the queue to execute, where it will + * have the execute() function called on it. + * @todo: Make IQ able to handle multiple FU pools. + */ +template <class Impl> +class InstQueue +{ + public: + //Typedefs from the Impl. + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::Params Params; + typedef typename Impl::IssueStruct IssueStruct; +/* + typedef typename Impl::CPUPol::IEW IEW; + typedef typename Impl::CPUPol::MemDepUnit MemDepUnit; + typedef typename Impl::CPUPol::IssueStruct IssueStruct; + typedef typename Impl::CPUPol::TimeStruct TimeStruct; +*/ + // Typedef of iterator through the list of instructions. + typedef typename std::list<DynInstPtr>::iterator ListIt; + + friend class Impl::FullCPU; +#if 0 + /** FU completion event class. */ + class FUCompletion : public Event { + private: + /** Executing instruction. */ + DynInstPtr inst; + + /** Index of the FU used for executing. */ + int fuIdx; + + /** Pointer back to the instruction queue. */ + InstQueue<Impl> *iqPtr; + + public: + /** Construct a FU completion event. */ + FUCompletion(DynInstPtr &_inst, int fu_idx, + InstQueue<Impl> *iq_ptr); + + virtual void process(); + virtual const char *description(); + }; +#endif + /** Constructs an IQ. */ + InstQueue(Params *params); + + /** Destructs the IQ. */ + ~InstQueue(); + + /** Returns the name of the IQ. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Sets CPU pointer. */ + void setCPU(FullCPU *_cpu) { cpu = _cpu; } +#if 0 + /** Sets active threads list. */ + void setActiveThreads(list<unsigned> *at_ptr); + + /** Sets the IEW pointer. */ + void setIEW(IEW *iew_ptr) { iewStage = iew_ptr; } +#endif + /** Sets the timer buffer between issue and execute. */ + void setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2eQueue); +#if 0 + /** Sets the global time buffer. */ + void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + + /** Number of entries needed for given amount of threads. */ + int entryAmount(int num_threads); + + /** Resets max entries for all threads. */ + void resetEntries(); +#endif + /** Returns total number of free entries. */ + unsigned numFreeEntries(); + + /** Returns number of free entries for a thread. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns whether or not the IQ is full. */ + bool isFull(); + + /** Returns whether or not the IQ is full for a specific thread. */ + bool isFull(unsigned tid); + + /** Returns if there are any ready instructions in the IQ. */ + bool hasReadyInsts(); + + /** Inserts a new instruction into the IQ. */ + void insert(DynInstPtr &new_inst); + + /** Inserts a new, non-speculative instruction into the IQ. */ + void insertNonSpec(DynInstPtr &new_inst); +#if 0 + /** + * Advances the tail of the IQ, used if an instruction is not added to the + * IQ for scheduling. + * @todo: Rename this function. + */ + void advanceTail(DynInstPtr &inst); + + /** Process FU completion event. */ + void processFUCompletion(DynInstPtr &inst, int fu_idx); +#endif + /** + * Schedules ready instructions, adding the ready ones (oldest first) to + * the queue to execute. + */ + void scheduleReadyInsts(); + + /** Schedules a single specific non-speculative instruction. */ + void scheduleNonSpec(const InstSeqNum &inst); + + /** + * Commits all instructions up to and including the given sequence number, + * for a specific thread. + */ + void commit(const InstSeqNum &inst, unsigned tid = 0); + + /** Wakes all dependents of a completed instruction. */ + void wakeDependents(DynInstPtr &completed_inst); + + /** Adds a ready memory instruction to the ready list. */ + void addReadyMemInst(DynInstPtr &ready_inst); +#if 0 + /** + * Reschedules a memory instruction. It will be ready to issue once + * replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &resched_inst); + + /** Replays a memory instruction. It must be rescheduled first. */ + void replayMemInst(DynInstPtr &replay_inst); +#endif + /** Completes a memory operation. */ + void completeMemInst(DynInstPtr &completed_inst); +#if 0 + /** Indicates an ordering violation between a store and a load. */ + void violation(DynInstPtr &store, DynInstPtr &faulting_load); +#endif + /** + * Squashes instructions for a thread. Squashing information is obtained + * from the time buffer. + */ + void squash(unsigned tid); // Probably want the ISN + + /** Returns the number of used entries for a thread. */ + unsigned getCount(unsigned tid) { return count[tid]; }; + + /** Updates the number of free entries. */ + void updateFreeEntries(int num) { freeEntries += num; } + + /** Debug function to print all instructions. */ + void printInsts(); + + private: + /** Does the actual squashing. */ + void doSquash(unsigned tid); + + ///////////////////////// + // Various pointers + ///////////////////////// + + /** Pointer to the CPU. */ + FullCPU *cpu; + + /** Cache interface. */ + MemInterface *dcacheInterface; +#if 0 + /** Pointer to IEW stage. */ + IEW *iewStage; + + /** The memory dependence unit, which tracks/predicts memory dependences + * between instructions. + */ + MemDepUnit memDepUnit[Impl::MaxThreads]; +#endif + /** The queue to the execute stage. Issued instructions will be written + * into it. + */ + TimeBuffer<IssueStruct> *issueToExecuteQueue; +#if 0 + /** The backwards time buffer. */ + TimeBuffer<TimeStruct> *timeBuffer; + + /** Wire to read information from timebuffer. */ + typename TimeBuffer<TimeStruct>::wire fromCommit; + + /** Function unit pool. */ + FUPool *fuPool; +#endif + ////////////////////////////////////// + // Instruction lists, ready queues, and ordering + ////////////////////////////////////// + + /** List of all the instructions in the IQ (some of which may be issued). */ + std::list<DynInstPtr> instList[Impl::MaxThreads]; + + /** + * Struct for comparing entries to be added to the priority queue. This + * gives reverse ordering to the instructions in terms of sequence + * numbers: the instructions with smaller sequence numbers (and hence + * are older) will be at the top of the priority queue. + */ + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; + + /** + * Struct for an IQ entry. It includes the instruction and an iterator + * to the instruction's spot in the IQ. + */ + struct IQEntry { + DynInstPtr inst; + ListIt iqIt; + }; + + typedef std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> + ReadyInstQueue; + + typedef std::map<DynInstPtr, pqCompare> ReadyInstMap; + typedef typename std::map<DynInstPtr, pqCompare>::iterator ReadyMapIt; + + /** List of ready instructions. + */ + ReadyInstQueue readyInsts; + + /** List of non-speculative instructions that will be scheduled + * once the IQ gets a signal from commit. While it's redundant to + * have the key be a part of the value (the sequence number is stored + * inside of DynInst), when these instructions are woken up only + * the sequence number will be available. Thus it is most efficient to be + * able to search by the sequence number alone. + */ + std::map<InstSeqNum, DynInstPtr> nonSpecInsts; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator NonSpecMapIt; +#if 0 + /** Entry for the list age ordering by op class. */ + struct ListOrderEntry { + OpClass queueType; + InstSeqNum oldestInst; + }; + + /** List that contains the age order of the oldest instruction of each + * ready queue. Used to select the oldest instruction available + * among op classes. + */ + std::list<ListOrderEntry> listOrder; + + typedef typename std::list<ListOrderEntry>::iterator ListOrderIt; + + /** Tracks if each ready queue is on the age order list. */ + bool queueOnList[Num_OpClasses]; + + /** Iterators of each ready queue. Points to their spot in the age order + * list. + */ + ListOrderIt readyIt[Num_OpClasses]; + + /** Add an op class to the age order list. */ + void addToOrderList(OpClass op_class); + + /** + * Called when the oldest instruction has been removed from a ready queue; + * this places that ready queue into the proper spot in the age order list. + */ + void moveToYoungerInst(ListOrderIt age_order_it); +#endif + ////////////////////////////////////// + // Various parameters + ////////////////////////////////////// +#if 0 + /** IQ Resource Sharing Policy */ + enum IQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** IQ sharing policy for SMT. */ + IQPolicy iqPolicy; +#endif + /** Number of Total Threads*/ + unsigned numThreads; +#if 0 + /** Pointer to list of active threads. */ + list<unsigned> *activeThreads; +#endif + /** Per Thread IQ count */ + unsigned count[Impl::MaxThreads]; + + /** Max IQ Entries Per Thread */ + unsigned maxEntries[Impl::MaxThreads]; + + /** Number of free IQ entries left. */ + unsigned freeEntries; + + /** The number of entries in the instruction queue. */ + unsigned numEntries; + + /** The total number of instructions that can be issued in one cycle. */ + unsigned totalWidth; +#if 0 + /** The number of physical registers in the CPU. */ + unsigned numPhysRegs; + + /** The number of physical integer registers in the CPU. */ + unsigned numPhysIntRegs; + + /** The number of floating point registers in the CPU. */ + unsigned numPhysFloatRegs; +#endif + /** Delay between commit stage and the IQ. + * @todo: Make there be a distinction between the delays within IEW. + */ + unsigned commitToIEWDelay; + + ////////////////////////////////// + // Variables needed for squashing + ////////////////////////////////// + + /** The sequence number of the squashed instruction. */ + InstSeqNum squashedSeqNum[Impl::MaxThreads]; + + /** Iterator that points to the last instruction that has been squashed. + * This will not be valid unless the IQ is in the process of squashing. + */ + ListIt squashIt[Impl::MaxThreads]; +#if 0 + /////////////////////////////////// + // Dependency graph stuff + /////////////////////////////////// + + class DependencyEntry + { + public: + DependencyEntry() + : inst(NULL), next(NULL) + { } + + DynInstPtr inst; + //Might want to include data about what arch. register the + //dependence is waiting on. + DependencyEntry *next; + + //This function, and perhaps this whole class, stand out a little + //bit as they don't fit a classification well. I want access + //to the underlying structure of the linked list, yet at + //the same time it feels like this should be something abstracted + //away. So for now it will sit here, within the IQ, until + //a better implementation is decided upon. + // This function probably shouldn't be within the entry... + void insert(DynInstPtr &new_inst); + + void remove(DynInstPtr &inst_to_remove); + + // Debug variable, remove when done testing. + static unsigned mem_alloc_counter; + }; + + /** Array of linked lists. Each linked list is a list of all the + * instructions that depend upon a given register. The actual + * register's index is used to index into the graph; ie all + * instructions in flight that are dependent upon r34 will be + * in the linked list of dependGraph[34]. + */ + DependencyEntry *dependGraph; + + /** A cache of the recently woken registers. It is 1 if the register + * has been woken up recently, and 0 if the register has been added + * to the dependency graph and has not yet received its value. It + * is basically a secondary scoreboard, and should pretty much mirror + * the scoreboard that exists in the rename map. + */ + vector<bool> regScoreboard; + + /** Adds an instruction to the dependency graph, as a producer. */ + bool addToDependents(DynInstPtr &new_inst); + + /** Adds an instruction to the dependency graph, as a consumer. */ + void createDependency(DynInstPtr &new_inst); +#endif + /** Moves an instruction to the ready queue if it is ready. */ + void addIfReady(DynInstPtr &inst); + + /** Debugging function to count how many entries are in the IQ. It does + * a linear walk through the instructions, so do not call this function + * during normal execution. + */ + int countInsts(); +#if 0 + /** Debugging function to dump out the dependency graph. + */ + void dumpDependGraph(); +#endif + /** Debugging function to dump all the list sizes, as well as print + * out the list of nonspeculative instructions. Should not be used + * in any other capacity, but it has no harmful sideaffects. + */ + void dumpLists(); + + /** Debugging function to dump out all instructions that are in the + * IQ. + */ + void dumpInsts(); + + /** Stat for number of instructions added. */ + Stats::Scalar<> iqInstsAdded; + /** Stat for number of non-speculative instructions added. */ + Stats::Scalar<> iqNonSpecInstsAdded; +// Stats::Scalar<> iqIntInstsAdded; + /** Stat for number of integer instructions issued. */ + Stats::Scalar<> iqIntInstsIssued; +// Stats::Scalar<> iqFloatInstsAdded; + /** Stat for number of floating point instructions issued. */ + Stats::Scalar<> iqFloatInstsIssued; +// Stats::Scalar<> iqBranchInstsAdded; + /** Stat for number of branch instructions issued. */ + Stats::Scalar<> iqBranchInstsIssued; +// Stats::Scalar<> iqMemInstsAdded; + /** Stat for number of memory instructions issued. */ + Stats::Scalar<> iqMemInstsIssued; +// Stats::Scalar<> iqMiscInstsAdded; + /** Stat for number of miscellaneous instructions issued. */ + Stats::Scalar<> iqMiscInstsIssued; + /** Stat for number of squashed instructions that were ready to issue. */ + Stats::Scalar<> iqSquashedInstsIssued; + /** Stat for number of squashed instructions examined when squashing. */ + Stats::Scalar<> iqSquashedInstsExamined; + /** Stat for number of squashed instruction operands examined when + * squashing. + */ + Stats::Scalar<> iqSquashedOperandsExamined; + /** Stat for number of non-speculative instructions removed due to a squash. + */ + Stats::Scalar<> iqSquashedNonSpecRemoved; + +}; + +#endif //__CPU_OZONE_INST_QUEUE_HH__ diff --git a/src/cpu/ozone/inst_queue_impl.hh b/src/cpu/ozone/inst_queue_impl.hh new file mode 100644 index 000000000..f2d80e621 --- /dev/null +++ b/src/cpu/ozone/inst_queue_impl.hh @@ -0,0 +1,1343 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +// Todo: +// Current ordering allows for 0 cycle added-to-scheduled. Could maybe fake +// it; either do in reverse order, or have added instructions put into a +// different ready queue that, in scheduleRreadyInsts(), gets put onto the +// normal ready queue. This would however give only a one cycle delay, +// but probably is more flexible to actually add in a delay parameter than +// just running it backwards. + +#include <vector> + +#include "sim/root.hh" + +#include "cpu/ozone/inst_queue.hh" +#if 0 +template <class Impl> +InstQueue<Impl>::FUCompletion::FUCompletion(DynInstPtr &_inst, + int fu_idx, + InstQueue<Impl> *iq_ptr) + : Event(&mainEventQueue, Stat_Event_Pri), + inst(_inst), fuIdx(fu_idx), iqPtr(iq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +InstQueue<Impl>::FUCompletion::process() +{ + iqPtr->processFUCompletion(inst, fuIdx); +} + + +template <class Impl> +const char * +InstQueue<Impl>::FUCompletion::description() +{ + return "Functional unit completion event"; +} +#endif +template <class Impl> +InstQueue<Impl>::InstQueue(Params *params) + : dcacheInterface(params->dcacheInterface), +// fuPool(params->fuPool), + numEntries(params->numIQEntries), + totalWidth(params->issueWidth), +// numPhysIntRegs(params->numPhysIntRegs), +// numPhysFloatRegs(params->numPhysFloatRegs), + commitToIEWDelay(params->commitToIEWDelay) +{ +// assert(fuPool); + +// numThreads = params->numberOfThreads; + numThreads = 1; + + //Initialize thread IQ counts + for (int i = 0; i <numThreads; i++) { + count[i] = 0; + } + + // Initialize the number of free IQ entries. + freeEntries = numEntries; + + // Set the number of physical registers as the number of int + float +// numPhysRegs = numPhysIntRegs + numPhysFloatRegs; + +// DPRINTF(IQ, "There are %i physical registers.\n", numPhysRegs); + + //Create an entry for each physical register within the + //dependency graph. +// dependGraph = new DependencyEntry[numPhysRegs]; + + // Resize the register scoreboard. +// regScoreboard.resize(numPhysRegs); +/* + //Initialize Mem Dependence Units + for (int i = 0; i < numThreads; i++) { + memDepUnit[i].init(params,i); + memDepUnit[i].setIQ(this); + } + + // Initialize all the head pointers to point to NULL, and all the + // entries as unready. + // Note that in actuality, the registers corresponding to the logical + // registers start off as ready. However this doesn't matter for the + // IQ as the instruction should have been correctly told if those + // registers are ready in rename. Thus it can all be initialized as + // unready. + for (int i = 0; i < numPhysRegs; ++i) { + dependGraph[i].next = NULL; + dependGraph[i].inst = NULL; + regScoreboard[i] = false; + } +*/ + for (int i = 0; i < numThreads; ++i) { + squashedSeqNum[i] = 0; + } +/* + for (int i = 0; i < Num_OpClasses; ++i) { + queueOnList[i] = false; + readyIt[i] = listOrder.end(); + } + + string policy = params->smtIQPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out resource sharing policy + if (policy == "dynamic") { + iqPolicy = Dynamic; + + //Set Max Entries to Total ROB Capacity + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = numEntries; + } + + } else if (policy == "partitioned") { + iqPolicy = Partitioned; + + //@todo:make work if part_amt doesnt divide evenly. + int part_amt = numEntries / numThreads; + + //Divide ROB up evenly + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = part_amt; + } + + DPRINTF(Fetch, "IQ sharing policy set to Partitioned:" + "%i entries per thread.\n",part_amt); + + } else if (policy == "threshold") { + iqPolicy = Threshold; + + double threshold = (double)params->smtIQThreshold / 100; + + int thresholdIQ = (int)((double)threshold * numEntries); + + //Divide up by threshold amount + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = thresholdIQ; + } + + DPRINTF(Fetch, "IQ sharing policy set to Threshold:" + "%i entries per thread.\n",thresholdIQ); + } else { + assert(0 && "Invalid IQ Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } +*/ +} + +template <class Impl> +InstQueue<Impl>::~InstQueue() +{ + // Clear the dependency graph +/* + DependencyEntry *curr; + DependencyEntry *prev; + + for (int i = 0; i < numPhysRegs; ++i) { + curr = dependGraph[i].next; + + while (curr) { + DependencyEntry::mem_alloc_counter--; + + prev = curr; + curr = prev->next; + prev->inst = NULL; + + delete prev; + } + + if (dependGraph[i].inst) { + dependGraph[i].inst = NULL; + } + + dependGraph[i].next = NULL; + } + + assert(DependencyEntry::mem_alloc_counter == 0); + + delete [] dependGraph; +*/ +} + +template <class Impl> +std::string +InstQueue<Impl>::name() const +{ + return cpu->name() + ".iq"; +} + +template <class Impl> +void +InstQueue<Impl>::regStats() +{ + iqInstsAdded + .name(name() + ".iqInstsAdded") + .desc("Number of instructions added to the IQ (excludes non-spec)") + .prereq(iqInstsAdded); + + iqNonSpecInstsAdded + .name(name() + ".iqNonSpecInstsAdded") + .desc("Number of non-speculative instructions added to the IQ") + .prereq(iqNonSpecInstsAdded); + +// iqIntInstsAdded; + + iqIntInstsIssued + .name(name() + ".iqIntInstsIssued") + .desc("Number of integer instructions issued") + .prereq(iqIntInstsIssued); + +// iqFloatInstsAdded; + + iqFloatInstsIssued + .name(name() + ".iqFloatInstsIssued") + .desc("Number of float instructions issued") + .prereq(iqFloatInstsIssued); + +// iqBranchInstsAdded; + + iqBranchInstsIssued + .name(name() + ".iqBranchInstsIssued") + .desc("Number of branch instructions issued") + .prereq(iqBranchInstsIssued); + +// iqMemInstsAdded; + + iqMemInstsIssued + .name(name() + ".iqMemInstsIssued") + .desc("Number of memory instructions issued") + .prereq(iqMemInstsIssued); + +// iqMiscInstsAdded; + + iqMiscInstsIssued + .name(name() + ".iqMiscInstsIssued") + .desc("Number of miscellaneous instructions issued") + .prereq(iqMiscInstsIssued); + + iqSquashedInstsIssued + .name(name() + ".iqSquashedInstsIssued") + .desc("Number of squashed instructions issued") + .prereq(iqSquashedInstsIssued); + + iqSquashedInstsExamined + .name(name() + ".iqSquashedInstsExamined") + .desc("Number of squashed instructions iterated over during squash;" + " mainly for profiling") + .prereq(iqSquashedInstsExamined); + + iqSquashedOperandsExamined + .name(name() + ".iqSquashedOperandsExamined") + .desc("Number of squashed operands that are examined and possibly " + "removed from graph") + .prereq(iqSquashedOperandsExamined); + + iqSquashedNonSpecRemoved + .name(name() + ".iqSquashedNonSpecRemoved") + .desc("Number of squashed non-spec instructions that were removed") + .prereq(iqSquashedNonSpecRemoved); +/* + for ( int i=0; i < numThreads; i++) { + // Tell mem dependence unit to reg stats as well. + memDepUnit[i].regStats(); + } +*/ +} +/* +template <class Impl> +void +InstQueue<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(IQ, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} +*/ +template <class Impl> +void +InstQueue<Impl>::setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2e_ptr) +{ + DPRINTF(IQ, "Set the issue to execute queue.\n"); + issueToExecuteQueue = i2e_ptr; +} +/* +template <class Impl> +void +InstQueue<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +{ + DPRINTF(IQ, "Set the time buffer.\n"); + timeBuffer = tb_ptr; + + fromCommit = timeBuffer->getWire(-commitToIEWDelay); +} + +template <class Impl> +int +InstQueue<Impl>::entryAmount(int num_threads) +{ + if (iqPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + + +template <class Impl> +void +InstQueue<Impl>::resetEntries() +{ + if (iqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + list<unsigned>::iterator list_end = (*activeThreads).end(); + + while (threads != list_end) { + if (iqPolicy == Partitioned) { + maxEntries[*threads++] = numEntries / active_threads; + } else if(iqPolicy == Threshold && active_threads == 1) { + maxEntries[*threads++] = numEntries; + } + } + } +} +*/ +template <class Impl> +unsigned +InstQueue<Impl>::numFreeEntries() +{ + return freeEntries; +} + +template <class Impl> +unsigned +InstQueue<Impl>::numFreeEntries(unsigned tid) +{ + return maxEntries[tid] - count[tid]; +} + +// Might want to do something more complex if it knows how many instructions +// will be issued this cycle. +template <class Impl> +bool +InstQueue<Impl>::isFull() +{ + if (freeEntries == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstQueue<Impl>::isFull(unsigned tid) +{ + if (numFreeEntries(tid) == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstQueue<Impl>::hasReadyInsts() +{ +/* + if (!listOrder.empty()) { + return true; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + return true; + } + } + + return false; +*/ + return readyInsts.empty(); +} + +template <class Impl> +void +InstQueue<Impl>::insert(DynInstPtr &new_inst) +{ + // Make sure the instruction is valid + assert(new_inst); + + DPRINTF(IQ, "Adding instruction PC %#x to the IQ.\n", + new_inst->readPC()); + + // Check if there are any free entries. Panic if there are none. + // Might want to have this return a fault in the future instead of + // panicing. + assert(freeEntries != 0); + + instList[new_inst->threadNumber].push_back(new_inst); + + // Decrease the number of free entries. + --freeEntries; + + //Mark Instruction as in IQ +// new_inst->setInIQ(); +/* + // Look through its source registers (physical regs), and mark any + // dependencies. + addToDependents(new_inst); + + // Have this instruction set itself as the producer of its destination + // register(s). + createDependency(new_inst); +*/ + // If it's a memory instruction, add it to the memory dependency + // unit. +// if (new_inst->isMemRef()) { +// memDepUnit[new_inst->threadNumber].insert(new_inst); +// } else { + // If the instruction is ready then add it to the ready list. + addIfReady(new_inst); +// } + + ++iqInstsAdded; + + + //Update Thread IQ Count + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); +} + +template <class Impl> +void +InstQueue<Impl>::insertNonSpec(DynInstPtr &new_inst) +{ + nonSpecInsts[new_inst->seqNum] = new_inst; + + // @todo: Clean up this code; can do it by setting inst as unable + // to issue, then calling normal insert on the inst. + + // Make sure the instruction is valid + assert(new_inst); + + DPRINTF(IQ, "Adding instruction PC %#x to the IQ.\n", + new_inst->readPC()); + + // Check if there are any free entries. Panic if there are none. + // Might want to have this return a fault in the future instead of + // panicing. + assert(freeEntries != 0); + + instList[new_inst->threadNumber].push_back(new_inst); + + // Decrease the number of free entries. + --freeEntries; + + //Mark Instruction as in IQ +// new_inst->setInIQ(); +/* + // Have this instruction set itself as the producer of its destination + // register(s). + createDependency(new_inst); + + // If it's a memory instruction, add it to the memory dependency + // unit. + if (new_inst->isMemRef()) { + memDepUnit[new_inst->threadNumber].insertNonSpec(new_inst); + } +*/ + ++iqNonSpecInstsAdded; + + //Update Thread IQ Count + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); +} +/* +template <class Impl> +void +InstQueue<Impl>::advanceTail(DynInstPtr &inst) +{ + // Have this instruction set itself as the producer of its destination + // register(s). + createDependency(inst); +} + +template <class Impl> +void +InstQueue<Impl>::addToOrderList(OpClass op_class) +{ + assert(!readyInsts[op_class].empty()); + + ListOrderEntry queue_entry; + + queue_entry.queueType = op_class; + + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; + + ListOrderIt list_it = listOrder.begin(); + ListOrderIt list_end_it = listOrder.end(); + + while (list_it != list_end_it) { + if ((*list_it).oldestInst > queue_entry.oldestInst) { + break; + } + + list_it++; + } + + readyIt[op_class] = listOrder.insert(list_it, queue_entry); + queueOnList[op_class] = true; +} + +template <class Impl> +void +InstQueue<Impl>::moveToYoungerInst(ListOrderIt list_order_it) +{ + // Get iterator of next item on the list + // Delete the original iterator + // Determine if the next item is either the end of the list or younger + // than the new instruction. If so, then add in a new iterator right here. + // If not, then move along. + ListOrderEntry queue_entry; + OpClass op_class = (*list_order_it).queueType; + ListOrderIt next_it = list_order_it; + + ++next_it; + + queue_entry.queueType = op_class; + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; + + while (next_it != listOrder.end() && + (*next_it).oldestInst < queue_entry.oldestInst) { + ++next_it; + } + + readyIt[op_class] = listOrder.insert(next_it, queue_entry); +} + +template <class Impl> +void +InstQueue<Impl>::processFUCompletion(DynInstPtr &inst, int fu_idx) +{ + // The CPU could have been sleeping until this op completed (*extremely* + // long latency op). Wake it if it was. This may be overkill. + iewStage->wakeCPU(); + + fuPool->freeUnit(fu_idx); + + int &size = issueToExecuteQueue->access(0)->size; + + issueToExecuteQueue->access(0)->insts[size++] = inst; +} +*/ +// @todo: Figure out a better way to remove the squashed items from the +// lists. Checking the top item of each list to see if it's squashed +// wastes time and forces jumps. +template <class Impl> +void +InstQueue<Impl>::scheduleReadyInsts() +{ + DPRINTF(IQ, "Attempting to schedule ready instructions from " + "the IQ.\n"); + +// IssueStruct *i2e_info = issueToExecuteQueue->access(0); +/* + // Will need to reorder the list if either a queue is not on the list, + // or it has an older instruction than last time. + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + if (!queueOnList[i]) { + addToOrderList(OpClass(i)); + } else if (readyInsts[i].top()->seqNum < + (*readyIt[i]).oldestInst) { + listOrder.erase(readyIt[i]); + addToOrderList(OpClass(i)); + } + } + } + + // Have iterator to head of the list + // While I haven't exceeded bandwidth or reached the end of the list, + // Try to get a FU that can do what this op needs. + // If successful, change the oldestInst to the new top of the list, put + // the queue in the proper place in the list. + // Increment the iterator. + // This will avoid trying to schedule a certain op class if there are no + // FUs that handle it. + ListOrderIt order_it = listOrder.begin(); + ListOrderIt order_end_it = listOrder.end(); + int total_issued = 0; + int exec_queue_slot = i2e_info->size; + + while (exec_queue_slot < totalWidth && order_it != order_end_it) { + OpClass op_class = (*order_it).queueType; + + assert(!readyInsts[op_class].empty()); + + DynInstPtr issuing_inst = readyInsts[op_class].top(); + + assert(issuing_inst->seqNum == (*order_it).oldestInst); + + if (issuing_inst->isSquashed()) { + readyInsts[op_class].pop(); + + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } + + listOrder.erase(order_it++); + + ++iqSquashedInstsIssued; + + continue; + } + + int idx = fuPool->getUnit(op_class); + + if (idx != -1) { + int op_latency = fuPool->getOpLatency(op_class); + + if (op_latency == 1) { + i2e_info->insts[exec_queue_slot++] = issuing_inst; + i2e_info->size++; + + // Add the FU onto the list of FU's to be freed next cycle. + fuPool->freeUnit(idx); + } else { + int issue_latency = fuPool->getIssueLatency(op_class); + + if (issue_latency > 1) { + // Generate completion event for the FU + FUCompletion *execution = new FUCompletion(issuing_inst, + idx, this); + + execution->schedule(curTick + issue_latency - 1); + } else { + i2e_info->insts[exec_queue_slot++] = issuing_inst; + i2e_info->size++; + + // Add the FU onto the list of FU's to be freed next cycle. + fuPool->freeUnit(idx); + } + } + + DPRINTF(IQ, "Thread %i: Issuing instruction PC %#x " + "[sn:%lli]\n", + issuing_inst->threadNumber, issuing_inst->readPC(), + issuing_inst->seqNum); + + readyInsts[op_class].pop(); + + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } + + issuing_inst->setIssued(); + ++total_issued; + + if (!issuing_inst->isMemRef()) { + // Memory instructions can not be freed from the IQ until they + // complete. + ++freeEntries; + count[issuing_inst->threadNumber]--; + issuing_inst->removeInIQ(); + } else { + memDepUnit[issuing_inst->threadNumber].issue(issuing_inst); + } + + listOrder.erase(order_it++); + } else { + ++order_it; + } + } + + if (total_issued) { + cpu->activityThisCycle(); + } else { + DPRINTF(IQ, "Not able to schedule any instructions.\n"); + } +*/ +} + +template <class Impl> +void +InstQueue<Impl>::scheduleNonSpec(const InstSeqNum &inst) +{ + DPRINTF(IQ, "Marking nonspeculative instruction with sequence " + "number %i as ready to execute.\n", inst); + + NonSpecMapIt inst_it = nonSpecInsts.find(inst); + + assert(inst_it != nonSpecInsts.end()); + +// unsigned tid = (*inst_it).second->threadNumber; + + // Mark this instruction as ready to issue. + (*inst_it).second->setCanIssue(); + + // Now schedule the instruction. +// if (!(*inst_it).second->isMemRef()) { + addIfReady((*inst_it).second); +// } else { +// memDepUnit[tid].nonSpecInstReady((*inst_it).second); +// } + + nonSpecInsts.erase(inst_it); +} + +template <class Impl> +void +InstQueue<Impl>::commit(const InstSeqNum &inst, unsigned tid) +{ + /*Need to go through each thread??*/ + DPRINTF(IQ, "[tid:%i]: Committing instructions older than [sn:%i]\n", + tid,inst); + + ListIt iq_it = instList[tid].begin(); + + while (iq_it != instList[tid].end() && + (*iq_it)->seqNum <= inst) { + ++iq_it; + instList[tid].pop_front(); + } + + assert(freeEntries == (numEntries - countInsts())); +} + +template <class Impl> +void +InstQueue<Impl>::wakeDependents(DynInstPtr &completed_inst) +{ + DPRINTF(IQ, "Waking dependents of completed instruction.\n"); + // Look at the physical destination register of the DynInst + // and look it up on the dependency graph. Then mark as ready + // any instructions within the instruction queue. +/* + DependencyEntry *curr; + DependencyEntry *prev; +*/ + // Tell the memory dependence unit to wake any dependents on this + // instruction if it is a memory instruction. Also complete the memory + // instruction at this point since we know it executed fine. + // @todo: Might want to rename "completeMemInst" to + // something that indicates that it won't need to be replayed, and call + // this earlier. Might not be a big deal. + if (completed_inst->isMemRef()) { +// memDepUnit[completed_inst->threadNumber].wakeDependents(completed_inst); + completeMemInst(completed_inst); + } + completed_inst->wakeDependents(); +/* + for (int dest_reg_idx = 0; + dest_reg_idx < completed_inst->numDestRegs(); + dest_reg_idx++) + { + PhysRegIndex dest_reg = + completed_inst->renamedDestRegIdx(dest_reg_idx); + + // Special case of uniq or control registers. They are not + // handled by the IQ and thus have no dependency graph entry. + // @todo Figure out a cleaner way to handle this. + if (dest_reg >= numPhysRegs) { + continue; + } + + DPRINTF(IQ, "Waking any dependents on register %i.\n", + (int) dest_reg); + + //Maybe abstract this part into a function. + //Go through the dependency chain, marking the registers as ready + //within the waiting instructions. + + curr = dependGraph[dest_reg].next; + + while (curr) { + DPRINTF(IQ, "Waking up a dependent instruction, PC%#x.\n", + curr->inst->readPC()); + + // Might want to give more information to the instruction + // so that it knows which of its source registers is ready. + // However that would mean that the dependency graph entries + // would need to hold the src_reg_idx. + curr->inst->markSrcRegReady(); + + addIfReady(curr->inst); + + DependencyEntry::mem_alloc_counter--; + + prev = curr; + curr = prev->next; + prev->inst = NULL; + + delete prev; + } + + // Reset the head node now that all of its dependents have been woken + // up. + dependGraph[dest_reg].next = NULL; + dependGraph[dest_reg].inst = NULL; + + // Mark the scoreboard as having that register ready. + regScoreboard[dest_reg] = true; + } +*/ +} + +template <class Impl> +void +InstQueue<Impl>::addReadyMemInst(DynInstPtr &ready_inst) +{ + OpClass op_class = ready_inst->opClass(); + + readyInsts.push(ready_inst); + + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + ready_inst->readPC(), op_class, ready_inst->seqNum); +} +/* +template <class Impl> +void +InstQueue<Impl>::rescheduleMemInst(DynInstPtr &resched_inst) +{ + memDepUnit[resched_inst->threadNumber].reschedule(resched_inst); +} + +template <class Impl> +void +InstQueue<Impl>::replayMemInst(DynInstPtr &replay_inst) +{ + memDepUnit[replay_inst->threadNumber].replay(replay_inst); +} +*/ +template <class Impl> +void +InstQueue<Impl>::completeMemInst(DynInstPtr &completed_inst) +{ + int tid = completed_inst->threadNumber; + + DPRINTF(IQ, "Completing mem instruction PC:%#x [sn:%lli]\n", + completed_inst->readPC(), completed_inst->seqNum); + + ++freeEntries; + +// completed_inst->memOpDone = true; + +// memDepUnit[tid].completed(completed_inst); + + count[tid]--; +} +/* +template <class Impl> +void +InstQueue<Impl>::violation(DynInstPtr &store, + DynInstPtr &faulting_load) +{ + memDepUnit[store->threadNumber].violation(store, faulting_load); +} +*/ +template <class Impl> +void +InstQueue<Impl>::squash(unsigned tid) +{ + DPRINTF(IQ, "[tid:%i]: Starting to squash instructions in " + "the IQ.\n", tid); + + // Read instruction sequence number of last instruction out of the + // time buffer. +// squashedSeqNum[tid] = fromCommit->commitInfo[tid].doneSeqNum; + + // Setup the squash iterator to point to the tail. + squashIt[tid] = instList[tid].end(); + --squashIt[tid]; + + // Call doSquash if there are insts in the IQ + if (count[tid] > 0) { + doSquash(tid); + } + + // Also tell the memory dependence unit to squash. +// memDepUnit[tid].squash(squashedSeqNum[tid], tid); +} + +template <class Impl> +void +InstQueue<Impl>::doSquash(unsigned tid) +{ + // Make sure the squashed sequence number is valid. + assert(squashedSeqNum[tid] != 0); + + DPRINTF(IQ, "[tid:%i]: Squashing until sequence number %i!\n", + tid, squashedSeqNum[tid]); + + // Squash any instructions younger than the squashed sequence number + // given. + while (squashIt[tid] != instList[tid].end() && + (*squashIt[tid])->seqNum > squashedSeqNum[tid]) { + + DynInstPtr squashed_inst = (*squashIt[tid]); + + // Only handle the instruction if it actually is in the IQ and + // hasn't already been squashed in the IQ. + if (squashed_inst->threadNumber != tid || + squashed_inst->isSquashedInIQ()) { + --squashIt[tid]; + continue; + } + + if (!squashed_inst->isIssued() || + (squashed_inst->isMemRef()/* && + !squashed_inst->memOpDone*/)) { + + // Remove the instruction from the dependency list. + if (!squashed_inst->isNonSpeculative()) { +/* + for (int src_reg_idx = 0; + src_reg_idx < squashed_inst->numSrcRegs(); + src_reg_idx++) + { + PhysRegIndex src_reg = + squashed_inst->renamedSrcRegIdx(src_reg_idx); + + // Only remove it from the dependency graph if it was + // placed there in the first place. + // HACK: This assumes that instructions woken up from the + // dependency chain aren't informed that a specific src + // register has become ready. This may not always be true + // in the future. + // Instead of doing a linked list traversal, we can just + // remove these squashed instructions either at issue time, + // or when the register is overwritten. The only downside + // to this is it leaves more room for error. + + if (!squashed_inst->isReadySrcRegIdx(src_reg_idx) && + src_reg < numPhysRegs) { + dependGraph[src_reg].remove(squashed_inst); + } + + + ++iqSquashedOperandsExamined; + } +*/ + // Might want to remove producers as well. + } else { + nonSpecInsts[squashed_inst->seqNum] = NULL; + + nonSpecInsts.erase(squashed_inst->seqNum); + + ++iqSquashedNonSpecRemoved; + } + + // Might want to also clear out the head of the dependency graph. + + // Mark it as squashed within the IQ. + squashed_inst->setSquashedInIQ(); + + // @todo: Remove this hack where several statuses are set so the + // inst will flow through the rest of the pipeline. + squashed_inst->setIssued(); + squashed_inst->setCanCommit(); +// squashed_inst->removeInIQ(); + + //Update Thread IQ Count + count[squashed_inst->threadNumber]--; + + ++freeEntries; + + if (numThreads > 1) { + DPRINTF(IQ, "[tid:%i]: Instruction PC %#x squashed.\n", + tid, squashed_inst->readPC()); + } else { + DPRINTF(IQ, "Instruction PC %#x squashed.\n", + squashed_inst->readPC()); + } + } + + --squashIt[tid]; + ++iqSquashedInstsExamined; + } +} +/* +template <class Impl> +void +InstQueue<Impl>::DependencyEntry::insert(DynInstPtr &new_inst) +{ + //Add this new, dependent instruction at the head of the dependency + //chain. + + // First create the entry that will be added to the head of the + // dependency chain. + DependencyEntry *new_entry = new DependencyEntry; + new_entry->next = this->next; + new_entry->inst = new_inst; + + // Then actually add it to the chain. + this->next = new_entry; + + ++mem_alloc_counter; +} + +template <class Impl> +void +InstQueue<Impl>::DependencyEntry::remove(DynInstPtr &inst_to_remove) +{ + DependencyEntry *prev = this; + DependencyEntry *curr = this->next; + + // Make sure curr isn't NULL. Because this instruction is being + // removed from a dependency list, it must have been placed there at + // an earlier time. The dependency chain should not be empty, + // unless the instruction dependent upon it is already ready. + if (curr == NULL) { + return; + } + + // Find the instruction to remove within the dependency linked list. + while (curr->inst != inst_to_remove) { + prev = curr; + curr = curr->next; + + assert(curr != NULL); + } + + // Now remove this instruction from the list. + prev->next = curr->next; + + --mem_alloc_counter; + + // Could push this off to the destructor of DependencyEntry + curr->inst = NULL; + + delete curr; +} + +template <class Impl> +bool +InstQueue<Impl>::addToDependents(DynInstPtr &new_inst) +{ + // Loop through the instruction's source registers, adding + // them to the dependency list if they are not ready. + int8_t total_src_regs = new_inst->numSrcRegs(); + bool return_val = false; + + for (int src_reg_idx = 0; + src_reg_idx < total_src_regs; + src_reg_idx++) + { + // Only add it to the dependency graph if it's not ready. + if (!new_inst->isReadySrcRegIdx(src_reg_idx)) { + PhysRegIndex src_reg = new_inst->renamedSrcRegIdx(src_reg_idx); + + // Check the IQ's scoreboard to make sure the register + // hasn't become ready while the instruction was in flight + // between stages. Only if it really isn't ready should + // it be added to the dependency graph. + if (src_reg >= numPhysRegs) { + continue; + } else if (regScoreboard[src_reg] == false) { + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " + "is being added to the dependency chain.\n", + new_inst->readPC(), src_reg); + + dependGraph[src_reg].insert(new_inst); + + // Change the return value to indicate that something + // was added to the dependency graph. + return_val = true; + } else { + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " + "became ready before it reached the IQ.\n", + new_inst->readPC(), src_reg); + // Mark a register ready within the instruction. + new_inst->markSrcRegReady(); + } + } + } + + return return_val; +} + +template <class Impl> +void +InstQueue<Impl>::createDependency(DynInstPtr &new_inst) +{ + //Actually nothing really needs to be marked when an + //instruction becomes the producer of a register's value, + //but for convenience a ptr to the producing instruction will + //be placed in the head node of the dependency links. + int8_t total_dest_regs = new_inst->numDestRegs(); + + for (int dest_reg_idx = 0; + dest_reg_idx < total_dest_regs; + dest_reg_idx++) + { + PhysRegIndex dest_reg = new_inst->renamedDestRegIdx(dest_reg_idx); + + // Instructions that use the misc regs will have a reg number + // higher than the normal physical registers. In this case these + // registers are not renamed, and there is no need to track + // dependencies as these instructions must be executed at commit. + if (dest_reg >= numPhysRegs) { + continue; + } + + if (dependGraph[dest_reg].next) { + dumpDependGraph(); + panic("Dependency graph %i not empty!", dest_reg); + } + + dependGraph[dest_reg].inst = new_inst; + + // Mark the scoreboard to say it's not yet ready. + regScoreboard[dest_reg] = false; + } +} +*/ +template <class Impl> +void +InstQueue<Impl>::addIfReady(DynInstPtr &inst) +{ + //If the instruction now has all of its source registers + // available, then add it to the list of ready instructions. + if (inst->readyToIssue()) { + + //Add the instruction to the proper ready list. + if (inst->isMemRef()) { + + DPRINTF(IQ, "Checking if memory instruction can issue.\n"); + + // Message to the mem dependence unit that this instruction has + // its registers ready. + +// memDepUnit[inst->threadNumber].regsReady(inst); + + return; + } + + OpClass op_class = inst->opClass(); + + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + inst->readPC(), op_class, inst->seqNum); + + readyInsts.push(inst); + } +} + +template <class Impl> +int +InstQueue<Impl>::countInsts() +{ + //ksewell:This works but definitely could use a cleaner write + //with a more intuitive way of counting. Right now it's + //just brute force .... + +#if 0 + int total_insts = 0; + + for (int i = 0; i < numThreads; ++i) { + ListIt count_it = instList[i].begin(); + + while (count_it != instList[i].end()) { + if (!(*count_it)->isSquashed() && !(*count_it)->isSquashedInIQ()) { + if (!(*count_it)->isIssued()) { + ++total_insts; + } else if ((*count_it)->isMemRef() && + !(*count_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++total_insts; + } + } + + ++count_it; + } + } + + return total_insts; +#else + return numEntries - freeEntries; +#endif +} +/* +template <class Impl> +void +InstQueue<Impl>::dumpDependGraph() +{ + DependencyEntry *curr; + + for (int i = 0; i < numPhysRegs; ++i) + { + curr = &dependGraph[i]; + + if (curr->inst) { + cprintf("dependGraph[%i]: producer: %#x [sn:%lli] consumer: ", + i, curr->inst->readPC(), curr->inst->seqNum); + } else { + cprintf("dependGraph[%i]: No producer. consumer: ", i); + } + + while (curr->next != NULL) { + curr = curr->next; + + cprintf("%#x [sn:%lli] ", + curr->inst->readPC(), curr->inst->seqNum); + } + + cprintf("\n"); + } +} +*/ +template <class Impl> +void +InstQueue<Impl>::dumpLists() +{ + for (int i = 0; i < Num_OpClasses; ++i) { + cprintf("Ready list %i size: %i\n", i, readyInsts.size()); + + cprintf("\n"); + } + + cprintf("Non speculative list size: %i\n", nonSpecInsts.size()); + + NonSpecMapIt non_spec_it = nonSpecInsts.begin(); + NonSpecMapIt non_spec_end_it = nonSpecInsts.end(); + + cprintf("Non speculative list: "); + + while (non_spec_it != non_spec_end_it) { + cprintf("%#x [sn:%lli]", (*non_spec_it).second->readPC(), + (*non_spec_it).second->seqNum); + ++non_spec_it; + } + + cprintf("\n"); +/* + ListOrderIt list_order_it = listOrder.begin(); + ListOrderIt list_order_end_it = listOrder.end(); + int i = 1; + + cprintf("List order: "); + + while (list_order_it != list_order_end_it) { + cprintf("%i OpClass:%i [sn:%lli] ", i, (*list_order_it).queueType, + (*list_order_it).oldestInst); + + ++list_order_it; + ++i; + } +*/ + cprintf("\n"); +} + + +template <class Impl> +void +InstQueue<Impl>::dumpInsts() +{ + for (int i = 0; i < numThreads; ++i) { +// int num = 0; +// int valid_num = 0; +/* + ListIt inst_list_it = instList[i].begin(); + + while (inst_list_it != instList[i].end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } +*/ + } +} diff --git a/src/cpu/ozone/lsq_unit.cc b/src/cpu/ozone/lsq_unit.cc new file mode 100644 index 000000000..e37971dba --- /dev/null +++ b/src/cpu/ozone/lsq_unit.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/lsq_unit_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class OzoneLSQ<OzoneImpl>; + diff --git a/src/cpu/ozone/lsq_unit.hh b/src/cpu/ozone/lsq_unit.hh new file mode 100644 index 000000000..1b5340e55 --- /dev/null +++ b/src/cpu/ozone/lsq_unit.hh @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_LSQ_UNIT_HH__ +#define __CPU_OZONE_LSQ_UNIT_HH__ + +#include <map> +#include <queue> +#include <algorithm> + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "config/full_system.hh" +#include "base/hashmap.hh" +#include "cpu/inst_seq.hh" +#include "mem/mem_interface.hh" +//#include "mem/page_table.hh" +#include "sim/sim_object.hh" + +class PageTable; + +/** + * Class that implements the actual LQ and SQ for each specific thread. + * Both are circular queues; load entries are freed upon committing, while + * store entries are freed once they writeback. The LSQUnit tracks if there + * are memory ordering violations, and also detects partial load to store + * forwarding cases (a store only has part of a load's data) that requires + * the load to wait until the store writes back. In the former case it + * holds onto the instruction until the dependence unit looks at it, and + * in the latter it stalls the LSQ until the store writes back. At that + * point the load is replayed. + */ +template <class Impl> +class OzoneLSQ { + public: + typedef typename Impl::Params Params; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::BackEnd BackEnd; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::IssueStruct IssueStruct; + + typedef TheISA::IntReg IntReg; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator LdMapIt; + + private: + class StoreCompletionEvent : public Event { + public: + /** Constructs a store completion event. */ + StoreCompletionEvent(int store_idx, Event *wb_event, OzoneLSQ *lsq_ptr); + + /** Processes the store completion event. */ + void process(); + + /** Returns the description of this event. */ + const char *description(); + + private: + /** The store index of the store being written back. */ + int storeIdx; + /** The writeback event for the store. Needed for store + * conditionals. + */ + Event *wbEvent; + /** The pointer to the LSQ unit that issued the store. */ + OzoneLSQ<Impl> *lsqPtr; + }; + + friend class StoreCompletionEvent; + + public: + /** Constructs an LSQ unit. init() must be called prior to use. */ + OzoneLSQ(); + + /** Initializes the LSQ unit with the specified number of entries. */ + void init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id); + + /** Returns the name of the LSQ unit. */ + std::string name() const; + + /** Sets the CPU pointer. */ + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + /** Sets the back-end stage pointer. */ + void setBE(BackEnd *be_ptr) + { be = be_ptr; } + + /** Ticks the LSQ unit, which in this case only resets the number of + * used cache ports. + * @todo: Move the number of used ports up to the LSQ level so it can + * be shared by all LSQ units. + */ + void tick() { usedPorts = 0; } + + /** Inserts an instruction. */ + void insert(DynInstPtr &inst); + /** Inserts a load instruction. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store instruction. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load instruction. */ + Fault executeLoad(DynInstPtr &inst); + + Fault executeLoad(int lq_idx); + /** Executes a store instruction. */ + Fault executeStore(DynInstPtr &inst); + + /** Commits the head load. */ + void commitLoad(); + /** Commits a specific load, given by the sequence number. */ + void commitLoad(InstSeqNum &inst); + /** Commits loads older than a specific sequence number. */ + void commitLoads(InstSeqNum &youngest_inst); + + /** Commits stores older than a specific sequence number. */ + void commitStores(InstSeqNum &youngest_inst); + + /** Writes back stores. */ + void writebackStores(); + + // @todo: Include stats in the LSQ unit. + //void regStats(); + + /** Clears all the entries in the LQ. */ + void clearLQ(); + + /** Clears all the entries in the SQ. */ + void clearSQ(); + + /** Resizes the LQ to a given size. */ + void resizeLQ(unsigned size); + + /** Resizes the SQ to a given size. */ + void resizeSQ(unsigned size); + + /** Squashes all instructions younger than a specific sequence number. */ + void squash(const InstSeqNum &squashed_num); + + /** Returns if there is a memory ordering violation. Value is reset upon + * call to getMemDepViolator(). + */ + bool violation() { return memDepViolator; } + + /** Returns the memory ordering violator. */ + DynInstPtr getMemDepViolator(); + + /** Returns if a load became blocked due to the memory system. It clears + * the bool's value upon this being called. + */ + inline bool loadBlocked(); + + /** Returns the number of free entries (min of free LQ and SQ entries). */ + unsigned numFreeEntries(); + + /** Returns the number of loads ready to execute. */ + int numLoadsReady(); + + /** Returns the number of loads in the LQ. */ + int numLoads() { return loads; } + + /** Returns the number of stores in the SQ. */ + int numStores() { return stores; } + + /** Returns if either the LQ or SQ is full. */ + bool isFull() { return lqFull() || sqFull(); } + + /** Returns if the LQ is full. */ + bool lqFull() { return loads >= (LQEntries - 1); } + + /** Returns if the SQ is full. */ + bool sqFull() { return stores >= (SQEntries - 1); } + + /** Debugging function to dump instructions in the LSQ. */ + void dumpInsts(); + + /** Returns the number of instructions in the LSQ. */ + unsigned getCount() { return loads + stores; } + + /** Returns if there are any stores to writeback. */ + bool hasStoresToWB() { return storesToWB; } + + /** Returns the number of stores to writeback. */ + int numStoresToWB() { return storesToWB; } + + /** Returns if the LSQ unit will writeback on this cycle. */ + bool willWB() { return storeQueue[storeWBIdx].canWB && + !storeQueue[storeWBIdx].completed && + !dcacheInterface->isBlocked(); } + + private: + /** Completes the store at the specified index. */ + void completeStore(int store_idx); + + /** Increments the given store index (circular queue). */ + inline void incrStIdx(int &store_idx); + /** Decrements the given store index (circular queue). */ + inline void decrStIdx(int &store_idx); + /** Increments the given load index (circular queue). */ + inline void incrLdIdx(int &load_idx); + /** Decrements the given load index (circular queue). */ + inline void decrLdIdx(int &load_idx); + + private: + /** Pointer to the CPU. */ + FullCPU *cpu; + + /** Pointer to the back-end stage. */ + BackEnd *be; + + /** Pointer to the D-cache. */ + MemInterface *dcacheInterface; + + /** Pointer to the page table. */ + PageTable *pTable; + + public: + struct SQEntry { + /** Constructs an empty store queue entry. */ + SQEntry() + : inst(NULL), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** Constructs a store queue entry for a given instruction. */ + SQEntry(DynInstPtr &_inst) + : inst(_inst), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** The store instruction. */ + DynInstPtr inst; + /** The memory request for the store. */ + MemReqPtr req; + /** The size of the store. */ + int size; + /** The store data. */ + IntReg data; + /** Whether or not the store can writeback. */ + bool canWB; + /** Whether or not the store is committed. */ + bool committed; + /** Whether or not the store is completed. */ + bool completed; + }; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissSwitch + }; + + private: + /** The OzoneLSQ thread id. */ + unsigned lsqID; + + /** The status of the LSQ unit. */ + Status _status; + + /** The store queue. */ + std::vector<SQEntry> storeQueue; + + /** The load queue. */ + std::vector<DynInstPtr> loadQueue; + + // Consider making these 16 bits + /** The number of LQ entries. */ + unsigned LQEntries; + /** The number of SQ entries. */ + unsigned SQEntries; + + /** The number of load instructions in the LQ. */ + int loads; + /** The number of store instructions in the SQ (excludes those waiting to + * writeback). + */ + int stores; + /** The number of store instructions in the SQ waiting to writeback. */ + int storesToWB; + + /** The index of the head instruction in the LQ. */ + int loadHead; + /** The index of the tail instruction in the LQ. */ + int loadTail; + + /** The index of the head instruction in the SQ. */ + int storeHead; + /** The index of the first instruction that is ready to be written back, + * and has not yet been written back. + */ + int storeWBIdx; + /** The index of the tail instruction in the SQ. */ + int storeTail; + + /// @todo Consider moving to a more advanced model with write vs read ports + /** The number of cache ports available each cycle. */ + int cachePorts; + + /** The number of used cache ports in this cycle. */ + int usedPorts; + + //list<InstSeqNum> mshrSeqNums; + + //Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + /** Wire to read information from the issue stage time queue. */ + typename TimeBuffer<IssueStruct>::wire fromIssue; + + // Make these per thread? + /** Whether or not the LSQ is stalled. */ + bool stalled; + /** The store that causes the stall due to partial store to load + * forwarding. + */ + InstSeqNum stallingStoreIsn; + /** The index of the above store. */ + int stallingLoadIdx; + + /** Whether or not a load is blocked due to the memory system. It is + * cleared when this value is checked via loadBlocked(). + */ + bool isLoadBlocked; + + /** The oldest faulting load instruction. */ + DynInstPtr loadFaultInst; + /** The oldest faulting store instruction. */ + DynInstPtr storeFaultInst; + + /** The oldest load that caused a memory ordering violation. */ + DynInstPtr memDepViolator; + + // Will also need how many read/write ports the Dcache has. Or keep track + // of that in stage that is one level up, and only call executeLoad/Store + // the appropriate number of times. + + public: + /** Executes the load at the given index. */ + template <class T> + Fault read(MemReqPtr &req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template <class T> + Fault write(MemReqPtr &req, T &data, int store_idx); + + /** Returns the index of the head load instruction. */ + int getLoadHead() { return loadHead; } + /** Returns the sequence number of the head load instruction. */ + InstSeqNum getLoadHeadSeqNum() + { + if (loadQueue[loadHead]) { + return loadQueue[loadHead]->seqNum; + } else { + return 0; + } + + } + + /** Returns the index of the head store instruction. */ + int getStoreHead() { return storeHead; } + /** Returns the sequence number of the head store instruction. */ + InstSeqNum getStoreHeadSeqNum() + { + if (storeQueue[storeHead].inst) { + return storeQueue[storeHead].inst->seqNum; + } else { + return 0; + } + + } + + /** Returns whether or not the LSQ unit is stalled. */ + bool isStalled() { return stalled; } +}; + +template <class Impl> +template <class T> +Fault +OzoneLSQ<Impl>::read(MemReqPtr &req, T &data, int load_idx) +{ + //Depending on issue2execute delay a squashed load could + //execute if it is found to be squashed in the same + //cycle it is scheduled to execute + assert(loadQueue[load_idx]); + + if (loadQueue[load_idx]->isExecuted()) { + panic("Should not reach this point with split ops!"); + + memcpy(&data,req->data,req->size); + + return NoFault; + } + + // Make sure this isn't an uncacheable access + // A bit of a hackish way to get uncached accesses to work only if they're + // at the head of the LSQ and are ready to commit (at the head of the ROB + // too). + // @todo: Fix uncached accesses. + if (req->flags & UNCACHEABLE && + (load_idx != loadHead || !loadQueue[load_idx]->readyToCommit())) { + + return TheISA::genMachineCheckFault(); + } + + // Check the SQ for any previous stores that might lead to forwarding + int store_idx = loadQueue[load_idx]->sqIdx; + + int store_size = 0; + + DPRINTF(OzoneLSQ, "Read called, load idx: %i, store idx: %i, " + "storeHead: %i addr: %#x\n", + load_idx, store_idx, storeHead, req->paddr); + + while (store_idx != -1) { + // End once we've reached the top of the LSQ + if (store_idx == storeWBIdx) { + break; + } + + // Move the index to one younger + if (--store_idx < 0) + store_idx += SQEntries; + + assert(storeQueue[store_idx].inst); + + store_size = storeQueue[store_idx].size; + + if (store_size == 0) + continue; + + // Check if the store data is within the lower and upper bounds of + // addresses that the request needs. + bool store_has_lower_limit = + req->vaddr >= storeQueue[store_idx].inst->effAddr; + bool store_has_upper_limit = + (req->vaddr + req->size) <= (storeQueue[store_idx].inst->effAddr + + store_size); + bool lower_load_has_store_part = + req->vaddr < (storeQueue[store_idx].inst->effAddr + + store_size); + bool upper_load_has_store_part = + (req->vaddr + req->size) > storeQueue[store_idx].inst->effAddr; + + // If the store's data has all of the data needed, we can forward. + if (store_has_lower_limit && store_has_upper_limit) { + + int shift_amt = req->vaddr & (store_size - 1); + // Assumes byte addressing + shift_amt = shift_amt << 3; + + // Cast this to type T? + data = storeQueue[store_idx].data >> shift_amt; + + req->cmd = Read; + assert(!req->completionEvent); + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + + memcpy(req->data, &data, req->size); + + DPRINTF(OzoneLSQ, "Forwarding from store idx %i to load to " + "addr %#x, data %#x\n", + store_idx, req->vaddr, *(req->data)); + + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(loadQueue[load_idx], + be); + + // We'll say this has a 1 cycle load-store forwarding latency + // for now. + // FIXME - Need to make this a parameter. + wb->schedule(curTick); + + // Should keep track of stat for forwarded data + return NoFault; + } else if ((store_has_lower_limit && lower_load_has_store_part) || + (store_has_upper_limit && upper_load_has_store_part) || + (lower_load_has_store_part && upper_load_has_store_part)) { + // This is the partial store-load forwarding case where a store + // has only part of the load's data. + + // If it's already been written back, then don't worry about + // stalling on it. + if (storeQueue[store_idx].completed) { + continue; + } + + // Must stall load and force it to retry, so long as it's the oldest + // load that needs to do so. + if (!stalled || + (stalled && + loadQueue[load_idx]->seqNum < + loadQueue[stallingLoadIdx]->seqNum)) { + stalled = true; + stallingStoreIsn = storeQueue[store_idx].inst->seqNum; + stallingLoadIdx = load_idx; + } + + // Tell IQ/mem dep unit that this instruction will need to be + // rescheduled eventually + be->rescheduleMemInst(loadQueue[load_idx]); + + DPRINTF(OzoneLSQ, "Load-store forwarding mis-match. " + "Store idx %i to load addr %#x\n", + store_idx, req->vaddr); + + return NoFault; + } + } + + + // If there's no forwarding case, then go access memory + DynInstPtr inst = loadQueue[load_idx]; + + ++usedPorts; + + // if we have a cache, do cache access too + if (dcacheInterface) { + if (dcacheInterface->isBlocked()) { + isLoadBlocked = true; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + + DPRINTF(OzoneLSQ, "D-cache: PC:%#x reading from paddr:%#x " + "vaddr:%#x flags:%i\n", + inst->readPC(), req->paddr, req->vaddr, req->flags); + + // Setup MemReq pointer + req->cmd = Read; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + + assert(!req->completionEvent); + typedef typename BackEnd::LdWritebackEvent LdWritebackEvent; + + LdWritebackEvent *wb = new LdWritebackEvent(loadQueue[load_idx], be); + + req->completionEvent = wb; + + // Do Cache Access + MemAccessResult result = dcacheInterface->access(req); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + // @todo: Probably should support having no events + if (result != MA_HIT) { + DPRINTF(OzoneLSQ, "D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + inst->seqNum); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + wb->setDcacheMiss(); + + } else { +// DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", +// inst->seqNum); + + DPRINTF(OzoneLSQ, "D-cache hit!\n"); + } + } else { + fatal("Must use D-cache with new memory system"); + } + + return NoFault; +} + +template <class Impl> +template <class T> +Fault +OzoneLSQ<Impl>::write(MemReqPtr &req, T &data, int store_idx) +{ + assert(storeQueue[store_idx].inst); + + DPRINTF(OzoneLSQ, "Doing write to store idx %i, addr %#x data %#x" + " | storeHead:%i [sn:%i]\n", + store_idx, req->paddr, data, storeHead, + storeQueue[store_idx].inst->seqNum); + + storeQueue[store_idx].req = req; + storeQueue[store_idx].size = sizeof(T); + storeQueue[store_idx].data = data; + + // This function only writes the data to the store queue, so no fault + // can happen here. + return NoFault; +} + +template <class Impl> +inline bool +OzoneLSQ<Impl>::loadBlocked() +{ + bool ret_val = isLoadBlocked; + isLoadBlocked = false; + return ret_val; +} + +#endif // __CPU_OZONE_LSQ_UNIT_HH__ diff --git a/src/cpu/ozone/lsq_unit_impl.hh b/src/cpu/ozone/lsq_unit_impl.hh new file mode 100644 index 000000000..f8cb18634 --- /dev/null +++ b/src/cpu/ozone/lsq_unit_impl.hh @@ -0,0 +1,840 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "arch/isa_traits.hh" +#include "base/str.hh" +#include "cpu/ozone/lsq_unit.hh" + +template <class Impl> +OzoneLSQ<Impl>::StoreCompletionEvent::StoreCompletionEvent(int store_idx, + Event *wb_event, + OzoneLSQ<Impl> *lsq_ptr) + : Event(&mainEventQueue), + storeIdx(store_idx), + wbEvent(wb_event), + lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +OzoneLSQ<Impl>::StoreCompletionEvent::process() +{ + DPRINTF(OzoneLSQ, "Cache miss complete for store idx:%i\n", storeIdx); + + //lsqPtr->removeMSHR(lsqPtr->storeQueue[storeIdx].inst->seqNum); + +// lsqPtr->cpu->wakeCPU(); + if (wbEvent) + wbEvent->process(); + lsqPtr->completeStore(storeIdx); +} + +template <class Impl> +const char * +OzoneLSQ<Impl>::StoreCompletionEvent::description() +{ + return "LSQ store completion event"; +} + +template <class Impl> +OzoneLSQ<Impl>::OzoneLSQ() + : loads(0), stores(0), storesToWB(0), stalled(false), isLoadBlocked(false) +{ +} + +template<class Impl> +void +OzoneLSQ<Impl>::init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) + +{ + DPRINTF(OzoneLSQ, "Creating OzoneLSQ%i object.\n",id); + + lsqID = id; + + LQEntries = maxLQEntries; + SQEntries = maxSQEntries; + + loadQueue.resize(LQEntries); + storeQueue.resize(SQEntries); + + + // May want to initialize these entries to NULL + + loadHead = loadTail = 0; + + storeHead = storeWBIdx = storeTail = 0; + + usedPorts = 0; + cachePorts = params->cachePorts; + + dcacheInterface = params->dcacheInterface; + + loadFaultInst = storeFaultInst = memDepViolator = NULL; +} + +template<class Impl> +std::string +OzoneLSQ<Impl>::name() const +{ + return "lsqunit"; +} + +template<class Impl> +void +OzoneLSQ<Impl>::clearLQ() +{ + loadQueue.clear(); +} + +template<class Impl> +void +OzoneLSQ<Impl>::clearSQ() +{ + storeQueue.clear(); +} + +template<class Impl> +void +OzoneLSQ<Impl>::resizeLQ(unsigned size) +{ + assert( size >= LQEntries); + + if (size > LQEntries) { + while (size > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size; + } + +} + +template<class Impl> +void +OzoneLSQ<Impl>::resizeSQ(unsigned size) +{ + if (size > SQEntries) { + while (size > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size; + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::insert(DynInstPtr &inst) +{ + // Make sure we really have a memory reference. + assert(inst->isMemRef()); + + // Make sure it's one of the two classes of memory references. + assert(inst->isLoad() || inst->isStore()); + + if (inst->isLoad()) { + insertLoad(inst); + } else { + insertStore(inst); + } + +// inst->setInLSQ(); +} + +template <class Impl> +void +OzoneLSQ<Impl>::insertLoad(DynInstPtr &load_inst) +{ + assert((loadTail + 1) % LQEntries != loadHead && loads < LQEntries); + + DPRINTF(OzoneLSQ, "Inserting load PC %#x, idx:%i [sn:%lli]\n", + load_inst->readPC(), loadTail, load_inst->seqNum); + + load_inst->lqIdx = loadTail; + + if (stores == 0) { + load_inst->sqIdx = -1; + } else { + load_inst->sqIdx = storeTail; + } + + loadQueue[loadTail] = load_inst; + + incrLdIdx(loadTail); + + ++loads; +} + +template <class Impl> +void +OzoneLSQ<Impl>::insertStore(DynInstPtr &store_inst) +{ + // Make sure it is not full before inserting an instruction. + assert((storeTail + 1) % SQEntries != storeHead); + assert(stores < SQEntries); + + DPRINTF(OzoneLSQ, "Inserting store PC %#x, idx:%i [sn:%lli]\n", + store_inst->readPC(), storeTail, store_inst->seqNum); + + store_inst->sqIdx = storeTail; + store_inst->lqIdx = loadTail; + + storeQueue[storeTail] = SQEntry(store_inst); + + incrStIdx(storeTail); + + ++stores; + +} + +template <class Impl> +typename Impl::DynInstPtr +OzoneLSQ<Impl>::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template <class Impl> +unsigned +OzoneLSQ<Impl>::numFreeEntries() +{ + unsigned free_lq_entries = LQEntries - loads; + unsigned free_sq_entries = SQEntries - stores; + + // Both the LQ and SQ entries have an extra dummy entry to differentiate + // empty/full conditions. Subtract 1 from the free entries. + if (free_lq_entries < free_sq_entries) { + return free_lq_entries - 1; + } else { + return free_sq_entries - 1; + } +} + +template <class Impl> +int +OzoneLSQ<Impl>::numLoadsReady() +{ + int load_idx = loadHead; + int retval = 0; + + while (load_idx != loadTail) { + assert(loadQueue[load_idx]); + + if (loadQueue[load_idx]->readyToIssue()) { + ++retval; + } + } + + return retval; +} + +#if 0 +template <class Impl> +Fault +OzoneLSQ<Impl>::executeLoad() +{ + Fault load_fault = NoFault; + DynInstPtr load_inst; + + assert(readyLoads.size() != 0); + + // Execute a ready load. + LdMapIt ready_it = readyLoads.begin(); + + load_inst = (*ready_it).second; + + // Execute the instruction, which is held in the data portion of the + // iterator. + load_fault = load_inst->execute(); + + // If it executed successfully, then switch it over to the executed + // loads list. + if (load_fault == NoFault) { + executedLoads[load_inst->seqNum] = load_inst; + + readyLoads.erase(ready_it); + } else { + loadFaultInst = load_inst; + } + + return load_fault; +} +#endif + +template <class Impl> +Fault +OzoneLSQ<Impl>::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(OzoneLSQ, "Executing load PC %#x, [sn:%lli]\n", + inst->readPC(),inst->seqNum); + + // Make sure it's really in the list. + // Normally it should always be in the list. However, + /* due to a syscall it may not be the list. +#ifdef DEBUG + int i = loadHead; + while (1) { + if (i == loadTail && !find(inst)) { + assert(0 && "Load not in the queue!"); + } else if (loadQueue[i] == inst) { + break; + } + + i = i + 1; + if (i >= LQEntries) { + i = 0; + } + } +#endif // DEBUG*/ + + load_fault = inst->initiateAcc(); + + // Might want to make sure that I'm not overwriting a previously faulting + // instruction that hasn't been checked yet. + // Actually probably want the oldest faulting load + if (load_fault != NoFault) { + // Maybe just set it as can commit here, although that might cause + // some other problems with sending traps to the ROB too quickly. +// iewStage->instToCommit(inst); +// iewStage->activityThisCycle(); + } + + return load_fault; +} + +template <class Impl> +Fault +OzoneLSQ<Impl>::executeLoad(int lq_idx) +{ + // Very hackish. Not sure the best way to check that this + // instruction is at the head of the ROB. I should have some sort + // of extra information here so that I'm not overloading the + // canCommit signal for 15 different things. + loadQueue[lq_idx]->setCanCommit(); + Fault ret_fault = executeLoad(loadQueue[lq_idx]); + loadQueue[lq_idx]->clearCanCommit(); + return ret_fault; +} + +template <class Impl> +Fault +OzoneLSQ<Impl>::executeStore(DynInstPtr &store_inst) +{ + // Make sure that a store exists. + assert(stores != 0); + + int store_idx = store_inst->sqIdx; + + DPRINTF(OzoneLSQ, "Executing store PC %#x [sn:%lli]\n", + store_inst->readPC(), store_inst->seqNum); + + // Check the recently completed loads to see if any match this store's + // address. If so, then we have a memory ordering violation. + int load_idx = store_inst->lqIdx; + + Fault store_fault = store_inst->initiateAcc(); + + // Store size should now be available. Use it to get proper offset for + // addr comparisons. + int size = storeQueue[store_idx].size; + + if (size == 0) { + DPRINTF(OzoneLSQ,"Fault on Store PC %#x, [sn:%lli],Size = 0\n", + store_inst->readPC(),store_inst->seqNum); + + return store_fault; + } + + assert(store_fault == NoFault); + + if (!storeFaultInst) { + if (store_fault != NoFault) { + panic("Fault in a store instruction!"); + storeFaultInst = store_inst; + } else if (store_inst->isNonSpeculative()) { + // Nonspeculative accesses (namely store conditionals) + // need to set themselves as able to writeback if we + // haven't had a fault by here. + storeQueue[store_idx].canWB = true; + + ++storesToWB; + } + } + + if (!memDepViolator) { + while (load_idx != loadTail) { + // Actually should only check loads that have actually executed + // Might be safe because effAddr is set to InvalAddr when the + // dyn inst is created. + + // Must actually check all addrs in the proper size range + // Which is more correct than needs to be. What if for now we just + // assume all loads are quad-word loads, and do the addr based + // on that. + // @todo: Fix this, magic number being used here + if ((loadQueue[load_idx]->effAddr >> 8) == + (store_inst->effAddr >> 8)) { + // A load incorrectly passed this store. Squash and refetch. + // For now return a fault to show that it was unsuccessful. + memDepViolator = loadQueue[load_idx]; + + return TheISA::genMachineCheckFault(); + } + + incrLdIdx(load_idx); + } + + // If we've reached this point, there was no violation. + memDepViolator = NULL; + } + + return store_fault; +} + +template <class Impl> +void +OzoneLSQ<Impl>::commitLoad() +{ + assert(loadQueue[loadHead]); + + DPRINTF(OzoneLSQ, "[sn:%lli] Committing head load instruction, PC %#x\n", + loadQueue[loadHead]->seqNum, loadQueue[loadHead]->readPC()); + + + loadQueue[loadHead] = NULL; + + incrLdIdx(loadHead); + + --loads; +} + +template <class Impl> +void +OzoneLSQ<Impl>::commitLoad(InstSeqNum &inst) +{ + // Hopefully I don't use this function too much + panic("Don't use this function!"); + + int i = loadHead; + while (1) { + if (i == loadTail) { + assert(0 && "Load not in the queue!"); + } else if (loadQueue[i]->seqNum == inst) { + break; + } + + ++i; + if (i >= LQEntries) { + i = 0; + } + } + +// loadQueue[i]->removeInLSQ(); + loadQueue[i] = NULL; + --loads; +} + +template <class Impl> +void +OzoneLSQ<Impl>::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || loadQueue[loadHead]); + + while (loads != 0 && loadQueue[loadHead]->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::commitStores(InstSeqNum &youngest_inst) +{ + assert(stores == 0 || storeQueue[storeHead].inst); + + int store_idx = storeHead; + + while (store_idx != storeTail) { + assert(storeQueue[store_idx].inst); + if (!storeQueue[store_idx].canWB) { + if (storeQueue[store_idx].inst->seqNum > youngest_inst) { + break; + } + DPRINTF(OzoneLSQ, "Marking store as able to write back, PC " + "%#x [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + storeQueue[store_idx].inst->seqNum); + + storeQueue[store_idx].canWB = true; + +// --stores; + ++storesToWB; + } + + incrStIdx(store_idx); + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::writebackStores() +{ + while (storesToWB > 0 && + storeWBIdx != storeTail && + storeQueue[storeWBIdx].inst && + storeQueue[storeWBIdx].canWB && + usedPorts < cachePorts) { + + if (storeQueue[storeWBIdx].size == 0) { + completeStore(storeWBIdx); + + incrStIdx(storeWBIdx); + + continue; + } + + if (dcacheInterface && dcacheInterface->isBlocked()) { + DPRINTF(OzoneLSQ, "Unable to write back any more stores, cache" + " is blocked!\n"); + break; + } + + ++usedPorts; + + if (storeQueue[storeWBIdx].inst->isDataPrefetch()) { + incrStIdx(storeWBIdx); + + continue; + } + + assert(storeQueue[storeWBIdx].req); + assert(!storeQueue[storeWBIdx].committed); + + MemReqPtr req = storeQueue[storeWBIdx].req; + storeQueue[storeWBIdx].committed = true; + +// Fault fault = cpu->translateDataReadReq(req); + req->cmd = Write; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + memcpy(req->data, (uint8_t *)&storeQueue[storeWBIdx].data, req->size); + + DPRINTF(OzoneLSQ, "D-Cache: Writing back store idx:%i PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + storeWBIdx,storeQueue[storeWBIdx].inst->readPC(), + req->paddr, *(req->data), + storeQueue[storeWBIdx].inst->seqNum); + +// if (fault != NoFault) { + //What should we do if there is a fault??? + //for now panic +// panic("Page Table Fault!!!!!\n"); +// } + + if (dcacheInterface) { + MemAccessResult result = dcacheInterface->access(req); + + //@todo temp fix for LL/SC (works fine for 1 CPU) + if (req->flags & LOCKED) { + req->result=1; + panic("LL/SC! oh no no support!!!"); + } + + if (isStalled() && + storeQueue[storeWBIdx].inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst(loadQueue[stallingLoadIdx]); + } + + if (result != MA_HIT && dcacheInterface->doEvents()) { + Event *wb = NULL; +/* + typename IEW::LdWritebackEvent *wb = NULL; + if (req->flags & LOCKED) { + // Stx_C does not generate a system port transaction. + req->result=0; + wb = new typename IEW::LdWritebackEvent(storeQueue[storeWBIdx].inst, + iewStage); + } +*/ + DPRINTF(OzoneLSQ,"D-Cache Write Miss!\n"); + +// DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", +// storeQueue[storeWBIdx].inst->seqNum); + + // Will stores need their own kind of writeback events? + // Do stores even need writeback events? + assert(!req->completionEvent); + req->completionEvent = new + StoreCompletionEvent(storeWBIdx, wb, this); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + //mshrSeqNums.push_back(storeQueue[storeWBIdx].inst->seqNum); + + //DPRINTF(OzoneLSQ, "Added MSHR. count = %i\n",mshrSeqNums.size()); + + // Increment stat here or something + } else { + DPRINTF(OzoneLSQ,"D-Cache: Write Hit on idx:%i !\n", + storeWBIdx); + +// DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", +// storeQueue[storeWBIdx].inst->seqNum); + + if (req->flags & LOCKED) { + // Stx_C does not generate a system port transaction. + req->result=1; + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(storeQueue[storeWBIdx].inst, + be); + wb->schedule(curTick); + } + + completeStore(storeWBIdx); + } + + incrStIdx(storeWBIdx); + } else { + panic("Must HAVE DCACHE!!!!!\n"); + } + } + + // Not sure this should set it to 0. + usedPorts = 0; + + assert(stores >= 0 && storesToWB >= 0); +} + +/*template <class Impl> +void +OzoneLSQ<Impl>::removeMSHR(InstSeqNum seqNum) +{ + list<InstSeqNum>::iterator mshr_it = find(mshrSeqNums.begin(), + mshrSeqNums.end(), + seqNum); + + if (mshr_it != mshrSeqNums.end()) { + mshrSeqNums.erase(mshr_it); + DPRINTF(OzoneLSQ, "Removing MSHR. count = %i\n",mshrSeqNums.size()); + } +}*/ + +template <class Impl> +void +OzoneLSQ<Impl>::squash(const InstSeqNum &squashed_num) +{ + DPRINTF(OzoneLSQ, "Squashing until [sn:%lli]!" + "(Loads:%i Stores:%i)\n",squashed_num,loads,stores); + + int load_idx = loadTail; + decrLdIdx(load_idx); + + while (loads != 0 && loadQueue[load_idx]->seqNum > squashed_num) { + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Load Instruction PC %#x squashed, " + "[sn:%lli]\n", + loadQueue[load_idx]->readPC(), + loadQueue[load_idx]->seqNum); + + if (isStalled() && load_idx == stallingLoadIdx) { + stalled = false; + stallingStoreIsn = 0; + stallingLoadIdx = 0; + } + +// loadQueue[load_idx]->squashed = true; + loadQueue[load_idx] = NULL; + --loads; + + // Inefficient! + loadTail = load_idx; + + decrLdIdx(load_idx); + } + + int store_idx = storeTail; + decrStIdx(store_idx); + + while (stores != 0 && storeQueue[store_idx].inst->seqNum > squashed_num) { + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Store Instruction PC %#x squashed, " + "idx:%i [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + store_idx, storeQueue[store_idx].inst->seqNum); + + // I don't think this can happen. It should have been cleared by the + // stalling load. + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + panic("Is stalled should have been cleared by stalling load!\n"); + stalled = false; + stallingStoreIsn = 0; + } + +// storeQueue[store_idx].inst->squashed = true; + storeQueue[store_idx].inst = NULL; + storeQueue[store_idx].canWB = 0; + + if (storeQueue[store_idx].req) { + assert(!storeQueue[store_idx].req->completionEvent); + } + storeQueue[store_idx].req = NULL; + --stores; + + // Inefficient! + storeTail = store_idx; + + decrStIdx(store_idx); + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::dumpInsts() +{ + cprintf("Load store queue: Dumping instructions.\n"); + cprintf("Load queue size: %i\n", loads); + cprintf("Load queue: "); + + int load_idx = loadHead; + + while (load_idx != loadTail && loadQueue[load_idx]) { + cprintf("[sn:%lli] %#x ", loadQueue[load_idx]->seqNum, + loadQueue[load_idx]->readPC()); + + incrLdIdx(load_idx); + } + + cprintf("\nStore queue size: %i\n", stores); + cprintf("Store queue: "); + + int store_idx = storeHead; + + while (store_idx != storeTail && storeQueue[store_idx].inst) { + cprintf("[sn:%lli] %#x ", storeQueue[store_idx].inst->seqNum, + storeQueue[store_idx].inst->readPC()); + + incrStIdx(store_idx); + } + + cprintf("\n"); +} + +template <class Impl> +void +OzoneLSQ<Impl>::completeStore(int store_idx) +{ + assert(storeQueue[store_idx].inst); + storeQueue[store_idx].completed = true; + --storesToWB; + // A bit conservative because a store completion may not free up entries, + // but hopefully avoids two store completions in one cycle from making + // the CPU tick twice. +// cpu->activityThisCycle(); + + if (store_idx == storeHead) { + do { + incrStIdx(storeHead); + + --stores; + } while (storeQueue[storeHead].completed && + storeHead != storeTail); + +// be->updateLSQNextCycle = true; + } + + DPRINTF(OzoneLSQ, "Store head idx:%i\n", storeHead); + + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst(loadQueue[stallingLoadIdx]); + } +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::incrStIdx(int &store_idx) +{ + if (++store_idx >= SQEntries) + store_idx = 0; +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::decrStIdx(int &store_idx) +{ + if (--store_idx < 0) + store_idx += SQEntries; +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::incrLdIdx(int &load_idx) +{ + if (++load_idx >= LQEntries) + load_idx = 0; +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::decrLdIdx(int &load_idx) +{ + if (--load_idx < 0) + load_idx += LQEntries; +} diff --git a/src/cpu/ozone/lw_back_end.cc b/src/cpu/ozone/lw_back_end.cc new file mode 100644 index 000000000..71e51f038 --- /dev/null +++ b/src/cpu/ozone/lw_back_end.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/lw_back_end_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +template class LWBackEnd<OzoneImpl>; diff --git a/src/cpu/ozone/lw_back_end.hh b/src/cpu/ozone/lw_back_end.hh new file mode 100644 index 000000000..bb3ef3a72 --- /dev/null +++ b/src/cpu/ozone/lw_back_end.hh @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_LW_BACK_END_HH__ +#define __CPU_OZONE_LW_BACK_END_HH__ + +#include <list> +#include <queue> +#include <set> +#include <string> + +#include "arch/faults.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +template <class> +class Checker; +class ThreadContext; + +template <class Impl> +class OzoneThreadState; + +template <class Impl> +class LWBackEnd +{ + public: + typedef OzoneThreadState<Impl> Thread; + + typedef typename Impl::Params Params; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::OzoneCPU OzoneCPU; + typedef typename Impl::FrontEnd FrontEnd; + typedef typename Impl::OzoneCPU::CommStruct CommStruct; + + struct SizeStruct { + int size; + }; + + typedef SizeStruct DispatchToIssue; + typedef SizeStruct IssueToExec; + typedef SizeStruct ExecToCommit; + typedef SizeStruct Writeback; + + TimeBuffer<DispatchToIssue> d2i; + typename TimeBuffer<DispatchToIssue>::wire instsToDispatch; + TimeBuffer<IssueToExec> i2e; + typename TimeBuffer<IssueToExec>::wire instsToExecute; + TimeBuffer<ExecToCommit> e2c; + TimeBuffer<Writeback> numInstsToWB; + + TimeBuffer<CommStruct> *comm; + typename TimeBuffer<CommStruct>::wire toIEW; + typename TimeBuffer<CommStruct>::wire fromCommit; + + class TrapEvent : public Event { + private: + LWBackEnd<Impl> *be; + + public: + TrapEvent(LWBackEnd<Impl> *_be); + + void process(); + const char *description(); + }; + + LWBackEnd(Params *params); + + std::string name() const; + + void regStats(); + + void setCPU(OzoneCPU *cpu_ptr); + + void setFrontEnd(FrontEnd *front_end_ptr) + { frontEnd = front_end_ptr; } + + void setTC(ThreadContext *tc_ptr) + { tc = tc_ptr; } + + void setThreadState(Thread *thread_ptr) + { thread = thread_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm); + + void tick(); + void squash(); + void generateTCEvent() { tcSquash = true; } + void squashFromTC(); + void squashFromTrap(); + void checkInterrupts(); + bool trapSquash; + bool tcSquash; + + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + Addr readCommitPC() { return commitPC; } + + Addr commitPC; + + Tick lastCommitCycle; + + bool robEmpty() { return instList.empty(); } + + bool isFull() { return numInsts >= numROBEntries; } + bool isBlocked() { return status == Blocked || dispatchStatus == Blocked; } + + void fetchFault(Fault &fault); + + int wakeDependents(DynInstPtr &inst, bool memory_deps = false); + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst); + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst); + + /** Completes memory instruction. */ + void completeMemInst(DynInstPtr &inst) { } + + void addDcacheMiss(DynInstPtr &inst) + { + waitingMemOps.insert(inst->seqNum); + numWaitingMemOps++; + DPRINTF(BE, "Adding a Dcache miss mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void removeDcacheMiss(DynInstPtr &inst) + { + assert(waitingMemOps.find(inst->seqNum) != waitingMemOps.end()); + waitingMemOps.erase(inst->seqNum); + numWaitingMemOps--; + DPRINTF(BE, "Removing a Dcache miss mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void addWaitingMemOp(DynInstPtr &inst) + { + waitingMemOps.insert(inst->seqNum); + numWaitingMemOps++; + DPRINTF(BE, "Adding a waiting mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void removeWaitingMemOp(DynInstPtr &inst) + { + assert(waitingMemOps.find(inst->seqNum) != waitingMemOps.end()); + waitingMemOps.erase(inst->seqNum); + numWaitingMemOps--; + DPRINTF(BE, "Removing a waiting mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void instToCommit(DynInstPtr &inst); + + void switchOut(); + void doSwitchOut(); + void takeOverFrom(ThreadContext *old_tc = NULL); + + bool isSwitchedOut() { return switchedOut; } + + private: + void generateTrapEvent(Tick latency = 0); + void handleFault(Fault &fault, Tick latency = 0); + void updateStructures(); + void dispatchInsts(); + void dispatchStall(); + void checkDispatchStatus(); + void executeInsts(); + void commitInsts(); + void addToLSQ(DynInstPtr &inst); + void writebackInsts(); + bool commitInst(int inst_num); + void squash(const InstSeqNum &sn); + void squashDueToBranch(DynInstPtr &inst); + void squashDueToMemViolation(DynInstPtr &inst); + void squashDueToMemBlocked(DynInstPtr &inst); + void updateExeInstStats(DynInstPtr &inst); + void updateComInstStats(DynInstPtr &inst); + + public: + OzoneCPU *cpu; + + FrontEnd *frontEnd; + + ThreadContext *tc; + + Thread *thread; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissComplete, + Blocked, + TrapPending + }; + + Status status; + + Status dispatchStatus; + + Status commitStatus; + + Counter funcExeInst; + + private: + typedef typename Impl::LdstQueue LdstQueue; + + LdstQueue LSQ; + public: + RenameTable<Impl> commitRenameTable; + + RenameTable<Impl> renameTable; + private: + // General back end width. Used if the more specific isn't given. + int width; + + // Dispatch width. + int dispatchWidth; + int numDispatchEntries; + int dispatchSize; + + int waitingInsts; + + int issueWidth; + + // Writeback width + int wbWidth; + + // Commit width + int commitWidth; + + /** Index into queue of instructions being written back. */ + unsigned wbNumInst; + + /** Cycle number within the queue of instructions being written + * back. Used in case there are too many instructions writing + * back at the current cycle and writesbacks need to be scheduled + * for the future. See comments in instToCommit(). + */ + unsigned wbCycle; + + int numROBEntries; + int numInsts; + + std::set<InstSeqNum> waitingMemOps; + typedef std::set<InstSeqNum>::iterator MemIt; + int numWaitingMemOps; + unsigned maxOutstandingMemOps; + + bool squashPending; + InstSeqNum squashSeqNum; + Addr squashNextPC; + + Fault faultFromFetch; + bool fetchHasFault; + + bool switchedOut; + bool switchPending; + + DynInstPtr memBarrier; + + private: + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; + + typedef typename std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> ReadyInstQueue; + ReadyInstQueue exeList; + + typedef typename std::list<DynInstPtr>::iterator InstListIt; + + std::list<DynInstPtr> instList; + std::list<DynInstPtr> waitingList; + std::list<DynInstPtr> replayList; + std::list<DynInstPtr> writeback; + + int latency; + + int squashLatency; + + bool exactFullStall; + + // number of cycles stalled for D-cache misses +/* Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; +*/ + Stats::Vector<> rob_cap_events; + Stats::Vector<> rob_cap_inst_count; + Stats::Vector<> iq_cap_events; + Stats::Vector<> iq_cap_inst_count; + // total number of instructions executed + Stats::Vector<> exe_inst; + Stats::Vector<> exe_swp; + Stats::Vector<> exe_nop; + Stats::Vector<> exe_refs; + Stats::Vector<> exe_loads; + Stats::Vector<> exe_branches; + + Stats::Vector<> issued_ops; + + // total number of loads forwaded from LSQ stores + Stats::Vector<> lsq_forw_loads; + + // total number of loads ignored due to invalid addresses + Stats::Vector<> inv_addr_loads; + + // total number of software prefetches ignored due to invalid addresses + Stats::Vector<> inv_addr_swpfs; + // ready loads blocked due to memory disambiguation + Stats::Vector<> lsq_blocked_loads; + + Stats::Scalar<> lsqInversion; + + Stats::Vector<> n_issued_dist; + Stats::VectorDistribution<> issue_delay_dist; + + Stats::VectorDistribution<> queue_res_dist; +/* + Stats::Vector<> stat_fu_busy; + Stats::Vector2d<> stat_fuBusy; + Stats::Vector<> dist_unissued; + Stats::Vector2d<> stat_issued_inst_type; + + Stats::Formula misspec_cnt; + Stats::Formula misspec_ipc; + Stats::Formula issue_rate; + Stats::Formula issue_stores; + Stats::Formula issue_op_rate; + Stats::Formula fu_busy_rate; + Stats::Formula commit_stores; + Stats::Formula commit_ipc; + Stats::Formula commit_ipb; + Stats::Formula lsq_inv_rate; +*/ + Stats::Vector<> writeback_count; + Stats::Vector<> producer_inst; + Stats::Vector<> consumer_inst; + Stats::Vector<> wb_penalized; + + Stats::Formula wb_rate; + Stats::Formula wb_fanout; + Stats::Formula wb_penalized_rate; + + // total number of instructions committed + Stats::Vector<> stat_com_inst; + Stats::Vector<> stat_com_swp; + Stats::Vector<> stat_com_refs; + Stats::Vector<> stat_com_loads; + Stats::Vector<> stat_com_membars; + Stats::Vector<> stat_com_branches; + + Stats::Distribution<> n_committed_dist; + + Stats::Scalar<> commit_eligible_samples; + Stats::Vector<> commit_eligible; + + Stats::Vector<> squashedInsts; + Stats::Vector<> ROBSquashedInsts; + + Stats::Scalar<> ROB_fcount; + Stats::Formula ROB_full_rate; + + Stats::Vector<> ROB_count; // cumulative ROB occupancy + Stats::Formula ROB_occ_rate; + Stats::VectorDistribution<> ROB_occ_dist; + public: + void dumpInsts(); + + Checker<DynInstPtr> *checker; +}; + +template <class Impl> +template <class T> +Fault +LWBackEnd<Impl>::read(RequestPtr req, T &data, int load_idx) +{ + return LSQ.read(req, data, load_idx); +} + +template <class Impl> +template <class T> +Fault +LWBackEnd<Impl>::write(RequestPtr req, T &data, int store_idx) +{ + return LSQ.write(req, data, store_idx); +} + +#endif // __CPU_OZONE_LW_BACK_END_HH__ diff --git a/src/cpu/ozone/lw_back_end_impl.hh b/src/cpu/ozone/lw_back_end_impl.hh new file mode 100644 index 000000000..a73d3ee6e --- /dev/null +++ b/src/cpu/ozone/lw_back_end_impl.hh @@ -0,0 +1,1626 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "config/use_checker.hh" + +#include "cpu/ozone/lw_back_end.hh" +#include "cpu/op_class.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + +template <class Impl> +void +LWBackEnd<Impl>::generateTrapEvent(Tick latency) +{ + DPRINTF(BE, "Generating trap event\n"); + + TrapEvent *trap = new TrapEvent(this); + + trap->schedule(curTick + cpu->cycles(latency)); + + thread->trapPending = true; +} + +template <class Impl> +int +LWBackEnd<Impl>::wakeDependents(DynInstPtr &inst, bool memory_deps) +{ + assert(!inst->isSquashed()); + std::vector<DynInstPtr> &dependents = memory_deps ? inst->getMemDeps() : + inst->getDependents(); + int num_outputs = dependents.size(); + + DPRINTF(BE, "Waking instruction [sn:%lli] dependents in IQ\n", inst->seqNum); + + for (int i = 0; i < num_outputs; i++) { + DynInstPtr dep_inst = dependents[i]; + if (!memory_deps) { + dep_inst->markSrcRegReady(); + } else { + if (!dep_inst->isSquashed()) + dep_inst->markMemInstReady(inst.get()); + } + + DPRINTF(BE, "Marking source reg ready [sn:%lli] in IQ\n", dep_inst->seqNum); + + if (dep_inst->readyToIssue() && dep_inst->isInROB() && + !dep_inst->isNonSpeculative() && !dep_inst->isStoreConditional() && + dep_inst->memDepReady() && !dep_inst->isMemBarrier() && + !dep_inst->isWriteBarrier()) { + DPRINTF(BE, "Adding instruction to exeList [sn:%lli]\n", + dep_inst->seqNum); + exeList.push(dep_inst); + if (dep_inst->iqItValid) { + DPRINTF(BE, "Removing instruction from waiting list\n"); + waitingList.erase(dep_inst->iqIt); + waitingInsts--; + dep_inst->iqItValid = false; + assert(waitingInsts >= 0); + } + if (dep_inst->isMemRef()) { + removeWaitingMemOp(dep_inst); + DPRINTF(BE, "Issued a waiting mem op [sn:%lli]\n", + dep_inst->seqNum); + } + } + } + return num_outputs; +} + +template <class Impl> +void +LWBackEnd<Impl>::rescheduleMemInst(DynInstPtr &inst) +{ + replayList.push_front(inst); +} + +template <class Impl> +LWBackEnd<Impl>::TrapEvent::TrapEvent(LWBackEnd<Impl> *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +LWBackEnd<Impl>::TrapEvent::process() +{ + be->trapSquash = true; +} + +template <class Impl> +const char * +LWBackEnd<Impl>::TrapEvent::description() +{ + return "Trap event"; +} + +template <class Impl> +void +LWBackEnd<Impl>::replayMemInst(DynInstPtr &inst) +{ + bool found_inst = false; + while (!replayList.empty()) { + exeList.push(replayList.front()); + if (replayList.front() == inst) { + found_inst = true; + } + replayList.pop_front(); + } + assert(found_inst); +} + +template <class Impl> +LWBackEnd<Impl>::LWBackEnd(Params *params) + : d2i(5, 5), i2e(5, 5), e2c(5, 5), numInstsToWB(5, 5), + trapSquash(false), tcSquash(false), LSQ(params), + width(params->backEndWidth), exactFullStall(true) +{ + numROBEntries = params->numROBEntries; + numInsts = 0; + numDispatchEntries = 32; + maxOutstandingMemOps = params->maxOutstandingMemOps; + numWaitingMemOps = 0; + waitingInsts = 0; + switchedOut = false; + switchPending = false; + + LSQ.setBE(this); + + // Setup IQ and LSQ with their parameters here. + instsToDispatch = d2i.getWire(-1); + + instsToExecute = i2e.getWire(-1); + + dispatchWidth = params->dispatchWidth ? params->dispatchWidth : width; + issueWidth = params->issueWidth ? params->issueWidth : width; + wbWidth = params->wbWidth ? params->wbWidth : width; + commitWidth = params->commitWidth ? params->commitWidth : width; + + LSQ.init(params, params->LQEntries, params->SQEntries, 0); + + dispatchStatus = Running; + commitStatus = Running; +} + +template <class Impl> +std::string +LWBackEnd<Impl>::name() const +{ + return cpu->name() + ".backend"; +} + +template <class Impl> +void +LWBackEnd<Impl>::regStats() +{ + using namespace Stats; + rob_cap_events + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_events") + .desc("number of cycles where ROB cap was active") + .flags(total) + ; + + rob_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_inst") + .desc("number of instructions held up by ROB cap") + .flags(total) + ; + + iq_cap_events + .init(cpu->number_of_threads) + .name(name() +".IQ:cap_events" ) + .desc("number of cycles where IQ cap was active") + .flags(total) + ; + + iq_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".IQ:cap_inst") + .desc("number of instructions held up by IQ cap") + .flags(total) + ; + + + exe_inst + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:count") + .desc("number of insts issued") + .flags(total) + ; + + exe_swp + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:swp") + .desc("number of swp insts issued") + .flags(total) + ; + + exe_nop + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:nop") + .desc("number of nop insts issued") + .flags(total) + ; + + exe_refs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:refs") + .desc("number of memory reference insts issued") + .flags(total) + ; + + exe_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:loads") + .desc("number of load insts issued") + .flags(total) + ; + + exe_branches + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:branches") + .desc("Number of branches issued") + .flags(total) + ; + + issued_ops + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:op_count") + .desc("number of insts issued") + .flags(total) + ; + +/* + for (int i=0; i<Num_OpClasses; ++i) { + stringstream subname; + subname << opClassStrings[i] << "_delay"; + issue_delay_dist.subname(i, subname.str()); + } +*/ + // + // Other stats + // + lsq_forw_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:forw_loads") + .desc("number of loads forwarded via LSQ") + .flags(total) + ; + + inv_addr_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_loads") + .desc("number of invalid-address loads") + .flags(total) + ; + + inv_addr_swpfs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_swpfs") + .desc("number of invalid-address SW prefetches") + .flags(total) + ; + + lsq_blocked_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:blocked_loads") + .desc("number of ready loads not issued due to memory disambiguation") + .flags(total) + ; + + lsqInversion + .name(name() + ".ISSUE:lsq_invert") + .desc("Number of times LSQ instruction issued early") + ; + + n_issued_dist + .init(issueWidth + 1) + .name(name() + ".ISSUE:issued_per_cycle") + .desc("Number of insts issued each cycle") + .flags(total | pdf | dist) + ; + issue_delay_dist + .init(Num_OpClasses,0,99,2) + .name(name() + ".ISSUE:") + .desc("cycles from operands ready to issue") + .flags(pdf | cdf) + ; + + queue_res_dist + .init(Num_OpClasses, 0, 99, 2) + .name(name() + ".IQ:residence:") + .desc("cycles from dispatch to issue") + .flags(total | pdf | cdf ) + ; + for (int i = 0; i < Num_OpClasses; ++i) { + queue_res_dist.subname(i, opClassStrings[i]); + } + + writeback_count + .init(cpu->number_of_threads) + .name(name() + ".WB:count") + .desc("cumulative count of insts written-back") + .flags(total) + ; + + producer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:producers") + .desc("num instructions producing a value") + .flags(total) + ; + + consumer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:consumers") + .desc("num instructions consuming a value") + .flags(total) + ; + + wb_penalized + .init(cpu->number_of_threads) + .name(name() + ".WB:penalized") + .desc("number of instrctions required to write to 'other' IQ") + .flags(total) + ; + + + wb_penalized_rate + .name(name() + ".WB:penalized_rate") + .desc ("fraction of instructions written-back that wrote to 'other' IQ") + .flags(total) + ; + + wb_penalized_rate = wb_penalized / writeback_count; + + wb_fanout + .name(name() + ".WB:fanout") + .desc("average fanout of values written-back") + .flags(total) + ; + + wb_fanout = producer_inst / consumer_inst; + + wb_rate + .name(name() + ".WB:rate") + .desc("insts written-back per cycle") + .flags(total) + ; + wb_rate = writeback_count / cpu->numCycles; + + stat_com_inst + .init(cpu->number_of_threads) + .name(name() + ".COM:count") + .desc("Number of instructions committed") + .flags(total) + ; + + stat_com_swp + .init(cpu->number_of_threads) + .name(name() + ".COM:swp_count") + .desc("Number of s/w prefetches committed") + .flags(total) + ; + + stat_com_refs + .init(cpu->number_of_threads) + .name(name() + ".COM:refs") + .desc("Number of memory references committed") + .flags(total) + ; + + stat_com_loads + .init(cpu->number_of_threads) + .name(name() + ".COM:loads") + .desc("Number of loads committed") + .flags(total) + ; + + stat_com_membars + .init(cpu->number_of_threads) + .name(name() + ".COM:membars") + .desc("Number of memory barriers committed") + .flags(total) + ; + + stat_com_branches + .init(cpu->number_of_threads) + .name(name() + ".COM:branches") + .desc("Number of branches committed") + .flags(total) + ; + n_committed_dist + .init(0,commitWidth,1) + .name(name() + ".COM:committed_per_cycle") + .desc("Number of insts commited each cycle") + .flags(pdf) + ; + + // + // Commit-Eligible instructions... + // + // -> The number of instructions eligible to commit in those + // cycles where we reached our commit BW limit (less the number + // actually committed) + // + // -> The average value is computed over ALL CYCLES... not just + // the BW limited cycles + // + // -> The standard deviation is computed only over cycles where + // we reached the BW limit + // + commit_eligible + .init(cpu->number_of_threads) + .name(name() + ".COM:bw_limited") + .desc("number of insts not committed due to BW limits") + .flags(total) + ; + + commit_eligible_samples + .name(name() + ".COM:bw_lim_events") + .desc("number cycles where commit BW limit reached") + ; + + squashedInsts + .init(cpu->number_of_threads) + .name(name() + ".COM:squashed_insts") + .desc("Number of instructions removed from inst list") + ; + + ROBSquashedInsts + .init(cpu->number_of_threads) + .name(name() + ".COM:rob_squashed_insts") + .desc("Number of instructions removed from inst list when they reached the head of the ROB") + ; + + ROB_fcount + .name(name() + ".ROB:full_count") + .desc("number of cycles where ROB was full") + ; + + ROB_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:occupancy") + .desc(name() + ".ROB occupancy (cumulative)") + .flags(total) + ; + + ROB_full_rate + .name(name() + ".ROB:full_rate") + .desc("ROB full per cycle") + ; + ROB_full_rate = ROB_fcount / cpu->numCycles; + + ROB_occ_rate + .name(name() + ".ROB:occ_rate") + .desc("ROB occupancy rate") + .flags(total) + ; + ROB_occ_rate = ROB_count / cpu->numCycles; + + ROB_occ_dist + .init(cpu->number_of_threads,0,numROBEntries,2) + .name(name() + ".ROB:occ_dist") + .desc("ROB Occupancy per cycle") + .flags(total | cdf) + ; +} + +template <class Impl> +void +LWBackEnd<Impl>::setCPU(OzoneCPU *cpu_ptr) +{ + cpu = cpu_ptr; + LSQ.setCPU(cpu_ptr); + checker = cpu->checker; +} + +template <class Impl> +void +LWBackEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) +{ + comm = _comm; + toIEW = comm->getWire(0); + fromCommit = comm->getWire(-1); +} + +#if FULL_SYSTEM +template <class Impl> +void +LWBackEnd<Impl>::checkInterrupts() +{ + if (cpu->checkInterrupts && + cpu->check_interrupts() && + !cpu->inPalMode(thread->readPC()) && + !trapSquash && + !tcSquash) { + frontEnd->interruptPending = true; + if (robEmpty() && !LSQ.hasStoresToWB()) { + // Will need to squash all instructions currently in flight and have + // the interrupt handler restart at the last non-committed inst. + // Most of that can be handled through the trap() function. The + // processInterrupts() function really just checks for interrupts + // and then calls trap() if there is an interrupt present. + + // Not sure which thread should be the one to interrupt. For now + // always do thread 0. + assert(!thread->inSyscall); + thread->inSyscall = true; + + // CPU will handle implementation of the interrupt. + cpu->processInterrupts(); + + // Now squash or record that I need to squash this cycle. + commitStatus = TrapPending; + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + // Generate trap squash event. + generateTrapEvent(); + + DPRINTF(BE, "Interrupt detected.\n"); + } else { + DPRINTF(BE, "Interrupt must wait for ROB to drain.\n"); + } + } +} + +template <class Impl> +void +LWBackEnd<Impl>::handleFault(Fault &fault, Tick latency) +{ + DPRINTF(BE, "Handling fault!\n"); + + assert(!thread->inSyscall); + + thread->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + fault->invoke(thread->getTC()); + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + commitStatus = TrapPending; + + // Generate trap squash event. + generateTrapEvent(latency); +} +#endif + +template <class Impl> +void +LWBackEnd<Impl>::tick() +{ + DPRINTF(BE, "Ticking back end\n"); + + if (switchPending && robEmpty() && !LSQ.hasStoresToWB()) { + cpu->signalSwitched(); + return; + } + + ROB_count[0]+= numInsts; + + wbCycle = 0; + + // Read in any done instruction information and update the IQ or LSQ. + updateStructures(); + +#if FULL_SYSTEM + checkInterrupts(); + + if (trapSquash) { + assert(!tcSquash); + squashFromTrap(); + } else if (tcSquash) { + squashFromTC(); + } +#endif + + if (dispatchStatus != Blocked) { + dispatchInsts(); + } else { + checkDispatchStatus(); + } + + if (commitStatus != TrapPending) { + executeInsts(); + + commitInsts(); + } + + LSQ.writebackStores(); + + DPRINTF(BE, "Waiting insts: %i, mem ops: %i, ROB entries in use: %i, " + "LSQ loads: %i, LSQ stores: %i\n", + waitingInsts, numWaitingMemOps, numInsts, + LSQ.numLoads(), LSQ.numStores()); + +#ifdef DEBUG + assert(numInsts == instList.size()); + assert(waitingInsts == waitingList.size()); + assert(numWaitingMemOps == waitingMemOps.size()); + assert(!switchedOut); +#endif +} + +template <class Impl> +void +LWBackEnd<Impl>::updateStructures() +{ + if (fromCommit->doneSeqNum) { + LSQ.commitLoads(fromCommit->doneSeqNum); + LSQ.commitStores(fromCommit->doneSeqNum); + } + + if (fromCommit->nonSpecSeqNum) { + if (fromCommit->uncached) { +// LSQ.executeLoad(fromCommit->lqIdx); + } else { +// IQ.scheduleNonSpec( +// fromCommit->nonSpecSeqNum); + } + } +} + +template <class Impl> +void +LWBackEnd<Impl>::addToLSQ(DynInstPtr &inst) +{ + // Do anything LSQ specific here? + LSQ.insert(inst); +} + +template <class Impl> +void +LWBackEnd<Impl>::dispatchInsts() +{ + DPRINTF(BE, "Trying to dispatch instructions.\n"); + + while (numInsts < numROBEntries && + numWaitingMemOps < maxOutstandingMemOps) { + // Get instruction from front of time buffer + DynInstPtr inst = frontEnd->getInst(); + if (!inst) { + break; + } else if (inst->isSquashed()) { + continue; + } + + ++numInsts; + instList.push_front(inst); + + inst->setInROB(); + + DPRINTF(BE, "Dispatching instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + for (int i = 0; i < inst->numDestRegs(); ++i) + renameTable[inst->destRegIdx(i)] = inst; + + if (inst->isMemBarrier() || inst->isWriteBarrier()) { + if (memBarrier) { + DPRINTF(BE, "Instruction [sn:%lli] is waiting on " + "barrier [sn:%lli].\n", + inst->seqNum, memBarrier->seqNum); + memBarrier->addMemDependent(inst); + inst->addSrcMemInst(memBarrier); + } + memBarrier = inst; + inst->setCanCommit(); + } else if (inst->readyToIssue() && + !inst->isNonSpeculative() && + !inst->isStoreConditional()) { + if (inst->isMemRef()) { + + LSQ.insert(inst); + if (memBarrier) { + DPRINTF(BE, "Instruction [sn:%lli] is waiting on " + "barrier [sn:%lli].\n", + inst->seqNum, memBarrier->seqNum); + memBarrier->addMemDependent(inst); + inst->addSrcMemInst(memBarrier); + addWaitingMemOp(inst); + + waitingList.push_front(inst); + inst->iqIt = waitingList.begin(); + inst->iqItValid = true; + waitingInsts++; + } else { + DPRINTF(BE, "Instruction [sn:%lli] ready, addding to " + "exeList.\n", + inst->seqNum); + exeList.push(inst); + } + } else if (inst->isNop()) { + DPRINTF(BE, "Nop encountered [sn:%lli], skipping exeList.\n", + inst->seqNum); + inst->setIssued(); + inst->setExecuted(); + inst->setCanCommit(); + } else { + DPRINTF(BE, "Instruction [sn:%lli] ready, addding to " + "exeList.\n", + inst->seqNum); + exeList.push(inst); + } + } else { + if (inst->isNonSpeculative() || inst->isStoreConditional()) { + inst->setCanCommit(); + DPRINTF(BE, "Adding non speculative instruction\n"); + } + + if (inst->isMemRef()) { + addWaitingMemOp(inst); + LSQ.insert(inst); + if (memBarrier) { + memBarrier->addMemDependent(inst); + inst->addSrcMemInst(memBarrier); + + DPRINTF(BE, "Instruction [sn:%lli] is waiting on " + "barrier [sn:%lli].\n", + inst->seqNum, memBarrier->seqNum); + } + } + + DPRINTF(BE, "Instruction [sn:%lli] not ready, addding to " + "waitingList.\n", + inst->seqNum); + waitingList.push_front(inst); + inst->iqIt = waitingList.begin(); + inst->iqItValid = true; + waitingInsts++; + } + } + + // Check if IQ or LSQ is full. If so we'll need to break and stop + // removing instructions. Also update the number of insts to remove + // from the queue. Check here if we don't care about exact stall + // conditions. +/* + bool stall = false; + if (IQ.isFull()) { + DPRINTF(BE, "IQ is full!\n"); + stall = true; + } else if (LSQ.isFull()) { + DPRINTF(BE, "LSQ is full!\n"); + stall = true; + } else if (isFull()) { + DPRINTF(BE, "ROB is full!\n"); + stall = true; + ROB_fcount++; + } + if (stall) { + d2i.advance(); + dispatchStall(); + return; + } +*/ +} + +template <class Impl> +void +LWBackEnd<Impl>::dispatchStall() +{ + dispatchStatus = Blocked; + if (!cpu->decoupledFrontEnd) { + // Tell front end to stall here through a timebuffer, or just tell + // it directly. + } +} + +template <class Impl> +void +LWBackEnd<Impl>::checkDispatchStatus() +{ + DPRINTF(BE, "Checking dispatch status\n"); + assert(dispatchStatus == Blocked); + if (!LSQ.isFull() && !isFull()) { + DPRINTF(BE, "Dispatch no longer blocked\n"); + dispatchStatus = Running; + dispatchInsts(); + } +} + +template <class Impl> +void +LWBackEnd<Impl>::executeInsts() +{ + DPRINTF(BE, "Trying to execute instructions\n"); + + int num_executed = 0; + while (!exeList.empty() && num_executed < issueWidth) { + DynInstPtr inst = exeList.top(); + + DPRINTF(BE, "Executing inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + + // Check if the instruction is squashed; if so then skip it + // and don't count it towards the FU usage. + if (inst->isSquashed()) { + DPRINTF(BE, "Execute: Instruction was squashed.\n"); + + // Not sure how to handle this plus the method of sending # of + // instructions to use. Probably will just have to count it + // towards the bandwidth usage, but not the FU usage. + ++num_executed; + + // Consider this instruction executed so that commit can go + // ahead and retire the instruction. + inst->setExecuted(); + + // Not sure if I should set this here or just let commit try to + // commit any squashed instructions. I like the latter a bit more. + inst->setCanCommit(); + +// ++iewExecSquashedInsts; + exeList.pop(); + + continue; + } + + Fault fault = NoFault; + + // Execute instruction. + // Note that if the instruction faults, it will be handled + // at the commit stage. + if (inst->isMemRef() && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(BE, "Execute: Initiating access for memory " + "reference.\n"); + + if (inst->isLoad()) { + LSQ.executeLoad(inst); + } else if (inst->isStore()) { + LSQ.executeStore(inst); + if (inst->req && !(inst->req->getFlags() & LOCKED)) { + inst->setExecuted(); + + instToCommit(inst); + } + } else { + panic("Unknown mem type!"); + } + } else { + inst->execute(); + + inst->setExecuted(); + + instToCommit(inst); + } + + updateExeInstStats(inst); + + ++funcExeInst; + ++num_executed; + + exeList.pop(); + + if (inst->mispredicted()) { + squashDueToBranch(inst); + break; + } else if (LSQ.violation()) { + // Get the DynInst that caused the violation. Note that this + // clears the violation signal. + DynInstPtr violator; + violator = LSQ.getMemDepViolator(); + + DPRINTF(BE, "LDSTQ detected a violation. Violator PC: " + "%#x, inst PC: %#x. Addr is: %#x.\n", + violator->readPC(), inst->readPC(), inst->physEffAddr); + + // Squash. + squashDueToMemViolation(inst); + } + } + + issued_ops[0]+= num_executed; + n_issued_dist[num_executed]++; +} + +template<class Impl> +void +LWBackEnd<Impl>::instToCommit(DynInstPtr &inst) +{ + + DPRINTF(BE, "Sending instructions to commit [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + if (!inst->isSquashed()) { + DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + inst->setCanCommit(); + + if (inst->isExecuted()) { + inst->setResultReady(); + int dependents = wakeDependents(inst); + if (dependents) { + producer_inst[0]++; + consumer_inst[0]+= dependents; + } + } + } + + writeback_count[0]++; +} +#if 0 +template <class Impl> +void +LWBackEnd<Impl>::writebackInsts() +{ + int wb_width = wbWidth; + // Using this method I'm not quite sure how to prevent an + // instruction from waking its own dependents multiple times, + // without the guarantee that commit always has enough bandwidth + // to accept all instructions being written back. This guarantee + // might not be too unrealistic. + InstListIt wb_inst_it = writeback.begin(); + InstListIt wb_end_it = writeback.end(); + int inst_num = 0; + int consumer_insts = 0; + + for (; inst_num < wb_width && + wb_inst_it != wb_end_it; inst_num++) { + DynInstPtr inst = (*wb_inst_it); + + // Some instructions will be sent to commit without having + // executed because they need commit to handle them. + // E.g. Uncached loads have not actually executed when they + // are first sent to commit. Instead commit must tell the LSQ + // when it's ready to execute the uncached load. + if (!inst->isSquashed()) { + DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + inst->setCanCommit(); + inst->setResultReady(); + + if (inst->isExecuted()) { + int dependents = wakeDependents(inst); + if (dependents) { + producer_inst[0]++; + consumer_insts+= dependents; + } + } + } + + writeback.erase(wb_inst_it++); + } + LSQ.writebackStores(); + consumer_inst[0]+= consumer_insts; + writeback_count[0]+= inst_num; +} +#endif +template <class Impl> +bool +LWBackEnd<Impl>::commitInst(int inst_num) +{ + // Read instruction from the head of the ROB + DynInstPtr inst = instList.back(); + + // Make sure instruction is valid + assert(inst); + + if (!inst->readyToCommit()) + return false; + + DPRINTF(BE, "Trying to commit instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + thread->setPC(inst->readPC()); + thread->setNextPC(inst->readNextPC()); + inst->setAtCommit(); + + // If the instruction is not executed yet, then it is a non-speculative + // or store inst. Signal backwards that it should be executed. + if (!inst->isExecuted()) { + if (inst->isNonSpeculative() || + inst->isStoreConditional() || + inst->isMemBarrier() || + inst->isWriteBarrier()) { +#if !FULL_SYSTEM + // Hack to make sure syscalls aren't executed until all stores + // write back their data. This direct communication shouldn't + // be used for anything other than this. + if (inst_num > 0 || LSQ.hasStoresToWB()) +#else + if ((inst->isMemBarrier() || inst->isWriteBarrier() || + inst->isQuiesce()) && + LSQ.hasStoresToWB()) +#endif + { + DPRINTF(BE, "Waiting for all stores to writeback.\n"); + return false; + } + + DPRINTF(BE, "Encountered a store or non-speculative " + "instruction at the head of the ROB, PC %#x.\n", + inst->readPC()); + + if (inst->isMemBarrier() || inst->isWriteBarrier()) { + DPRINTF(BE, "Waking dependents on barrier [sn:%lli]\n", + inst->seqNum); + assert(memBarrier); + wakeDependents(inst, true); + if (memBarrier == inst) + memBarrier = NULL; + inst->clearMemDependents(); + } + + // Send back the non-speculative instruction's sequence number. + if (inst->iqItValid) { + DPRINTF(BE, "Removing instruction from waiting list\n"); + waitingList.erase(inst->iqIt); + inst->iqItValid = false; + waitingInsts--; + assert(waitingInsts >= 0); + if (inst->isStore()) + removeWaitingMemOp(inst); + } + + exeList.push(inst); + + // Change the instruction so it won't try to commit again until + // it is executed. + inst->clearCanCommit(); + +// ++commitNonSpecStalls; + + return false; + } else if (inst->isLoad()) { + DPRINTF(BE, "[sn:%lli]: Uncached load, PC %#x.\n", + inst->seqNum, inst->readPC()); + + // Send back the non-speculative instruction's sequence + // number. Maybe just tell the lsq to re-execute the load. + + // Send back the non-speculative instruction's sequence number. + if (inst->iqItValid) { + DPRINTF(BE, "Removing instruction from waiting list\n"); + waitingList.erase(inst->iqIt); + inst->iqItValid = false; + waitingInsts--; + assert(waitingInsts >= 0); + removeWaitingMemOp(inst); + } + replayMemInst(inst); + + inst->clearCanCommit(); + + return false; + } else { + panic("Trying to commit un-executed instruction " + "of unknown type!\n"); + } + } + + // Not handled for now. + assert(!inst->isThreadSync()); + assert(inst->memDepReady()); + // Stores will mark themselves as totally completed as they need + // to wait to writeback to memory. @todo: Hack...attempt to fix + // having the checker be forced to wait until a store completes in + // order to check all of the instructions. If the store at the + // head of the check list misses, but a later store hits, then + // loads in the checker may see the younger store values instead + // of the store they should see. Either the checker needs its own + // memory (annoying to update), its own store buffer (how to tell + // which value is correct?), or something else... + if (!inst->isStore()) { + inst->setCompleted(); + } + // Check if the instruction caused a fault. If so, trap. + Fault inst_fault = inst->getFault(); + + // Use checker prior to updating anything due to traps or PC + // based events. +#if USE_CHECKER + if (checker) { + checker->verify(inst); + } +#endif + + if (inst_fault != NoFault) { + DPRINTF(BE, "Inst [sn:%lli] PC %#x has a fault\n", + inst->seqNum, inst->readPC()); + + // Instruction is completed as it has a fault. + inst->setCompleted(); + + if (LSQ.hasStoresToWB()) { + DPRINTF(BE, "Stores still in flight, will wait until drained.\n"); + return false; + } else if (inst_num != 0) { + DPRINTF(BE, "Will wait until instruction is head of commit group.\n"); + return false; + } +#if USE_CHECKER + else if (checker && inst->isStore()) { + checker->verify(inst); + } +#endif + + thread->setInst( + static_cast<TheISA::MachInst>(inst->staticInst->machInst)); +#if FULL_SYSTEM + handleFault(inst_fault); + return false; +#else // !FULL_SYSTEM + panic("fault (%d) detected @ PC %08p", inst_fault, + inst->PC); +#endif // FULL_SYSTEM + } + + int freed_regs = 0; + + for (int i = 0; i < inst->numDestRegs(); ++i) { + DPRINTF(BE, "Commit rename map setting reg %i to [sn:%lli]\n", + (int)inst->destRegIdx(i), inst->seqNum); + thread->renameTable[inst->destRegIdx(i)] = inst; + ++freed_regs; + } + + if (inst->traceData) { + inst->traceData->setFetchSeq(inst->seqNum); + inst->traceData->setCPSeq(thread->numInst); + inst->traceData->finalize(); + inst->traceData = NULL; + } + + inst->clearDependents(); + + frontEnd->addFreeRegs(freed_regs); + + instList.pop_back(); + + --numInsts; + ++thread->funcExeInst; + // Maybe move this to where the fault is handled; if the fault is + // handled, don't try to set this myself as the fault will set it. + // If not, then I set thread->PC = thread->nextPC and + // thread->nextPC = thread->nextPC + 4. + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextPC() + sizeof(TheISA::MachInst)); + updateComInstStats(inst); + + // Write the done sequence number here. + toIEW->doneSeqNum = inst->seqNum; + lastCommitCycle = curTick; + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread->inSyscall && !thread->trapPending); + oldpc = thread->readPC(); + cpu->system->pcEventQueue.service( + thread->getTC()); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + DPRINTF(BE, "PC skip function event, stopping commit\n"); + tcSquash = true; + return false; + } +#endif + return true; +} + +template <class Impl> +void +LWBackEnd<Impl>::commitInsts() +{ + // Not sure this should be a loop or not. + int inst_num = 0; + while (!instList.empty() && inst_num < commitWidth) { + if (instList.back()->isSquashed()) { + instList.back()->clearDependents(); + instList.pop_back(); + --numInsts; + ROBSquashedInsts[instList.back()->threadNumber]++; + continue; + } + + if (!commitInst(inst_num++)) { + DPRINTF(BE, "Can't commit, Instruction [sn:%lli] PC " + "%#x is head of ROB and not ready\n", + instList.back()->seqNum, instList.back()->readPC()); + --inst_num; + break; + } + } + n_committed_dist.sample(inst_num); +} + +template <class Impl> +void +LWBackEnd<Impl>::squash(const InstSeqNum &sn) +{ + LSQ.squash(sn); + + int freed_regs = 0; + InstListIt waiting_list_end = waitingList.end(); + InstListIt insts_it = waitingList.begin(); + + while (insts_it != waiting_list_end && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + ++insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on waitingList PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + if ((*insts_it)->isMemRef()) { + DPRINTF(BE, "Squashing a waiting mem op [sn:%lli]\n", + (*insts_it)->seqNum); + removeWaitingMemOp((*insts_it)); + } + + waitingList.erase(insts_it++); + waitingInsts--; + } + assert(waitingInsts >= 0); + + insts_it = instList.begin(); + + while (!instList.empty() && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + ++insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on inst list PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*insts_it)->setSquashed(); + + (*insts_it)->setCanCommit(); + + (*insts_it)->clearInROB(); + + for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { + DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); + DPRINTF(BE, "Commit rename map setting reg %i to [sn:%lli]\n", + (int)(*insts_it)->destRegIdx(i), prev_dest->seqNum); + renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; + ++freed_regs; + } + + (*insts_it)->clearDependents(); + + squashedInsts[(*insts_it)->threadNumber]++; + + instList.erase(insts_it++); + --numInsts; + } + + insts_it = waitingList.begin(); + while (!waitingList.empty() && insts_it != waitingList.end()) { + if ((*insts_it)->seqNum < sn) { + ++insts_it; + continue; + } + assert((*insts_it)->isSquashed()); + + waitingList.erase(insts_it++); + waitingInsts--; + } + + while (memBarrier && memBarrier->seqNum > sn) { + DPRINTF(BE, "[sn:%lli] Memory barrier squashed (or previously " + "squashed)\n", memBarrier->seqNum); + memBarrier->clearMemDependents(); + if (memBarrier->memDepReady()) { + DPRINTF(BE, "No previous barrier\n"); + memBarrier = NULL; + } else { + std::list<DynInstPtr> &srcs = memBarrier->getMemSrcs(); + memBarrier = srcs.front(); + srcs.pop_front(); + assert(srcs.empty()); + DPRINTF(BE, "Previous barrier: [sn:%lli]\n", + memBarrier->seqNum); + } + } + + frontEnd->addFreeRegs(freed_regs); +} + +template <class Impl> +void +LWBackEnd<Impl>::squashFromTC() +{ + InstSeqNum squashed_inst = robEmpty() ? 0 : instList.back()->seqNum - 1; + squash(squashed_inst); + frontEnd->squash(squashed_inst, thread->readPC(), + false, false); + frontEnd->interruptPending = false; + + thread->trapPending = false; + thread->inSyscall = false; + tcSquash = false; + commitStatus = Running; +} + +template <class Impl> +void +LWBackEnd<Impl>::squashFromTrap() +{ + InstSeqNum squashed_inst = robEmpty() ? 0 : instList.back()->seqNum - 1; + squash(squashed_inst); + frontEnd->squash(squashed_inst, thread->readPC(), + false, false); + frontEnd->interruptPending = false; + + thread->trapPending = false; + thread->inSyscall = false; + trapSquash = false; + commitStatus = Running; +} + +template <class Impl> +void +LWBackEnd<Impl>::squashDueToBranch(DynInstPtr &inst) +{ + // Update the branch predictor state I guess + DPRINTF(BE, "Squashing due to branch [sn:%lli], will restart at PC %#x\n", + inst->seqNum, inst->readNextPC()); + squash(inst->seqNum); + frontEnd->squash(inst->seqNum, inst->readNextPC(), + true, inst->mispredicted()); +} + +template <class Impl> +void +LWBackEnd<Impl>::squashDueToMemViolation(DynInstPtr &inst) +{ + // Update the branch predictor state I guess + DPRINTF(BE, "Squashing due to violation [sn:%lli], will restart at PC %#x\n", + inst->seqNum, inst->readNextPC()); + squash(inst->seqNum); + frontEnd->squash(inst->seqNum, inst->readNextPC(), + false, inst->mispredicted()); +} + +template <class Impl> +void +LWBackEnd<Impl>::squashDueToMemBlocked(DynInstPtr &inst) +{ + DPRINTF(IEW, "Memory blocked, squashing load and younger insts, " + "PC: %#x [sn:%i].\n", inst->readPC(), inst->seqNum); + + squash(inst->seqNum - 1); + frontEnd->squash(inst->seqNum - 1, inst->readPC()); +} + +template <class Impl> +void +LWBackEnd<Impl>::fetchFault(Fault &fault) +{ + faultFromFetch = fault; + fetchHasFault = true; +} + +template <class Impl> +void +LWBackEnd<Impl>::switchOut() +{ + switchPending = true; +} + +template <class Impl> +void +LWBackEnd<Impl>::doSwitchOut() +{ + switchedOut = true; + switchPending = false; + // Need to get rid of all committed, non-speculative state and write it + // to memory/TC. In this case this is stores that have committed and not + // yet written back. + assert(robEmpty()); + assert(!LSQ.hasStoresToWB()); + + LSQ.switchOut(); + + squash(0); +} + +template <class Impl> +void +LWBackEnd<Impl>::takeOverFrom(ThreadContext *old_tc) +{ + switchedOut = false; + tcSquash = false; + trapSquash = false; + + numInsts = 0; + numWaitingMemOps = 0; + waitingMemOps.clear(); + waitingInsts = 0; + switchedOut = false; + dispatchStatus = Running; + commitStatus = Running; + LSQ.takeOverFrom(old_tc); +} + +template <class Impl> +void +LWBackEnd<Impl>::updateExeInstStats(DynInstPtr &inst) +{ + int thread_number = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) + exe_swp[thread_number]++; + else + exe_inst[thread_number]++; +#else + exe_inst[thread_number]++; +#endif + + // + // Control operations + // + if (inst->isControl()) + exe_branches[thread_number]++; + + // + // Memory operations + // + if (inst->isMemRef()) { + exe_refs[thread_number]++; + + if (inst->isLoad()) + exe_loads[thread_number]++; + } +} + +template <class Impl> +void +LWBackEnd<Impl>::updateComInstStats(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + // keep an instruction count + thread->numInst++; + thread->numInsts++; + + cpu->numInst++; + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) { + stat_com_swp[tid]++; + } else { + stat_com_inst[tid]++; + } +#else + stat_com_inst[tid]++; +#endif + + // + // Control Instructions + // + if (inst->isControl()) + stat_com_branches[tid]++; + + // + // Memory references + // + if (inst->isMemRef()) { + stat_com_refs[tid]++; + + if (inst->isLoad()) { + stat_com_loads[tid]++; + } + } + + if (inst->isMemBarrier()) { + stat_com_membars[tid]++; + } +} + +template <class Impl> +void +LWBackEnd<Impl>::dumpInsts() +{ + int num = 0; + int valid_num = 0; + + InstListIt inst_list_it = --(instList.end()); + + cprintf("ExeList size: %i\n", exeList.size()); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("Waiting list size: %i\n", waitingList.size()); + + inst_list_it = --(waitingList.end()); + + while (inst_list_it != waitingList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("waitingMemOps list size: %i\n", waitingMemOps.size()); + + MemIt waiting_it = waitingMemOps.begin(); + + while (waiting_it != waitingMemOps.end()) + { + cprintf("[sn:%lli] ", (*waiting_it)); + waiting_it++; + ++num; + } + cprintf("\n"); +} diff --git a/src/cpu/ozone/lw_lsq.cc b/src/cpu/ozone/lw_lsq.cc new file mode 100644 index 000000000..8674e83a0 --- /dev/null +++ b/src/cpu/ozone/lw_lsq.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/lw_lsq_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class OzoneLWLSQ<OzoneImpl>; + diff --git a/src/cpu/ozone/lw_lsq.hh b/src/cpu/ozone/lw_lsq.hh new file mode 100644 index 000000000..c749e3aee --- /dev/null +++ b/src/cpu/ozone/lw_lsq.hh @@ -0,0 +1,703 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_LW_LSQ_HH__ +#define __CPU_OZONE_LW_LSQ_HH__ + +#include <list> +#include <map> +#include <queue> +#include <algorithm> + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "config/full_system.hh" +#include "base/hashmap.hh" +#include "cpu/inst_seq.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +//#include "mem/page_table.hh" +#include "sim/debug.hh" +#include "sim/sim_object.hh" + +class MemObject; + +/** + * Class that implements the actual LQ and SQ for each specific thread. + * Both are circular queues; load entries are freed upon committing, while + * store entries are freed once they writeback. The LSQUnit tracks if there + * are memory ordering violations, and also detects partial load to store + * forwarding cases (a store only has part of a load's data) that requires + * the load to wait until the store writes back. In the former case it + * holds onto the instruction until the dependence unit looks at it, and + * in the latter it stalls the LSQ until the store writes back. At that + * point the load is replayed. + */ +template <class Impl> +class OzoneLWLSQ { + public: + typedef typename Impl::Params Params; + typedef typename Impl::OzoneCPU OzoneCPU; + typedef typename Impl::BackEnd BackEnd; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::IssueStruct IssueStruct; + + typedef TheISA::IntReg IntReg; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator LdMapIt; + + public: + /** Constructs an LSQ unit. init() must be called prior to use. */ + OzoneLWLSQ(); + + /** Initializes the LSQ unit with the specified number of entries. */ + void init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id); + + /** Returns the name of the LSQ unit. */ + std::string name() const; + + /** Sets the CPU pointer. */ + void setCPU(OzoneCPU *cpu_ptr); + + /** Sets the back-end stage pointer. */ + void setBE(BackEnd *be_ptr) + { be = be_ptr; } + + /** Sets the page table pointer. */ +// void setPageTable(PageTable *pt_ptr); + + /** Ticks the LSQ unit, which in this case only resets the number of + * used cache ports. + * @todo: Move the number of used ports up to the LSQ level so it can + * be shared by all LSQ units. + */ + void tick() { usedPorts = 0; } + + /** Inserts an instruction. */ + void insert(DynInstPtr &inst); + /** Inserts a load instruction. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store instruction. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load instruction. */ + Fault executeLoad(DynInstPtr &inst); + + /** Executes a store instruction. */ + Fault executeStore(DynInstPtr &inst); + + /** Commits the head load. */ + void commitLoad(); + /** Commits loads older than a specific sequence number. */ + void commitLoads(InstSeqNum &youngest_inst); + + /** Commits stores older than a specific sequence number. */ + void commitStores(InstSeqNum &youngest_inst); + + /** Writes back stores. */ + void writebackStores(); + + /** Completes the data access that has been returned from the + * memory system. */ + void completeDataAccess(PacketPtr pkt); + + // @todo: Include stats in the LSQ unit. + //void regStats(); + + /** Clears all the entries in the LQ. */ + void clearLQ(); + + /** Clears all the entries in the SQ. */ + void clearSQ(); + + /** Resizes the LQ to a given size. */ + void resizeLQ(unsigned size); + + /** Resizes the SQ to a given size. */ + void resizeSQ(unsigned size); + + /** Squashes all instructions younger than a specific sequence number. */ + void squash(const InstSeqNum &squashed_num); + + /** Returns if there is a memory ordering violation. Value is reset upon + * call to getMemDepViolator(). + */ + bool violation() { return memDepViolator; } + + /** Returns the memory ordering violator. */ + DynInstPtr getMemDepViolator(); + + /** Returns if a load became blocked due to the memory system. It clears + * the bool's value upon this being called. + */ + bool loadBlocked() + { return isLoadBlocked; } + + void clearLoadBlocked() + { isLoadBlocked = false; } + + bool isLoadBlockedHandled() + { return loadBlockedHandled; } + + void setLoadBlockedHandled() + { loadBlockedHandled = true; } + + /** Returns the number of free entries (min of free LQ and SQ entries). */ + unsigned numFreeEntries(); + + /** Returns the number of loads ready to execute. */ + int numLoadsReady(); + + /** Returns the number of loads in the LQ. */ + int numLoads() { return loads; } + + /** Returns the number of stores in the SQ. */ + int numStores() { return stores; } + + /** Returns if either the LQ or SQ is full. */ + bool isFull() { return lqFull() || sqFull(); } + + /** Returns if the LQ is full. */ + bool lqFull() { return loads >= (LQEntries - 1); } + + /** Returns if the SQ is full. */ + bool sqFull() { return stores >= (SQEntries - 1); } + + /** Debugging function to dump instructions in the LSQ. */ + void dumpInsts(); + + /** Returns the number of instructions in the LSQ. */ + unsigned getCount() { return loads + stores; } + + /** Returns if there are any stores to writeback. */ + bool hasStoresToWB() { return storesToWB; } + + /** Returns the number of stores to writeback. */ + int numStoresToWB() { return storesToWB; } + + /** Returns if the LSQ unit will writeback on this cycle. */ + bool willWB() { return storeQueue.back().canWB && + !storeQueue.back().completed && + !isStoreBlocked; } + + void switchOut(); + + void takeOverFrom(ThreadContext *old_tc = NULL); + + bool isSwitchedOut() { return switchedOut; } + + bool switchedOut; + + private: + /** Writes back the instruction, sending it to IEW. */ + void writeback(DynInstPtr &inst, PacketPtr pkt); + + /** Handles completing the send of a store to memory. */ + void storePostSend(Packet *pkt, DynInstPtr &inst); + + /** Completes the store at the specified index. */ + void completeStore(int store_idx); + + /** Handles doing the retry. */ + void recvRetry(); + + private: + /** Pointer to the CPU. */ + OzoneCPU *cpu; + + /** Pointer to the back-end stage. */ + BackEnd *be; + + MemObject *mem; + + class DcachePort : public Port + { + protected: + OzoneCPU *cpu; + + OzoneLWLSQ *lsq; + + public: + DcachePort(OzoneCPU *_cpu, OzoneLWLSQ *_lsq) + : Port(_lsq->name() + "-dport"), cpu(_cpu), lsq(_lsq) + { } + + protected: + virtual Tick recvAtomic(PacketPtr pkt); + + virtual void recvFunctional(PacketPtr pkt); + + virtual void recvStatusChange(Status status); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + virtual bool recvTiming(PacketPtr pkt); + + virtual void recvRetry(); + }; + + /** Pointer to the D-cache. */ + DcachePort *dcachePort; + + /** Pointer to the page table. */ +// PageTable *pTable; + + public: + struct SQEntry { + /** Constructs an empty store queue entry. */ + SQEntry() + : inst(NULL), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0), lqIt(NULL) + { } + + /** Constructs a store queue entry for a given instruction. */ + SQEntry(DynInstPtr &_inst) + : inst(_inst), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0), lqIt(NULL) + { } + + /** The store instruction. */ + DynInstPtr inst; + /** The memory request for the store. */ + RequestPtr req; + /** The size of the store. */ + int size; + /** The store data. */ + IntReg data; + /** Whether or not the store can writeback. */ + bool canWB; + /** Whether or not the store is committed. */ + bool committed; + /** Whether or not the store is completed. */ + bool completed; + + typename std::list<DynInstPtr>::iterator lqIt; + }; + + /** Derived class to hold any sender state the LSQ needs. */ + class LSQSenderState : public Packet::SenderState + { + public: + /** Default constructor. */ + LSQSenderState() + : noWB(false) + { } + + /** Instruction who initiated the access to memory. */ + DynInstPtr inst; + /** Whether or not it is a load. */ + bool isLoad; + /** The LQ/SQ index of the instruction. */ + int idx; + /** Whether or not the instruction will need to writeback. */ + bool noWB; + }; + + /** Writeback event, specifically for when stores forward data to loads. */ + class WritebackEvent : public Event { + public: + /** Constructs a writeback event. */ + WritebackEvent(DynInstPtr &_inst, PacketPtr pkt, OzoneLWLSQ *lsq_ptr); + + /** Processes the writeback event. */ + void process(); + + /** Returns the description of this event. */ + const char *description(); + + private: + /** Instruction whose results are being written back. */ + DynInstPtr inst; + + /** The packet that would have been sent to memory. */ + PacketPtr pkt; + + /** The pointer to the LSQ unit that issued the store. */ + OzoneLWLSQ<Impl> *lsqPtr; + }; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissSwitch + }; + + private: + /** The OzoneLWLSQ thread id. */ + unsigned lsqID; + + /** The status of the LSQ unit. */ + Status _status; + + /** The store queue. */ + std::list<SQEntry> storeQueue; + /** The load queue. */ + std::list<DynInstPtr> loadQueue; + + typedef typename std::list<SQEntry>::iterator SQIt; + typedef typename std::list<DynInstPtr>::iterator LQIt; + + + struct HashFn { + size_t operator() (const int a) const + { + unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; + + return hash; + } + }; + + m5::hash_map<int, SQIt, HashFn> SQItHash; + std::queue<int> SQIndices; + m5::hash_map<int, LQIt, HashFn> LQItHash; + std::queue<int> LQIndices; + + typedef typename m5::hash_map<int, LQIt, HashFn>::iterator LQHashIt; + typedef typename m5::hash_map<int, SQIt, HashFn>::iterator SQHashIt; + // Consider making these 16 bits + /** The number of LQ entries. */ + unsigned LQEntries; + /** The number of SQ entries. */ + unsigned SQEntries; + + /** The number of load instructions in the LQ. */ + int loads; + /** The number of store instructions in the SQ (excludes those waiting to + * writeback). + */ + int stores; + + int storesToWB; + + /// @todo Consider moving to a more advanced model with write vs read ports + /** The number of cache ports available each cycle. */ + int cachePorts; + + /** The number of used cache ports in this cycle. */ + int usedPorts; + + //list<InstSeqNum> mshrSeqNums; + + //Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + // Make these per thread? + /** Whether or not the LSQ is stalled. */ + bool stalled; + /** The store that causes the stall due to partial store to load + * forwarding. + */ + InstSeqNum stallingStoreIsn; + /** The index of the above store. */ + LQIt stallingLoad; + + /** The packet that needs to be retried. */ + PacketPtr retryPkt; + + /** Whehter or not a store is blocked due to the memory system. */ + bool isStoreBlocked; + + /** Whether or not a load is blocked due to the memory system. It is + * cleared when this value is checked via loadBlocked(). + */ + bool isLoadBlocked; + + bool loadBlockedHandled; + + InstSeqNum blockedLoadSeqNum; + + /** The oldest faulting load instruction. */ + DynInstPtr loadFaultInst; + /** The oldest faulting store instruction. */ + DynInstPtr storeFaultInst; + + /** The oldest load that caused a memory ordering violation. */ + DynInstPtr memDepViolator; + + // Will also need how many read/write ports the Dcache has. Or keep track + // of that in stage that is one level up, and only call executeLoad/Store + // the appropriate number of times. + + public: + /** Executes the load at the given index. */ + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + /** Returns the sequence number of the head load instruction. */ + InstSeqNum getLoadHeadSeqNum() + { + if (!loadQueue.empty()) { + return loadQueue.back()->seqNum; + } else { + return 0; + } + + } + + /** Returns the sequence number of the head store instruction. */ + InstSeqNum getStoreHeadSeqNum() + { + if (!storeQueue.empty()) { + return storeQueue.back().inst->seqNum; + } else { + return 0; + } + + } + + /** Returns whether or not the LSQ unit is stalled. */ + bool isStalled() { return stalled; } +}; + +template <class Impl> +template <class T> +Fault +OzoneLWLSQ<Impl>::read(RequestPtr req, T &data, int load_idx) +{ + //Depending on issue2execute delay a squashed load could + //execute if it is found to be squashed in the same + //cycle it is scheduled to execute + typename m5::hash_map<int, LQIt, HashFn>::iterator + lq_hash_it = LQItHash.find(load_idx); + assert(lq_hash_it != LQItHash.end()); + DynInstPtr inst = (*(*lq_hash_it).second); + + // Make sure this isn't an uncacheable access + // A bit of a hackish way to get uncached accesses to work only if they're + // at the head of the LSQ and are ready to commit (at the head of the ROB + // too). + // @todo: Fix uncached accesses. + if (req->getFlags() & UNCACHEABLE && + (inst != loadQueue.back() || !inst->isAtCommit())) { + DPRINTF(OzoneLSQ, "[sn:%lli] Uncached load and not head of " + "commit/LSQ!\n", + inst->seqNum); + be->rescheduleMemInst(inst); + return TheISA::genMachineCheckFault(); + } + + // Check the SQ for any previous stores that might lead to forwarding + SQIt sq_it = storeQueue.begin(); + int store_size = 0; + + DPRINTF(OzoneLSQ, "Read called, load idx: %i addr: %#x\n", + load_idx, req->getPaddr()); + + while (sq_it != storeQueue.end() && (*sq_it).inst->seqNum > inst->seqNum) + ++sq_it; + + while (1) { + // End once we've reached the top of the LSQ + if (sq_it == storeQueue.end()) { + break; + } + + assert((*sq_it).inst); + + store_size = (*sq_it).size; + + if (store_size == 0) { + sq_it++; + continue; + } + + // Check if the store data is within the lower and upper bounds of + // addresses that the request needs. + bool store_has_lower_limit = + req->getVaddr() >= (*sq_it).inst->effAddr; + bool store_has_upper_limit = + (req->getVaddr() + req->getSize()) <= ((*sq_it).inst->effAddr + + store_size); + bool lower_load_has_store_part = + req->getVaddr() < ((*sq_it).inst->effAddr + + store_size); + bool upper_load_has_store_part = + (req->getVaddr() + req->getSize()) > (*sq_it).inst->effAddr; + + // If the store's data has all of the data needed, we can forward. + if (store_has_lower_limit && store_has_upper_limit) { + int shift_amt = req->getVaddr() & (store_size - 1); + // Assumes byte addressing + shift_amt = shift_amt << 3; + + // Cast this to type T? + data = (*sq_it).data >> shift_amt; + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + + memcpy(inst->memData, &data, req->getSize()); + + DPRINTF(OzoneLSQ, "Forwarding from store [sn:%lli] to load to " + "[sn:%lli] addr %#x, data %#x\n", + (*sq_it).inst->seqNum, inst->seqNum, req->getVaddr(), + *(inst->memData)); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + WritebackEvent *wb = new WritebackEvent(inst, data_pkt, this); + + // We'll say this has a 1 cycle load-store forwarding latency + // for now. + // @todo: Need to make this a parameter. + wb->schedule(curTick); + + // Should keep track of stat for forwarded data + return NoFault; + } else if ((store_has_lower_limit && lower_load_has_store_part) || + (store_has_upper_limit && upper_load_has_store_part) || + (lower_load_has_store_part && upper_load_has_store_part)) { + // This is the partial store-load forwarding case where a store + // has only part of the load's data. + + // If it's already been written back, then don't worry about + // stalling on it. + if ((*sq_it).completed) { + sq_it++; + break; + } + + // Must stall load and force it to retry, so long as it's the oldest + // load that needs to do so. + if (!stalled || + (stalled && + inst->seqNum < + (*stallingLoad)->seqNum)) { + stalled = true; + stallingStoreIsn = (*sq_it).inst->seqNum; + stallingLoad = (*lq_hash_it).second; + } + + // Tell IQ/mem dep unit that this instruction will need to be + // rescheduled eventually + be->rescheduleMemInst(inst); + + DPRINTF(OzoneLSQ, "Load-store forwarding mis-match. " + "Store [sn:%lli] to load addr %#x\n", + (*sq_it).inst->seqNum, req->getVaddr()); + + return NoFault; + } + sq_it++; + } + + // If there's no forwarding case, then go access memory + DPRINTF(OzoneLSQ, "Doing functional access for inst PC %#x\n", + inst->readPC()); + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + + ++usedPorts; + + DPRINTF(OzoneLSQ, "Doing timing access for inst PC %#x\n", + inst->readPC()); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = true; + state->idx = load_idx; + state->inst = inst; + data_pkt->senderState = state; + + // if we have a cache, do cache access too + if (!dcachePort->sendTiming(data_pkt)) { + // There's an older load that's already going to squash. + if (isLoadBlocked && blockedLoadSeqNum < inst->seqNum) + return NoFault; + + // Record that the load was blocked due to memory. This + // load will squash all instructions after it, be + // refetched, and re-executed. + isLoadBlocked = true; + loadBlockedHandled = false; + blockedLoadSeqNum = inst->seqNum; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + + if (req->getFlags() & LOCKED) { + cpu->lockFlag = true; + } + + if (data_pkt->result != Packet::Success) { + DPRINTF(OzoneLSQ, "OzoneLSQ: D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + inst->seqNum); + } else { + DPRINTF(OzoneLSQ, "OzoneLSQ: D-cache hit!\n"); + DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", + inst->seqNum); + } + + return NoFault; +} + +template <class Impl> +template <class T> +Fault +OzoneLWLSQ<Impl>::write(RequestPtr req, T &data, int store_idx) +{ + SQHashIt sq_hash_it = SQItHash.find(store_idx); + assert(sq_hash_it != SQItHash.end()); + + SQIt sq_it = (*sq_hash_it).second; + assert((*sq_it).inst); + + DPRINTF(OzoneLSQ, "Doing write to store idx %i, addr %#x data %#x" + " | [sn:%lli]\n", + store_idx, req->getPaddr(), data, (*sq_it).inst->seqNum); + + (*sq_it).req = req; + (*sq_it).size = sizeof(T); + (*sq_it).data = data; +/* + assert(!req->data); + req->data = new uint8_t[64]; + memcpy(req->data, (uint8_t *)&(*sq_it).data, req->size); +*/ + + // This function only writes the data to the store queue, so no fault + // can happen here. + return NoFault; +} + +#endif // __CPU_OZONE_LW_LSQ_HH__ diff --git a/src/cpu/ozone/lw_lsq_impl.hh b/src/cpu/ozone/lw_lsq_impl.hh new file mode 100644 index 000000000..a65a2a4d3 --- /dev/null +++ b/src/cpu/ozone/lw_lsq_impl.hh @@ -0,0 +1,973 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "config/use_checker.hh" + +#include "arch/isa_traits.hh" +#include "base/str.hh" +#include "cpu/ozone/lw_lsq.hh" +#include "cpu/checker/cpu.hh" + +template<class Impl> +OzoneLWLSQ<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, PacketPtr _pkt, + OzoneLWLSQ *lsq_ptr) + : Event(&mainEventQueue), inst(_inst), pkt(_pkt), lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::WritebackEvent::process() +{ + if (!lsqPtr->isSwitchedOut()) { + lsqPtr->writeback(inst, pkt); + } + delete pkt; +} + +template<class Impl> +const char * +OzoneLWLSQ<Impl>::WritebackEvent::description() +{ + return "Store writeback event"; +} + +template <class Impl> +Tick +OzoneLWLSQ<Impl>::DcachePort::recvAtomic(PacketPtr pkt) +{ + panic("O3CPU model does not work with atomic mode!"); + return curTick; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::DcachePort::recvFunctional(PacketPtr pkt) +{ + panic("O3CPU doesn't expect recvFunctional callback!"); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::DcachePort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("O3CPU doesn't expect recvStatusChange callback!"); +} + +template <class Impl> +bool +OzoneLWLSQ<Impl>::DcachePort::recvTiming(PacketPtr pkt) +{ + lsq->completeDataAccess(pkt); + return true; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::DcachePort::recvRetry() +{ + lsq->recvRetry(); +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::completeDataAccess(PacketPtr pkt) +{ + LSQSenderState *state = dynamic_cast<LSQSenderState *>(pkt->senderState); + DynInstPtr inst = state->inst; + DPRINTF(IEW, "Writeback event [sn:%lli]\n", inst->seqNum); + DPRINTF(Activity, "Activity: Writeback event [sn:%lli]\n", inst->seqNum); + + //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); + + if (isSwitchedOut() || inst->isSquashed()) { + delete state; + delete pkt; + return; + } else { + if (!state->noWB) { + writeback(inst, pkt); + } + + if (inst->isStore()) { + completeStore(state->idx); + } + } + + delete state; + delete pkt; +} + +template <class Impl> +OzoneLWLSQ<Impl>::OzoneLWLSQ() + : switchedOut(false), loads(0), stores(0), storesToWB(0), stalled(false), + isStoreBlocked(false), isLoadBlocked(false), loadBlockedHandled(false) +{ +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) +{ + DPRINTF(OzoneLSQ, "Creating OzoneLWLSQ%i object.\n",id); + + lsqID = id; + + LQEntries = maxLQEntries; + SQEntries = maxSQEntries; + + for (int i = 0; i < LQEntries * 2; i++) { + LQIndices.push(i); + SQIndices.push(i); + } + + mem = params->mem; + + usedPorts = 0; + cachePorts = params->cachePorts; + + loadFaultInst = storeFaultInst = memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} + +template<class Impl> +std::string +OzoneLWLSQ<Impl>::name() const +{ + return "lsqunit"; +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::setCPU(OzoneCPU *cpu_ptr) +{ + cpu = cpu_ptr; + dcachePort = new DcachePort(cpu, this); + + Port *mem_dport = mem->getPort(""); + dcachePort->setPeer(mem_dport); + mem_dport->setPeer(dcachePort); + +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->setDcachePort(dcachePort); + } +#endif +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::clearLQ() +{ + loadQueue.clear(); +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::clearSQ() +{ + storeQueue.clear(); +} +/* +template<class Impl> +void +OzoneLWLSQ<Impl>::setPageTable(PageTable *pt_ptr) +{ + DPRINTF(OzoneLSQ, "Setting the page table pointer.\n"); + pTable = pt_ptr; +} +*/ +template<class Impl> +void +OzoneLWLSQ<Impl>::resizeLQ(unsigned size) +{ + assert( size >= LQEntries); + + if (size > LQEntries) { + while (size > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size; + } + +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::resizeSQ(unsigned size) +{ + if (size > SQEntries) { + while (size > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size; + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::insert(DynInstPtr &inst) +{ + // Make sure we really have a memory reference. + assert(inst->isMemRef()); + + // Make sure it's one of the two classes of memory references. + assert(inst->isLoad() || inst->isStore()); + + if (inst->isLoad()) { + insertLoad(inst); + } else { + insertStore(inst); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::insertLoad(DynInstPtr &load_inst) +{ + assert(loads < LQEntries * 2); + assert(!LQIndices.empty()); + int load_index = LQIndices.front(); + LQIndices.pop(); + + DPRINTF(OzoneLSQ, "Inserting load PC %#x, idx:%i [sn:%lli]\n", + load_inst->readPC(), load_index, load_inst->seqNum); + + load_inst->lqIdx = load_index; + + loadQueue.push_front(load_inst); + LQItHash[load_index] = loadQueue.begin(); + + ++loads; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::insertStore(DynInstPtr &store_inst) +{ + // Make sure it is not full before inserting an instruction. + assert(stores - storesToWB < SQEntries); + + assert(!SQIndices.empty()); + int store_index = SQIndices.front(); + SQIndices.pop(); + + DPRINTF(OzoneLSQ, "Inserting store PC %#x, idx:%i [sn:%lli]\n", + store_inst->readPC(), store_index, store_inst->seqNum); + + store_inst->sqIdx = store_index; + SQEntry entry(store_inst); + if (loadQueue.empty()) { + entry.lqIt = loadQueue.end(); + } else { + entry.lqIt = loadQueue.begin(); + } + storeQueue.push_front(entry); + + SQItHash[store_index] = storeQueue.begin(); + + ++stores; +} + +template <class Impl> +typename Impl::DynInstPtr +OzoneLWLSQ<Impl>::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template <class Impl> +unsigned +OzoneLWLSQ<Impl>::numFreeEntries() +{ + unsigned free_lq_entries = LQEntries - loads; + unsigned free_sq_entries = SQEntries - stores; + + // Both the LQ and SQ entries have an extra dummy entry to differentiate + // empty/full conditions. Subtract 1 from the free entries. + if (free_lq_entries < free_sq_entries) { + return free_lq_entries - 1; + } else { + return free_sq_entries - 1; + } +} + +template <class Impl> +int +OzoneLWLSQ<Impl>::numLoadsReady() +{ + int retval = 0; + LQIt lq_it = loadQueue.begin(); + LQIt end_it = loadQueue.end(); + + while (lq_it != end_it) { + if ((*lq_it)->readyToIssue()) { + ++retval; + } + } + + return retval; +} + +template <class Impl> +Fault +OzoneLWLSQ<Impl>::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(OzoneLSQ, "Executing load PC %#x, [sn:%lli]\n", + inst->readPC(),inst->seqNum); + + // Make sure it's really in the list. + // Normally it should always be in the list. However, + /* due to a syscall it may not be the list. +#ifdef DEBUG + int i = loadHead; + while (1) { + if (i == loadTail && !find(inst)) { + assert(0 && "Load not in the queue!"); + } else if (loadQueue[i] == inst) { + break; + } + + i = i + 1; + if (i >= LQEntries) { + i = 0; + } + } +#endif // DEBUG*/ + + load_fault = inst->initiateAcc(); + + // Might want to make sure that I'm not overwriting a previously faulting + // instruction that hasn't been checked yet. + // Actually probably want the oldest faulting load + if (load_fault != NoFault) { + DPRINTF(OzoneLSQ, "Load [sn:%lli] has a fault\n", inst->seqNum); + // Maybe just set it as can commit here, although that might cause + // some other problems with sending traps to the ROB too quickly. + be->instToCommit(inst); +// iewStage->activityThisCycle(); + } + + return load_fault; +} + +template <class Impl> +Fault +OzoneLWLSQ<Impl>::executeStore(DynInstPtr &store_inst) +{ + // Make sure that a store exists. + assert(stores != 0); + + int store_idx = store_inst->sqIdx; + SQHashIt sq_hash_it = SQItHash.find(store_idx); + assert(sq_hash_it != SQItHash.end()); + DPRINTF(OzoneLSQ, "Executing store PC %#x [sn:%lli]\n", + store_inst->readPC(), store_inst->seqNum); + + SQIt sq_it = (*sq_hash_it).second; + + Fault store_fault = store_inst->initiateAcc(); + + // Store size should now be available. Use it to get proper offset for + // addr comparisons. + int size = (*sq_it).size; + + if (size == 0) { + DPRINTF(OzoneLSQ,"Fault on Store PC %#x, [sn:%lli],Size = 0\n", + store_inst->readPC(),store_inst->seqNum); + + return store_fault; + } + + assert(store_fault == NoFault); + + if (!storeFaultInst) { + if (store_fault != NoFault) { + panic("Fault in a store instruction!"); + storeFaultInst = store_inst; + } else if (store_inst->isStoreConditional()) { + // Store conditionals need to set themselves as able to + // writeback if we haven't had a fault by here. + (*sq_it).canWB = true; + + ++storesToWB; + DPRINTF(OzoneLSQ, "Nonspeculative store! storesToWB:%i\n", + storesToWB); + } + } + + LQIt lq_it = --(loadQueue.end()); + + if (!memDepViolator) { + while (lq_it != loadQueue.end()) { + if ((*lq_it)->seqNum < store_inst->seqNum) { + lq_it--; + continue; + } + // Actually should only check loads that have actually executed + // Might be safe because effAddr is set to InvalAddr when the + // dyn inst is created. + + // Must actually check all addrs in the proper size range + // Which is more correct than needs to be. What if for now we just + // assume all loads are quad-word loads, and do the addr based + // on that. + // @todo: Fix this, magic number being used here + if (((*lq_it)->effAddr >> 8) == + (store_inst->effAddr >> 8)) { + // A load incorrectly passed this store. Squash and refetch. + // For now return a fault to show that it was unsuccessful. + memDepViolator = (*lq_it); + + return TheISA::genMachineCheckFault(); + } + + lq_it--; + } + + // If we've reached this point, there was no violation. + memDepViolator = NULL; + } + + return store_fault; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::commitLoad() +{ + assert(!loadQueue.empty()); + + DPRINTF(OzoneLSQ, "[sn:%lli] Committing head load instruction, PC %#x\n", + loadQueue.back()->seqNum, loadQueue.back()->readPC()); + + LQIndices.push(loadQueue.back()->lqIdx); + LQItHash.erase(loadQueue.back()->lqIdx); + + loadQueue.pop_back(); + + --loads; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || !loadQueue.empty()); + + while (loads != 0 && + loadQueue.back()->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::commitStores(InstSeqNum &youngest_inst) +{ + assert(stores == 0 || !storeQueue.empty()); + + SQIt sq_it = --(storeQueue.end()); + while (!storeQueue.empty() && sq_it != storeQueue.end()) { + assert((*sq_it).inst); + if (!(*sq_it).canWB) { + if ((*sq_it).inst->seqNum > youngest_inst) { + break; + } + ++storesToWB; + + DPRINTF(OzoneLSQ, "Marking store as able to write back, PC " + "%#x [sn:%lli], storesToWB:%i\n", + (*sq_it).inst->readPC(), + (*sq_it).inst->seqNum, + storesToWB); + + (*sq_it).canWB = true; + } + + sq_it--; + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::writebackStores() +{ + SQIt sq_it = --(storeQueue.end()); + while (storesToWB > 0 && + sq_it != storeQueue.end() && + (*sq_it).inst && + (*sq_it).canWB && + usedPorts < cachePorts) { + + if (isStoreBlocked) { + DPRINTF(OzoneLSQ, "Unable to write back any more stores, cache" + " is blocked!\n"); + break; + } + + DynInstPtr inst = (*sq_it).inst; + + if ((*sq_it).size == 0 && !(*sq_it).completed) { + sq_it--; + completeStore(inst->sqIdx); + + continue; + } + + if (inst->isDataPrefetch() || (*sq_it).committed) { + sq_it--; + continue; + } + + ++usedPorts; + + assert((*sq_it).req); + assert(!(*sq_it).committed); + + Request *req = (*sq_it).req; + (*sq_it).committed = true; + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + memcpy(inst->memData, (uint8_t *)&(*sq_it).data, + req->getSize()); + + PacketPtr data_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = false; + state->idx = inst->sqIdx; + state->inst = inst; + data_pkt->senderState = state; + + DPRINTF(OzoneLSQ, "D-Cache: Writing back store PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + (*sq_it).inst->readPC(), + req->getPaddr(), *(inst->memData), + inst->seqNum); + + // @todo: Remove this SC hack once the memory system handles it. + if (req->getFlags() & LOCKED) { + if (req->getFlags() & UNCACHEABLE) { + req->setScResult(2); + } else { + if (cpu->lockFlag) { + req->setScResult(1); + } else { + req->setScResult(0); + // Hack: Instantly complete this store. + completeDataAccess(data_pkt); + --sq_it; + continue; + } + } + } else { + // Non-store conditionals do not need a writeback. + state->noWB = true; + } + + if (!dcachePort->sendTiming(data_pkt)) { + // Need to handle becoming blocked on a store. + isStoreBlocked = true; + assert(retryPkt == NULL); + retryPkt = data_pkt; + } else { + storePostSend(data_pkt, inst); + --sq_it; + } +/* + DPRINTF(OzoneLSQ, "D-Cache: Writing back store idx:%i PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + inst->sqIdx,inst->readPC(), + req->paddr, *(req->data), + inst->seqNum); + + if (dcacheInterface) { + assert(!req->completionEvent); + StoreCompletionEvent *store_event = new + StoreCompletionEvent(inst, be, NULL, this); + req->completionEvent = store_event; + + MemAccessResult result = dcacheInterface->access(req); + + if (isStalled() && + inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load [sn:%lli]\n", + stallingStoreIsn, (*stallingLoad)->seqNum); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst((*stallingLoad)); + } + + if (result != MA_HIT && dcacheInterface->doEvents()) { + store_event->miss = true; + typename BackEnd::LdWritebackEvent *wb = NULL; + if (req->flags & LOCKED) { + wb = new typename BackEnd::LdWritebackEvent(inst, + be); + store_event->wbEvent = wb; + } + + DPRINTF(OzoneLSQ,"D-Cache Write Miss!\n"); + +// DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", +// inst->seqNum); + + be->addDcacheMiss(inst); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + // Increment stat here or something + + sq_it--; + } else { + DPRINTF(OzoneLSQ,"D-Cache: Write Hit on idx:%i !\n", + inst->sqIdx); + +// DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", +// inst->seqNum); + + if (req->flags & LOCKED) { + // Stx_C does not generate a system port + // transaction in the 21264, but that might be + // hard to accomplish in this model. + + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(inst, + be); + store_event->wbEvent = wb; + } + sq_it--; + } + } else { + panic("Must HAVE DCACHE!!!!!\n"); + } +*/ + } + + // Not sure this should set it to 0. + usedPorts = 0; + + assert(stores >= 0 && storesToWB >= 0); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::squash(const InstSeqNum &squashed_num) +{ + DPRINTF(OzoneLSQ, "Squashing until [sn:%lli]!" + "(Loads:%i Stores:%i)\n",squashed_num,loads,stores); + + + LQIt lq_it = loadQueue.begin(); + + while (loads != 0 && (*lq_it)->seqNum > squashed_num) { + assert(!loadQueue.empty()); + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Load Instruction PC %#x squashed, " + "[sn:%lli]\n", + (*lq_it)->readPC(), + (*lq_it)->seqNum); + + if (isStalled() && lq_it == stallingLoad) { + stalled = false; + stallingStoreIsn = 0; + stallingLoad = NULL; + } + + --loads; + + // Inefficient! + LQHashIt lq_hash_it = LQItHash.find((*lq_it)->lqIdx); + assert(lq_hash_it != LQItHash.end()); + LQItHash.erase(lq_hash_it); + LQIndices.push((*lq_it)->lqIdx); + loadQueue.erase(lq_it++); + } + + if (isLoadBlocked) { + if (squashed_num < blockedLoadSeqNum) { + isLoadBlocked = false; + loadBlockedHandled = false; + blockedLoadSeqNum = 0; + } + } + + SQIt sq_it = storeQueue.begin(); + + while (stores != 0 && (*sq_it).inst->seqNum > squashed_num) { + assert(!storeQueue.empty()); + + if ((*sq_it).canWB) { + break; + } + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Store Instruction PC %#x idx:%i squashed [sn:%lli]\n", + (*sq_it).inst->readPC(), (*sq_it).inst->sqIdx, + (*sq_it).inst->seqNum); + + // I don't think this can happen. It should have been cleared by the + // stalling load. + if (isStalled() && + (*sq_it).inst->seqNum == stallingStoreIsn) { + panic("Is stalled should have been cleared by stalling load!\n"); + stalled = false; + stallingStoreIsn = 0; + } + + SQHashIt sq_hash_it = SQItHash.find((*sq_it).inst->sqIdx); + assert(sq_hash_it != SQItHash.end()); + SQItHash.erase(sq_hash_it); + SQIndices.push((*sq_it).inst->sqIdx); + (*sq_it).inst = NULL; + (*sq_it).canWB = 0; + (*sq_it).req = NULL; + --stores; + storeQueue.erase(sq_it++); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::dumpInsts() +{ + cprintf("Load store queue: Dumping instructions.\n"); + cprintf("Load queue size: %i\n", loads); + cprintf("Load queue: "); + + LQIt lq_it = --(loadQueue.end()); + + while (lq_it != loadQueue.end() && (*lq_it)) { + cprintf("[sn:%lli] %#x ", (*lq_it)->seqNum, + (*lq_it)->readPC()); + + lq_it--; + } + + cprintf("\nStore queue size: %i\n", stores); + cprintf("Store queue: "); + + SQIt sq_it = --(storeQueue.end()); + + while (sq_it != storeQueue.end() && (*sq_it).inst) { + cprintf("[sn:%lli]\nPC:%#x\nSize:%i\nCommitted:%i\nCompleted:%i\ncanWB:%i\n", + (*sq_it).inst->seqNum, + (*sq_it).inst->readPC(), + (*sq_it).size, + (*sq_it).committed, + (*sq_it).completed, + (*sq_it).canWB); + + sq_it--; + } + + cprintf("\n"); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::storePostSend(Packet *pkt, DynInstPtr &inst) +{ + if (isStalled() && + inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load [sn:%lli]\n", + stallingStoreIsn, (*stallingLoad)->seqNum); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst((*stallingLoad)); + } + + if (!inst->isStoreConditional()) { + // The store is basically completed at this time. This + // only works so long as the checker doesn't try to + // verify the value in memory for stores. + inst->setCompleted(); +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->verify(inst); + } +#endif + } + + if (pkt->result != Packet::Success) { + DPRINTF(OzoneLSQ,"D-Cache Write Miss!\n"); + + DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", + inst->seqNum); + + //mshrSeqNums.push_back(storeQueue[storeWBIdx].inst->seqNum); + + //DPRINTF(OzoneLWLSQ, "Added MSHR. count = %i\n",mshrSeqNums.size()); + + // @todo: Increment stat here. + } else { + DPRINTF(OzoneLSQ,"D-Cache: Write Hit!\n"); + + DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", + inst->seqNum); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::writeback(DynInstPtr &inst, PacketPtr pkt) +{ + // Squashed instructions do not need to complete their access. + if (inst->isSquashed()) { + assert(!inst->isStore()); + return; + } + + if (!inst->isExecuted()) { + inst->setExecuted(); + + // Complete access to copy data to proper place. + inst->completeAcc(pkt); + } + + // Need to insert instruction into queue to commit + be->instToCommit(inst); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::completeStore(int store_idx) +{ + SQHashIt sq_hash_it = SQItHash.find(store_idx); + assert(sq_hash_it != SQItHash.end()); + SQIt sq_it = (*sq_hash_it).second; + + assert((*sq_it).inst); + (*sq_it).completed = true; + DynInstPtr inst = (*sq_it).inst; + + --storesToWB; + + if (isStalled() && + inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load [sn:%lli]\n", + stallingStoreIsn, (*stallingLoad)->seqNum); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst((*stallingLoad)); + } + + DPRINTF(OzoneLSQ, "Completing store idx:%i [sn:%lli], storesToWB:%i\n", + inst->sqIdx, inst->seqNum, storesToWB); + + assert(!storeQueue.empty()); + SQItHash.erase(sq_hash_it); + SQIndices.push(inst->sqIdx); + storeQueue.erase(sq_it); + --stores; + + inst->setCompleted(); +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->verify(inst); + } +#endif +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::recvRetry() +{ + panic("Unimplemented!"); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::switchOut() +{ + assert(storesToWB == 0); + switchedOut = true; + + // Clear the queue to free up resources + storeQueue.clear(); + loadQueue.clear(); + loads = stores = storesToWB = 0; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::takeOverFrom(ThreadContext *old_tc) +{ + // Clear out any old state. May be redundant if this is the first time + // the CPU is being used. + stalled = false; + isLoadBlocked = false; + loadBlockedHandled = false; + switchedOut = false; + + // Could do simple checks here to see if indices are on twice + while (!LQIndices.empty()) + LQIndices.pop(); + while (!SQIndices.empty()) + SQIndices.pop(); + + for (int i = 0; i < LQEntries * 2; i++) { + LQIndices.push(i); + SQIndices.push(i); + } + + usedPorts = 0; + + loadFaultInst = storeFaultInst = memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} diff --git a/src/cpu/ozone/null_predictor.hh b/src/cpu/ozone/null_predictor.hh new file mode 100644 index 000000000..a98c89d69 --- /dev/null +++ b/src/cpu/ozone/null_predictor.hh @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_NULL_PREDICTOR_HH__ +#define __CPU_OZONE_NULL_PREDICTOR_HH__ + +#include "arch/isa_traits.hh" +#include "cpu/inst_seq.hh" + +template <class Impl> +class NullPredictor +{ + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInstPtr DynInstPtr; + + NullPredictor(Params *p) { } + + struct BPredInfo { + BPredInfo() + : PC(0), nextPC(0) + { } + + BPredInfo(const Addr &pc, const Addr &next_pc) + : PC(pc), nextPC(next_pc) + { } + + Addr PC; + Addr nextPC; + }; + + BPredInfo lookup(Addr &PC) { return BPredInfo(PC, PC+4); } + + void undo(BPredInfo &bp_info) { return; } + + /** + * Predicts whether or not the instruction is a taken branch, and the + * target of the branch if it is taken. + * @param inst The branch instruction. + * @param PC The predicted PC is passed back through this parameter. + * @param tid The thread id. + * @return Returns if the branch is taken or not. + */ + bool predict(DynInstPtr &inst, Addr &PC, unsigned tid) + { return false; } + + /** + * Tells the branch predictor to commit any updates until the given + * sequence number. + * @param done_sn The sequence number to commit any older updates up until. + * @param tid The thread id. + */ + void update(const InstSeqNum &done_sn, unsigned tid) { } + + /** + * Squashes all outstanding updates until a given sequence number. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param tid The thread id. + */ + void squash(const InstSeqNum &squashed_sn, unsigned tid) { } + + /** + * Squashes all outstanding updates until a given sequence number, and + * corrects that sn's update with the proper address and taken/not taken. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param corr_target The correct branch target. + * @param actually_taken The correct branch direction. + * @param tid The thread id. + */ + void squash(const InstSeqNum &squashed_sn, const Addr &corr_target, + bool actually_taken, unsigned tid) + { } + +}; + +#endif // __CPU_OZONE_NULL_PREDICTOR_HH__ diff --git a/src/cpu/ozone/ozone_base_dyn_inst.cc b/src/cpu/ozone/ozone_base_dyn_inst.cc new file mode 100644 index 000000000..5a3a69dff --- /dev/null +++ b/src/cpu/ozone/ozone_base_dyn_inst.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/base_dyn_inst_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +// Explicit instantiation +template class BaseDynInst<OzoneImpl>; + +template <> +int +BaseDynInst<OzoneImpl>::instcount = 0; diff --git a/src/cpu/ozone/ozone_impl.hh b/src/cpu/ozone/ozone_impl.hh new file mode 100644 index 000000000..503675738 --- /dev/null +++ b/src/cpu/ozone/ozone_impl.hh @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_OZONE_IMPL_HH__ +#define __CPU_OZONE_OZONE_IMPL_HH__ + +#include "arch/alpha/isa_traits.hh" +#include "cpu/o3/bpred_unit.hh" +#include "cpu/ozone/front_end.hh" +#include "cpu/ozone/inst_queue.hh" +#include "cpu/ozone/lw_lsq.hh" +#include "cpu/ozone/lw_back_end.hh" +#include "cpu/ozone/null_predictor.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/simple_params.hh" + +template <class Impl> +class OzoneCPU; + +template <class Impl> +class OzoneDynInst; + +struct OzoneImpl { + typedef SimpleParams Params; + typedef OzoneCPU<OzoneImpl> OzoneCPU; + typedef OzoneCPU CPUType; + + // Would like to put these into their own area. +// typedef NullPredictor BranchPred; + typedef BPredUnit<OzoneImpl> BranchPred; + typedef FrontEnd<OzoneImpl> FrontEnd; + // Will need IQ, LSQ eventually + typedef LWBackEnd<OzoneImpl> BackEnd; + + typedef InstQueue<OzoneImpl> InstQueue; + typedef OzoneLWLSQ<OzoneImpl> LdstQueue; + + typedef OzoneDynInst<OzoneImpl> DynInst; + typedef RefCountingPtr<DynInst> DynInstPtr; + + typedef uint64_t IssueStruct; + + enum { + MaxThreads = 1 + }; +}; + +#endif // __CPU_OZONE_OZONE_IMPL_HH__ diff --git a/src/cpu/ozone/rename_table.cc b/src/cpu/ozone/rename_table.cc new file mode 100644 index 000000000..a44054b6e --- /dev/null +++ b/src/cpu/ozone/rename_table.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/rename_table_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +template class RenameTable<OzoneImpl>; +//template class RenameTable<SimpleImpl>; diff --git a/src/cpu/ozone/rename_table.hh b/src/cpu/ozone/rename_table.hh new file mode 100644 index 000000000..0b67d9635 --- /dev/null +++ b/src/cpu/ozone/rename_table.hh @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_RENAME_TABLE_HH__ +#define __CPU_OZONE_RENAME_TABLE_HH__ + +#include "arch/isa_traits.hh" + +/** Rename table that holds the rename of each architectural register to + * producing DynInst. Needs to support copying from one table to another. + */ + +template <class Impl> +class RenameTable { + public: + typedef typename Impl::DynInstPtr DynInstPtr; + + RenameTable(); + + void copyFrom(const RenameTable<Impl> &table_to_copy); + + DynInstPtr &operator [] (int index) + { return table[index]; } + + DynInstPtr table[TheISA::TotalNumRegs]; +}; + +#endif // __CPU_OZONE_RENAME_TABLE_HH__ diff --git a/src/cpu/ozone/rename_table_impl.hh b/src/cpu/ozone/rename_table_impl.hh new file mode 100644 index 000000000..67bab7337 --- /dev/null +++ b/src/cpu/ozone/rename_table_impl.hh @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include <cstdlib> // Not really sure what to include to get NULL +#include "cpu/ozone/rename_table.hh" + +template <class Impl> +RenameTable<Impl>::RenameTable() +{ + // Actually should set these to dummy dyn insts that have the initial value + // and force their values to be initialized. This keeps everything the + // same. + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + table[i] = NULL; + } +} + +template <class Impl> +void +RenameTable<Impl>::copyFrom(const RenameTable<Impl> &table_to_copy) +{ + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + table[i] = table_to_copy.table[i]; + } +} diff --git a/src/cpu/ozone/simple_base_dyn_inst.cc b/src/cpu/ozone/simple_base_dyn_inst.cc new file mode 100644 index 000000000..fdaeaf57e --- /dev/null +++ b/src/cpu/ozone/simple_base_dyn_inst.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/base_dyn_inst_impl.hh" +#include "cpu/ozone/simple_impl.hh" + +// Explicit instantiation +template class BaseDynInst<SimpleImpl>; + +template <> +int +BaseDynInst<SimpleImpl>::instcount = 0; diff --git a/src/cpu/ozone/simple_cpu_builder.cc b/src/cpu/ozone/simple_cpu_builder.cc new file mode 100644 index 000000000..baaf7c708 --- /dev/null +++ b/src/cpu/ozone/simple_cpu_builder.cc @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/cpu_impl.hh" +#include "cpu/ozone/simple_impl.hh" +#include "cpu/ozone/simple_params.hh" +#include "mem/cache/base_cache.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +template +class OzoneCPU<SimpleImpl>; + +class SimpleOzoneCPU : public OzoneCPU<SimpleImpl> +{ + public: + SimpleOzoneCPU(SimpleParams *p) + : OzoneCPU<SimpleImpl>(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// OzoneCPU Simulation Object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + + Param<int> clock; + Param<int> numThreads; + +#if FULL_SYSTEM +SimObjectParam<System *> system; +Param<int> cpu_id; +SimObjectParam<AlphaITB *> itb; +SimObjectParam<AlphaDTB *> dtb; +#else +SimObjectVectorParam<Process *> workload; +//SimObjectParam<PageTable *> page_table; +#endif // FULL_SYSTEM + +SimObjectParam<FunctionalMemory *> mem; + +SimObjectParam<BaseCPU *> checker; + +Param<Counter> max_insts_any_thread; +Param<Counter> max_insts_all_threads; +Param<Counter> max_loads_any_thread; +Param<Counter> max_loads_all_threads; + +SimObjectParam<BaseCache *> icache; +SimObjectParam<BaseCache *> dcache; + +Param<unsigned> cachePorts; +Param<unsigned> width; +Param<unsigned> frontEndWidth; +Param<unsigned> backEndWidth; +Param<unsigned> backEndSquashLatency; +Param<unsigned> backEndLatency; +Param<unsigned> maxInstBufferSize; +Param<unsigned> numPhysicalRegs; + +Param<unsigned> decodeToFetchDelay; +Param<unsigned> renameToFetchDelay; +Param<unsigned> iewToFetchDelay; +Param<unsigned> commitToFetchDelay; +Param<unsigned> fetchWidth; + +Param<unsigned> renameToDecodeDelay; +Param<unsigned> iewToDecodeDelay; +Param<unsigned> commitToDecodeDelay; +Param<unsigned> fetchToDecodeDelay; +Param<unsigned> decodeWidth; + +Param<unsigned> iewToRenameDelay; +Param<unsigned> commitToRenameDelay; +Param<unsigned> decodeToRenameDelay; +Param<unsigned> renameWidth; + +Param<unsigned> commitToIEWDelay; +Param<unsigned> renameToIEWDelay; +Param<unsigned> issueToExecuteDelay; +Param<unsigned> issueWidth; +Param<unsigned> executeWidth; +Param<unsigned> executeIntWidth; +Param<unsigned> executeFloatWidth; +Param<unsigned> executeBranchWidth; +Param<unsigned> executeMemoryWidth; + +Param<unsigned> iewToCommitDelay; +Param<unsigned> renameToROBDelay; +Param<unsigned> commitWidth; +Param<unsigned> squashWidth; + +Param<std::string> predType; +Param<unsigned> localPredictorSize; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; + +Param<unsigned> BTBEntries; +Param<unsigned> BTBTagSize; + +Param<unsigned> RASSize; + +Param<unsigned> LQEntries; +Param<unsigned> SQEntries; +Param<unsigned> LFSTSize; +Param<unsigned> SSITSize; + +Param<unsigned> numPhysIntRegs; +Param<unsigned> numPhysFloatRegs; +Param<unsigned> numIQEntries; +Param<unsigned> numROBEntries; + +Param<bool> decoupledFrontEnd; +Param<int> dispatchWidth; +Param<int> wbWidth; + +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + +Param<unsigned> instShiftAmt; + +Param<bool> defer_registration; + +Param<bool> function_trace; +Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(numThreads, "number of HW thread contexts"), + +#if FULL_SYSTEM + INIT_PARAM(system, "System object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(itb, "Instruction translation buffer"), + INIT_PARAM(dtb, "Data translation buffer"), +#else + INIT_PARAM(workload, "Processes to run"), +// INIT_PARAM(page_table, "Page table"), +#endif // FULL_SYSTEM + + INIT_PARAM_DFLT(mem, "Memory", NULL), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), + + INIT_PARAM_DFLT(max_insts_any_thread, + "Terminate when any thread reaches this inst count", + 0), + INIT_PARAM_DFLT(max_insts_all_threads, + "Terminate when all threads have reached" + "this inst count", + 0), + INIT_PARAM_DFLT(max_loads_any_thread, + "Terminate when any thread reaches this load count", + 0), + INIT_PARAM_DFLT(max_loads_all_threads, + "Terminate when all threads have reached this load" + "count", + 0), + + INIT_PARAM_DFLT(icache, "L1 instruction cache", NULL), + INIT_PARAM_DFLT(dcache, "L1 data cache", NULL), + + INIT_PARAM_DFLT(cachePorts, "Cache Ports", 200), + INIT_PARAM_DFLT(width, "Width", 1), + INIT_PARAM_DFLT(frontEndWidth, "Front end width", 1), + INIT_PARAM_DFLT(backEndWidth, "Back end width", 1), + INIT_PARAM_DFLT(backEndSquashLatency, "Back end squash latency", 1), + INIT_PARAM_DFLT(backEndLatency, "Back end latency", 1), + INIT_PARAM_DFLT(maxInstBufferSize, "Maximum instruction buffer size", 16), + INIT_PARAM(numPhysicalRegs, "Number of physical registers"), + + INIT_PARAM(decodeToFetchDelay, "Decode to fetch delay"), + INIT_PARAM(renameToFetchDelay, "Rename to fetch delay"), + INIT_PARAM(iewToFetchDelay, "Issue/Execute/Writeback to fetch" + "delay"), + INIT_PARAM(commitToFetchDelay, "Commit to fetch delay"), + INIT_PARAM(fetchWidth, "Fetch width"), + INIT_PARAM(renameToDecodeDelay, "Rename to decode delay"), + INIT_PARAM(iewToDecodeDelay, "Issue/Execute/Writeback to decode" + "delay"), + INIT_PARAM(commitToDecodeDelay, "Commit to decode delay"), + INIT_PARAM(fetchToDecodeDelay, "Fetch to decode delay"), + INIT_PARAM(decodeWidth, "Decode width"), + + INIT_PARAM(iewToRenameDelay, "Issue/Execute/Writeback to rename" + "delay"), + INIT_PARAM(commitToRenameDelay, "Commit to rename delay"), + INIT_PARAM(decodeToRenameDelay, "Decode to rename delay"), + INIT_PARAM(renameWidth, "Rename width"), + + INIT_PARAM(commitToIEWDelay, "Commit to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(renameToIEWDelay, "Rename to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(issueToExecuteDelay, "Issue to execute delay (internal" + "to the IEW stage)"), + INIT_PARAM(issueWidth, "Issue width"), + INIT_PARAM(executeWidth, "Execute width"), + INIT_PARAM(executeIntWidth, "Integer execute width"), + INIT_PARAM(executeFloatWidth, "Floating point execute width"), + INIT_PARAM(executeBranchWidth, "Branch execute width"), + INIT_PARAM(executeMemoryWidth, "Memory execute width"), + + INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " + "delay"), + INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), + INIT_PARAM(commitWidth, "Commit width"), + INIT_PARAM(squashWidth, "Squash width"), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + INIT_PARAM(localPredictorSize, "Size of local predictor"), + INIT_PARAM(localCtrBits, "Bits per counter"), + INIT_PARAM(localHistoryTableSize, "Size of local history table"), + INIT_PARAM(localHistoryBits, "Bits for the local history"), + INIT_PARAM(globalPredictorSize, "Size of global predictor"), + INIT_PARAM(globalCtrBits, "Bits per counter"), + INIT_PARAM(globalHistoryBits, "Bits of history"), + INIT_PARAM(choicePredictorSize, "Size of choice predictor"), + INIT_PARAM(choiceCtrBits, "Bits of choice counters"), + + INIT_PARAM(BTBEntries, "Number of BTB entries"), + INIT_PARAM(BTBTagSize, "Size of the BTB tags, in bits"), + + INIT_PARAM(RASSize, "RAS size"), + + INIT_PARAM(LQEntries, "Number of load queue entries"), + INIT_PARAM(SQEntries, "Number of store queue entries"), + INIT_PARAM(LFSTSize, "Last fetched store table size"), + INIT_PARAM(SSITSize, "Store set ID table size"), + + INIT_PARAM(numPhysIntRegs, "Number of physical integer registers"), + INIT_PARAM(numPhysFloatRegs, "Number of physical floating point " + "registers"), + INIT_PARAM(numIQEntries, "Number of instruction queue entries"), + INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + + INIT_PARAM_DFLT(decoupledFrontEnd, "Decoupled front end", true), + INIT_PARAM_DFLT(dispatchWidth, "Dispatch width", 0), + INIT_PARAM_DFLT(wbWidth, "Writeback width", 0), + + INIT_PARAM_DFLT(smtNumFetchingThreads, "SMT Number of Fetching Threads", 1), + INIT_PARAM_DFLT(smtFetchPolicy, "SMT Fetch Policy", "SingleThread"), + INIT_PARAM_DFLT(smtLSQPolicy, "SMT LSQ Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtLSQThreshold,"SMT LSQ Threshold", 100), + INIT_PARAM_DFLT(smtIQPolicy, "SMT IQ Policy", "Partitioned"), + INIT_PARAM_DFLT(smtIQThreshold, "SMT IQ Threshold", 100), + INIT_PARAM_DFLT(smtROBPolicy, "SMT ROB Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtROBThreshold,"SMT ROB Threshold", 100), + INIT_PARAM_DFLT(smtCommitPolicy,"SMT Commit Fetch Policy", "RoundRobin"), + + INIT_PARAM(instShiftAmt, "Number of bits to shift instructions by"), + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + +CREATE_SIM_OBJECT(SimpleOzoneCPU) +{ + SimpleOzoneCPU *cpu; + +#if FULL_SYSTEM + // Full-system only supports a single thread for the moment. + int actual_num_threads = 1; +#else + // In non-full-system mode, we infer the number of threads from + // the workload if it's not explicitly specified. + int actual_num_threads = + numThreads.isValid() ? numThreads : workload.size(); + + if (workload.size() == 0) { + fatal("Must specify at least one workload!"); + } + +#endif + + SimpleParams *params = new SimpleParams; + + params->clock = clock; + + params->name = getInstanceName(); + params->numberOfThreads = actual_num_threads; + +#if FULL_SYSTEM + params->system = system; + params->cpu_id = cpu_id; + params->itb = itb; + params->dtb = dtb; +#else + params->workload = workload; +// params->pTable = page_table; +#endif // FULL_SYSTEM + + params->mem = mem; + params->checker = checker; + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; + + // + // Caches + // + params->icacheInterface = icache ? icache->getInterface() : NULL; + params->dcacheInterface = dcache ? dcache->getInterface() : NULL; + params->cachePorts = cachePorts; + + params->width = width; + params->frontEndWidth = frontEndWidth; + params->backEndWidth = backEndWidth; + params->backEndSquashLatency = backEndSquashLatency; + params->backEndLatency = backEndLatency; + params->maxInstBufferSize = maxInstBufferSize; + params->numPhysicalRegs = numPhysIntRegs + numPhysFloatRegs; + + params->decodeToFetchDelay = decodeToFetchDelay; + params->renameToFetchDelay = renameToFetchDelay; + params->iewToFetchDelay = iewToFetchDelay; + params->commitToFetchDelay = commitToFetchDelay; + params->fetchWidth = fetchWidth; + + params->renameToDecodeDelay = renameToDecodeDelay; + params->iewToDecodeDelay = iewToDecodeDelay; + params->commitToDecodeDelay = commitToDecodeDelay; + params->fetchToDecodeDelay = fetchToDecodeDelay; + params->decodeWidth = decodeWidth; + + params->iewToRenameDelay = iewToRenameDelay; + params->commitToRenameDelay = commitToRenameDelay; + params->decodeToRenameDelay = decodeToRenameDelay; + params->renameWidth = renameWidth; + + params->commitToIEWDelay = commitToIEWDelay; + params->renameToIEWDelay = renameToIEWDelay; + params->issueToExecuteDelay = issueToExecuteDelay; + params->issueWidth = issueWidth; + params->executeWidth = executeWidth; + params->executeIntWidth = executeIntWidth; + params->executeFloatWidth = executeFloatWidth; + params->executeBranchWidth = executeBranchWidth; + params->executeMemoryWidth = executeMemoryWidth; + + params->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + + params->predType = predType; + params->localPredictorSize = localPredictorSize; + params->localCtrBits = localCtrBits; + params->localHistoryTableSize = localHistoryTableSize; + params->localHistoryBits = localHistoryBits; + params->globalPredictorSize = globalPredictorSize; + params->globalCtrBits = globalCtrBits; + params->globalHistoryBits = globalHistoryBits; + params->choicePredictorSize = choicePredictorSize; + params->choiceCtrBits = choiceCtrBits; + + params->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->decoupledFrontEnd = decoupledFrontEnd; + params->dispatchWidth = dispatchWidth; + params->wbWidth = wbWidth; + + params->smtNumFetchingThreads = smtNumFetchingThreads; + params->smtFetchPolicy = smtFetchPolicy; + params->smtIQPolicy = smtIQPolicy; + params->smtLSQPolicy = smtLSQPolicy; + params->smtLSQThreshold = smtLSQThreshold; + params->smtROBPolicy = smtROBPolicy; + params->smtROBThreshold = smtROBThreshold; + params->smtCommitPolicy = smtCommitPolicy; + + params->instShiftAmt = 2; + + params->deferRegistration = defer_registration; + + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + + cpu = new SimpleOzoneCPU(params); + + return cpu; +} + +REGISTER_SIM_OBJECT("SimpleOzoneCPU", SimpleOzoneCPU) + diff --git a/src/cpu/ozone/simple_impl.hh b/src/cpu/ozone/simple_impl.hh new file mode 100644 index 000000000..3199d8d8a --- /dev/null +++ b/src/cpu/ozone/simple_impl.hh @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_SIMPLE_IMPL_HH__ +#define __CPU_OZONE_SIMPLE_IMPL_HH__ + +#include "arch/isa_traits.hh" +#include "cpu/o3/bpred_unit.hh" +#include "cpu/ozone/cpu.hh" +#include "cpu/ozone/front_end.hh" +#include "cpu/ozone/inorder_back_end.hh" +#include "cpu/ozone/null_predictor.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/simple_params.hh" + +//template <class Impl> +//class OzoneCPU; + +template <class Impl> +class OzoneDynInst; + +struct SimpleImpl { + typedef SimpleParams Params; + typedef OzoneCPU<SimpleImpl> OzoneCPU; + typedef OzoneCPU FullCPU; + + // Would like to put these into their own area. +// typedef NullPredictor BranchPred; + typedef BPredUnit<SimpleImpl> BranchPred; + typedef FrontEnd<SimpleImpl> FrontEnd; + // Will need IQ, LSQ eventually + typedef InorderBackEnd<SimpleImpl> BackEnd; + + typedef OzoneDynInst<SimpleImpl> DynInst; + typedef RefCountingPtr<DynInst> DynInstPtr; + + typedef uint64_t IssueStruct; + + enum { + MaxThreads = 1 + }; +}; + +#endif // __CPU_OZONE_SIMPLE_IMPL_HH__ diff --git a/src/cpu/ozone/simple_params.hh b/src/cpu/ozone/simple_params.hh new file mode 100644 index 000000000..11cee716f --- /dev/null +++ b/src/cpu/ozone/simple_params.hh @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_SIMPLE_PARAMS_HH__ +#define __CPU_OZONE_SIMPLE_PARAMS_HH__ + +#include "cpu/ozone/cpu.hh" + +//Forward declarations +class AlphaDTB; +class AlphaITB; +class FUPool; +class MemObject; +class PageTable; +class Process; +class System; + +/** + * This file defines the parameters that will be used for the OzoneCPU. + * This must be defined externally so that the Impl can have a params class + * defined that it can pass to all of the individual stages. + */ + +class SimpleParams : public BaseCPU::Params +{ + public: + +#if FULL_SYSTEM + AlphaITB *itb; AlphaDTB *dtb; +#else + std::vector<Process *> workload; +#endif // FULL_SYSTEM + + //Page Table + PageTable *pTable; + + MemObject *mem; + + // + // Caches + // +// MemInterface *icacheInterface; +// MemInterface *dcacheInterface; + + unsigned cachePorts; + unsigned width; + unsigned frontEndWidth; + unsigned backEndWidth; + unsigned backEndSquashLatency; + unsigned backEndLatency; + unsigned maxInstBufferSize; + unsigned numPhysicalRegs; + unsigned maxOutstandingMemOps; + // + // Fetch + // + unsigned decodeToFetchDelay; + unsigned renameToFetchDelay; + unsigned iewToFetchDelay; + unsigned commitToFetchDelay; + unsigned fetchWidth; + + // + // Decode + // + unsigned renameToDecodeDelay; + unsigned iewToDecodeDelay; + unsigned commitToDecodeDelay; + unsigned fetchToDecodeDelay; + unsigned decodeWidth; + + // + // Rename + // + unsigned iewToRenameDelay; + unsigned commitToRenameDelay; + unsigned decodeToRenameDelay; + unsigned renameWidth; + + // + // IEW + // + unsigned commitToIEWDelay; + unsigned renameToIEWDelay; + unsigned issueToExecuteDelay; + unsigned issueWidth; + unsigned executeWidth; + unsigned executeIntWidth; + unsigned executeFloatWidth; + unsigned executeBranchWidth; + unsigned executeMemoryWidth; + FUPool *fuPool; + + // + // Commit + // + unsigned iewToCommitDelay; + unsigned renameToROBDelay; + unsigned commitWidth; + unsigned squashWidth; + + // + // Branch predictor (BP & BTB) + // + std::string predType; + unsigned localPredictorSize; + unsigned localCtrBits; + unsigned localHistoryTableSize; + unsigned localHistoryBits; + unsigned globalPredictorSize; + unsigned globalCtrBits; + unsigned globalHistoryBits; + unsigned choicePredictorSize; + unsigned choiceCtrBits; + + unsigned BTBEntries; + unsigned BTBTagSize; + + unsigned RASSize; + + // + // Load store queue + // + unsigned LQEntries; + unsigned SQEntries; + + // + // Memory dependence + // + unsigned SSITSize; + unsigned LFSTSize; + + // + // Miscellaneous + // + unsigned numPhysIntRegs; + unsigned numPhysFloatRegs; + unsigned numIQEntries; + unsigned numROBEntries; + + bool decoupledFrontEnd; + int dispatchWidth; + int wbWidth; + + //SMT Parameters + unsigned smtNumFetchingThreads; + + std::string smtFetchPolicy; + + std::string smtIQPolicy; + unsigned smtIQThreshold; + + std::string smtLSQPolicy; + unsigned smtLSQThreshold; + + std::string smtCommitPolicy; + + std::string smtROBPolicy; + unsigned smtROBThreshold; + + // Probably can get this from somewhere. + unsigned instShiftAmt; +}; + +#endif // __CPU_OZONE_SIMPLE_PARAMS_HH__ diff --git a/src/cpu/ozone/thread_state.hh b/src/cpu/ozone/thread_state.hh new file mode 100644 index 000000000..ef4b1429d --- /dev/null +++ b/src/cpu/ozone/thread_state.hh @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_THREAD_STATE_HH__ +#define __CPU_OZONE_THREAD_STATE_HH__ + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "cpu/thread_context.hh" +#include "cpu/thread_state.hh" +#include "sim/process.hh" + +class Event; +//class Process; + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +#else +class Process; +class FunctionalMemory; +#endif + +// Maybe this ozone thread state should only really have committed state? +// I need to think about why I'm using this and what it's useful for. Clearly +// has benefits for SMT; basically serves same use as SimpleThread. +// Makes the ExecContext proxy easier. Gives organization/central access point +// to state of a thread that can be accessed normally (i.e. not in-flight +// stuff within a OoO processor). Does this need an TC proxy within it? +template <class Impl> +struct OzoneThreadState : public ThreadState { + typedef typename ThreadContext::Status Status; + typedef typename Impl::CPUType CPUType; + typedef TheISA::MiscReg MiscReg; + +#if FULL_SYSTEM + OzoneThreadState(CPUType *_cpu, int _thread_num) + : ThreadState(-1, _thread_num), + intrflag(0), inSyscall(0), trapPending(0) + { + miscRegFile.clear(); + } +#else + OzoneThreadState(CPUType *_cpu, int _thread_num, Process *_process, + int _asid, MemObject *mem) + : ThreadState(-1, _thread_num, _process, _asid, mem), + cpu(_cpu), inSyscall(0), trapPending(0) + { + miscRegFile.clear(); + } +#endif + + RenameTable<Impl> renameTable; + + Addr PC; + + Addr nextPC; + + TheISA::MiscRegFile miscRegFile; + + int intrflag; + + typename Impl::CPUType *cpu; + + bool inSyscall; + + bool trapPending; + + ThreadContext *tc; + + ThreadContext *getTC() { return tc; } + + MiscReg readMiscReg(int misc_reg) + { + return miscRegFile.readReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return miscRegFile.readRegWithEffect(misc_reg, fault, tc); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + return miscRegFile.setReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + return miscRegFile.setRegWithEffect(misc_reg, val, tc); + } + + uint64_t readPC() + { return PC; } + + void setPC(uint64_t val) + { PC = val; } + + uint64_t readNextPC() + { return nextPC; } + + void setNextPC(uint64_t val) + { nextPC = val; } +}; + +#endif // __CPU_OZONE_THREAD_STATE_HH__ diff --git a/src/cpu/pc_event.cc b/src/cpu/pc_event.cc index 050bf1a88..fca357fe3 100644 --- a/src/cpu/pc_event.cc +++ b/src/cpu/pc_event.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <algorithm> @@ -34,7 +37,7 @@ #include "base/trace.hh" #include "config/full_system.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/pc_event.hh" #include "sim/debug.hh" #include "sim/root.hh" @@ -78,9 +81,9 @@ PCEventQueue::schedule(PCEvent *event) } bool -PCEventQueue::doService(ExecContext *xc) +PCEventQueue::doService(ThreadContext *tc) { - Addr pc = xc->readPC() & ~0x3; + Addr pc = tc->readPC() & ~0x3; int serviced = 0; range_t range = equal_range(pc); for (iterator i = range.first; i != range.second; ++i) { @@ -88,13 +91,13 @@ PCEventQueue::doService(ExecContext *xc) // another event. This for example, prevents two invocations // of the SkipFuncEvent. Maybe we should have separate PC // event queues for each processor? - if (pc != (xc->readPC() & ~0x3)) + if (pc != (tc->readPC() & ~0x3)) continue; DPRINTF(PCEvent, "PC based event serviced at %#x: %s\n", (*i)->pc(), (*i)->descr()); - (*i)->process(xc); + (*i)->process(tc); ++serviced; } @@ -125,9 +128,9 @@ BreakPCEvent::BreakPCEvent(PCEventQueue *q, const std::string &desc, Addr addr, } void -BreakPCEvent::process(ExecContext *xc) +BreakPCEvent::process(ThreadContext *tc) { - StringWrap name(xc->getCpuPtr()->name() + ".break_event"); + StringWrap name(tc->getCpuPtr()->name() + ".break_event"); DPRINTFN("break event %s triggered\n", descr()); debug_break(); if (remove) diff --git a/src/cpu/pc_event.hh b/src/cpu/pc_event.hh index 32b7f3ef5..6b048b2c2 100644 --- a/src/cpu/pc_event.hh +++ b/src/cpu/pc_event.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __PC_EVENT_HH__ @@ -33,7 +36,7 @@ #include "base/misc.hh" -class ExecContext; +class ThreadContext; class PCEventQueue; class PCEvent @@ -55,7 +58,7 @@ class PCEvent Addr pc() const { return evpc; } bool remove(); - virtual void process(ExecContext *xc) = 0; + virtual void process(ThreadContext *tc) = 0; }; class PCEventQueue @@ -87,7 +90,7 @@ class PCEventQueue protected: map_t pc_map; - bool doService(ExecContext *xc); + bool doService(ThreadContext *tc); public: PCEventQueue(); @@ -95,12 +98,12 @@ class PCEventQueue bool remove(PCEvent *event); bool schedule(PCEvent *event); - bool service(ExecContext *xc) + bool service(ThreadContext *tc) { if (pc_map.empty()) return false; - return doService(xc); + return doService(tc); } range_t equal_range(Addr pc); @@ -134,7 +137,7 @@ class BreakPCEvent : public PCEvent public: BreakPCEvent(PCEventQueue *q, const std::string &desc, Addr addr, bool del = false); - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; #endif // __PC_EVENT_HH__ diff --git a/src/cpu/profile.cc b/src/cpu/profile.cc index fe3458b61..4f04615e9 100644 --- a/src/cpu/profile.cc +++ b/src/cpu/profile.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <string> @@ -34,7 +36,7 @@ #include "base/trace.hh" #include "base/loader/symtab.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/profile.hh" using namespace std; @@ -118,7 +120,7 @@ FunctionProfile::clear() } void -FunctionProfile::dump(ExecContext *xc, ostream &os) const +FunctionProfile::dump(ThreadContext *tc, ostream &os) const { ccprintf(os, ">>>PC data\n"); map<Addr, Counter>::const_iterator i, end = pc_count.end(); diff --git a/src/cpu/profile.hh b/src/cpu/profile.hh index d55c9eec9..7f9625241 100644 --- a/src/cpu/profile.hh +++ b/src/cpu/profile.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __CPU_PROFILE_HH__ @@ -35,7 +37,7 @@ #include "sim/host.hh" #include "arch/stacktrace.hh" -class ExecContext; +class ThreadContext; class ProfileNode { @@ -70,17 +72,17 @@ class FunctionProfile FunctionProfile(const SymbolTable *symtab); ~FunctionProfile(); - ProfileNode *consume(ExecContext *xc, StaticInstPtr inst); + ProfileNode *consume(ThreadContext *tc, StaticInstPtr inst); ProfileNode *consume(const std::vector<Addr> &stack); void clear(); - void dump(ExecContext *xc, std::ostream &out) const; + void dump(ThreadContext *tc, std::ostream &out) const; void sample(ProfileNode *node, Addr pc); }; inline ProfileNode * -FunctionProfile::consume(ExecContext *xc, StaticInstPtr inst) +FunctionProfile::consume(ThreadContext *tc, StaticInstPtr inst) { - if (!trace.trace(xc, inst)) + if (!trace.trace(tc, inst)) return NULL; trace.dprintf(); return consume(trace.getstack()); diff --git a/src/cpu/quiesce_event.cc b/src/cpu/quiesce_event.cc new file mode 100644 index 000000000..8dd20db02 --- /dev/null +++ b/src/cpu/quiesce_event.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "cpu/thread_context.hh" +#include "cpu/quiesce_event.hh" + +EndQuiesceEvent::EndQuiesceEvent(ThreadContext *_tc) + : Event(&mainEventQueue), tc(_tc) +{ +} + +void +EndQuiesceEvent::process() +{ + tc->activate(); +} + +const char* +EndQuiesceEvent::description() +{ + return "End Quiesce Event."; +} diff --git a/src/cpu/quiesce_event.hh b/src/cpu/quiesce_event.hh new file mode 100644 index 000000000..3de40f97e --- /dev/null +++ b/src/cpu/quiesce_event.hh @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_QUIESCE_EVENT_HH__ +#define __CPU_QUIESCE_EVENT_HH__ + +#include "sim/eventq.hh" + +class ThreadContext; + +/** Event for timing out quiesce instruction */ +struct EndQuiesceEvent : public Event +{ + /** A pointer to the thread context that is quiesced */ + ThreadContext *tc; + + EndQuiesceEvent(ThreadContext *_tc); + + /** Event process to occur at interrupt*/ + virtual void process(); + + /** Event description */ + virtual const char *description(); +}; + +#endif // __CPU_QUIESCE_EVENT_HH__ diff --git a/src/cpu/simple/atomic.cc b/src/cpu/simple/atomic.cc index 3cad6e43f..b7202cbbb 100644 --- a/src/cpu/simple/atomic.cc +++ b/src/cpu/simple/atomic.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #include "arch/utility.hh" @@ -68,11 +70,11 @@ AtomicSimpleCPU::init() BaseCPU::init(); #if FULL_SYSTEM - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; // initialize CPU, including PC - TheISA::initCPU(xc, xc->readCpuId()); + TheISA::initCPU(tc, tc->readCpuId()); } #endif } @@ -106,11 +108,10 @@ AtomicSimpleCPU::CpuPort::recvStatusChange(Status status) panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!"); } -Packet * +void AtomicSimpleCPU::CpuPort::recvRetry() { panic("AtomicSimpleCPU doesn't expect recvRetry callback!"); - return NULL; } @@ -121,26 +122,20 @@ AtomicSimpleCPU::AtomicSimpleCPU(Params *p) { _status = Idle; - ifetch_req = new Request(true); - ifetch_req->setAsid(0); - // @todo fix me and get the real cpu iD!!! - ifetch_req->setCpuNum(0); - ifetch_req->setSize(sizeof(MachInst)); + // @todo fix me and get the real cpu id & thread number!!! + ifetch_req = new Request(); + ifetch_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast); ifetch_pkt->dataStatic(&inst); - data_read_req = new Request(true); - // @todo fix me and get the real cpu iD!!! - data_read_req->setCpuNum(0); - data_read_req->setAsid(0); + data_read_req = new Request(); + data_read_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE data_read_pkt = new Packet(data_read_req, Packet::ReadReq, Packet::Broadcast); data_read_pkt->dataStatic(&dataReg); - data_write_req = new Request(true); - // @todo fix me and get the real cpu iD!!! - data_write_req->setCpuNum(0); - data_write_req->setAsid(0); + data_write_req = new Request(); + data_write_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE data_write_pkt = new Packet(data_write_req, Packet::WriteReq, Packet::Broadcast); } @@ -153,8 +148,8 @@ AtomicSimpleCPU::~AtomicSimpleCPU() void AtomicSimpleCPU::serialize(ostream &os) { - BaseSimpleCPU::serialize(os); SERIALIZE_ENUM(_status); + BaseSimpleCPU::serialize(os); nameOut(os, csprintf("%s.tickEvent", name())); tickEvent.serialize(os); } @@ -162,21 +157,18 @@ AtomicSimpleCPU::serialize(ostream &os) void AtomicSimpleCPU::unserialize(Checkpoint *cp, const string §ion) { - BaseSimpleCPU::unserialize(cp, section); UNSERIALIZE_ENUM(_status); + BaseSimpleCPU::unserialize(cp, section); tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); } void -AtomicSimpleCPU::switchOut(Sampler *s) +AtomicSimpleCPU::switchOut() { - sampler = s; - if (status() == Running) { - _status = SwitchedOut; + assert(status() == Running || status() == Idle); + _status = SwitchedOut; - tickEvent.squash(); - } - sampler->signalSwitched(); + tickEvent.squash(); } @@ -187,11 +179,11 @@ AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU) assert(!tickEvent.scheduled()); - // if any of this CPU's ExecContexts are active, mark the CPU as + // if any of this CPU's ThreadContexts are active, mark the CPU as // running and schedule its tick event. - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; - if (xc->status() == ExecContext::Active && _status != Running) { + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && _status != Running) { _status = Running; tickEvent.schedule(curTick); break; @@ -204,7 +196,7 @@ void AtomicSimpleCPU::activateContext(int thread_num, int delay) { assert(thread_num == 0); - assert(cpuXC); + assert(thread); assert(_status == Idle); assert(!tickEvent.scheduled()); @@ -219,7 +211,7 @@ void AtomicSimpleCPU::suspendContext(int thread_num) { assert(thread_num == 0); - assert(cpuXC); + assert(thread); assert(_status == Running); @@ -237,24 +229,20 @@ template <class T> Fault AtomicSimpleCPU::read(Addr addr, T &data, unsigned flags) { - data_read_req->setVaddr(addr); - data_read_req->setSize(sizeof(T)); - data_read_req->setFlags(flags); - data_read_req->setTime(curTick); + data_read_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); if (traceData) { traceData->setAddr(addr); } // translate to physical address - Fault fault = cpuXC->translateDataReadReq(data_read_req); + Fault fault = thread->translateDataReadReq(data_read_req); // Now do the access. if (fault == NoFault) { - data_read_pkt->reset(); data_read_pkt->reinitFromRequest(); - dcache_complete = dcachePort.sendAtomic(data_read_pkt); + dcache_latency = dcachePort.sendAtomic(data_read_pkt); dcache_access = true; assert(data_read_pkt->result == Packet::Success); @@ -316,26 +304,22 @@ template <class T> Fault AtomicSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) { - data_write_req->setVaddr(addr); - data_write_req->setTime(curTick); - data_write_req->setSize(sizeof(T)); - data_write_req->setFlags(flags); + data_write_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); if (traceData) { traceData->setAddr(addr); } // translate to physical address - Fault fault = cpuXC->translateDataWriteReq(data_write_req); + Fault fault = thread->translateDataWriteReq(data_write_req); // Now do the access. if (fault == NoFault) { - data_write_pkt->reset(); data = htog(data); - data_write_pkt->dataStatic(&data); data_write_pkt->reinitFromRequest(); + data_write_pkt->dataStatic(&data); - dcache_complete = dcachePort.sendAtomic(data_write_pkt); + dcache_latency = dcachePort.sendAtomic(data_write_pkt); dcache_access = true; assert(data_write_pkt->result == Packet::Success); @@ -411,12 +395,12 @@ AtomicSimpleCPU::tick() checkForInterrupts(); - ifetch_req->resetMin(); - ifetch_pkt->reset(); - Fault fault = setupFetchPacket(ifetch_pkt); + Fault fault = setupFetchRequest(ifetch_req); if (fault == NoFault) { - Tick icache_complete = icachePort.sendAtomic(ifetch_pkt); + ifetch_pkt->reinitFromRequest(); + + Tick icache_latency = icachePort.sendAtomic(ifetch_pkt); // ifetch_req is initialized to read the instruction directly // into the CPU object's inst field. @@ -426,15 +410,14 @@ AtomicSimpleCPU::tick() postExecute(); if (simulate_stalls) { - // This calculation assumes that the icache and dcache - // access latencies are always a multiple of the CPU's - // cycle time. If not, the next tick event may get - // scheduled at a non-integer multiple of the CPU - // cycle time. - Tick icache_stall = icache_complete - curTick - cycles(1); + Tick icache_stall = icache_latency - cycles(1); Tick dcache_stall = - dcache_access ? dcache_complete - curTick - cycles(1) : 0; - latency += icache_stall + dcache_stall; + dcache_access ? dcache_latency - cycles(1) : 0; + Tick stall_cycles = (icache_stall + dcache_stall) / cycles(1); + if (cycles(stall_cycles) < (icache_stall + dcache_stall)) + latency += cycles(stall_cycles+1); + else + latency += cycles(stall_cycles); } } diff --git a/src/cpu/simple/atomic.hh b/src/cpu/simple/atomic.hh index ab3a3e8ef..951a8da06 100644 --- a/src/cpu/simple/atomic.hh +++ b/src/cpu/simple/atomic.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __CPU_SIMPLE_ATOMIC_HH__ @@ -98,7 +100,7 @@ class AtomicSimpleCPU : public BaseSimpleCPU virtual void recvStatusChange(Status status); - virtual Packet *recvRetry(); + virtual void recvRetry(); virtual void getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) @@ -116,14 +118,14 @@ class AtomicSimpleCPU : public BaseSimpleCPU Packet *data_write_pkt; bool dcache_access; - Tick dcache_complete; + Tick dcache_latency; public: virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); - void switchOut(Sampler *s); + void switchOut(); void takeOverFrom(BaseCPU *oldCPU); virtual void activateContext(int thread_num, int delay); diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc index 18f170449..db5dd2acf 100644 --- a/src/cpu/simple/base.cc +++ b/src/cpu/simple/base.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Korey Sewell */ #include "arch/utility.hh" @@ -36,18 +39,18 @@ #include "base/stats/events.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" -#include "cpu/exec_context.hh" #include "cpu/exetrace.hh" #include "cpu/profile.hh" #include "cpu/sampler/sampler.hh" #include "cpu/simple/base.hh" +#include "cpu/simple_thread.hh" #include "cpu/smt.hh" #include "cpu/static_inst.hh" +#include "cpu/thread_context.hh" #include "kern/kernel_stats.hh" #include "mem/packet_impl.hh" -#include "sim/byteswap.hh" #include "sim/builder.hh" +#include "sim/byteswap.hh" #include "sim/debug.hh" #include "sim/host.hh" #include "sim/sim_events.hh" @@ -68,16 +71,18 @@ using namespace std; using namespace TheISA; BaseSimpleCPU::BaseSimpleCPU(Params *p) - : BaseCPU(p), mem(p->mem), cpuXC(NULL) + : BaseCPU(p), mem(p->mem), thread(NULL) { #if FULL_SYSTEM - cpuXC = new CPUExecContext(this, 0, p->system, p->itb, p->dtb); + thread = new SimpleThread(this, 0, p->system, p->itb, p->dtb); #else - cpuXC = new CPUExecContext(this, /* thread_num */ 0, p->process, + thread = new SimpleThread(this, /* thread_num */ 0, p->process, /* asid */ 0, mem); #endif // !FULL_SYSTEM - xcProxy = cpuXC->getProxy(); + thread->setStatus(ThreadContext::Suspended); + + tc = thread->getTC(); numInst = 0; startNumInst = 0; @@ -86,7 +91,7 @@ BaseSimpleCPU::BaseSimpleCPU(Params *p) lastIcacheStall = 0; lastDcacheStall = 0; - execContexts.push_back(xcProxy); + threadContexts.push_back(tc); } BaseSimpleCPU::~BaseSimpleCPU() @@ -176,7 +181,7 @@ BaseSimpleCPU::serialize(ostream &os) BaseCPU::serialize(os); SERIALIZE_SCALAR(inst); nameOut(os, csprintf("%s.xc", name())); - cpuXC->serialize(os); + thread->serialize(os); } void @@ -184,7 +189,7 @@ BaseSimpleCPU::unserialize(Checkpoint *cp, const string §ion) { BaseCPU::unserialize(cp, section); UNSERIALIZE_SCALAR(inst); - cpuXC->unserialize(cp, csprintf("%s.xc", section)); + thread->unserialize(cp, csprintf("%s.xc", section)); } void @@ -213,16 +218,16 @@ BaseSimpleCPU::copySrcTranslate(Addr src) memReq->reset(src & ~(blk_size - 1), blk_size); // translate to physical address - Fault fault = cpuXC->translateDataReadReq(req); + Fault fault = thread->translateDataReadReq(req); if (fault == NoFault) { - cpuXC->copySrcAddr = src; - cpuXC->copySrcPhysAddr = memReq->paddr + offset; + thread->copySrcAddr = src; + thread->copySrcPhysAddr = memReq->paddr + offset; } else { assert(!fault->isAlignmentFault()); - cpuXC->copySrcAddr = 0; - cpuXC->copySrcPhysAddr = 0; + thread->copySrcAddr = 0; + thread->copySrcPhysAddr = 0; } return fault; #else @@ -239,7 +244,7 @@ BaseSimpleCPU::copy(Addr dest) // Only support block sizes of 64 atm. assert(blk_size == 64); uint8_t data[blk_size]; - //assert(cpuXC->copySrcAddr); + //assert(thread->copySrcAddr); int offset = dest & (blk_size - 1); // Make sure block doesn't span page @@ -252,19 +257,19 @@ BaseSimpleCPU::copy(Addr dest) memReq->reset(dest & ~(blk_size -1), blk_size); // translate to physical address - Fault fault = cpuXC->translateDataWriteReq(req); + Fault fault = thread->translateDataWriteReq(req); if (fault == NoFault) { Addr dest_addr = memReq->paddr + offset; // Need to read straight from memory since we have more than 8 bytes. - memReq->paddr = cpuXC->copySrcPhysAddr; - cpuXC->mem->read(memReq, data); + memReq->paddr = thread->copySrcPhysAddr; + thread->mem->read(memReq, data); memReq->paddr = dest_addr; - cpuXC->mem->write(memReq, data); + thread->mem->write(memReq, data); if (dcacheInterface) { memReq->cmd = Copy; memReq->completionEvent = NULL; - memReq->paddr = cpuXC->copySrcPhysAddr; + memReq->paddr = thread->copySrcPhysAddr; memReq->dest = dest_addr; memReq->size = 64; memReq->time = curTick; @@ -286,7 +291,7 @@ BaseSimpleCPU::copy(Addr dest) Addr BaseSimpleCPU::dbg_vtophys(Addr addr) { - return vtophys(xcProxy, addr); + return vtophys(tc, addr); } #endif // FULL_SYSTEM @@ -296,9 +301,9 @@ BaseSimpleCPU::post_interrupt(int int_num, int index) { BaseCPU::post_interrupt(int_num, index); - if (cpuXC->status() == ExecContext::Suspended) { + if (thread->status() == ThreadContext::Suspended) { DPRINTF(IPI,"Suspended Processor awoke\n"); - cpuXC->activate(); + thread->activate(); } } #endif // FULL_SYSTEM @@ -307,15 +312,15 @@ void BaseSimpleCPU::checkForInterrupts() { #if FULL_SYSTEM - if (checkInterrupts && check_interrupts() && !cpuXC->inPalMode()) { + if (checkInterrupts && check_interrupts() && !thread->inPalMode()) { int ipl = 0; int summary = 0; checkInterrupts = false; - if (cpuXC->readMiscReg(IPR_SIRR)) { + if (thread->readMiscReg(IPR_SIRR)) { for (int i = INTLEVEL_SOFTWARE_MIN; i < INTLEVEL_SOFTWARE_MAX; i++) { - if (cpuXC->readMiscReg(IPR_SIRR) & (ULL(1) << i)) { + if (thread->readMiscReg(IPR_SIRR) & (ULL(1) << i)) { // See table 4-19 of 21164 hardware reference ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; summary |= (ULL(1) << i); @@ -323,7 +328,7 @@ BaseSimpleCPU::checkForInterrupts() } } - uint64_t interrupts = cpuXC->cpu->intr_status(); + uint64_t interrupts = thread->cpu->intr_status(); for (int i = INTLEVEL_EXTERNAL_MIN; i < INTLEVEL_EXTERNAL_MAX; i++) { if (interrupts & (ULL(1) << i)) { @@ -333,17 +338,17 @@ BaseSimpleCPU::checkForInterrupts() } } - if (cpuXC->readMiscReg(IPR_ASTRR)) + if (thread->readMiscReg(IPR_ASTRR)) panic("asynchronous traps not implemented\n"); - if (ipl && ipl > cpuXC->readMiscReg(IPR_IPLR)) { - cpuXC->setMiscReg(IPR_ISR, summary); - cpuXC->setMiscReg(IPR_INTID, ipl); + if (ipl && ipl > thread->readMiscReg(IPR_IPLR)) { + thread->setMiscReg(IPR_ISR, summary); + thread->setMiscReg(IPR_INTID, ipl); - Fault(new InterruptFault)->invoke(xcProxy); + Fault(new InterruptFault)->invoke(tc); DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", - cpuXC->readMiscReg(IPR_IPLR), ipl, summary); + thread->readMiscReg(IPR_IPLR), ipl, summary); } } #endif @@ -351,29 +356,22 @@ BaseSimpleCPU::checkForInterrupts() Fault -BaseSimpleCPU::setupFetchPacket(Packet *ifetch_pkt) +BaseSimpleCPU::setupFetchRequest(Request *req) { - // Try to fetch an instruction - // set up memory request for instruction fetch - - DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",cpuXC->readPC(), - cpuXC->readNextPC(),cpuXC->readNextNPC()); - - Request *ifetch_req = ifetch_pkt->req; - ifetch_req->setVaddr(cpuXC->readPC() & ~3); - ifetch_req->setTime(curTick); -#if FULL_SYSTEM - ifetch_req->setFlags((cpuXC->readPC() & 1) ? PHYSICAL : 0); +#if THE_ISA == ALPHA_ISA + DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",thread->readPC(), + thread->readNextPC()); #else - ifetch_req->setFlags(0); + DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",thread->readPC(), + thread->readNextPC(),thread->readNextNPC()); #endif - Fault fault = cpuXC->translateInstReq(ifetch_req); + req->setVirt(0, thread->readPC() & ~3, sizeof(MachInst), + (FULL_SYSTEM && (thread->readPC() & 1)) ? PHYSICAL : 0, + thread->readPC()); - if (fault == NoFault) { - ifetch_pkt->reinitFromRequest(); - } + Fault fault = thread->translateInstReq(req); return fault; } @@ -383,33 +381,33 @@ void BaseSimpleCPU::preExecute() { // maintain $r0 semantics - cpuXC->setIntReg(ZeroReg, 0); + thread->setIntReg(ZeroReg, 0); #if THE_ISA == ALPHA_ISA - cpuXC->setFloatReg(ZeroReg, 0.0); + thread->setFloatReg(ZeroReg, 0.0); #endif // ALPHA_ISA // keep an instruction count numInst++; numInsts++; - cpuXC->func_exe_inst++; + thread->funcExeInst++; // check for instruction-count-based events comInstEventQueue[0]->serviceEvents(numInst); // decode the instruction inst = gtoh(inst); - curStaticInst = StaticInst::decode(makeExtMI(inst, cpuXC->readPC())); + curStaticInst = StaticInst::decode(makeExtMI(inst, thread->readPC())); - traceData = Trace::getInstRecord(curTick, xcProxy, this, curStaticInst, - cpuXC->readPC()); + traceData = Trace::getInstRecord(curTick, tc, this, curStaticInst, + thread->readPC()); DPRINTF(Decode,"Decode: Decoded %s instruction (opcode: 0x%x): 0x%x\n", curStaticInst->getName(), curStaticInst->getOpcode(), curStaticInst->machInst); #if FULL_SYSTEM - cpuXC->setInst(inst); + thread->setInst(inst); #endif // FULL_SYSTEM } @@ -417,18 +415,13 @@ void BaseSimpleCPU::postExecute() { #if FULL_SYSTEM - if (system->kernelBinning->fnbin) { - assert(kernelStats); - system->kernelBinning->execute(xcProxy, inst); - } - - if (cpuXC->profile) { + if (thread->profile) { bool usermode = - (cpuXC->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; - cpuXC->profilePC = usermode ? 1 : cpuXC->readPC(); - ProfileNode *node = cpuXC->profile->consume(xcProxy, inst); + (thread->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; + thread->profilePC = usermode ? 1 : thread->readPC(); + ProfileNode *node = thread->profile->consume(tc, inst); if (node) - cpuXC->profileNode = node; + thread->profileNode = node; } #endif @@ -441,7 +434,7 @@ BaseSimpleCPU::postExecute() comLoadEventQueue[0]->serviceEvents(numLoad); } - traceFunctions(cpuXC->readPC()); + traceFunctions(thread->readPC()); if (traceData) { traceData->finalize(); @@ -453,20 +446,16 @@ void BaseSimpleCPU::advancePC(Fault fault) { if (fault != NoFault) { -#if FULL_SYSTEM - fault->invoke(xcProxy); -#else // !FULL_SYSTEM - fatal("fault (%s) detected @ PC %08p", fault->name(), cpuXC->readPC()); -#endif // FULL_SYSTEM + fault->invoke(tc); } else { // go to the next instruction - cpuXC->setPC(cpuXC->readNextPC()); + thread->setPC(thread->readNextPC()); #if THE_ISA == ALPHA_ISA - cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst)); + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); #else - cpuXC->setNextPC(cpuXC->readNextNPC()); - cpuXC->setNextNPC(cpuXC->readNextNPC() + sizeof(MachInst)); + thread->setNextPC(thread->readNextNPC()); + thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); #endif } @@ -474,9 +463,9 @@ BaseSimpleCPU::advancePC(Fault fault) #if FULL_SYSTEM Addr oldpc; do { - oldpc = cpuXC->readPC(); - system->pcEventQueue.service(xcProxy); - } while (oldpc != cpuXC->readPC()); + oldpc = thread->readPC(); + system->pcEventQueue.service(tc); + } while (oldpc != thread->readPC()); #endif } diff --git a/src/cpu/simple/base.hh b/src/cpu/simple/base.hh index 4c0e6f3c7..39bc86050 100644 --- a/src/cpu/simple/base.hh +++ b/src/cpu/simple/base.hh @@ -24,6 +24,10 @@ * 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. + * + * Authors: Steve Reinhardt + * Dave Greene + * Nathan Binkert */ #ifndef __CPU_SIMPLE_BASE_HH__ @@ -32,7 +36,7 @@ #include "base/statistics.hh" #include "config/full_system.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" +#include "cpu/simple_thread.hh" #include "cpu/pc_event.hh" #include "cpu/sampler/sampler.hh" #include "cpu/static_inst.hh" @@ -57,7 +61,7 @@ class Process; #endif // FULL_SYSTEM -class ExecContext; +class ThreadContext; class Checkpoint; namespace Trace { @@ -104,10 +108,13 @@ class BaseSimpleCPU : public BaseCPU virtual ~BaseSimpleCPU(); public: - // execution context - CPUExecContext *cpuXC; + /** SimpleThread object, provides all the architectural state. */ + SimpleThread *thread; - ExecContext *xcProxy; + /** ThreadContext object, provides an interface for external + * objects to modify this thread's state. + */ + ThreadContext *tc; #if FULL_SYSTEM Addr dbg_vtophys(Addr addr); @@ -129,7 +136,7 @@ class BaseSimpleCPU : public BaseCPU StaticInstPtr curStaticInst; void checkForInterrupts(); - Fault setupFetchPacket(Packet *ifetch_pkt); + Fault setupFetchRequest(Request *req); void preExecute(); void postExecute(); void advancePC(Fault fault); @@ -213,104 +220,104 @@ class BaseSimpleCPU : public BaseCPU uint64_t readIntReg(const StaticInst *si, int idx) { - return cpuXC->readIntReg(si->srcRegIdx(idx)); + return thread->readIntReg(si->srcRegIdx(idx)); } FloatReg readFloatReg(const StaticInst *si, int idx, int width) { int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return cpuXC->readFloatReg(reg_idx, width); + return thread->readFloatReg(reg_idx, width); } FloatReg readFloatReg(const StaticInst *si, int idx) { int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return cpuXC->readFloatReg(reg_idx); + return thread->readFloatReg(reg_idx); } FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) { int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return cpuXC->readFloatRegBits(reg_idx, width); + return thread->readFloatRegBits(reg_idx, width); } FloatRegBits readFloatRegBits(const StaticInst *si, int idx) { int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; - return cpuXC->readFloatRegBits(reg_idx); + return thread->readFloatRegBits(reg_idx); } void setIntReg(const StaticInst *si, int idx, uint64_t val) { - cpuXC->setIntReg(si->destRegIdx(idx), val); + thread->setIntReg(si->destRegIdx(idx), val); } void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - cpuXC->setFloatReg(reg_idx, val, width); + thread->setFloatReg(reg_idx, val, width); } void setFloatReg(const StaticInst *si, int idx, FloatReg val) { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - cpuXC->setFloatReg(reg_idx, val); + thread->setFloatReg(reg_idx, val); } void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val, int width) { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - cpuXC->setFloatRegBits(reg_idx, val, width); + thread->setFloatRegBits(reg_idx, val, width); } void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) { int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; - cpuXC->setFloatRegBits(reg_idx, val); + thread->setFloatRegBits(reg_idx, val); } - uint64_t readPC() { return cpuXC->readPC(); } - uint64_t readNextPC() { return cpuXC->readNextPC(); } - uint64_t readNextNPC() { return cpuXC->readNextNPC(); } + uint64_t readPC() { return thread->readPC(); } + uint64_t readNextPC() { return thread->readNextPC(); } + uint64_t readNextNPC() { return thread->readNextNPC(); } - void setPC(uint64_t val) { cpuXC->setPC(val); } - void setNextPC(uint64_t val) { cpuXC->setNextPC(val); } - void setNextNPC(uint64_t val) { cpuXC->setNextNPC(val); } + void setPC(uint64_t val) { thread->setPC(val); } + void setNextPC(uint64_t val) { thread->setNextPC(val); } + void setNextNPC(uint64_t val) { thread->setNextNPC(val); } MiscReg readMiscReg(int misc_reg) { - return cpuXC->readMiscReg(misc_reg); + return thread->readMiscReg(misc_reg); } MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) { - return cpuXC->readMiscRegWithEffect(misc_reg, fault); + return thread->readMiscRegWithEffect(misc_reg, fault); } Fault setMiscReg(int misc_reg, const MiscReg &val) { - return cpuXC->setMiscReg(misc_reg, val); + return thread->setMiscReg(misc_reg, val); } Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) { - return cpuXC->setMiscRegWithEffect(misc_reg, val); + return thread->setMiscRegWithEffect(misc_reg, val); } #if FULL_SYSTEM - Fault hwrei() { return cpuXC->hwrei(); } - int readIntrFlag() { return cpuXC->readIntrFlag(); } - void setIntrFlag(int val) { cpuXC->setIntrFlag(val); } - bool inPalMode() { return cpuXC->inPalMode(); } - void ev5_trap(Fault fault) { fault->invoke(xcProxy); } - bool simPalCheck(int palFunc) { return cpuXC->simPalCheck(palFunc); } + Fault hwrei() { return thread->hwrei(); } + int readIntrFlag() { return thread->readIntrFlag(); } + void setIntrFlag(int val) { thread->setIntrFlag(val); } + bool inPalMode() { return thread->inPalMode(); } + void ev5_trap(Fault fault) { fault->invoke(tc); } + bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); } #else - void syscall(int64_t callnum) { cpuXC->syscall(callnum); } + void syscall(int64_t callnum) { thread->syscall(callnum); } #endif - bool misspeculating() { return cpuXC->misspeculating(); } - ExecContext *xcBase() { return xcProxy; } + bool misspeculating() { return thread->misspeculating(); } + ThreadContext *tcBase() { return tc; } }; #endif // __CPU_SIMPLE_BASE_HH__ diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc index 7cdcdafa1..d5bdcfa9b 100644 --- a/src/cpu/simple/timing.cc +++ b/src/cpu/simple/timing.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #include "arch/utility.hh" @@ -50,11 +52,11 @@ TimingSimpleCPU::init() BaseCPU::init(); #if FULL_SYSTEM - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; // initialize CPU, including PC - TheISA::initCPU(xc, xc->readCpuId()); + TheISA::initCPU(tc, tc->readCpuId()); } #endif } @@ -86,6 +88,8 @@ TimingSimpleCPU::TimingSimpleCPU(Params *p) { _status = Idle; ifetch_pkt = dcache_pkt = NULL; + quiesceEvent = NULL; + state = SimObject::Timing; } @@ -96,25 +100,54 @@ TimingSimpleCPU::~TimingSimpleCPU() void TimingSimpleCPU::serialize(ostream &os) { - BaseSimpleCPU::serialize(os); SERIALIZE_ENUM(_status); + BaseSimpleCPU::serialize(os); } void TimingSimpleCPU::unserialize(Checkpoint *cp, const string §ion) { - BaseSimpleCPU::unserialize(cp, section); UNSERIALIZE_ENUM(_status); + BaseSimpleCPU::unserialize(cp, section); +} + +bool +TimingSimpleCPU::quiesce(Event *quiesce_event) +{ + // TimingSimpleCPU is ready to quiesce if it's not waiting for + // an access to complete. + if (status() == Idle || status() == Running || status() == SwitchedOut) { + DPRINTF(Config, "Ready to quiesce\n"); + return false; + } else { + DPRINTF(Config, "Waiting to quiesce\n"); + changeState(SimObject::Quiescing); + quiesceEvent = quiesce_event; + return true; + } } void -TimingSimpleCPU::switchOut(Sampler *s) +TimingSimpleCPU::resume() { - sampler = s; - if (status() == Running) { - _status = SwitchedOut; + if (_status != SwitchedOut && _status != Idle) { + Event *e = + new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, true); + e->schedule(curTick); } - sampler->signalSwitched(); +} + +void +TimingSimpleCPU::setMemoryMode(State new_mode) +{ + assert(new_mode == SimObject::Timing); +} + +void +TimingSimpleCPU::switchOut() +{ + assert(status() == Running || status() == Idle); + _status = SwitchedOut; } @@ -123,11 +156,11 @@ TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU) { BaseCPU::takeOverFrom(oldCPU); - // if any of this CPU's ExecContexts are active, mark the CPU as + // if any of this CPU's ThreadContexts are active, mark the CPU as // running and schedule its tick event. - for (int i = 0; i < execContexts.size(); ++i) { - ExecContext *xc = execContexts[i]; - if (xc->status() == ExecContext::Active && _status != Running) { + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && _status != Running) { _status = Running; break; } @@ -139,7 +172,7 @@ void TimingSimpleCPU::activateContext(int thread_num, int delay) { assert(thread_num == 0); - assert(cpuXC); + assert(thread); assert(_status == Idle); @@ -156,7 +189,7 @@ void TimingSimpleCPU::suspendContext(int thread_num) { assert(thread_num == 0); - assert(cpuXC); + assert(thread); assert(_status == Running); @@ -172,19 +205,17 @@ template <class T> Fault TimingSimpleCPU::read(Addr addr, T &data, unsigned flags) { - Request *data_read_req = new Request(true); - - data_read_req->setVaddr(addr); - data_read_req->setSize(sizeof(T)); - data_read_req->setFlags(flags); - data_read_req->setTime(curTick); + // need to fill in CPU & thread IDs here + Request *data_read_req = new Request(); + data_read_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE + data_read_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); if (traceData) { traceData->setAddr(data_read_req->getVaddr()); } // translate to physical address - Fault fault = cpuXC->translateDataReadReq(data_read_req); + Fault fault = thread->translateDataReadReq(data_read_req); // Now do the access. if (fault == NoFault) { @@ -255,14 +286,13 @@ template <class T> Fault TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) { - Request *data_write_req = new Request(true); - data_write_req->setVaddr(addr); - data_write_req->setTime(curTick); - data_write_req->setSize(sizeof(T)); - data_write_req->setFlags(flags); + // need to fill in CPU & thread IDs here + Request *data_write_req = new Request(); + data_write_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE + data_write_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); // translate to physical address - Fault fault = cpuXC->translateDataWriteReq(data_write_req); + Fault fault = thread->translateDataWriteReq(data_write_req); // Now do the access. if (fault == NoFault) { Packet *data_write_pkt = @@ -340,13 +370,14 @@ TimingSimpleCPU::fetch() { checkForInterrupts(); - Request *ifetch_req = new Request(true); - ifetch_req->setSize(sizeof(MachInst)); + // need to fill in CPU & thread IDs here + Request *ifetch_req = new Request(); + ifetch_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE + Fault fault = setupFetchRequest(ifetch_req); ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast); ifetch_pkt->dataStatic(&inst); - Fault fault = setupFetchPacket(ifetch_pkt); if (fault == NoFault) { if (!icachePort.sendTiming(ifetch_pkt)) { // Need to wait for retry @@ -385,11 +416,17 @@ TimingSimpleCPU::completeIfetch(Packet *pkt) // instruction assert(pkt->result == Packet::Success); assert(_status == IcacheWaitResponse); + _status = Running; delete pkt->req; delete pkt; + if (getState() == SimObject::Quiescing) { + completeQuiesce(); + return; + } + preExecute(); if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) { // load or store: just send to dcache @@ -419,17 +456,18 @@ TimingSimpleCPU::IcachePort::recvTiming(Packet *pkt) return true; } -Packet * +void TimingSimpleCPU::IcachePort::recvRetry() { // we shouldn't get a retry unless we have a packet that we're // waiting to transmit assert(cpu->ifetch_pkt != NULL); assert(cpu->_status == IcacheRetry); - cpu->_status = IcacheWaitResponse; Packet *tmp = cpu->ifetch_pkt; - cpu->ifetch_pkt = NULL; - return tmp; + if (sendTiming(tmp)) { + cpu->_status = IcacheWaitResponse; + cpu->ifetch_pkt = NULL; + } } void @@ -441,6 +479,15 @@ TimingSimpleCPU::completeDataAccess(Packet *pkt) assert(_status == DcacheWaitResponse); _status = Running; + if (getState() == SimObject::Quiescing) { + completeQuiesce(); + + delete pkt->req; + delete pkt; + + return; + } + Fault fault = curStaticInst->completeAcc(pkt, this, traceData); delete pkt->req; @@ -451,6 +498,13 @@ TimingSimpleCPU::completeDataAccess(Packet *pkt) } +void +TimingSimpleCPU::completeQuiesce() +{ + DPRINTF(Config, "Done quiescing\n"); + changeState(SimObject::QuiescedTiming); + quiesceEvent->process(); +} bool TimingSimpleCPU::DcachePort::recvTiming(Packet *pkt) @@ -459,17 +513,18 @@ TimingSimpleCPU::DcachePort::recvTiming(Packet *pkt) return true; } -Packet * +void TimingSimpleCPU::DcachePort::recvRetry() { // we shouldn't get a retry unless we have a packet that we're // waiting to transmit assert(cpu->dcache_pkt != NULL); assert(cpu->_status == DcacheRetry); - cpu->_status = DcacheWaitResponse; Packet *tmp = cpu->dcache_pkt; - cpu->dcache_pkt = NULL; - return tmp; + if (sendTiming(tmp)) { + cpu->_status = DcacheWaitResponse; + cpu->dcache_pkt = NULL; + } } diff --git a/src/cpu/simple/timing.hh b/src/cpu/simple/timing.hh index b46631d5a..d91144e4a 100644 --- a/src/cpu/simple/timing.hh +++ b/src/cpu/simple/timing.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __CPU_SIMPLE_TIMING_HH__ @@ -62,6 +64,8 @@ class TimingSimpleCPU : public BaseSimpleCPU Status status() const { return _status; } + Event *quiesceEvent; + private: class CpuPort : public Port @@ -100,7 +104,7 @@ class TimingSimpleCPU : public BaseSimpleCPU virtual bool recvTiming(Packet *pkt); - virtual Packet *recvRetry(); + virtual void recvRetry(); }; class DcachePort : public CpuPort @@ -115,7 +119,7 @@ class TimingSimpleCPU : public BaseSimpleCPU virtual bool recvTiming(Packet *pkt); - virtual Packet *recvRetry(); + virtual void recvRetry(); }; IcachePort icachePort; @@ -129,7 +133,11 @@ class TimingSimpleCPU : public BaseSimpleCPU virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); - void switchOut(Sampler *s); + virtual bool quiesce(Event *quiesce_event); + virtual void resume(); + virtual void setMemoryMode(State new_mode); + + void switchOut(); void takeOverFrom(BaseCPU *oldCPU); virtual void activateContext(int thread_num, int delay); @@ -145,6 +153,8 @@ class TimingSimpleCPU : public BaseSimpleCPU void completeIfetch(Packet *); void completeDataAccess(Packet *); void advanceInst(Fault fault); + private: + void completeQuiesce(); }; #endif // __CPU_SIMPLE_TIMING_HH__ diff --git a/src/cpu/cpu_exec_context.cc b/src/cpu/simple_thread.cc index b8aa9a67e..db28b23e8 100644 --- a/src/cpu/cpu_exec_context.cc +++ b/src/cpu/simple_thread.cc @@ -24,14 +24,19 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + * Lisa Hsu + * Kevin Lim */ #include <string> #include "arch/isa_traits.hh" #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" -#include "cpu/exec_context.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" #if FULL_SYSTEM #include "base/callback.hh" @@ -39,6 +44,7 @@ #include "base/output.hh" #include "base/trace.hh" #include "cpu/profile.hh" +#include "cpu/quiesce_event.hh" #include "kern/kernel_stats.hh" #include "sim/serialize.hh" #include "sim/sim_exit.hh" @@ -53,23 +59,24 @@ using namespace std; // constructor #if FULL_SYSTEM -CPUExecContext::CPUExecContext(BaseCPU *_cpu, int _thread_num, System *_sys, - AlphaITB *_itb, AlphaDTB *_dtb) - : _status(ExecContext::Unallocated), cpu(_cpu), thread_num(_thread_num), - cpu_id(-1), lastActivate(0), lastSuspend(0), system(_sys), itb(_itb), - dtb(_dtb), profile(NULL), quiesceEvent(this), func_exe_inst(0), - storeCondFailures(0) +SimpleThread::SimpleThread(BaseCPU *_cpu, int _thread_num, System *_sys, + AlphaITB *_itb, AlphaDTB *_dtb, + bool use_kernel_stats) + : ThreadState(-1, _thread_num), cpu(_cpu), system(_sys), itb(_itb), + dtb(_dtb) { - proxy = new ProxyExecContext<CPUExecContext>(this); + tc = new ProxyThreadContext<SimpleThread>(this); + + quiesceEvent = new EndQuiesceEvent(tc); regs.clear(); if (cpu->params->profile) { profile = new FunctionProfile(system->kernelSymtab); Callback *cb = - new MakeCallback<CPUExecContext, - &CPUExecContext::dumpFuncProfile>(this); + new MakeCallback<SimpleThread, + &SimpleThread::dumpFuncProfile>(this); registerExitCallback(cb); } @@ -79,98 +86,59 @@ CPUExecContext::CPUExecContext(BaseCPU *_cpu, int _thread_num, System *_sys, profileNode = &dummyNode; profilePC = 3; + if (use_kernel_stats) { + kernelStats = new Kernel::Statistics(system); + } else { + kernelStats = NULL; + } Port *mem_port; physPort = new FunctionalPort(csprintf("%s-%d-funcport", - cpu->name(), thread_num)); + cpu->name(), tid)); mem_port = system->physmem->getPort("functional"); mem_port->setPeer(physPort); physPort->setPeer(mem_port); virtPort = new VirtualPort(csprintf("%s-%d-vport", - cpu->name(), thread_num)); + cpu->name(), tid)); mem_port = system->physmem->getPort("functional"); mem_port->setPeer(virtPort); virtPort->setPeer(mem_port); } #else -CPUExecContext::CPUExecContext(BaseCPU *_cpu, int _thread_num, +SimpleThread::SimpleThread(BaseCPU *_cpu, int _thread_num, Process *_process, int _asid, MemObject* memobj) - : _status(ExecContext::Unallocated), - cpu(_cpu), thread_num(_thread_num), cpu_id(-1), lastActivate(0), - lastSuspend(0), process(_process), asid(_asid), - func_exe_inst(0), storeCondFailures(0) + : ThreadState(-1, _thread_num, _process, _asid, memobj), + cpu(_cpu) { /* Use this port to for syscall emulation writes to memory. */ Port *mem_port; port = new TranslatingPort(csprintf("%s-%d-funcport", - cpu->name(), thread_num), + cpu->name(), tid), process->pTable, false); mem_port = memobj->getPort("functional"); mem_port->setPeer(port); port->setPeer(mem_port); regs.clear(); - proxy = new ProxyExecContext<CPUExecContext>(this); + tc = new ProxyThreadContext<SimpleThread>(this); } -CPUExecContext::CPUExecContext(RegFile *regFile) - : cpu(NULL), thread_num(-1), process(NULL), asid(-1), - func_exe_inst(0), storeCondFailures(0) +SimpleThread::SimpleThread(RegFile *regFile) + : ThreadState(-1, -1, NULL, -1, NULL), cpu(NULL) { regs = *regFile; - proxy = new ProxyExecContext<CPUExecContext>(this); + tc = new ProxyThreadContext<SimpleThread>(this); } #endif -CPUExecContext::~CPUExecContext() -{ - delete proxy; -} - -#if FULL_SYSTEM -void -CPUExecContext::dumpFuncProfile() -{ - std::ostream *os = simout.create(csprintf("profile.%s.dat", cpu->name())); - profile->dump(proxy, *os); -} - -CPUExecContext::EndQuiesceEvent::EndQuiesceEvent(CPUExecContext *_cpuXC) - : Event(&mainEventQueue), cpuXC(_cpuXC) -{ -} - -void -CPUExecContext::EndQuiesceEvent::process() -{ - cpuXC->activate(); -} - -const char* -CPUExecContext::EndQuiesceEvent::description() -{ - return "End Quiesce Event."; -} - -void -CPUExecContext::profileClear() -{ - if (profile) - profile->clear(); -} - -void -CPUExecContext::profileSample() +SimpleThread::~SimpleThread() { - if (profile) - profile->sample(profileNode, profilePC); + delete tc; } -#endif - void -CPUExecContext::takeOverFrom(ExecContext *oldContext) +SimpleThread::takeOverFrom(ThreadContext *oldContext) { // some things should already be set up #if FULL_SYSTEM @@ -182,69 +150,97 @@ CPUExecContext::takeOverFrom(ExecContext *oldContext) // copy over functional state _status = oldContext->status(); copyArchRegs(oldContext); - cpu_id = oldContext->readCpuId(); + cpuId = oldContext->readCpuId(); #if !FULL_SYSTEM - func_exe_inst = oldContext->readFuncExeInst(); + funcExeInst = oldContext->readFuncExeInst(); +#else + EndQuiesceEvent *quiesce = oldContext->getQuiesceEvent(); + if (quiesce) { + // Point the quiesce event's TC at this TC so that it wakes up + // the proper CPU. + quiesce->tc = tc; + } + if (quiesceEvent) { + quiesceEvent->tc = tc; + } #endif storeCondFailures = 0; - oldContext->setStatus(ExecContext::Unallocated); + oldContext->setStatus(ThreadContext::Unallocated); } void -CPUExecContext::serialize(ostream &os) +SimpleThread::serialize(ostream &os) { SERIALIZE_ENUM(_status); regs.serialize(os); // thread_num and cpu_id are deterministic from the config - SERIALIZE_SCALAR(func_exe_inst); + SERIALIZE_SCALAR(funcExeInst); SERIALIZE_SCALAR(inst); #if FULL_SYSTEM Tick quiesceEndTick = 0; - if (quiesceEvent.scheduled()) - quiesceEndTick = quiesceEvent.when(); + if (quiesceEvent->scheduled()) + quiesceEndTick = quiesceEvent->when(); SERIALIZE_SCALAR(quiesceEndTick); - + if (kernelStats) + kernelStats->serialize(os); #endif } void -CPUExecContext::unserialize(Checkpoint *cp, const std::string §ion) +SimpleThread::unserialize(Checkpoint *cp, const std::string §ion) { UNSERIALIZE_ENUM(_status); regs.unserialize(cp, section); // thread_num and cpu_id are deterministic from the config - UNSERIALIZE_SCALAR(func_exe_inst); + UNSERIALIZE_SCALAR(funcExeInst); UNSERIALIZE_SCALAR(inst); #if FULL_SYSTEM Tick quiesceEndTick; UNSERIALIZE_SCALAR(quiesceEndTick); if (quiesceEndTick) - quiesceEvent.schedule(quiesceEndTick); + quiesceEvent->schedule(quiesceEndTick); + if (kernelStats) + kernelStats->unserialize(cp, section); #endif } +#if FULL_SYSTEM +void +SimpleThread::dumpFuncProfile() +{ + std::ostream *os = simout.create(csprintf("profile.%s.dat", cpu->name())); + profile->dump(tc, *os); +} +#endif void -CPUExecContext::activate(int delay) +SimpleThread::activate(int delay) { - if (status() == ExecContext::Active) + if (status() == ThreadContext::Active) return; lastActivate = curTick; - _status = ExecContext::Active; - cpu->activateContext(thread_num, delay); + if (status() == ThreadContext::Unallocated) { + cpu->activateWhenReady(tid); + return; + } + + _status = ThreadContext::Active; + + // status() == Suspended + cpu->activateContext(tid, delay); } void -CPUExecContext::suspend() +SimpleThread::suspend() { - if (status() == ExecContext::Suspended) + if (status() == ThreadContext::Suspended) return; lastActivate = curTick; @@ -253,58 +249,62 @@ CPUExecContext::suspend() #if FULL_SYSTEM // Don't change the status from active if there are pending interrupts if (cpu->check_interrupts()) { - assert(status() == ExecContext::Active); + assert(status() == ThreadContext::Active); return; } #endif */ - _status = ExecContext::Suspended; - cpu->suspendContext(thread_num); + _status = ThreadContext::Suspended; + cpu->suspendContext(tid); } void -CPUExecContext::deallocate() +SimpleThread::deallocate() { - if (status() == ExecContext::Unallocated) + if (status() == ThreadContext::Unallocated) return; - _status = ExecContext::Unallocated; - cpu->deallocateContext(thread_num); + _status = ThreadContext::Unallocated; + cpu->deallocateContext(tid); } void -CPUExecContext::halt() +SimpleThread::halt() { - if (status() == ExecContext::Halted) + if (status() == ThreadContext::Halted) return; - _status = ExecContext::Halted; - cpu->haltContext(thread_num); + _status = ThreadContext::Halted; + cpu->haltContext(tid); } void -CPUExecContext::regStats(const string &name) +SimpleThread::regStats(const string &name) { +#if FULL_SYSTEM + if (kernelStats) + kernelStats->regStats(name + ".kern"); +#endif } void -CPUExecContext::copyArchRegs(ExecContext *xc) +SimpleThread::copyArchRegs(ThreadContext *src_tc) { - TheISA::copyRegs(xc, proxy); + TheISA::copyRegs(src_tc, tc); } #if FULL_SYSTEM VirtualPort* -CPUExecContext::getVirtPort(ExecContext *xc) +SimpleThread::getVirtPort(ThreadContext *src_tc) { - if (!xc) + if (!src_tc) return virtPort; VirtualPort *vp; Port *mem_port; - vp = new VirtualPort("xc-vport", xc); + vp = new VirtualPort("tc-vport", src_tc); mem_port = system->physmem->getPort("functional"); mem_port->setPeer(vp); vp->setPeer(mem_port); @@ -312,11 +312,12 @@ CPUExecContext::getVirtPort(ExecContext *xc) } void -CPUExecContext::delVirtPort(VirtualPort *vp) +SimpleThread::delVirtPort(VirtualPort *vp) { -// assert(!vp->nullExecContext()); - delete vp->getPeer(); - delete vp; + if (vp != virtPort) { + delete vp->getPeer(); + delete vp; + } } diff --git a/src/cpu/cpu_exec_context.hh b/src/cpu/simple_thread.hh index 2c06a7b3b..de65e9891 100644 --- a/src/cpu/cpu_exec_context.hh +++ b/src/cpu/simple_thread.hh @@ -24,14 +24,18 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ -#ifndef __CPU_CPU_EXEC_CONTEXT_HH__ -#define __CPU_CPU_EXEC_CONTEXT_HH__ +#ifndef __CPU_SIMPLE_THREAD_HH__ +#define __CPU_SIMPLE_THREAD_HH__ #include "arch/isa_traits.hh" #include "config/full_system.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" +#include "cpu/thread_state.hh" #include "mem/physical.hh" #include "mem/request.hh" #include "sim/byteswap.hh" @@ -51,6 +55,9 @@ class ProfileNode; class FunctionalPort; class PhysicalPort; +namespace Kernel { + class Statistics; +}; #else // !FULL_SYSTEM @@ -58,16 +65,25 @@ class PhysicalPort; #include "mem/page_table.hh" class TranslatingPort; - #endif // FULL_SYSTEM -// -// The CPUExecContext object represents a functional context for -// instruction execution. It incorporates everything required for -// architecture-level functional simulation of a single thread. -// +/** + * The SimpleThread object provides a combination of the ThreadState + * object and the ThreadContext interface. It implements the + * ThreadContext interface so that a ProxyThreadContext class can be + * made using SimpleThread as the template parameter (see + * thread_context.hh). It adds to the ThreadState object by adding all + * the objects needed for simple functional execution, including a + * simple architectural register file, and pointers to the ITB and DTB + * in full system mode. For CPU models that do not need more advanced + * ways to hold state (i.e. a separate physical register file, or + * separate fetch and commit PC's), this SimpleThread class provides + * all the necessary state for full architecture-level functional + * simulation. See the AtomicSimpleCPU or TimingSimpleCPU for + * examples. + */ -class CPUExecContext +class SimpleThread : public ThreadState { protected: typedef TheISA::RegFile RegFile; @@ -77,202 +93,83 @@ class CPUExecContext typedef TheISA::FloatReg FloatReg; typedef TheISA::FloatRegBits FloatRegBits; public: - typedef ExecContext::Status Status; - - private: - Status _status; - - public: - Status status() const { return _status; } - - void setStatus(Status newStatus) { _status = newStatus; } - - /// Set the status to Active. Optional delay indicates number of - /// cycles to wait before beginning execution. - void activate(int delay = 1); - - /// Set the status to Suspended. - void suspend(); - - /// Set the status to Unallocated. - void deallocate(); - - /// Set the status to Halted. - void halt(); + typedef ThreadContext::Status Status; protected: RegFile regs; // correct-path register context public: - // pointer to CPU associated with this context + // pointer to CPU associated with this SimpleThread BaseCPU *cpu; - ProxyExecContext<CPUExecContext> *proxy; - - // Current instruction - MachInst inst; - - // Index of hardware thread context on the CPU that this represents. - int thread_num; - - // ID of this context w.r.t. the System or Process object to which - // it belongs. For full-system mode, this is the system CPU ID. - int cpu_id; - - Tick lastActivate; - Tick lastSuspend; + ProxyThreadContext<SimpleThread> *tc; System *system; - #if FULL_SYSTEM AlphaITB *itb; AlphaDTB *dtb; - - /** A functional port outgoing only for functional accesses to physical - * addresses.*/ - FunctionalPort *physPort; - - /** A functional port, outgoing only, for functional accesse to virtual - * addresses. That doen't require execution context information */ - VirtualPort *virtPort; - - FunctionProfile *profile; - ProfileNode *profileNode; - Addr profilePC; - void dumpFuncProfile(); - - /** Event for timing out quiesce instruction */ - struct EndQuiesceEvent : public Event - { - /** A pointer to the execution context that is quiesced */ - CPUExecContext *cpuXC; - - EndQuiesceEvent(CPUExecContext *_cpuXC); - - /** Event process to occur at interrupt*/ - virtual void process(); - - /** Event description */ - virtual const char *description(); - }; - EndQuiesceEvent quiesceEvent; - - Event *getQuiesceEvent() { return &quiesceEvent; } - - Tick readLastActivate() { return lastActivate; } - - Tick readLastSuspend() { return lastSuspend; } - - void profileClear(); - - void profileSample(); - -#else - /// Port that syscalls can use to access memory (provides translation step). - TranslatingPort *port; - - Process *process; - - // Address space ID. Note that this is used for TIMING cache - // simulation only; all functional memory accesses should use - // one of the FunctionalMemory pointers above. - short asid; - #endif - /** - * Temporary storage to pass the source address from copy_load to - * copy_store. - * @todo Remove this temporary when we have a better way to do it. - */ - Addr copySrcAddr; - /** - * Temp storage for the physical source address of a copy. - * @todo Remove this temporary when we have a better way to do it. - */ - Addr copySrcPhysAddr; - - - /* - * number of executed instructions, for matching with syscall trace - * points in EIO files. - */ - Counter func_exe_inst; - - // - // Count failed store conditionals so we can warn of apparent - // application deadlock situations. - unsigned storeCondFailures; - - // constructor: initialize context from given process structure + // constructor: initialize SimpleThread from given process structure #if FULL_SYSTEM - CPUExecContext(BaseCPU *_cpu, int _thread_num, System *_system, - AlphaITB *_itb, AlphaDTB *_dtb); + SimpleThread(BaseCPU *_cpu, int _thread_num, System *_system, + AlphaITB *_itb, AlphaDTB *_dtb, + bool use_kernel_stats = true); #else - CPUExecContext(BaseCPU *_cpu, int _thread_num, Process *_process, int _asid, - MemObject *memobj); - // Constructor to use XC to pass reg file around. Not used for anything - // else. - CPUExecContext(RegFile *regFile); + SimpleThread(BaseCPU *_cpu, int _thread_num, Process *_process, int _asid, + MemObject *memobj); + // Constructor to use SimpleThread to pass reg file around. Not + // used for anything else. + SimpleThread(RegFile *regFile); #endif - virtual ~CPUExecContext(); + virtual ~SimpleThread(); - virtual void takeOverFrom(ExecContext *oldContext); + virtual void takeOverFrom(ThreadContext *oldContext); void regStats(const std::string &name); void serialize(std::ostream &os); void unserialize(Checkpoint *cp, const std::string §ion); - BaseCPU *getCpuPtr() { return cpu; } - - ExecContext *getProxy() { return proxy; } + /*************************************************************** + * SimpleThread functions to provide CPU with access to various + * state, and to provide address translation methods. + **************************************************************/ - int getThreadNum() { return thread_num; } + /** Returns the pointer to this SimpleThread's ThreadContext. Used + * when a ThreadContext must be passed to objects outside of the + * CPU. + */ + ThreadContext *getTC() { return tc; } #if FULL_SYSTEM - System *getSystemPtr() { return system; } - - AlphaITB *getITBPtr() { return itb; } - - AlphaDTB *getDTBPtr() { return dtb; } - int getInstAsid() { return regs.instAsid(); } int getDataAsid() { return regs.dataAsid(); } Fault translateInstReq(RequestPtr &req) { - return itb->translate(req, proxy); + return itb->translate(req, tc); } Fault translateDataReadReq(RequestPtr &req) { - return dtb->translate(req, proxy, false); + return dtb->translate(req, tc, false); } Fault translateDataWriteReq(RequestPtr &req) { - return dtb->translate(req, proxy, true); + return dtb->translate(req, tc, true); } - FunctionalPort *getPhysPort() { return physPort; } - - /** Return a virtual port. If no exec context is specified then a static - * port is returned. Otherwise a port is created and returned. It must be - * deleted by deleteVirtPort(). */ - VirtualPort *getVirtPort(ExecContext *xc); + void dumpFuncProfile(); - void delVirtPort(VirtualPort *vp); + int readIntrFlag() { return regs.intrflag; } + void setIntrFlag(int val) { regs.intrflag = val; } + Fault hwrei(); + bool simPalCheck(int palFunc); #else - TranslatingPort *getMemPort() { return port; } - - Process *getProcessPtr() { return process; } - - int getInstAsid() { return asid; } - int getDataAsid() { return asid; } - Fault translateInstReq(RequestPtr &req) { return process->pTable->translate(req); @@ -287,9 +184,50 @@ class CPUExecContext { return process->pTable->translate(req); } +#endif + + /******************************************* + * ThreadContext interface functions. + ******************************************/ + + BaseCPU *getCpuPtr() { return cpu; } + + int getThreadNum() { return tid; } + +#if FULL_SYSTEM + System *getSystemPtr() { return system; } + + AlphaITB *getITBPtr() { return itb; } + + AlphaDTB *getDTBPtr() { return dtb; } + + FunctionalPort *getPhysPort() { return physPort; } + + /** Return a virtual port. If no thread context is specified then a static + * port is returned. Otherwise a port is created and returned. It must be + * deleted by deleteVirtPort(). */ + VirtualPort *getVirtPort(ThreadContext *tc); + void delVirtPort(VirtualPort *vp); #endif + Status status() const { return _status; } + + void setStatus(Status newStatus) { _status = newStatus; } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1); + + /// Set the status to Suspended. + void suspend(); + + /// Set the status to Unallocated. + void deallocate(); + + /// Set the status to Halted. + void halt(); + /* template <class T> Fault read(RequestPtr &req, T &data) @@ -361,14 +299,6 @@ class CPUExecContext */ virtual bool misspeculating(); - - MachInst getInst() { return inst; } - - void setInst(MachInst new_inst) - { - inst = new_inst; - } - Fault instRead(RequestPtr &req) { panic("instRead not implemented"); @@ -376,11 +306,9 @@ class CPUExecContext return NoFault; } - void setCpuId(int id) { cpu_id = id; } - - int readCpuId() { return cpu_id; } + void copyArchRegs(ThreadContext *tc); - void copyArchRegs(ExecContext *xc); + void clearArchRegs() { regs.clear(); } // // New accessors for new decoder. @@ -465,7 +393,6 @@ class CPUExecContext regs.setNextNPC(val); } - MiscReg readMiscReg(int misc_reg) { return regs.readMiscReg(misc_reg); @@ -473,7 +400,7 @@ class CPUExecContext MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) { - return regs.readMiscRegWithEffect(misc_reg, fault, proxy); + return regs.readMiscRegWithEffect(misc_reg, fault, tc); } Fault setMiscReg(int misc_reg, const MiscReg &val) @@ -483,7 +410,7 @@ class CPUExecContext Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) { - return regs.setMiscRegWithEffect(misc_reg, val, proxy); + return regs.setMiscRegWithEffect(misc_reg, val, tc); } unsigned readStCondFailures() { return storeCondFailures; } @@ -491,14 +418,8 @@ class CPUExecContext void setStCondFailures(unsigned sc_failures) { storeCondFailures = sc_failures; } - void clearArchRegs() { regs.clear(); } - #if FULL_SYSTEM - int readIntrFlag() { return regs.intrflag; } - void setIntrFlag(int val) { regs.intrflag = val; } - Fault hwrei(); bool inPalMode() { return AlphaISA::PcPAL(regs.readPC()); } - bool simPalCheck(int palFunc); #endif #if !FULL_SYSTEM @@ -520,12 +441,8 @@ class CPUExecContext void syscall(int64_t callnum) { - process->syscall(callnum, proxy); + process->syscall(callnum, tc); } - - Counter readFuncExeInst() { return func_exe_inst; } - - void setFuncExeInst(Counter new_val) { func_exe_inst = new_val; } #endif void changeRegFileContext(RegFile::ContextParam param, @@ -538,7 +455,7 @@ class CPUExecContext // for non-speculative execution context, spec_mode is always false inline bool -CPUExecContext::misspeculating() +SimpleThread::misspeculating() { return false; } diff --git a/src/cpu/smt.hh b/src/cpu/smt.hh index 9c52abf95..cac1a8fd5 100644 --- a/src/cpu/smt.hh +++ b/src/cpu/smt.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /** diff --git a/src/cpu/static_inst.cc b/src/cpu/static_inst.cc index c307dc6fc..c311d2282 100644 --- a/src/cpu/static_inst.cc +++ b/src/cpu/static_inst.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #include <iostream> @@ -57,7 +60,7 @@ StaticInst::dumpDecodeCacheStats() } bool -StaticInst::hasBranchTarget(Addr pc, ExecContext *xc, Addr &tgt) const +StaticInst::hasBranchTarget(Addr pc, ThreadContext *tc, Addr &tgt) const { if (isDirectCtrl()) { tgt = branchTarget(pc); @@ -65,7 +68,7 @@ StaticInst::hasBranchTarget(Addr pc, ExecContext *xc, Addr &tgt) const } if (isIndirectCtrl()) { - tgt = branchTarget(xc); + tgt = branchTarget(tc); return true; } diff --git a/src/cpu/static_inst.hh b/src/cpu/static_inst.hh index 33c9144fb..a98078634 100644 --- a/src/cpu/static_inst.hh +++ b/src/cpu/static_inst.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __CPU_STATIC_INST_HH__ @@ -32,6 +34,7 @@ #include <bitset> #include <string> +#include "base/bitfield.hh" #include "base/hashmap.hh" #include "base/misc.hh" #include "base/refcnt.hh" @@ -41,13 +44,19 @@ // forward declarations struct AlphaSimpleImpl; -class ExecContext; +struct OzoneImpl; +struct SimpleImpl; +class ThreadContext; class DynInst; class Packet; template <class Impl> class AlphaDynInst; +template <class Impl> +class OzoneDynInst; + +class CheckerCPU; class FastCPU; class AtomicSimpleCPU; class TimingSimpleCPU; @@ -100,6 +109,7 @@ class StaticInstBase : public RefCounted IsMemRef, ///< References memory (load, store, or prefetch). IsLoad, ///< Reads from memory (load or prefetch). IsStore, ///< Writes to memory. + IsStoreConditional, ///< Store conditional instruction. IsInstPrefetch, ///< Instruction-cache prefetch. IsDataPrefetch, ///< Data-cache prefetch. IsCopy, ///< Fast Cache block copy @@ -124,6 +134,10 @@ class StaticInstBase : public RefCounted IsWriteBarrier, ///< Is a write barrier IsNonSpeculative, ///< Should not be executed speculatively + IsQuiesce, ///< Is a quiesce instruction + + IsIprAccess, ///< Accesses IPRs + IsUnverifiable, ///< Can't be verified by a checker NumFlags }; @@ -187,6 +201,7 @@ class StaticInstBase : public RefCounted bool isMemRef() const { return flags[IsMemRef]; } bool isLoad() const { return flags[IsLoad]; } bool isStore() const { return flags[IsStore]; } + bool isStoreConditional() const { return flags[IsStoreConditional]; } bool isInstPrefetch() const { return flags[IsInstPrefetch]; } bool isDataPrefetch() const { return flags[IsDataPrefetch]; } bool isCopy() const { return flags[IsCopy];} @@ -211,6 +226,9 @@ class StaticInstBase : public RefCounted bool isMemBarrier() const { return flags[IsMemBarrier]; } bool isWriteBarrier() const { return flags[IsWriteBarrier]; } bool isNonSpeculative() const { return flags[IsNonSpeculative]; } + bool isQuiesce() const { return flags[IsQuiesce]; } + bool isIprAccess() const { return flags[IsIprAccess]; } + bool isUnverifiable() const { return flags[IsUnverifiable]; } //@} /// Operation class. Used to select appropriate function unit in issue. @@ -340,12 +358,12 @@ class StaticInst : public StaticInstBase /** * Return the target address for an indirect branch (jump). The - * register value is read from the supplied execution context, so - * the result is valid only if the execution context is about to + * register value is read from the supplied thread context, so + * the result is valid only if the thread context is about to * execute the branch in question. Invalid if not an indirect * branch (i.e. isIndirectCtrl() should be true). */ - virtual Addr branchTarget(ExecContext *xc) const + virtual Addr branchTarget(ThreadContext *tc) const { panic("StaticInst::branchTarget() called on instruction " "that is not an indirect branch."); @@ -355,7 +373,7 @@ class StaticInst : public StaticInstBase * Return true if the instruction is a control transfer, and if so, * return the target address as well. */ - bool hasBranchTarget(Addr pc, ExecContext *xc, Addr &tgt) const; + bool hasBranchTarget(Addr pc, ThreadContext *tc, Addr &tgt) const; /** * Return string representation of disassembled instruction. @@ -394,16 +412,10 @@ class StaticInst : public StaticInstBase //This is defined as inline below. static StaticInstPtr decode(ExtMachInst mach_inst); - //MIPS Decoder Debug Functions - int getOpcode() { return (machInst & 0xFC000000) >> 26 ; }//31..26 - int getRs() { return (machInst & 0x03E00000) >> 21; } //25...21 - int getRt() { return (machInst & 0x001F0000) >> 16; } //20...16 - int getRd() { return (machInst & 0x0000F800) >> 11; } //15...11 - int getImm() { return (machInst & 0x0000FFFF); } //15...0 - int getFunction(){ return (machInst & 0x0000003F); }//5...0 - int getBranch(){ return (machInst & 0x0000FFFF); }//15...0 - int getJump(){ return (machInst & 0x03FFFFFF); }//5...0 - int getHint(){ return (machInst & 0x000007C0) >> 6; } //10...6 + /// Return opcode of machine instruction + uint32_t getOpcode() { return bits(machInst, 31, 26);} + + /// Return name of machine instruction std::string getName() { return mnemonic; } }; diff --git a/src/cpu/thread_context.hh b/src/cpu/thread_context.hh new file mode 100644 index 000000000..70d705144 --- /dev/null +++ b/src/cpu/thread_context.hh @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_THREAD_CONTEXT_HH__ +#define __CPU_THREAD_CONTEXT_HH__ + +#include "config/full_system.hh" +#include "mem/request.hh" +#include "sim/faults.hh" +#include "sim/host.hh" +#include "sim/serialize.hh" +#include "sim/byteswap.hh" + +// @todo: Figure out a more architecture independent way to obtain the ITB and +// DTB pointers. +class AlphaDTB; +class AlphaITB; +class BaseCPU; +class EndQuiesceEvent; +class Event; +class TranslatingPort; +class FunctionalPort; +class VirtualPort; +class Process; +class System; +namespace Kernel { + class Statistics; +}; + +/** + * ThreadContext is the external interface to all thread state for + * anything outside of the CPU. It provides all accessor methods to + * state that might be needed by external objects, ranging from + * register values to things such as kernel stats. It is an abstract + * base class; the CPU can create its own ThreadContext by either + * deriving from it, or using the templated ProxyThreadContext. + * + * The ThreadContext is slightly different than the ExecContext. The + * ThreadContext provides access to an individual thread's state; an + * ExecContext provides ISA access to the CPU (meaning it is + * implicitly multithreaded on SMT systems). Additionally the + * ThreadState is an abstract class that exactly defines the + * interface; the ExecContext is a more implicit interface that must + * be implemented so that the ISA can access whatever state it needs. + */ +class ThreadContext +{ + protected: + typedef TheISA::RegFile RegFile; + typedef TheISA::MachInst MachInst; + typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscRegFile MiscRegFile; + typedef TheISA::MiscReg MiscReg; + public: + enum Status + { + /// Initialized but not running yet. All CPUs start in + /// this state, but most transition to Active on cycle 1. + /// In MP or SMT systems, non-primary contexts will stay + /// in this state until a thread is assigned to them. + Unallocated, + + /// Running. Instructions should be executed only when + /// the context is in this state. + Active, + + /// Temporarily inactive. Entered while waiting for + /// synchronization, etc. + Suspended, + + /// Permanently shut down. Entered when target executes + /// m5exit pseudo-instruction. When all contexts enter + /// this state, the simulation will terminate. + Halted + }; + + virtual ~ThreadContext() { }; + + virtual BaseCPU *getCpuPtr() = 0; + + virtual void setCpuId(int id) = 0; + + virtual int readCpuId() = 0; + +#if FULL_SYSTEM + virtual System *getSystemPtr() = 0; + + virtual AlphaITB *getITBPtr() = 0; + + virtual AlphaDTB * getDTBPtr() = 0; + + virtual Kernel::Statistics *getKernelStats() = 0; + + virtual FunctionalPort *getPhysPort() = 0; + + virtual VirtualPort *getVirtPort(ThreadContext *tc = NULL) = 0; + + virtual void delVirtPort(VirtualPort *vp) = 0; +#else + virtual TranslatingPort *getMemPort() = 0; + + virtual Process *getProcessPtr() = 0; +#endif + + virtual Status status() const = 0; + + virtual void setStatus(Status new_status) = 0; + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + virtual void activate(int delay = 1) = 0; + + /// Set the status to Suspended. + virtual void suspend() = 0; + + /// Set the status to Unallocated. + virtual void deallocate() = 0; + + /// Set the status to Halted. + virtual void halt() = 0; + +#if FULL_SYSTEM + virtual void dumpFuncProfile() = 0; +#endif + + virtual void takeOverFrom(ThreadContext *old_context) = 0; + + virtual void regStats(const std::string &name) = 0; + + virtual void serialize(std::ostream &os) = 0; + virtual void unserialize(Checkpoint *cp, const std::string §ion) = 0; + +#if FULL_SYSTEM + virtual EndQuiesceEvent *getQuiesceEvent() = 0; + + // Not necessarily the best location for these... + // Having an extra function just to read these is obnoxious + virtual Tick readLastActivate() = 0; + virtual Tick readLastSuspend() = 0; + + virtual void profileClear() = 0; + virtual void profileSample() = 0; +#endif + + virtual int getThreadNum() = 0; + + // Also somewhat obnoxious. Really only used for the TLB fault. + // However, may be quite useful in SPARC. + virtual TheISA::MachInst getInst() = 0; + + virtual void copyArchRegs(ThreadContext *tc) = 0; + + virtual void clearArchRegs() = 0; + + // + // New accessors for new decoder. + // + virtual uint64_t readIntReg(int reg_idx) = 0; + + virtual FloatReg readFloatReg(int reg_idx, int width) = 0; + + virtual FloatReg readFloatReg(int reg_idx) = 0; + + virtual FloatRegBits readFloatRegBits(int reg_idx, int width) = 0; + + virtual FloatRegBits readFloatRegBits(int reg_idx) = 0; + + virtual void setIntReg(int reg_idx, uint64_t val) = 0; + + virtual void setFloatReg(int reg_idx, FloatReg val, int width) = 0; + + virtual void setFloatReg(int reg_idx, FloatReg val) = 0; + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val) = 0; + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val, int width) = 0; + + virtual uint64_t readPC() = 0; + + virtual void setPC(uint64_t val) = 0; + + virtual uint64_t readNextPC() = 0; + + virtual void setNextPC(uint64_t val) = 0; + + virtual uint64_t readNextNPC() = 0; + + virtual void setNextNPC(uint64_t val) = 0; + + virtual MiscReg readMiscReg(int misc_reg) = 0; + + virtual MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) = 0; + + virtual Fault setMiscReg(int misc_reg, const MiscReg &val) = 0; + + virtual Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) = 0; + + // Also not necessarily the best location for these two. Hopefully will go + // away once we decide upon where st cond failures goes. + virtual unsigned readStCondFailures() = 0; + + virtual void setStCondFailures(unsigned sc_failures) = 0; + +#if FULL_SYSTEM + virtual bool inPalMode() = 0; +#endif + + // Only really makes sense for old CPU model. Still could be useful though. + virtual bool misspeculating() = 0; + +#if !FULL_SYSTEM + virtual IntReg getSyscallArg(int i) = 0; + + // used to shift args for indirect syscall + virtual void setSyscallArg(int i, IntReg val) = 0; + + virtual void setSyscallReturn(SyscallReturn return_value) = 0; + + // Same with st cond failures. + virtual Counter readFuncExeInst() = 0; + + // This function exits the thread context in the CPU and returns + // 1 if the CPU has no more active threads (meaning it's OK to exit); + // Used in syscall-emulation mode when a thread calls the exit syscall. + virtual int exit() { return 1; }; +#endif + + virtual void changeRegFileContext(RegFile::ContextParam param, + RegFile::ContextVal val) = 0; +}; + +/** + * ProxyThreadContext class that provides a way to implement a + * ThreadContext without having to derive from it. ThreadContext is an + * abstract class, so anything that derives from it and uses its + * interface will pay the overhead of virtual function calls. This + * class is created to enable a user-defined Thread object to be used + * wherever ThreadContexts are used, without paying the overhead of + * virtual function calls when it is used by itself. See + * simple_thread.hh for an example of this. + */ +template <class TC> +class ProxyThreadContext : public ThreadContext +{ + public: + ProxyThreadContext(TC *actual_tc) + { actualTC = actual_tc; } + + private: + TC *actualTC; + + public: + + BaseCPU *getCpuPtr() { return actualTC->getCpuPtr(); } + + void setCpuId(int id) { actualTC->setCpuId(id); } + + int readCpuId() { return actualTC->readCpuId(); } + +#if FULL_SYSTEM + System *getSystemPtr() { return actualTC->getSystemPtr(); } + + AlphaITB *getITBPtr() { return actualTC->getITBPtr(); } + + AlphaDTB *getDTBPtr() { return actualTC->getDTBPtr(); } + + Kernel::Statistics *getKernelStats() { return actualTC->getKernelStats(); } + + FunctionalPort *getPhysPort() { return actualTC->getPhysPort(); } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) { return actualTC->getVirtPort(tc); } + + void delVirtPort(VirtualPort *vp) { return actualTC->delVirtPort(vp); } +#else + TranslatingPort *getMemPort() { return actualTC->getMemPort(); } + + Process *getProcessPtr() { return actualTC->getProcessPtr(); } +#endif + + Status status() const { return actualTC->status(); } + + void setStatus(Status new_status) { actualTC->setStatus(new_status); } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1) { actualTC->activate(delay); } + + /// Set the status to Suspended. + void suspend() { actualTC->suspend(); } + + /// Set the status to Unallocated. + void deallocate() { actualTC->deallocate(); } + + /// Set the status to Halted. + void halt() { actualTC->halt(); } + +#if FULL_SYSTEM + void dumpFuncProfile() { actualTC->dumpFuncProfile(); } +#endif + + void takeOverFrom(ThreadContext *oldContext) + { actualTC->takeOverFrom(oldContext); } + + void regStats(const std::string &name) { actualTC->regStats(name); } + + void serialize(std::ostream &os) { actualTC->serialize(os); } + void unserialize(Checkpoint *cp, const std::string §ion) + { actualTC->unserialize(cp, section); } + +#if FULL_SYSTEM + EndQuiesceEvent *getQuiesceEvent() { return actualTC->getQuiesceEvent(); } + + Tick readLastActivate() { return actualTC->readLastActivate(); } + Tick readLastSuspend() { return actualTC->readLastSuspend(); } + + void profileClear() { return actualTC->profileClear(); } + void profileSample() { return actualTC->profileSample(); } +#endif + + int getThreadNum() { return actualTC->getThreadNum(); } + + // @todo: Do I need this? + MachInst getInst() { return actualTC->getInst(); } + + // @todo: Do I need this? + void copyArchRegs(ThreadContext *tc) { actualTC->copyArchRegs(tc); } + + void clearArchRegs() { actualTC->clearArchRegs(); } + + // + // New accessors for new decoder. + // + uint64_t readIntReg(int reg_idx) + { return actualTC->readIntReg(reg_idx); } + + FloatReg readFloatReg(int reg_idx, int width) + { return actualTC->readFloatReg(reg_idx, width); } + + FloatReg readFloatReg(int reg_idx) + { return actualTC->readFloatReg(reg_idx); } + + FloatRegBits readFloatRegBits(int reg_idx, int width) + { return actualTC->readFloatRegBits(reg_idx, width); } + + FloatRegBits readFloatRegBits(int reg_idx) + { return actualTC->readFloatRegBits(reg_idx); } + + void setIntReg(int reg_idx, uint64_t val) + { actualTC->setIntReg(reg_idx, val); } + + void setFloatReg(int reg_idx, FloatReg val, int width) + { actualTC->setFloatReg(reg_idx, val, width); } + + void setFloatReg(int reg_idx, FloatReg val) + { actualTC->setFloatReg(reg_idx, val); } + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width) + { actualTC->setFloatRegBits(reg_idx, val, width); } + + void setFloatRegBits(int reg_idx, FloatRegBits val) + { actualTC->setFloatRegBits(reg_idx, val); } + + uint64_t readPC() { return actualTC->readPC(); } + + void setPC(uint64_t val) { actualTC->setPC(val); } + + uint64_t readNextPC() { return actualTC->readNextPC(); } + + void setNextPC(uint64_t val) { actualTC->setNextPC(val); } + + uint64_t readNextNPC() { return actualTC->readNextNPC(); } + + void setNextNPC(uint64_t val) { actualTC->setNextNPC(val); } + + MiscReg readMiscReg(int misc_reg) + { return actualTC->readMiscReg(misc_reg); } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return actualTC->readMiscRegWithEffect(misc_reg, fault); } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { return actualTC->setMiscReg(misc_reg, val); } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { return actualTC->setMiscRegWithEffect(misc_reg, val); } + + unsigned readStCondFailures() + { return actualTC->readStCondFailures(); } + + void setStCondFailures(unsigned sc_failures) + { actualTC->setStCondFailures(sc_failures); } +#if FULL_SYSTEM + bool inPalMode() { return actualTC->inPalMode(); } +#endif + + // @todo: Fix this! + bool misspeculating() { return actualTC->misspeculating(); } + +#if !FULL_SYSTEM + IntReg getSyscallArg(int i) { return actualTC->getSyscallArg(i); } + + // used to shift args for indirect syscall + void setSyscallArg(int i, IntReg val) + { actualTC->setSyscallArg(i, val); } + + void setSyscallReturn(SyscallReturn return_value) + { actualTC->setSyscallReturn(return_value); } + + Counter readFuncExeInst() { return actualTC->readFuncExeInst(); } +#endif + + void changeRegFileContext(RegFile::ContextParam param, + RegFile::ContextVal val) + { + actualTC->changeRegFileContext(param, val); + } +}; + +#endif diff --git a/src/cpu/thread_state.cc b/src/cpu/thread_state.cc new file mode 100644 index 000000000..872678a41 --- /dev/null +++ b/src/cpu/thread_state.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#include "base/output.hh" +#include "cpu/profile.hh" +#include "cpu/thread_state.hh" + +#if FULL_SYSTEM +ThreadState::ThreadState(int _cpuId, int _tid) + : cpuId(_cpuId), tid(_tid), lastActivate(0), lastSuspend(0), + profile(NULL), profileNode(NULL), profilePC(0), quiesceEvent(NULL), + funcExeInst(0), storeCondFailures(0) +#else +ThreadState::ThreadState(int _cpuId, int _tid, Process *_process, + short _asid, MemObject *mem) + : cpuId(_cpuId), tid(_tid), lastActivate(0), lastSuspend(0), + process(_process), asid(_asid), + funcExeInst(0), storeCondFailures(0) +#endif +{ + numInst = 0; + numLoad = 0; +} + +#if FULL_SYSTEM + +void +ThreadState::profileClear() +{ + if (profile) + profile->clear(); +} + +void +ThreadState::profileSample() +{ + if (profile) + profile->sample(profileNode, profilePC); +} + +#endif diff --git a/src/cpu/thread_state.hh b/src/cpu/thread_state.hh new file mode 100644 index 000000000..cb1449ac5 --- /dev/null +++ b/src/cpu/thread_state.hh @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_THREAD_STATE_HH__ +#define __CPU_THREAD_STATE_HH__ + +#include "arch/isa_traits.hh" +#include "cpu/thread_context.hh" + +#if !FULL_SYSTEM +#include "mem/mem_object.hh" +#include "mem/translating_port.hh" +#include "sim/process.hh" +#endif + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +namespace Kernel { + class Statistics; +}; +#endif + +/** + * Struct for holding general thread state that is needed across CPU + * models. This includes things such as pointers to the process, + * memory, quiesce events, and certain stats. This can be expanded + * to hold more thread-specific stats within it. + */ +struct ThreadState { + typedef ThreadContext::Status Status; + +#if FULL_SYSTEM + ThreadState(int _cpuId, int _tid); +#else + ThreadState(int _cpuId, int _tid, Process *_process, + short _asid, MemObject *mem); +#endif + + void setCpuId(int id) { cpuId = id; } + + int readCpuId() { return cpuId; } + + void setTid(int id) { tid = id; } + + int readTid() { return tid; } + + Tick readLastActivate() { return lastActivate; } + + Tick readLastSuspend() { return lastSuspend; } + +#if FULL_SYSTEM + void dumpFuncProfile(); + + EndQuiesceEvent *getQuiesceEvent() { return quiesceEvent; } + + void profileClear(); + + void profileSample(); + + Kernel::Statistics *getKernelStats() { return kernelStats; } + + FunctionalPort *getPhysPort() { return physPort; } + + void setPhysPort(FunctionalPort *port) { physPort = port; } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) { return virtPort; } + + void setVirtPort(VirtualPort *port) { virtPort = port; } +#else + Process *getProcessPtr() { return process; } + + TranslatingPort *getMemPort() { return port; } + + void setMemPort(TranslatingPort *_port) { port = _port; } + + int getInstAsid() { return asid; } + int getDataAsid() { return asid; } +#endif + + /** Sets the current instruction being committed. */ + void setInst(TheISA::MachInst _inst) { inst = _inst; } + + /** Returns the current instruction being committed. */ + TheISA::MachInst getInst() { return inst; } + + /** Reads the number of instructions functionally executed and + * committed. + */ + Counter readFuncExeInst() { return funcExeInst; } + + /** Sets the total number of instructions functionally executed + * and committed. + */ + void setFuncExeInst(Counter new_val) { funcExeInst = new_val; } + + /** Returns the status of this thread. */ + Status status() const { return _status; } + + /** Sets the status of this thread. */ + void setStatus(Status new_status) { _status = new_status; } + + /** Number of instructions committed. */ + Counter numInst; + /** Stat for number instructions committed. */ + Stats::Scalar<> numInsts; + /** Stat for number of memory references. */ + Stats::Scalar<> numMemRefs; + + /** Number of simulated loads, used for tracking events based on + * the number of loads committed. + */ + Counter numLoad; + + /** The number of simulated loads committed prior to this run. */ + Counter startNumLoad; + + protected: + ThreadContext::Status _status; + + // ID of this context w.r.t. the System or Process object to which + // it belongs. For full-system mode, this is the system CPU ID. + int cpuId; + + // Index of hardware thread context on the CPU that this represents. + int tid; + + public: + /** Last time activate was called on this thread. */ + Tick lastActivate; + + /** Last time suspend was called on this thread. */ + Tick lastSuspend; + +#if FULL_SYSTEM + public: + FunctionProfile *profile; + ProfileNode *profileNode; + Addr profilePC; + EndQuiesceEvent *quiesceEvent; + + Kernel::Statistics *kernelStats; + protected: + /** A functional port outgoing only for functional accesses to physical + * addresses.*/ + FunctionalPort *physPort; + + /** A functional port, outgoing only, for functional accesse to virtual + * addresses. That doen't require execution context information */ + VirtualPort *virtPort; +#else + TranslatingPort *port; + + Process *process; + + // Address space ID. Note that this is used for TIMING cache + // simulation only; all functional memory accesses should use + // one of the FunctionalMemory pointers above. + short asid; +#endif + + /** Current instruction the thread is committing. Only set and + * used for DTB faults currently. + */ + TheISA::MachInst inst; + + public: + /** + * Temporary storage to pass the source address from copy_load to + * copy_store. + * @todo Remove this temporary when we have a better way to do it. + */ + Addr copySrcAddr; + /** + * Temp storage for the physical source address of a copy. + * @todo Remove this temporary when we have a better way to do it. + */ + Addr copySrcPhysAddr; + + /* + * number of executed instructions, for matching with syscall trace + * points in EIO files. + */ + Counter funcExeInst; + + // + // Count failed store conditionals so we can warn of apparent + // application deadlock situations. + unsigned storeCondFailures; +}; + +#endif // __CPU_THREAD_STATE_HH__ diff --git a/src/cpu/trace/opt_cpu.cc b/src/cpu/trace/opt_cpu.cc index 6cd23b0dd..996e89f01 100644 --- a/src/cpu/trace/opt_cpu.cc +++ b/src/cpu/trace/opt_cpu.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** @@ -174,7 +176,7 @@ OptCPU::tick() fprintf(stderr,"sys.cpu.misses %d #opt cache misses\n",misses); fprintf(stderr,"sys.cpu.hits %d #opt cache hits\n", hits); fprintf(stderr,"sys.cpu.accesses %d #opt cache acceses\n", references); - new SimExitEvent("Finshed Memory Trace"); + exitSimLoop("end of memory trace reached"); } void diff --git a/src/cpu/trace/opt_cpu.hh b/src/cpu/trace/opt_cpu.hh index f81691733..dfb122319 100644 --- a/src/cpu/trace/opt_cpu.hh +++ b/src/cpu/trace/opt_cpu.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/ibm_reader.cc b/src/cpu/trace/reader/ibm_reader.cc index 420101b63..87e13f307 100644 --- a/src/cpu/trace/reader/ibm_reader.cc +++ b/src/cpu/trace/reader/ibm_reader.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/ibm_reader.hh b/src/cpu/trace/reader/ibm_reader.hh index ce29206a2..a72f62e03 100644 --- a/src/cpu/trace/reader/ibm_reader.hh +++ b/src/cpu/trace/reader/ibm_reader.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/itx_reader.cc b/src/cpu/trace/reader/itx_reader.cc index 39ba27393..e4738eed8 100644 --- a/src/cpu/trace/reader/itx_reader.cc +++ b/src/cpu/trace/reader/itx_reader.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/itx_reader.hh b/src/cpu/trace/reader/itx_reader.hh index a16a08085..63a4c9ac9 100644 --- a/src/cpu/trace/reader/itx_reader.hh +++ b/src/cpu/trace/reader/itx_reader.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/m5_reader.cc b/src/cpu/trace/reader/m5_reader.cc index ce44672f2..8efcb022b 100644 --- a/src/cpu/trace/reader/m5_reader.cc +++ b/src/cpu/trace/reader/m5_reader.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/m5_reader.hh b/src/cpu/trace/reader/m5_reader.hh index 974a83ffa..5007bfd5b 100644 --- a/src/cpu/trace/reader/m5_reader.hh +++ b/src/cpu/trace/reader/m5_reader.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/mem_trace_reader.cc b/src/cpu/trace/reader/mem_trace_reader.cc index 769f0be27..5623f168a 100644 --- a/src/cpu/trace/reader/mem_trace_reader.cc +++ b/src/cpu/trace/reader/mem_trace_reader.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/reader/mem_trace_reader.hh b/src/cpu/trace/reader/mem_trace_reader.hh index b433cdbdd..628a3ecdc 100644 --- a/src/cpu/trace/reader/mem_trace_reader.hh +++ b/src/cpu/trace/reader/mem_trace_reader.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/cpu/trace/trace_cpu.cc b/src/cpu/trace/trace_cpu.cc index 20d0a567f..3c9da4849 100644 --- a/src/cpu/trace/trace_cpu.cc +++ b/src/cpu/trace/trace_cpu.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** @@ -106,7 +108,7 @@ TraceCPU::tick() if (!nextReq) { // No more requests to send. Finish trailing events and exit. if (mainEventQueue.empty()) { - new SimExitEvent("Finshed Memory Trace"); + exitSimLoop("end of memory trace reached"); } else { tickEvent.schedule(mainEventQueue.nextEventTime() + cycles(1)); } diff --git a/src/cpu/trace/trace_cpu.hh b/src/cpu/trace/trace_cpu.hh index 69ca35321..9c96d71d5 100644 --- a/src/cpu/trace/trace_cpu.hh +++ b/src/cpu/trace/trace_cpu.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Erik Hallnor */ /** diff --git a/src/dev/alpha_access.h b/src/dev/alpha_access.h index 5a1df6f39..4adeaf84b 100644 --- a/src/dev/alpha_access.h +++ b/src/dev/alpha_access.h @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __ALPHA_ACCESS_H__ diff --git a/src/dev/alpha_console.cc b/src/dev/alpha_console.cc index 0b4bb048c..181bbf934 100644 --- a/src/dev/alpha_console.cc +++ b/src/dev/alpha_console.cc @@ -24,6 +24,11 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + * Steve Reinhardt + * Erik Hallnor */ /** @file @@ -38,7 +43,7 @@ #include "base/str.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "dev/alpha_console.hh" #include "dev/platform.hh" #include "dev/simconsole.hh" @@ -98,7 +103,6 @@ AlphaConsole::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - pkt->time += pioDelay; Addr daddr = pkt->getAddr() - pioAddr; pkt->allocate(); @@ -191,8 +195,6 @@ AlphaConsole::read(Packet *pkt) Tick AlphaConsole::write(Packet *pkt) { - pkt->time += pioDelay; - assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; diff --git a/src/dev/alpha_console.hh b/src/dev/alpha_console.hh index 34d21e15c..b6360d40f 100644 --- a/src/dev/alpha_console.hh +++ b/src/dev/alpha_console.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /** @file diff --git a/src/dev/baddev.cc b/src/dev/baddev.cc index 66fbd8a86..e4297be19 100644 --- a/src/dev/baddev.cc +++ b/src/dev/baddev.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/baddev.hh b/src/dev/baddev.hh index 35ae0382a..d7d778af4 100644 --- a/src/dev/baddev.hh +++ b/src/dev/baddev.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/disk_image.cc b/src/dev/disk_image.cc index 185a8b083..f70d2ccdb 100644 --- a/src/dev/disk_image.cc +++ b/src/dev/disk_image.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /** @file diff --git a/src/dev/disk_image.hh b/src/dev/disk_image.hh index 648aa20c6..45d5af649 100644 --- a/src/dev/disk_image.hh +++ b/src/dev/disk_image.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /** @file diff --git a/src/dev/etherbus.cc b/src/dev/etherbus.cc index 906e324d3..348bb818a 100644 --- a/src/dev/etherbus.cc +++ b/src/dev/etherbus.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherbus.hh b/src/dev/etherbus.hh index 4a364abd8..2ff1d0a5e 100644 --- a/src/dev/etherbus.hh +++ b/src/dev/etherbus.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherdump.cc b/src/dev/etherdump.cc index cb5f0b70e..0c986cc21 100644 --- a/src/dev/etherdump.cc +++ b/src/dev/etherdump.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherdump.hh b/src/dev/etherdump.hh index 8bba073fe..f3080f341 100644 --- a/src/dev/etherdump.hh +++ b/src/dev/etherdump.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherint.cc b/src/dev/etherint.cc index 8fb047373..3f81591e9 100644 --- a/src/dev/etherint.cc +++ b/src/dev/etherint.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include "dev/etherint.hh" diff --git a/src/dev/etherint.hh b/src/dev/etherint.hh index 1f641fadb..dfc224ecc 100644 --- a/src/dev/etherint.hh +++ b/src/dev/etherint.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherlink.cc b/src/dev/etherlink.cc index 5b6531c2e..cd3812270 100644 --- a/src/dev/etherlink.cc +++ b/src/dev/etherlink.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ron Dreslinski */ /* @file diff --git a/src/dev/etherlink.hh b/src/dev/etherlink.hh index 570444e1b..bb2854810 100644 --- a/src/dev/etherlink.hh +++ b/src/dev/etherlink.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/etherpkt.cc b/src/dev/etherpkt.cc index 85e18e981..5c552b4bd 100644 --- a/src/dev/etherpkt.cc +++ b/src/dev/etherpkt.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/dev/etherpkt.hh b/src/dev/etherpkt.hh index 01741b3d5..80c7baff7 100644 --- a/src/dev/etherpkt.hh +++ b/src/dev/etherpkt.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu */ /* @file diff --git a/src/dev/ethertap.cc b/src/dev/ethertap.cc index b5abb1d62..2d72383c5 100644 --- a/src/dev/ethertap.cc +++ b/src/dev/ethertap.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/ethertap.hh b/src/dev/ethertap.hh index 40ce6af0b..f64ed7187 100644 --- a/src/dev/ethertap.hh +++ b/src/dev/ethertap.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/ide_ctrl.cc b/src/dev/ide_ctrl.cc index eb03d5db8..63435e87c 100644 --- a/src/dev/ide_ctrl.cc +++ b/src/dev/ide_ctrl.cc @@ -24,6 +24,10 @@ * 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. + * + * Authors: Andrew Schultz + * Ali Saidi + * Miguel Serrano */ #include <cstddef> @@ -430,7 +434,6 @@ IdeController::read(Packet *pkt) IdeRegType reg_type; int disk; - pkt->time += pioDelay; pkt->allocate(); if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) panic("Bad IDE read size: %d\n", pkt->getSize()); @@ -518,8 +521,6 @@ IdeController::write(Packet *pkt) int disk; uint8_t oldVal, newVal; - pkt->time += pioDelay; - parseAddr(pkt->getAddr(), offset, channel, reg_type); if (!io_enabled) { diff --git a/src/dev/ide_ctrl.hh b/src/dev/ide_ctrl.hh index dda2cbb66..1d30c8b31 100644 --- a/src/dev/ide_ctrl.hh +++ b/src/dev/ide_ctrl.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Andrew Schultz + * Miguel Serrano */ /** @file diff --git a/src/dev/ide_disk.cc b/src/dev/ide_disk.cc index 909f35c60..dc78021f8 100644 --- a/src/dev/ide_disk.cc +++ b/src/dev/ide_disk.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Andrew Schultz + * Ali Saidi */ /** @file diff --git a/src/dev/ide_disk.hh b/src/dev/ide_disk.hh index 5379e5e73..fb0614d4d 100644 --- a/src/dev/ide_disk.hh +++ b/src/dev/ide_disk.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Andrew Schultz */ /** @file diff --git a/src/dev/io_device.cc b/src/dev/io_device.cc index 87fdbb765..e769ef037 100644 --- a/src/dev/io_device.cc +++ b/src/dev/io_device.cc @@ -24,8 +24,12 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ +#include "base/trace.hh" #include "dev/io_device.hh" #include "sim/builder.hh" @@ -55,15 +59,17 @@ PioPort::getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) } -Packet * +void PioPort::recvRetry() { - Packet* pkt = transmitList.front(); - transmitList.pop_front(); - return pkt; + bool result = true; + while (result && transmitList.size()) { + result = Port::sendTiming(transmitList.front()); + if (result) + transmitList.pop_front(); + } } - void PioPort::SendEvent::process() { @@ -74,13 +80,24 @@ PioPort::SendEvent::process() } + bool PioPort::recvTiming(Packet *pkt) { - device->recvAtomic(pkt); - // turn packet around to go back to requester - pkt->makeTimingResponse(); - sendTiming(pkt, pkt->time - pkt->req->getTime()); + if (pkt->result == Packet::Nacked) { + pkt->reinitNacked(); + if (transmitList.size()) { + transmitList.push_front(pkt); + } else { + if (!Port::sendTiming(pkt)) + transmitList.push_front(pkt); + } + } else { + Tick latency = device->recvAtomic(pkt); + // turn packet around to go back to requester + pkt->makeTimingResponse(); + sendTiming(pkt, latency); + } return true; } @@ -114,18 +131,35 @@ DmaPort::DmaPort(DmaDevice *dev, Platform *p) bool DmaPort::recvTiming(Packet *pkt) { - if (pkt->senderState) { + + + if (pkt->result == Packet::Nacked) { + DPRINTF(DMA, "Received nacked Pkt %#x with State: %#x Addr: %#x\n", + pkt, pkt->senderState, pkt->getAddr()); + pkt->reinitNacked(); + sendDma(pkt, true); + } else if (pkt->senderState) { DmaReqState *state; + DPRINTF(DMA, "Received response Pkt %#x with State: %#x Addr: %#x\n", + pkt, pkt->senderState, pkt->getAddr()); state = dynamic_cast<DmaReqState*>(pkt->senderState); - state->completionEvent->schedule(pkt->time - pkt->req->getTime()); + pendingCount--; + + assert(pendingCount >= 0); + assert(state); + + state->numBytes += pkt->req->getSize(); + if (state->totBytes == state->numBytes) { + state->completionEvent->process(); + delete state; + } delete pkt->req; delete pkt; } else { - delete pkt->req; - delete pkt; + panic("Got packet without sender state... huh?\n"); } - return Packet::Success; + return true; } DmaDevice::DmaDevice(Params *p) @@ -133,20 +167,21 @@ DmaDevice::DmaDevice(Params *p) { } void -DmaPort::SendEvent::process() -{ - if (port->Port::sendTiming(packet)) - return; - - port->transmitList.push_back(packet); -} - -Packet * DmaPort::recvRetry() { Packet* pkt = transmitList.front(); - transmitList.pop_front(); - return pkt; + bool result = true; + while (result && transmitList.size()) { + DPRINTF(DMA, "Retry on Packet %#x with senderState: %#x\n", + pkt, pkt->senderState); + result = sendTiming(pkt); + if (result) { + DPRINTF(DMA, "-- Done\n"); + transmitList.pop_front(); + } else { + DPRINTF(DMA, "-- Failed, queued\n"); + } + } } @@ -156,27 +191,19 @@ DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, { assert(event); - int prevSize = 0; + DmaReqState *reqState = new DmaReqState(event, this, size); for (ChunkGenerator gen(addr, size, peerBlockSize()); !gen.done(); gen.next()) { - Request *req = new Request(false); - req->setPaddr(gen.addr()); - req->setSize(gen.size()); - req->setTime(curTick); + Request *req = new Request(gen.addr(), gen.size(), 0); Packet *pkt = new Packet(req, cmd, Packet::Broadcast); // Increment the data pointer on a write if (data) - pkt->dataStatic(data + prevSize) ; + pkt->dataStatic(data + gen.complete()); - prevSize += gen.size(); + pkt->senderState = reqState; - // Set the last bit of the dma as the final packet for this dma - // and set it's completion event. - if (prevSize == size) { - pkt->senderState = new DmaReqState(event, true); - } assert(pendingCount >= 0); pendingCount++; sendDma(pkt); @@ -185,28 +212,40 @@ DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, void -DmaPort::sendDma(Packet *pkt) +DmaPort::sendDma(Packet *pkt, bool front) { // some kind of selction between access methods // more work is going to have to be done to make // switching actually work /* MemState state = device->platform->system->memState; - if (state == Timing) { - if (!sendTiming(pkt)) - transmitList.push_back(&packet); - } else if (state == Atomic) {*/ + if (state == Timing) { */ + DPRINTF(DMA, "Attempting to send Packet %#x with addr: %#x\n", + pkt, pkt->getAddr()); + if (transmitList.size() || !sendTiming(pkt)) { + if (front) + transmitList.push_front(pkt); + else + transmitList.push_back(pkt); + DPRINTF(DMA, "-- Failed: queued\n"); + } else { + DPRINTF(DMA, "-- Done\n"); + } + /* } else if (state == Atomic) { sendAtomic(pkt); if (pkt->senderState) { DmaReqState *state = dynamic_cast<DmaReqState*>(pkt->senderState); - state->completionEvent->schedule(curTick + (pkt->time - pkt->req->getTime()) +1); + assert(state); + state->completionEvent->schedule(curTick + (pkt->time - + pkt->req->getTime()) +1); + delete state; } pendingCount--; assert(pendingCount >= 0); delete pkt->req; delete pkt; -/* } else if (state == Functional) { + } else if (state == Functional) { sendFunctional(pkt); // Is this correct??? completionEvent->schedule(pkt->req->responseTime - pkt->req->requestTime); diff --git a/src/dev/io_device.hh b/src/dev/io_device.hh index 74730ad92..a2b61c7f4 100644 --- a/src/dev/io_device.hh +++ b/src/dev/io_device.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __DEV_IO_DEVICE_HH__ @@ -105,8 +108,9 @@ class PioPort : public Port void sendTiming(Packet *pkt, Tick time) { new PioPort::SendEvent(this, pkt, time); } - /** This function pops the last element off the transmit list and sends it.*/ - virtual Packet *recvRetry(); + /** This function is notification that the device should attempt to send a + * packet again. */ + virtual void recvRetry(); public: PioPort(PioDevice *dev, Platform *p); @@ -115,18 +119,29 @@ class PioPort : public Port }; -struct DmaReqState : public Packet::SenderState -{ - Event *completionEvent; - bool final; - DmaReqState(Event *ce, bool f) - : completionEvent(ce), final(f) - {} -}; - class DmaPort : public Port { protected: + struct DmaReqState : public Packet::SenderState + { + /** Event to call on the device when this transaction (all packets) + * complete. */ + Event *completionEvent; + + /** Where we came from for some sanity checking. */ + Port *outPort; + + /** Total number of bytes that this transaction involves. */ + Addr totBytes; + + /** Number of bytes that have been acked for this transaction. */ + Addr numBytes; + + DmaReqState(Event *ce, Port *p, Addr tb) + : completionEvent(ce), outPort(p), totBytes(tb), numBytes(0) + {} + }; + DmaDevice *device; std::list<Packet*> transmitList; @@ -146,29 +161,12 @@ class DmaPort : public Port virtual void recvStatusChange(Status status) { ; } - virtual Packet *recvRetry() ; + virtual void recvRetry() ; virtual void getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) { resp.clear(); snoop.clear(); } - class SendEvent : public Event - { - DmaPort *port; - Packet *packet; - - SendEvent(PioPort *p, Packet *pkt, Tick t) - : Event(&mainEventQueue), packet(pkt) - { schedule(curTick + t); } - - virtual void process(); - - virtual const char *description() - { return "Future scheduled sendTiming event"; } - - friend class DmaPort; - }; - - void sendDma(Packet *pkt); + void sendDma(Packet *pkt, bool front = false); public: DmaPort(DmaDevice *dev, Platform *p); @@ -178,8 +176,6 @@ class DmaPort : public Port bool dmaPending() { return pendingCount > 0; } - friend class DmaPort::SendEvent; - }; /** @@ -250,7 +246,7 @@ class PioDevice : public MemObject virtual void init(); - virtual Port *getPort(const std::string &if_name) + virtual Port *getPort(const std::string &if_name, int idx = -1) { if (if_name == "pio") { if (pioPort != NULL) @@ -312,7 +308,7 @@ class DmaDevice : public PioDevice bool dmaPending() { return dmaPort->dmaPending(); } - virtual Port *getPort(const std::string &if_name) + virtual Port *getPort(const std::string &if_name, int idx = -1) { if (if_name == "pio") { if (pioPort != NULL) diff --git a/src/dev/isa_fake.cc b/src/dev/isa_fake.cc index c303ffc30..9622f015c 100644 --- a/src/dev/isa_fake.cc +++ b/src/dev/isa_fake.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Miguel Serrano + * Ali Saidi */ /** @file @@ -54,8 +57,6 @@ IsaFake::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - pkt->time += pioDelay; - DPRINTF(Tsunami, "read va=%#x size=%d\n", pkt->getAddr(), pkt->getSize()); switch (pkt->getSize()) { @@ -80,7 +81,6 @@ IsaFake::read(Packet *pkt) Tick IsaFake::write(Packet *pkt) { - pkt->time += pioDelay; DPRINTF(Tsunami, "write - va=%#x size=%d \n", pkt->getAddr(), pkt->getSize()); pkt->result = Packet::Success; return pioDelay; diff --git a/src/dev/isa_fake.hh b/src/dev/isa_fake.hh index 65d44f6a5..a7a469e17 100644 --- a/src/dev/isa_fake.hh +++ b/src/dev/isa_fake.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Miguel Serrano + * Ali Saidi */ /** @file diff --git a/src/dev/ns_gige.cc b/src/dev/ns_gige.cc index 963675847..360fe8c9b 100644 --- a/src/dev/ns_gige.cc +++ b/src/dev/ns_gige.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu */ /** @file @@ -35,7 +38,7 @@ #include "arch/alpha/ev5.hh" #include "base/inet.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "dev/etherlink.hh" #include "dev/ns_gige.hh" #include "dev/pciconfigall.hh" @@ -492,7 +495,6 @@ NSGigE::read(Packet *pkt) { assert(ioEnable); - pkt->time += pioDelay; pkt->allocate(); //The mask is to give you only the offset into the device register file @@ -728,8 +730,6 @@ NSGigE::write(Packet *pkt) DPRINTF(EthernetPIO, "write da=%#x pa=%#x size=%d\n", daddr, pkt->getAddr(), pkt->getSize()); - pkt->time += pioDelay; - if (daddr > LAST && daddr <= RESERVED) { panic("Accessing reserved register"); } else if (daddr > RESERVED && daddr <= 0x3FC) { diff --git a/src/dev/ns_gige.hh b/src/dev/ns_gige.hh index 51520fd00..2f47026f3 100644 --- a/src/dev/ns_gige.hh +++ b/src/dev/ns_gige.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu */ /** @file diff --git a/src/dev/ns_gige_reg.h b/src/dev/ns_gige_reg.h index 5f6fa2cc5..80ac5800d 100644 --- a/src/dev/ns_gige_reg.h +++ b/src/dev/ns_gige_reg.h @@ -24,6 +24,8 @@ * 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. + * + * Authors: Lisa Hsu */ /** @file diff --git a/src/dev/pciconfigall.cc b/src/dev/pciconfigall.cc index 14a28e86d..785774ff4 100644 --- a/src/dev/pciconfigall.cc +++ b/src/dev/pciconfigall.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Andrew Schultz + * Ali Saidi */ /* @file @@ -99,7 +102,6 @@ PciConfigAll::read(Packet *pkt) int func = (daddr >> 8) & 0x7; int reg = daddr & 0xFF; - pkt->time += pioDelay; pkt->allocate(); DPRINTF(PciConfigAll, "read va=%#x da=%#x size=%d\n", pkt->getAddr(), daddr, @@ -134,8 +136,6 @@ PciConfigAll::read(Packet *pkt) Tick PciConfigAll::write(Packet *pkt) { - pkt->time += pioDelay; - assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); assert(pkt->getSize() == sizeof(uint8_t) || pkt->getSize() == sizeof(uint16_t) || diff --git a/src/dev/pciconfigall.hh b/src/dev/pciconfigall.hh index df4eae448..e60fd949b 100644 --- a/src/dev/pciconfigall.hh +++ b/src/dev/pciconfigall.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Andrew Schultz + * Ali Saidi */ /* diff --git a/src/dev/pcidev.cc b/src/dev/pcidev.cc index 76392ccfe..f8db2efbc 100644 --- a/src/dev/pcidev.cc +++ b/src/dev/pcidev.cc @@ -24,6 +24,10 @@ * 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. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano */ /* @file diff --git a/src/dev/pcidev.hh b/src/dev/pcidev.hh index fc4773908..92786427b 100644 --- a/src/dev/pcidev.hh +++ b/src/dev/pcidev.hh @@ -24,6 +24,10 @@ * 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. + * + * Authors: Ali Saidi + * Andrew Schultz + * Nathan Binkert */ /* @file diff --git a/src/dev/pcireg.h b/src/dev/pcireg.h index 9d2737c20..0aa4ba8ef 100644 --- a/src/dev/pcireg.h +++ b/src/dev/pcireg.h @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Miguel Serrano */ /* @file diff --git a/src/dev/pitreg.h b/src/dev/pitreg.h index 5fe1cf03f..d42925a41 100644 --- a/src/dev/pitreg.h +++ b/src/dev/pitreg.h @@ -24,6 +24,8 @@ * 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. + * + * Authors: Miguel Serrano */ /* @file diff --git a/src/dev/pktfifo.cc b/src/dev/pktfifo.cc index 922a66912..37f7ff680 100644 --- a/src/dev/pktfifo.cc +++ b/src/dev/pktfifo.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include "base/misc.hh" diff --git a/src/dev/pktfifo.hh b/src/dev/pktfifo.hh index 336da22d8..45157ba41 100644 --- a/src/dev/pktfifo.hh +++ b/src/dev/pktfifo.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __DEV_PKTFIFO_HH__ diff --git a/src/dev/platform.cc b/src/dev/platform.cc index 9d10e0828..ed021e3b6 100644 --- a/src/dev/platform.cc +++ b/src/dev/platform.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ #include "dev/platform.hh" diff --git a/src/dev/platform.hh b/src/dev/platform.hh index f149ca2fb..0e6f4ba4a 100644 --- a/src/dev/platform.hh +++ b/src/dev/platform.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Andrew Schultz + * Nathan Binkert */ /** diff --git a/src/dev/rtcreg.h b/src/dev/rtcreg.h index 5025a0e95..37255777b 100644 --- a/src/dev/rtcreg.h +++ b/src/dev/rtcreg.h @@ -24,6 +24,10 @@ * 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. + * + * Authors: Ali Saidi + * Miguel Serrano + * Nathan Binkert */ #define RTC_SEC 0x00 diff --git a/src/dev/simconsole.cc b/src/dev/simconsole.cc index c3e4f554a..77aafd9fa 100644 --- a/src/dev/simconsole.cc +++ b/src/dev/simconsole.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ /* @file @@ -202,32 +205,31 @@ SimConsole::write(const uint8_t *buf, size_t len) #define RECEIVE_NONE (ULL(2) << 62) #define RECEIVE_ERROR (ULL(3) << 62) -bool -SimConsole::in(uint8_t &c) +uint8_t +SimConsole::in() { - bool empty, ret; + bool empty; + uint8_t c; empty = rxbuf.empty(); - ret = !empty; - if (!empty) { - rxbuf.read((char *)&c, 1); - empty = rxbuf.empty(); - } + assert(!empty); + rxbuf.read((char *)&c, 1); + empty = rxbuf.empty(); - DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d, return: %d\n", - isprint(c) ? c : ' ', c, !empty, ret); - return ret; + DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d\n", + isprint(c) ? c : ' ', c, !empty); + + return c; } uint64_t SimConsole::console_in() { - uint8_t c; uint64_t value; - if (in(c)) { - value = RECEIVE_SUCCESS | c; + if (dataAvailable()) { + value = RECEIVE_SUCCESS | in(); if (!rxbuf.empty()) value |= MORE_PENDING; } else { diff --git a/src/dev/simconsole.hh b/src/dev/simconsole.hh index cf0641f9e..ec99c6028 100644 --- a/src/dev/simconsole.hh +++ b/src/dev/simconsole.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ /* @file @@ -102,7 +105,7 @@ class SimConsole : public SimObject // OS interface // Get a character from the console. - bool in(uint8_t &value); + uint8_t in(); // get a character from the console in the console specific format // corresponds to GETC: diff --git a/src/dev/simple_disk.cc b/src/dev/simple_disk.cc index 9eee4668c..e29bfa726 100644 --- a/src/dev/simple_disk.cc +++ b/src/dev/simple_disk.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/simple_disk.hh b/src/dev/simple_disk.hh index 19967f208..9f588cb95 100644 --- a/src/dev/simple_disk.hh +++ b/src/dev/simple_disk.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /* @file diff --git a/src/dev/sinic.cc b/src/dev/sinic.cc index 31fbc49aa..a0223733b 100644 --- a/src/dev/sinic.cc +++ b/src/dev/sinic.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <deque> @@ -31,7 +33,7 @@ #include <string> #include "base/inet.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/intr_control.hh" #include "dev/etherlink.hh" #include "dev/sinic.hh" @@ -322,7 +324,6 @@ Device::read(Packet *pkt) Addr index = daddr >> Regs::VirtualShift; Addr raddr = daddr & Regs::VirtualMask; - pkt->time += pioDelay; pkt->allocate(); if (!regValid(raddr)) @@ -410,8 +411,6 @@ Device::write(Packet *pkt) Addr index = daddr >> Regs::VirtualShift; Addr raddr = daddr & Regs::VirtualMask; - pkt->time += pioDelay; - if (!regValid(raddr)) panic("invalid register: cpu=%d, da=%#x pa=%#x size=%d", cpu, daddr, pkt->getAddr(), pkt->getSize()); diff --git a/src/dev/sinic.hh b/src/dev/sinic.hh index 1bb4c77e0..f6c229039 100644 --- a/src/dev/sinic.hh +++ b/src/dev/sinic.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __DEV_SINIC_HH__ diff --git a/src/dev/sinicreg.hh b/src/dev/sinicreg.hh index d41eb5b16..de4188145 100644 --- a/src/dev/sinicreg.hh +++ b/src/dev/sinicreg.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __DEV_SINICREG_HH__ diff --git a/src/dev/tsunami.cc b/src/dev/tsunami.cc index ed011531d..c9e15581d 100644 --- a/src/dev/tsunami.cc +++ b/src/dev/tsunami.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/tsunami.hh b/src/dev/tsunami.hh index 668c82674..13fc4417c 100644 --- a/src/dev/tsunami.hh +++ b/src/dev/tsunami.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** diff --git a/src/dev/tsunami_cchip.cc b/src/dev/tsunami_cchip.cc index 146321b9f..3feb7439f 100644 --- a/src/dev/tsunami_cchip.cc +++ b/src/dev/tsunami_cchip.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Ron Dreslinski */ /** @file @@ -40,7 +43,7 @@ #include "dev/tsunamireg.h" #include "dev/tsunami.hh" #include "mem/port.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/intr_control.hh" #include "sim/builder.hh" #include "sim/system.hh" @@ -76,7 +79,6 @@ TsunamiCChip::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - pkt->time += pioDelay; Addr regnum = (pkt->getAddr() - pioAddr) >> 6; Addr daddr = (pkt->getAddr() - pioAddr); @@ -182,9 +184,6 @@ TsunamiCChip::read(Packet *pkt) Tick TsunamiCChip::write(Packet *pkt) { - pkt->time += pioDelay; - - assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; Addr regnum = (pkt->getAddr() - pioAddr) >> 6 ; @@ -369,7 +368,7 @@ TsunamiCChip::write(Packet *pkt) void TsunamiCChip::clearIPI(uint64_t ipintr) { - int numcpus = tsunami->intrctrl->cpu->system->execContexts.size(); + int numcpus = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(numcpus <= Tsunami::Max_CPUs); if (ipintr) { @@ -395,7 +394,7 @@ TsunamiCChip::clearIPI(uint64_t ipintr) void TsunamiCChip::clearITI(uint64_t itintr) { - int numcpus = tsunami->intrctrl->cpu->system->execContexts.size(); + int numcpus = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(numcpus <= Tsunami::Max_CPUs); if (itintr) { @@ -415,7 +414,7 @@ TsunamiCChip::clearITI(uint64_t itintr) void TsunamiCChip::reqIPI(uint64_t ipreq) { - int numcpus = tsunami->intrctrl->cpu->system->execContexts.size(); + int numcpus = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(numcpus <= Tsunami::Max_CPUs); if (ipreq) { @@ -442,7 +441,7 @@ TsunamiCChip::reqIPI(uint64_t ipreq) void TsunamiCChip::postRTC() { - int size = tsunami->intrctrl->cpu->system->execContexts.size(); + int size = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(size <= Tsunami::Max_CPUs); for (int i = 0; i < size; i++) { @@ -460,7 +459,7 @@ void TsunamiCChip::postDRIR(uint32_t interrupt) { uint64_t bitvector = ULL(1) << interrupt; - uint64_t size = tsunami->intrctrl->cpu->system->execContexts.size(); + uint64_t size = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(size <= Tsunami::Max_CPUs); drir |= bitvector; @@ -478,7 +477,7 @@ void TsunamiCChip::clearDRIR(uint32_t interrupt) { uint64_t bitvector = ULL(1) << interrupt; - uint64_t size = tsunami->intrctrl->cpu->system->execContexts.size(); + uint64_t size = tsunami->intrctrl->cpu->system->threadContexts.size(); assert(size <= Tsunami::Max_CPUs); if (drir & bitvector) diff --git a/src/dev/tsunami_cchip.hh b/src/dev/tsunami_cchip.hh index 3dde0131e..ef43f621f 100644 --- a/src/dev/tsunami_cchip.hh +++ b/src/dev/tsunami_cchip.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/tsunami_io.cc b/src/dev/tsunami_io.cc index 7c649a84a..e3da10eb5 100644 --- a/src/dev/tsunami_io.cc +++ b/src/dev/tsunami_io.cc @@ -24,6 +24,10 @@ * 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. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano */ /** @file @@ -118,25 +122,27 @@ TsunamiIO::RTC::writeData(const uint8_t data) } } -void -TsunamiIO::RTC::readData(uint8_t *data) +uint8_t +TsunamiIO::RTC::readData() { if (addr < RTC_STAT_REGA) - *data = clock_data[addr]; + return clock_data[addr]; else { switch (addr) { case RTC_STAT_REGA: // toggle UIP bit for linux stat_regA ^= RTCA_UIP; - *data = stat_regA; + return stat_regA; break; case RTC_STAT_REGB: - *data = stat_regB; + return stat_regB; break; case RTC_STAT_REGC: case RTC_STAT_REGD: - *data = 0x00; + return 0x00; break; + default: + panic("Shouldn't be here"); } } } @@ -263,31 +269,35 @@ TsunamiIO::PITimer::Counter::latchCount() } } -void -TsunamiIO::PITimer::Counter::read(uint8_t *data) +uint8_t +TsunamiIO::PITimer::Counter::read() { if (latch_on) { switch (read_byte) { case LSB: read_byte = MSB; - *data = (uint8_t)latched_count; + return (uint8_t)latched_count; break; case MSB: read_byte = LSB; latch_on = false; - *data = latched_count >> 8; + return latched_count >> 8; break; + default: + panic("Shouldn't be here"); } } else { switch (read_byte) { case LSB: read_byte = MSB; - *data = (uint8_t)count; + return (uint8_t)count; break; case MSB: read_byte = LSB; - *data = count >> 8; + return count >> 8; break; + default: + panic("Shouldn't be here"); } } } @@ -441,7 +451,6 @@ TsunamiIO::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - pkt->time += pioDelay; Addr daddr = pkt->getAddr() - pioAddr; DPRINTF(Tsunami, "io read va=%#x size=%d IOPorrt=%#x\n", pkt->getAddr(), @@ -469,16 +478,16 @@ TsunamiIO::read(Packet *pkt) pkt->set(0x00); break; case TSDEV_TMR0_DATA: - pitimer.counter0.read(pkt->getPtr<uint8_t>()); + pkt->set(pitimer.counter0.read()); break; case TSDEV_TMR1_DATA: - pitimer.counter1.read(pkt->getPtr<uint8_t>()); + pkt->set(pitimer.counter1.read()); break; case TSDEV_TMR2_DATA: - pitimer.counter2.read(pkt->getPtr<uint8_t>()); + pkt->set(pitimer.counter2.read()); break; case TSDEV_RTC_DATA: - rtc.readData(pkt->getPtr<uint8_t>()); + pkt->set(rtc.readData()); break; case TSDEV_CTRL_PORTB: if (pitimer.counter2.outputHigh()) @@ -505,8 +514,6 @@ TsunamiIO::read(Packet *pkt) Tick TsunamiIO::write(Packet *pkt) { - pkt->time += pioDelay; - assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = pkt->getAddr() - pioAddr; diff --git a/src/dev/tsunami_io.hh b/src/dev/tsunami_io.hh index 4e4fb2036..ee25bbdfd 100644 --- a/src/dev/tsunami_io.hh +++ b/src/dev/tsunami_io.hh @@ -24,6 +24,10 @@ * 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. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano */ /** @file @@ -118,7 +122,7 @@ class TsunamiIO : public BasicPioDevice void writeData(const uint8_t data); /** RTC read data */ - void readData(uint8_t *data); + uint8_t readData(); /** * Serialize this object to the given output stream. @@ -207,7 +211,7 @@ class TsunamiIO : public BasicPioDevice void setBCD(int bcd_val); /** Read a count byte */ - void read(uint8_t *data); + uint8_t read(); /** Write a count byte */ void write(const uint8_t data); diff --git a/src/dev/tsunami_pchip.cc b/src/dev/tsunami_pchip.cc index c430ca0a0..a376b908d 100644 --- a/src/dev/tsunami_pchip.cc +++ b/src/dev/tsunami_pchip.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Andrew Schultz */ /** @file @@ -70,8 +73,6 @@ TsunamiPChip::read(Packet *pkt) assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); - - pkt->time += pioDelay; pkt->allocate(); Addr daddr = (pkt->getAddr() - pioAddr) >> 6;; assert(pkt->getSize() == sizeof(uint64_t)); @@ -151,8 +152,6 @@ TsunamiPChip::read(Packet *pkt) Tick TsunamiPChip::write(Packet *pkt) { - pkt->time += pioDelay; - assert(pkt->result == Packet::Unknown); assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); Addr daddr = (pkt->getAddr() - pioAddr) >> 6; diff --git a/src/dev/tsunami_pchip.hh b/src/dev/tsunami_pchip.hh index bb84339c9..9f80f7d68 100644 --- a/src/dev/tsunami_pchip.hh +++ b/src/dev/tsunami_pchip.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/tsunamireg.h b/src/dev/tsunamireg.h index a10259900..d603972be 100644 --- a/src/dev/tsunamireg.h +++ b/src/dev/tsunamireg.h @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/uart.cc b/src/dev/uart.cc index 4a9f2b505..f769b720b 100644 --- a/src/dev/uart.cc +++ b/src/dev/uart.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/uart.hh b/src/dev/uart.hh index 2dd15d9b8..f0ae0854c 100644 --- a/src/dev/uart.hh +++ b/src/dev/uart.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/dev/uart8250.cc b/src/dev/uart8250.cc index 8e6843841..b92527b5a 100644 --- a/src/dev/uart8250.cc +++ b/src/dev/uart8250.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file @@ -114,7 +116,6 @@ Uart8250::read(Packet *pkt) assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); assert(pkt->getSize() == 1); - pkt->time += pioDelay; Addr daddr = pkt->getAddr() - pioAddr; pkt->allocate(); @@ -124,7 +125,7 @@ Uart8250::read(Packet *pkt) case 0x0: if (!(LCR & 0x80)) { // read byte if (cons->dataAvailable()) - cons->in(*pkt->getPtr<uint8_t>()); + pkt->set(cons->in()); else { pkt->set((uint8_t)0); // A limited amount of these are ok. @@ -198,7 +199,6 @@ Uart8250::write(Packet *pkt) assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); assert(pkt->getSize() == 1); - pkt->time += pioDelay; Addr daddr = pkt->getAddr() - pioAddr; DPRINTF(Uart, " write register %#x value %#x\n", daddr, pkt->get<uint8_t>()); diff --git a/src/dev/uart8250.hh b/src/dev/uart8250.hh index 4b7c2f1e4..44e90132f 100644 --- a/src/dev/uart8250.hh +++ b/src/dev/uart8250.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @file diff --git a/src/kern/kernel_stats.cc b/src/kern/kernel_stats.cc index b85d88145..f7868b50f 100644 --- a/src/kern/kernel_stats.cc +++ b/src/kern/kernel_stats.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Lisa Hsu + * Nathan Binkert */ #include <map> @@ -32,7 +35,7 @@ #include "arch/alpha/osfpal.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/kernel_stats.hh" #include "kern/tru64/tru64_syscalls.hh" #include "sim/system.hh" @@ -42,13 +45,12 @@ using namespace Stats; namespace Kernel { -const char *modestr[] = { "kernel", "user", "idle", "interrupt" }; +const char *modestr[] = { "kernel", "user", "idle" }; Statistics::Statistics(System *system) : idleProcess((Addr)-1), themode(kernel), lastModeTick(0), iplLast(0), iplLastTick(0) { - bin_int = system->params()->bin_int; } void @@ -181,16 +183,16 @@ Statistics::regStats(const string &_name) } void -Statistics::setIdleProcess(Addr idlepcbb, ExecContext *xc) +Statistics::setIdleProcess(Addr idlepcbb, ThreadContext *tc) { - assert(themode == kernel || themode == interrupt); + assert(themode == kernel); idleProcess = idlepcbb; themode = idle; - changeMode(themode, xc); + changeMode(themode, tc); } void -Statistics::changeMode(cpu_mode newmode, ExecContext *xc) +Statistics::changeMode(cpu_mode newmode, ThreadContext *tc) { _mode[newmode]++; @@ -203,8 +205,6 @@ Statistics::changeMode(cpu_mode newmode, ExecContext *xc) _modeGood[newmode]++; _modeTicks[themode] += curTick - lastModeTick; - xc->getSystemPtr()->kernelBinning->changeMode(newmode); - lastModeTick = curTick; themode = newmode; } @@ -226,31 +226,27 @@ Statistics::swpipl(int ipl) } void -Statistics::mode(cpu_mode newmode, ExecContext *xc) +Statistics::mode(cpu_mode newmode, ThreadContext *tc) { - Addr pcbb = xc->readMiscReg(AlphaISA::IPR_PALtemp23); + Addr pcbb = tc->readMiscReg(AlphaISA::IPR_PALtemp23); - if ((newmode == kernel || newmode == interrupt) && - pcbb == idleProcess) + if (newmode == kernel && pcbb == idleProcess) newmode = idle; - if (bin_int == false && newmode == interrupt) - newmode = kernel; - - changeMode(newmode, xc); + changeMode(newmode, tc); } void -Statistics::context(Addr oldpcbb, Addr newpcbb, ExecContext *xc) +Statistics::context(Addr oldpcbb, Addr newpcbb, ThreadContext *tc) { assert(themode != user); _swap_context++; - changeMode(newpcbb == idleProcess ? idle : kernel, xc); + changeMode(newpcbb == idleProcess ? idle : kernel, tc); } void -Statistics::callpal(int code, ExecContext *xc) +Statistics::callpal(int code, ThreadContext *tc) { if (!PAL::name(code)) return; @@ -259,17 +255,12 @@ Statistics::callpal(int code, ExecContext *xc) switch (code) { case PAL::callsys: { - int number = xc->readIntReg(0); + int number = tc->readIntReg(0); if (SystemCalls<Tru64>::validSyscallNumber(number)) { int cvtnum = SystemCalls<Tru64>::convert(number); _syscall[cvtnum]++; } } break; - - case PAL::swpctx: - if (xc->getSystemPtr()->kernelBinning) - xc->getSystemPtr()->kernelBinning->palSwapContext(xc); - break; } } diff --git a/src/kern/kernel_stats.hh b/src/kern/kernel_stats.hh index 16ec721d0..c691ad8cf 100644 --- a/src/kern/kernel_stats.hh +++ b/src/kern/kernel_stats.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Lisa Hsu + * Nathan Binkert */ #ifndef __KERNEL_STATS_HH__ @@ -37,104 +40,26 @@ #include "cpu/static_inst.hh" class BaseCPU; -class ExecContext; +class ThreadContext; class FnEvent; // What does kernel stats expect is included? class System; namespace Kernel { -enum cpu_mode { kernel, user, idle, interrupt, cpu_mode_num }; +enum cpu_mode { kernel, user, idle, cpu_mode_num }; extern const char *modestr[]; -class Binning -{ - private: - std::string myname; - System *system; - - private: - // lisa's binning stuff - struct fnCall - { - Stats::MainBin *myBin; - std::string name; - }; - - struct SWContext - { - Counter calls; - std::stack<fnCall *> callStack; - }; - - std::map<const std::string, Stats::MainBin *> fnBins; - std::map<const Addr, SWContext *> swCtxMap; - - std::multimap<const std::string, std::string> callerMap; - void populateMap(std::string caller, std::string callee); - - std::vector<FnEvent *> fnEvents; - - Stats::Scalar<> fnCalls; - - Stats::MainBin *getBin(const std::string &name); - bool findCaller(std::string, std::string) const; - - SWContext *findContext(Addr pcb); - bool addContext(Addr pcb, SWContext *ctx) - { - return (swCtxMap.insert(std::make_pair(pcb, ctx))).second; - } - - void remContext(Addr pcb) - { - swCtxMap.erase(pcb); - } - - void dumpState() const; - - SWContext *swctx; - std::vector<std::string> binned_fns; - - private: - Stats::MainBin *modeBin[cpu_mode_num]; - - public: - const bool bin; - const bool fnbin; - - cpu_mode themode; - void palSwapContext(ExecContext *xc); - void execute(ExecContext *xc, StaticInstPtr inst); - void call(ExecContext *xc, Stats::MainBin *myBin); - void changeMode(cpu_mode mode); - - public: - Binning(System *sys); - virtual ~Binning(); - - const std::string name() const { return myname; } - void regStats(const std::string &name); - - public: - virtual void serialize(std::ostream &os); - virtual void unserialize(Checkpoint *cp, const std::string §ion); -}; - class Statistics : public Serializable { private: - friend class Binning; - - private: std::string myname; Addr idleProcess; cpu_mode themode; Tick lastModeTick; - bool bin_int; - void changeMode(cpu_mode newmode, ExecContext *xc); + void changeMode(cpu_mode newmode, ThreadContext *tc); private: Stats::Scalar<> _arm; @@ -176,11 +101,11 @@ class Statistics : public Serializable void ivle() { _ivle++; } void hwrei() { _hwrei++; } void swpipl(int ipl); - void mode(cpu_mode newmode, ExecContext *xc); - void context(Addr oldpcbb, Addr newpcbb, ExecContext *xc); - void callpal(int code, ExecContext *xc); + void mode(cpu_mode newmode, ThreadContext *tc); + void context(Addr oldpcbb, Addr newpcbb, ThreadContext *tc); + void callpal(int code, ThreadContext *tc); - void setIdleProcess(Addr idle, ExecContext *xc); + void setIdleProcess(Addr idle, ThreadContext *tc); public: virtual void serialize(std::ostream &os); diff --git a/src/kern/linux/events.cc b/src/kern/linux/events.cc index b688e9dd0..289ece5ce 100644 --- a/src/kern/linux/events.cc +++ b/src/kern/linux/events.cc @@ -24,11 +24,14 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ #include "arch/arguments.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/linux/events.hh" #include "kern/linux/printk.hh" #include "kern/system_events.hh" @@ -38,17 +41,17 @@ namespace Linux { void -DebugPrintkEvent::process(ExecContext *xc) +DebugPrintkEvent::process(ThreadContext *tc) { if (DTRACE(DebugPrintf)) { if (!raw) { - StringWrap name(xc->getSystemPtr()->name() + ".dprintk"); + StringWrap name(tc->getSystemPtr()->name() + ".dprintk"); DPRINTFN(""); } - AlphaISA::AlphaArguments args(xc); + AlphaISA::AlphaArguments args(tc); Printk(args); - SkipFuncEvent::process(xc); + SkipFuncEvent::process(tc); } } diff --git a/src/kern/linux/events.hh b/src/kern/linux/events.hh index 95c268976..b0510c18f 100644 --- a/src/kern/linux/events.hh +++ b/src/kern/linux/events.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ #ifndef __KERN_LINUX_EVENTS_HH__ @@ -42,7 +45,7 @@ class DebugPrintkEvent : public SkipFuncEvent DebugPrintkEvent(PCEventQueue *q, const std::string &desc, Addr addr, bool r = false) : SkipFuncEvent(q, desc, addr), raw(r) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *xc); }; } diff --git a/src/kern/linux/linux.hh b/src/kern/linux/linux.hh index 63e0dd5ca..e3f554a22 100644 --- a/src/kern/linux/linux.hh +++ b/src/kern/linux/linux.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #ifndef __LINUX_HH__ @@ -67,7 +69,7 @@ class Linux { typedef uint32_t gid_t; //@} -#if BSD_HOST +#if NO_STAT64 typedef struct stat hst_stat; typedef struct stat hst_stat64; #else @@ -174,7 +176,7 @@ class Linux { /// Helper function to convert a host stat buffer to a target stat /// buffer. Also copies the target buffer out to the simulated /// memory space. Used by stat(), fstat(), and lstat(). -#if !BSD_HOST +#if !NO_STAT64 static void copyOutStatBuf(TranslatingPort *mem, Addr addr, hst_stat *host) { diff --git a/src/kern/linux/linux_syscalls.cc b/src/kern/linux/linux_syscalls.cc index c85b6d28f..7c2821dec 100644 --- a/src/kern/linux/linux_syscalls.cc +++ b/src/kern/linux/linux_syscalls.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #include "kern/linux/linux_syscalls.hh" diff --git a/src/kern/linux/linux_syscalls.hh b/src/kern/linux/linux_syscalls.hh index 7f2a08ea9..65616e862 100644 --- a/src/kern/linux/linux_syscalls.hh +++ b/src/kern/linux/linux_syscalls.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #ifndef __KERN_LINUX_LINUX_SYSCALLS_HH__ diff --git a/src/kern/linux/printk.cc b/src/kern/linux/printk.cc index 918b8dabe..004d1be2f 100644 --- a/src/kern/linux/printk.cc +++ b/src/kern/linux/printk.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ #include <sys/types.h> diff --git a/src/kern/linux/printk.hh b/src/kern/linux/printk.hh index b88c40f5e..5ddf0a018 100644 --- a/src/kern/linux/printk.hh +++ b/src/kern/linux/printk.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ #ifndef __PRINTK_HH__ diff --git a/src/kern/linux/sched.hh b/src/kern/linux/sched.hh index a11fa590d..f849cee30 100644 --- a/src/kern/linux/sched.hh +++ b/src/kern/linux/sched.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ #ifndef __KERN_LINUX_SCHED_HH__ diff --git a/src/kern/solaris/solaris.hh b/src/kern/solaris/solaris.hh index e2b61d613..b819fb6d2 100644 --- a/src/kern/solaris/solaris.hh +++ b/src/kern/solaris/solaris.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #ifndef __SOLARIS_HH__ @@ -72,7 +74,7 @@ class Solaris { typedef uint32_t nlink_t; //@} -#if BSD_HOST +#if NO_STAT64 typedef struct stat hst_stat; typedef struct stat hst_stat64; #else @@ -175,7 +177,7 @@ class Solaris { /// Helper function to convert a host stat buffer to a target stat /// buffer. Also copies the target buffer out to the simulated /// memory space. Used by stat(), fstat(), and lstat(). -#if !BSD_HOST +#if !NO_STAT64 static void copyOutStatBuf(TranslatingPort *mem, Addr addr, hst_stat *host) { diff --git a/src/kern/system_events.cc b/src/kern/system_events.cc index fd5c12e44..177ce96d1 100644 --- a/src/kern/system_events.cc +++ b/src/kern/system_events.cc @@ -24,10 +24,13 @@ * 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. + * + * Authors: Lisa Hsu + * Nathan Binkert */ #include "cpu/base.hh" -#include "cpu/cpu_exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/kernel_stats.hh" #include "kern/system_events.hh" #include "sim/system.hh" @@ -35,57 +38,28 @@ using namespace TheISA; void -SkipFuncEvent::process(ExecContext *xc) +SkipFuncEvent::process(ThreadContext *tc) { - Addr newpc = xc->readIntReg(ReturnAddressReg); + Addr newpc = tc->readIntReg(ReturnAddressReg); DPRINTF(PCEvent, "skipping %s: pc=%x, newpc=%x\n", description, - xc->readPC(), newpc); + tc->readPC(), newpc); - xc->setPC(newpc); - xc->setNextPC(xc->readPC() + sizeof(TheISA::MachInst)); + tc->setPC(newpc); + tc->setNextPC(tc->readPC() + sizeof(TheISA::MachInst)); /* - BranchPred *bp = xc->getCpuPtr()->getBranchPred(); + BranchPred *bp = tc->getCpuPtr()->getBranchPred(); if (bp != NULL) { - bp->popRAS(xc->getThreadNum()); + bp->popRAS(tc->getThreadNum()); } */ } - -FnEvent::FnEvent(PCEventQueue *q, const std::string &desc, Addr addr, - Stats::MainBin *bin) - : PCEvent(q, desc, addr), _name(desc), mybin(bin) -{ -} - -void -FnEvent::process(ExecContext *xc) -{ - if (xc->misspeculating()) - return; - - xc->getSystemPtr()->kernelBinning->call(xc, mybin); -} - void -IdleStartEvent::process(ExecContext *xc) +IdleStartEvent::process(ThreadContext *tc) { - xc->getCpuPtr()->kernelStats->setIdleProcess( - xc->readMiscReg(AlphaISA::IPR_PALtemp23), xc); + if (tc->getKernelStats()) + tc->getKernelStats()->setIdleProcess( + tc->readMiscReg(AlphaISA::IPR_PALtemp23), tc); remove(); } - -void -InterruptStartEvent::process(ExecContext *xc) -{ - xc->getCpuPtr()->kernelStats->mode(Kernel::interrupt, xc); -} - -void -InterruptEndEvent::process(ExecContext *xc) -{ - // We go back to kernel, if we are user, inside the rti - // pal code we will get switched to user because of the ICM write - xc->getCpuPtr()->kernelStats->mode(Kernel::kernel, xc); -} diff --git a/src/kern/system_events.hh b/src/kern/system_events.hh index 246140a09..93b5eb528 100644 --- a/src/kern/system_events.hh +++ b/src/kern/system_events.hh @@ -24,6 +24,10 @@ * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu + * Ali Saidi */ #ifndef __SYSTEM_EVENTS_HH__ @@ -39,20 +43,7 @@ class SkipFuncEvent : public PCEvent SkipFuncEvent(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); -}; - -class FnEvent : public PCEvent -{ - public: - FnEvent(PCEventQueue *q, const std::string &desc, Addr addr, - Stats::MainBin *bin); - virtual void process(ExecContext *xc); - std::string myname() const { return _name; } - - private: - std::string _name; - Stats::MainBin *mybin; + virtual void process(ThreadContext *tc); }; class IdleStartEvent : public PCEvent @@ -61,26 +52,7 @@ class IdleStartEvent : public PCEvent IdleStartEvent(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; -class InterruptStartEvent : public PCEvent -{ - public: - InterruptStartEvent(PCEventQueue *q, const std::string &desc, Addr addr) - : PCEvent(q, desc, addr) - {} - virtual void process(ExecContext *xc); -}; - -class InterruptEndEvent : public PCEvent -{ - public: - InterruptEndEvent(PCEventQueue *q, const std::string &desc, Addr addr) - : PCEvent(q, desc, addr) - {} - virtual void process(ExecContext *xc); -}; - - #endif // __SYSTEM_EVENTS_HH__ diff --git a/src/kern/tru64/dump_mbuf.cc b/src/kern/tru64/dump_mbuf.cc index c3c37531a..8f88f8904 100644 --- a/src/kern/tru64/dump_mbuf.cc +++ b/src/kern/tru64/dump_mbuf.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <sys/types.h> @@ -32,7 +34,7 @@ #include "base/cprintf.hh" #include "base/trace.hh" #include "base/loader/symtab.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "kern/tru64/mbuf.hh" #include "sim/host.hh" #include "sim/system.hh" @@ -47,11 +49,11 @@ namespace tru64 { void DumpMbuf(AlphaArguments args) { - ExecContext *xc = args.getExecContext(); + ThreadContext *tc = args.getThreadContext(); Addr addr = (Addr)args; struct mbuf m; - CopyOut(xc, &m, addr, sizeof(m)); + CopyOut(tc, &m, addr, sizeof(m)); int count = m.m_pkthdr.len; @@ -62,8 +64,8 @@ DumpMbuf(AlphaArguments args) ccprintf(DebugOut(), "m=%#lx, m->m_data=%#lx, m->m_len=%d\n", addr, m.m_data, m.m_len); char *buffer = new char[m.m_len]; - CopyOut(xc, buffer, m.m_data, m.m_len); - Trace::dataDump(curTick, xc->getSystemPtr()->name(), (uint8_t *)buffer, + CopyOut(tc, buffer, m.m_data, m.m_len); + Trace::dataDump(curTick, tc->getSystemPtr()->name(), (uint8_t *)buffer, m.m_len); delete [] buffer; @@ -71,7 +73,7 @@ DumpMbuf(AlphaArguments args) if (!m.m_next) break; - CopyOut(xc, &m, m.m_next, sizeof(m)); + CopyOut(tc, &m, m.m_next, sizeof(m)); } } diff --git a/src/kern/tru64/dump_mbuf.hh b/src/kern/tru64/dump_mbuf.hh index 9e1698ff1..25c6fd31d 100644 --- a/src/kern/tru64/dump_mbuf.hh +++ b/src/kern/tru64/dump_mbuf.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __DUMP_MBUF_HH__ diff --git a/src/kern/tru64/mbuf.hh b/src/kern/tru64/mbuf.hh index 93424858f..cb5a84a7e 100644 --- a/src/kern/tru64/mbuf.hh +++ b/src/kern/tru64/mbuf.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __MBUF_HH__ diff --git a/src/kern/tru64/printf.cc b/src/kern/tru64/printf.cc index 319d36673..29dd443d2 100644 --- a/src/kern/tru64/printf.cc +++ b/src/kern/tru64/printf.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/kern/tru64/printf.hh b/src/kern/tru64/printf.hh index 61236e83a..f6a4544ad 100644 --- a/src/kern/tru64/printf.hh +++ b/src/kern/tru64/printf.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __PRINTF_HH__ diff --git a/src/kern/tru64/tru64.hh b/src/kern/tru64/tru64.hh index 91db5bb84..d2b7c862f 100644 --- a/src/kern/tru64/tru64.hh +++ b/src/kern/tru64/tru64.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ #ifndef __TRU64_HH__ @@ -576,23 +579,23 @@ class Tru64 { /// Target getdirentries() handler. static SyscallReturn getdirentriesFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; #ifdef __CYGWIN__ panic("getdirent not implemented on cygwin!"); #else - int fd = process->sim_fd(xc->getSyscallArg(0)); - Addr tgt_buf = xc->getSyscallArg(1); - int tgt_nbytes = xc->getSyscallArg(2); - Addr tgt_basep = xc->getSyscallArg(3); + int fd = process->sim_fd(tc->getSyscallArg(0)); + Addr tgt_buf = tc->getSyscallArg(1); + int tgt_nbytes = tc->getSyscallArg(2); + Addr tgt_basep = tc->getSyscallArg(3); char * const host_buf = new char[tgt_nbytes]; // just pass basep through uninterpreted. TypedBufferArg<int64_t> basep(tgt_basep); - basep.copyIn(xc->getMemPort()); + basep.copyIn(tc->getMemPort()); long host_basep = (off_t)htog((int64_t)*basep); int host_result = getdirentries(fd, host_buf, tgt_nbytes, &host_basep); @@ -619,7 +622,7 @@ class Tru64 { tgt_dp->d_reclen = tgt_bufsize; tgt_dp->d_namlen = namelen; strcpy(tgt_dp->d_name, host_dp->d_name); - tgt_dp.copyOut(xc->getMemPort()); + tgt_dp.copyOut(tc->getMemPort()); tgt_buf_ptr += tgt_bufsize; host_buf_ptr += host_dp->d_reclen; @@ -628,7 +631,7 @@ class Tru64 { delete [] host_buf; *basep = htog((int64_t)host_basep); - basep.copyOut(xc->getMemPort()); + basep.copyOut(tc->getMemPort()); return tgt_buf_ptr - tgt_buf; #endif @@ -637,27 +640,27 @@ class Tru64 { /// Target sigreturn() handler. static SyscallReturn sigreturnFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; using TheISA::RegFile; - TypedBufferArg<Tru64::sigcontext> sc(xc->getSyscallArg(0)); + TypedBufferArg<Tru64::sigcontext> sc(tc->getSyscallArg(0)); - sc.copyIn(xc->getMemPort()); + sc.copyIn(tc->getMemPort()); // Restore state from sigcontext structure. // Note that we'll advance PC <- NPC before the end of the cycle, // so we need to restore the desired PC into NPC. // The current regs->pc will get clobbered. - xc->setNextPC(htog(sc->sc_pc)); + tc->setNextPC(htog(sc->sc_pc)); for (int i = 0; i < 31; ++i) { - xc->setIntReg(i, htog(sc->sc_regs[i])); - xc->setFloatRegBits(i, htog(sc->sc_fpregs[i])); + tc->setIntReg(i, htog(sc->sc_regs[i])); + tc->setFloatRegBits(i, htog(sc->sc_fpregs[i])); } - xc->setMiscReg(TheISA::Fpcr_DepTag, htog(sc->sc_fpcr)); + tc->setMiscReg(TheISA::Fpcr_DepTag, htog(sc->sc_fpcr)); return 0; } @@ -670,13 +673,13 @@ class Tru64 { /// Create a stack region for a thread. static SyscallReturn stack_createFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; - TypedBufferArg<Tru64::vm_stack> argp(xc->getSyscallArg(0)); + TypedBufferArg<Tru64::vm_stack> argp(tc->getSyscallArg(0)); - argp.copyIn(xc->getMemPort()); + argp.copyIn(tc->getMemPort()); // if the user chose an address, just let them have it. Otherwise // pick one for them. @@ -685,7 +688,7 @@ class Tru64 { int stack_size = (htog(argp->rsize) + htog(argp->ysize) + htog(argp->gsize)); process->next_thread_stack_base -= stack_size; - argp.copyOut(xc->getMemPort()); + argp.copyOut(tc->getMemPort()); } return 0; @@ -700,15 +703,15 @@ class Tru64 { /// region has several structs, some global, some per-RAD, some per-VP. static SyscallReturn nxm_task_initFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; using namespace TheISA; - TypedBufferArg<Tru64::nxm_task_attr> attrp(xc->getSyscallArg(0)); - TypedBufferArg<Addr> configptr_ptr(xc->getSyscallArg(1)); + TypedBufferArg<Tru64::nxm_task_attr> attrp(tc->getSyscallArg(0)); + TypedBufferArg<Addr> configptr_ptr(tc->getSyscallArg(1)); - attrp.copyIn(xc->getMemPort()); + attrp.copyIn(tc->getMemPort()); if (gtoh(attrp->nxm_version) != NXM_LIB_VERSION) { cerr << "nxm_task_init: thread library version mismatch! " @@ -749,7 +752,7 @@ class Tru64 { config->nxm_slot_state = htog(slot_state_addr); config->nxm_rad[0] = htog(rad_state_addr); - config.copyOut(xc->getMemPort()); + config.copyOut(tc->getMemPort()); // initialize the slot_state array and copy it out TypedBufferArg<Tru64::nxm_slot_state_t> slot_state(slot_state_addr, @@ -762,7 +765,7 @@ class Tru64 { (i == 0) ? Tru64::NXM_SLOT_BOUND : Tru64::NXM_SLOT_AVAIL; } - slot_state.copyOut(xc->getMemPort()); + slot_state.copyOut(tc->getMemPort()); // same for the per-RAD "shared" struct. Note that we need to // allocate extra bytes for the per-VP array which is embedded at @@ -786,7 +789,7 @@ class Tru64 { ssp->nxm_sysevent = htog(0); if (i == 0) { - uint64_t uniq = xc->readMiscReg(TheISA::Uniq_DepTag); + uint64_t uniq = tc->readMiscReg(TheISA::Uniq_DepTag); ssp->nxm_u.pth_id = htog(uniq + gtoh(attrp->nxm_uniq_offset)); ssp->nxm_u.nxm_active = htog(uniq | 1); } @@ -796,13 +799,13 @@ class Tru64 { } } - rad_state.copyOut(xc->getMemPort()); + rad_state.copyOut(tc->getMemPort()); // // copy pointer to shared config area out to user // *configptr_ptr = htog(config_addr); - configptr_ptr.copyOut(xc->getMemPort()); + configptr_ptr.copyOut(tc->getMemPort()); // Register this as a valid address range with the process process->nxm_start = base_addr; @@ -811,40 +814,40 @@ class Tru64 { return 0; } - /// Initialize execution context. + /// Initialize thread context. static void - init_exec_context(ExecContext *ec, + init_thread_context(ThreadContext *tc, Tru64::nxm_thread_attr *attrp, uint64_t uniq_val) { using namespace TheISA; - ec->clearArchRegs(); + tc->clearArchRegs(); - ec->setIntReg(TheISA::ArgumentReg0, gtoh(attrp->registers.a0)); - ec->setIntReg(27/*t12*/, gtoh(attrp->registers.pc)); - ec->setIntReg(TheISA::StackPointerReg, gtoh(attrp->registers.sp)); - ec->setMiscReg(TheISA::Uniq_DepTag, uniq_val); + tc->setIntReg(TheISA::ArgumentReg0, gtoh(attrp->registers.a0)); + tc->setIntReg(27/*t12*/, gtoh(attrp->registers.pc)); + tc->setIntReg(TheISA::StackPointerReg, gtoh(attrp->registers.sp)); + tc->setMiscReg(TheISA::Uniq_DepTag, uniq_val); - ec->setPC(gtoh(attrp->registers.pc)); - ec->setNextPC(gtoh(attrp->registers.pc) + sizeof(TheISA::MachInst)); + tc->setPC(gtoh(attrp->registers.pc)); + tc->setNextPC(gtoh(attrp->registers.pc) + sizeof(TheISA::MachInst)); - ec->activate(); + tc->activate(); } /// Create thread. static SyscallReturn nxm_thread_createFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; using namespace TheISA; - TypedBufferArg<Tru64::nxm_thread_attr> attrp(xc->getSyscallArg(0)); - TypedBufferArg<uint64_t> kidp(xc->getSyscallArg(1)); - int thread_index = xc->getSyscallArg(2); + TypedBufferArg<Tru64::nxm_thread_attr> attrp(tc->getSyscallArg(0)); + TypedBufferArg<uint64_t> kidp(tc->getSyscallArg(1)); + int thread_index = tc->getSyscallArg(2); // get attribute args - attrp.copyIn(xc->getMemPort()); + attrp.copyIn(tc->getMemPort()); if (gtoh(attrp->version) != NXM_LIB_VERSION) { cerr << "nxm_thread_create: thread library version mismatch! " @@ -869,7 +872,7 @@ class Tru64 { TypedBufferArg<Tru64::nxm_shared> rad_state(0x14000, rad_state_size); - rad_state.copyIn(xc->getMemPort()); + rad_state.copyIn(tc->getMemPort()); uint64_t uniq_val = gtoh(attrp->pthid) - gtoh(rad_state->nxm_uniq_offset); @@ -880,7 +883,7 @@ class Tru64 { // This is supposed to be a port number. Make something up. *kidp = htog(99); - kidp.copyOut(xc->getMemPort()); + kidp.copyOut(tc->getMemPort()); return 0; } else if (gtoh(attrp->type) == Tru64::NXM_TYPE_VP) { @@ -894,7 +897,7 @@ class Tru64 { ssp->nxm_u.pth_id = attrp->pthid; ssp->nxm_u.nxm_active = htog(uniq_val | 1); - rad_state.copyOut(xc->getMemPort()); + rad_state.copyOut(tc->getMemPort()); Addr slot_state_addr = 0x12000 + sizeof(Tru64::nxm_config_info); int slot_state_size = @@ -904,7 +907,7 @@ class Tru64 { slot_state(slot_state_addr, slot_state_size); - slot_state.copyIn(xc->getMemPort()); + slot_state.copyIn(tc->getMemPort()); if (slot_state[thread_index] != Tru64::NXM_SLOT_AVAIL) { cerr << "nxm_thread_createFunc: requested VP slot " @@ -916,21 +919,21 @@ class Tru64 { // doesn't work anyway slot_state[thread_index] = Tru64::NXM_SLOT_BOUND; - slot_state.copyOut(xc->getMemPort()); + slot_state.copyOut(tc->getMemPort()); - // Find a free simulator execution context. + // Find a free simulator thread context. for (int i = 0; i < process->numCpus(); ++i) { - ExecContext *xc = process->execContexts[i]; + ThreadContext *tc = process->threadContexts[i]; - if (xc->status() == ExecContext::Unallocated) { + if (tc->status() == ThreadContext::Suspended) { // inactive context... grab it - init_exec_context(xc, attrp, uniq_val); + init_thread_context(tc, attrp, uniq_val); // This is supposed to be a port number, but we'll try // and get away with just sticking the thread index // here. *kidp = htog(thread_index); - kidp.copyOut(xc->getMemPort()); + kidp.copyOut(tc->getMemPort()); return 0; } @@ -951,7 +954,7 @@ class Tru64 { /// Thread idle call (like yield()). static SyscallReturn nxm_idleFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 0; } @@ -959,17 +962,17 @@ class Tru64 { /// Block thread. static SyscallReturn nxm_thread_blockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; - uint64_t tid = xc->getSyscallArg(0); - uint64_t secs = xc->getSyscallArg(1); - uint64_t flags = xc->getSyscallArg(2); - uint64_t action = xc->getSyscallArg(3); - uint64_t usecs = xc->getSyscallArg(4); + uint64_t tid = tc->getSyscallArg(0); + uint64_t secs = tc->getSyscallArg(1); + uint64_t flags = tc->getSyscallArg(2); + uint64_t action = tc->getSyscallArg(3); + uint64_t usecs = tc->getSyscallArg(4); - cout << xc->getCpuPtr()->name() << ": nxm_thread_block " << tid << " " + cout << tc->getCpuPtr()->name() << ": nxm_thread_block " << tid << " " << secs << " " << flags << " " << action << " " << usecs << endl; return 0; @@ -978,17 +981,17 @@ class Tru64 { /// block. static SyscallReturn nxm_blockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; - Addr uaddr = xc->getSyscallArg(0); - uint64_t val = xc->getSyscallArg(1); - uint64_t secs = xc->getSyscallArg(2); - uint64_t usecs = xc->getSyscallArg(3); - uint64_t flags = xc->getSyscallArg(4); + Addr uaddr = tc->getSyscallArg(0); + uint64_t val = tc->getSyscallArg(1); + uint64_t secs = tc->getSyscallArg(2); + uint64_t usecs = tc->getSyscallArg(3); + uint64_t flags = tc->getSyscallArg(4); - BaseCPU *cpu = xc->getCpuPtr(); + BaseCPU *cpu = tc->getCpuPtr(); cout << cpu->name() << ": nxm_block " << hex << uaddr << dec << " " << val @@ -1001,13 +1004,13 @@ class Tru64 { /// Unblock thread. static SyscallReturn nxm_unblockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace std; - Addr uaddr = xc->getSyscallArg(0); + Addr uaddr = tc->getSyscallArg(0); - cout << xc->getCpuPtr()->name() << ": nxm_unblock " + cout << tc->getCpuPtr()->name() << ": nxm_unblock " << hex << uaddr << dec << endl; return 0; @@ -1016,7 +1019,7 @@ class Tru64 { /// Switch thread priority. static SyscallReturn swtch_priFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Attempts to switch to another runnable thread (if there is // one). Returns false if there are no other threads to run @@ -1029,7 +1032,7 @@ class Tru64 { } - /// Activate exec context waiting on a channel. Just activate one + /// Activate thread context waiting on a channel. Just activate one /// by default. static int activate_waiting_context(Addr uaddr, Process *process, @@ -1045,8 +1048,8 @@ class Tru64 { while (i != end && (num_activated == 0 || activate_all)) { if (i->waitChan == uaddr) { // found waiting process: make it active - ExecContext *newCtx = i->waitingContext; - assert(newCtx->status() == ExecContext::Suspended); + ThreadContext *newCtx = i->waitingContext; + assert(newCtx->status() == ThreadContext::Suspended); newCtx->activate(); // get rid of this record @@ -1063,32 +1066,32 @@ class Tru64 { /// M5 hacked-up lock acquire. static void - m5_lock_mutex(Addr uaddr, Process *process, ExecContext *xc) + m5_lock_mutex(Addr uaddr, Process *process, ThreadContext *tc) { using namespace TheISA; TypedBufferArg<uint64_t> lockp(uaddr); - lockp.copyIn(xc->getMemPort()); + lockp.copyIn(tc->getMemPort()); if (gtoh(*lockp) == 0) { // lock is free: grab it *lockp = htog(1); - lockp.copyOut(xc->getMemPort()); + lockp.copyOut(tc->getMemPort()); } else { // lock is busy: disable until free - process->waitList.push_back(Process::WaitRec(uaddr, xc)); - xc->suspend(); + process->waitList.push_back(Process::WaitRec(uaddr, tc)); + tc->suspend(); } } /// M5 unlock call. static void - m5_unlock_mutex(Addr uaddr, Process *process, ExecContext *xc) + m5_unlock_mutex(Addr uaddr, Process *process, ThreadContext *tc) { TypedBufferArg<uint64_t> lockp(uaddr); - lockp.copyIn(xc->getMemPort()); + lockp.copyIn(tc->getMemPort()); assert(*lockp != 0); // Check for a process waiting on the lock. @@ -1097,18 +1100,18 @@ class Tru64 { // clear lock field if no waiting context is taking over the lock if (num_waiting == 0) { *lockp = 0; - lockp.copyOut(xc->getMemPort()); + lockp.copyOut(tc->getMemPort()); } } /// Lock acquire syscall handler. static SyscallReturn m5_mutex_lockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - Addr uaddr = xc->getSyscallArg(0); + Addr uaddr = tc->getSyscallArg(0); - m5_lock_mutex(uaddr, process, xc); + m5_lock_mutex(uaddr, process, tc); // Return 0 since we will always return to the user with the lock // acquired. We will just keep the context inactive until that is @@ -1119,19 +1122,19 @@ class Tru64 { /// Try lock (non-blocking). static SyscallReturn m5_mutex_trylockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; - Addr uaddr = xc->getSyscallArg(0); + Addr uaddr = tc->getSyscallArg(0); TypedBufferArg<uint64_t> lockp(uaddr); - lockp.copyIn(xc->getMemPort()); + lockp.copyIn(tc->getMemPort()); if (gtoh(*lockp) == 0) { // lock is free: grab it *lockp = htog(1); - lockp.copyOut(xc->getMemPort()); + lockp.copyOut(tc->getMemPort()); return 0; } else { return 1; @@ -1141,11 +1144,11 @@ class Tru64 { /// Unlock syscall handler. static SyscallReturn m5_mutex_unlockFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - Addr uaddr = xc->getSyscallArg(0); + Addr uaddr = tc->getSyscallArg(0); - m5_unlock_mutex(uaddr, process, xc); + m5_unlock_mutex(uaddr, process, tc); return 0; } @@ -1153,9 +1156,9 @@ class Tru64 { /// Signal ocndition. static SyscallReturn m5_cond_signalFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - Addr cond_addr = xc->getSyscallArg(0); + Addr cond_addr = tc->getSyscallArg(0); // Wake up one process waiting on the condition variable. activate_waiting_context(cond_addr, process); @@ -1166,9 +1169,9 @@ class Tru64 { /// Wake up all processes waiting on the condition variable. static SyscallReturn m5_cond_broadcastFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - Addr cond_addr = xc->getSyscallArg(0); + Addr cond_addr = tc->getSyscallArg(0); activate_waiting_context(cond_addr, process, true); @@ -1178,23 +1181,23 @@ class Tru64 { /// Wait on a condition. static SyscallReturn m5_cond_waitFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { using namespace TheISA; - Addr cond_addr = xc->getSyscallArg(0); - Addr lock_addr = xc->getSyscallArg(1); + Addr cond_addr = tc->getSyscallArg(0); + Addr lock_addr = tc->getSyscallArg(1); TypedBufferArg<uint64_t> condp(cond_addr); TypedBufferArg<uint64_t> lockp(lock_addr); // user is supposed to acquire lock before entering - lockp.copyIn(xc->getMemPort()); + lockp.copyIn(tc->getMemPort()); assert(gtoh(*lockp) != 0); - m5_unlock_mutex(lock_addr, process, xc); + m5_unlock_mutex(lock_addr, process, tc); - process->waitList.push_back(Process::WaitRec(cond_addr, xc)); - xc->suspend(); + process->waitList.push_back(Process::WaitRec(cond_addr, tc)); + tc->suspend(); return 0; } @@ -1202,10 +1205,10 @@ class Tru64 { /// Thread exit. static SyscallReturn m5_thread_exitFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - assert(xc->status() == ExecContext::Active); - xc->deallocate(); + assert(tc->status() == ThreadContext::Active); + tc->deallocate(); return 0; } @@ -1213,21 +1216,21 @@ class Tru64 { /// Indirect syscall invocation (call #0). static SyscallReturn indirectSyscallFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int new_callnum = xc->getSyscallArg(0); + int new_callnum = tc->getSyscallArg(0); LiveProcess *lp = dynamic_cast<LiveProcess*>(process); assert(lp); for (int i = 0; i < 5; ++i) - xc->setSyscallArg(i, xc->getSyscallArg(i+1)); + tc->setSyscallArg(i, tc->getSyscallArg(i+1)); SyscallDesc *new_desc = lp->getDesc(new_callnum); if (desc == NULL) fatal("Syscall %d out of range", callnum); - new_desc->doSyscall(new_callnum, process, xc); + new_desc->doSyscall(new_callnum, process, tc); return 0; } diff --git a/src/kern/tru64/tru64_events.cc b/src/kern/tru64/tru64_events.cc index 69fc5c55d..69638bde1 100644 --- a/src/kern/tru64/tru64_events.cc +++ b/src/kern/tru64/tru64_events.cc @@ -24,9 +24,12 @@ * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu */ -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "kern/system_events.hh" #include "kern/tru64/tru64_events.hh" @@ -39,23 +42,23 @@ using namespace TheISA; -//void SkipFuncEvent::process(ExecContext *xc); +//void SkipFuncEvent::process(ExecContext *tc); void -BadAddrEvent::process(ExecContext *xc) +BadAddrEvent::process(ThreadContext *tc) { // The following gross hack is the equivalent function to the // annotation for vmunix::badaddr in: // simos/simulation/apps/tcl/osf/tlaser.tcl - uint64_t a0 = xc->readIntReg(ArgumentReg0); + uint64_t a0 = tc->readIntReg(ArgumentReg0); AddrRangeList resp; AddrRangeList snoop; AddrRangeIter iter; bool found = false; - xc->getPhysPort()->getPeerAddressRanges(resp, snoop); + tc->getPhysPort()->getPeerAddressRanges(resp, snoop); for(iter = resp.begin(); iter != resp.end(); iter++) { if (*iter == (TheISA::K0Seg2Phys(a0) & EV5::PAddrImplMask)) @@ -65,41 +68,41 @@ BadAddrEvent::process(ExecContext *xc) if (!TheISA::IsK0Seg(a0) || found ) { DPRINTF(BADADDR, "badaddr arg=%#x bad\n", a0); - xc->setIntReg(ReturnValueReg, 0x1); - SkipFuncEvent::process(xc); + tc->setIntReg(ReturnValueReg, 0x1); + SkipFuncEvent::process(tc); } else DPRINTF(BADADDR, "badaddr arg=%#x good\n", a0); } void -PrintfEvent::process(ExecContext *xc) +PrintfEvent::process(ThreadContext *tc) { if (DTRACE(Printf)) { - DebugOut() << curTick << ": " << xc->getCpuPtr()->name() << ": "; + DebugOut() << curTick << ": " << tc->getCpuPtr()->name() << ": "; - AlphaArguments args(xc); + AlphaArguments args(tc); tru64::Printf(args); } } void -DebugPrintfEvent::process(ExecContext *xc) +DebugPrintfEvent::process(ThreadContext *tc) { if (DTRACE(DebugPrintf)) { if (!raw) - DebugOut() << curTick << ": " << xc->getCpuPtr()->name() << ": "; + DebugOut() << curTick << ": " << tc->getCpuPtr()->name() << ": "; - AlphaArguments args(xc); + AlphaArguments args(tc); tru64::Printf(args); } } void -DumpMbufEvent::process(ExecContext *xc) +DumpMbufEvent::process(ThreadContext *tc) { if (DTRACE(DebugPrintf)) { - AlphaArguments args(xc); + AlphaArguments args(tc); tru64::DumpMbuf(args); } } diff --git a/src/kern/tru64/tru64_events.hh b/src/kern/tru64/tru64_events.hh index 9b5bcfea2..6a1ab2e51 100644 --- a/src/kern/tru64/tru64_events.hh +++ b/src/kern/tru64/tru64_events.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu */ #ifndef __TRU64_EVENTS_HH__ @@ -34,14 +37,14 @@ #include "cpu/pc_event.hh" #include "kern/system_events.hh" -class ExecContext; +class ThreadContext; class BadAddrEvent : public SkipFuncEvent { public: BadAddrEvent(PCEventQueue *q, const std::string &desc, Addr addr) : SkipFuncEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; class PrintfEvent : public PCEvent @@ -49,7 +52,7 @@ class PrintfEvent : public PCEvent public: PrintfEvent(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; class DebugPrintfEvent : public PCEvent @@ -61,7 +64,7 @@ class DebugPrintfEvent : public PCEvent DebugPrintfEvent(PCEventQueue *q, const std::string &desc, Addr addr, bool r = false) : PCEvent(q, desc, addr), raw(r) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; class DebugPrintfrEvent : public DebugPrintfEvent @@ -77,7 +80,7 @@ class DumpMbufEvent : public PCEvent public: DumpMbufEvent(PCEventQueue *q, const std::string &desc, Addr addr) : PCEvent(q, desc, addr) {} - virtual void process(ExecContext *xc); + virtual void process(ThreadContext *tc); }; #endif // __TRU64_EVENTS_HH__ diff --git a/src/kern/tru64/tru64_syscalls.cc b/src/kern/tru64/tru64_syscalls.cc index 8aa57dbaa..8051b9efb 100644 --- a/src/kern/tru64/tru64_syscalls.cc +++ b/src/kern/tru64/tru64_syscalls.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include "kern/tru64/tru64_syscalls.hh" diff --git a/src/kern/tru64/tru64_syscalls.hh b/src/kern/tru64/tru64_syscalls.hh index 69fa9101c..66f5c2d39 100644 --- a/src/kern/tru64/tru64_syscalls.hh +++ b/src/kern/tru64/tru64_syscalls.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __KERN_TRU64_TRU64_SYSCALLS_HH__ diff --git a/src/mem/bridge.cc b/src/mem/bridge.cc index 736e8dc81..29ea2e12f 100644 --- a/src/mem/bridge.cc +++ b/src/mem/bridge.cc @@ -25,6 +25,9 @@ * 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. + * + * Authors: Ali Saidi + * Steve Reinhardt */ /** @@ -56,7 +59,7 @@ Bridge::Bridge(const std::string &n, int qsa, int qsb, } Port * -Bridge::getPort(const std::string &if_name) +Bridge::getPort(const std::string &if_name, int idx) { BridgePort *port; @@ -90,7 +93,17 @@ Bridge::BridgePort::recvTiming(Packet *pkt) DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", pkt->getSrc(), pkt->getDest(), pkt->getAddr()); - if (pkt->isResponse()) { + return otherPort->queueForSendTiming(pkt); +} + + +bool +Bridge::BridgePort::queueForSendTiming(Packet *pkt) +{ + if (queueFull()) + return false; + + if (pkt->isResponse()) { // This is a response for a request we forwarded earlier. The // corresponding PacketBuffer should be stored in the packet's // senderState field. @@ -99,22 +112,16 @@ Bridge::BridgePort::recvTiming(Packet *pkt) // set up new packet dest & senderState based on values saved // from original request buf->fixResponse(pkt); + DPRINTF(BusBridge, "restoring sender state: %#X, from packet buffer: %#X\n", + pkt->senderState, buf); DPRINTF(BusBridge, " is response, new dest %d\n", pkt->getDest()); delete buf; } - return otherPort->queueForSendTiming(pkt); -} - - -bool -Bridge::BridgePort::queueForSendTiming(Packet *pkt) -{ - if (queueFull()) - return false; - Tick readyTime = curTick + delay; PacketBuffer *buf = new PacketBuffer(pkt, readyTime); + DPRINTF(BusBridge, "old sender state: %#X, new sender state: %#X\n", + buf->origSenderState, buf); // If we're about to put this packet at the head of the queue, we // need to schedule an event to do the transmit. Otherwise there @@ -126,43 +133,16 @@ Bridge::BridgePort::queueForSendTiming(Packet *pkt) sendQueue.push_back(buf); - // Did we just become blocked? If yes, let other side know. - if (queueFull()) - otherPort->sendStatusChange(Port::Blocked); - return true; } - -void -Bridge::BridgePort::finishSend(PacketBuffer *buf) -{ - if (buf->expectResponse) { - // Must wait for response. We just need to count outstanding - // responses (in case we want to cap them); PacketBuffer - // pointer will be recovered on response. - ++outstandingResponses; - DPRINTF(BusBridge, " successful: awaiting response (%d)\n", - outstandingResponses); - } else { - // no response expected... deallocate packet buffer now. - DPRINTF(BusBridge, " successful: no response expected\n"); - delete buf; - } - - // If there are more packets to send, schedule event to try again. - if (!sendQueue.empty()) { - buf = sendQueue.front(); - sendEvent.schedule(std::max(buf->ready, curTick + 1)); - } -} - - void Bridge::BridgePort::trySend() { assert(!sendQueue.empty()); + bool was_full = queueFull(); + PacketBuffer *buf = sendQueue.front(); assert(buf->ready <= curTick); @@ -176,20 +156,41 @@ Bridge::BridgePort::trySend() // send successful sendQueue.pop_front(); buf->pkt = NULL; // we no longer own packet, so it's not safe to look at it - finishSend(buf); + + if (buf->expectResponse) { + // Must wait for response. We just need to count outstanding + // responses (in case we want to cap them); PacketBuffer + // pointer will be recovered on response. + ++outstandingResponses; + DPRINTF(BusBridge, " successful: awaiting response (%d)\n", + outstandingResponses); + } else { + // no response expected... deallocate packet buffer now. + DPRINTF(BusBridge, " successful: no response expected\n"); + delete buf; + } + + // If there are more packets to send, schedule event to try again. + if (!sendQueue.empty()) { + buf = sendQueue.front(); + sendEvent.schedule(std::max(buf->ready, curTick + 1)); + } + // Let things start sending again + if (was_full) { + DPRINTF(BusBridge, "Queue was full, sending retry\n"); + otherPort->sendRetry(); + } + } else { DPRINTF(BusBridge, " unsuccessful\n"); } } -Packet * +void Bridge::BridgePort::recvRetry() { - PacketBuffer *buf = sendQueue.front(); - Packet *pkt = buf->pkt; - finishSend(buf); - return pkt; + trySend(); } /** Function called by the port when the bus is receiving a Atomic @@ -223,9 +224,6 @@ Bridge::BridgePort::recvFunctional(Packet *pkt) void Bridge::BridgePort::recvStatusChange(Port::Status status) { - if (status == Port::Blocked || status == Port::Unblocked) - return; - otherPort->sendStatusChange(status); } diff --git a/src/mem/bridge.hh b/src/mem/bridge.hh index 8a5cbf92a..b3525d3e0 100644 --- a/src/mem/bridge.hh +++ b/src/mem/bridge.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Steve Reinhardt */ /** @@ -38,7 +41,6 @@ #include <inttypes.h> #include <queue> - #include "mem/mem_object.hh" #include "mem/packet.hh" #include "mem/port.hh" @@ -77,7 +79,8 @@ class Bridge : public MemObject origSenderState(_pkt->senderState), origSrc(_pkt->getSrc()), expectResponse(_pkt->needsResponse()) { - pkt->senderState = this; + if (!pkt->isResponse()) + pkt->senderState = this; } void fixResponse(Packet *pkt) @@ -146,7 +149,7 @@ class Bridge : public MemObject /** When receiving a retry request from the peer port, pass it to the bridge. */ - virtual Packet* recvRetry(); + virtual void recvRetry(); /** When receiving a Atomic requestfrom the peer port, pass it to the bridge. */ @@ -174,7 +177,7 @@ class Bridge : public MemObject public: /** A function used to return the port associated with this bus object. */ - virtual Port *getPort(const std::string &if_name); + virtual Port *getPort(const std::string &if_name, int idx = -1); virtual void init(); diff --git a/src/mem/bus.cc b/src/mem/bus.cc index cfc99a64f..19a3dc9e4 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @@ -36,7 +38,7 @@ #include "sim/builder.hh" Port * -Bus::getPort(const std::string &if_name) +Bus::getPort(const std::string &if_name, int idx) { // if_name ignored? forced to be empty? int id = interfaces.size(); @@ -72,9 +74,35 @@ Bus::recvTiming(Packet *pkt) assert(dest != pkt->getSrc()); // catch infinite loops port = interfaces[dest]; } - return port->sendTiming(pkt); + if (port->sendTiming(pkt)) { + // packet was successfully sent, just return true. + return true; + } + + // packet not successfully sent + retryList.push_back(interfaces[pkt->getSrc()]); + return false; } +void +Bus::recvRetry(int id) +{ + // Go through all the elements on the list calling sendRetry on each + // This is not very efficient at all but it works. Ultimately we should end + // up with something that is more intelligent. + int initialSize = retryList.size(); + int i; + Port *p; + + for (i = 0; i < initialSize; i++) { + assert(retryList.size() > 0); + p = retryList.front(); + retryList.pop_front(); + p->sendRetry(); + } +} + + Port * Bus::findPort(Addr addr, int id) { diff --git a/src/mem/bus.hh b/src/mem/bus.hh index 5eeb07904..9c7054b94 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ron Dreslinski + * Ali Saidi */ /** @@ -67,6 +70,10 @@ class Bus : public MemObject transaction.*/ void recvFunctional(Packet *pkt); + /** Timing function called by port when it is once again able to process + * requests. */ + void recvRetry(int id); + /** Function called by the port when the bus is recieving a status change.*/ void recvStatusChange(Port::Status status, int id); @@ -126,6 +133,11 @@ class Bus : public MemObject virtual void recvStatusChange(Status status) { bus->recvStatusChange(status, id); } + /** When reciving a retry from the peer port (at id), + pass it to the bus. */ + virtual void recvRetry() + { bus->recvRetry(id); } + // This should return all the 'owned' addresses that are // downstream from this bus, yes? That is, the union of all // the 'owned' address ranges of all the other interfaces on @@ -143,10 +155,14 @@ class Bus : public MemObject connected to this bus.*/ std::vector<Port*> interfaces; + /** An array of pointers to ports that retry should be called on because the + * original send failed for whatever reason.*/ + std::list<Port*> retryList; + public: /** A function used to return the port associated with this bus object. */ - virtual Port *getPort(const std::string &if_name); + virtual Port *getPort(const std::string &if_name, int idx = -1); virtual void init(); diff --git a/src/mem/cache/base_cache.cc b/src/mem/cache/base_cache.cc new file mode 100644 index 000000000..aaaf1bdef --- /dev/null +++ b/src/mem/cache/base_cache.cc @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definition of BaseCache functions. + */ + +#include "mem/cache/base_cache.hh" +#include "cpu/smt.hh" +#include "cpu/base.hh" + +using namespace std; + +BaseCache::CachePort::CachePort(const std::string &_name, BaseCache *_cache, + bool _isCpuSide) + : Port(_name), cache(_cache), isCpuSide(_isCpuSide) +{ + blocked = false; + //Start ports at null if more than one is created we should panic + //cpuSidePort = NULL; + //memSidePort = NULL; +} + +void +BaseCache::CachePort::recvStatusChange(Port::Status status) +{ + cache->recvStatusChange(status, isCpuSide); +} + +void +BaseCache::CachePort::getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) +{ + cache->getAddressRanges(resp, snoop); +} + +int +BaseCache::CachePort::deviceBlockSize() +{ + return cache->getBlockSize(); +} + +bool +BaseCache::CachePort::recvTiming(Packet *pkt) +{ + return cache->doTimingAccess(pkt, this, isCpuSide); +} + +Tick +BaseCache::CachePort::recvAtomic(Packet *pkt) +{ + return cache->doAtomicAccess(pkt, isCpuSide); +} + +void +BaseCache::CachePort::recvFunctional(Packet *pkt) +{ + cache->doFunctionalAccess(pkt, isCpuSide); +} + +void +BaseCache::CachePort::setBlocked() +{ + blocked = true; +} + +void +BaseCache::CachePort::clearBlocked() +{ + blocked = false; +} + +Port* +BaseCache::getPort(const std::string &if_name, int idx) +{ + if (if_name == "") + { + if(cpuSidePort == NULL) + cpuSidePort = new CachePort(name() + "-cpu_side_port", this, true); + return cpuSidePort; + } + if (if_name == "functional") + { + if(cpuSidePort == NULL) + cpuSidePort = new CachePort(name() + "-cpu_side_port", this, true); + return cpuSidePort; + } + else if (if_name == "mem_side") + { + if (memSidePort != NULL) + panic("Already have a mem side for this cache\n"); + memSidePort = new CachePort(name() + "-mem_side_port", this, false); + return memSidePort; + } + else panic("Port name %s unrecognized\n", if_name); +} + +void +BaseCache::regStats() +{ + Request temp_req((Addr) NULL, 4, 0); + Packet::Command temp_cmd = Packet::ReadReq; + Packet temp_pkt(&temp_req, temp_cmd, 0); //@todo FIx command strings so this isn't neccessary + temp_pkt.allocate(); //Temp allocate, all need data + + using namespace Stats; + + // Hit statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + hits[access_idx] + .init(maxThreadsPerCPU) + .name(name() + "." + cstr + "_hits") + .desc("number of " + cstr + " hits") + .flags(total | nozero | nonan) + ; + } + + demandHits + .name(name() + ".demand_hits") + .desc("number of demand (read+write) hits") + .flags(total) + ; + demandHits = hits[Packet::ReadReq] + hits[Packet::WriteReq]; + + overallHits + .name(name() + ".overall_hits") + .desc("number of overall hits") + .flags(total) + ; + overallHits = demandHits + hits[Packet::SoftPFReq] + hits[Packet::HardPFReq] + + hits[Packet::Writeback]; + + // Miss statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + misses[access_idx] + .init(maxThreadsPerCPU) + .name(name() + "." + cstr + "_misses") + .desc("number of " + cstr + " misses") + .flags(total | nozero | nonan) + ; + } + + demandMisses + .name(name() + ".demand_misses") + .desc("number of demand (read+write) misses") + .flags(total) + ; + demandMisses = misses[Packet::ReadReq] + misses[Packet::WriteReq]; + + overallMisses + .name(name() + ".overall_misses") + .desc("number of overall misses") + .flags(total) + ; + overallMisses = demandMisses + misses[Packet::SoftPFReq] + + misses[Packet::HardPFReq] + misses[Packet::Writeback]; + + // Miss latency statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + missLatency[access_idx] + .init(maxThreadsPerCPU) + .name(name() + "." + cstr + "_miss_latency") + .desc("number of " + cstr + " miss cycles") + .flags(total | nozero | nonan) + ; + } + + demandMissLatency + .name(name() + ".demand_miss_latency") + .desc("number of demand (read+write) miss cycles") + .flags(total) + ; + demandMissLatency = missLatency[Packet::ReadReq] + missLatency[Packet::WriteReq]; + + overallMissLatency + .name(name() + ".overall_miss_latency") + .desc("number of overall miss cycles") + .flags(total) + ; + overallMissLatency = demandMissLatency + missLatency[Packet::SoftPFReq] + + missLatency[Packet::HardPFReq]; + + // access formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + accesses[access_idx] + .name(name() + "." + cstr + "_accesses") + .desc("number of " + cstr + " accesses(hits+misses)") + .flags(total | nozero | nonan) + ; + + accesses[access_idx] = hits[access_idx] + misses[access_idx]; + } + + demandAccesses + .name(name() + ".demand_accesses") + .desc("number of demand (read+write) accesses") + .flags(total) + ; + demandAccesses = demandHits + demandMisses; + + overallAccesses + .name(name() + ".overall_accesses") + .desc("number of overall (read+write) accesses") + .flags(total) + ; + overallAccesses = overallHits + overallMisses; + + // miss rate formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + missRate[access_idx] + .name(name() + "." + cstr + "_miss_rate") + .desc("miss rate for " + cstr + " accesses") + .flags(total | nozero | nonan) + ; + + missRate[access_idx] = misses[access_idx] / accesses[access_idx]; + } + + demandMissRate + .name(name() + ".demand_miss_rate") + .desc("miss rate for demand accesses") + .flags(total) + ; + demandMissRate = demandMisses / demandAccesses; + + overallMissRate + .name(name() + ".overall_miss_rate") + .desc("miss rate for overall accesses") + .flags(total) + ; + overallMissRate = overallMisses / overallAccesses; + + // miss latency formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + avgMissLatency[access_idx] + .name(name() + "." + cstr + "_avg_miss_latency") + .desc("average " + cstr + " miss latency") + .flags(total | nozero | nonan) + ; + + avgMissLatency[access_idx] = + missLatency[access_idx] / misses[access_idx]; + } + + demandAvgMissLatency + .name(name() + ".demand_avg_miss_latency") + .desc("average overall miss latency") + .flags(total) + ; + demandAvgMissLatency = demandMissLatency / demandMisses; + + overallAvgMissLatency + .name(name() + ".overall_avg_miss_latency") + .desc("average overall miss latency") + .flags(total) + ; + overallAvgMissLatency = overallMissLatency / overallMisses; + + blocked_cycles.init(NUM_BLOCKED_CAUSES); + blocked_cycles + .name(name() + ".blocked_cycles") + .desc("number of cycles access was blocked") + .subname(Blocked_NoMSHRs, "no_mshrs") + .subname(Blocked_NoTargets, "no_targets") + ; + + + blocked_causes.init(NUM_BLOCKED_CAUSES); + blocked_causes + .name(name() + ".blocked") + .desc("number of cycles access was blocked") + .subname(Blocked_NoMSHRs, "no_mshrs") + .subname(Blocked_NoTargets, "no_targets") + ; + + avg_blocked + .name(name() + ".avg_blocked_cycles") + .desc("average number of cycles each access was blocked") + .subname(Blocked_NoMSHRs, "no_mshrs") + .subname(Blocked_NoTargets, "no_targets") + ; + + avg_blocked = blocked_cycles / blocked_causes; + + fastWrites + .name(name() + ".fast_writes") + .desc("number of fast writes performed") + ; + + cacheCopies + .name(name() + ".cache_copies") + .desc("number of cache copies performed") + ; + +} diff --git a/src/mem/cache/base_cache.hh b/src/mem/cache/base_cache.hh new file mode 100644 index 000000000..2754fab5a --- /dev/null +++ b/src/mem/cache/base_cache.hh @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declares a basic cache interface BaseCache. + */ + +#ifndef __BASE_CACHE_HH__ +#define __BASE_CACHE_HH__ + +#include <vector> +#include <string> +#include <list> +#include <inttypes.h> + +#include "base/misc.hh" +#include "base/statistics.hh" +#include "base/trace.hh" +#include "mem/mem_object.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +/** + * Reasons for Caches to be Blocked. + */ +enum BlockedCause{ + Blocked_NoMSHRs, + Blocked_NoTargets, + Blocked_NoWBBuffers, + Blocked_Coherence, + Blocked_Copy, + NUM_BLOCKED_CAUSES +}; + +/** + * Reasons for cache to request a bus. + */ +enum RequestCause{ + Request_MSHR, + Request_WB, + Request_Coherence, + Request_PF +}; + +/** + * A basic cache interface. Implements some common functions for speed. + */ +class BaseCache : public MemObject +{ + class CachePort : public Port + { + BaseCache *cache; + + public: + CachePort(const std::string &_name, BaseCache *_cache, bool _isCpuSide); + + protected: + virtual bool recvTiming(Packet *pkt); + + virtual Tick recvAtomic(Packet *pkt); + + virtual void recvFunctional(Packet *pkt); + + virtual void recvStatusChange(Status status); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop); + + virtual int deviceBlockSize(); + + public: + void setBlocked(); + + void clearBlocked(); + + bool blocked; + + bool isCpuSide; + }; + + struct CacheEvent : public Event + { + Packet *pkt; + CachePort *cachePort; + + CacheEvent(Packet *pkt, CachePort *cachePort); + void process(); + const char *description(); + }; + + protected: + CachePort *cpuSidePort; + CachePort *memSidePort; + + public: + virtual Port *getPort(const std::string &if_name, int idx = -1); + + private: + //To be defined in cache_impl.hh not in base class + virtual bool doTimingAccess(Packet *pkt, CachePort *cachePort, bool isCpuSide) + { + fatal("No implementation"); + } + + virtual Tick doAtomicAccess(Packet *pkt, bool isCpuSide) + { + fatal("No implementation"); + } + + virtual void doFunctionalAccess(Packet *pkt, bool isCpuSide) + { + fatal("No implementation"); + } + + virtual void recvStatusChange(Port::Status status, bool isCpuSide) + { + fatal("No implementation"); + } + + /** + * Bit vector of the blocking reasons for the access path. + * @sa #BlockedCause + */ + uint8_t blocked; + + /** + * Bit vector for the blocking reasons for the snoop path. + * @sa #BlockedCause + */ + uint8_t blockedSnoop; + + /** + * Bit vector for the outstanding requests for the master interface. + */ + uint8_t masterRequests; + + /** + * Bit vector for the outstanding requests for the slave interface. + */ + uint8_t slaveRequests; + + protected: + + /** True if this cache is connected to the CPU. */ + bool topLevelCache; + + /** Stores time the cache blocked for statistics. */ + Tick blockedCycle; + + /** Block size of this cache */ + const int blkSize; + + /** The number of misses to trigger an exit event. */ + Counter missCount; + + public: + // Statistics + /** + * @addtogroup CacheStatistics + * @{ + */ + + /** Number of hits per thread for each type of command. @sa Packet::Command */ + Stats::Vector<> hits[NUM_MEM_CMDS]; + /** Number of hits for demand accesses. */ + Stats::Formula demandHits; + /** Number of hit for all accesses. */ + Stats::Formula overallHits; + + /** Number of misses per thread for each type of command. @sa Packet::Command */ + Stats::Vector<> misses[NUM_MEM_CMDS]; + /** Number of misses for demand accesses. */ + Stats::Formula demandMisses; + /** Number of misses for all accesses. */ + Stats::Formula overallMisses; + + /** + * Total number of cycles per thread/command spent waiting for a miss. + * Used to calculate the average miss latency. + */ + Stats::Vector<> missLatency[NUM_MEM_CMDS]; + /** Total number of cycles spent waiting for demand misses. */ + Stats::Formula demandMissLatency; + /** Total number of cycles spent waiting for all misses. */ + Stats::Formula overallMissLatency; + + /** The number of accesses per command and thread. */ + Stats::Formula accesses[NUM_MEM_CMDS]; + /** The number of demand accesses. */ + Stats::Formula demandAccesses; + /** The number of overall accesses. */ + Stats::Formula overallAccesses; + + /** The miss rate per command and thread. */ + Stats::Formula missRate[NUM_MEM_CMDS]; + /** The miss rate of all demand accesses. */ + Stats::Formula demandMissRate; + /** The miss rate for all accesses. */ + Stats::Formula overallMissRate; + + /** The average miss latency per command and thread. */ + Stats::Formula avgMissLatency[NUM_MEM_CMDS]; + /** The average miss latency for demand misses. */ + Stats::Formula demandAvgMissLatency; + /** The average miss latency for all misses. */ + Stats::Formula overallAvgMissLatency; + + /** The total number of cycles blocked for each blocked cause. */ + Stats::Vector<> blocked_cycles; + /** The number of times this cache blocked for each blocked cause. */ + Stats::Vector<> blocked_causes; + + /** The average number of cycles blocked for each blocked cause. */ + Stats::Formula avg_blocked; + + /** The number of fast writes (WH64) performed. */ + Stats::Scalar<> fastWrites; + + /** The number of cache copies performed. */ + Stats::Scalar<> cacheCopies; + + /** + * @} + */ + + /** + * Register stats for this object. + */ + virtual void regStats(); + + public: + + class Params + { + public: + /** List of address ranges of this cache. */ + std::vector<Range<Addr> > addrRange; + /** The hit latency for this cache. */ + int hitLatency; + /** The block size of this cache. */ + int blkSize; + /** + * The maximum number of misses this cache should handle before + * ending the simulation. + */ + Counter maxMisses; + + /** + * Construct an instance of this parameter class. + */ + Params(std::vector<Range<Addr> > addr_range, + int hit_latency, int _blkSize, Counter max_misses) + : addrRange(addr_range), hitLatency(hit_latency), blkSize(_blkSize), + maxMisses(max_misses) + { + } + }; + + /** + * Create and initialize a basic cache object. + * @param name The name of this cache. + * @param hier_params Pointer to the HierParams object for this hierarchy + * of this cache. + * @param params The parameter object for this BaseCache. + */ + BaseCache(const std::string &name, Params ¶ms) + : MemObject(name), blocked(0), blockedSnoop(0), masterRequests(0), + slaveRequests(0), topLevelCache(false), blkSize(params.blkSize), + missCount(params.maxMisses) + { + //Start ports at null if more than one is created we should panic + cpuSidePort = NULL; + memSidePort = NULL; + } + + /** + * Query block size of a cache. + * @return The block size + */ + int getBlockSize() const + { + return blkSize; + } + + /** + * Returns true if this cache is connect to the CPU. + * @return True if this is a L1 cache. + */ + bool isTopLevel() + { + return topLevelCache; + } + + /** + * Returns true if the cache is blocked for accesses. + */ + bool isBlocked() + { + return blocked != 0; + } + + /** + * Returns true if the cache is blocked for snoops. + */ + bool isBlockedForSnoop() + { + return blockedSnoop != 0; + } + + /** + * Marks the access path of the cache as blocked for the given cause. This + * also sets the blocked flag in the slave interface. + * @param cause The reason for the cache blocking. + */ + void setBlocked(BlockedCause cause) + { + uint8_t flag = 1 << cause; + if (blocked == 0) { + blocked_causes[cause]++; + blockedCycle = curTick; + } + blocked |= flag; + DPRINTF(Cache,"Blocking for cause %s\n", cause); + cpuSidePort->setBlocked(); + } + + /** + * Marks the snoop path of the cache as blocked for the given cause. This + * also sets the blocked flag in the master interface. + * @param cause The reason to block the snoop path. + */ + void setBlockedForSnoop(BlockedCause cause) + { + uint8_t flag = 1 << cause; + blockedSnoop |= flag; + memSidePort->setBlocked(); + } + + /** + * Marks the cache as unblocked for the given cause. This also clears the + * blocked flags in the appropriate interfaces. + * @param cause The newly unblocked cause. + * @warning Calling this function can cause a blocked request on the bus to + * access the cache. The cache must be in a state to handle that request. + */ + void clearBlocked(BlockedCause cause) + { + uint8_t flag = 1 << cause; + blocked &= ~flag; + blockedSnoop &= ~flag; + DPRINTF(Cache,"Unblocking for cause %s, causes left=%i\n", + cause, blocked); + if (!isBlocked()) { + blocked_cycles[cause] += curTick - blockedCycle; + DPRINTF(Cache,"Unblocking from all causes\n"); + cpuSidePort->clearBlocked(); + } + if (!isBlockedForSnoop()) { + memSidePort->clearBlocked(); + } + + } + + /** + * True if the master bus should be requested. + * @return True if there are outstanding requests for the master bus. + */ + bool doMasterRequest() + { + return masterRequests != 0; + } + + /** + * Request the master bus for the given cause and time. + * @param cause The reason for the request. + * @param time The time to make the request. + */ + void setMasterRequest(RequestCause cause, Tick time) + { + uint8_t flag = 1<<cause; + masterRequests |= flag; + assert("Implement\n" && 0); +// mi->pktuest(time); + } + + /** + * Clear the master bus request for the given cause. + * @param cause The request reason to clear. + */ + void clearMasterRequest(RequestCause cause) + { + uint8_t flag = 1<<cause; + masterRequests &= ~flag; + } + + /** + * Return true if the slave bus should be requested. + * @return True if there are outstanding requests for the slave bus. + */ + bool doSlaveRequest() + { + return slaveRequests != 0; + } + + /** + * Request the slave bus for the given reason and time. + * @param cause The reason for the request. + * @param time The time to make the request. + */ + void setSlaveRequest(RequestCause cause, Tick time) + { + uint8_t flag = 1<<cause; + slaveRequests |= flag; + assert("Implement\n" && 0); +// si->pktuest(time); + } + + /** + * Clear the slave bus request for the given reason. + * @param cause The request reason to clear. + */ + void clearSlaveRequest(RequestCause cause) + { + uint8_t flag = 1<<cause; + slaveRequests &= ~flag; + } + + /** + * Send a response to the slave interface. + * @param req The request being responded to. + * @param time The time the response is ready. + */ + void respond(Packet *pkt, Tick time) + { + assert("Implement\n" && 0); +// si->respond(pkt,time); + } + + /** + * Send a reponse to the slave interface and calculate miss latency. + * @param req The request to respond to. + * @param time The time the response is ready. + */ + void respondToMiss(Packet *pkt, Tick time) + { + if (!pkt->req->isUncacheable()) { + missLatency[pkt->cmdToIndex()][pkt->req->getThreadNum()] += time - pkt->time; + } + assert("Implement\n" && 0); +// si->respond(pkt,time); + } + + /** + * Suppliess the data if cache to cache transfers are enabled. + * @param req The bus transaction to fulfill. + */ + void respondToSnoop(Packet *pkt) + { + assert("Implement\n" && 0); +// mi->respond(pkt,curTick + hitLatency); + } + + /** + * Notification from master interface that a address range changed. Nothing + * to do for a cache. + */ + void rangeChange() {} + + void getAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) + { + panic("Unimplimented\n"); + } +}; + +#endif //__BASE_CACHE_HH__ diff --git a/src/mem/cache/cache.cc b/src/mem/cache/cache.cc new file mode 100644 index 000000000..db66c096e --- /dev/null +++ b/src/mem/cache/cache.cc @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Steve Reinhardt + * Lisa Hsu + * Kevin Lim + */ + +/** + * @file + * Cache template instantiations. + */ + +#include "mem/config/cache.hh" +#include "mem/config/compression.hh" + +#include "mem/cache/tags/cache_tags.hh" + +#if defined(USE_CACHE_LRU) +#include "mem/cache/tags/lru.hh" +#endif + +#if defined(USE_CACHE_FALRU) +#include "mem/cache/tags/fa_lru.hh" +#endif + +#if defined(USE_CACHE_IIC) +#include "mem/cache/tags/iic.hh" +#endif + +#if defined(USE_CACHE_SPLIT) +#include "mem/cache/tags/split.hh" +#endif + +#if defined(USE_CACHE_SPLIT_LIFO) +#include "mem/cache/tags/split_lifo.hh" +#endif + +#include "base/compression/null_compression.hh" +#if defined(USE_LZSS_COMPRESSION) +#include "base/compression/lzss_compression.hh" +#endif + +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/miss/blocking_buffer.hh" + +#include "mem/cache/coherence/uni_coherence.hh" +#include "mem/cache/coherence/simple_coherence.hh" + +#include "mem/cache/cache_impl.hh" + +// Template Instantiations +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + +#if defined(USE_CACHE_FALRU) +template class Cache<CacheTags<FALRU,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<FALRU,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<FALRU,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<FALRU,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<FALRU,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<FALRU,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<FALRU,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<FALRU,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#if defined(USE_CACHE_IIC) +template class Cache<CacheTags<IIC,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<IIC,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<IIC,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<IIC,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<IIC,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<IIC,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<IIC,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<IIC,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#if defined(USE_CACHE_LRU) +template class Cache<CacheTags<LRU,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<LRU,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<LRU,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<LRU,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<LRU,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<LRU,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<LRU,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<LRU,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#if defined(USE_CACHE_SPLIT) +template class Cache<CacheTags<Split,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<Split,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<Split,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<Split,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<Split,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<Split,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<Split,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<Split,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#if defined(USE_CACHE_SPLIT_LIFO) +template class Cache<CacheTags<SplitLIFO,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<SplitLIFO,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<SplitLIFO,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<SplitLIFO,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<SplitLIFO,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<SplitLIFO,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<SplitLIFO,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<SplitLIFO,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/cache.hh b/src/mem/cache/cache.hh new file mode 100644 index 000000000..1243c9d9e --- /dev/null +++ b/src/mem/cache/cache.hh @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Dave Greene + * Steve Reinhardt + */ + +/** + * @file + * Describes a cache based on template policies. + */ + +#ifndef __CACHE_HH__ +#define __CACHE_HH__ + +#include "base/misc.hh" // fatal, panic, and warn +#include "cpu/smt.hh" // SMT_MAX_THREADS + +#include "mem/cache/base_cache.hh" +#include "mem/cache/prefetch/prefetcher.hh" + +//Forward decleration +class MSHR; + + +/** + * A template-policy based cache. The behavior of the cache can be altered by + * supplying different template policies. TagStore handles all tag and data + * storage @sa TagStore. Buffering handles all misses and writes/writebacks + * @sa MissQueue. Coherence handles all coherence policy details @sa + * UniCoherence, SimpleMultiCoherence. + */ +template <class TagStore, class Buffering, class Coherence> +class Cache : public BaseCache +{ + public: + /** Define the type of cache block to use. */ + typedef typename TagStore::BlkType BlkType; + + bool prefetchAccess; + protected: + + /** Tag and data Storage */ + TagStore *tags; + /** Miss and Writeback handler */ + Buffering *missQueue; + /** Coherence protocol. */ + Coherence *coherence; + + /** Prefetcher */ + Prefetcher<TagStore, Buffering> *prefetcher; + + /** Do fast copies in this cache. */ + bool doCopy; + + /** Block on a delayed copy. */ + bool blockOnCopy; + + /** + * The clock ratio of the outgoing bus. + * Used for calculating critical word first. + */ + int busRatio; + + /** + * The bus width in bytes of the outgoing bus. + * Used for calculating critical word first. + */ + int busWidth; + + /** + * The latency of a hit in this device. + */ + int hitLatency; + + /** + * A permanent mem req to always be used to cause invalidations. + * Used to append to target list, to cause an invalidation. + */ + Packet * invalidatePkt; + + /** + * Temporarily move a block into a MSHR. + * @todo Remove this when LSQ/SB are fixed and implemented in memtest. + */ + void pseudoFill(Addr addr, int asid); + + /** + * Temporarily move a block into an existing MSHR. + * @todo Remove this when LSQ/SB are fixed and implemented in memtest. + */ + void pseudoFill(MSHR *mshr); + + public: + + class Params + { + public: + TagStore *tags; + Buffering *missQueue; + Coherence *coherence; + bool doCopy; + bool blockOnCopy; + BaseCache::Params baseParams; + Prefetcher<TagStore, Buffering> *prefetcher; + bool prefetchAccess; + int hitLatency; + + Params(TagStore *_tags, Buffering *mq, Coherence *coh, + bool do_copy, BaseCache::Params params, + Prefetcher<TagStore, Buffering> *_prefetcher, + bool prefetch_access, int hit_latency) + : tags(_tags), missQueue(mq), coherence(coh), doCopy(do_copy), + blockOnCopy(false), baseParams(params), + prefetcher(_prefetcher), prefetchAccess(prefetch_access), + hitLatency(hit_latency) + { + } + }; + + /** Instantiates a basic cache object. */ + Cache(const std::string &_name, Params ¶ms); + + virtual bool doTimingAccess(Packet *pkt, CachePort *cachePort, + bool isCpuSide); + + virtual Tick doAtomicAccess(Packet *pkt, bool isCpuSide); + + virtual void doFunctionalAccess(Packet *pkt, bool isCpuSide); + + virtual void recvStatusChange(Port::Status status, bool isCpuSide); + + void regStats(); + + /** + * Performs the access specified by the request. + * @param req The request to perform. + * @return The result of the access. + */ + bool access(Packet * &pkt); + + /** + * Selects a request to send on the bus. + * @return The memory request to service. + */ + Packet * getPacket(); + + /** + * Was the request was sent successfully? + * @param req The request. + * @param success True if the request was sent successfully. + */ + void sendResult(Packet * &pkt, bool success); + + /** + * Handles a response (cache line fill/write ack) from the bus. + * @param req The request being responded to. + */ + void handleResponse(Packet * &pkt); + + /** + * Start handling a copy transaction. + * @param req The copy request to perform. + */ + void startCopy(Packet * &pkt); + + /** + * Handle a delayed copy transaction. + * @param req The delayed copy request to continue. + * @param addr The address being responded to. + * @param blk The block of the current response. + * @param mshr The mshr being handled. + */ + void handleCopy(Packet * &pkt, Addr addr, BlkType *blk, MSHR *mshr); + + /** + * Selects a coherence message to forward to lower levels of the hierarchy. + * @return The coherence message to forward. + */ + Packet * getCoherenceReq(); + + /** + * Snoops bus transactions to maintain coherence. + * @param req The current bus transaction. + */ + void snoop(Packet * &pkt); + + void snoopResponse(Packet * &pkt); + + /** + * Invalidates the block containing address if found. + * @param addr The address to look for. + * @param asid The address space ID of the address. + * @todo Is this function necessary? + */ + void invalidateBlk(Addr addr, int asid); + + /** + * Aquash all requests associated with specified thread. + * intended for use by I-cache. + * @param req->getThreadNum()ber The thread to squash. + */ + void squash(int threadNum) + { + missQueue->squash(threadNum); + } + + /** + * Return the number of outstanding misses in a Cache. + * Default returns 0. + * + * @retval unsigned The number of missing still outstanding. + */ + unsigned outstandingMisses() const + { + return missQueue->getMisses(); + } + + /** + * Send a response to the slave interface. + * @param req The request being responded to. + * @param time The time the response is ready. + */ + void respond(Packet * &pkt, Tick time) + { + //si->respond(pkt,time); + cpuSidePort->sendAtomic(pkt); + } + + /** + * Perform the access specified in the request and return the estimated + * time of completion. This function can either update the hierarchy state + * or just perform the access wherever the data is found depending on the + * state of the update flag. + * @param req The memory request to satisfy + * @param update If true, update the hierarchy, otherwise just perform the + * request. + * @return The estimated completion time. + */ + Tick probe(Packet * &pkt, bool update); + + /** + * Snoop for the provided request in the cache and return the estimated + * time of completion. + * @todo Can a snoop probe not change state? + * @param req The memory request to satisfy + * @param update If true, update the hierarchy, otherwise just perform the + * request. + * @return The estimated completion time. + */ + Tick snoopProbe(Packet * &pkt, bool update); +}; + +#endif // __CACHE_HH__ diff --git a/src/mem/cache/cache_blk.hh b/src/mem/cache/cache_blk.hh new file mode 100644 index 000000000..67e65d25b --- /dev/null +++ b/src/mem/cache/cache_blk.hh @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** @file + * Definitions of a simple cache block class. + */ + +#ifndef __CACHE_BLK_HH__ +#define __CACHE_BLK_HH__ + +#include "sim/root.hh" // for Tick +#include "arch/isa_traits.hh" // for Addr + +/** + * Cache block status bit assignments + */ +enum CacheBlkStatusBits { + /** valid, readable */ + BlkValid = 0x01, + /** write permission */ + BlkWritable = 0x02, + /** dirty (modified) */ + BlkDirty = 0x04, + /** compressed */ + BlkCompressed = 0x08, + /** block was referenced */ + BlkReferenced = 0x10, + /** block was a hardware prefetch yet unaccessed*/ + BlkHWPrefetched = 0x20 +}; + +/** + * A Basic Cache block. + * Contains the tag, status, and a pointer to data. + */ +class CacheBlk +{ + public: + /** The address space ID of this block. */ + int asid; + /** Data block tag value. */ + Addr tag; + /** + * Contains a copy of the data in this block for easy access. This is used + * for efficient execution when the data could be actually stored in + * another format (COW, compressed, sub-blocked, etc). In all cases the + * data stored here should be kept consistant with the actual data + * referenced by this block. + */ + uint8_t *data; + /** the number of bytes stored in this block. */ + int size; + + /** block state: OR of CacheBlkStatusBit */ + typedef unsigned State; + + /** The current status of this block. @sa CacheBlockStatusBits */ + State status; + + /** Which curTick will this block be accessable */ + Tick whenReady; + + /** + * The set this block belongs to. + * @todo Move this into subclasses when we fix CacheTags to use them. + */ + int set; + + /** Number of references to this block since it was brought in. */ + int refCount; + + CacheBlk() + : asid(-1), tag(0), data(0) ,size(0), status(0), whenReady(0), + set(-1), refCount(0) + {} + + /** + * Copy the state of the given block into this one. + * @param rhs The block to copy. + * @return a const reference to this block. + */ + const CacheBlk& operator=(const CacheBlk& rhs) + { + asid = rhs.asid; + tag = rhs.tag; + data = rhs.data; + size = rhs.size; + status = rhs.status; + whenReady = rhs.whenReady; + set = rhs.set; + refCount = rhs.refCount; + return *this; + } + + /** + * Checks the write permissions of this block. + * @return True if the block is writable. + */ + bool isWritable() const + { + const int needed_bits = BlkWritable | BlkValid; + return (status & needed_bits) == needed_bits; + } + + /** + * Checks that a block is valid (readable). + * @return True if the block is valid. + */ + bool isValid() const + { + return (status & BlkValid) != 0; + } + + /** + * Check to see if a block has been written. + * @return True if the block is dirty. + */ + bool isModified() const + { + return (status & BlkDirty) != 0; + } + + /** + * Check to see if this block contains compressed data. + * @return True iF the block's data is compressed. + */ + bool isCompressed() const + { + return (status & BlkCompressed) != 0; + } + + /** + * Check if this block has been referenced. + * @return True if the block has been referenced. + */ + bool isReferenced() const + { + return (status & BlkReferenced) != 0; + } + + /** + * Check if this block was the result of a hardware prefetch, yet to + * be touched. + * @return True if the block was a hardware prefetch, unaccesed. + */ + bool isPrefetch() const + { + return (status & BlkHWPrefetched) != 0; + } + + +}; + +/** + * Output a CacheBlk to the given ostream. + * @param out The stream for the output. + * @param blk The cache block to print. + * + * @return The output stream. + */ +inline std::ostream & +operator<<(std::ostream &out, const CacheBlk &blk) +{ + out << std::hex << std::endl; + out << " Tag: " << blk.tag << std::endl; + out << " Status: " << blk.status << std::endl; + + return(out << std::dec); +} + +#endif //__CACHE_BLK_HH__ diff --git a/src/mem/cache/cache_builder.cc b/src/mem/cache/cache_builder.cc new file mode 100644 index 000000000..05a149a1c --- /dev/null +++ b/src/mem/cache/cache_builder.cc @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Nathan Binkert + */ + +/** + * @file + * Simobject instatiation of caches. + */ +#include <vector> + +// Must be included first to determine which caches we want +#include "mem/config/cache.hh" +#include "mem/config/compression.hh" +#include "mem/config/prefetch.hh" + +#include "mem/cache/base_cache.hh" +#include "mem/cache/cache.hh" +#include "mem/bus.hh" +#include "mem/cache/coherence/coherence_protocol.hh" +#include "sim/builder.hh" + +// Tag Templates +#if defined(USE_CACHE_LRU) +#include "mem/cache/tags/lru.hh" +#endif + +#if defined(USE_CACHE_FALRU) +#include "mem/cache/tags/fa_lru.hh" +#endif + +#if defined(USE_CACHE_IIC) +#include "mem/cache/tags/iic.hh" +#endif + +#if defined(USE_CACHE_SPLIT) +#include "mem/cache/tags/split.hh" +#endif + +#if defined(USE_CACHE_SPLIT_LIFO) +#include "mem/cache/tags/split_lifo.hh" +#endif + +// Compression Templates +#include "base/compression/null_compression.hh" +#if defined(USE_LZSS_COMPRESSION) +#include "base/compression/lzss_compression.hh" +#endif + +// CacheTags Templates +#include "mem/cache/tags/cache_tags.hh" + +// MissQueue Templates +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/miss/blocking_buffer.hh" + +// Coherence Templates +#include "mem/cache/coherence/uni_coherence.hh" +#include "mem/cache/coherence/simple_coherence.hh" + +//Prefetcher Headers +#if defined(USE_GHB) +#include "mem/cache/prefetch/ghb_prefetcher.hh" +#endif +#if defined(USE_TAGGED) +#include "mem/cache/prefetch/tagged_prefetcher.hh" +#endif +#if defined(USE_STRIDED) +#include "mem/cache/prefetch/stride_prefetcher.hh" +#endif + + +using namespace std; +using namespace TheISA; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(BaseCache) + + Param<int> size; + Param<int> assoc; + Param<int> block_size; + Param<int> latency; + Param<int> mshrs; + Param<int> tgts_per_mshr; + Param<int> write_buffers; + Param<bool> prioritizeRequests; +// SimObjectParam<Bus *> in_bus; +// SimObjectParam<Bus *> out_bus; + Param<bool> do_copy; + SimObjectParam<CoherenceProtocol *> protocol; + Param<Addr> trace_addr; + Param<int> hash_delay; +#if defined(USE_CACHE_IIC) + SimObjectParam<Repl *> repl; +#endif + Param<bool> compressed_bus; + Param<bool> store_compressed; + Param<bool> adaptive_compression; + Param<int> compression_latency; + Param<int> subblock_size; + Param<Counter> max_miss_count; +// SimObjectParam<HierParams *> hier; + VectorParam<Range<Addr> > addr_range; +// SimObjectParam<MemTraceWriter *> mem_trace; + Param<bool> split; + Param<int> split_size; + Param<bool> lifo; + Param<bool> two_queue; + Param<bool> prefetch_miss; + Param<bool> prefetch_access; + Param<int> prefetcher_size; + Param<bool> prefetch_past_page; + Param<bool> prefetch_serial_squash; + Param<Tick> prefetch_latency; + Param<int> prefetch_degree; + Param<string> prefetch_policy; + Param<bool> prefetch_cache_check_push; + Param<bool> prefetch_use_cpu_id; + Param<bool> prefetch_data_accesses_only; + Param<int> hit_latency; + +END_DECLARE_SIM_OBJECT_PARAMS(BaseCache) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(BaseCache) + + INIT_PARAM(size, "capacity in bytes"), + INIT_PARAM(assoc, "associativity"), + INIT_PARAM(block_size, "block size in bytes"), + INIT_PARAM(latency, "hit latency in CPU cycles"), + INIT_PARAM(mshrs, "number of MSHRs (max outstanding requests)"), + INIT_PARAM(tgts_per_mshr, "max number of accesses per MSHR"), + INIT_PARAM_DFLT(write_buffers, "number of write buffers", 8), + INIT_PARAM_DFLT(prioritizeRequests, "always service demand misses first", + false), +/* INIT_PARAM_DFLT(in_bus, "incoming bus object", NULL), + INIT_PARAM(out_bus, "outgoing bus object"), +*/ + INIT_PARAM_DFLT(do_copy, "perform fast copies in the cache", false), + INIT_PARAM_DFLT(protocol, "coherence protocol to use in the cache", NULL), + INIT_PARAM_DFLT(trace_addr, "address to trace", 0), + + INIT_PARAM_DFLT(hash_delay, "time in cycles of hash access",1), +#if defined(USE_CACHE_IIC) + INIT_PARAM_DFLT(repl, "replacement policy",NULL), +#endif + INIT_PARAM_DFLT(compressed_bus, + "This cache connects to a compressed memory", + false), + INIT_PARAM_DFLT(store_compressed, "Store compressed data in the cache", + false), + INIT_PARAM_DFLT(adaptive_compression, "Use an adaptive compression scheme", + false), + INIT_PARAM_DFLT(compression_latency, + "Latency in cycles of compression algorithm", + 0), + INIT_PARAM_DFLT(subblock_size, + "Size of subblock in IIC used for compression", + 0), + INIT_PARAM_DFLT(max_miss_count, + "The number of misses to handle before calling exit", + 0), +/* INIT_PARAM_DFLT(hier, + "Hierarchy global variables", + &defaultHierParams), +*/ + INIT_PARAM_DFLT(addr_range, "The address range in bytes", + vector<Range<Addr> >(1,RangeIn((Addr)0, MaxAddr))), +// INIT_PARAM_DFLT(mem_trace, "Memory trace to write accesses to", NULL), + INIT_PARAM_DFLT(split, "Whether this is a partitioned cache", false), + INIT_PARAM_DFLT(split_size, "the number of \"ways\" belonging to the LRU partition", 0), + INIT_PARAM_DFLT(lifo, "whether you are using a LIFO repl. policy", false), + INIT_PARAM_DFLT(two_queue, "whether the lifo should have two queue replacement", false), + INIT_PARAM_DFLT(prefetch_miss, "wheter you are using the hardware prefetcher from Miss stream", false), + INIT_PARAM_DFLT(prefetch_access, "wheter you are using the hardware prefetcher from Access stream", false), + INIT_PARAM_DFLT(prefetcher_size, "Number of entries in the harware prefetch queue", 100), + INIT_PARAM_DFLT(prefetch_past_page, "Allow prefetches to cross virtual page boundaries", false), + INIT_PARAM_DFLT(prefetch_serial_squash, "Squash prefetches with a later time on a subsequent miss", false), + INIT_PARAM_DFLT(prefetch_latency, "Latency of the prefetcher", 10), + INIT_PARAM_DFLT(prefetch_degree, "Degree of the prefetch depth", 1), + INIT_PARAM_DFLT(prefetch_policy, "Type of prefetcher to use", "none"), + INIT_PARAM_DFLT(prefetch_cache_check_push, "Check if in cash on push or pop of prefetch queue", true), + INIT_PARAM_DFLT(prefetch_use_cpu_id, "Use the CPU ID to seperate calculations of prefetches", true), + INIT_PARAM_DFLT(prefetch_data_accesses_only, "Only prefetch on data not on instruction accesses", false), + INIT_PARAM_DFLT(hit_latency, "Hit Latecny for a succesful access", 1) +END_INIT_SIM_OBJECT_PARAMS(BaseCache) + + +#define BUILD_CACHE(t, comp, b, c) do { \ + Prefetcher<CacheTags<t, comp>, b> *pf; \ + if (pf_policy == "tagged") { \ + BUILD_TAGGED_PREFETCHER(t, comp, b); \ + } \ + else if (pf_policy == "stride") { \ + BUILD_STRIDED_PREFETCHER(t, comp, b); \ + } \ + else if (pf_policy == "ghb") { \ + BUILD_GHB_PREFETCHER(t, comp, b); \ + } \ + else { \ + BUILD_NULL_PREFETCHER(t, comp, b); \ + } \ + Cache<CacheTags<t, comp>, b, c>::Params params(tagStore, mq, coh, \ + do_copy, base_params, \ + /*in_bus, out_bus,*/ pf, \ + prefetch_access, hit_latency); \ + Cache<CacheTags<t, comp>, b, c> *retval = \ + new Cache<CacheTags<t, comp>, b, c>(getInstanceName(), /*hier,*/ \ + params); \ +/* if (in_bus == NULL) { \ + retval->setSlaveInterface(new MemoryInterface<Cache<CacheTags<t, comp>, b, c> >(getInstanceName(), hier, retval, mem_trace)); \ + } else { \ + retval->setSlaveInterface(new SlaveInterface<Cache<CacheTags<t, comp>, b, c>, Bus>(getInstanceName(), hier, retval, in_bus, mem_trace)); \ + } \ + retval->setMasterInterface(new MasterInterface<Cache<CacheTags<t, comp>, b, c>, Bus>(getInstanceName(), hier, retval, out_bus)); \ + out_bus->rangeChange(); \ + return retval; \ +*/return retval; \ + } while (0) + +#define BUILD_CACHE_PANIC(x) do { \ + panic("%s not compiled into M5", x); \ + } while (0) + +#if defined(USE_LZSS_COMPRESSION) +#define BUILD_COMPRESSED_CACHE(TAGS, tags, b, c) do { \ + if (compressed_bus || store_compressed){ \ + CacheTags<TAGS, LZSSCompression> *tagStore = \ + new CacheTags<TAGS, LZSSCompression>(tags, \ + compression_latency, \ + true, store_compressed, \ + adaptive_compression, \ + prefetch_miss); \ + BUILD_CACHE(TAGS, LZSSCompression, b, c); \ + } else { \ + CacheTags<TAGS, NullCompression> *tagStore = \ + new CacheTags<TAGS, NullCompression>(tags, \ + compression_latency, \ + true, store_compressed, \ + adaptive_compression, \ + prefetch_miss); \ + BUILD_CACHE(TAGS, NullCompression, b, c); \ + } \ + } while (0) +#else +#define BUILD_COMPRESSED_CACHE(TAGS, tags, b, c) do { \ + if (compressed_bus || store_compressed){ \ + BUILD_CACHE_PANIC("compressed caches"); \ + } else { \ + CacheTags<TAGS, NullCompression> *tagStore = \ + new CacheTags<TAGS, NullCompression>(tags, \ + compression_latency, \ + true, store_compressed, \ + adaptive_compression \ + prefetch_miss); \ + BUILD_CACHE(TAGS, NullCompression, b, c); \ + } \ + } while (0) +#endif + +#if defined(USE_CACHE_FALRU) +#define BUILD_FALRU_CACHE(b,c) do { \ + FALRU *tags = new FALRU(block_size, size, latency); \ + BUILD_COMPRESSED_CACHE(FALRU, tags, b, c); \ + } while (0) +#else +#define BUILD_FALRU_CACHE(b, c) BUILD_CACHE_PANIC("falru cache") +#endif + +#if defined(USE_CACHE_LRU) +#define BUILD_LRU_CACHE(b, c) do { \ + LRU *tags = new LRU(numSets, block_size, assoc, latency); \ + BUILD_COMPRESSED_CACHE(LRU, tags, b, c); \ + } while (0) +#else +#define BUILD_LRU_CACHE(b, c) BUILD_CACHE_PANIC("lru cache") +#endif + +#if defined(USE_CACHE_SPLIT) +#define BUILD_SPLIT_CACHE(b, c) do { \ + Split *tags = new Split(numSets, block_size, assoc, split_size, lifo, \ + two_queue, latency); \ + BUILD_COMPRESSED_CACHE(Split, tags, b, c); \ + } while (0) +#else +#define BUILD_SPLIT_CACHE(b, c) BUILD_CACHE_PANIC("split cache") +#endif + +#if defined(USE_CACHE_SPLIT_LIFO) +#define BUILD_SPLIT_LIFO_CACHE(b, c) do { \ + SplitLIFO *tags = new SplitLIFO(block_size, size, assoc, \ + latency, two_queue, -1); \ + BUILD_COMPRESSED_CACHE(SplitLIFO, tags, b, c); \ + } while (0) +#else +#define BUILD_SPLIT_LIFO_CACHE(b, c) BUILD_CACHE_PANIC("lifo cache") +#endif + +#if defined(USE_CACHE_IIC) +#define BUILD_IIC_CACHE(b ,c) do { \ + IIC *tags = new IIC(iic_params); \ + BUILD_COMPRESSED_CACHE(IIC, tags, b, c); \ + } while (0) +#else +#define BUILD_IIC_CACHE(b, c) BUILD_CACHE_PANIC("iic") +#endif + +#define BUILD_CACHES(b, c) do { \ + if (repl == NULL) { \ + if (numSets == 1) { \ + BUILD_FALRU_CACHE(b, c); \ + } else { \ + if (split == true) { \ + BUILD_SPLIT_CACHE(b, c); \ + } else if (lifo == true) { \ + BUILD_SPLIT_LIFO_CACHE(b, c); \ + } else { \ + BUILD_LRU_CACHE(b, c); \ + } \ + } \ + } else { \ + BUILD_IIC_CACHE(b, c); \ + } \ + } while (0) + +#define BUILD_COHERENCE(b) do { \ + if (protocol == NULL) { \ + UniCoherence *coh = new UniCoherence(); \ + BUILD_CACHES(b, UniCoherence); \ + } else { \ + SimpleCoherence *coh = new SimpleCoherence(protocol); \ + BUILD_CACHES(b, SimpleCoherence); \ + } \ + } while (0) + +#if defined(USE_TAGGED) +#define BUILD_TAGGED_PREFETCHER(t, comp, b) pf = new \ + TaggedPrefetcher<CacheTags<t, comp>, b>(prefetcher_size, \ + !prefetch_past_page, \ + prefetch_serial_squash, \ + prefetch_cache_check_push, \ + prefetch_data_accesses_only, \ + prefetch_latency, \ + prefetch_degree) +#else +#define BUILD_TAGGED_PREFETCHER(t, comp, b) BUILD_CACHE_PANIC("Tagged Prefetcher") +#endif + +#if defined(USE_STRIDED) +#define BUILD_STRIDED_PREFETCHER(t, comp, b) pf = new \ + StridePrefetcher<CacheTags<t, comp>, b>(prefetcher_size, \ + !prefetch_past_page, \ + prefetch_serial_squash, \ + prefetch_cache_check_push, \ + prefetch_data_accesses_only, \ + prefetch_latency, \ + prefetch_degree, \ + prefetch_use_cpu_id) +#else +#define BUILD_STRIDED_PREFETCHER(t, comp, b) BUILD_CACHE_PANIC("Stride Prefetcher") +#endif + +#if defined(USE_GHB) +#define BUILD_GHB_PREFETCHER(t, comp, b) pf = new \ + GHBPrefetcher<CacheTags<t, comp>, b>(prefetcher_size, \ + !prefetch_past_page, \ + prefetch_serial_squash, \ + prefetch_cache_check_push, \ + prefetch_data_accesses_only, \ + prefetch_latency, \ + prefetch_degree, \ + prefetch_use_cpu_id) +#else +#define BUILD_GHB_PREFETCHER(t, comp, b) BUILD_CACHE_PANIC("GHB Prefetcher") +#endif + +#if defined(USE_TAGGED) +#define BUILD_NULL_PREFETCHER(t, comp, b) pf = new \ + TaggedPrefetcher<CacheTags<t, comp>, b>(prefetcher_size, \ + !prefetch_past_page, \ + prefetch_serial_squash, \ + prefetch_cache_check_push, \ + prefetch_data_accesses_only, \ + prefetch_latency, \ + prefetch_degree) +#else +#define BUILD_NULL_PREFETCHER(t, comp, b) BUILD_CACHE_PANIC("NULL Prefetcher (uses Tagged)") +#endif + +CREATE_SIM_OBJECT(BaseCache) +{ + string name = getInstanceName(); + int numSets = size / (assoc * block_size); + string pf_policy = prefetch_policy; + if (subblock_size == 0) { + subblock_size = block_size; + } + + // Build BaseCache param object + BaseCache::Params base_params(addr_range, latency, + block_size, max_miss_count); + + //Warnings about prefetcher policy + if (pf_policy == "none" && (prefetch_miss || prefetch_access)) { + panic("With no prefetcher, you shouldn't prefetch from" + " either miss or access stream\n"); + } + if ((pf_policy == "tagged" || pf_policy == "stride" || + pf_policy == "ghb") && !(prefetch_miss || prefetch_access)) { + warn("With this prefetcher you should chose a prefetch" + " stream (miss or access)\nNo Prefetching will occur\n"); + } + if ((pf_policy == "tagged" || pf_policy == "stride" || + pf_policy == "ghb") && prefetch_miss && prefetch_access) { + panic("Can't do prefetches from both miss and access" + " stream\n"); + } + if (pf_policy != "tagged" && pf_policy != "stride" && + pf_policy != "ghb" && pf_policy != "none") { + panic("Unrecognized form of a prefetcher: %s, try using" + "['none','stride','tagged','ghb']\n", pf_policy); + } + +#if defined(USE_CACHE_IIC) + // Build IIC params + IIC::Params iic_params; + iic_params.size = size; + iic_params.numSets = numSets; + iic_params.blkSize = block_size; + iic_params.assoc = assoc; + iic_params.hashDelay = hash_delay; + iic_params.hitLatency = latency; + iic_params.rp = repl; + iic_params.subblockSize = subblock_size; +#else + const void *repl = NULL; +#endif + + if (mshrs == 1 /*|| out_bus->doEvents() == false*/) { + BlockingBuffer *mq = new BlockingBuffer(true); + BUILD_COHERENCE(BlockingBuffer); + } else { + MissQueue *mq = new MissQueue(mshrs, tgts_per_mshr, write_buffers, + true, prefetch_miss); + BUILD_COHERENCE(MissQueue); + } + return NULL; +} + +REGISTER_SIM_OBJECT("BaseCache", BaseCache) + + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh new file mode 100644 index 000000000..aae5cbf01 --- /dev/null +++ b/src/mem/cache/cache_impl.hh @@ -0,0 +1,660 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Dave Greene + * Nathan Binkert + */ + +/** + * @file + * Cache definitions. + */ + +#include <assert.h> +#include <math.h> + +#include <cassert> +#include <iostream> +#include <string> + +#include "sim/host.hh" +#include "base/misc.hh" +#include "cpu/smt.hh" + +#include "mem/cache/cache.hh" +#include "mem/cache/cache_blk.hh" +#include "mem/cache/miss/mshr.hh" +#include "mem/cache/prefetch/prefetcher.hh" + +#include "sim/sim_events.hh" // for SimExitEvent + +using namespace std; + +template<class TagStore, class Buffering, class Coherence> +bool +Cache<TagStore,Buffering,Coherence>:: +doTimingAccess(Packet *pkt, CachePort *cachePort, bool isCpuSide) +{ + if (isCpuSide) + { + access(pkt); + } + else + { + if (pkt->isResponse()) + handleResponse(pkt); + else + snoop(pkt); + } + return true; //Deal with blocking.... +} + +template<class TagStore, class Buffering, class Coherence> +Tick +Cache<TagStore,Buffering,Coherence>:: +doAtomicAccess(Packet *pkt, bool isCpuSide) +{ + if (isCpuSide) + { + probe(pkt, true); + //TEMP ALWAYS SUCCES FOR NOW + pkt->result = Packet::Success; + } + else + { + if (pkt->isResponse()) + handleResponse(pkt); + else + snoopProbe(pkt, true); + } + //Fix this timing info + return hitLatency; +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>:: +doFunctionalAccess(Packet *pkt, bool isCpuSide) +{ + if (isCpuSide) + { + //TEMP USE CPU?THREAD 0 0 + pkt->req->setThreadContext(0,0); + probe(pkt, true); + //TEMP ALWAYS SUCCESFUL FOR NOW + pkt->result = Packet::Success; + } + else + { + if (pkt->isResponse()) + handleResponse(pkt); + else + snoopProbe(pkt, true); + } +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>:: +recvStatusChange(Port::Status status, bool isCpuSide) +{ + +} + + +template<class TagStore, class Buffering, class Coherence> +Cache<TagStore,Buffering,Coherence>:: +Cache(const std::string &_name, + Cache<TagStore,Buffering,Coherence>::Params ¶ms) + : BaseCache(_name, params.baseParams), + prefetchAccess(params.prefetchAccess), + tags(params.tags), missQueue(params.missQueue), + coherence(params.coherence), prefetcher(params.prefetcher), + doCopy(params.doCopy), blockOnCopy(params.blockOnCopy) +{ +//FIX BUS POINTERS +// if (params.in == NULL) { + topLevelCache = true; +// } +//PLEASE FIX THIS, BUS SIZES NOT BEING USED + tags->setCache(this, blkSize, 1/*params.out->width, params.out->clockRate*/); + tags->setPrefetcher(prefetcher); + missQueue->setCache(this); + missQueue->setPrefetcher(prefetcher); + coherence->setCache(this); + prefetcher->setCache(this); + prefetcher->setTags(tags); + prefetcher->setBuffer(missQueue); +#if 0 + invalidatePkt = new Packet; + invalidatePkt->cmd = Packet::InvalidateReq; +#endif +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::regStats() +{ + BaseCache::regStats(); + tags->regStats(name()); + missQueue->regStats(name()); + coherence->regStats(name()); + prefetcher->regStats(name()); +} + +template<class TagStore, class Buffering, class Coherence> +bool +Cache<TagStore,Buffering,Coherence>::access(PacketPtr &pkt) +{ +//@todo Add back in MemDebug Calls +// MemDebug::cacheAccess(pkt); + BlkType *blk = NULL; + PacketList writebacks; + int size = blkSize; + int lat = hitLatency; + if (prefetchAccess) { + //We are determining prefetches on access stream, call prefetcher + prefetcher->handleMiss(pkt, curTick); + } + if (!pkt->req->isUncacheable()) { + if (pkt->isInvalidate() && !pkt->isRead() + && !pkt->isWrite()) { + //Upgrade or Invalidate + //Look into what happens if two slave caches on bus + DPRINTF(Cache, "%s %d %x ? blk_addr: %x\n", pkt->cmdString(), + pkt->req->getAsid(), pkt->getAddr() & (((ULL(1))<<48)-1), + pkt->getAddr() & ~((Addr)blkSize - 1)); + + //@todo Should this return latency have the hit latency in it? +// respond(pkt,curTick+lat); + pkt->flags |= SATISFIED; +// return MA_HIT; //@todo, return values + return true; + } + blk = tags->handleAccess(pkt, lat, writebacks); + } else { + size = pkt->getSize(); + } + // If this is a block size write/hint (WH64) allocate the block here + // if the coherence protocol allows it. + /** @todo make the fast write alloc (wh64) work with coherence. */ + /** @todo Do we want to do fast writes for writebacks as well? */ + if (!blk && pkt->getSize() >= blkSize && coherence->allowFastWrites() && + (pkt->cmd == Packet::WriteReq || pkt->cmd == Packet::WriteInvalidateReq) ) { + // not outstanding misses, can do this + MSHR* outstanding_miss = missQueue->findMSHR(pkt->getAddr(), pkt->req->getAsid()); + if (pkt->cmd == Packet::WriteInvalidateReq || !outstanding_miss) { + if (outstanding_miss) { + warn("WriteInv doing a fastallocate" + "with an outstanding miss to the same address\n"); + } + blk = tags->handleFill(NULL, pkt, BlkValid | BlkWritable, + writebacks); + ++fastWrites; + } + } + while (!writebacks.empty()) { + missQueue->doWriteback(writebacks.front()); + writebacks.pop_front(); + } + DPRINTF(Cache, "%s %d %x %s blk_addr: %x pc %x\n", pkt->cmdString(), + pkt->req->getAsid(), pkt->getAddr() & (((ULL(1))<<48)-1), (blk) ? "hit" : "miss", + pkt->getAddr() & ~((Addr)blkSize - 1), pkt->req->getPC()); + if (blk) { + // Hit + hits[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + // clear dirty bit if write through + if (pkt->needsResponse()) + respond(pkt, curTick+lat); +// return MA_HIT; + return true; + } + + // Miss + if (!pkt->req->isUncacheable()) { + misses[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + /** @todo Move miss count code into BaseCache */ + if (missCount) { + --missCount; + if (missCount == 0) + new SimLoopExitEvent(curTick, "A cache reached the maximum miss count"); + } + } + missQueue->handleMiss(pkt, size, curTick + hitLatency); +// return MA_CACHE_MISS; + return true; +} + + +template<class TagStore, class Buffering, class Coherence> +Packet * +Cache<TagStore,Buffering,Coherence>::getPacket() +{ + Packet * pkt = missQueue->getPacket(); + if (pkt) { + if (!pkt->req->isUncacheable()) { + if (pkt->cmd == Packet::HardPFReq) misses[Packet::HardPFReq][pkt->req->getThreadNum()]++; + BlkType *blk = tags->findBlock(pkt); + Packet::Command cmd = coherence->getBusCmd(pkt->cmd, + (blk)? blk->status : 0); + missQueue->setBusCmd(pkt, cmd); + } + } + + assert(!doMasterRequest() || missQueue->havePending()); + assert(!pkt || pkt->time <= curTick); + return pkt; +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::sendResult(PacketPtr &pkt, bool success) +{ + if (success) { + missQueue->markInService(pkt); + //Temp Hack for UPGRADES + if (pkt->cmd == Packet::UpgradeReq) { + handleResponse(pkt); + } + } else if (pkt && !pkt->req->isUncacheable()) { + missQueue->restoreOrigCmd(pkt); + } +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::handleResponse(Packet * &pkt) +{ + BlkType *blk = NULL; + if (pkt->senderState) { +// MemDebug::cacheResponse(pkt); + DPRINTF(Cache, "Handling reponse to %x, blk addr: %x\n",pkt->getAddr(), + pkt->getAddr() & (((ULL(1))<<48)-1)); + + if (pkt->isCacheFill() && !pkt->isNoAllocate()) { + blk = tags->findBlock(pkt); + CacheBlk::State old_state = (blk) ? blk->status : 0; + PacketList writebacks; + blk = tags->handleFill(blk, (MSHR*)pkt->senderState, + coherence->getNewState(pkt,old_state), + writebacks); + while (!writebacks.empty()) { + missQueue->doWriteback(writebacks.front()); + } + } + missQueue->handleResponse(pkt, curTick + hitLatency); + } +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::pseudoFill(Addr addr, int asid) +{ + // Need to temporarily move this blk into MSHRs + MSHR *mshr = missQueue->allocateTargetList(addr, asid); + int lat; + PacketList dummy; + // Read the data into the mshr + BlkType *blk = tags->handleAccess(mshr->pkt, lat, dummy, false); + assert(dummy.empty()); + assert(mshr->pkt->flags & SATISFIED); + // can overload order since it isn't used on non pending blocks + mshr->order = blk->status; + // temporarily remove the block from the cache. + tags->invalidateBlk(addr, asid); +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::pseudoFill(MSHR *mshr) +{ + // Need to temporarily move this blk into MSHRs + assert(mshr->pkt->cmd == Packet::ReadReq); + int lat; + PacketList dummy; + // Read the data into the mshr + BlkType *blk = tags->handleAccess(mshr->pkt, lat, dummy, false); + assert(dummy.empty()); + assert(mshr->pkt->flags & SATISFIED); + // can overload order since it isn't used on non pending blocks + mshr->order = blk->status; + // temporarily remove the block from the cache. + tags->invalidateBlk(mshr->pkt->getAddr(), mshr->pkt->req->getAsid()); +} + + +template<class TagStore, class Buffering, class Coherence> +Packet * +Cache<TagStore,Buffering,Coherence>::getCoherenceReq() +{ + return coherence->getPacket(); +} + + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::snoop(Packet * &pkt) +{ + + Addr blk_addr = pkt->getAddr() & ~(Addr(blkSize-1)); + BlkType *blk = tags->findBlock(pkt); + MSHR *mshr = missQueue->findMSHR(blk_addr, pkt->req->getAsid()); + if (isTopLevel() && coherence->hasProtocol()) { //@todo Move this into handle bus req + //If we find an mshr, and it is in service, we need to NACK or invalidate + if (mshr) { + if (mshr->inService) { + if ((mshr->pkt->isInvalidate() || !mshr->pkt->isCacheFill()) + && (pkt->cmd != Packet::InvalidateReq && pkt->cmd != Packet::WriteInvalidateReq)) { + //If the outstanding request was an invalidate (upgrade,readex,..) + //Then we need to ACK the request until we get the data + //Also NACK if the outstanding request is not a cachefill (writeback) + pkt->flags |= NACKED_LINE; + return; + } + else { + //The supplier will be someone else, because we are waiting for + //the data. This should cause this cache to be forced to go to + //the shared state, not the exclusive even though the shared line + //won't be asserted. But for now we will just invlidate ourselves + //and allow the other cache to go into the exclusive state. + //@todo Make it so a read to a pending read doesn't invalidate. + //@todo Make it so that a read to a pending read can't be exclusive now. + + //Set the address so find match works + invalidatePkt->addrOverride(pkt->getAddr()); + + //Append the invalidate on + missQueue->addTarget(mshr,invalidatePkt); + DPRINTF(Cache, "Appending Invalidate to blk_addr: %x\n", pkt->getAddr() & (((ULL(1))<<48)-1)); + return; + } + } + } + //We also need to check the writeback buffers and handle those + std::vector<MSHR *> writebacks; + if (missQueue->findWrites(blk_addr, pkt->req->getAsid(), writebacks)) { + DPRINTF(Cache, "Snoop hit in writeback to blk_addr: %x\n", pkt->getAddr() & (((ULL(1))<<48)-1)); + + //Look through writebacks for any non-uncachable writes, use that + for (int i=0; i<writebacks.size(); i++) { + mshr = writebacks[i]; + + if (!mshr->pkt->req->isUncacheable()) { + if (pkt->isRead()) { + //Only Upgrades don't get here + //Supply the data + pkt->flags |= SATISFIED; + + //If we are in an exclusive protocol, make it ask again + //to get write permissions (upgrade), signal shared + pkt->flags |= SHARED_LINE; + + assert(pkt->isRead()); + Addr offset = pkt->getAddr() & ~(blkSize - 1); + assert(offset < blkSize); + assert(pkt->getSize() <= blkSize); + assert(offset + pkt->getSize() <=blkSize); + memcpy(pkt->getPtr<uint8_t>(), mshr->pkt->getPtr<uint8_t>() + offset, pkt->getSize()); + + respondToSnoop(pkt); + } + + if (pkt->isInvalidate()) { + //This must be an upgrade or other cache will take ownership + missQueue->markInService(mshr->pkt); + } + return; + } + } + } + } + CacheBlk::State new_state; + bool satisfy = coherence->handleBusRequest(pkt,blk,mshr, new_state); + if (satisfy) { + tags->handleSnoop(blk, new_state, pkt); + respondToSnoop(pkt); + return; + } + tags->handleSnoop(blk, new_state); +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::snoopResponse(Packet * &pkt) +{ + //Need to handle the response, if NACKED + if (pkt->flags & NACKED_LINE) { + //Need to mark it as not in service, and retry for bus + assert(0); //Yeah, we saw a NACK come through + + //For now this should never get called, we return false when we see a NACK + //instead, by doing this we allow the bus_blocked mechanism to handle the retry + //For now it retrys in just 2 cycles, need to figure out how to change that + //Eventually we will want to also have success come in as a parameter + //Need to make sure that we handle the functionality that happens on successufl + //return of the sendAddr function + } +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::invalidateBlk(Addr addr, int asid) +{ + tags->invalidateBlk(addr,asid); +} + + +/** + * @todo Fix to not assume write allocate + */ +template<class TagStore, class Buffering, class Coherence> +Tick +Cache<TagStore,Buffering,Coherence>::probe(Packet * &pkt, bool update) +{ +// MemDebug::cacheProbe(pkt); + if (!pkt->req->isUncacheable()) { + if (pkt->isInvalidate() && !pkt->isRead() + && !pkt->isWrite()) { + //Upgrade or Invalidate, satisfy it, don't forward + DPRINTF(Cache, "%s %d %x ? blk_addr: %x\n", pkt->cmdString(), + pkt->req->getAsid(), pkt->getAddr() & (((ULL(1))<<48)-1), + pkt->getAddr() & ~((Addr)blkSize - 1)); + pkt->flags |= SATISFIED; + return 0; + } + } + + PacketList writebacks; + int lat; + BlkType *blk = tags->handleAccess(pkt, lat, writebacks, update); + + if (!blk) { + // Need to check for outstanding misses and writes + Addr blk_addr = pkt->getAddr() & ~(blkSize - 1); + + // There can only be one matching outstanding miss. + MSHR* mshr = missQueue->findMSHR(blk_addr, pkt->req->getAsid()); + + // There can be many matching outstanding writes. + vector<MSHR*> writes; + missQueue->findWrites(blk_addr, pkt->req->getAsid(), writes); + + if (!update) { + memSidePort->sendFunctional(pkt); + // Check for data in MSHR and writebuffer. + if (mshr) { + warn("Found outstanding miss on an non-update probe"); + MSHR::TargetList *targets = mshr->getTargetList(); + MSHR::TargetList::iterator i = targets->begin(); + MSHR::TargetList::iterator end = targets->end(); + for (; i != end; ++i) { + Packet * target = *i; + // If the target contains data, and it overlaps the + // probed request, need to update data + if (target->isWrite() && target->intersect(pkt)) { + uint8_t* pkt_data; + uint8_t* write_data; + int data_size; + if (target->getAddr() < pkt->getAddr()) { + int offset = pkt->getAddr() - target->getAddr(); + pkt_data = pkt->getPtr<uint8_t>(); + write_data = target->getPtr<uint8_t>() + offset; + data_size = target->getSize() - offset; + assert(data_size > 0); + if (data_size > pkt->getSize()) + data_size = pkt->getSize(); + } else { + int offset = target->getAddr() - pkt->getAddr(); + pkt_data = pkt->getPtr<uint8_t>() + offset; + write_data = target->getPtr<uint8_t>(); + data_size = pkt->getSize() - offset; + assert(data_size > pkt->getSize()); + if (data_size > target->getSize()) + data_size = target->getSize(); + } + + if (pkt->isWrite()) { + memcpy(pkt_data, write_data, data_size); + } else { + memcpy(write_data, pkt_data, data_size); + } + } + } + } + for (int i = 0; i < writes.size(); ++i) { + Packet * write = writes[i]->pkt; + if (write->intersect(pkt)) { + warn("Found outstanding write on an non-update probe"); + uint8_t* pkt_data; + uint8_t* write_data; + int data_size; + if (write->getAddr() < pkt->getAddr()) { + int offset = pkt->getAddr() - write->getAddr(); + pkt_data = pkt->getPtr<uint8_t>(); + write_data = write->getPtr<uint8_t>() + offset; + data_size = write->getSize() - offset; + assert(data_size > 0); + if (data_size > pkt->getSize()) + data_size = pkt->getSize(); + } else { + int offset = write->getAddr() - pkt->getAddr(); + pkt_data = pkt->getPtr<uint8_t>() + offset; + write_data = write->getPtr<uint8_t>(); + data_size = pkt->getSize() - offset; + assert(data_size > pkt->getSize()); + if (data_size > write->getSize()) + data_size = write->getSize(); + } + + if (pkt->isWrite()) { + memcpy(pkt_data, write_data, data_size); + } else { + memcpy(write_data, pkt_data, data_size); + } + + } + } + return 0; + } else { + // update the cache state and statistics + if (mshr || !writes.empty()){ + // Can't handle it, return pktuest unsatisfied. + return 0; + } + if (!pkt->req->isUncacheable()) { + // Fetch the cache block to fill + BlkType *blk = tags->findBlock(pkt); + Packet::Command temp_cmd = coherence->getBusCmd(pkt->cmd, + (blk)? blk->status : 0); + + Packet * busPkt = new Packet(pkt->req,temp_cmd, -1, blkSize); + + busPkt->allocate(); + + busPkt->time = curTick; + + lat = memSidePort->sendAtomic(busPkt); + +/* if (!(busPkt->flags & SATISFIED)) { + // blocked at a higher level, just return + return 0; + } + +*/ misses[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + + CacheBlk::State old_state = (blk) ? blk->status : 0; + tags->handleFill(blk, busPkt, + coherence->getNewState(busPkt, old_state), + writebacks, pkt); + // Handle writebacks if needed + while (!writebacks.empty()){ + memSidePort->sendAtomic(writebacks.front()); + writebacks.pop_front(); + } + return lat + hitLatency; + } else { + return memSidePort->sendAtomic(pkt); + } + } + } else { + // There was a cache hit. + // Handle writebacks if needed + while (!writebacks.empty()){ + memSidePort->sendAtomic(writebacks.front()); + writebacks.pop_front(); + } + + if (update) { + hits[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + } else if (pkt->isWrite()) { + // Still need to change data in all locations. + return memSidePort->sendAtomic(pkt); + } + return curTick + lat; + } + fatal("Probe not handled.\n"); + return 0; +} + +template<class TagStore, class Buffering, class Coherence> +Tick +Cache<TagStore,Buffering,Coherence>::snoopProbe(PacketPtr &pkt, bool update) +{ + Addr blk_addr = pkt->getAddr() & ~(Addr(blkSize-1)); + BlkType *blk = tags->findBlock(pkt); + MSHR *mshr = missQueue->findMSHR(blk_addr, pkt->req->getAsid()); + CacheBlk::State new_state = 0; + bool satisfy = coherence->handleBusRequest(pkt,blk,mshr, new_state); + if (satisfy) { + tags->handleSnoop(blk, new_state, pkt); + return hitLatency; + } + tags->handleSnoop(blk, new_state); + return 0; +} + diff --git a/src/mem/cache/coherence/coherence_protocol.cc b/src/mem/cache/coherence/coherence_protocol.cc new file mode 100644 index 000000000..bcf3ce9c5 --- /dev/null +++ b/src/mem/cache/coherence/coherence_protocol.cc @@ -0,0 +1,567 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Steve Reinhardt + * Ron Dreslinski + */ + +/** + * @file + * Definitions of CoherenceProtocol. + */ + +#include <string> + +#include "base/misc.hh" +#include "mem/cache/miss/mshr.hh" +#include "mem/cache/cache.hh" +#include "mem/cache/coherence/coherence_protocol.hh" +#include "sim/builder.hh" + +using namespace std; + + +CoherenceProtocol::StateTransition::StateTransition() + : busCmd(Packet::InvalidCmd), newState(-1), snoopFunc(invalidTransition) +{ +} + + +void +CoherenceProtocol::regStats() +{ + // Even though we count all the possible transitions in the + // requestCount and snoopCount arrays, most of these are invalid, + // so we just select the interesting ones to print here. + + requestCount[Invalid][Packet::ReadReq] + .name(name() + ".read_invalid") + .desc("read misses to invalid blocks") + ; + + requestCount[Invalid][Packet::WriteReq] + .name(name() +".write_invalid") + .desc("write misses to invalid blocks") + ; + + requestCount[Invalid][Packet::SoftPFReq] + .name(name() +".swpf_invalid") + .desc("soft prefetch misses to invalid blocks") + ; + + requestCount[Invalid][Packet::HardPFReq] + .name(name() +".hwpf_invalid") + .desc("hard prefetch misses to invalid blocks") + ; + + requestCount[Shared][Packet::WriteReq] + .name(name() + ".write_shared") + .desc("write misses to shared blocks") + ; + + requestCount[Owned][Packet::WriteReq] + .name(name() + ".write_owned") + .desc("write misses to owned blocks") + ; + + snoopCount[Shared][Packet::ReadReq] + .name(name() + ".snoop_read_shared") + .desc("read snoops on shared blocks") + ; + + snoopCount[Shared][Packet::ReadExReq] + .name(name() + ".snoop_readex_shared") + .desc("readEx snoops on shared blocks") + ; + + snoopCount[Shared][Packet::UpgradeReq] + .name(name() + ".snoop_upgrade_shared") + .desc("upgradee snoops on shared blocks") + ; + + snoopCount[Modified][Packet::ReadReq] + .name(name() + ".snoop_read_modified") + .desc("read snoops on modified blocks") + ; + + snoopCount[Modified][Packet::ReadExReq] + .name(name() + ".snoop_readex_modified") + .desc("readEx snoops on modified blocks") + ; + + snoopCount[Owned][Packet::ReadReq] + .name(name() + ".snoop_read_owned") + .desc("read snoops on owned blocks") + ; + + snoopCount[Owned][Packet::ReadExReq] + .name(name() + ".snoop_readex_owned") + .desc("readEx snoops on owned blocks") + ; + + snoopCount[Owned][Packet::UpgradeReq] + .name(name() + ".snoop_upgrade_owned") + .desc("upgrade snoops on owned blocks") + ; + + snoopCount[Exclusive][Packet::ReadReq] + .name(name() + ".snoop_read_exclusive") + .desc("read snoops on exclusive blocks") + ; + + snoopCount[Exclusive][Packet::ReadExReq] + .name(name() + ".snoop_readex_exclusive") + .desc("readEx snoops on exclusive blocks") + ; + + snoopCount[Shared][Packet::InvalidateReq] + .name(name() + ".snoop_inv_shared") + .desc("Invalidate snoops on shared blocks") + ; + + snoopCount[Owned][Packet::InvalidateReq] + .name(name() + ".snoop_inv_owned") + .desc("Invalidate snoops on owned blocks") + ; + + snoopCount[Exclusive][Packet::InvalidateReq] + .name(name() + ".snoop_inv_exclusive") + .desc("Invalidate snoops on exclusive blocks") + ; + + snoopCount[Modified][Packet::InvalidateReq] + .name(name() + ".snoop_inv_modified") + .desc("Invalidate snoops on modified blocks") + ; + + snoopCount[Invalid][Packet::InvalidateReq] + .name(name() + ".snoop_inv_invalid") + .desc("Invalidate snoops on invalid blocks") + ; + + snoopCount[Shared][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_shared") + .desc("WriteInvalidate snoops on shared blocks") + ; + + snoopCount[Owned][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_owned") + .desc("WriteInvalidate snoops on owned blocks") + ; + + snoopCount[Exclusive][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_exclusive") + .desc("WriteInvalidate snoops on exclusive blocks") + ; + + snoopCount[Modified][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_modified") + .desc("WriteInvalidate snoops on modified blocks") + ; + + snoopCount[Invalid][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_invalid") + .desc("WriteInvalidate snoops on invalid blocks") + ; +} + + +bool +CoherenceProtocol::invalidateTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, MSHR *mshr, + CacheBlk::State & new_state) +{ + // invalidate the block + new_state = (blk->status & ~stateMask) | Invalid; + return false; +} + + +bool +CoherenceProtocol::supplyTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state + ) +{ + return true; +} + + +bool +CoherenceProtocol::supplyAndGotoSharedTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + new_state = (blk->status & ~stateMask) | Shared; + pkt->flags |= SHARED_LINE; + return supplyTrans(cache, pkt, blk, mshr, new_state); +} + + +bool +CoherenceProtocol::supplyAndGotoOwnedTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + new_state = (blk->status & ~stateMask) | Owned; + pkt->flags |= SHARED_LINE; + return supplyTrans(cache, pkt, blk, mshr, new_state); +} + + +bool +CoherenceProtocol::supplyAndInvalidateTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + new_state = (blk->status & ~stateMask) | Invalid; + return supplyTrans(cache, pkt, blk, mshr, new_state); +} + +bool +CoherenceProtocol::assertShared(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + new_state = (blk->status & ~stateMask) | Shared; + pkt->flags |= SHARED_LINE; + return false; +} + +CoherenceProtocol::CoherenceProtocol(const string &name, + const string &protocol, + const bool doUpgrades) + : SimObject(name) +{ + if ((protocol == "mosi" || protocol == "moesi") && !doUpgrades) { + cerr << "CoherenceProtocol: ownership protocols require upgrade transactions" + << "(write miss on owned block generates ReadExcl, which will clobber dirty block)" + << endl; + fatal(""); + } + + Packet::Command writeToSharedCmd = doUpgrades ? Packet::UpgradeReq : Packet::ReadExReq; + Packet::Command writeToSharedResp = doUpgrades ? Packet::UpgradeResp : Packet::ReadExResp; + +//@todo add in hardware prefetch to this list + if (protocol == "msi") { + // incoming requests: specify outgoing bus request + transitionTable[Invalid][Packet::ReadReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::WriteReq].onRequest(Packet::ReadExReq); + transitionTable[Shared][Packet::WriteReq].onRequest(writeToSharedCmd); + //Prefetching causes a read + transitionTable[Invalid][Packet::SoftPFReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::HardPFReq].onRequest(Packet::ReadReq); + + // on response to given request: specify new state + transitionTable[Invalid][Packet::ReadResp].onResponse(Shared); + transitionTable[Invalid][Packet::ReadExResp].onResponse(Modified); + transitionTable[Shared][writeToSharedResp].onResponse(Modified); + + // bus snoop transition functions + transitionTable[Invalid][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::ReadExReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Modified][Packet::ReadReq].onSnoop(supplyAndGotoSharedTrans); + //Tansitions on seeing a DMA (writeInv(samelevel) or DMAInv) + transitionTable[Invalid][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Invalid][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + + if (doUpgrades) { + transitionTable[Invalid][Packet::UpgradeReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::UpgradeReq].onSnoop(invalidateTrans); + } + } + + else if(protocol == "mesi") { + // incoming requests: specify outgoing bus request + transitionTable[Invalid][Packet::ReadReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::WriteReq].onRequest(Packet::ReadExReq); + transitionTable[Shared][Packet::WriteReq].onRequest(writeToSharedCmd); + //Prefetching causes a read + transitionTable[Invalid][Packet::SoftPFReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::HardPFReq].onRequest(Packet::ReadReq); + + // on response to given request: specify new state + transitionTable[Invalid][Packet::ReadResp].onResponse(Exclusive); + //It will move into shared if the shared line is asserted in the + //getNewState function + transitionTable[Invalid][Packet::ReadExResp].onResponse(Modified); + transitionTable[Shared][writeToSharedResp].onResponse(Modified); + + // bus snoop transition functions + transitionTable[Invalid][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::ReadExReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Shared][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Exclusive][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Modified][Packet::ReadReq].onSnoop(supplyAndGotoSharedTrans); + //Tansitions on seeing a DMA (writeInv(samelevel) or DMAInv) + transitionTable[Invalid][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Invalid][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + + if (doUpgrades) { + transitionTable[Invalid][Packet::UpgradeReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::UpgradeReq].onSnoop(invalidateTrans); + } + } + + else if(protocol == "mosi") { + // incoming requests: specify outgoing bus request + transitionTable[Invalid][Packet::ReadReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::WriteReq].onRequest(Packet::ReadExReq); + transitionTable[Shared][Packet::WriteReq].onRequest(writeToSharedCmd); + transitionTable[Owned][Packet::WriteReq].onRequest(writeToSharedCmd); + //Prefetching causes a read + transitionTable[Invalid][Packet::SoftPFReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::HardPFReq].onRequest(Packet::ReadReq); + + // on response to given request: specify new state + transitionTable[Invalid][Packet::ReadResp].onResponse(Shared); + transitionTable[Invalid][Packet::ReadExResp].onResponse(Modified); + transitionTable[Shared][writeToSharedResp].onResponse(Modified); + transitionTable[Owned][writeToSharedResp].onResponse(Modified); + + // bus snoop transition functions + transitionTable[Invalid][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::ReadExReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::UpgradeReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Shared][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::UpgradeReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Modified][Packet::ReadReq].onSnoop(supplyAndGotoOwnedTrans); + transitionTable[Owned][Packet::ReadReq].onSnoop(supplyAndGotoOwnedTrans); + transitionTable[Owned][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Owned][Packet::UpgradeReq].onSnoop(invalidateTrans); + //Tansitions on seeing a DMA (writeInv(samelevel) or DMAInv) + transitionTable[Invalid][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Owned][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Invalid][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Owned][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + } + + else if(protocol == "moesi") { + // incoming requests: specify outgoing bus request + transitionTable[Invalid][Packet::ReadReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::WriteReq].onRequest(Packet::ReadExReq); + transitionTable[Shared][Packet::WriteReq].onRequest(writeToSharedCmd); + transitionTable[Owned][Packet::WriteReq].onRequest(writeToSharedCmd); + //Prefetching causes a read + transitionTable[Invalid][Packet::SoftPFReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::HardPFReq].onRequest(Packet::ReadReq); + + // on response to given request: specify new state + transitionTable[Invalid][Packet::ReadResp].onResponse(Exclusive); + //It will move into shared if the shared line is asserted in the + //getNewState function + transitionTable[Invalid][Packet::ReadExResp].onResponse(Modified); + transitionTable[Shared][writeToSharedResp].onResponse(Modified); + transitionTable[Owned][writeToSharedResp].onResponse(Modified); + + // bus snoop transition functions + transitionTable[Invalid][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::ReadExReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::UpgradeReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Shared][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::UpgradeReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Exclusive][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::ReadReq].onSnoop(supplyAndGotoOwnedTrans); + transitionTable[Modified][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Owned][Packet::ReadReq].onSnoop(supplyAndGotoOwnedTrans); + transitionTable[Owned][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Owned][Packet::UpgradeReq].onSnoop(invalidateTrans); + //Transitions on seeing a DMA (writeInv(samelevel) or DMAInv) + transitionTable[Invalid][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Owned][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Invalid][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Owned][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + } + + else { + cerr << "CoherenceProtocol: unrecognized protocol " << protocol + << endl; + fatal(""); + } +} + + +Packet::Command +CoherenceProtocol::getBusCmd(Packet::Command cmdIn, CacheBlk::State state, + MSHR *mshr) +{ + state &= stateMask; + int cmd_idx = (int) cmdIn; + + assert(0 <= state && state <= stateMax); + assert(0 <= cmd_idx && cmd_idx < NUM_MEM_CMDS); + + Packet::Command cmdOut = transitionTable[state][cmd_idx].busCmd; + + assert(cmdOut != Packet::InvalidCmd); + + ++requestCount[state][cmd_idx]; + + return cmdOut; +} + + +CacheBlk::State +CoherenceProtocol::getNewState(Packet * &pkt, CacheBlk::State oldState) +{ + CacheBlk::State state = oldState & stateMask; + int cmd_idx = pkt->cmdToIndex(); + + assert(0 <= state && state <= stateMax); + assert(0 <= cmd_idx && cmd_idx < NUM_MEM_CMDS); + + CacheBlk::State newState = transitionTable[state][cmd_idx].newState; + + //Check if it's exclusive and the shared line was asserted, + //then goto shared instead + if (newState == Exclusive && (pkt->flags & SHARED_LINE)) { + newState = Shared; + } + + assert(newState != -1); + + //Make sure not to loose any other state information + newState = (oldState & ~stateMask) | newState; + return newState; +} + + +bool +CoherenceProtocol::handleBusRequest(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + if (blk == NULL) { + // nothing to do if we don't have a block + return false; + } + + CacheBlk::State state = blk->status & stateMask; + int cmd_idx = pkt->cmdToIndex(); + + assert(0 <= state && state <= stateMax); + assert(0 <= cmd_idx && cmd_idx < NUM_MEM_CMDS); + +// assert(mshr == NULL); // can't currently handle outstanding requests + //Check first if MSHR, and also insure, if there is one, that it is not in service + assert(!mshr || mshr->inService == 0); + ++snoopCount[state][cmd_idx]; + + bool ret = transitionTable[state][cmd_idx].snoopFunc(cache, pkt, blk, mshr, + new_state); + + + + return ret; +} + +bool +CoherenceProtocol::nullTransition(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, MSHR *mshr, + CacheBlk::State & new_state) +{ + // do nothing + if (blk) + new_state = blk->status; + return false; +} + + +bool +CoherenceProtocol::invalidTransition(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, MSHR *mshr, + CacheBlk::State & new_state) +{ + panic("Invalid transition"); + return false; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(CoherenceProtocol) + + Param<string> protocol; + Param<bool> do_upgrades; + +END_DECLARE_SIM_OBJECT_PARAMS(CoherenceProtocol) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(CoherenceProtocol) + + INIT_PARAM(protocol, "name of coherence protocol"), + INIT_PARAM_DFLT(do_upgrades, "use upgrade transactions?", true) + +END_INIT_SIM_OBJECT_PARAMS(CoherenceProtocol) + + +CREATE_SIM_OBJECT(CoherenceProtocol) +{ + return new CoherenceProtocol(getInstanceName(), protocol, + do_upgrades); +} + +REGISTER_SIM_OBJECT("CoherenceProtocol", CoherenceProtocol) + +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/coherence/coherence_protocol.hh b/src/mem/cache/coherence/coherence_protocol.hh new file mode 100644 index 000000000..21351ace4 --- /dev/null +++ b/src/mem/cache/coherence/coherence_protocol.hh @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Ron Dreslinski + * Steve Reinhardt + */ + +/** + * @file + * Declaration of CoherenceProcotol a basic coherence policy. + */ +#ifndef __COHERENCE_PROTOCOL_HH__ +#define __COHERENCE_PROTOCOL_HH__ + +#include <string> + +#include "sim/sim_object.hh" +#include "mem/packet.hh" +#include "mem/cache/cache_blk.hh" +#include "base/statistics.hh" + +class BaseCache; +class MSHR; + +/** + * A simple coherence policy for the memory hierarchy. Currently implements + * MSI, MESI, and MOESI protocols. + */ +class CoherenceProtocol : public SimObject +{ + public: + /** + * Contruct and initialize this policy. + * @param name The name of this policy. + * @param protocol The string representation of the protocol to use. + * @param doUpgrades True if bus upgrades should be used. + */ + CoherenceProtocol(const std::string &name, const std::string &protocol, + const bool doUpgrades); + + /** + * Destructor. + */ + virtual ~CoherenceProtocol() {}; + + /** + * Register statistics + */ + virtual void regStats(); + + /** + * Get the proper bus command for the given command and status. + * @param cmd The request's command. + * @param status The current state of the cache block. + * @param mshr The MSHR matching the request. + * @return The proper bus command, as determined by the protocol. + */ + Packet::Command getBusCmd(Packet::Command cmd, CacheBlk::State status, + MSHR *mshr = NULL); + + /** + * Return the proper state given the current state and the bus response. + * @param req The bus response. + * @param oldState The current block state. + * @return The new state. + */ + CacheBlk::State getNewState(Packet * &pkt, + CacheBlk::State oldState); + + /** + * Handle snooped bus requests. + * @param cache The cache that snooped the request. + * @param req The snooped bus request. + * @param blk The cache block corresponding to the request, if any. + * @param mshr The MSHR corresponding to the request, if any. + * @param new_state The new coherence state of the block. + * @return True if the request should be satisfied locally. + */ + bool handleBusRequest(BaseCache *cache, Packet * &pkt, CacheBlk *blk, + MSHR *mshr, CacheBlk::State &new_state); + + protected: + /** Snoop function type. */ + typedef bool (*SnoopFuncType)(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + // + // Standard snoop transition functions + // + + /** + * Do nothing transition. + */ + static bool nullTransition(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Invalid transition, basically panic. + */ + static bool invalidTransition(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Invalidate block, move to Invalid state. + */ + static bool invalidateTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Supply data, no state transition. + */ + static bool supplyTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Supply data and go to Shared state. + */ + static bool supplyAndGotoSharedTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Supply data and go to Owned state. + */ + static bool supplyAndGotoOwnedTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Invalidate block, supply data, and go to Invalid state. + */ + static bool supplyAndInvalidateTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Assert the shared line for a block that is shared/exclusive. + */ + static bool assertShared(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Definition of protocol state transitions. + */ + class StateTransition + { + friend class CoherenceProtocol; + + /** The bus command of this transition. */ + Packet::Command busCmd; + /** The state to transition to. */ + int newState; + /** The snoop function for this transition. */ + SnoopFuncType snoopFunc; + + /** + * Constructor, defaults to invalid transition. + */ + StateTransition(); + + /** + * Initialize bus command. + * @param cmd The bus command to use. + */ + void onRequest(Packet::Command cmd) + { + busCmd = cmd; + } + + /** + * Set the transition state. + * @param s The new state. + */ + void onResponse(CacheBlk::State s) + { + newState = s; + } + + /** + * Initialize the snoop function. + * @param f The new snoop function. + */ + void onSnoop(SnoopFuncType f) + { + snoopFunc = f; + } + }; + + friend class CoherenceProtocol::StateTransition; + + /** Mask to select status bits relevant to coherence protocol. */ + const static CacheBlk::State + stateMask = BlkValid | BlkWritable | BlkDirty; + + /** The Modified (M) state. */ + const static CacheBlk::State + Modified = BlkValid | BlkWritable | BlkDirty; + /** The Owned (O) state. */ + const static CacheBlk::State + Owned = BlkValid | BlkDirty; + /** The Exclusive (E) state. */ + const static CacheBlk::State + Exclusive = BlkValid | BlkWritable; + /** The Shared (S) state. */ + const static CacheBlk::State + Shared = BlkValid; + /** The Invalid (I) state. */ + const static CacheBlk::State + Invalid = 0; + + /** + * Maximum state encoding value (used to size transition lookup + * table). Could be more than number of states, depends on + * encoding of status bits. + */ + const static int stateMax = stateMask; + + /** + * The table of all possible transitions, organized by starting state and + * request command. + */ + StateTransition transitionTable[stateMax+1][NUM_MEM_CMDS]; + + /** + * @addtogroup CoherenceStatistics + * @{ + */ + /** + * State accesses from parent cache. + */ + Stats::Scalar<> requestCount[stateMax+1][NUM_MEM_CMDS]; + /** + * State accesses from snooped requests. + */ + Stats::Scalar<> snoopCount[stateMax+1][NUM_MEM_CMDS]; + /** + * @} + */ +}; + +#endif // __COHERENCE_PROTOCOL_HH__ diff --git a/src/mem/cache/coherence/simple_coherence.hh b/src/mem/cache/coherence/simple_coherence.hh new file mode 100644 index 000000000..ca9d18beb --- /dev/null +++ b/src/mem/cache/coherence/simple_coherence.hh @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Ron Dreslinski + */ + +/** + * @file + * Declaration of a simple coherence policy. + */ + +#ifndef __SIMPLE_COHERENCE_HH__ +#define __SIMPLE_COHERENCE_HH__ + +#include <string> + +#include "mem/packet.hh" +#include "mem/cache/cache_blk.hh" +#include "mem/cache/miss/mshr_queue.hh" +#include "mem/cache/coherence/coherence_protocol.hh" + +class BaseCache; + +/** + * A simple MP coherence policy. This policy assumes an atomic bus and only one + * level of cache. + */ +class SimpleCoherence +{ + protected: + /** Pointer to the parent cache. */ + BaseCache *cache; + /** Pointer to the coherence protocol. */ + CoherenceProtocol *protocol; + + public: + /** + * Construct and initialize this coherence policy. + * @param _protocol The coherence protocol to use. + */ + SimpleCoherence(CoherenceProtocol *_protocol) + : protocol(_protocol) + { + } + + /** + * Set the pointer to the parent cache. + * @param _cache The parent cache. + */ + void setCache(BaseCache *_cache) + { + cache = _cache; + } + + /** + * Register statistics. + * @param name The name to prepend to stat descriptions. + */ + void regStats(const std::string &name) + { + } + + /** + * This policy does not forward invalidates, return NULL. + * @return NULL. + */ + Packet * getPacket() + { + return NULL; + } + + /** + * Return the proper state given the current state and the bus response. + * @param req The bus response. + * @param current The current block state. + * @return The new state. + */ + CacheBlk::State getNewState(Packet * &pkt, CacheBlk::State current) + { + return protocol->getNewState(pkt, current); + } + + /** + * Handle snooped bus requests. + * @param req The snooped bus request. + * @param blk The cache block corresponding to the request, if any. + * @param mshr The MSHR corresponding to the request, if any. + * @param new_state Return the new state for the block. + */ + bool handleBusRequest(Packet * &pkt, CacheBlk *blk, MSHR *mshr, + CacheBlk::State &new_state) + { +// assert(mshr == NULL); +//Got rid of, there could be an MSHR, but it can't be in service + if (blk != NULL) + { + if (pkt->cmd != Packet::Writeback) { + return protocol->handleBusRequest(cache, pkt, blk, mshr, + new_state); + } + else { //It is a writeback, must be ownership protocol, just keep state + new_state = blk->status; + } + } + return false; + } + + /** + * Get the proper bus command for the given command and status. + * @param cmd The request's command. + * @param state The current state of the cache block. + * @return The proper bus command, as determined by the protocol. + */ + Packet::Command getBusCmd(Packet::Command &cmd, CacheBlk::State state) + { + if (cmd == Packet::Writeback) return Packet::Writeback; + return protocol->getBusCmd(cmd, state); + } + + /** + * Return true if this coherence policy can handle fast cache writes. + */ + bool allowFastWrites() { return false; } + + bool hasProtocol() { return true; } +}; + +#endif //__SIMPLE_COHERENCE_HH__ + + + + + + + + diff --git a/src/mem/cache/coherence/uni_coherence.cc b/src/mem/cache/coherence/uni_coherence.cc new file mode 100644 index 000000000..5ab706269 --- /dev/null +++ b/src/mem/cache/coherence/uni_coherence.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +#include "mem/cache/coherence/uni_coherence.hh" +#include "mem/cache/base_cache.hh" + +#include "base/trace.hh" + +using namespace std; + +UniCoherence::UniCoherence() + : cshrs(50) +{ +} + +Packet * +UniCoherence::getPacket() +{ + bool unblock = cshrs.isFull(); + Packet* pkt = cshrs.getReq(); + cshrs.markInService((MSHR*)pkt->senderState); + if (!cshrs.havePending()) { + cache->clearSlaveRequest(Request_Coherence); + } + if (unblock) { + //since CSHRs are always used as buffers, should always get rid of one + assert(!cshrs.isFull()); + cache->clearBlocked(Blocked_Coherence); + } + return pkt; +} + +/** + * @todo add support for returning slave requests, not doing them here. + */ +bool +UniCoherence::handleBusRequest(Packet * &pkt, CacheBlk *blk, MSHR *mshr, + CacheBlk::State &new_state) +{ + new_state = 0; + if (pkt->isInvalidate()) { + DPRINTF(Cache, "snoop inval on blk %x (blk ptr %x)\n", + pkt->getAddr(), blk); + if (!cache->isTopLevel()) { + // Forward to other caches + Packet * tmp = new Packet(pkt->req, Packet::InvalidateReq, -1); + cshrs.allocate(tmp); + cache->setSlaveRequest(Request_Coherence, curTick); + if (cshrs.isFull()) { + cache->setBlockedForSnoop(Blocked_Coherence); + } + } + } else { + if (blk) { + new_state = blk->status; + } + } + return false; +} diff --git a/src/mem/cache/coherence/uni_coherence.hh b/src/mem/cache/coherence/uni_coherence.hh new file mode 100644 index 000000000..764bf6276 --- /dev/null +++ b/src/mem/cache/coherence/uni_coherence.hh @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +#ifndef __UNI_COHERENCE_HH__ +#define __UNI_COHERENCE_HH__ + +#include "base/trace.hh" +#include "base/misc.hh" +#include "mem/cache/cache_blk.hh" +#include "mem/cache/miss/mshr_queue.hh" +#include "mem/packet.hh" + +class BaseCache; + +class UniCoherence +{ + protected: + /** Buffers to hold forwarded invalidates. */ + MSHRQueue cshrs; + /** Pointer to the parent cache. */ + BaseCache *cache; + + public: + /** + * Construct and initialize this coherence policy. + */ + UniCoherence(); + + /** + * Set the pointer to the parent cache. + * @param _cache The parent cache. + */ + void setCache(BaseCache *_cache) + { + cache = _cache; + } + + /** + * Register statistics. + * @param name The name to prepend to stat descriptions. + */ + void regStats(const std::string &name) + { + } + + /** + * Return Read. + * @param cmd The request's command. + * @param state The current state of the cache block. + * @return The proper bus command, as determined by the protocol. + * @todo Make changes so writebacks don't get here. + */ + Packet::Command getBusCmd(Packet::Command &cmd, CacheBlk::State state) + { + if (cmd == Packet::HardPFReq && state) + warn("Trying to issue a prefetch to a block we already have\n"); + if (cmd == Packet::Writeback) + return Packet::Writeback; + return Packet::ReadReq; + } + + /** + * Just return readable and writeable. + * @param req The bus response. + * @param current The current block state. + * @return The new state. + */ + CacheBlk::State getNewState(Packet * &pkt, CacheBlk::State current) + { + if (pkt->senderState) //Blocking Buffers don't get mshrs + { + if (((MSHR *)(pkt->senderState))->originalCmd == Packet::HardPFReq) { + DPRINTF(HWPrefetch, "Marking a hardware prefetch as such in the state\n"); + return BlkHWPrefetched | BlkValid | BlkWritable; + } + else { + return BlkValid | BlkWritable; + } + } + //@todo What about prefetching with blocking buffers + else + return BlkValid | BlkWritable; + } + /** + * Return outstanding invalidate to forward. + * @return The next invalidate to forward to lower levels of cache. + */ + Packet * getPacket(); + + /** + * Handle snooped bus requests. + * @param req The snooped bus request. + * @param blk The cache block corresponding to the request, if any. + * @param mshr The MSHR corresponding to the request, if any. + * @param new_state The new coherence state of the block. + * @return True if the request should be satisfied locally. + */ + bool handleBusRequest(Packet * &pkt, CacheBlk *blk, MSHR *mshr, + CacheBlk::State &new_state); + + /** + * Return true if this coherence policy can handle fast cache writes. + */ + bool allowFastWrites() { return true; } + + bool hasProtocol() { return false; } +}; + +#endif //__UNI_COHERENCE_HH__ diff --git a/src/mem/cache/miss/blocking_buffer.cc b/src/mem/cache/miss/blocking_buffer.cc new file mode 100644 index 000000000..10d53b109 --- /dev/null +++ b/src/mem/cache/miss/blocking_buffer.cc @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definitions of a simple buffer for a blocking cache. + */ + +#include "cpu/smt.hh" //for maxThreadsPerCPU +#include "mem/cache/base_cache.hh" +#include "mem/cache/miss/blocking_buffer.hh" +#include "mem/cache/prefetch/base_prefetcher.hh" +#include "sim/eventq.hh" // for Event declaration. +#include "mem/request.hh" + +using namespace TheISA; + +/** + * @todo Move writebacks into shared BaseBuffer class. + */ +void +BlockingBuffer::regStats(const std::string &name) +{ + using namespace Stats; + writebacks + .init(maxThreadsPerCPU) + .name(name + ".writebacks") + .desc("number of writebacks") + .flags(total) + ; +} + +void +BlockingBuffer::setCache(BaseCache *_cache) +{ + cache = _cache; + blkSize = cache->getBlockSize(); +} + +void +BlockingBuffer::setPrefetcher(BasePrefetcher *_prefetcher) +{ + prefetcher = _prefetcher; +} +void +BlockingBuffer::handleMiss(Packet * &pkt, int blk_size, Tick time) +{ + Addr blk_addr = pkt->getAddr() & ~(Addr)(blk_size - 1); + if (pkt->isWrite() && (pkt->req->isUncacheable() || !writeAllocate || + !pkt->needsResponse())) { + if (!pkt->needsResponse()) { + wb.allocateAsBuffer(pkt); + } else { + wb.allocate(pkt->cmd, blk_addr, pkt->req->getAsid(), blk_size, pkt); + } + + memcpy(wb.pkt->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), blk_size); + + cache->setBlocked(Blocked_NoWBBuffers); + cache->setMasterRequest(Request_WB, time); + return; + } + + if (!pkt->needsResponse()) { + miss.allocateAsBuffer(pkt); + } else { + miss.allocate(pkt->cmd, blk_addr, pkt->req->getAsid(), blk_size, pkt); + } + if (!pkt->req->isUncacheable()) { + miss.pkt->flags |= CACHE_LINE_FILL; + } + cache->setBlocked(Blocked_NoMSHRs); + cache->setMasterRequest(Request_MSHR, time); +} + +Packet * +BlockingBuffer::getPacket() +{ + if (miss.pkt && !miss.inService) { + return miss.pkt; + } + return wb.pkt; +} + +void +BlockingBuffer::setBusCmd(Packet * &pkt, Packet::Command cmd) +{ + MSHR *mshr = (MSHR*) pkt->senderState; + mshr->originalCmd = pkt->cmd; + if (pkt->isCacheFill()) + pkt->cmdOverride(cmd); +} + +void +BlockingBuffer::restoreOrigCmd(Packet * &pkt) +{ + pkt->cmdOverride(((MSHR*)(pkt->senderState))->originalCmd); +} + +void +BlockingBuffer::markInService(Packet * &pkt) +{ + if (!pkt->isCacheFill() && pkt->isWrite()) { + // Forwarding a write/ writeback, don't need to change + // the command + assert((MSHR*)pkt->senderState == &wb); + cache->clearMasterRequest(Request_WB); + if (!pkt->needsResponse()) { + assert(wb.getNumTargets() == 0); + wb.deallocate(); + cache->clearBlocked(Blocked_NoWBBuffers); + } else { + wb.inService = true; + } + } else { + assert((MSHR*)pkt->senderState == &miss); + cache->clearMasterRequest(Request_MSHR); + if (!pkt->needsResponse()) { + assert(miss.getNumTargets() == 0); + miss.deallocate(); + cache->clearBlocked(Blocked_NoMSHRs); + } else { + //mark in service + miss.inService = true; + } + } +} + +void +BlockingBuffer::handleResponse(Packet * &pkt, Tick time) +{ + if (pkt->isCacheFill()) { + // targets were handled in the cache tags + assert((MSHR*)pkt->senderState == &miss); + miss.deallocate(); + cache->clearBlocked(Blocked_NoMSHRs); + } else { + if (((MSHR*)(pkt->senderState))->hasTargets()) { + // Should only have 1 target if we had any + assert(((MSHR*)(pkt->senderState))->getNumTargets() == 1); + Packet * target = ((MSHR*)(pkt->senderState))->getTarget(); + ((MSHR*)(pkt->senderState))->popTarget(); + if (pkt->isRead()) { + memcpy(target->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), target->getSize()); + } + cache->respond(target, time); + assert(!((MSHR*)(pkt->senderState))->hasTargets()); + } + + if (pkt->isWrite()) { + assert(((MSHR*)(pkt->senderState)) == &wb); + wb.deallocate(); + cache->clearBlocked(Blocked_NoWBBuffers); + } else { + miss.deallocate(); + cache->clearBlocked(Blocked_NoMSHRs); + } + } +} + +void +BlockingBuffer::squash(int threadNum) +{ + if (miss.threadNum == threadNum) { + Packet * target = miss.getTarget(); + miss.popTarget(); + assert(target->req->getThreadNum() == threadNum); + target = NULL; + assert(!miss.hasTargets()); + miss.ntargets=0; + if (!miss.inService) { + miss.deallocate(); + cache->clearBlocked(Blocked_NoMSHRs); + cache->clearMasterRequest(Request_MSHR); + } + } +} + +void +BlockingBuffer::doWriteback(Addr addr, int asid, + int size, uint8_t *data, bool compressed) +{ + // Generate request + Request * req = new Request(addr, size, 0); + Packet * pkt = new Packet(req, Packet::Writeback, -1); + pkt->allocate(); + if (data) { + memcpy(pkt->getPtr<uint8_t>(), data, size); + } + + if (compressed) { + pkt->flags |= COMPRESSED; + } + + ///All writebacks charged to same thread @todo figure this out + writebacks[pkt->req->getThreadNum()]++; + + wb.allocateAsBuffer(pkt); + cache->setMasterRequest(Request_WB, curTick); + cache->setBlocked(Blocked_NoWBBuffers); +} + + + +void +BlockingBuffer::doWriteback(Packet * &pkt) +{ + writebacks[pkt->req->getThreadNum()]++; + + wb.allocateAsBuffer(pkt); + + // Since allocate as buffer copies the request, + // need to copy data here. + memcpy(wb.pkt->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), pkt->getSize()); + + cache->setBlocked(Blocked_NoWBBuffers); + cache->setMasterRequest(Request_WB, curTick); +} diff --git a/src/mem/cache/miss/blocking_buffer.hh b/src/mem/cache/miss/blocking_buffer.hh new file mode 100644 index 000000000..39a06a377 --- /dev/null +++ b/src/mem/cache/miss/blocking_buffer.hh @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a simple buffer for a blocking cache. + */ + +#ifndef __BLOCKING_BUFFER_HH__ +#define __BLOCKING_BUFFER_HH__ + +#include <vector> + +#include "mem/cache/miss/mshr.hh" +#include "base/statistics.hh" + +class BaseCache; +class BasePrefetcher; + +/** + * Miss and writeback storage for a blocking cache. + */ +class BlockingBuffer +{ +protected: + /** Miss storage. */ + MSHR miss; + /** WB storage. */ + MSHR wb; + + //Params + + /** Allocate on write misses. */ + const bool writeAllocate; + + /** Pointer to the parent cache. */ + BaseCache* cache; + + BasePrefetcher* prefetcher; + + /** Block size of the parent cache. */ + int blkSize; + + // Statistics + /** + * @addtogroup CacheStatistics + * @{ + */ + /** Number of blocks written back per thread. */ + Stats::Vector<> writebacks; + + /** + * @} + */ + +public: + /** + * Builds and initializes this buffer. + * @param write_allocate If true, treat write misses the same as reads. + */ + BlockingBuffer(bool write_allocate) + : writeAllocate(write_allocate) + { + } + + /** + * Register statistics for this object. + * @param name The name of the parent cache. + */ + void regStats(const std::string &name); + + /** + * Called by the parent cache to set the back pointer. + * @param _cache A pointer to the parent cache. + */ + void setCache(BaseCache *_cache); + + void setPrefetcher(BasePrefetcher *_prefetcher); + + /** + * Handle a cache miss properly. Requests the bus and marks the cache as + * blocked. + * @param req The request that missed in the cache. + * @param blk_size The block size of the cache. + * @param time The time the miss is detected. + */ + void handleMiss(Packet * &pkt, int blk_size, Tick time); + + /** + * Fetch the block for the given address and buffer the given target. + * @param addr The address to fetch. + * @param asid The address space of the address. + * @param blk_size The block size of the cache. + * @param time The time the miss is detected. + * @param target The target for the fetch. + */ + MSHR* fetchBlock(Addr addr, int asid, int blk_size, Tick time, + Packet * &target) + { + fatal("Unimplemented"); + } + + /** + * Selects a outstanding request to service. + * @return The request to service, NULL if none found. + */ + Packet * getPacket(); + + /** + * Set the command to the given bus command. + * @param req The request to update. + * @param cmd The bus command to use. + */ + void setBusCmd(Packet * &pkt, Packet::Command cmd); + + /** + * Restore the original command in case of a bus transmission error. + * @param req The request to reset. + */ + void restoreOrigCmd(Packet * &pkt); + + /** + * Marks a request as in service (sent on the bus). This can have side + * effect since storage for no response commands is deallocated once they + * are successfully sent. + * @param req The request that was sent on the bus. + */ + void markInService(Packet * &pkt); + + /** + * Frees the resources of the request and unblock the cache. + * @param req The request that has been satisfied. + * @param time The time when the request is satisfied. + */ + void handleResponse(Packet * &pkt, Tick time); + + /** + * Removes all outstanding requests for a given thread number. If a request + * has been sent to the bus, this function removes all of its targets. + * @param req->getThreadNum()ber The thread number of the requests to squash. + */ + void squash(int threadNum); + + /** + * Return the current number of outstanding misses. + * @return the number of outstanding misses. + */ + int getMisses() + { + return miss.getNumTargets(); + } + + /** + * Searches for the supplied address in the miss "queue". + * @param addr The address to look for. + * @param asid The address space id. + * @return A pointer to miss if it matches. + */ + MSHR* findMSHR(Addr addr, int asid) + { + if (miss.addr == addr && miss.pkt) + return &miss; + return NULL; + } + + /** + * Searches for the supplied address in the write buffer. + * @param addr The address to look for. + * @param asid The address space id. + * @param writes List of pointers to the matching writes. + * @return True if there is a matching write. + */ + bool findWrites(Addr addr, int asid, std::vector<MSHR*>& writes) + { + if (wb.addr == addr && wb.pkt) { + writes.push_back(&wb); + return true; + } + return false; + } + + + + /** + * Perform a writeback of dirty data to the given address. + * @param addr The address to write to. + * @param asid The address space id. + * @param size The number of bytes to write. + * @param data The data to write, can be NULL. + * @param compressed True if the data is compressed. + */ + void doWriteback(Addr addr, int asid, + int size, uint8_t *data, bool compressed); + + /** + * Perform a writeback request. + * @param req The writeback request. + */ + void doWriteback(Packet * &pkt); + + /** + * Returns true if there are outstanding requests. + * @return True if there are outstanding requests. + */ + bool havePending() + { + return !miss.inService || !wb.inService; + } + + /** + * Add a target to the given MSHR. This assumes it is in the miss queue. + * @param mshr The mshr to add a target to. + * @param req The target to add. + */ + void addTarget(MSHR *mshr, Packet * &pkt) + { + fatal("Shouldn't call this on a blocking buffer."); + } + + /** + * Dummy implmentation. + */ + MSHR* allocateTargetList(Addr addr, int asid) + { + fatal("Unimplemented"); + } +}; + +#endif // __BLOCKING_BUFFER_HH__ diff --git a/src/mem/cache/miss/miss_queue.cc b/src/mem/cache/miss/miss_queue.cc new file mode 100644 index 000000000..da0448ad3 --- /dev/null +++ b/src/mem/cache/miss/miss_queue.cc @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Ron Dreslinski + */ + +/** + * @file + * Miss and writeback queue definitions. + */ + +#include "cpu/smt.hh" //for maxThreadsPerCPU +#include "mem/cache/base_cache.hh" +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/prefetch/base_prefetcher.hh" + +using namespace std; + +// simple constructor +/** + * @todo Remove the +16 from the write buffer constructor once we handle + * stalling on writebacks do to compression writes. + */ +MissQueue::MissQueue(int numMSHRs, int numTargets, int write_buffers, + bool write_allocate, bool prefetch_miss) + : mq(numMSHRs, 4), wb(write_buffers,numMSHRs+1000), numMSHR(numMSHRs), + numTarget(numTargets), writeBuffers(write_buffers), + writeAllocate(write_allocate), order(0), prefetchMiss(prefetch_miss) +{ + noTargetMSHR = NULL; +} + +void +MissQueue::regStats(const string &name) +{ + Request temp_req((Addr) NULL, 4, 0); + Packet::Command temp_cmd = Packet::ReadReq; + Packet temp_pkt(&temp_req, temp_cmd, 0); //@todo FIx command strings so this isn't neccessary + temp_pkt.allocate(); + + using namespace Stats; + + writebacks + .init(maxThreadsPerCPU) + .name(name + ".writebacks") + .desc("number of writebacks") + .flags(total) + ; + + // MSHR hit statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_hits[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_hits") + .desc("number of " + cstr + " MSHR hits") + .flags(total | nozero | nonan) + ; + } + + demandMshrHits + .name(name + ".demand_mshr_hits") + .desc("number of demand (read+write) MSHR hits") + .flags(total) + ; + demandMshrHits = mshr_hits[Packet::ReadReq] + mshr_hits[Packet::WriteReq]; + + overallMshrHits + .name(name + ".overall_mshr_hits") + .desc("number of overall MSHR hits") + .flags(total) + ; + overallMshrHits = demandMshrHits + mshr_hits[Packet::SoftPFReq] + + mshr_hits[Packet::HardPFReq]; + + // MSHR miss statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_misses[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_misses") + .desc("number of " + cstr + " MSHR misses") + .flags(total | nozero | nonan) + ; + } + + demandMshrMisses + .name(name + ".demand_mshr_misses") + .desc("number of demand (read+write) MSHR misses") + .flags(total) + ; + demandMshrMisses = mshr_misses[Packet::ReadReq] + mshr_misses[Packet::WriteReq]; + + overallMshrMisses + .name(name + ".overall_mshr_misses") + .desc("number of overall MSHR misses") + .flags(total) + ; + overallMshrMisses = demandMshrMisses + mshr_misses[Packet::SoftPFReq] + + mshr_misses[Packet::HardPFReq]; + + // MSHR miss latency statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_miss_latency[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_miss_latency") + .desc("number of " + cstr + " MSHR miss cycles") + .flags(total | nozero | nonan) + ; + } + + demandMshrMissLatency + .name(name + ".demand_mshr_miss_latency") + .desc("number of demand (read+write) MSHR miss cycles") + .flags(total) + ; + demandMshrMissLatency = mshr_miss_latency[Packet::ReadReq] + + mshr_miss_latency[Packet::WriteReq]; + + overallMshrMissLatency + .name(name + ".overall_mshr_miss_latency") + .desc("number of overall MSHR miss cycles") + .flags(total) + ; + overallMshrMissLatency = demandMshrMissLatency + + mshr_miss_latency[Packet::SoftPFReq] + mshr_miss_latency[Packet::HardPFReq]; + + // MSHR uncacheable statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_uncacheable[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_uncacheable") + .desc("number of " + cstr + " MSHR uncacheable") + .flags(total | nozero | nonan) + ; + } + + overallMshrUncacheable + .name(name + ".overall_mshr_uncacheable_misses") + .desc("number of overall MSHR uncacheable misses") + .flags(total) + ; + overallMshrUncacheable = mshr_uncacheable[Packet::ReadReq] + + mshr_uncacheable[Packet::WriteReq] + mshr_uncacheable[Packet::SoftPFReq] + + mshr_uncacheable[Packet::HardPFReq]; + + // MSHR miss latency statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_uncacheable_lat[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_uncacheable_latency") + .desc("number of " + cstr + " MSHR uncacheable cycles") + .flags(total | nozero | nonan) + ; + } + + overallMshrUncacheableLatency + .name(name + ".overall_mshr_uncacheable_latency") + .desc("number of overall MSHR uncacheable cycles") + .flags(total) + ; + overallMshrUncacheableLatency = mshr_uncacheable_lat[Packet::ReadReq] + + mshr_uncacheable_lat[Packet::WriteReq] + + mshr_uncacheable_lat[Packet::SoftPFReq] + + mshr_uncacheable_lat[Packet::HardPFReq]; + +#if 0 + // MSHR access formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshrAccesses[access_idx] + .name(name + "." + cstr + "_mshr_accesses") + .desc("number of " + cstr + " mshr accesses(hits+misses)") + .flags(total | nozero | nonan) + ; + mshrAccesses[access_idx] = + mshr_hits[access_idx] + mshr_misses[access_idx] + + mshr_uncacheable[access_idx]; + } + + demandMshrAccesses + .name(name + ".demand_mshr_accesses") + .desc("number of demand (read+write) mshr accesses") + .flags(total | nozero | nonan) + ; + demandMshrAccesses = demandMshrHits + demandMshrMisses; + + overallMshrAccesses + .name(name + ".overall_mshr_accesses") + .desc("number of overall (read+write) mshr accesses") + .flags(total | nozero | nonan) + ; + overallMshrAccesses = overallMshrHits + overallMshrMisses + + overallMshrUncacheable; +#endif + + // MSHR miss rate formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshrMissRate[access_idx] + .name(name + "." + cstr + "_mshr_miss_rate") + .desc("mshr miss rate for " + cstr + " accesses") + .flags(total | nozero | nonan) + ; + + mshrMissRate[access_idx] = + mshr_misses[access_idx] / cache->accesses[access_idx]; + } + + demandMshrMissRate + .name(name + ".demand_mshr_miss_rate") + .desc("mshr miss rate for demand accesses") + .flags(total) + ; + demandMshrMissRate = demandMshrMisses / cache->demandAccesses; + + overallMshrMissRate + .name(name + ".overall_mshr_miss_rate") + .desc("mshr miss rate for overall accesses") + .flags(total) + ; + overallMshrMissRate = overallMshrMisses / cache->overallAccesses; + + // mshrMiss latency formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + avgMshrMissLatency[access_idx] + .name(name + "." + cstr + "_avg_mshr_miss_latency") + .desc("average " + cstr + " mshr miss latency") + .flags(total | nozero | nonan) + ; + + avgMshrMissLatency[access_idx] = + mshr_miss_latency[access_idx] / mshr_misses[access_idx]; + } + + demandAvgMshrMissLatency + .name(name + ".demand_avg_mshr_miss_latency") + .desc("average overall mshr miss latency") + .flags(total) + ; + demandAvgMshrMissLatency = demandMshrMissLatency / demandMshrMisses; + + overallAvgMshrMissLatency + .name(name + ".overall_avg_mshr_miss_latency") + .desc("average overall mshr miss latency") + .flags(total) + ; + overallAvgMshrMissLatency = overallMshrMissLatency / overallMshrMisses; + + // mshrUncacheable latency formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + avgMshrUncacheableLatency[access_idx] + .name(name + "." + cstr + "_avg_mshr_uncacheable_latency") + .desc("average " + cstr + " mshr uncacheable latency") + .flags(total | nozero | nonan) + ; + + avgMshrUncacheableLatency[access_idx] = + mshr_uncacheable_lat[access_idx] / mshr_uncacheable[access_idx]; + } + + overallAvgMshrUncacheableLatency + .name(name + ".overall_avg_mshr_uncacheable_latency") + .desc("average overall mshr uncacheable latency") + .flags(total) + ; + overallAvgMshrUncacheableLatency = overallMshrUncacheableLatency / overallMshrUncacheable; + + mshr_cap_events + .init(maxThreadsPerCPU) + .name(name + ".mshr_cap_events") + .desc("number of times MSHR cap was activated") + .flags(total) + ; + + //software prefetching stats + soft_prefetch_mshr_full + .init(maxThreadsPerCPU) + .name(name + ".soft_prefetch_mshr_full") + .desc("number of mshr full events for SW prefetching instrutions") + .flags(total) + ; + + mshr_no_allocate_misses + .name(name +".no_allocate_misses") + .desc("Number of misses that were no-allocate") + ; + +} + +void +MissQueue::setCache(BaseCache *_cache) +{ + cache = _cache; + blkSize = cache->getBlockSize(); +} + +void +MissQueue::setPrefetcher(BasePrefetcher *_prefetcher) +{ + prefetcher = _prefetcher; +} + +MSHR* +MissQueue::allocateMiss(Packet * &pkt, int size, Tick time) +{ + MSHR* mshr = mq.allocate(pkt, size); + mshr->order = order++; + if (!pkt->req->isUncacheable() ){//&& !pkt->isNoAllocate()) { + // Mark this as a cache line fill + mshr->pkt->flags |= CACHE_LINE_FILL; + } + if (mq.isFull()) { + cache->setBlocked(Blocked_NoMSHRs); + } + if (pkt->cmd != Packet::HardPFReq) { + //If we need to request the bus (not on HW prefetch), do so + cache->setMasterRequest(Request_MSHR, time); + } + return mshr; +} + + +MSHR* +MissQueue::allocateWrite(Packet * &pkt, int size, Tick time) +{ + MSHR* mshr = wb.allocate(pkt,pkt->getSize()); + mshr->order = order++; + +//REMOVING COMPRESSION FOR NOW +#if 0 + if (pkt->isCompressed()) { + mshr->pkt->deleteData(); + mshr->pkt->actualSize = pkt->actualSize; + mshr->pkt->data = new uint8_t[pkt->actualSize]; + memcpy(mshr->pkt->data, pkt->data, pkt->actualSize); + } else { +#endif + memcpy(mshr->pkt->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), pkt->getSize()); + //{ + + if (wb.isFull()) { + cache->setBlocked(Blocked_NoWBBuffers); + } + + cache->setMasterRequest(Request_WB, time); + + return mshr; +} + + +/** + * @todo Remove SW prefetches on mshr hits. + */ +void +MissQueue::handleMiss(Packet * &pkt, int blkSize, Tick time) +{ +// if (!cache->isTopLevel()) + if (prefetchMiss) prefetcher->handleMiss(pkt, time); + + int size = blkSize; + Addr blkAddr = pkt->getAddr() & ~(Addr)(blkSize-1); + MSHR* mshr = NULL; + if (!pkt->req->isUncacheable()) { + mshr = mq.findMatch(blkAddr, pkt->req->getAsid()); + if (mshr) { + //@todo remove hw_pf here + mshr_hits[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + if (mshr->threadNum != pkt->req->getThreadNum()) { + mshr->threadNum = -1; + } + mq.allocateTarget(mshr, pkt); + if (mshr->pkt->isNoAllocate() && !pkt->isNoAllocate()) { + //We are adding an allocate after a no-allocate + mshr->pkt->flags &= ~NO_ALLOCATE; + } + if (mshr->getNumTargets() == numTarget) { + noTargetMSHR = mshr; + cache->setBlocked(Blocked_NoTargets); + mq.moveToFront(mshr); + } + return; + } + if (pkt->isNoAllocate()) { + //Count no-allocate requests differently + mshr_no_allocate_misses++; + } + else { + mshr_misses[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + } + } else { + //Count uncacheable accesses + mshr_uncacheable[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + size = pkt->getSize(); + } + if (pkt->isWrite() && (pkt->req->isUncacheable() || !writeAllocate || + !pkt->needsResponse())) { + /** + * @todo Add write merging here. + */ + mshr = allocateWrite(pkt, pkt->getSize(), time); + return; + } + + mshr = allocateMiss(pkt, size, time); +} + +MSHR* +MissQueue::fetchBlock(Addr addr, int asid, int blk_size, Tick time, + Packet * &target) +{ + Addr blkAddr = addr & ~(Addr)(blk_size - 1); + assert(mq.findMatch(addr, asid) == NULL); + MSHR *mshr = mq.allocateFetch(blkAddr, asid, blk_size, target); + mshr->order = order++; + mshr->pkt->flags |= CACHE_LINE_FILL; + if (mq.isFull()) { + cache->setBlocked(Blocked_NoMSHRs); + } + cache->setMasterRequest(Request_MSHR, time); + return mshr; +} + +Packet * +MissQueue::getPacket() +{ + Packet * pkt = mq.getReq(); + if (((wb.isFull() && wb.inServiceMSHRs == 0) || !pkt || + pkt->time > curTick) && wb.havePending()) { + pkt = wb.getReq(); + // Need to search for earlier miss. + MSHR *mshr = mq.findPending(pkt); + if (mshr && mshr->order < ((MSHR*)(pkt->senderState))->order) { + // Service misses in order until conflict is cleared. + return mq.getReq(); + } + } + if (pkt) { + MSHR* mshr = wb.findPending(pkt); + if (mshr /*&& mshr->order < pkt->senderState->order*/) { + // The only way this happens is if we are + // doing a write and we didn't have permissions + // then subsequently saw a writeback(owned got evicted) + // We need to make sure to perform the writeback first + // To preserve the dirty data, then we can issue the write + return wb.getReq(); + } + } + else if (!mq.isFull()){ + //If we have a miss queue slot, we can try a prefetch + pkt = prefetcher->getPacket(); + if (pkt) { + //Update statistic on number of prefetches issued (hwpf_mshr_misses) + mshr_misses[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + //It will request the bus for the future, but should clear that immedieatley + allocateMiss(pkt, pkt->getSize(), curTick); + pkt = mq.getReq(); + assert(pkt); //We should get back a req b/c we just put one in + } + } + return pkt; +} + +void +MissQueue::setBusCmd(Packet * &pkt, Packet::Command cmd) +{ + assert(pkt->senderState != 0); + MSHR * mshr = (MSHR*)pkt->senderState; + mshr->originalCmd = pkt->cmd; + if (pkt->isCacheFill() || pkt->isNoAllocate()) + pkt->cmd = cmd; +} + +void +MissQueue::restoreOrigCmd(Packet * &pkt) +{ + pkt->cmd = ((MSHR*)(pkt->senderState))->originalCmd; +} + +void +MissQueue::markInService(Packet * &pkt) +{ + assert(pkt->senderState != 0); + bool unblock = false; + BlockedCause cause = NUM_BLOCKED_CAUSES; + + /** + * @todo Should include MSHRQueue pointer in MSHR to select the correct + * one. + */ + if ((!pkt->isCacheFill() && pkt->isWrite())) { + // Forwarding a write/ writeback, don't need to change + // the command + unblock = wb.isFull(); + wb.markInService((MSHR*)pkt->senderState); + if (!wb.havePending()){ + cache->clearMasterRequest(Request_WB); + } + if (unblock) { + // Do we really unblock? + unblock = !wb.isFull(); + cause = Blocked_NoWBBuffers; + } + } else { + unblock = mq.isFull(); + mq.markInService((MSHR*)pkt->senderState); + if (!mq.havePending()){ + cache->clearMasterRequest(Request_MSHR); + } + if (((MSHR*)(pkt->senderState))->originalCmd == Packet::HardPFReq) { + DPRINTF(HWPrefetch, "%s:Marking a HW_PF in service\n", + cache->name()); + //Also clear pending if need be + if (!prefetcher->havePending()) + { + cache->clearMasterRequest(Request_PF); + } + } + if (unblock) { + unblock = !mq.isFull(); + cause = Blocked_NoMSHRs; + } + } + if (unblock) { + cache->clearBlocked(cause); + } +} + + +void +MissQueue::handleResponse(Packet * &pkt, Tick time) +{ + MSHR* mshr = (MSHR*)pkt->senderState; + if (((MSHR*)(pkt->senderState))->originalCmd == Packet::HardPFReq) { + DPRINTF(HWPrefetch, "%s:Handling the response to a HW_PF\n", + cache->name()); + } +#ifndef NDEBUG + int num_targets = mshr->getNumTargets(); +#endif + + bool unblock = false; + bool unblock_target = false; + BlockedCause cause = NUM_BLOCKED_CAUSES; + + if (pkt->isCacheFill() && !pkt->isNoAllocate()) { + mshr_miss_latency[mshr->originalCmd][pkt->req->getThreadNum()] += + curTick - pkt->time; + // targets were handled in the cache tags + if (mshr == noTargetMSHR) { + // we always clear at least one target + unblock_target = true; + cause = Blocked_NoTargets; + noTargetMSHR = NULL; + } + + if (mshr->hasTargets()) { + // Didn't satisfy all the targets, need to resend + Packet::Command cmd = mshr->getTarget()->cmd; + mq.markPending(mshr, cmd); + mshr->order = order++; + cache->setMasterRequest(Request_MSHR, time); + } + else { + unblock = mq.isFull(); + mq.deallocate(mshr); + if (unblock) { + unblock = !mq.isFull(); + cause = Blocked_NoMSHRs; + } + } + } else { + if (pkt->req->isUncacheable()) { + mshr_uncacheable_lat[pkt->cmd][pkt->req->getThreadNum()] += + curTick - pkt->time; + } + if (mshr->hasTargets() && pkt->req->isUncacheable()) { + // Should only have 1 target if we had any + assert(num_targets == 1); + Packet * target = mshr->getTarget(); + mshr->popTarget(); + if (pkt->isRead()) { + memcpy(target->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), + target->getSize()); + } + cache->respond(target, time); + assert(!mshr->hasTargets()); + } + else if (mshr->hasTargets()) { + //Must be a no_allocate with possibly more than one target + assert(mshr->pkt->isNoAllocate()); + while (mshr->hasTargets()) { + Packet * target = mshr->getTarget(); + mshr->popTarget(); + if (pkt->isRead()) { + memcpy(target->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), + target->getSize()); + } + cache->respond(target, time); + } + } + + if (pkt->isWrite()) { + // If the wrtie buffer is full, we might unblock now + unblock = wb.isFull(); + wb.deallocate(mshr); + if (unblock) { + // Did we really unblock? + unblock = !wb.isFull(); + cause = Blocked_NoWBBuffers; + } + } else { + unblock = mq.isFull(); + mq.deallocate(mshr); + if (unblock) { + unblock = !mq.isFull(); + cause = Blocked_NoMSHRs; + } + } + } + if (unblock || unblock_target) { + cache->clearBlocked(cause); + } +} + +void +MissQueue::squash(int threadNum) +{ + bool unblock = false; + BlockedCause cause = NUM_BLOCKED_CAUSES; + + if (noTargetMSHR && noTargetMSHR->threadNum == threadNum) { + noTargetMSHR = NULL; + unblock = true; + cause = Blocked_NoTargets; + } + if (mq.isFull()) { + unblock = true; + cause = Blocked_NoMSHRs; + } + mq.squash(threadNum); + if (!mq.havePending()) { + cache->clearMasterRequest(Request_MSHR); + } + if (unblock && !mq.isFull()) { + cache->clearBlocked(cause); + } + +} + +MSHR* +MissQueue::findMSHR(Addr addr, int asid) const +{ + return mq.findMatch(addr,asid); +} + +bool +MissQueue::findWrites(Addr addr, int asid, vector<MSHR*> &writes) const +{ + return wb.findMatches(addr,asid,writes); +} + +void +MissQueue::doWriteback(Addr addr, int asid, + int size, uint8_t *data, bool compressed) +{ + // Generate request + Request * req = new Request(addr, size, 0); + Packet * pkt = new Packet(req, Packet::Writeback, -1); + pkt->allocate(); + if (data) { + memcpy(pkt->getPtr<uint8_t>(), data, size); + } + + if (compressed) { + pkt->flags |= COMPRESSED; + } + + ///All writebacks charged to same thread @todo figure this out + writebacks[pkt->req->getThreadNum()]++; + + allocateWrite(pkt, 0, curTick); +} + + +void +MissQueue::doWriteback(Packet * &pkt) +{ + writebacks[pkt->req->getThreadNum()]++; + allocateWrite(pkt, 0, curTick); +} + + +MSHR* +MissQueue::allocateTargetList(Addr addr, int asid) +{ + MSHR* mshr = mq.allocateTargetList(addr, asid, blkSize); + mshr->pkt->flags |= CACHE_LINE_FILL; + if (mq.isFull()) { + cache->setBlocked(Blocked_NoMSHRs); + } + return mshr; +} + +bool +MissQueue::havePending() +{ + return mq.havePending() || wb.havePending() || prefetcher->havePending(); +} diff --git a/src/mem/cache/miss/miss_queue.hh b/src/mem/cache/miss/miss_queue.hh new file mode 100644 index 000000000..b88b7038c --- /dev/null +++ b/src/mem/cache/miss/miss_queue.hh @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Miss and writeback queue declarations. + */ + +#ifndef __MISS_QUEUE_HH__ +#define __MISS_QUEUE_HH__ + +#include <vector> + +#include "mem/cache/miss/mshr.hh" +#include "mem/cache/miss/mshr_queue.hh" +#include "base/statistics.hh" + +class BaseCache; +class BasePrefetcher; +/** + * Manages cache misses and writebacks. Contains MSHRs to store miss data + * and the writebuffer for writes/writebacks. + * @todo need to handle data on writes better (encapsulate). + * @todo need to make replacements/writebacks happen in Cache::access + */ +class MissQueue +{ + protected: + /** The MSHRs. */ + MSHRQueue mq; + /** Write Buffer. */ + MSHRQueue wb; + + // PARAMTERS + + /** The number of MSHRs in the miss queue. */ + const int numMSHR; + /** The number of targets for each MSHR. */ + const int numTarget; + /** The number of write buffers. */ + const int writeBuffers; + /** True if the cache should allocate on a write miss. */ + const bool writeAllocate; + /** Pointer to the parent cache. */ + BaseCache* cache; + + /** The Prefetcher */ + BasePrefetcher *prefetcher; + + /** The block size of the parent cache. */ + int blkSize; + + /** Increasing order number assigned to each incoming request. */ + uint64_t order; + + bool prefetchMiss; + + // Statistics + /** + * @addtogroup CacheStatistics + * @{ + */ + /** Number of blocks written back per thread. */ + Stats::Vector<> writebacks; + + /** Number of misses that hit in the MSHRs per command and thread. */ + Stats::Vector<> mshr_hits[NUM_MEM_CMDS]; + /** Demand misses that hit in the MSHRs. */ + Stats::Formula demandMshrHits; + /** Total number of misses that hit in the MSHRs. */ + Stats::Formula overallMshrHits; + + /** Number of misses that miss in the MSHRs, per command and thread. */ + Stats::Vector<> mshr_misses[NUM_MEM_CMDS]; + /** Demand misses that miss in the MSHRs. */ + Stats::Formula demandMshrMisses; + /** Total number of misses that miss in the MSHRs. */ + Stats::Formula overallMshrMisses; + + /** Number of misses that miss in the MSHRs, per command and thread. */ + Stats::Vector<> mshr_uncacheable[NUM_MEM_CMDS]; + /** Total number of misses that miss in the MSHRs. */ + Stats::Formula overallMshrUncacheable; + + /** Total cycle latency of each MSHR miss, per command and thread. */ + Stats::Vector<> mshr_miss_latency[NUM_MEM_CMDS]; + /** Total cycle latency of demand MSHR misses. */ + Stats::Formula demandMshrMissLatency; + /** Total cycle latency of overall MSHR misses. */ + Stats::Formula overallMshrMissLatency; + + /** Total cycle latency of each MSHR miss, per command and thread. */ + Stats::Vector<> mshr_uncacheable_lat[NUM_MEM_CMDS]; + /** Total cycle latency of overall MSHR misses. */ + Stats::Formula overallMshrUncacheableLatency; + + /** The total number of MSHR accesses per command and thread. */ + Stats::Formula mshrAccesses[NUM_MEM_CMDS]; + /** The total number of demand MSHR accesses. */ + Stats::Formula demandMshrAccesses; + /** The total number of MSHR accesses. */ + Stats::Formula overallMshrAccesses; + + /** The miss rate in the MSHRs pre command and thread. */ + Stats::Formula mshrMissRate[NUM_MEM_CMDS]; + /** The demand miss rate in the MSHRs. */ + Stats::Formula demandMshrMissRate; + /** The overall miss rate in the MSHRs. */ + Stats::Formula overallMshrMissRate; + + /** The average latency of an MSHR miss, per command and thread. */ + Stats::Formula avgMshrMissLatency[NUM_MEM_CMDS]; + /** The average latency of a demand MSHR miss. */ + Stats::Formula demandAvgMshrMissLatency; + /** The average overall latency of an MSHR miss. */ + Stats::Formula overallAvgMshrMissLatency; + + /** The average latency of an MSHR miss, per command and thread. */ + Stats::Formula avgMshrUncacheableLatency[NUM_MEM_CMDS]; + /** The average overall latency of an MSHR miss. */ + Stats::Formula overallAvgMshrUncacheableLatency; + + /** The number of times a thread hit its MSHR cap. */ + Stats::Vector<> mshr_cap_events; + /** The number of times software prefetches caused the MSHR to block. */ + Stats::Vector<> soft_prefetch_mshr_full; + + Stats::Scalar<> mshr_no_allocate_misses; + + /** + * @} + */ + + private: + /** Pointer to the MSHR that has no targets. */ + MSHR* noTargetMSHR; + + /** + * Allocate a new MSHR to handle the provided miss. + * @param req The miss to buffer. + * @param size The number of bytes to fetch. + * @param time The time the miss occurs. + * @return A pointer to the new MSHR. + */ + MSHR* allocateMiss(Packet * &pkt, int size, Tick time); + + /** + * Allocate a new WriteBuffer to handle the provided write. + * @param req The write to handle. + * @param size The number of bytes to write. + * @param time The time the write occurs. + * @return A pointer to the new write buffer. + */ + MSHR* allocateWrite(Packet * &pkt, int size, Tick time); + + public: + /** + * Simple Constructor. Initializes all needed internal storage and sets + * parameters. + * @param numMSHRs The number of outstanding misses to handle. + * @param numTargets The number of outstanding targets to each miss. + * @param write_buffers The number of outstanding writes to handle. + * @param write_allocate If true, treat write misses the same as reads. + */ + MissQueue(int numMSHRs, int numTargets, int write_buffers, + bool write_allocate, bool prefetch_miss); + + /** + * Deletes all allocated internal storage. + */ + ~MissQueue(); + + /** + * Register statistics for this object. + * @param name The name of the parent cache. + */ + void regStats(const std::string &name); + + /** + * Called by the parent cache to set the back pointer. + * @param _cache A pointer to the parent cache. + */ + void setCache(BaseCache *_cache); + + void setPrefetcher(BasePrefetcher *_prefetcher); + + /** + * Handle a cache miss properly. Either allocate an MSHR for the request, + * or forward it through the write buffer. + * @param req The request that missed in the cache. + * @param blk_size The block size of the cache. + * @param time The time the miss is detected. + */ + void handleMiss(Packet * &pkt, int blk_size, Tick time); + + /** + * Fetch the block for the given address and buffer the given target. + * @param addr The address to fetch. + * @param asid The address space of the address. + * @param blk_size The block size of the cache. + * @param time The time the miss is detected. + * @param target The target for the fetch. + */ + MSHR* fetchBlock(Addr addr, int asid, int blk_size, Tick time, + Packet * &target); + + /** + * Selects a outstanding request to service. + * @return The request to service, NULL if none found. + */ + Packet * getPacket(); + + /** + * Set the command to the given bus command. + * @param req The request to update. + * @param cmd The bus command to use. + */ + void setBusCmd(Packet * &pkt, Packet::Command cmd); + + /** + * Restore the original command in case of a bus transmission error. + * @param req The request to reset. + */ + void restoreOrigCmd(Packet * &pkt); + + /** + * Marks a request as in service (sent on the bus). This can have side + * effect since storage for no response commands is deallocated once they + * are successfully sent. + * @param req The request that was sent on the bus. + */ + void markInService(Packet * &pkt); + + /** + * Collect statistics and free resources of a satisfied request. + * @param req The request that has been satisfied. + * @param time The time when the request is satisfied. + */ + void handleResponse(Packet * &pkt, Tick time); + + /** + * Removes all outstanding requests for a given thread number. If a request + * has been sent to the bus, this function removes all of its targets. + * @param req->getThreadNum()ber The thread number of the requests to squash. + */ + void squash(int threadNum); + + /** + * Return the current number of outstanding misses. + * @return the number of outstanding misses. + */ + int getMisses() + { + return mq.getAllocatedTargets(); + } + + /** + * Searches for the supplied address in the miss queue. + * @param addr The address to look for. + * @param asid The address space id. + * @return The MSHR that contains the address, NULL if not found. + * @warning Currently only searches the miss queue. If non write allocate + * might need to search the write buffer for coherence. + */ + MSHR* findMSHR(Addr addr, int asid) const; + + /** + * Searches for the supplied address in the write buffer. + * @param addr The address to look for. + * @param asid The address space id. + * @param writes The list of writes that match the address. + * @return True if any writes are found + */ + bool findWrites(Addr addr, int asid, std::vector<MSHR*>& writes) const; + + /** + * Perform a writeback of dirty data to the given address. + * @param addr The address to write to. + * @param asid The address space id. + * @param xc The execution context of the address space. + * @param size The number of bytes to write. + * @param data The data to write, can be NULL. + * @param compressed True if the data is compressed. + */ + void doWriteback(Addr addr, int asid, + int size, uint8_t *data, bool compressed); + + /** + * Perform the given writeback request. + * @param req The writeback request. + */ + void doWriteback(Packet * &pkt); + + /** + * Returns true if there are outstanding requests. + * @return True if there are outstanding requests. + */ + bool havePending(); + + /** + * Add a target to the given MSHR. This assumes it is in the miss queue. + * @param mshr The mshr to add a target to. + * @param req The target to add. + */ + void addTarget(MSHR *mshr, Packet * &pkt) + { + mq.allocateTarget(mshr, pkt); + } + + /** + * Allocate a MSHR to hold a list of targets to a block involved in a copy. + * If the block is marked done then the MSHR already holds the data to + * fill the block. Otherwise the block needs to be fetched. + * @param addr The address to buffer. + * @param asid The address space ID. + * @return A pointer to the allocated MSHR. + */ + MSHR* allocateTargetList(Addr addr, int asid); + +}; + +#endif //__MISS_QUEUE_HH__ diff --git a/src/mem/cache/miss/mshr.cc b/src/mem/cache/miss/mshr.cc new file mode 100644 index 000000000..1a85d3018 --- /dev/null +++ b/src/mem/cache/miss/mshr.cc @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Dave Greene + */ + +/** + * @file + * Miss Status and Handling Register (MSHR) definitions. + */ + +#include <assert.h> +#include <string> +#include <vector> + +#include "mem/cache/miss/mshr.hh" +#include "sim/root.hh" // for curTick +#include "sim/host.hh" +#include "base/misc.hh" +#include "mem/cache/cache.hh" + +using namespace std; + +MSHR::MSHR() +{ + inService = false; + ntargets = 0; + threadNum = -1; +} + +void +MSHR::allocate(Packet::Command cmd, Addr _addr, int _asid, int size, + Packet * &target) +{ + if (target) + { + //Have a request, just use it + pkt = new Packet(target->req, cmd, Packet::Broadcast, size); + pkt->time = curTick; + pkt->allocate(); + pkt->senderState = (Packet::SenderState *)this; + allocateTarget(target); + } + else + { + //need a request first + Request * req = new Request(); + req->setPhys(addr, size, 0); + //Thread context?? + pkt = new Packet(req, cmd, Packet::Broadcast, size); + pkt->time = curTick; + pkt->allocate(); + pkt->senderState = (Packet::SenderState *)this; + } +} + +// Since we aren't sure if data is being used, don't copy here. +/** + * @todo When we have a "global" data flag, might want to copy data here. + */ +void +MSHR::allocateAsBuffer(Packet * &target) +{ + addr = target->getAddr(); + asid = target->req->getAsid(); + threadNum = target->req->getThreadNum(); + pkt = new Packet(target->req, target->cmd, -1); + pkt->allocate(); + pkt->senderState = (Packet::SenderState*)this; + pkt->time = curTick; +} + +void +MSHR::deallocate() +{ + assert(targets.empty()); + assert(ntargets == 0); + pkt = NULL; + inService = false; + allocIter = NULL; + readyIter = NULL; +} + +/* + * Adds a target to an MSHR + */ +void +MSHR::allocateTarget(Packet * &target) +{ + //If we append an invalidate and we issued a read to the bus, + //but now have some pending writes, we need to move + //the invalidate to before the first non-read + if (inService && pkt->isRead() && target->isInvalidate()) { + std::list<Packet *> temp; + + while (!targets.empty()) { + if (!targets.front()->isRead()) break; + //Place on top of temp stack + temp.push_front(targets.front()); + //Remove from targets + targets.pop_front(); + } + + //Now that we have all the reads off until first non-read, we can + //place the invalidate on + targets.push_front(target); + + //Now we pop off the temp_stack and put them back + while (!temp.empty()) { + targets.push_front(temp.front()); + temp.pop_front(); + } + } + else { + targets.push_back(target); + } + + ++ntargets; + assert(targets.size() == ntargets); + /** + * @todo really prioritize the target commands. + */ + + if (!inService && target->isWrite()) { + pkt->cmd = Packet::WriteReq; + } +} + + + +void +MSHR::dump() +{ + ccprintf(cerr, + "inService: %d thread: %d\n" + "Addr: %x asid: %d ntargets %d\n" + "Targets:\n", + inService, threadNum, addr, asid, ntargets); + + TargetListIterator tar_it = targets.begin(); + for (int i = 0; i < ntargets; i++) { + assert(tar_it != targets.end()); + + ccprintf(cerr, "\t%d: Addr: %x cmd: %d\n", + i, (*tar_it)->getAddr(), (*tar_it)->cmdToIndex()); + + tar_it++; + } + ccprintf(cerr, "\n"); +} + +MSHR::~MSHR() +{ + if (pkt) + pkt = NULL; +} diff --git a/src/mem/cache/miss/mshr.hh b/src/mem/cache/miss/mshr.hh new file mode 100644 index 000000000..167aa26cd --- /dev/null +++ b/src/mem/cache/miss/mshr.hh @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Miss Status and Handling Register (MSHR) declaration. + */ + +#ifndef __MSHR_HH__ +#define __MSHR_HH__ + +#include "mem/packet.hh" +#include <list> +#include <deque> + +class MSHR; + +/** + * Miss Status and handling Register. This class keeps all the information + * needed to handle a cache miss including a list of target requests. + */ +class MSHR { + public: + /** Defines the Data structure of the MSHR targetlist. */ + typedef std::list<Packet *> TargetList; + /** Target list iterator. */ + typedef std::list<Packet *>::iterator TargetListIterator; + /** A list of MSHRs. */ + typedef std::list<MSHR *> List; + /** MSHR list iterator. */ + typedef List::iterator Iterator; + /** MSHR list const_iterator. */ + typedef List::const_iterator ConstIterator; + + /** Address of the miss. */ + Addr addr; + /** Adress space id of the miss. */ + short asid; + /** True if the request has been sent to the bus. */ + bool inService; + /** Thread number of the miss. */ + int threadNum; + /** The request that is forwarded to the next level of the hierarchy. */ + Packet * pkt; + /** The number of currently allocated targets. */ + short ntargets; + /** The original requesting command. */ + Packet::Command originalCmd; + /** Order number of assigned by the miss queue. */ + uint64_t order; + + /** + * Pointer to this MSHR on the ready list. + * @sa MissQueue, MSHRQueue::readyList + */ + Iterator readyIter; + /** + * Pointer to this MSHR on the allocated list. + * @sa MissQueue, MSHRQueue::allocatedList + */ + Iterator allocIter; + +private: + /** List of all requests that match the address */ + TargetList targets; + +public: + /** + * Allocate a miss to this MSHR. + * @param cmd The requesting command. + * @param addr The address of the miss. + * @param asid The address space id of the miss. + * @param size The number of bytes to request. + * @param req The original miss. + */ + void allocate(Packet::Command cmd, Addr addr, int asid, int size, + Packet * &pkt); + + /** + * Allocate this MSHR as a buffer for the given request. + * @param target The memory request to buffer. + */ + void allocateAsBuffer(Packet * &target); + + /** + * Mark this MSHR as free. + */ + void deallocate(); + + /** + * Add a request to the list of targets. + * @param target The target. + */ + void allocateTarget(Packet * &target); + + /** A simple constructor. */ + MSHR(); + /** A simple destructor. */ + ~MSHR(); + + /** + * Returns the current number of allocated targets. + * @return The current number of allocated targets. + */ + int getNumTargets() + { + return(ntargets); + } + + /** + * Returns a pointer to the target list. + * @return a pointer to the target list. + */ + TargetList* getTargetList() + { + return &targets; + } + + /** + * Returns a reference to the first target. + * @return A pointer to the first target. + */ + Packet * getTarget() + { + return targets.front(); + } + + /** + * Pop first target. + */ + void popTarget() + { + --ntargets; + targets.pop_front(); + } + + /** + * Returns true if there are targets left. + * @return true if there are targets + */ + bool hasTargets() + { + return !targets.empty(); + } + + /** + * Prints the contents of this MSHR to stderr. + */ + void dump(); +}; + +#endif //__MSHR_HH__ diff --git a/src/mem/cache/miss/mshr_queue.cc b/src/mem/cache/miss/mshr_queue.cc new file mode 100644 index 000000000..6516a99f8 --- /dev/null +++ b/src/mem/cache/miss/mshr_queue.cc @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** @file + * Definition of the MSHRQueue. + */ + +#include "mem/cache/miss/mshr_queue.hh" +#include "sim/eventq.hh" + +using namespace std; + +MSHRQueue::MSHRQueue(int num_mshrs, int reserve) + : numMSHRs(num_mshrs + reserve - 1), numReserve(reserve) +{ + allocated = 0; + inServiceMSHRs = 0; + allocatedTargets = 0; + registers = new MSHR[numMSHRs]; + for (int i = 0; i < numMSHRs; ++i) { + freeList.push_back(®isters[i]); + } +} + +MSHRQueue::~MSHRQueue() +{ + delete [] registers; +} + +MSHR* +MSHRQueue::findMatch(Addr addr, int asid) const +{ + MSHR::ConstIterator i = allocatedList.begin(); + MSHR::ConstIterator end = allocatedList.end(); + for (; i != end; ++i) { + MSHR *mshr = *i; + if (mshr->addr == addr) { + return mshr; + } + } + return NULL; +} + +bool +MSHRQueue::findMatches(Addr addr, int asid, vector<MSHR*>& matches) const +{ + // Need an empty vector + assert(matches.empty()); + bool retval = false; + MSHR::ConstIterator i = allocatedList.begin(); + MSHR::ConstIterator end = allocatedList.end(); + for (; i != end; ++i) { + MSHR *mshr = *i; + if (mshr->addr == addr) { + retval = true; + matches.push_back(mshr); + } + } + return retval; + +} + +MSHR* +MSHRQueue::findPending(Packet * &pkt) const +{ + MSHR::ConstIterator i = pendingList.begin(); + MSHR::ConstIterator end = pendingList.end(); + for (; i != end; ++i) { + MSHR *mshr = *i; + if (mshr->addr < pkt->getAddr()) { + if (mshr->addr + mshr->pkt->getSize() > pkt->getAddr()) { + return mshr; + } + } else { + if (pkt->getAddr() + pkt->getSize() > mshr->addr) { + return mshr; + } + } + + //need to check destination address for copies. + //TEMP NOT DOING COPIES +#if 0 + if (mshr->pkt->cmd == Copy) { + Addr dest = mshr->pkt->dest; + if (dest < pkt->addr) { + if (dest + mshr->pkt->size > pkt->addr) { + return mshr; + } + } else { + if (pkt->addr + pkt->size > dest) { + return mshr; + } + } + } +#endif + } + return NULL; +} + +MSHR* +MSHRQueue::allocate(Packet * &pkt, int size) +{ + Addr aligned_addr = pkt->getAddr() & ~((Addr)size - 1); + MSHR *mshr = freeList.front(); + assert(mshr->getNumTargets() == 0); + freeList.pop_front(); + + if (!pkt->needsResponse()) { + mshr->allocateAsBuffer(pkt); + } else { + assert(size !=0); + mshr->allocate(pkt->cmd, aligned_addr, pkt->req->getAsid(), size, pkt); + allocatedTargets += 1; + } + mshr->allocIter = allocatedList.insert(allocatedList.end(), mshr); + mshr->readyIter = pendingList.insert(pendingList.end(), mshr); + + allocated += 1; + return mshr; +} + +MSHR* +MSHRQueue::allocateFetch(Addr addr, int asid, int size, Packet * &target) +{ + MSHR *mshr = freeList.front(); + assert(mshr->getNumTargets() == 0); + freeList.pop_front(); + mshr->allocate(Packet::ReadReq, addr, asid, size, target); + mshr->allocIter = allocatedList.insert(allocatedList.end(), mshr); + mshr->readyIter = pendingList.insert(pendingList.end(), mshr); + + allocated += 1; + return mshr; +} + +MSHR* +MSHRQueue::allocateTargetList(Addr addr, int asid, int size) +{ + MSHR *mshr = freeList.front(); + assert(mshr->getNumTargets() == 0); + freeList.pop_front(); + Packet * dummy; + mshr->allocate(Packet::ReadReq, addr, asid, size, dummy); + mshr->allocIter = allocatedList.insert(allocatedList.end(), mshr); + mshr->inService = true; + ++inServiceMSHRs; + ++allocated; + return mshr; +} + + +void +MSHRQueue::deallocate(MSHR* mshr) +{ + deallocateOne(mshr); +} + +MSHR::Iterator +MSHRQueue::deallocateOne(MSHR* mshr) +{ + MSHR::Iterator retval = allocatedList.erase(mshr->allocIter); + freeList.push_front(mshr); + allocated--; + allocatedTargets -= mshr->getNumTargets(); + if (mshr->inService) { + inServiceMSHRs--; + } else { + pendingList.erase(mshr->readyIter); + } + mshr->deallocate(); + return retval; +} + +void +MSHRQueue::moveToFront(MSHR *mshr) +{ + if (!mshr->inService) { + assert(mshr == *(mshr->readyIter)); + pendingList.erase(mshr->readyIter); + mshr->readyIter = pendingList.insert(pendingList.begin(), mshr); + } +} + +void +MSHRQueue::markInService(MSHR* mshr) +{ + //assert(mshr == pendingList.front()); + if (!mshr->pkt->needsResponse()) { + assert(mshr->getNumTargets() == 0); + deallocate(mshr); + return; + } + mshr->inService = true; + pendingList.erase(mshr->readyIter); + mshr->readyIter = NULL; + inServiceMSHRs += 1; + //pendingList.pop_front(); +} + +void +MSHRQueue::markPending(MSHR* mshr, Packet::Command cmd) +{ + assert(mshr->readyIter == NULL); + mshr->pkt->cmd = cmd; + mshr->pkt->flags &= ~SATISFIED; + mshr->inService = false; + --inServiceMSHRs; + /** + * @ todo might want to add rerequests to front of pending list for + * performance. + */ + mshr->readyIter = pendingList.insert(pendingList.end(), mshr); +} + +void +MSHRQueue::squash(int threadNum) +{ + MSHR::Iterator i = allocatedList.begin(); + MSHR::Iterator end = allocatedList.end(); + for (; i != end;) { + MSHR *mshr = *i; + if (mshr->threadNum == threadNum) { + while (mshr->hasTargets()) { + Packet * target = mshr->getTarget(); + mshr->popTarget(); + + assert(target->req->getThreadNum() == threadNum); + target = NULL; + } + assert(!mshr->hasTargets()); + assert(mshr->ntargets==0); + if (!mshr->inService) { + i = deallocateOne(mshr); + } else { + //mshr->pkt->flags &= ~CACHE_LINE_FILL; + ++i; + } + } else { + ++i; + } + } +} diff --git a/src/mem/cache/miss/mshr_queue.hh b/src/mem/cache/miss/mshr_queue.hh new file mode 100644 index 000000000..a67f1b9a6 --- /dev/null +++ b/src/mem/cache/miss/mshr_queue.hh @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** @file + * Declaration of a structure to manage MSHRs. + */ + +#ifndef __MSHR_QUEUE_HH__ +#define __MSHR_QUEUE_HH__ + +#include <vector> +#include "mem/cache/miss/mshr.hh" + +/** + * A Class for maintaining a list of pending and allocated memory requests. + */ +class MSHRQueue { + private: + /** MSHR storage. */ + MSHR* registers; + /** Holds pointers to all allocated MSHRs. */ + MSHR::List allocatedList; + /** Holds pointers to MSHRs that haven't been sent to the bus. */ + MSHR::List pendingList; + /** Holds non allocated MSHRs. */ + MSHR::List freeList; + + // Parameters + /** + * The total number of MSHRs in this queue. This number is set as the + * number of MSHRs requested plus (numReserve - 1). This allows for + * the same number of effective MSHRs while still maintaining the reserve. + */ + const int numMSHRs; + + /** + * The number of MSHRs to hold in reserve. This is needed because copy + * operations can allocate upto 4 MSHRs at one time. + */ + const int numReserve; + + public: + /** The number of allocated MSHRs. */ + int allocated; + /** The number of MSHRs that have been forwarded to the bus. */ + int inServiceMSHRs; + /** The number of targets waiting for response. */ + int allocatedTargets; + + /** + * Create a queue with a given number of MSHRs. + * @param num_mshrs The number of MSHRs in this queue. + * @param reserve The minimum number of MSHRs needed to satisfy any access. + */ + MSHRQueue(int num_mshrs, int reserve = 1); + + /** Destructor */ + ~MSHRQueue(); + + /** + * Find the first MSHR that matches the provide address and asid. + * @param addr The address to find. + * @param asid The address space id. + * @return Pointer to the matching MSHR, null if not found. + */ + MSHR* findMatch(Addr addr, int asid) const; + + /** + * Find and return all the matching MSHRs in the provided vector. + * @param addr The address to find. + * @param asid The address space ID. + * @param matches The vector to return pointers to the matching MSHRs. + * @return True if any matches are found, false otherwise. + * @todo Typedef the vector?? + */ + bool findMatches(Addr addr, int asid, std::vector<MSHR*>& matches) const; + + /** + * Find any pending requests that overlap the given request. + * @param req The request to find. + * @return A pointer to the earliest matching MSHR. + */ + MSHR* findPending(Packet * &pkt) const; + + /** + * Allocates a new MSHR for the request and size. This places the request + * as the first target in the MSHR. + * @param req The request to handle. + * @param size The number in bytes to fetch from memory. + * @return The a pointer to the MSHR allocated. + * + * @pre There are free MSHRs. + */ + MSHR* allocate(Packet * &pkt, int size = 0); + + /** + * Allocate a read request for the given address, and places the given + * target on the target list. + * @param addr The address to fetch. + * @param asid The address space for the fetch. + * @param size The number of bytes to request. + * @param target The first target for the request. + * @return Pointer to the new MSHR. + */ + MSHR* allocateFetch(Addr addr, int asid, int size, Packet * &target); + + /** + * Allocate a target list for the given address. + * @param addr The address to fetch. + * @param asid The address space for the fetch. + * @param size The number of bytes to request. + * @return Pointer to the new MSHR. + */ + MSHR* allocateTargetList(Addr addr, int asid, int size); + + /** + * Removes the given MSHR from the queue. This places the MSHR on the + * free list. + * @param mshr + */ + void deallocate(MSHR* mshr); + + /** + * Allocates a target to the given MSHR. Used to keep track of the number + * of outstanding targets. + * @param mshr The MSHR to allocate the target to. + * @param req The target request. + */ + void allocateTarget(MSHR* mshr, Packet * &pkt) + { + mshr->allocateTarget(pkt); + allocatedTargets += 1; + } + + /** + * Remove a MSHR from the queue. Returns an iterator into the allocatedList + * for faster squash implementation. + * @param mshr The MSHR to remove. + * @return An iterator to the next entry in the allocatedList. + */ + MSHR::Iterator deallocateOne(MSHR* mshr); + + /** + * Moves the MSHR to the front of the pending list if it is not in service. + * @param mshr The mshr to move. + */ + void moveToFront(MSHR *mshr); + + /** + * Mark the given MSHR as in service. This removes the MSHR from the + * pendingList. Deallocates the MSHR if it does not expect a response. + * @param mshr The MSHR to mark in service. + */ + void markInService(MSHR* mshr); + + /** + * Mark an in service mshr as pending, used to resend a request. + * @param mshr The MSHR to resend. + * @param cmd The command to resend. + */ + void markPending(MSHR* mshr, Packet::Command cmd); + + /** + * Squash outstanding requests with the given thread number. If a request + * is in service, just squashes the targets. + * @param req->getThreadNum()ber The thread to squash. + */ + void squash(int threadNum); + + /** + * Returns true if the pending list is not empty. + * @return True if there are outstanding requests. + */ + bool havePending() const + { + return !pendingList.empty(); + } + + /** + * Returns true if there are no free MSHRs. + * @return True if this queue is full. + */ + bool isFull() const + { + return (allocated > numMSHRs - numReserve); + } + + /** + * Returns the request at the head of the pendingList. + * @return The next request to service. + */ + Packet * getReq() const + { + if (pendingList.empty()) { + return NULL; + } + MSHR* mshr = pendingList.front(); + return mshr->pkt; + } + + /** + * Returns the number of outstanding targets. + * @return the number of allocated targets. + */ + int getAllocatedTargets() const + { + return allocatedTargets; + } + +}; + +#endif //__MSHR_QUEUE_HH__ diff --git a/src/mem/cache/prefetch/base_prefetcher.cc b/src/mem/cache/prefetch/base_prefetcher.cc new file mode 100644 index 000000000..897551989 --- /dev/null +++ b/src/mem/cache/prefetch/base_prefetcher.cc @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Hardware Prefetcher Definition. + */ + +#include "base/trace.hh" +#include "mem/cache/base_cache.hh" +#include "mem/cache/prefetch/base_prefetcher.hh" +#include "mem/request.hh" +#include <list> + +BasePrefetcher::BasePrefetcher(int size, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData) + :size(size), pageStop(pageStop), serialSquash(serialSquash), + cacheCheckPush(cacheCheckPush), only_data(onlyData) +{ +} + +void +BasePrefetcher::setCache(BaseCache *_cache) +{ + cache = _cache; + blkSize = cache->getBlockSize(); +} + +void +BasePrefetcher::regStats(const std::string &name) +{ + pfIdentified + .name(name + ".prefetcher.num_hwpf_identified") + .desc("number of hwpf identified") + ; + + pfMSHRHit + .name(name + ".prefetcher.num_hwpf_already_in_mshr") + .desc("number of hwpf that were already in mshr") + ; + + pfCacheHit + .name(name + ".prefetcher.num_hwpf_already_in_cache") + .desc("number of hwpf that were already in the cache") + ; + + pfBufferHit + .name(name + ".prefetcher.num_hwpf_already_in_prefetcher") + .desc("number of hwpf that were already in the prefetch queue") + ; + + pfRemovedFull + .name(name + ".prefetcher.num_hwpf_evicted") + .desc("number of hwpf removed due to no buffer left") + ; + + pfRemovedMSHR + .name(name + ".prefetcher.num_hwpf_removed_MSHR_hit") + .desc("number of hwpf removed because MSHR allocated") + ; + + pfIssued + .name(name + ".prefetcher.num_hwpf_issued") + .desc("number of hwpf issued") + ; + + pfSpanPage + .name(name + ".prefetcher.num_hwpf_span_page") + .desc("number of hwpf spanning a virtual page") + ; + + pfSquashed + .name(name + ".prefetcher.num_hwpf_squashed_from_miss") + .desc("number of hwpf that got squashed due to a miss aborting calculation time") + ; +} + +Packet * +BasePrefetcher::getPacket() +{ + DPRINTF(HWPrefetch, "%s:Requesting a hw_pf to issue\n", cache->name()); + + if (pf.empty()) { + DPRINTF(HWPrefetch, "%s:No HW_PF found\n", cache->name()); + return NULL; + } + + Packet * pkt; + bool keepTrying = false; + do { + pkt = *pf.begin(); + pf.pop_front(); + if (!cacheCheckPush) { + keepTrying = inCache(pkt); + } + if (pf.empty()) { + cache->clearMasterRequest(Request_PF); + if (keepTrying) return NULL; //None left, all were in cache + } + } while (keepTrying); + + pfIssued++; + return pkt; +} + +void +BasePrefetcher::handleMiss(Packet * &pkt, Tick time) +{ + if (!pkt->req->isUncacheable() && !(pkt->req->isInstRead() && only_data)) + { + //Calculate the blk address + Addr blkAddr = pkt->getAddr() & ~(Addr)(blkSize-1); + + //Check if miss is in pfq, if so remove it + std::list<Packet *>::iterator iter = inPrefetch(blkAddr); + if (iter != pf.end()) { + DPRINTF(HWPrefetch, "%s:Saw a miss to a queued prefetch, removing it\n", cache->name()); + pfRemovedMSHR++; + pf.erase(iter); + if (pf.empty()) + cache->clearMasterRequest(Request_PF); + } + + //Remove anything in queue with delay older than time + //since everything is inserted in time order, start from end + //and work until pf.empty() or time is earlier + //This is done to emulate Aborting the previous work on a new miss + //Needed for serial calculators like GHB + if (serialSquash) { + iter = pf.end(); + iter--; + while (!pf.empty() && ((*iter)->time >= time)) { + pfSquashed++; + pf.pop_back(); + iter--; + } + if (pf.empty()) + cache->clearMasterRequest(Request_PF); + } + + + std::list<Addr> addresses; + std::list<Tick> delays; + calculatePrefetch(pkt, addresses, delays); + + std::list<Addr>::iterator addr = addresses.begin(); + std::list<Tick>::iterator delay = delays.begin(); + while (addr != addresses.end()) + { + DPRINTF(HWPrefetch, "%s:Found a pf canidate, inserting into prefetch queue\n", cache->name()); + //temp calc this here... + pfIdentified++; + //create a prefetch memreq + Request * prefetchReq = new Request(*addr, blkSize, 0); + Packet * prefetch; + prefetch = new Packet(prefetchReq, Packet::HardPFReq, -1); + prefetch->allocate(); + prefetch->req->setThreadContext(pkt->req->getCpuNum(), + pkt->req->getThreadNum()); + + prefetch->time = time + (*delay); //@todo ADD LATENCY HERE + //... initialize + + //Check if it is already in the cache + if (cacheCheckPush) { + if (inCache(prefetch)) { + addr++; + delay++; + continue; + } + } + + //Check if it is already in the miss_queue + if (inMissQueue(prefetch->getAddr(), prefetch->req->getAsid())) { + addr++; + delay++; + continue; + } + + //Check if it is already in the pf buffer + if (inPrefetch(prefetch->getAddr()) != pf.end()) { + pfBufferHit++; + addr++; + delay++; + continue; + } + + //We just remove the head if we are full + if (pf.size() == size) + { + DPRINTF(HWPrefetch, "%s:Inserting into prefetch queue, it was full removing oldest\n", cache->name()); + pfRemovedFull++; + pf.pop_front(); + } + + pf.push_back(prefetch); + prefetch->flags |= CACHE_LINE_FILL; + + //Make sure to request the bus, with proper delay + cache->setMasterRequest(Request_PF, prefetch->time); + + //Increment through the list + addr++; + delay++; + } + } +} + +std::list<Packet *>::iterator +BasePrefetcher::inPrefetch(Addr address) +{ + //Guaranteed to only be one match, we always check before inserting + std::list<Packet *>::iterator iter; + for (iter=pf.begin(); iter != pf.end(); iter++) { + if (((*iter)->getAddr() & ~(Addr)(blkSize-1)) == address) { + return iter; + } + } + return pf.end(); +} + + diff --git a/src/mem/cache/prefetch/base_prefetcher.hh b/src/mem/cache/prefetch/base_prefetcher.hh new file mode 100644 index 000000000..3e4fc89d1 --- /dev/null +++ b/src/mem/cache/prefetch/base_prefetcher.hh @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Miss and writeback queue declarations. + */ + +#ifndef __MEM_CACHE_PREFETCH_BASE_PREFETCHER_HH__ +#define __MEM_CACHE_PREFETCH_BASE_PREFETCHER_HH__ + +#include "mem/packet.hh" +#include <list> + +class BaseCache; +class BasePrefetcher +{ + protected: + + /** The Prefetch Queue. */ + std::list<Packet *> pf; + + // PARAMETERS + + /** The number of MSHRs in the Prefetch Queue. */ + const int size; + + /** Pointr to the parent cache. */ + BaseCache* cache; + + /** The block size of the parent cache. */ + int blkSize; + + /** Do we prefetch across page boundaries. */ + bool pageStop; + + /** Do we remove prefetches with later times than a new miss.*/ + bool serialSquash; + + /** Do we check if it is in the cache when inserting into buffer, + or removing.*/ + bool cacheCheckPush; + + /** Do we prefetch on only data reads, or on inst reads as well. */ + bool only_data; + + public: + + Stats::Scalar<> pfIdentified; + Stats::Scalar<> pfMSHRHit; + Stats::Scalar<> pfCacheHit; + Stats::Scalar<> pfBufferHit; + Stats::Scalar<> pfRemovedFull; + Stats::Scalar<> pfRemovedMSHR; + Stats::Scalar<> pfIssued; + Stats::Scalar<> pfSpanPage; + Stats::Scalar<> pfSquashed; + + void regStats(const std::string &name); + + public: + BasePrefetcher(int numMSHRS, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData); + + virtual ~BasePrefetcher() {} + + void setCache(BaseCache *_cache); + + void handleMiss(Packet * &pkt, Tick time); + + Packet * getPacket(); + + bool havePending() + { + return !pf.empty(); + } + + virtual void calculatePrefetch(Packet * &pkt, + std::list<Addr> &addresses, + std::list<Tick> &delays) = 0; + + virtual bool inCache(Packet * &pkt) = 0; + + virtual bool inMissQueue(Addr address, int asid) = 0; + + std::list<Packet *>::iterator inPrefetch(Addr address); +}; + + +#endif //__MEM_CACHE_PREFETCH_BASE_PREFETCHER_HH__ diff --git a/src/mem/cache/prefetch/ghb_prefetcher.cc b/src/mem/cache/prefetch/ghb_prefetcher.cc new file mode 100644 index 000000000..247ec6e8b --- /dev/null +++ b/src/mem/cache/prefetch/ghb_prefetcher.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + */ + +/** + * @file + * GHB Prefetcher template instantiations. + */ + +#include "mem/cache/tags/cache_tags.hh" + +#include "mem/cache/tags/lru.hh" + +#include "base/compression/null_compression.hh" + +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/miss/blocking_buffer.hh" + +#include "mem/cache/prefetch/ghb_prefetcher.hh" + +// Template Instantiations +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template class GHBPrefetcher<CacheTags<LRU,NullCompression>, MissQueue>; +template class GHBPrefetcher<CacheTags<LRU,NullCompression>, BlockingBuffer>; + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/prefetch/ghb_prefetcher.hh b/src/mem/cache/prefetch/ghb_prefetcher.hh new file mode 100644 index 000000000..c22b763d1 --- /dev/null +++ b/src/mem/cache/prefetch/ghb_prefetcher.hh @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Describes a ghb prefetcher based on template policies. + */ + +#ifndef __MEM_CACHE_PREFETCH_GHB_PREFETCHER_HH__ +#define __MEM_CACHE_PREFETCH_GHB_PREFETCHER_HH__ + +#include "base/misc.hh" // fatal, panic, and warn + +#include "mem/cache/prefetch/prefetcher.hh" + +/** + * A template-policy based cache. The behavior of the cache can be altered by + * supplying different template policies. TagStore handles all tag and data + * storage @sa TagStore. Buffering handles all misses and writes/writebacks + * @sa MissQueue. Coherence handles all coherence policy details @sa + * UniCoherence, SimpleMultiCoherence. + */ +template <class TagStore, class Buffering> +class GHBPrefetcher : public Prefetcher<TagStore, Buffering> +{ + protected: + + Buffering* mq; + TagStore* tags; + + Addr second_last_miss_addr[64/*MAX_CPUS*/]; + Addr last_miss_addr[64/*MAX_CPUS*/]; + + Tick latency; + int degree; + bool useCPUId; + + public: + + GHBPrefetcher(int size, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData, + Tick latency, int degree, bool useCPUId) + :Prefetcher<TagStore, Buffering>(size, pageStop, serialSquash, + cacheCheckPush, onlyData), + latency(latency), degree(degree), useCPUId(useCPUId) + { + } + + ~GHBPrefetcher() {} + + void calculatePrefetch(Packet * &pkt, std::list<Addr> &addresses, + std::list<Tick> &delays) + { + Addr blkAddr = pkt->getAddr() & ~(Addr)(this->blkSize-1); + int cpuID = pkt->req->getCpuNum(); + if (!useCPUId) cpuID = 0; + + + int new_stride = blkAddr - last_miss_addr[cpuID]; + int old_stride = last_miss_addr[cpuID] - + second_last_miss_addr[cpuID]; + + second_last_miss_addr[cpuID] = last_miss_addr[cpuID]; + last_miss_addr[cpuID] = blkAddr; + + if (new_stride == old_stride) { + for (int d=1; d <= degree; d++) { + Addr newAddr = blkAddr + d * new_stride; + if (this->pageStop && + (blkAddr & ~(TheISA::VMPageSize - 1)) != + (newAddr & ~(TheISA::VMPageSize - 1))) + { + //Spanned the page, so now stop + this->pfSpanPage += degree - d + 1; + return; + } + else + { + addresses.push_back(newAddr); + delays.push_back(latency); + } + } + } + } +}; + +#endif // __MEM_CACHE_PREFETCH_GHB_PREFETCHER_HH__ diff --git a/src/mem/cache/prefetch/stride_prefetcher.cc b/src/mem/cache/prefetch/stride_prefetcher.cc new file mode 100644 index 000000000..93a096468 --- /dev/null +++ b/src/mem/cache/prefetch/stride_prefetcher.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + */ + +/** + * @file + * Stride Prefetcher template instantiations. + */ + +#include "mem/cache/tags/cache_tags.hh" + +#include "mem/cache/tags/lru.hh" + +#include "base/compression/null_compression.hh" + +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/miss/blocking_buffer.hh" + +#include "mem/cache/prefetch/stride_prefetcher.hh" + +// Template Instantiations +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template class StridePrefetcher<CacheTags<LRU,NullCompression>, MissQueue>; +template class StridePrefetcher<CacheTags<LRU,NullCompression>, BlockingBuffer>; + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/prefetch/stride_prefetcher.hh b/src/mem/cache/prefetch/stride_prefetcher.hh new file mode 100644 index 000000000..4a8ee7de4 --- /dev/null +++ b/src/mem/cache/prefetch/stride_prefetcher.hh @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Describes a strided prefetcher based on template policies. + */ + +#ifndef __MEM_CACHE_PREFETCH_STRIDE_PREFETCHER_HH__ +#define __MEM_CACHE_PREFETCH_STRIDE_PREFETCHER_HH__ + +#include "base/misc.hh" // fatal, panic, and warn + +#include "mem/cache/prefetch/prefetcher.hh" + +/** + * A template-policy based cache. The behavior of the cache can be altered by + * supplying different template policies. TagStore handles all tag and data + * storage @sa TagStore. Buffering handles all misses and writes/writebacks + * @sa MissQueue. Coherence handles all coherence policy details @sa + * UniCoherence, SimpleMultiCoherence. + */ +template <class TagStore, class Buffering> +class StridePrefetcher : public Prefetcher<TagStore, Buffering> +{ + protected: + + Buffering* mq; + TagStore* tags; + + class strideEntry + { + public: + Addr IAddr; + Addr MAddr; + int stride; + int64_t confidence; + +/* bool operator < (strideEntry a,strideEntry b) + { + if (a.confidence == b.confidence) { + return true; //?????? + } + else return a.confidence < b.confidence; + }*/ + }; + Addr* lastMissAddr[64/*MAX_CPUS*/]; + + std::list<strideEntry*> table[64/*MAX_CPUS*/]; + Tick latency; + int degree; + bool useCPUId; + + + public: + + StridePrefetcher(int size, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData, + Tick latency, int degree, bool useCPUId) + :Prefetcher<TagStore, Buffering>(size, pageStop, serialSquash, + cacheCheckPush, onlyData), + latency(latency), degree(degree), useCPUId(useCPUId) + { + } + + ~StridePrefetcher() {} + + void calculatePrefetch(Packet * &pkt, std::list<Addr> &addresses, + std::list<Tick> &delays) + { +// Addr blkAddr = pkt->paddr & ~(Addr)(this->blkSize-1); + int cpuID = pkt->req->getCpuNum(); + if (!useCPUId) cpuID = 0; + + /* Scan Table for IAddr Match */ +/* std::list<strideEntry*>::iterator iter; + for (iter=table[cpuID].begin(); + iter !=table[cpuID].end(); + iter++) { + if ((*iter)->IAddr == pkt->pc) break; + } + + if (iter != table[cpuID].end()) { + //Hit in table + + int newStride = blkAddr - (*iter)->MAddr; + if (newStride == (*iter)->stride) { + (*iter)->confidence++; + } + else { + (*iter)->stride = newStride; + (*iter)->confidence--; + } + + (*iter)->MAddr = blkAddr; + + for (int d=1; d <= degree; d++) { + Addr newAddr = blkAddr + d * newStride; + if (this->pageStop && + (blkAddr & ~(TheISA::VMPageSize - 1)) != + (newAddr & ~(TheISA::VMPageSize - 1))) + { + //Spanned the page, so now stop + this->pfSpanPage += degree - d + 1; + return; + } + else + { + addresses.push_back(newAddr); + delays.push_back(latency); + } + } + } + else { + //Miss in table + //Find lowest confidence and replace + + } +*/ } +}; + +#endif // __MEM_CACHE_PREFETCH_STRIDE_PREFETCHER_HH__ diff --git a/src/mem/cache/prefetch/tagged_prefetcher.hh b/src/mem/cache/prefetch/tagged_prefetcher.hh new file mode 100644 index 000000000..17f500dd8 --- /dev/null +++ b/src/mem/cache/prefetch/tagged_prefetcher.hh @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Describes a tagged prefetcher based on template policies. + */ + +#ifndef __MEM_CACHE_PREFETCH_TAGGED_PREFETCHER_HH__ +#define __MEM_CACHE_PREFETCH_TAGGED_PREFETCHER_HH__ + +#include "mem/cache/prefetch/prefetcher.hh" + +/** + * A template-policy based cache. The behavior of the cache can be altered by + * supplying different template policies. TagStore handles all tag and data + * storage @sa TagStore. Buffering handles all misses and writes/writebacks + * @sa MissQueue. Coherence handles all coherence policy details @sa + * UniCoherence, SimpleMultiCoherence. + */ +template <class TagStore, class Buffering> +class TaggedPrefetcher : public Prefetcher<TagStore, Buffering> +{ + protected: + + Buffering* mq; + TagStore* tags; + + Tick latency; + int degree; + + public: + + TaggedPrefetcher(int size, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData, + Tick latency, int degree); + + ~TaggedPrefetcher() {} + + void calculatePrefetch(Packet * &pkt, std::list<Addr> &addresses, + std::list<Tick> &delays); +}; + +#endif // __MEM_CACHE_PREFETCH_TAGGED_PREFETCHER_HH__ diff --git a/src/mem/cache/prefetch/tagged_prefetcher_impl.hh b/src/mem/cache/prefetch/tagged_prefetcher_impl.hh index 6c27256a9..db5c94820 100644 --- a/src/mem/cache/prefetch/tagged_prefetcher_impl.hh +++ b/src/mem/cache/prefetch/tagged_prefetcher_impl.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ron Dreslinski */ /** @@ -47,10 +49,10 @@ TaggedPrefetcher(int size, bool pageStop, bool serialSquash, template <class TagStore, class Buffering> void TaggedPrefetcher<TagStore, Buffering>:: -calculatePrefetch(MemReqPtr &req, std::list<Addr> &addresses, +calculatePrefetch(Packet * &pkt, std::list<Addr> &addresses, std::list<Tick> &delays) { - Addr blkAddr = req->paddr & ~(Addr)(this->blkSize-1); + Addr blkAddr = pkt->getAddr() & ~(Addr)(this->blkSize-1); for (int d=1; d <= degree; d++) { Addr newAddr = blkAddr + d*(this->blkSize); diff --git a/src/mem/cache/tags/base_tags.cc b/src/mem/cache/tags/base_tags.cc new file mode 100644 index 000000000..153737300 --- /dev/null +++ b/src/mem/cache/tags/base_tags.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Ron Dreslinski + */ + +/** + * @file + * Definitions of BaseTags. + */ + +#include "mem/cache/tags/base_tags.hh" + +#include "mem/cache/base_cache.hh" +#include "cpu/smt.hh" //maxThreadsPerCPU +#include "sim/sim_exit.hh" + +using namespace std; + +void +BaseTags::setCache(BaseCache *_cache) +{ + cache = _cache; + objName = cache->name(); +} + +void +BaseTags::regStats(const string &name) +{ + using namespace Stats; + replacements + .init(maxThreadsPerCPU) + .name(name + ".replacements") + .desc("number of replacements") + .flags(total) + ; + + tagsInUse + .name(name + ".tagsinuse") + .desc("Cycle average of tags in use") + ; + + totalRefs + .name(name + ".total_refs") + .desc("Total number of references to valid blocks.") + ; + + sampledRefs + .name(name + ".sampled_refs") + .desc("Sample count of references to valid blocks.") + ; + + avgRefs + .name(name + ".avg_refs") + .desc("Average number of references to valid blocks.") + ; + + avgRefs = totalRefs/sampledRefs; + + warmupCycle + .name(name + ".warmup_cycle") + .desc("Cycle when the warmup percentage was hit.") + ; + + registerExitCallback(new BaseTagsCallback(this)); +} diff --git a/src/mem/cache/tags/base_tags.hh b/src/mem/cache/tags/base_tags.hh new file mode 100644 index 000000000..b7b0c7ef0 --- /dev/null +++ b/src/mem/cache/tags/base_tags.hh @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Ron Dreslinski + */ + +/** + * @file + * Declaration of a common base class for cache tagstore objects. + */ + +#ifndef __BASE_TAGS_HH__ +#define __BASE_TAGS_HH__ + +#include <string> +#include "base/statistics.hh" +#include "base/callback.hh" + +class BaseCache; + +/** + * A common base class of Cache tagstore objects. + */ +class BaseTags +{ + protected: + /** Pointer to the parent cache. */ + BaseCache *cache; + + /** Local copy of the parent cache name. Used for DPRINTF. */ + std::string objName; + + /** + * The number of tags that need to be touched to meet the warmup + * percentage. + */ + int warmupBound; + /** Marked true when the cache is warmed up. */ + bool warmedUp; + + // Statistics + /** + * @addtogroup CacheStatistics + * @{ + */ + + /** Number of replacements of valid blocks per thread. */ + Stats::Vector<> replacements; + /** Per cycle average of the number of tags that hold valid data. */ + Stats::Average<> tagsInUse; + + /** The total number of references to a block before it is replaced. */ + Stats::Scalar<> totalRefs; + + /** + * The number of reference counts sampled. This is different from + * replacements because we sample all the valid blocks when the simulator + * exits. + */ + Stats::Scalar<> sampledRefs; + + /** + * Average number of references to a block before is was replaced. + * @todo This should change to an average stat once we have them. + */ + Stats::Formula avgRefs; + + /** The cycle that the warmup percentage was hit. */ + Stats::Scalar<> warmupCycle; + /** + * @} + */ + + public: + + /** + * Destructor. + */ + virtual ~BaseTags() {} + + /** + * Set the parent cache back pointer. Also copies the cache name to + * objName. + * @param _cache Pointer to parent cache. + */ + void setCache(BaseCache *_cache); + + /** + * Return the parent cache name. + * @return the parent cache name. + */ + const std::string &name() const + { + return objName; + } + + /** + * Register local statistics. + * @param name The name to preceed each statistic name. + */ + void regStats(const std::string &name); + + /** + * Average in the reference count for valid blocks when the simulation + * exits. + */ + virtual void cleanupRefs() {} +}; + +class BaseTagsCallback : public Callback +{ + BaseTags *tags; + public: + BaseTagsCallback(BaseTags *t) : tags(t) {} + virtual void process() { tags->cleanupRefs(); }; +}; + +#endif //__BASE_TAGS_HH__ diff --git a/src/mem/cache/tags/fa_lru.cc b/src/mem/cache/tags/fa_lru.cc new file mode 100644 index 000000000..82d2c410d --- /dev/null +++ b/src/mem/cache/tags/fa_lru.cc @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definitions a fully associative LRU tagstore. + */ + +#include <sstream> + +#include <assert.h> + +#include "mem/cache/tags/fa_lru.hh" +#include "base/intmath.hh" +#include "base/misc.hh" + +using namespace std; + +FALRU::FALRU(int _blkSize, int _size, int hit_latency) + : blkSize(_blkSize), size(_size), + numBlks(size/blkSize), hitLatency(hit_latency) +{ + if (!isPowerOf2(blkSize)) + fatal("cache block size (in bytes) `%d' must be a power of two", + blkSize); + if (!(hitLatency > 0)) + fatal("Access latency in cycles must be at least one cycle"); + if (!isPowerOf2(size)) + fatal("Cache Size must be power of 2 for now"); + + // Track all cache sizes from 128K up by powers of 2 + numCaches = floorLog2(size) - 17; + if (numCaches >0){ + cacheBoundaries = new FALRUBlk *[numCaches]; + cacheMask = (1 << numCaches) - 1; + } else { + cacheMask = 0; + } + + warmedUp = false; + warmupBound = size/blkSize; + + blks = new FALRUBlk[numBlks]; + head = &(blks[0]); + tail = &(blks[numBlks-1]); + + head->prev = NULL; + head->next = &(blks[1]); + head->inCache = cacheMask; + + tail->prev = &(blks[numBlks-2]); + tail->next = NULL; + tail->inCache = 0; + + int index = (1 << 17) / blkSize; + int j = 0; + int flags = cacheMask; + for (int i = 1; i < numBlks-1; i++) { + blks[i].inCache = flags; + if (i == index - 1){ + cacheBoundaries[j] = &(blks[i]); + flags &= ~ (1<<j); + ++j; + index = index << 1; + } + blks[i].prev = &(blks[i-1]); + blks[i].next = &(blks[i+1]); + blks[i].isTouched = false; + } + assert(j == numCaches); + assert(index == numBlks); + //assert(check()); +} + +void +FALRU::regStats(const string &name) +{ + using namespace Stats; + BaseTags::regStats(name); + hits + .init(numCaches+1) + .name(name + ".falru_hits") + .desc("The number of hits in each cache size.") + ; + misses + .init(numCaches+1) + .name(name + ".falru_misses") + .desc("The number of misses in each cache size.") + ; + accesses + .name(name + ".falru_accesses") + .desc("The number of accesses to the FA LRU cache.") + ; + + for (int i = 0; i < numCaches+1; ++i) { + stringstream size_str; + if (i < 3){ + size_str << (1<<(i+7)) <<"K"; + } else { + size_str << (1<<(i-3)) <<"M"; + } + + hits.subname(i, size_str.str()); + hits.subdesc(i, "Hits in a " + size_str.str() +" cache"); + misses.subname(i, size_str.str()); + misses.subdesc(i, "Misses in a " + size_str.str() +" cache"); + } +} + +FALRUBlk * +FALRU::hashLookup(Addr addr) const +{ + tagIterator iter = tagHash.find(addr); + if (iter != tagHash.end()) { + return (*iter).second; + } + return NULL; +} + +bool +FALRU::probe(int asid, Addr addr) const +{ + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = hashLookup(blkAddr); + return blk && blk->tag == blkAddr && blk->isValid(); +} + +void +FALRU::invalidateBlk(int asid, Addr addr) +{ + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = (*tagHash.find(blkAddr)).second; + if (blk) { + assert(blk->tag == blkAddr); + blk->status = 0; + blk->isTouched = false; + tagsInUse--; + } +} + +FALRUBlk* +FALRU::findBlock(Addr addr, int asid, int &lat, int *inCache) +{ + accesses++; + int tmp_in_cache = 0; + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = hashLookup(blkAddr); + + if (blk && blk->isValid()) { + assert(blk->tag == blkAddr); + tmp_in_cache = blk->inCache; + for (int i = 0; i < numCaches; i++) { + if (1<<i & blk->inCache) { + hits[i]++; + } else { + misses[i]++; + } + } + hits[numCaches]++; + if (blk != head){ + moveToHead(blk); + } + } else { + blk = NULL; + for (int i = 0; i < numCaches+1; ++i) { + misses[i]++; + } + } + if (inCache) { + *inCache = tmp_in_cache; + } + + lat = hitLatency; + //assert(check()); + return blk; +} + +FALRUBlk* +FALRU::findBlock(Packet * &pkt, int &lat, int *inCache) +{ + Addr addr = pkt->getAddr(); + + accesses++; + int tmp_in_cache = 0; + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = hashLookup(blkAddr); + + if (blk && blk->isValid()) { + assert(blk->tag == blkAddr); + tmp_in_cache = blk->inCache; + for (int i = 0; i < numCaches; i++) { + if (1<<i & blk->inCache) { + hits[i]++; + } else { + misses[i]++; + } + } + hits[numCaches]++; + if (blk != head){ + moveToHead(blk); + } + } else { + blk = NULL; + for (int i = 0; i < numCaches+1; ++i) { + misses[i]++; + } + } + if (inCache) { + *inCache = tmp_in_cache; + } + + lat = hitLatency; + //assert(check()); + return blk; +} + +FALRUBlk* +FALRU::findBlock(Addr addr, int asid) const +{ + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = hashLookup(blkAddr); + + if (blk && blk->isValid()) { + assert(blk->tag == blkAddr); + } else { + blk = NULL; + } + return blk; +} + +FALRUBlk* +FALRU::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + FALRUBlk * blk = tail; + assert(blk->inCache == 0); + moveToHead(blk); + tagHash.erase(blk->tag); + tagHash[blkAlign(pkt->getAddr())] = blk; + if (blk->isValid()) { + replacements[0]++; + } else { + tagsInUse++; + blk->isTouched = true; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + } + //assert(check()); + return blk; +} + +void +FALRU::moveToHead(FALRUBlk *blk) +{ + int updateMask = blk->inCache ^ cacheMask; + for (int i = 0; i < numCaches; i++){ + if ((1<<i) & updateMask) { + cacheBoundaries[i]->inCache &= ~(1<<i); + cacheBoundaries[i] = cacheBoundaries[i]->prev; + } else if (cacheBoundaries[i] == blk) { + cacheBoundaries[i] = blk->prev; + } + } + blk->inCache = cacheMask; + if (blk != head) { + if (blk == tail){ + assert(blk->next == NULL); + tail = blk->prev; + tail->next = NULL; + } else { + blk->prev->next = blk->next; + blk->next->prev = blk->prev; + } + blk->next = head; + blk->prev = NULL; + head->prev = blk; + head = blk; + } +} + +bool +FALRU::check() +{ + FALRUBlk* blk = head; + int size = 0; + int boundary = 1<<17; + int j = 0; + int flags = cacheMask; + while (blk) { + size += blkSize; + if (blk->inCache != flags) { + return false; + } + if (size == boundary && blk != tail) { + if (cacheBoundaries[j] != blk) { + return false; + } + flags &=~(1 << j); + boundary = boundary<<1; + ++j; + } + blk = blk->next; + } + return true; +} diff --git a/src/mem/cache/tags/fa_lru.hh b/src/mem/cache/tags/fa_lru.hh new file mode 100644 index 000000000..566e36c27 --- /dev/null +++ b/src/mem/cache/tags/fa_lru.hh @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a fully associative LRU tag store. + */ + +#ifndef __FA_LRU_HH__ +#define __FA_LRU_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" +#include "mem/packet.hh" +#include "base/hashmap.hh" +#include "mem/cache/tags/base_tags.hh" + +/** + * A fully associative cache block. + */ +class FALRUBlk : public CacheBlk +{ +public: + /** The previous block in LRU order. */ + FALRUBlk *prev; + /** The next block in LRU order. */ + FALRUBlk *next; + /** Has this block been touched? */ + bool isTouched; + + /** + * A bit mask of the sizes of cache that this block is resident in. + * Each bit represents a power of 2 in MB size cache. + * If bit 0 is set, this block is in a 1MB cache + * If bit 2 is set, this block is in a 4MB cache, etc. + * There is one bit for each cache smaller than the full size (default + * 16MB). + */ + int inCache; +}; + +/** + * A fully associative LRU cache. Keeps statistics for accesses to a number of + * cache sizes at once. + */ +class FALRU : public BaseTags +{ + public: + /** Typedef the block type used in this class. */ + typedef FALRUBlk BlkType; + /** Typedef a list of pointers to the local block type. */ + typedef std::list<FALRUBlk*> BlkList; + protected: + /** The block size of the cache. */ + const int blkSize; + /** The size of the cache. */ + const int size; + /** The number of blocks in the cache. */ + const int numBlks; // calculated internally + /** The hit latency of the cache. */ + const int hitLatency; + + /** Array of pointers to blocks at the cache size boundaries. */ + FALRUBlk **cacheBoundaries; + /** A mask for the FALRUBlk::inCache bits. */ + int cacheMask; + /** The number of different size caches being tracked. */ + int numCaches; + + /** The cache blocks. */ + FALRUBlk *blks; + + /** The MRU block. */ + FALRUBlk *head; + /** The LRU block. */ + FALRUBlk *tail; + + /** Hash table type mapping addresses to cache block pointers. */ + typedef m5::hash_map<Addr, FALRUBlk *, m5::hash<Addr> > hash_t; + /** Iterator into the address hash table. */ + typedef hash_t::const_iterator tagIterator; + + /** The address hash table. */ + hash_t tagHash; + + /** + * Find the cache block for the given address. + * @param addr The address to find. + * @return The cache block of the address, if any. + */ + FALRUBlk * hashLookup(Addr addr) const; + + /** + * Move a cache block to the MRU position. + * @param blk The block to promote. + */ + void moveToHead(FALRUBlk *blk); + + /** + * Check to make sure all the cache boundaries are still where they should + * be. Used for debugging. + * @return True if everything is correct. + */ + bool check(); + + /** + * @defgroup FALRUStats Fully Associative LRU specific statistics + * The FA lru stack lets us track multiple cache sizes at once. These + * statistics track the hits and misses for different cache sizes. + * @{ + */ + + /** Hits in each cache size >= 128K. */ + Stats::Vector<> hits; + /** Misses in each cache size >= 128K. */ + Stats::Vector<> misses; + /** Total number of accesses. */ + Stats::Scalar<> accesses; + + /** + * @} + */ + +public: + /** + * Construct and initialize this cache tagstore. + * @param blkSize The block size of the cache. + * @param size The size of the cache. + * @param hit_latency The hit latency of the cache. + */ + FALRU(int blkSize, int size, int hit_latency); + + /** + * Register the stats for this object. + * @param name The name to prepend to the stats name. + */ + void regStats(const std::string &name); + + /** + * Return true if the address is found in the cache. + * @param asid The address space ID. + * @param addr The address to look for. + * @return True if the address is in the cache. + */ + bool probe(int asid, Addr addr) const; + + /** + * Invalidate the cache block that contains the given addr. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(int asid, Addr addr); + + /** + * Find the block in the cache and update the replacement data. Returns + * the access latency and the in cache flags as a side effect + * @param addr The address to look for. + * @param asid The address space ID. + * @param lat The latency of the access. + * @param inCache The FALRUBlk::inCache flags. + * @return Pointer to the cache block. + */ + FALRUBlk* findBlock(Addr addr, int asid, int &lat, int *inCache = 0); + + /** + * Find the block in the cache and update the replacement data. Returns + * the access latency and the in cache flags as a side effect + * @param req The req whose block to find + * @param lat The latency of the access. + * @param inCache The FALRUBlk::inCache flags. + * @return Pointer to the cache block. + */ + FALRUBlk* findBlock(Packet * &pkt, int &lat, int *inCache = 0); + + /** + * Find the block in the cache, do not update the replacement data. + * @param addr The address to look for. + * @param asid The address space ID. + * @return Pointer to the cache block. + */ + FALRUBlk* findBlock(Addr addr, int asid) const; + + /** + * Find a replacement block for the address provided. + * @param req The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + FALRUBlk* findReplacement(Packet * &pkt, PacketList & writebacks, + BlkList &compress_blocks); + + /** + * Return the hit latency of this cache. + * @return The hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Return the block size of this cache. + * @return The block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size of this cache, always the block size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The aligned address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)(blkSize-1)); + } + + /** + * Generate the tag from the addres. For fully associative this is just the + * block address. + * @param addr The address to get the tag from. + * @param blk ignored here + * @return The tag. + */ + Addr extractTag(Addr addr, FALRUBlk *blk) const + { + return blkAlign(addr); + } + + /** + * Return the set of an address. Only one set in a fully associative cache. + * @param addr The address to get the set from. + * @return 0. + */ + int extractSet(Addr addr) const + { + return 0; + } + + /** + * Calculate the block offset of an address. + * @param addr the address to get the offset of. + * @return the block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & (Addr)(blkSize-1)); + } + + /** + * Regenerate the block address from the tag and the set. + * @param tag The tag of the block. + * @param set The set the block belongs to. + * @return the block address. + */ + Addr regenerateBlkAddr(Addr tag, int set) const + { + return (tag); + } + + /** + * Read the data out of the internal storage of a cache block. FALRU + * currently doesn't support data storage. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The data from the cache block. + */ + void readData(FALRUBlk *blk, uint8_t *data) + { + } + + /** + * Write data into the internal storage of a cache block. FALRU + * currently doesn't support data storage. + * @param blk The cache block to be written. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(FALRUBlk *blk, uint8_t *data, int size, + PacketList &writebacks) + { + } + + /** + * Unimplemented. Perform a cache block copy from block aligned addresses. + * @param source The block aligned source address. + * @param dest The block aligned destination adddress. + * @param asid The address space ID. + * @param writebacks List for any generated writeback requests. + */ + void doCopy(Addr source, Addr dest, int asid, PacketList &writebacks) + { + } + + /** + * Unimplemented. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + +}; + +#endif diff --git a/src/mem/cache/tags/iic.cc b/src/mem/cache/tags/iic.cc new file mode 100644 index 000000000..847fabc88 --- /dev/null +++ b/src/mem/cache/tags/iic.cc @@ -0,0 +1,880 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definitions of the Indirect Index Cache tagstore. + */ + +#include <algorithm> +#include <string> +#include <vector> + +#include <math.h> + +#include "mem/cache/base_cache.hh" +#include "mem/cache/tags/iic.hh" +#include "base/intmath.hh" +#include "sim/root.hh" // for curTick + +#include "base/trace.hh" // for DPRINTF + + +using namespace std; + +/** Track the number of accesses to each cache set. */ +#define PROFILE_IIC 1 + +IIC::IIC(IIC::Params ¶ms) : + hashSets(params.numSets), blkSize(params.blkSize), assoc(params.assoc), + hitLatency(params.hitLatency), subSize(params.subblockSize), + numSub(blkSize/subSize), + trivialSize((floorLog2(params.size/subSize)*numSub)/8), + tagShift(floorLog2(blkSize)), blkMask(blkSize - 1), + subShift(floorLog2(subSize)), subMask(numSub - 1), + hashDelay(params.hashDelay), + numBlocks(params.size/subSize), + numTags(hashSets * assoc + params.size/blkSize -1), + numSecondary(params.size/blkSize), + tagNull(numTags), + primaryBound(hashSets * assoc) +{ + int i; + + // Check parameters + if (blkSize < 4 || !isPowerOf2(blkSize)) { + fatal("Block size must be at least 4 and a power of 2"); + } + if (hashSets <= 0 || !isPowerOf2(hashSets)) { + fatal("# of hashsets must be non-zero and a power of 2"); + } + if (assoc <= 0) { + fatal("associativity must be greater than zero"); + } + if (hitLatency <= 0) { + fatal("access latency must be greater than zero"); + } + if (numSub*subSize != blkSize) { + fatal("blocksize must be evenly divisible by subblock size"); + } + + // debug stuff + freeSecond = numSecondary; + + warmedUp = false; + warmupBound = params.size/blkSize; + + // Replacement Policy Initialization + repl = params.rp; + repl->setIIC(this); + + //last_miss_time = 0 + + // allocate data reference counters + dataReferenceCount = new int[numBlocks]; + memset(dataReferenceCount, 0, numBlocks*sizeof(int)); + + // Allocate storage for both internal data and block fast access data. + // We allocate it as one large chunk to reduce overhead and to make + // deletion easier. + int data_index = 0; + dataStore = new uint8_t[(numBlocks + numTags) * blkSize]; + dataBlks = new uint8_t*[numBlocks]; + for (i = 0; i < numBlocks; ++i) { + dataBlks[i] = &dataStore[data_index]; + freeDataBlock(i); + data_index += subSize; + } + + assert(data_index == numBlocks * subSize); + + // allocate and init tag store + tagStore = new IICTag[numTags]; + + int blkIndex = 0; + // allocate and init sets + sets = new IICSet[hashSets]; + for (i = 0; i < hashSets; ++i) { + sets[i].assoc = assoc; + sets[i].tags = new IICTag*[assoc]; + sets[i].chain_ptr = tagNull; + + for (int j = 0; j < assoc; ++j) { + IICTag *tag = &tagStore[blkIndex++]; + tag->chain_ptr = tagNull; + tag->data_ptr.resize(numSub); + tag->size = blkSize; + tag->trivialData = new uint8_t[trivialSize]; + tag->numData = 0; + sets[i].tags[j] = tag; + tag->set = i; + tag->data = &dataStore[data_index]; + data_index += blkSize; + } + } + + assert(blkIndex == primaryBound); + + for (i = primaryBound; i < tagNull; i++) { + tagStore[i].chain_ptr = i+1; + //setup data ptrs to subblocks + tagStore[i].data_ptr.resize(numSub); + tagStore[i].size = blkSize; + tagStore[i].trivialData = new uint8_t[trivialSize]; + tagStore[i].numData = 0; + tagStore[i].set = 0; + tagStore[i].data = &dataStore[data_index]; + data_index += blkSize; + } + freelist = primaryBound; +} + +IIC::~IIC() +{ + delete [] dataReferenceCount; + delete [] dataStore; + delete [] tagStore; + delete [] sets; +} + +/* register cache stats */ +void +IIC::regStats(const string &name) +{ + using namespace Stats; + + BaseTags::regStats(name); + + hitHashDepth.init(0, 20, 1); + missHashDepth.init(0, 20, 1); + setAccess.init(0, hashSets, 1); + + /** IIC Statistics */ + hitHashDepth + .name(name + ".hit_hash_depth_dist") + .desc("Dist. of Hash lookup depths") + .flags(pdf) + ; + + missHashDepth + .name(name + ".miss_hash_depth_dist") + .desc("Dist. of Hash lookup depths") + .flags(pdf) + ; + + repl->regStats(name); + + if (PROFILE_IIC) + setAccess + .name(name + ".set_access_dist") + .desc("Dist. of Accesses across sets") + .flags(pdf) + ; + + missDepthTotal + .name(name + ".miss_depth_total") + .desc("Total of miss depths") + ; + + hashMiss + .name(name + ".hash_miss") + .desc("Total of misses in hash table") + ; + + hitDepthTotal + .name(name + ".hit_depth_total") + .desc("Total of hit depths") + ; + + hashHit + .name(name + ".hash_hit") + .desc("Total of hites in hash table") + ; +} + +// probe cache for presence of given block. +bool +IIC::probe(int asid, Addr addr) const +{ + return (findBlock(addr,asid) != NULL); +} + +IICTag* +IIC::findBlock(Addr addr, int asid, int &lat) +{ + Addr tag = extractTag(addr); + unsigned set = hash(addr); + int set_lat; + + unsigned long chain_ptr; + + if (PROFILE_IIC) + setAccess.sample(set); + + IICTag *tag_ptr = sets[set].findTag(asid, tag, chain_ptr); + set_lat = 1; + if (tag_ptr == NULL && chain_ptr != tagNull) { + int secondary_depth; + tag_ptr = secondaryChain(asid, tag, chain_ptr, &secondary_depth); + set_lat += secondary_depth; + // set depth for statistics fix this later!!! egh + sets[set].depth = set_lat; + + if (tag_ptr != NULL) { + /* need to move tag into primary table */ + // need to preserve chain: fix this egh + sets[set].tags[assoc-1]->chain_ptr = tag_ptr->chain_ptr; + tagSwap(tag_ptr - tagStore, sets[set].tags[assoc-1] - tagStore); + tag_ptr = sets[set].findTag(asid, tag, chain_ptr); + assert(tag_ptr!=NULL); + } + + } + set_lat = set_lat * hashDelay + hitLatency; + if (tag_ptr != NULL) { + // IIC replacement: if this is not the first element of + // list, reorder + sets[set].moveToHead(tag_ptr); + + hitHashDepth.sample(sets[set].depth); + hashHit++; + hitDepthTotal += sets[set].depth; + tag_ptr->status |= BlkReferenced; + lat = set_lat; + if (tag_ptr->whenReady > curTick && tag_ptr->whenReady - curTick > set_lat) { + lat = tag_ptr->whenReady - curTick; + } + + tag_ptr->refCount += 1; + } + else { + // fall through: cache block not found, not a hit... + missHashDepth.sample(sets[set].depth); + hashMiss++; + missDepthTotal += sets[set].depth; + lat = set_lat; + } + return tag_ptr; +} + +IICTag* +IIC::findBlock(Packet * &pkt, int &lat) +{ + Addr addr = pkt->getAddr(); + int asid = pkt->req->getAsid(); + + Addr tag = extractTag(addr); + unsigned set = hash(addr); + int set_lat; + + unsigned long chain_ptr; + + if (PROFILE_IIC) + setAccess.sample(set); + + IICTag *tag_ptr = sets[set].findTag(asid, tag, chain_ptr); + set_lat = 1; + if (tag_ptr == NULL && chain_ptr != tagNull) { + int secondary_depth; + tag_ptr = secondaryChain(asid, tag, chain_ptr, &secondary_depth); + set_lat += secondary_depth; + // set depth for statistics fix this later!!! egh + sets[set].depth = set_lat; + + if (tag_ptr != NULL) { + /* need to move tag into primary table */ + // need to preserve chain: fix this egh + sets[set].tags[assoc-1]->chain_ptr = tag_ptr->chain_ptr; + tagSwap(tag_ptr - tagStore, sets[set].tags[assoc-1] - tagStore); + tag_ptr = sets[set].findTag(asid, tag, chain_ptr); + assert(tag_ptr!=NULL); + } + + } + set_lat = set_lat * hashDelay + hitLatency; + if (tag_ptr != NULL) { + // IIC replacement: if this is not the first element of + // list, reorder + sets[set].moveToHead(tag_ptr); + + hitHashDepth.sample(sets[set].depth); + hashHit++; + hitDepthTotal += sets[set].depth; + tag_ptr->status |= BlkReferenced; + lat = set_lat; + if (tag_ptr->whenReady > curTick && tag_ptr->whenReady - curTick > set_lat) { + lat = tag_ptr->whenReady - curTick; + } + + tag_ptr->refCount += 1; + } + else { + // fall through: cache block not found, not a hit... + missHashDepth.sample(sets[set].depth); + hashMiss++; + missDepthTotal += sets[set].depth; + lat = set_lat; + } + return tag_ptr; +} + +IICTag* +IIC::findBlock(Addr addr, int asid) const +{ + Addr tag = extractTag(addr); + unsigned set = hash(addr); + + unsigned long chain_ptr; + + IICTag *tag_ptr = sets[set].findTag(asid, tag, chain_ptr); + if (tag_ptr == NULL && chain_ptr != tagNull) { + int secondary_depth; + tag_ptr = secondaryChain(asid, tag, chain_ptr, &secondary_depth); + } + return tag_ptr; +} + + +IICTag* +IIC::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + DPRINTF(IIC, "Finding Replacement for %x\n", pkt->getAddr()); + unsigned set = hash(pkt->getAddr()); + IICTag *tag_ptr; + unsigned long *tmp_data = new unsigned long[numSub]; + + // Get a enough subblocks for a full cache line + for (int i = 0; i < numSub; ++i){ + tmp_data[i] = getFreeDataBlock(writebacks); + assert(dataReferenceCount[tmp_data[i]]==0); + } + + tag_ptr = getFreeTag(set, writebacks); + + tag_ptr->set = set; + for (int i=0; i< numSub; ++i) { + tag_ptr->data_ptr[i] = tmp_data[i]; + dataReferenceCount[tag_ptr->data_ptr[i]]++; + } + tag_ptr->numData = numSub; + assert(tag_ptr - tagStore < primaryBound); // make sure it is in primary + tag_ptr->chain_ptr = tagNull; + sets[set].moveToHead(tag_ptr); + delete [] tmp_data; + + list<unsigned long> tag_indexes; + repl->doAdvance(tag_indexes); + while (!tag_indexes.empty()) { + if (!tagStore[tag_indexes.front()].isCompressed()) { + compress_blocks.push_back(&tagStore[tag_indexes.front()]); + } + tag_indexes.pop_front(); + } + + tag_ptr->re = (void*)repl->add(tag_ptr-tagStore); + + return tag_ptr; +} + +void +IIC::freeReplacementBlock(PacketList & writebacks) +{ + IICTag *tag_ptr; + unsigned long data_ptr; + /* consult replacement policy */ + tag_ptr = &tagStore[repl->getRepl()]; + assert(tag_ptr->isValid()); + + DPRINTF(Cache, "Replacing %x in IIC: %s\n", + regenerateBlkAddr(tag_ptr->tag,0), + tag_ptr->isModified() ? "writeback" : "clean"); + /* write back replaced block data */ + if (tag_ptr && (tag_ptr->isValid())) { + replacements[0]++; + totalRefs += tag_ptr->refCount; + ++sampledRefs; + tag_ptr->refCount = 0; + + if (tag_ptr->isModified()) { +/* Packet * writeback = + buildWritebackReq(regenerateBlkAddr(tag_ptr->tag, 0), + tag_ptr->req->asid, tag_ptr->xc, blkSize, + tag_ptr->data, + tag_ptr->size); +*/ + Request *writebackReq = new Request(regenerateBlkAddr(tag_ptr->tag, 0), + blkSize, 0); + Packet *writeback = new Packet(writebackReq, Packet::Writeback, -1); + writeback->allocate(); + memcpy(writeback->getPtr<uint8_t>(), tag_ptr->data, blkSize); + + writebacks.push_back(writeback); + } + } + + // free the data blocks + for (int i = 0; i < tag_ptr->numData; ++i) { + data_ptr = tag_ptr->data_ptr[i]; + assert(dataReferenceCount[data_ptr]>0); + if (--dataReferenceCount[data_ptr] == 0) { + freeDataBlock(data_ptr); + } + } + freeTag(tag_ptr); +} + +unsigned long +IIC::getFreeDataBlock(PacketList & writebacks) +{ + struct IICTag *tag_ptr; + unsigned long data_ptr; + + tag_ptr = NULL; + /* find data block */ + while (blkFreelist.empty()) { + freeReplacementBlock(writebacks); + } + + data_ptr = blkFreelist.front(); + blkFreelist.pop_front(); + DPRINTF(IICMore,"Found free data at %d\n",data_ptr); + return data_ptr; +} + + + +IICTag* +IIC::getFreeTag(int set, PacketList & writebacks) +{ + unsigned long tag_index; + IICTag *tag_ptr; + // Add new tag + tag_ptr = sets[set].findFree(); + // if no free in primary, and secondary exists + if (!tag_ptr && numSecondary) { + // need to spill a tag into secondary storage + while (freelist == tagNull) { + // get replacements until one is in secondary + freeReplacementBlock(writebacks); + } + + tag_index = freelist; + freelist = tagStore[freelist].chain_ptr; + freeSecond--; + + assert(tag_index != tagNull); + tagSwap(tag_index, sets[set].tags[assoc-1] - tagStore); + tagStore[tag_index].chain_ptr = sets[set].chain_ptr; + sets[set].chain_ptr = tag_index; + + tag_ptr = sets[set].tags[assoc-1]; + } + DPRINTF(IICMore,"Found free tag at %d\n",tag_ptr - tagStore); + tagsInUse++; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + + return tag_ptr; +} + +void +IIC::freeTag(IICTag *tag_ptr) +{ + unsigned long tag_index, tmp_index; + // Fix tag_ptr + if (tag_ptr) { + // we have a tag to clear + DPRINTF(IICMore,"Freeing Tag for %x\n", + regenerateBlkAddr(tag_ptr->tag,0)); + tagsInUse--; + tag_ptr->status = 0; + tag_ptr->numData = 0; + tag_ptr->re = NULL; + tag_index = tag_ptr - tagStore; + if (tag_index >= primaryBound) { + // tag_ptr points to secondary store + assert(tag_index < tagNull); // remove this?? egh + if (tag_ptr->chain_ptr == tagNull) { + // need to fix chain list + unsigned tmp_set = hash(tag_ptr->tag << tagShift); + if (sets[tmp_set].chain_ptr == tag_index) { + sets[tmp_set].chain_ptr = tagNull; + } else { + tmp_index = sets[tmp_set].chain_ptr; + while (tmp_index != tagNull + && tagStore[tmp_index].chain_ptr != tag_index) { + tmp_index = tagStore[tmp_index].chain_ptr; + } + assert(tmp_index != tagNull); + tagStore[tmp_index].chain_ptr = tagNull; + } + tag_ptr->chain_ptr = freelist; + freelist = tag_index; + freeSecond++; + } else { + // copy next chained entry to this tag location + tmp_index = tag_ptr->chain_ptr; + tagSwap(tmp_index, tag_index); + tagStore[tmp_index].chain_ptr = freelist; + freelist = tmp_index; + freeSecond++; + } + } else { + // tag_ptr in primary hash table + assert(tag_index < primaryBound); + tag_ptr->status = 0; + unsigned tmp_set = hash(tag_ptr->tag << tagShift); + if (sets[tmp_set].chain_ptr != tagNull) { // collapse chain + tmp_index = sets[tmp_set].chain_ptr; + tagSwap(tag_index, tmp_index); + tagStore[tmp_index].chain_ptr = freelist; + freelist = tmp_index; + freeSecond++; + sets[tmp_set].chain_ptr = tag_ptr->chain_ptr; + sets[tmp_set].moveToTail(tag_ptr); + } + } + } +} + +void +IIC::freeDataBlock(unsigned long data_ptr) +{ + assert(dataReferenceCount[data_ptr] == 0); + DPRINTF(IICMore, "Freeing data at %d\n", data_ptr); + blkFreelist.push_front(data_ptr); +} + +/** Use a simple modulo hash. */ +#define SIMPLE_HASH 0 + +unsigned +IIC::hash(Addr addr) const { +#if SIMPLE_HASH + return extractTag(addr) % iic_hash_size; +#else + Addr tag, mask, x, y; + tag = extractTag(addr); + mask = hashSets-1; /* assumes iic_hash_size is a power of 2 */ + x = tag & mask; + y = (tag >> (int)(::log(hashSets)/::log(2))) & mask; + assert (x < hashSets && y < hashSets); + return x ^ y; +#endif +} + + +void +IICSet::moveToHead(IICTag *tag) +{ + if (tags[0] == tag) + return; + + // write 'next' block into blks[i], moving up from MRU toward LRU + // until we overwrite the block we moved to head. + + // start by setting up to write 'blk' into blks[0] + int i = 0; + IICTag *next = tag; + + do { + assert(i < assoc); + // swap blks[i] and next + IICTag *tmp = tags[i]; + tags[i] = next; + next = tmp; + ++i; + } while (next != tag); +} + +void +IICSet::moveToTail(IICTag *tag) +{ + if (tags[assoc-1] == tag) + return; + + // write 'next' block into blks[i], moving up from MRU toward LRU + // until we overwrite the block we moved to head. + + // start by setting up to write 'blk' into blks[0] + int i = assoc - 1; + IICTag *next = tag; + + do { + assert(i >= 0); + // swap blks[i] and next + IICTag *tmp = tags[i]; + tags[i] = next; + next = tmp; + --i; + } while (next != tag); +} + +void +IIC::tagSwap(unsigned long index1, unsigned long index2) +{ + DPRINTF(IIC,"Swapping tag[%d]=%x for tag[%d]=%x\n",index1, + tagStore[index1].tag<<tagShift, index2, + tagStore[index2].tag<<tagShift); + IICTag tmp_tag; + tmp_tag = tagStore[index1]; + tagStore[index1] = tagStore[index2]; + tagStore[index2] = tmp_tag; + if (tagStore[index1].isValid()) + repl->fixTag(tagStore[index1].re, index2, index1); + if (tagStore[index2].isValid()) + repl->fixTag(tagStore[index2].re, index1, index2); +} + + +IICTag * +IIC::secondaryChain(int asid, Addr tag, unsigned long chain_ptr, + int *_depth) const +{ + int depth = 0; + while (chain_ptr != tagNull) { + DPRINTF(IIC,"Searching secondary at %d for %x\n", chain_ptr, + tag<<tagShift); + if (tagStore[chain_ptr].tag == tag && + tagStore[chain_ptr].asid == asid && + (tagStore[chain_ptr].isValid())) { + *_depth = depth; + return &tagStore[chain_ptr]; + } + depth++; + chain_ptr = tagStore[chain_ptr].chain_ptr; + } + *_depth = depth; + return NULL; +} + +void +IIC::decompressBlock(unsigned long index) +{ + IICTag *tag_ptr = &tagStore[index]; + if (tag_ptr->isCompressed()) { + // decompress the data here. + } +} + +void +IIC::compressBlock(unsigned long index) +{ + IICTag *tag_ptr = &tagStore[index]; + if (!tag_ptr->isCompressed()) { + // Compress the data here. + } +} + +void +IIC::invalidateBlk(int asid, Addr addr) +{ + IICTag* tag_ptr = findBlock(addr, asid); + if (tag_ptr) { + for (int i = 0; i < tag_ptr->numData; ++i) { + dataReferenceCount[tag_ptr->data_ptr[i]]--; + if (dataReferenceCount[tag_ptr->data_ptr[i]] == 0) { + freeDataBlock(tag_ptr->data_ptr[i]); + } + } + repl->removeEntry(tag_ptr->re); + freeTag(tag_ptr); + } +} + +void +IIC::readData(IICTag *blk, uint8_t *data){ +// assert(cache->doData()); + assert(blk->size <= trivialSize || blk->numData > 0); + int data_size = blk->size; + if (data_size > trivialSize) { + for (int i = 0; i < blk->numData; ++i){ + memcpy(data+i*subSize, + &(dataBlks[blk->data_ptr[i]][0]), + (data_size>subSize)?subSize:data_size); + data_size -= subSize; + } + } else { + memcpy(data,blk->trivialData,data_size); + } +} + +void +IIC::writeData(IICTag *blk, uint8_t *write_data, int size, + PacketList & writebacks){ +// assert(cache->doData()); + assert(size < blkSize || !blk->isCompressed()); + DPRINTF(IIC, "Writing %d bytes to %x\n", size, + blk->tag<<tagShift); + // Find the number of subblocks needed, (round up) + int num_subs = (size + (subSize -1))/subSize; + if (size <= trivialSize) { + num_subs = 0; + } + assert(num_subs <= numSub); + if (num_subs > blk->numData) { + // need to allocate more data blocks + for (int i = blk->numData; i < num_subs; ++i){ + blk->data_ptr[i] = getFreeDataBlock(writebacks); + dataReferenceCount[blk->data_ptr[i]] += 1; + } + } else if (num_subs < blk->numData){ + // can free data blocks + for (int i=num_subs; i < blk->numData; ++i){ + // decrement reference count and compare to zero + /** + * @todo + * Make this work with copying. + */ + if (--dataReferenceCount[blk->data_ptr[i]] == 0) { + freeDataBlock(blk->data_ptr[i]); + } + } + } + + blk->numData = num_subs; + blk->size = size; + assert(size <= trivialSize || blk->numData > 0); + if (size > trivialSize){ + for (int i = 0; i < blk->numData; ++i){ + memcpy(&dataBlks[blk->data_ptr[i]][0], write_data + i*subSize, + (size>subSize)?subSize:size); + size -= subSize; + } + } else { + memcpy(blk->trivialData,write_data,size); + } +} + + +/** + * @todo This code can break if the src is evicted to get a tag for the dest. + */ +void +IIC::doCopy(Addr source, Addr dest, int asid, PacketList &writebacks) +{ +//Copy unsuported now +#if 0 + IICTag *dest_tag = findBlock(dest, asid); + + if (dest_tag) { + for (int i = 0; i < dest_tag->numData; ++i) { + if (--dataReferenceCount[dest_tag->data_ptr[i]] == 0) { + freeDataBlock(dest_tag->data_ptr[i]); + } + } + // Reset replacement entry + } else { + dest_tag = getFreeTag(hash(dest), writebacks); + dest_tag->re = (void*) repl->add(dest_tag - tagStore); + dest_tag->set = hash(dest); + dest_tag->tag = extractTag(dest); + dest_tag->asid = asid; + dest_tag->status = BlkValid | BlkWritable; + } + // Find the source tag here since it might move if we need to find a + // tag for the destination. + IICTag *src_tag = findBlock(source, asid); + assert(src_tag); + assert(!cache->doData() || src_tag->size <= trivialSize + || src_tag->numData > 0); + // point dest to source data and inc counter + for (int i = 0; i < src_tag->numData; ++i) { + dest_tag->data_ptr[i] = src_tag->data_ptr[i]; + ++dataReferenceCount[dest_tag->data_ptr[i]]; + } + + // Maintain fast access data. + memcpy(dest_tag->data, src_tag->data, blkSize); + + dest_tag->xc = src_tag->xc; + dest_tag->size = src_tag->size; + dest_tag->numData = src_tag->numData; + if (src_tag->numData == 0) { + // Data is stored in the trivial data, just copy it. + memcpy(dest_tag->trivialData, src_tag->trivialData, src_tag->size); + } + + dest_tag->status |= BlkDirty; + if (dest_tag->size < blkSize) { + dest_tag->status |= BlkCompressed; + } else { + dest_tag->status &= ~BlkCompressed; + } +#endif +} + +void +IIC::fixCopy(Packet * &pkt, PacketList &writebacks) +{ +#if 0 + // if reference counter is greater than 1, do copy + // else do write + Addr blk_addr = blkAlign(pkt->getAddr); + IICTag* blk = findBlock(blk_addr, pkt->req->getAsid()); + + if (blk->numData > 0 && dataReferenceCount[blk->data_ptr[0]] != 1) { + // copy the data + // Mark the block as referenced so it doesn't get replaced. + blk->status |= BlkReferenced; + for (int i = 0; i < blk->numData; ++i){ + unsigned long new_data = getFreeDataBlock(writebacks); + // Need to refresh pointer + /** + * @todo Remove this refetch once we change IIC to pointer based + */ + blk = findBlock(blk_addr, pkt->req->getAsid()); + assert(blk); + if (cache->doData()) { + memcpy(&(dataBlks[new_data][0]), + &(dataBlks[blk->data_ptr[i]][0]), + subSize); + } + dataReferenceCount[blk->data_ptr[i]]--; + dataReferenceCount[new_data]++; + blk->data_ptr[i] = new_data; + } + } +#endif +} + +void +IIC::cleanupRefs() +{ + for (int i = 0; i < numTags; ++i) { + if (tagStore[i].isValid()) { + totalRefs += tagStore[i].refCount; + ++sampledRefs; + } + } +} diff --git a/src/mem/cache/tags/iic.hh b/src/mem/cache/tags/iic.hh new file mode 100644 index 000000000..6628f7e7a --- /dev/null +++ b/src/mem/cache/tags/iic.hh @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of the Indirect Index Cache (IIC) tags store. + */ + +#ifndef __IIC_HH__ +#define __IIC_HH__ + +#include <list> +#include <vector> + +#include "mem/cache/cache_blk.hh" +#include "mem/cache/tags/repl/repl.hh" +#include "mem/packet.hh" +#include "base/statistics.hh" +#include "mem/cache/tags/base_tags.hh" + +class BaseCache; // Forward declaration + +/** + * IIC cache blk. + */ +class IICTag : public CacheBlk +{ + public: + /** + * Copy the contents of the given IICTag into this one. + * @param rhs The tag to copy. + * @return const reference to this tag. + */ + const IICTag& operator=(const IICTag& rhs) + { + CacheBlk::operator=(rhs); + chain_ptr = rhs.chain_ptr; + re = rhs.re; + set = rhs.set; + trivialData = rhs.trivialData; + numData = rhs.numData; + data_ptr.clear(); + for (int i = 0; i < rhs.numData; ++i) { + data_ptr.push_back(rhs.data_ptr[i]); + } + return *this; + } + + /** Hash chain pointer into secondary store. */ + unsigned long chain_ptr; + /** Data array pointers for each subblock. */ + std::vector<unsigned long> data_ptr; + /** Replacement Entry pointer. */ + void *re; + /** + * An array to store small compressed data. Conceputally the same size + * as the unsused data array pointers. + */ + uint8_t *trivialData; + /** + * The number of allocated subblocks. + */ + int numData; +}; + +/** + * A hash set for the IIC primary lookup table. + */ +class IICSet{ + public: + /** The associativity of the primary table. */ + int assoc; + + /** The number of hash chains followed when finding the last block. */ + int depth; + /** The current number of blocks on the chain. */ + int size; + + /** Tag pointer into the secondary tag storage. */ + unsigned long chain_ptr; + + /** The LRU list of the primary table. MRU is at 0 index. */ + IICTag ** tags; + + /** + * Find the addr in this set, return the chain pointer to the secondary if + * it isn't found. + * @param asid The address space ID. + * @param tag The address to find. + * @param chain_ptr The chain pointer to start the search of the secondary + * @return Pointer to the tag, NULL if not found. + */ + IICTag* findTag(int asid, Addr tag, unsigned long &chain_ptr) + { + depth = 1; + for (int i = 0; i < assoc; ++i) { + if (tags[i]->tag == tag && tags[i]->isValid()) { + return tags[i]; + } + } + chain_ptr = this->chain_ptr; + return 0; + } + + /** + * Find an usused tag in this set. + * @return Pointer to the unused tag, NULL if none are free. + */ + IICTag* findFree() + { + for (int i = 0; i < assoc; ++i) { + if (!tags[i]->isValid()) { + return tags[i]; + } + } + return 0; + } + + /** + * Move a tag to the head of the LRU list + * @param tag The tag to move. + */ + void moveToHead(IICTag *tag); + + /** + * Move a tag to the tail (LRU) of the LRU list + * @param tag The tag to move. + */ + void moveToTail(IICTag *tag); +}; + +/** + * The IIC tag store. This is a hardware-realizable, fully-associative tag + * store that uses software replacement, e.g. Gen. + */ +class IIC : public BaseTags +{ + public: + /** Typedef of the block type used in this class. */ + typedef IICTag BlkType; + /** Typedef for list of pointers to the local block type. */ + typedef std::list<IICTag*> BlkList; + protected: + /** The number of set in the primary table. */ + const int hashSets; + /** The block size in bytes. */ + const int blkSize; + /** The associativity of the primary table. */ + const int assoc; + /** The base hit latency. */ + const int hitLatency; + /** The subblock size, used for compression. */ + const int subSize; + + /** The number of subblocks */ + const int numSub; + /** The number of bytes used by data pointers */ + const int trivialSize; + + /** The amount to shift address to get the tag. */ + const int tagShift; + /** The mask to get block offset bits. */ + const unsigned blkMask; + + /** The amount to shift to get the subblock number. */ + const int subShift; + /** The mask to get the correct subblock number. */ + const unsigned subMask; + + /** The latency of a hash lookup. */ + const int hashDelay; + /** The number of data blocks. */ + const int numBlocks; + /** The total number of tags in primary and secondary. */ + const int numTags; + /** The number of tags in the secondary tag store. */ + const int numSecondary; + + /** The Null tag pointer. */ + const int tagNull; + /** The last tag in the primary table. */ + const int primaryBound; + + /** All of the tags */ + IICTag *tagStore; + /** + * Pointer to the head of the secondary freelist (maintained with chain + * pointers. + */ + unsigned long freelist; + /** + * The data block freelist. + */ + std::list<unsigned long> blkFreelist; + + /** The primary table. */ + IICSet *sets; + + /** The replacement policy. */ + Repl *repl; + + /** An array of data reference counters. */ + int *dataReferenceCount; + + /** The data blocks. */ + uint8_t *dataStore; + + /** Storage for the fast access data of each cache block. */ + uint8_t **dataBlks; + + /** + * Count of the current number of free secondary tags. + * Used for debugging. + */ + int freeSecond; + + // IIC Statistics + /** + * @addtogroup IICStatistics IIC Statistics + * @{ + */ + + /** Hash hit depth of cache hits. */ + Stats::Distribution<> hitHashDepth; + /** Hash depth for cache misses. */ + Stats::Distribution<> missHashDepth; + /** Count of accesses to each hash set. */ + Stats::Distribution<> setAccess; + + /** The total hash depth for every miss. */ + Stats::Scalar<> missDepthTotal; + /** The total hash depth for all hits. */ + Stats::Scalar<> hitDepthTotal; + /** The number of hash misses. */ + Stats::Scalar<> hashMiss; + /** The number of hash hits. */ + Stats::Scalar<> hashHit; + /** @} */ + + public: + /** + * Collection of parameters for the IIC. + */ + class Params { + public: + /** The size in bytes of the cache. */ + int size; + /** The number of sets in the primary table. */ + int numSets; + /** The block size in bytes. */ + int blkSize; + /** The associativity of the primary table. */ + int assoc; + /** The number of cycles for each hash lookup. */ + int hashDelay; + /** The number of cycles to read the data. */ + int hitLatency; + /** The replacement policy. */ + Repl *rp; + /** The subblock size in bytes. */ + int subblockSize; + }; + + /** + * Construct and initialize this tag store. + * @param params The IIC parameters. + * @todo + * Should make a way to have less tags in the primary than blks in the + * cache. Also should be able to specify number of secondary blks. + */ + IIC(Params ¶ms); + + /** + * Destructor. + */ + virtual ~IIC(); + + /** + * Register the statistics. + * @param name The name to prepend to the statistic descriptions. + */ + void regStats(const std::string &name); + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set Not needed for the iic. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, int set) { + return (((Addr)tag << tagShift)); + } + + /** + * Return the block size. + * @return The block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. + * @return The subblock size. + */ + int getSubBlockSize() + { + return subSize; + } + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Generate the tag from the address. + * @param addr The address to a get a tag for. + * @param blk Ignored here. + * @return the tag. + */ + Addr extractTag(Addr addr, IICTag *blk) const + { + return (addr >> tagShift); + } + + /** + * Generate the tag from the address. + * @param addr The address to a get a tag for. + * @return the tag. + */ + Addr extractTag(Addr addr) const + { + return (addr >> tagShift); + } + + /** + * Return the set, always 0 for IIC. + * @return 0. + */ + int extractSet(Addr addr) const + { + return 0; + } + + /** + * Get the block offset of an address. + * @param addr The address to get the offset of. + * @return the block offset of the address. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)blkMask); + } + + /** + * Check for the address in the tagstore. + * @param asid The address space ID. + * @param addr The address to find. + * @return true if it is found. + */ + bool probe(int asid, Addr addr) const; + + /** + * Swap the position of two tags. + * @param index1 The first tag location. + * @param index2 The second tag location. + */ + void tagSwap(unsigned long index1, unsigned long index2); + + /** + * Clear the reference bit of the tag and return its old value. + * @param index The pointer of the tag to manipulate. + * @return The previous state of the reference bit. + */ + bool clearRef(unsigned long index) + { + bool tmp = tagStore[index].isReferenced(); + tagStore[index].status &= ~BlkReferenced; + return tmp; + } + + /** + * Decompress a block if it is compressed. + * @param index The tag store index for the block to uncompress. + */ + void decompressBlock(unsigned long index); + + /** + * Try and compress a block if it is not already compressed. + * @param index The tag store index for the block to compress. + */ + void compressBlock(unsigned long index); + + /** + * Invalidate the block containing the address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(int asid, Addr addr); + + /** + * Find the block and update the replacement data. This call also returns + * the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return A pointer to the block found, if any. + */ + IICTag* findBlock(Addr addr, int asid, int &lat); + + /** + * Find the block and update the replacement data. This call also returns + * the access latency as a side effect. + * @param req The req whose block to find + * @param lat The access latency. + * @return A pointer to the block found, if any. + */ + IICTag* findBlock(Packet * &pkt, int &lat); + + /** + * Find the block, do not update the replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return A pointer to the block found, if any. + */ + IICTag* findBlock(Addr addr, int asid) const; + + /** + * Find a replacement block for the address provided. + * @param req The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + IICTag* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + /** + * Read the data from the internal storage of the given cache block. + * @param blk The block to read the data from. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(IICTag *blk, uint8_t *data); + + /** + * Write the data into the internal storage of the given cache block. + * @param blk The block to write to. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(IICTag *blk, uint8_t *data, int size, + PacketList & writebacks); + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback requests. + */ + void doCopy(Addr source, Addr dest, int asid, PacketList &writebacks); + + /** + * If a block is currently marked copy on write, copy it before writing. + * @param req The write request. + * @param writebacks List for any generated writeback requests. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks); + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +private: + /** + * Return the hash of the address. + * @param addr The address to hash. + * @return the hash of the address. + */ + unsigned hash(Addr addr) const; + + /** + * Search for a block in the secondary tag store. Returns the number of + * hash lookups as a side effect. + * @param asid The address space ID. + * @param tag The tag to match. + * @param chain_ptr The first entry to search. + * @param depth The number of hash lookups made while searching. + * @return A pointer to the block if found. + */ + IICTag *secondaryChain(int asid, Addr tag, unsigned long chain_ptr, + int *depth) const; + + /** + * Free the resources associated with the next replacement block. + * @param writebacks A list of any writebacks to perform. + */ + void freeReplacementBlock(PacketList & writebacks); + + /** + * Return the pointer to a free data block. + * @param writebacks A list of any writebacks to perform. + * @return A pointer to a free data block. + */ + unsigned long getFreeDataBlock(PacketList & writebacks); + + /** + * Get a free tag in the given hash set. + * @param set The hash set to search. + * @param writebacks A list of any writebacks to perform. + * @return a pointer to a free tag. + */ + IICTag* getFreeTag(int set, PacketList & writebacks); + + /** + * Free the resources associated with the given tag. + * @param tag_ptr The tag to free. + */ + void freeTag(IICTag *tag_ptr); + + /** + * Mark the given data block as being available. + * @param data_ptr The data block to free. + */ + void freeDataBlock(unsigned long data_ptr); +}; +#endif // __IIC_HH__ + diff --git a/src/mem/cache/tags/lru.cc b/src/mem/cache/tags/lru.cc new file mode 100644 index 000000000..556025a3a --- /dev/null +++ b/src/mem/cache/tags/lru.cc @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definitions of LRU tag store. + */ + +#include <string> + +#include "mem/cache/base_cache.hh" +#include "base/intmath.hh" +#include "mem/cache/tags/lru.hh" +#include "sim/root.hh" + +using namespace std; + +LRUBlk* +CacheSet::findBlk(int asid, Addr tag) const +{ + for (int i = 0; i < assoc; ++i) { + if (blks[i]->tag == tag && blks[i]->isValid()) { + return blks[i]; + } + } + return 0; +} + + +void +CacheSet::moveToHead(LRUBlk *blk) +{ + // nothing to do if blk is already head + if (blks[0] == blk) + return; + + // write 'next' block into blks[i], moving up from MRU toward LRU + // until we overwrite the block we moved to head. + + // start by setting up to write 'blk' into blks[0] + int i = 0; + LRUBlk *next = blk; + + do { + assert(i < assoc); + // swap blks[i] and next + LRUBlk *tmp = blks[i]; + blks[i] = next; + next = tmp; + ++i; + } while (next != blk); +} + + +// create and initialize a LRU/MRU cache structure +LRU::LRU(int _numSets, int _blkSize, int _assoc, int _hit_latency) : + numSets(_numSets), blkSize(_blkSize), assoc(_assoc), hitLatency(_hit_latency) +{ + // Check parameters + if (blkSize < 4 || !isPowerOf2(blkSize)) { + fatal("Block size must be at least 4 and a power of 2"); + } + if (numSets <= 0 || !isPowerOf2(numSets)) { + fatal("# of sets must be non-zero and a power of 2"); + } + if (assoc <= 0) { + fatal("associativity must be greater than zero"); + } + if (hitLatency <= 0) { + fatal("access latency must be greater than zero"); + } + + LRUBlk *blk; + int i, j, blkIndex; + + blkMask = blkSize - 1; + setShift = floorLog2(blkSize); + setMask = numSets - 1; + tagShift = setShift + floorLog2(numSets); + warmedUp = false; + /** @todo Make warmup percentage a parameter. */ + warmupBound = numSets * assoc; + + sets = new CacheSet[numSets]; + blks = new LRUBlk[numSets * assoc]; + // allocate data storage in one big chunk + dataBlks = new uint8_t[numSets*assoc*blkSize]; + + blkIndex = 0; // index into blks array + for (i = 0; i < numSets; ++i) { + sets[i].assoc = assoc; + + sets[i].blks = new LRUBlk*[assoc]; + + // link in the data blocks + for (j = 0; j < assoc; ++j) { + // locate next cache block + blk = &blks[blkIndex]; + blk->data = &dataBlks[blkSize*blkIndex]; + ++blkIndex; + + // invalidate new cache block + blk->status = 0; + + //EGH Fix Me : do we need to initialize blk? + + // Setting the tag to j is just to prevent long chains in the hash + // table; won't matter because the block is invalid + blk->tag = j; + blk->whenReady = 0; + blk->asid = -1; + blk->isTouched = false; + blk->size = blkSize; + sets[i].blks[j]=blk; + blk->set = i; + } + } +} + +LRU::~LRU() +{ + delete [] dataBlks; + delete [] blks; + delete [] sets; +} + +// probe cache for presence of given block. +bool +LRU::probe(int asid, Addr addr) const +{ + // return(findBlock(Read, addr, asid) != 0); + Addr tag = extractTag(addr); + unsigned myset = extractSet(addr); + + LRUBlk *blk = sets[myset].findBlk(asid, tag); + + return (blk != NULL); // true if in cache +} + +LRUBlk* +LRU::findBlock(Addr addr, int asid, int &lat) +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + LRUBlk *blk = sets[set].findBlk(asid, tag); + lat = hitLatency; + if (blk != NULL) { + // move this block to head of the MRU list + sets[set].moveToHead(blk); + if (blk->whenReady > curTick + && blk->whenReady - curTick > hitLatency) { + lat = blk->whenReady - curTick; + } + blk->refCount += 1; + } + + return blk; +} + +LRUBlk* +LRU::findBlock(Packet * &pkt, int &lat) +{ + Addr addr = pkt->getAddr(); + int asid = 0;//pkt->req->getAsid(); + + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + LRUBlk *blk = sets[set].findBlk(asid, tag); + lat = hitLatency; + if (blk != NULL) { + // move this block to head of the MRU list + sets[set].moveToHead(blk); + if (blk->whenReady > curTick + && blk->whenReady - curTick > hitLatency) { + lat = blk->whenReady - curTick; + } + blk->refCount += 1; + } + + return blk; +} + +LRUBlk* +LRU::findBlock(Addr addr, int asid) const +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + LRUBlk *blk = sets[set].findBlk(asid, tag); + return blk; +} + +LRUBlk* +LRU::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + unsigned set = extractSet(pkt->getAddr()); + // grab a replacement candidate + LRUBlk *blk = sets[set].blks[assoc-1]; + sets[set].moveToHead(blk); + if (blk->isValid()) { + replacements[0]++; + totalRefs += blk->refCount; + ++sampledRefs; + blk->refCount = 0; + } else if (!blk->isTouched) { + tagsInUse++; + blk->isTouched = true; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + } + + return blk; +} + +void +LRU::invalidateBlk(int asid, Addr addr) +{ + LRUBlk *blk = findBlock(addr, asid); + if (blk) { + blk->status = 0; + blk->isTouched = false; + tagsInUse--; + } +} + +void +LRU::doCopy(Addr source, Addr dest, int asid, PacketList &writebacks) +{ + assert(source == blkAlign(source)); + assert(dest == blkAlign(dest)); + LRUBlk *source_blk = findBlock(source, asid); + assert(source_blk); + LRUBlk *dest_blk = findBlock(dest, asid); + if (dest_blk == NULL) { + // Need to do a replacement + Request *search = new Request(dest,1,0); + Packet * pkt = new Packet(search, Packet::ReadReq, -1); + BlkList dummy_list; + dest_blk = findReplacement(pkt, writebacks, dummy_list); + if (dest_blk->isValid() && dest_blk->isModified()) { + // Need to writeback data. +/* pkt = buildWritebackReq(regenerateBlkAddr(dest_blk->tag, + dest_blk->set), + dest_blk->req->asid, + dest_blk->xc, + blkSize, + dest_blk->data, + dest_blk->size); +*/ + Request *writebackReq = new Request(regenerateBlkAddr(dest_blk->tag, + dest_blk->set), + blkSize, 0); + Packet *writeback = new Packet(writebackReq, Packet::Writeback, -1); + writeback->allocate(); + memcpy(writeback->getPtr<uint8_t>(),dest_blk->data, blkSize); + writebacks.push_back(writeback); + } + dest_blk->tag = extractTag(dest); + dest_blk->asid = asid; + delete search; + delete pkt; + } + /** + * @todo Can't assume the status once we have coherence on copies. + */ + + // Set this block as readable, writeable, and dirty. + dest_blk->status = 7; + memcpy(dest_blk->data, source_blk->data, blkSize); +} + +void +LRU::cleanupRefs() +{ + for (int i = 0; i < numSets*assoc; ++i) { + if (blks[i].isValid()) { + totalRefs += blks[i].refCount; + ++sampledRefs; + } + } +} diff --git a/src/mem/cache/tags/lru.hh b/src/mem/cache/tags/lru.hh new file mode 100644 index 000000000..437244660 --- /dev/null +++ b/src/mem/cache/tags/lru.hh @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a LRU tag store. + */ + +#ifndef __LRU_HH__ +#define __LRU_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" // base class +#include "mem/packet.hh" // for inlined functions +#include <assert.h> +#include "mem/cache/tags/base_tags.hh" + +class BaseCache; + +/** + * LRU cache block. + */ +class LRUBlk : public CacheBlk { + public: + /** Has this block been touched? Used to aid calculation of warmup time. */ + bool isTouched; +}; + +/** + * An associative set of cache blocks. + */ +class CacheSet +{ + public: + /** The associativity of this set. */ + int assoc; + + /** Cache blocks in this set, maintained in LRU order 0 = MRU. */ + LRUBlk **blks; + + /** + * Find a block matching the tag in this set. + * @param asid The address space ID. + * @param tag The Tag to find. + * @return Pointer to the block if found. + */ + LRUBlk* findBlk(int asid, Addr tag) const; + + /** + * Move the given block to the head of the list. + * @param blk The block to move. + */ + void moveToHead(LRUBlk *blk); +}; + +/** + * A LRU cache tag store. + */ +class LRU : public BaseTags +{ + public: + /** Typedef the block type used in this tag store. */ + typedef LRUBlk BlkType; + /** Typedef for a list of pointers to the local block class. */ + typedef std::list<LRUBlk*> BlkList; + protected: + /** The number of sets in the cache. */ + const int numSets; + /** The number of bytes in a block. */ + const int blkSize; + /** The associativity of the cache. */ + const int assoc; + /** The hit latency. */ + const int hitLatency; + + /** The cache sets. */ + CacheSet *sets; + + /** The cache blocks. */ + LRUBlk *blks; + /** The data blocks, 1 per cache block. */ + uint8_t *dataBlks; + + /** The amount to shift the address to get the set. */ + int setShift; + /** The amount to shift the address to get the tag. */ + int tagShift; + /** Mask out all bits that aren't part of the set index. */ + unsigned setMask; + /** Mask out all bits that aren't part of the block offset. */ + unsigned blkMask; + +public: + /** + * Construct and initialize this tag store. + * @param _numSets The number of sets in the cache. + * @param _blkSize The number of bytes in a block. + * @param _assoc The associativity of the cache. + * @param _hit_latency The latency in cycles for a hit. + */ + LRU(int _numSets, int _blkSize, int _assoc, int _hit_latency); + + /** + * Destructor + */ + virtual ~LRU(); + + /** + * Return the block size. + * @return the block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. In the case of LRU it is always the block + * size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Search for the address in the cache. + * @param asid The address space ID. + * @param addr The address to find. + * @return True if the address is in the cache. + */ + bool probe(int asid, Addr addr) const; + + /** + * Invalidate the block containing the given address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(int asid, Addr addr); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param req The request whose block to find. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + LRUBlk* findBlock(Packet * &pkt, int &lat); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + LRUBlk* findBlock(Addr addr, int asid, int &lat); + + /** + * Finds the given address in the cache, do not update replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return Pointer to the cache block if found. + */ + LRUBlk* findBlock(Addr addr, int asid) const; + + /** + * Find a replacement block for the address provided. + * @param req The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + LRUBlk* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @return The tag of the address. + */ + Addr extractTag(Addr addr) const + { + return (addr >> tagShift); + } + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @param blk Ignored. + * @return The tag of the address. + */ + Addr extractTag(Addr addr, LRUBlk *blk) const + { + return (addr >> tagShift); + } + + /** + * Calculate the set index from the address. + * @param addr The address to get the set from. + * @return The set index of the address. + */ + int extractSet(Addr addr) const + { + return ((addr >> setShift) & setMask); + } + + /** + * Get the block offset from an address. + * @param addr The address to get the offset of. + * @return The block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)blkMask); + } + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set The set of the block. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, unsigned set) const + { + return ((tag << tagShift) | ((Addr)set << setShift)); + } + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Read the data out of the internal storage of the given cache block. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(LRUBlk *blk, uint8_t *data) + { + memcpy(data, blk->data, blk->size); + } + + /** + * Write data into the internal storage of the given cache block. Since in + * LRU does not store data differently this just needs to update the size. + * @param blk The cache block to write. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(LRUBlk *blk, uint8_t *data, int size, + PacketList & writebacks) + { + assert(size <= blkSize); + blk->size = size; + } + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback requests. + */ + void doCopy(Addr source, Addr dest, int asid, PacketList &writebacks); + + /** + * No impl. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +}; + +#endif diff --git a/src/mem/cache/tags/repl/gen.cc b/src/mem/cache/tags/repl/gen.cc new file mode 100644 index 000000000..ec1c2aaf3 --- /dev/null +++ b/src/mem/cache/tags/repl/gen.cc @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Steve Reinhardt + */ + +/** + * @file + * Definitions of the Generational replacement policy. + */ + +#include <string> + +#include "base/misc.hh" +#include "mem/cache/tags/iic.hh" +#include "mem/cache/tags/repl/gen.hh" +#include "sim/builder.hh" +#include "sim/host.hh" + +using namespace std; + +GenRepl::GenRepl(const string &_name, + int _num_pools, + int _fresh_res, + int _pool_res) // fix this, should be set by cache + : Repl(_name) +{ + num_pools = _num_pools; + fresh_res = _fresh_res; + pool_res = _pool_res; + num_entries = 0; + num_pool_entries = 0; + misses = 0; + pools = new GenPool[num_pools+1]; +} + +GenRepl::~GenRepl() +{ + delete [] pools; +} + +unsigned long +GenRepl::getRepl() +{ + unsigned long tmp; + GenReplEntry *re; + int i; + int num_seen = 0; + if (!(num_pool_entries>0)) { + fatal("No blks available to replace"); + } + num_entries--; + num_pool_entries--; + for (i = 0; i < num_pools; i++) { + while ((re = pools[i].pop())) { + num_seen++; + // Remove invalidated entries + if (!re->valid) { + delete re; + continue; + } + if (iic->clearRef(re->tag_ptr)) { + pools[(((i+1)== num_pools)? i :i+1)].push(re, misses); + } + else { + tmp = re->tag_ptr; + delete re; + + repl_pool.sample(i); + + return tmp; + } + } + } + fatal("No replacement found"); + return 0xffffffff; +} + +unsigned long * +GenRepl::getNRepl(int n) +{ + unsigned long *tmp; + GenReplEntry *re; + int i; + if (!(num_pool_entries>(n-1))) { + fatal("Not enough blks available to replace"); + } + num_entries -= n; + num_pool_entries -= n; + tmp = new unsigned long[n]; /* array of cache_blk pointers */ + int blk_index = 0; + for (i = 0; i < num_pools && blk_index < n; i++) { + while (blk_index < n && (re = pools[i].pop())) { + // Remove invalidated entries + if (!re->valid) { + delete re; + continue; + } + if (iic->clearRef(re->tag_ptr)) { + pools[(((i+1)== num_pools)? i :i+1)].push(re, misses); + } + else { + tmp[blk_index] = re->tag_ptr; + blk_index++; + delete re; + repl_pool.sample(i); + } + } + } + if (blk_index >= n) + return tmp; + /* search the fresh pool */ + + fatal("No N replacements found"); + return NULL; +} + +void +GenRepl::doAdvance(std::list<unsigned long> &demoted) +{ + int i; + int num_seen = 0; + GenReplEntry *re; + misses++; + for (i=0; i<num_pools; i++) { + while (misses-pools[i].oldest > pool_res && (re = pools[i].pop())!=NULL) { + if (iic->clearRef(re->tag_ptr)) { + pools[(((i+1)== num_pools)? i :i+1)].push(re, misses); + /** @todo Not really demoted, but use it for now. */ + demoted.push_back(re->tag_ptr); + advance_pool.sample(i); + } + else { + pools[(((i-1)<0)?i:i-1)].push(re, misses); + demoted.push_back(re->tag_ptr); + demote_pool.sample(i); + } + } + num_seen += pools[i].size; + } + while (misses-pools[num_pools].oldest > fresh_res + && (re = pools[num_pools].pop())!=NULL) { + num_pool_entries++; + if (iic->clearRef(re->tag_ptr)) { + pools[num_pools/2].push(re, misses); + /** @todo Not really demoted, but use it for now. */ + demoted.push_back(re->tag_ptr); + advance_pool.sample(num_pools); + } + else { + pools[num_pools/2-1].push(re, misses); + demoted.push_back(re->tag_ptr); + demote_pool.sample(num_pools); + } + } +} + +void* +GenRepl::add(unsigned long tag_index) +{ + GenReplEntry *re = new GenReplEntry; + re->tag_ptr = tag_index; + re->valid = true; + pools[num_pools].push(re, misses); + num_entries++; + return (void*)re; +} + +void +GenRepl::regStats(const string name) +{ + using namespace Stats; + + /** GEN statistics */ + repl_pool + .init(0, 16, 1) + .name(name + ".repl_pool_dist") + .desc("Dist. of Repl. across pools") + .flags(pdf) + ; + + advance_pool + .init(0, 16, 1) + .name(name + ".advance_pool_dist") + .desc("Dist. of Repl. across pools") + .flags(pdf) + ; + + demote_pool + .init(0, 16, 1) + .name(name + ".demote_pool_dist") + .desc("Dist. of Repl. across pools") + .flags(pdf) + ; +} + +int +GenRepl::fixTag(void* _re, unsigned long old_index, unsigned long new_index) +{ + GenReplEntry *re = (GenReplEntry*)_re; + assert(re->valid); + if (re->tag_ptr == old_index) { + re->tag_ptr = new_index; + return 1; + } + fatal("Repl entry: tag ptrs do not match"); + return 0; +} + +bool +GenRepl::findTagPtr(unsigned long index) +{ + for (int i = 0; i < num_pools + 1; ++i) { + list<GenReplEntry*>::const_iterator iter = pools[i].entries.begin(); + list<GenReplEntry*>::const_iterator end = pools[i].entries.end(); + for (; iter != end; ++iter) { + if ((*iter)->valid && (*iter)->tag_ptr == index) { + return true; + } + } + } + return false; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(GenRepl) + + Param<int> num_pools; + Param<int> fresh_res; + Param<int> pool_res; + +END_DECLARE_SIM_OBJECT_PARAMS(GenRepl) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(GenRepl) + + INIT_PARAM(num_pools, "capacity in bytes"), + INIT_PARAM(fresh_res, "associativity"), + INIT_PARAM(pool_res, "block size in bytes") + +END_INIT_SIM_OBJECT_PARAMS(GenRepl) + + +CREATE_SIM_OBJECT(GenRepl) +{ + return new GenRepl(getInstanceName(), num_pools, fresh_res, pool_res); +} + +REGISTER_SIM_OBJECT("GenRepl", GenRepl) + +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/tags/repl/gen.hh b/src/mem/cache/tags/repl/gen.hh new file mode 100644 index 000000000..c1ceb3f4e --- /dev/null +++ b/src/mem/cache/tags/repl/gen.hh @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declarations of generational replacement policy + */ + +#ifndef ___GEN_HH__ +#define __GEN_HH__ + +#include <list> + +#include "base/statistics.hh" +#include "mem/cache/tags/repl/repl.hh" + +/** + * Generational Replacement entry. + */ +class GenReplEntry +{ + public: + /** Valid flag, used to quickly invalidate bogus entries. */ + bool valid; + /** The difference between this entry and the previous in the pool. */ + int delta; + /** Pointer to the corresponding tag in the IIC. */ + unsigned long tag_ptr; +}; + +/** + * Generational replacement pool + */ +class GenPool +{ + public: + /** The time the last entry was added. */ + Tick newest; + /** The time the oldest entry was added. */ + Tick oldest; + /** List of the replacement entries in this pool. */ + std::list<GenReplEntry*> entries; + + /** The number of entries in this pool. */ + int size; + + /** + * Simple constructor. + */ + GenPool() { + newest = 0; + oldest = 0; + size = 0; + } + + /** + * Add an entry to this pool. + * @param re The entry to add. + * @param now The current time. + */ + void push(GenReplEntry *re, Tick now) { + ++size; + if (!entries.empty()) { + re->delta = now - newest; + newest = now; + } else { + re->delta = 0; + newest = oldest = now; + } + entries.push_back(re); + } + + /** + * Remove an entry from the pool. + * @return The entry at the front of the list. + */ + GenReplEntry* pop() { + GenReplEntry *tmp = NULL; + if (!entries.empty()) { + --size; + tmp = entries.front(); + entries.pop_front(); + oldest += tmp->delta; + } + return tmp; + } + + /** + * Return the entry at the front of the list. + * @return the entry at the front of the list. + */ + GenReplEntry* top() { + return entries.front(); + } + + /** + * Destructor. + */ + ~GenPool() { + while (!entries.empty()) { + GenReplEntry *tmp = entries.front(); + entries.pop_front(); + delete tmp; + } + } +}; + +/** + * Generational replacement policy for use with the IIC. + * @todo update to use STL and for efficiency + */ +class GenRepl : public Repl +{ + public: + /** The array of pools. */ + GenPool *pools; + /** The number of pools. */ + int num_pools; + /** The amount of time to stay in the fresh pool. */ + int fresh_res; + /** The amount of time to stay in the normal pools. */ + int pool_res; + /** The maximum number of entries */ + int num_entries; + /** The number of entries currently in the pools. */ + int num_pool_entries; + /** The number of misses. Used as the internal time. */ + Tick misses; + + // Statistics + + /** + * @addtogroup CacheStatistics + * @{ + */ + /** The number of replacements from each pool. */ + Stats::Distribution<> repl_pool; + /** The number of advances out of each pool. */ + Stats::Distribution<> advance_pool; + /** The number of demotions from each pool. */ + Stats::Distribution<> demote_pool; + /** + * @} + */ + + /** + * Constructs and initializes this replacement policy. + * @param name The name of the policy. + * @param num_pools The number of pools to use. + * @param fresh_res The amount of time to wait in the fresh pool. + * @param pool_res The amount of time to wait in the normal pools. + */ + GenRepl(const std::string &name, int num_pools, + int fresh_res, int pool_res); + + /** + * Destructor. + */ + ~GenRepl(); + + /** + * Returns the tag pointer of the cache block to replace. + * @return The tag to replace. + */ + virtual unsigned long getRepl(); + + /** + * Return an array of N tag pointers to replace. + * @param n The number of tag pointer to return. + * @return An array of tag pointers to replace. + */ + virtual unsigned long *getNRepl(int n); + + /** + * Update replacement data + */ + virtual void doAdvance(std::list<unsigned long> &demoted); + + /** + * Add a tag to the replacement policy and return a pointer to the + * replacement entry. + * @param tag_index The tag to add. + * @return The replacement entry. + */ + virtual void* add(unsigned long tag_index); + + /** + * Register statistics. + * @param name The name to prepend to statistic descriptions. + */ + virtual void regStats(const std::string name); + + /** + * Update the tag pointer to when the tag moves. + * @param re The replacement entry of the tag. + * @param old_index The old tag pointer. + * @param new_index The new tag pointer. + * @return 1 if successful, 0 otherwise. + */ + virtual int fixTag(void *re, unsigned long old_index, + unsigned long new_index); + + /** + * Remove this entry from the replacement policy. + * @param re The replacement entry to remove + */ + virtual void removeEntry(void *re) + { + ((GenReplEntry*)re)->valid = false; + } + + protected: + /** + * Debug function to verify that there is only one repl entry per tag. + * @param index The tag index to check. + */ + bool findTagPtr(unsigned long index); +}; + +#endif /* __GEN_HH__ */ diff --git a/src/mem/cache/tags/repl/repl.cc b/src/mem/cache/tags/repl/repl.cc new file mode 100644 index 000000000..ce781eb9f --- /dev/null +++ b/src/mem/cache/tags/repl/repl.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Nathan Binkert + */ + +/** + * Definitions of the base replacement class. + */ + +#include "sim/param.hh" +#include "mem/cache/tags/repl/repl.hh" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +DEFINE_SIM_OBJECT_CLASS_NAME("Repl", Repl) + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/tags/repl/repl.hh b/src/mem/cache/tags/repl/repl.hh new file mode 100644 index 000000000..7c289a5c1 --- /dev/null +++ b/src/mem/cache/tags/repl/repl.hh @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Erik Hallnor + * Steve Reinhardt + * Nathan Binkert + */ + +/** + * @file + * Declaration of a base replacement policy class. + */ + +#ifndef __REPL_HH__ +#define __REPL_HH__ + +#include <string> +#include <list> + +#include "cpu/smt.hh" +#include "sim/host.hh" +#include "sim/sim_object.hh" + + +class IIC; + +/** + * A pure virtual base class that defines the interface of a replacement + * policy. + */ +class Repl : public SimObject +{ + public: + /** Pointer to the IIC using this policy. */ + IIC *iic; + + /** + * Construct and initialize this polixy. + * @param name The instance name of this policy. + */ + Repl (const std::string &name) + : SimObject(name) + { + iic = NULL; + } + + /** + * Set the back pointer to the IIC. + * @param iic_ptr Pointer to the IIC. + */ + void setIIC(IIC *iic_ptr) + { + iic = iic_ptr; + } + + /** + * Returns the tag pointer of the cache block to replace. + * @return The tag to replace. + */ + virtual unsigned long getRepl() = 0; + + /** + * Return an array of N tag pointers to replace. + * @param n The number of tag pointer to return. + * @return An array of tag pointers to replace. + */ + virtual unsigned long *getNRepl(int n) = 0; + + /** + * Update replacement data + */ + virtual void doAdvance(std::list<unsigned long> &demoted) = 0; + + /** + * Add a tag to the replacement policy and return a pointer to the + * replacement entry. + * @param tag_index The tag to add. + * @return The replacement entry. + */ + virtual void* add(unsigned long tag_index) = 0; + + /** + * Register statistics. + * @param name The name to prepend to statistic descriptions. + */ + virtual void regStats(const std::string name) = 0; + + /** + * Update the tag pointer to when the tag moves. + * @param re The replacement entry of the tag. + * @param old_index The old tag pointer. + * @param new_index The new tag pointer. + * @return 1 if successful, 0 otherwise. + */ + virtual int fixTag(void *re, unsigned long old_index, + unsigned long new_index) = 0; + + /** + * Remove this entry from the replacement policy. + * @param re The replacement entry to remove + */ + virtual void removeEntry(void *re) = 0; +}; + +#endif /* SMT_REPL_HH */ diff --git a/src/mem/cache/tags/split.cc b/src/mem/cache/tags/split.cc new file mode 100644 index 000000000..bf23fb8cb --- /dev/null +++ b/src/mem/cache/tags/split.cc @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Definitions of split cache tag store. + */ + +#include <string> +#include <iostream> +#include <fstream> + +#include "base/cprintf.hh" +#include "base/intmath.hh" +#include "base/output.hh" +#include "base/trace.hh" +#include "mem/cache/base_cache.hh" +#include "mem/cache/tags/split.hh" +#include "mem/cache/tags/split_lifo.hh" +#include "mem/cache/tags/split_lru.hh" + + +using namespace std; +using namespace TheISA; + +// create and initialize a partitioned cache structure +Split::Split(int _numSets, int _blkSize, int total_ways, int LRU1_assoc, + bool _lifo, bool _two_queue, int _hit_latency) : + numSets(_numSets), blkSize(_blkSize), lifo(_lifo), hitLatency(_hit_latency) +{ + DPRINTF(Split, "new split cache!!\n"); + + DPRINTF(Split, "lru has %d numSets, %d blkSize, %d assoc, and %d hit_latency\n", + numSets, blkSize, LRU1_assoc, hitLatency); + + lru = new SplitLRU(_numSets, _blkSize, LRU1_assoc, _hit_latency, 1); + + if (total_ways - LRU1_assoc == 0) { + lifo_net = NULL; + lru_net = NULL; + } else { + if (lifo) { + DPRINTF(Split, "Other partition is a LIFO with size %d in bytes. it gets %d ways\n", + (total_ways - LRU1_assoc)*_numSets*_blkSize, (total_ways - LRU1_assoc)); + lifo_net = new SplitLIFO(_blkSize, (total_ways - LRU1_assoc)*_numSets*_blkSize, + (total_ways - LRU1_assoc), _hit_latency, _two_queue, 2); + lru_net = NULL; + } + else { + DPRINTF(Split, "other LRU gets %d ways\n", total_ways - LRU1_assoc); + lru_net = new SplitLRU(_numSets, _blkSize, total_ways - LRU1_assoc, _hit_latency, 2); + lifo_net = NULL; + } + } + + blkMask = blkSize - 1; + + if (!isPowerOf2(total_ways)) + warn("total cache ways/columns %d should be power of 2", + total_ways); + + warmedUp = false; + /** @todo Make warmup percentage a parameter. */ + warmupBound = numSets * total_ways; + +} + +Split::~Split() +{ + delete lru; + if (lifo) + delete lifo_net; + else + delete lru_net; +} + +void +Split::regStats(const string &name) +{ + using namespace Stats; + + BaseTags::regStats(name); + + usedEvictDist.init(0,3000,40); + unusedEvictDist.init(0,3000,40); + useByCPUCycleDist.init(0,35,1); + + nic_repl + .name(name + ".nic_repl") + .desc("number of replacements in the nic partition") + .precision(0) + ; + + cpu_repl + .name(name + ".cpu_repl") + .desc("number of replacements in the cpu partition") + .precision(0) + ; + + lru->regStats(name + ".lru"); + + if (lifo && lifo_net) { + lifo_net->regStats(name + ".lifo_net"); + } else if (lru_net) { + lru_net->regStats(name + ".lru_net"); + } + + nicUsedWhenEvicted + .name(name + ".nicUsedWhenEvicted") + .desc("number of NIC blks that were used before evicted") + ; + + nicUsedTotLatency + .name(name + ".nicUsedTotLatency") + .desc("total cycles before eviction of used NIC blks") + ; + + nicUsedTotEvicted + .name(name + ".nicUsedTotEvicted") + .desc("total number of used NIC blks evicted") + ; + + nicUsedAvgLatency + .name(name + ".nicUsedAvgLatency") + .desc("avg number of cycles a used NIC blk is in cache") + .precision(0) + ; + nicUsedAvgLatency = nicUsedTotLatency / nicUsedTotEvicted; + + usedEvictDist + .name(name + ".usedEvictDist") + .desc("distribution of used NIC blk eviction times") + .flags(pdf | cdf) + ; + + nicUnusedWhenEvicted + .name(name + ".nicUnusedWhenEvicted") + .desc("number of NIC blks that were unused when evicted") + ; + + nicUnusedTotLatency + .name(name + ".nicUnusedTotLatency") + .desc("total cycles before eviction of unused NIC blks") + ; + + nicUnusedTotEvicted + .name(name + ".nicUnusedTotEvicted") + .desc("total number of unused NIC blks evicted") + ; + + nicUnusedAvgLatency + .name(name + ".nicUnusedAvgLatency") + .desc("avg number of cycles an unused NIC blk is in cache") + .precision(0) + ; + nicUnusedAvgLatency = nicUnusedTotLatency / nicUnusedTotEvicted; + + unusedEvictDist + .name(name + ".unusedEvictDist") + .desc("distribution of unused NIC blk eviction times") + .flags(pdf | cdf) + ; + + nicUseByCPUCycleTotal + .name(name + ".nicUseByCPUCycleTotal") + .desc("total latency of NIC blks til usage time") + ; + + nicBlksUsedByCPU + .name(name + ".nicBlksUsedByCPU") + .desc("total number of NIC blks used") + ; + + nicAvgUsageByCPULatency + .name(name + ".nicAvgUsageByCPULatency") + .desc("average number of cycles before a NIC blk that is used gets used") + .precision(0) + ; + nicAvgUsageByCPULatency = nicUseByCPUCycleTotal / nicBlksUsedByCPU; + + useByCPUCycleDist + .name(name + ".useByCPUCycleDist") + .desc("the distribution of cycle time in cache before NIC blk is used") + .flags(pdf | cdf) + ; + + cpuUsedBlks + .name(name + ".cpuUsedBlks") + .desc("number of cpu blks that were used before evicted") + ; + + cpuUnusedBlks + .name(name + ".cpuUnusedBlks") + .desc("number of cpu blks that were unused before evicted") + ; + + nicAvgLatency + .name(name + ".nicAvgLatency") + .desc("avg number of cycles a NIC blk is in cache before evicted") + .precision(0) + ; + nicAvgLatency = (nicUnusedTotLatency + nicUsedTotLatency) / + (nicUnusedTotEvicted + nicUsedTotEvicted); + + NR_CP_hits + .name(name + ".NR_CP_hits") + .desc("NIC requests hitting in CPU Partition") + ; + + NR_NP_hits + .name(name + ".NR_NP_hits") + .desc("NIC requests hitting in NIC Partition") + ; + + CR_CP_hits + .name(name + ".CR_CP_hits") + .desc("CPU requests hitting in CPU partition") + ; + + CR_NP_hits + .name(name + ".CR_NP_hits") + .desc("CPU requests hitting in NIC partition") + ; + +} + +// probe cache for presence of given block. +bool +Split::probe(int asid, Addr addr) const +{ + bool success = lru->probe(asid, addr); + if (!success) { + if (lifo && lifo_net) + success = lifo_net->probe(asid, addr); + else if (lru_net) + success = lru_net->probe(asid, addr); + } + + return success; +} + +SplitBlk* +Split::findBlock(Packet * &pkt, int &lat) +{ + + Addr aligned = blkAlign(pkt->getAddr()); + + if (memHash.count(aligned)) { + memHash[aligned]++; + } else if (pkt->nic_pkt()) { + memHash[aligned] = 1; + } + + SplitBlk *blk = lru->findBlock(pkt->getAddr(), pkt->req->getAsid(), lat); + if (blk) { + if (pkt->nic_pkt()) { + NR_CP_hits++; + } else { + CR_CP_hits++; + } + } else { + if (lifo && lifo_net) { + blk = lifo_net->findBlock(pkt->getAddr(), pkt->req->getAsid(), lat); + + } else if (lru_net) { + blk = lru_net->findBlock(pkt->getAddr(), pkt->req->getAsid(), lat); + } + if (blk) { + if (pkt->nic_pkt()) { + NR_NP_hits++; + } else { + CR_NP_hits++; + } + } + } + + if (blk) { + Tick latency = curTick - blk->ts; + if (blk->isNIC) { + if (!blk->isUsed && !pkt->nic_pkt()) { + useByCPUCycleDist.sample(latency); + nicUseByCPUCycleTotal += latency; + nicBlksUsedByCPU++; + } + } + blk->isUsed = true; + + if (pkt->nic_pkt()) { + DPRINTF(Split, "found block in partition %d\n", blk->part); + } + } + return blk; +} + +SplitBlk* +Split::findBlock(Addr addr, int asid, int &lat) +{ + SplitBlk *blk = lru->findBlock(addr, asid, lat); + if (!blk) { + if (lifo && lifo_net) { + blk = lifo_net->findBlock(addr, asid, lat); + } else if (lru_net) { + blk = lru_net->findBlock(addr, asid, lat); + } + } + + return blk; +} + +SplitBlk* +Split::findBlock(Addr addr, int asid) const +{ + SplitBlk *blk = lru->findBlock(addr, asid); + if (!blk) { + if (lifo && lifo_net) { + blk = lifo_net->findBlock(addr, asid); + } else if (lru_net) { + blk = lru_net->findBlock(addr, asid); + } + } + + return blk; +} + +SplitBlk* +Split::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + SplitBlk *blk; + + if (pkt->nic_pkt()) { + DPRINTF(Split, "finding a replacement for nic_req\n"); + nic_repl++; + if (lifo && lifo_net) + blk = lifo_net->findReplacement(pkt, writebacks, + compress_blocks); + else if (lru_net) + blk = lru_net->findReplacement(pkt, writebacks, + compress_blocks); + // in this case, this is an LRU only cache, it's non partitioned + else + blk = lru->findReplacement(pkt, writebacks, compress_blocks); + } else { + DPRINTF(Split, "finding replacement for cpu_req\n"); + blk = lru->findReplacement(pkt, writebacks, + compress_blocks); + cpu_repl++; + } + + Tick latency = curTick - blk->ts; + if (blk->isNIC) { + if (blk->isUsed) { + nicUsedWhenEvicted++; + usedEvictDist.sample(latency); + nicUsedTotLatency += latency; + nicUsedTotEvicted++; + } else { + nicUnusedWhenEvicted++; + unusedEvictDist.sample(latency); + nicUnusedTotLatency += latency; + nicUnusedTotEvicted++; + } + } else { + if (blk->isUsed) { + cpuUsedBlks++; + } else { + cpuUnusedBlks++; + } + } + + // blk attributes for the new blk coming IN + blk->ts = curTick; + blk->isNIC = (pkt->nic_pkt()) ? true : false; + + return blk; +} + +void +Split::invalidateBlk(int asid, Addr addr) +{ + SplitBlk *blk = lru->findBlock(addr, asid); + if (!blk) { + if (lifo && lifo_net) + blk = lifo_net->findBlock(addr, asid); + else if (lru_net) + blk = lru_net->findBlock(addr, asid); + + if (!blk) + return; + } + + blk->status = 0; + blk->isTouched = false; + tagsInUse--; +} + +void +Split::doCopy(Addr source, Addr dest, int asid, PacketList &writebacks) +{ + if (lru->probe(asid, source)) + lru->doCopy(source, dest, asid, writebacks); + else { + if (lifo && lifo_net) + lifo_net->doCopy(source, dest, asid, writebacks); + else if (lru_net) + lru_net->doCopy(source, dest, asid, writebacks); + } +} + +void +Split::cleanupRefs() +{ + lru->cleanupRefs(); + if (lifo && lifo_net) + lifo_net->cleanupRefs(); + else if (lru_net) + lru_net->cleanupRefs(); + + ofstream memPrint(simout.resolve("memory_footprint.txt").c_str(), + ios::trunc); + + // this shouldn't be here but it happens at the end, which is what i want + memIter end = memHash.end(); + for (memIter iter = memHash.begin(); iter != end; ++iter) { + ccprintf(memPrint, "%8x\t%d\n", (*iter).first, (*iter).second); + } +} + +Addr +Split::regenerateBlkAddr(Addr tag, int set) const +{ + if (lifo_net) + return lifo_net->regenerateBlkAddr(tag, set); + else + return lru->regenerateBlkAddr(tag, set); +} + +Addr +Split::extractTag(Addr addr, SplitBlk *blk) const +{ + if (blk->part == 2) { + if (lifo_net) + return lifo_net->extractTag(addr); + else if (lru_net) + return lru_net->extractTag(addr); + else + panic("this shouldn't happen"); + } else + return lru->extractTag(addr); +} + diff --git a/src/mem/cache/tags/split.hh b/src/mem/cache/tags/split.hh new file mode 100644 index 000000000..5e0340269 --- /dev/null +++ b/src/mem/cache/tags/split.hh @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Declaration of a split/partitioned tag store. + */ + +#ifndef __SPLIT_HH__ +#define __SPLIT_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" // base class +#include "mem/cache/tags/split_blk.hh" +#include "mem/packet.hh" // for inlined functions +#include <assert.h> +#include "mem/cache/tags/base_tags.hh" +#include "base/hashmap.hh" + +class BaseCache; +class SplitLRU; +class SplitLIFO; + +/** + * A cache tag store. + */ +class Split : public BaseTags +{ + public: + /** Typedef the block type used in this tag store. */ + typedef SplitBlk BlkType; + /** Typedef for a list of pointers to the local block class. */ + typedef std::list<SplitBlk*> BlkList; + protected: + /** The number of sets in the cache. */ + const int numSets; + /** The number of bytes in a block. */ + const int blkSize; + /** Whether the 2nd partition (for the nic) is LIFO or not */ + const bool lifo; + /** The hit latency. */ + const int hitLatency; + + Addr blkMask; + + /** Number of NIC requests that hit in the NIC partition */ + Stats::Scalar<> NR_NP_hits; + /** Number of NIC requests that hit in the CPU partition */ + Stats::Scalar<> NR_CP_hits; + /** Number of CPU requests that hit in the NIC partition */ + Stats::Scalar<> CR_NP_hits; + /** Number of CPU requests that hit in the CPU partition */ + Stats::Scalar<> CR_CP_hits; + /** The number of nic replacements (i.e. misses) */ + Stats::Scalar<> nic_repl; + /** The number of cpu replacements (i.e. misses) */ + Stats::Scalar<> cpu_repl; + + //For latency studies + /** the number of NIC blks that were used before evicted */ + Stats::Scalar<> nicUsedWhenEvicted; + /** the total latency of used NIC blocks in the cache */ + Stats::Scalar<> nicUsedTotLatency; + /** the total number of used NIC blocks evicted */ + Stats::Scalar<> nicUsedTotEvicted; + /** the average number of cycles a used NIC blk is in the cache */ + Stats::Formula nicUsedAvgLatency; + /** the Distribution of used NIC blk eviction times */ + Stats::Distribution<> usedEvictDist; + + /** the number of NIC blks that were unused before evicted */ + Stats::Scalar<> nicUnusedWhenEvicted; + /** the total latency of unused NIC blks in the cache */ + Stats::Scalar<> nicUnusedTotLatency; + /** the total number of unused NIC blocks evicted */ + Stats::Scalar<> nicUnusedTotEvicted; + /** the average number of cycles an unused NIC blk is in the cache */ + Stats::Formula nicUnusedAvgLatency; + /** the Distribution of unused NIC blk eviction times */ + Stats::Distribution<> unusedEvictDist; + + /** The total latency of NIC blocks to 1st usage time by CPU */ + Stats::Scalar<> nicUseByCPUCycleTotal; + /** The total number of NIC blocks used */ + Stats::Scalar<> nicBlksUsedByCPU; + /** the average number of cycles before a NIC blk that is used gets used by CPU */ + Stats::Formula nicAvgUsageByCPULatency; + /** the Distribution of cycles time before a NIC blk is used by CPU*/ + Stats::Distribution<> useByCPUCycleDist; + + /** the number of CPU blks that were used before evicted */ + Stats::Scalar<> cpuUsedBlks; + /** the number of CPU blks that were unused before evicted */ + Stats::Scalar<> cpuUnusedBlks; + + /** the avg number of cycles before a NIC blk is evicted */ + Stats::Formula nicAvgLatency; + + typedef m5::hash_map<Addr, int, m5::hash<Addr> > hash_t; + typedef hash_t::const_iterator memIter; + hash_t memHash; + + + private: + SplitLRU *lru; + SplitLRU *lru_net; + SplitLIFO *lifo_net; + + public: + /** + * Construct and initialize this tag store. + * @param _numSets The number of sets in the cache. + * @param _blkSize The number of bytes in a block. + * @param _assoc The associativity of the cache. + * @param _hit_latency The latency in cycles for a hit. + */ + Split(int _numSets, int _blkSize, int total_ways, int LRU1_assoc, + bool _lifo, bool _two_queue, int _hit_latency); + + /** + * Destructor + */ + virtual ~Split(); + + /** + * Register the stats for this object + * @param name The name to prepend to the stats name. + */ + void regStats(const std::string &name); + + /** + * Return the block size. + * @return the block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. In the case of Split it is always the block + * size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Search for the address in the cache. + * @param asid The address space ID. + * @param addr The address to find. + * @return True if the address is in the cache. + */ + bool probe(int asid, Addr addr) const; + + /** + * Invalidate the block containing the given address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(int asid, Addr addr); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr, int asid, int &lat); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param req The memory request whose block to find + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Packet * &pkt, int &lat); + + /** + * Finds the given address in the cache, do not update replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr, int asid) const; + + /** + * Find a replacement block for the address provided. + * @param req The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + SplitBlk* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @param blk The block to find the partition it's in + * @return The tag of the address. + */ + Addr extractTag(Addr addr, SplitBlk *blk) const; + + /** + * Calculate the set index from the address. + * @param addr The address to get the set from. + * @return The set index of the address. + */ + int extractSet(Addr addr) const + { + panic("should never call this!\n"); + } + + /** + * Get the block offset from an address. + * @param addr The address to get the offset of. + * @return The block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr) (blkMask)); + } + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set The set of the block. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, int set) const; + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Read the data out of the internal storage of the given cache block. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(SplitBlk *blk, uint8_t *data) + { + memcpy(data, blk->data, blk->size); + } + + /** + * Write data into the internal storage of the given cache block. Since in + * Split does not store data differently this just needs to update the size. + * @param blk The cache block to write. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(SplitBlk *blk, uint8_t *data, int size, + PacketList & writebacks) + { + assert(size <= blkSize); + blk->size = size; + } + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback requests. + */ + void doCopy(Addr source, Addr dest, int asid, PacketList &writebacks); + + /** + * No impl. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +}; + +#endif diff --git a/src/mem/cache/tags/split_blk.hh b/src/mem/cache/tags/split_blk.hh new file mode 100644 index 000000000..f38516180 --- /dev/null +++ b/src/mem/cache/tags/split_blk.hh @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Declaration of partitioned tag store cache block class. + */ + +#ifndef __SPLIT_BLK_HH__ +#define __SPLIT_BLK_HH__ + +#include "mem/cache/cache_blk.hh" // base class + +/** + * Split cache block. + */ +class SplitBlk : public CacheBlk { + public: + /** Has this block been touched? Used to aid calculation of warmup time. */ + bool isTouched; + /** Has this block been used after being brought in? (for LIFO partition) */ + bool isUsed; + /** is this blk a NIC block? (i.e. requested by the NIC) */ + bool isNIC; + /** timestamp of the arrival of this block into the cache */ + Tick ts; + /** the previous block in the LIFO partition (brought in before than me) */ + SplitBlk *prev; + /** the next block in the LIFO partition (brought in later than me) */ + SplitBlk *next; + /** which partition this block is in */ + int part; + + SplitBlk() + : isTouched(false), isUsed(false), isNIC(false), ts(0), prev(NULL), next(NULL), + part(0) + {} +}; + +#endif + diff --git a/src/mem/cache/tags/split_lifo.cc b/src/mem/cache/tags/split_lifo.cc new file mode 100644 index 000000000..f6493fdd2 --- /dev/null +++ b/src/mem/cache/tags/split_lifo.cc @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Definitions of LIFO tag store usable in a partitioned cache. + */ + +#include <string> + +#include "mem/cache/base_cache.hh" +#include "base/intmath.hh" +#include "mem/cache/tags/split_lifo.hh" +#include "sim/root.hh" +#include "base/trace.hh" + +using namespace std; + +SplitBlk* +LIFOSet::findBlk(int asid, Addr tag) const +{ + for (SplitBlk *blk = firstIn; blk != NULL; blk = blk->next) { + if (blk->tag == tag && blk->isValid()) { + return blk; + } + } + return NULL; +} + +void +LIFOSet::moveToLastIn(SplitBlk *blk) +{ + if (blk == lastIn) + return; + + if (blk == firstIn) { + blk->next->prev = NULL; + } else { + blk->prev->next = blk->next; + blk->next->prev = blk->prev; + } + blk->next = NULL; + blk->prev = lastIn; + lastIn->next = blk; + + lastIn = blk; +} + +void +LIFOSet::moveToFirstIn(SplitBlk *blk) +{ + if (blk == firstIn) + return; + + if (blk == lastIn) { + blk->prev->next = NULL; + } else { + blk->next->prev = blk->prev; + blk->prev->next = blk->next; + } + + blk->prev = NULL; + blk->next = firstIn; + firstIn->prev = blk; + + firstIn = blk; +} + +// create and initialize a LIFO cache structure +SplitLIFO::SplitLIFO(int _blkSize, int _size, int _ways, int _hit_latency, bool two_Queue, int _part) : + blkSize(_blkSize), size(_size), numBlks(_size/_blkSize), numSets((_size/_ways)/_blkSize), ways(_ways), + hitLatency(_hit_latency), twoQueue(two_Queue), part(_part) +{ + if (!isPowerOf2(blkSize)) + fatal("cache block size (in bytes) must be a power of 2"); + if (!(hitLatency > 0)) + fatal("access latency in cycles must be at least on cycle"); + if (_ways == 0) + fatal("if instantiating a splitLIFO, needs non-zero size!"); + + + SplitBlk *blk; + int i, j, blkIndex; + + setShift = floorLog2(blkSize); + blkMask = blkSize - 1; + setMask = numSets - 1; + tagShift = setShift + floorLog2(numSets); + + warmedUp = false; + /** @todo Make warmup percentage a parameter. */ + warmupBound = size/blkSize; + + // allocate data blocks + blks = new SplitBlk[numBlks]; + sets = new LIFOSet[numSets]; + dataBlks = new uint8_t[size]; + +/* + // these start off point to same blk + top = &(blks[0]); + head = top; +*/ + + blkIndex = 0; + for (i=0; i < numSets; ++i) { + sets[i].ways = ways; + sets[i].lastIn = &blks[blkIndex]; + sets[i].firstIn = &blks[blkIndex + ways - 1]; + + /* 3 cases: if there is 1 way, if there are 2 ways, or if there are 3+. + in the case of 1 way, last in and first out point to the same blocks, + and the next and prev pointers need to be assigned specially. and so on + */ + /* deal with the first way */ + blk = &blks[blkIndex]; + blk->prev = &blks[blkIndex + 1]; + blk->next = NULL; + blk->data = &dataBlks[blkSize*blkIndex]; + blk->size = blkSize; + blk->part = part; + blk->set = i; + ++blkIndex; + + /* if there are "middle" ways, do them here */ + if (ways > 2) { + for (j=1; j < ways-1; ++j) { + blk = &blks[blkIndex]; + blk->data = &dataBlks[blkSize*blkIndex]; + blk->prev = &blks[blkIndex+1]; + blk->next = &blks[blkIndex-1]; + blk->data = &(dataBlks[blkSize*blkIndex]); + blk->size = blkSize; + blk->part = part; + blk->set = i; + ++blkIndex; + } + } + + /* do the final way here, depending on whether the final way is the only + way or not + */ + if (ways > 1) { + blk = &blks[blkIndex]; + blk->prev = NULL; + blk->next = &blks[blkIndex - 1]; + blk->data = &dataBlks[blkSize*blkIndex]; + blk->size = blkSize; + blk->part = part; + blk->set = i; + ++blkIndex; + } else { + blk->prev = NULL; + } + } + assert(blkIndex == numBlks); +} + +SplitLIFO::~SplitLIFO() +{ + delete [] blks; + delete [] sets; + delete [] dataBlks; +} + +void +SplitLIFO::regStats(const std::string &name) +{ + BaseTags::regStats(name); + + hits + .name(name + ".hits") + .desc("number of hits on this partition") + .precision(0) + ; + + misses + .name(name + ".misses") + .desc("number of misses in this partition") + .precision(0) + ; + + invalidations + .name(name + ".invalidations") + .desc("number of invalidations in this partition") + .precision(0) + ; +} + +// probe cache for presence of given block. +bool +SplitLIFO::probe(int asid, Addr addr) const +{ + Addr tag = extractTag(addr); + unsigned myset = extractSet(addr); + + SplitBlk* blk = sets[myset].findBlk(asid, tag); + return (blk != NULL); +} + +SplitBlk* +SplitLIFO::findBlock(Addr addr, int asid, int &lat) +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(asid, tag); + + lat = hitLatency; + + if (blk) { + DPRINTF(Split, "Found LIFO blk %#x in set %d, with tag %#x\n", + addr, set, tag); + hits++; + + if (blk->whenReady > curTick && blk->whenReady - curTick > hitLatency) + lat = blk->whenReady - curTick; + blk->refCount +=1; + + if (twoQueue) { + blk->isUsed = true; + sets[set].moveToFirstIn(blk); + } else { + sets[set].moveToLastIn(blk); + } + } + + return blk; +} + +SplitBlk* +SplitLIFO::findBlock(Packet * &pkt, int &lat) +{ + Addr addr = pkt->getAddr(); + int asid = pkt->req->getAsid(); + + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(asid, tag); + + if (blk) { + DPRINTF(Split, "Found LIFO blk %#x in set %d, with tag %#x\n", + addr, set, tag); + hits++; + + if (twoQueue) { + blk->isUsed = true; + sets[set].moveToFirstIn(blk); + } else { + sets[set].moveToLastIn(blk); + } + } + lat = hitLatency; + + return blk; +} + +SplitBlk* +SplitLIFO::findBlock(Addr addr, int asid) const +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(asid, tag); + + return blk; +} + +SplitBlk* +SplitLIFO::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + unsigned set = extractSet(pkt->getAddr()); + + SplitBlk *firstIn = sets[set].firstIn; + SplitBlk *lastIn = sets[set].lastIn; + + SplitBlk *blk; + if (twoQueue && firstIn->isUsed) { + blk = firstIn; + blk->isUsed = false; + sets[set].moveToLastIn(blk); + } else { + int withValue = sets[set].withValue; + if (withValue == ways) { + blk = lastIn; + } else { + blk = &(sets[set].firstIn[ways - ++withValue]); + } + } + + DPRINTF(Split, "just assigned %#x addr into LIFO, replacing %#x status %#x\n", + pkt->getAddr(), regenerateBlkAddr(blk->tag, set), blk->status); + if (blk->isValid()) { + replacements[0]++; + totalRefs += blk->refCount; + ++sampledRefs; + blk->refCount = 0; + } else { + tagsInUse++; + blk->isTouched = true; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + } + + misses++; + + return blk; +} + +void +SplitLIFO::invalidateBlk(int asid, Addr addr) +{ + SplitBlk *blk = findBlock(addr, asid); + if (blk) { + blk->status = 0; + blk->isTouched = false; + tagsInUse--; + invalidations++; + } +} + +void +SplitLIFO::doCopy(Addr source, Addr dest, int asid, PacketList &writebacks) +{ +//Copy Unsuported for now +#if 0 + assert(source == blkAlign(source)); + assert(dest == blkAlign(dest)); + SplitBlk *source_blk = findBlock(source, asid); + assert(source_blk); + SplitBlk *dest_blk = findBlock(dest, asid); + if (dest_blk == NULL) { + // Need to do a replacement + Packet * pkt = new Packet(); + pkt->paddr = dest; + BlkList dummy_list; + dest_blk = findReplacement(pkt, writebacks, dummy_list); + if (dest_blk->isValid() && dest_blk->isModified()) { + // Need to writeback data. + pkt = buildWritebackReq(regenerateBlkAddr(dest_blk->tag, + dest_blk->set), + dest_blk->req->asid, + dest_blk->xc, + blkSize, + (cache->doData())?dest_blk->data:0, + dest_blk->size); + writebacks.push_back(pkt); + } + dest_blk->tag = extractTag(dest); + dest_blk->req->asid = asid; + /** + * @todo Do we need to pass in the execution context, or can we + * assume its the same? + */ + assert(source_blk->xc); + dest_blk->xc = source_blk->xc; + } + /** + * @todo Can't assume the status once we have coherence on copies. + */ + + // Set this block as readable, writeable, and dirty. + dest_blk->status = 7; + if (cache->doData()) { + memcpy(dest_blk->data, source_blk->data, blkSize); + } +#endif +} + +void +SplitLIFO::cleanupRefs() +{ + for (int i = 0; i < numBlks; ++i) { + if (blks[i].isValid()) { + totalRefs += blks[i].refCount; + ++sampledRefs; + } + } +} diff --git a/src/mem/cache/tags/split_lifo.hh b/src/mem/cache/tags/split_lifo.hh new file mode 100644 index 000000000..dfcaa0b67 --- /dev/null +++ b/src/mem/cache/tags/split_lifo.hh @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Declaration of a LIFO tag store usable in a partitioned cache. + */ + +#ifndef __SPLIT_LIFO_HH__ +#define __SPLIT_LIFO_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" // base class +#include "mem/cache/tags/split_blk.hh" +#include "mem/packet.hh" // for inlined functions +#include "base/hashmap.hh" +#include <assert.h> +#include "mem/cache/tags/base_tags.hh" + +class BaseCache; + +/** + * A LIFO set of cache blks + */ +class LIFOSet { + public: + /** the number of blocks in this set */ + int ways; + + /** Cache blocks in this set, maintained in LIFO order where + 0 = Last in (head) */ + SplitBlk *lastIn; + SplitBlk *firstIn; + + /** has the initial "filling" of this set finished? i.e., have you had + * 'ways' number of compulsory misses in this set yet? if withValue == ways, + * then yes. withValue is meant to be the number of blocks in the set that have + * gone through their first compulsory miss. + */ + int withValue; + + /** + * Find a block matching the tag in this set. + * @param asid The address space ID. + * @param tag the Tag you are looking for + * @return Pointer to the block, if found, NULL otherwise + */ + SplitBlk* findBlk(int asid, Addr tag) const; + + void moveToLastIn(SplitBlk *blk); + void moveToFirstIn(SplitBlk *blk); + + LIFOSet() + : ways(-1), lastIn(NULL), firstIn(NULL), withValue(0) + {} +}; + +/** + * A LIFO cache tag store. + */ +class SplitLIFO : public BaseTags +{ + public: + /** Typedef the block type used in this tag store. */ + typedef SplitBlk BlkType; + /** Typedef for a list of pointers to the local block class. */ + typedef std::list<SplitBlk*> BlkList; + protected: + /** The number of bytes in a block. */ + const int blkSize; + /** the size of the cache in bytes */ + const int size; + /** the number of blocks in the cache */ + const int numBlks; + /** the number of sets in the cache */ + const int numSets; + /** the number of ways in the cache */ + const int ways; + /** The hit latency. */ + const int hitLatency; + /** whether this is a "2 queue" replacement @sa moveToLastIn @sa moveToFirstIn */ + const bool twoQueue; + /** indicator for which partition this is */ + const int part; + + /** The cache blocks. */ + SplitBlk *blks; + /** The Cache sets */ + LIFOSet *sets; + /** The data blocks, 1 per cache block. */ + uint8_t *dataBlks; + + /** The amount to shift the address to get the set. */ + int setShift; + /** The amount to shift the address to get the tag. */ + int tagShift; + /** Mask out all bits that aren't part of the set index. */ + unsigned setMask; + /** Mask out all bits that aren't part of the block offset. */ + unsigned blkMask; + + + /** the number of hit in this partition */ + Stats::Scalar<> hits; + /** the number of blocks brought into this partition (i.e. misses) */ + Stats::Scalar<> misses; + /** the number of invalidations in this partition */ + Stats::Scalar<> invalidations; + +public: + /** + * Construct and initialize this tag store. + * @param _numSets The number of sets in the cache. + * @param _blkSize The number of bytes in a block. + * @param _assoc The associativity of the cache. + * @param _hit_latency The latency in cycles for a hit. + */ + SplitLIFO(int _blkSize, int _size, int _ways, int _hit_latency, bool twoQueue, int _part); + + /** + * Destructor + */ + virtual ~SplitLIFO(); + + /** + * Register the statistics for this object + * @param name The name to precede the stat + */ + void regStats(const std::string &name); + + /** + * Return the block size. + * @return the block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. In the case of LIFO it is always the block + * size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Search for the address in the cache. + * @param asid The address space ID. + * @param addr The address to find. + * @return True if the address is in the cache. + */ + bool probe(int asid, Addr addr) const; + + /** + * Invalidate the block containing the given address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(int asid, Addr addr); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr, int asid, int &lat); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param req The req whose block to find + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Packet * &pkt, int &lat); + + /** + * Finds the given address in the cache, do not update replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr, int asid) const; + + /** + * Find a replacement block for the address provided. + * @param req The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + SplitBlk* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @return The tag of the address. + */ + Addr extractTag(Addr addr) const + { + return (addr >> tagShift); + } + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @param blk Ignored + * @return The tag of the address. + */ + Addr extractTag(Addr addr, SplitBlk *blk) const + { + return (addr >> tagShift); + } + + /** + * Calculate the set index from the address. + * @param addr The address to get the set from. + * @return The set index of the address. + */ + int extractSet(Addr addr) const + { + return ((addr >> setShift) & setMask); + } + + /** + * Get the block offset from an address. + * @param addr The address to get the offset of. + * @return The block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)blkMask); + } + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set The set of the block. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, unsigned set) const + { + return ((tag << tagShift) | ((Addr)set << setShift)); + } + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Read the data out of the internal storage of the given cache block. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(SplitBlk *blk, uint8_t *data) + { + memcpy(data, blk->data, blk->size); + } + + /** + * Write data into the internal storage of the given cache block. Since in + * LIFO does not store data differently this just needs to update the size. + * @param blk The cache block to write. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(SplitBlk *blk, uint8_t *data, int size, + PacketList & writebacks) + { + assert(size <= blkSize); + blk->size = size; + } + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback requests. + */ + void doCopy(Addr source, Addr dest, int asid, PacketList &writebacks); + + /** + * No impl. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +}; + +#endif diff --git a/src/mem/cache/tags/split_lru.cc b/src/mem/cache/tags/split_lru.cc new file mode 100644 index 000000000..7fc742001 --- /dev/null +++ b/src/mem/cache/tags/split_lru.cc @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Definitions of LRU tag store for a partitioned cache. + */ + +#include <string> + +#include "mem/cache/base_cache.hh" +#include "base/intmath.hh" +#include "mem/cache/tags/split_lru.hh" +#include "sim/root.hh" + +using namespace std; + +SplitBlk* +SplitCacheSet::findBlk(int asid, Addr tag) const +{ + for (int i = 0; i < assoc; ++i) { + if (blks[i]->tag == tag && blks[i]->isValid()) { + return blks[i]; + } + } + return 0; +} + + +void +SplitCacheSet::moveToHead(SplitBlk *blk) +{ + // nothing to do if blk is already head + if (blks[0] == blk) + return; + + // write 'next' block into blks[i], moving up from MRU toward LRU + // until we overwrite the block we moved to head. + + // start by setting up to write 'blk' into blks[0] + int i = 0; + SplitBlk *next = blk; + + do { + assert(i < assoc); + // swap blks[i] and next + SplitBlk *tmp = blks[i]; + blks[i] = next; + next = tmp; + ++i; + } while (next != blk); +} + + +// create and initialize a LRU/MRU cache structure +SplitLRU::SplitLRU(int _numSets, int _blkSize, int _assoc, int _hit_latency, int _part) : + numSets(_numSets), blkSize(_blkSize), assoc(_assoc), hitLatency(_hit_latency), part(_part) +{ + // Check parameters + if (blkSize < 4 || !isPowerOf2(blkSize)) { + fatal("Block size must be at least 4 and a power of 2"); + } + if (numSets <= 0 || !isPowerOf2(numSets)) { + fatal("# of sets must be non-zero and a power of 2"); + } + if (assoc <= 0) { + fatal("associativity must be greater than zero"); + } + if (hitLatency <= 0) { + fatal("access latency must be greater than zero"); + } + + SplitBlk *blk; + int i, j, blkIndex; + + blkMask = blkSize - 1; + setShift = floorLog2(blkSize); + setMask = numSets - 1; + tagShift = setShift + floorLog2(numSets); + warmedUp = false; + /** @todo Make warmup percentage a parameter. */ + warmupBound = numSets * assoc; + + sets = new SplitCacheSet[numSets]; + blks = new SplitBlk[numSets * assoc]; + // allocate data storage in one big chunk + dataBlks = new uint8_t[numSets*assoc*blkSize]; + + blkIndex = 0; // index into blks array + for (i = 0; i < numSets; ++i) { + sets[i].assoc = assoc; + + sets[i].blks = new SplitBlk*[assoc]; + + // link in the data blocks + for (j = 0; j < assoc; ++j) { + // locate next cache block + blk = &blks[blkIndex]; + blk->data = &dataBlks[blkSize*blkIndex]; + ++blkIndex; + + // invalidate new cache block + blk->status = 0; + + //EGH Fix Me : do we need to initialize blk? + + // Setting the tag to j is just to prevent long chains in the hash + // table; won't matter because the block is invalid + blk->tag = j; + blk->whenReady = 0; + blk->asid = -1; + blk->isTouched = false; + blk->size = blkSize; + sets[i].blks[j]=blk; + blk->set = i; + blk->part = part; + } + } +} + +SplitLRU::~SplitLRU() +{ + delete [] dataBlks; + delete [] blks; + delete [] sets; +} + +void +SplitLRU::regStats(const std::string &name) +{ + BaseTags::regStats(name); + + hits + .name(name + ".hits") + .desc("number of hits on this partition") + .precision(0) + ; + + misses + .name(name + ".misses") + .desc("number of misses in this partition") + .precision(0) + ; +} + +// probe cache for presence of given block. +bool +SplitLRU::probe(int asid, Addr addr) const +{ + // return(findBlock(Read, addr, asid) != 0); + Addr tag = extractTag(addr); + unsigned myset = extractSet(addr); + + SplitBlk *blk = sets[myset].findBlk(asid, tag); + + return (blk != NULL); // true if in cache +} + +SplitBlk* +SplitLRU::findBlock(Addr addr, int asid, int &lat) +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(asid, tag); + lat = hitLatency; + if (blk != NULL) { + // move this block to head of the MRU list + sets[set].moveToHead(blk); + if (blk->whenReady > curTick && blk->whenReady - curTick > hitLatency){ + lat = blk->whenReady - curTick; + } + blk->refCount += 1; + hits++; + } + + return blk; +} + +SplitBlk* +SplitLRU::findBlock(Packet * &pkt, int &lat) +{ + Addr addr = pkt->getAddr(); + int asid = pkt->req->getAsid(); + + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(asid, tag); + lat = hitLatency; + if (blk != NULL) { + // move this block to head of the MRU list + sets[set].moveToHead(blk); + if (blk->whenReady > curTick && blk->whenReady - curTick > hitLatency){ + lat = blk->whenReady - curTick; + } + blk->refCount += 1; + hits++; + } + + return blk; +} + +SplitBlk* +SplitLRU::findBlock(Addr addr, int asid) const +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(asid, tag); + return blk; +} + +SplitBlk* +SplitLRU::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + unsigned set = extractSet(pkt->getAddr()); + // grab a replacement candidate + SplitBlk *blk = sets[set].blks[assoc-1]; + sets[set].moveToHead(blk); + if (blk->isValid()) { + replacements[0]++; + totalRefs += blk->refCount; + ++sampledRefs; + blk->refCount = 0; + } else if (!blk->isTouched) { + tagsInUse++; + blk->isTouched = true; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + } + + misses++; + + return blk; +} + +void +SplitLRU::invalidateBlk(int asid, Addr addr) +{ + SplitBlk *blk = findBlock(addr, asid); + if (blk) { + blk->status = 0; + blk->isTouched = false; + tagsInUse--; + } +} + +void +SplitLRU::doCopy(Addr source, Addr dest, int asid, PacketList &writebacks) +{ +//Copy not supported for now +#if 0 + assert(source == blkAlign(source)); + assert(dest == blkAlign(dest)); + SplitBlk *source_blk = findBlock(source, asid); + assert(source_blk); + SplitBlk *dest_blk = findBlock(dest, asid); + if (dest_blk == NULL) { + // Need to do a replacement + Packet * pkt = new Packet(); + pkt->paddr = dest; + BlkList dummy_list; + dest_blk = findReplacement(pkt, writebacks, dummy_list); + if (dest_blk->isValid() && dest_blk->isModified()) { + // Need to writeback data. + pkt = buildWritebackReq(regenerateBlkAddr(dest_blk->tag, + dest_blk->set), + dest_blk->req->asid, + dest_blk->xc, + blkSize, + (cache->doData())?dest_blk->data:0, + dest_blk->size); + writebacks.push_back(pkt); + } + dest_blk->tag = extractTag(dest); + dest_blk->req->asid = asid; + /** + * @todo Do we need to pass in the execution context, or can we + * assume its the same? + */ + assert(source_blk->xc); + dest_blk->xc = source_blk->xc; + } + /** + * @todo Can't assume the status once we have coherence on copies. + */ + + // Set this block as readable, writeable, and dirty. + dest_blk->status = 7; + if (cache->doData()) { + memcpy(dest_blk->data, source_blk->data, blkSize); + } +#endif +} + +void +SplitLRU::cleanupRefs() +{ + for (int i = 0; i < numSets*assoc; ++i) { + if (blks[i].isValid()) { + totalRefs += blks[i].refCount; + ++sampledRefs; + } + } +} diff --git a/src/mem/cache/tags/split_lru.hh b/src/mem/cache/tags/split_lru.hh new file mode 100644 index 000000000..03886b1d8 --- /dev/null +++ b/src/mem/cache/tags/split_lru.hh @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Declaration of a LRU tag store for a partitioned cache. + */ + +#ifndef __SPLIT_LRU_HH__ +#define __SPLIT_LRU_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" // base class +#include "mem/cache/tags/split_blk.hh" +#include "mem/packet.hh" // for inlined functions +#include <assert.h> +#include "mem/cache/tags/base_tags.hh" + +class BaseCache; + +/** + * An associative set of cache blocks. + */ + +class SplitCacheSet +{ + public: + /** The associativity of this set. */ + int assoc; + + /** Cache blocks in this set, maintained in LRU order 0 = MRU. */ + SplitBlk **blks; + + /** + * Find a block matching the tag in this set. + * @param asid The address space ID. + * @param tag The Tag to find. + * @return Pointer to the block if found. + */ + SplitBlk* findBlk(int asid, Addr tag) const; + + /** + * Move the given block to the head of the list. + * @param blk The block to move. + */ + void moveToHead(SplitBlk *blk); +}; + +/** + * A LRU cache tag store. + */ +class SplitLRU : public BaseTags +{ + public: + /** Typedef the block type used in this tag store. */ + typedef SplitBlk BlkType; + /** Typedef for a list of pointers to the local block class. */ + typedef std::list<SplitBlk*> BlkList; + protected: + /** The number of sets in the cache. */ + const int numSets; + /** The number of bytes in a block. */ + const int blkSize; + /** The associativity of the cache. */ + const int assoc; + /** The hit latency. */ + const int hitLatency; + /** indicator for which partition this is */ + const int part; + + /** The cache sets. */ + SplitCacheSet *sets; + + /** The cache blocks. */ + SplitBlk *blks; + /** The data blocks, 1 per cache block. */ + uint8_t *dataBlks; + + /** The amount to shift the address to get the set. */ + int setShift; + /** The amount to shift the address to get the tag. */ + int tagShift; + /** Mask out all bits that aren't part of the set index. */ + unsigned setMask; + /** Mask out all bits that aren't part of the block offset. */ + unsigned blkMask; + + /** number of hits in this partition */ + Stats::Scalar<> hits; + /** number of blocks brought into this partition (i.e. misses) */ + Stats::Scalar<> misses; + +public: + /** + * Construct and initialize this tag store. + * @param _numSets The number of sets in the cache. + * @param _blkSize The number of bytes in a block. + * @param _assoc The associativity of the cache. + * @param _hit_latency The latency in cycles for a hit. + */ + SplitLRU(int _numSets, int _blkSize, int _assoc, int _hit_latency, int _part); + + /** + * Destructor + */ + virtual ~SplitLRU(); + + /** + * Register the statistics for this object + * @param name The name to precede the stat + */ + void regStats(const std::string &name); + + /** + * Return the block size. + * @return the block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. In the case of LRU it is always the block + * size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Search for the address in the cache. + * @param asid The address space ID. + * @param addr The address to find. + * @return True if the address is in the cache. + */ + bool probe(int asid, Addr addr) const; + + /** + * Invalidate the block containing the given address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(int asid, Addr addr); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr, int asid, int &lat); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param req The req whose block to find. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Packet * &pkt, int &lat); + + /** + * Finds the given address in the cache, do not update replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr, int asid) const; + + /** + * Find a replacement block for the address provided. + * @param req The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + SplitBlk* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @return The tag of the address. + */ + Addr extractTag(Addr addr) const + { + return (addr >> tagShift); + } + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @param blk Ignored. + * @return The tag of the address. + */ + Addr extractTag(Addr addr, SplitBlk *blk) const + { + return (addr >> tagShift); + } + + /** + * Calculate the set index from the address. + * @param addr The address to get the set from. + * @return The set index of the address. + */ + int extractSet(Addr addr) const + { + return ((addr >> setShift) & setMask); + } + + /** + * Get the block offset from an address. + * @param addr The address to get the offset of. + * @return The block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)blkMask); + } + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set The set of the block. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, unsigned set) const + { + return ((tag << tagShift) | ((Addr)set << setShift)); + } + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Read the data out of the internal storage of the given cache block. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(SplitBlk *blk, uint8_t *data) + { + memcpy(data, blk->data, blk->size); + } + + /** + * Write data into the internal storage of the given cache block. Since in + * LRU does not store data differently this just needs to update the size. + * @param blk The cache block to write. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(SplitBlk *blk, uint8_t *data, int size, + PacketList & writebacks) + { + assert(size <= blkSize); + blk->size = size; + } + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback requests. + */ + void doCopy(Addr source, Addr dest, int asid, PacketList &writebacks); + + /** + * No impl. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +}; + +#endif diff --git a/src/mem/config/cache.hh b/src/mem/config/cache.hh new file mode 100644 index 000000000..24da04021 --- /dev/null +++ b/src/mem/config/cache.hh @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Nathan Binkert + */ + +/** + * @file + * Central location to configure which cache types we want to build + * into the simulator. In the future, this should probably be + * autogenerated by some sort of configuration script. + */ +#define USE_CACHE_LRU 1 +#define USE_CACHE_FALRU 1 +// #define USE_CACHE_SPLIT 1 +// #define USE_CACHE_SPLIT_LIFO 1 +#define USE_CACHE_IIC 1 + diff --git a/src/mem/config/prefetch.hh b/src/mem/config/prefetch.hh index 03eb570f0..d24db79da 100644 --- a/src/mem/config/prefetch.hh +++ b/src/mem/config/prefetch.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ron Dreslinski */ /** diff --git a/src/mem/mem_object.cc b/src/mem/mem_object.cc index f579a0727..d4d3fd283 100644 --- a/src/mem/mem_object.cc +++ b/src/mem/mem_object.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #include "mem/mem_object.hh" diff --git a/src/mem/mem_object.hh b/src/mem/mem_object.hh index 58930eccc..c81ea03d8 100644 --- a/src/mem/mem_object.hh +++ b/src/mem/mem_object.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ron Dreslinski */ /** @@ -48,7 +50,7 @@ class MemObject : public SimObject public: /** Additional function to return the Port of a memory object. */ - virtual Port *getPort(const std::string &if_name) = 0; + virtual Port *getPort(const std::string &if_name, int idx = -1) = 0; }; #endif //__MEM_MEM_OBJECT_HH__ diff --git a/src/mem/packet.cc b/src/mem/packet.cc index 3b415d77f..91298df8c 100644 --- a/src/mem/packet.cc +++ b/src/mem/packet.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Steve Reinhardt */ /** @@ -54,6 +57,19 @@ Packet::cmdString() const } } +const std::string & +Packet::cmdIdxToString(Packet::Command idx) +{ + switch (idx) { + case ReadReq: return ReadReqString; + case WriteReq: return WriteReqString; + case WriteReqNoAck: return WriteReqNoAckString; + case ReadResp: return ReadRespString; + case WriteResp: return WriteRespString; + default: return OtherCmdString; + } +} + /** delete the data pointed to in the data pointer. Ok to call to matter how * data was allocted. */ void @@ -97,20 +113,6 @@ Packet::intersect(Packet *p) return false; } -/** Minimally reset a packet so something like simple cpu can reuse it. */ -void -Packet::reset() -{ - result = Unknown; - if (dynamicData) { - deleteData(); - dynamicData = false; - arrayData = false; - time = curTick; - } -} - - bool fixPacket(Packet *func, Packet *timing) { diff --git a/src/mem/packet.hh b/src/mem/packet.hh index 83f52ede5..2b97ab0c1 100644 --- a/src/mem/packet.hh +++ b/src/mem/packet.hh @@ -24,12 +24,15 @@ * 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. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + * Ali Saidi */ /** * @file - * Declaration of the Packet Class, a packet is a transaction occuring - * between a single level of the memory heirarchy (ie L1->L2). + * Declaration of the Packet class. */ #ifndef __MEM_PACKET_HH__ @@ -38,87 +41,124 @@ #include "mem/request.hh" #include "arch/isa_traits.hh" #include "sim/root.hh" +#include <list> struct Packet; typedef Packet* PacketPtr; typedef uint8_t* PacketDataPtr; +typedef std::list<PacketPtr> PacketList; + +//Coherence Flags +#define NACKED_LINE 1 << 0 +#define SATISFIED 1 << 1 +#define SHARED_LINE 1 << 2 +#define CACHE_LINE_FILL 1 << 3 +#define COMPRESSED 1 << 4 +#define NO_ALLOCATE 1 << 5 + +//For statistics we need max number of commands, hard code it at +//20 for now. @todo fix later +#define NUM_MEM_CMDS 1 << 9 /** - * A Packet is the structure to handle requests between two levels - * of the memory system. The Request is a global object that trancends - * all of the memory heirarchy, but at each levels interface a packet - * is created to transfer data/requests. For example, a request would - * be used to initiate a request to go to memory/IOdevices, as the request - * passes through the memory system several packets will be created. One - * will be created to go between the L1 and L2 caches and another to go to - * the next level and so forth. - * - * Packets are assumed to be returned in the case of a single response. If - * the transaction has no response, then the consumer will delete the packet. + * A Packet is used to encapsulate a transfer between two objects in + * the memory system (e.g., the L1 and L2 cache). (In contrast, a + * single Request travels all the way from the requester to the + * ultimate destination and back, possibly being conveyed by several + * different Packets along the way.) */ class Packet { + public: + /** Temporary FLAGS field until cache gets working, this should be in coherence/sender state. */ + uint64_t flags; + private: - /** A pointer to the data being transfered. It can be differnt sizes - at each level of the heirarchy so it belongs in the packet, - not request. This may or may not be populated when a responder recieves - the packet. If not populated it memory should be allocated. + /** A pointer to the data being transfered. It can be differnt + * sizes at each level of the heirarchy so it belongs in the + * packet, not request. This may or may not be populated when a + * responder recieves the packet. If not populated it memory + * should be allocated. */ PacketDataPtr data; - /** Is the data pointer set to a value that shouldn't be freed when the - * packet is destroyed? */ + /** Is the data pointer set to a value that shouldn't be freed + * when the packet is destroyed? */ bool staticData; - /** The data pointer points to a value that should be freed when the packet - * is destroyed. */ + /** The data pointer points to a value that should be freed when + * the packet is destroyed. */ bool dynamicData; - /** the data pointer points to an array (thus delete [] ) needs to be called - * on it rather than simply delete.*/ + /** the data pointer points to an array (thus delete [] ) needs to + * be called on it rather than simply delete.*/ bool arrayData; - /** The address of the request, could be virtual or physical (depending on - cache configurations). */ + /** The address of the request. This address could be virtual or + * physical, depending on the system configuration. */ Addr addr; - /** Indicates the size of the request. */ + /** The size of the request or transfer. */ int size; - /** A index of the source of the transaction. */ + /** Device address (e.g., bus ID) of the source of the + * transaction. The source is not responsible for setting this + * field; it is set implicitly by the interconnect when the + * packet * is first sent. */ short src; - /** A index to the destination of the transaction. */ + /** Device address (e.g., bus ID) of the destination of the + * transaction. The special value Broadcast indicates that the + * packet should be routed based on its address. This field is + * initialized in the constructor and is thus always valid + * (unlike * addr, size, and src). */ short dest; - bool addrValid; - bool sizeValid; + /** Are the 'addr' and 'size' fields valid? */ + bool addrSizeValid; + /** Is the 'src' field valid? */ bool srcValid; + public: + /** Used to calculate latencies for each packet.*/ + Tick time; + + /** The special destination address indicating that the packet + * should be routed based on its address. */ static const short Broadcast = -1; - /** A pointer to the overall request. */ + /** A pointer to the original request. */ RequestPtr req; + /** A virtual base opaque structure used to hold coherence-related + * state. A specific subclass would be derived from this to + * carry state specific to a particular coherence protocol. */ class CoherenceState { public: virtual ~CoherenceState() {} }; - /** A virtual base opaque structure used to hold - coherence status messages. */ - CoherenceState *coherence; // virtual base opaque, - // assert(dynamic_cast<Foo>) etc. - + /** This packet's coherence state. Caches should use + * dynamic_cast<> to cast to the state appropriate for the + * system's coherence protocol. */ + CoherenceState *coherence; + + /** A virtual base opaque structure used to hold state associated + * with the packet but specific to the sending device (e.g., an + * MSHR). A pointer to this state is returned in the packet's + * response so that the sender can quickly look up the state + * needed to process it. A specific subclass would be derived + * from this to carry state specific to a particular sending + * device. */ class SenderState { public: virtual ~SenderState() {} }; - /** A virtual base opaque structure used to hold the senders state. */ - SenderState *senderState; // virtual base opaque, - // assert(dynamic_cast<Foo>) etc. + /** This packet's sender state. Devices should use dynamic_cast<> + * to cast to the state appropriate to the sender. */ + SenderState *senderState; private: /** List of command attributes. */ @@ -131,51 +171,69 @@ class Packet IsRequest = 1 << 4, IsResponse = 1 << 5, NeedsResponse = 1 << 6, + IsSWPrefetch = 1 << 7, + IsHWPrefetch = 1 << 8 }; public: /** List of all commands associated with a packet. */ enum Command { + InvalidCmd = 0, ReadReq = IsRead | IsRequest | NeedsResponse, WriteReq = IsWrite | IsRequest | NeedsResponse, WriteReqNoAck = IsWrite | IsRequest, ReadResp = IsRead | IsResponse, - WriteResp = IsWrite | IsResponse + WriteResp = IsWrite | IsResponse, + Writeback = IsWrite | IsRequest, + SoftPFReq = IsRead | IsRequest | IsSWPrefetch | NeedsResponse, + HardPFReq = IsRead | IsRequest | IsHWPrefetch | NeedsResponse, + SoftPFResp = IsRead | IsRequest | IsSWPrefetch | IsResponse, + HardPFResp = IsRead | IsRequest | IsHWPrefetch | IsResponse, + InvalidateReq = IsInvalidate | IsRequest, + WriteInvalidateReq = IsWrite | IsInvalidate | IsRequest, + UpgradeReq = IsInvalidate | NeedsResponse, + UpgradeResp = IsInvalidate | IsResponse, + ReadExReq = IsRead | IsInvalidate | NeedsResponse, + ReadExResp = IsRead | IsInvalidate | IsResponse }; + /** Return the string name of the cmd field (for debugging and + * tracing). */ const std::string &cmdString() const; - /** The command of the transaction. */ + /** Reutrn the string to a cmd given by idx. */ + const std::string &cmdIdxToString(Command idx); + + /** Return the index of this command. */ + inline int cmdToIndex() const { return (int) cmd; } + + /** The command field of the packet. */ Command cmd; bool isRead() { return (cmd & IsRead) != 0; } + bool isWrite() { return (cmd & IsWrite) != 0; } bool isRequest() { return (cmd & IsRequest) != 0; } bool isResponse() { return (cmd & IsResponse) != 0; } bool needsResponse() { return (cmd & NeedsResponse) != 0; } + bool isInvalidate() { return (cmd * IsInvalidate) != 0; } - void makeTimingResponse() { - assert(needsResponse()); - int icmd = (int)cmd; - icmd &= ~(IsRequest | NeedsResponse); - icmd |= IsResponse; - cmd = (Command)icmd; - dest = src; - srcValid = false; - } + bool isCacheFill() { return (flags & CACHE_LINE_FILL) != 0; } + bool isNoAllocate() { return (flags & NO_ALLOCATE) != 0; } + bool isCompressed() { return (flags & COMPRESSED) != 0; } - /** The time this request was responded to. Used to calculate latencies. */ - Tick time; + bool nic_pkt() { assert("Unimplemented\n" && 0); } - /** The result of a particular packets request. */ + /** Possible results of a packet's request. */ enum Result { Success, BadAddress, + Nacked, Unknown }; - /** The result of the packet transaction. */ + /** The result of this packet's request. */ Result result; /** Accessor function that returns the source index of the packet. */ @@ -187,35 +245,90 @@ class Packet short getDest() const { return dest; } void setDest(short _dest) { dest = _dest; } - Addr getAddr() const { assert(addrValid); return addr; } - void setAddr(Addr _addr) { addr = _addr; addrValid = true; } - - int getSize() const { assert(sizeValid); return size; } - void setSize(int _size) { size = _size; sizeValid = true; } + Addr getAddr() const { assert(addrSizeValid); return addr; } + int getSize() const { assert(addrSizeValid); return size; } + Addr getOffset(int blkSize) const { return req->getPaddr() & (Addr)(blkSize - 1); } + void addrOverride(Addr newAddr) { assert(addrSizeValid); addr = newAddr; } + void cmdOverride(Command newCmd) { cmd = newCmd; } + /** Constructor. Note that a Request object must be constructed + * first, but the Requests's physical address and size fields + * need not be valid. The command and destination addresses + * must be supplied. */ Packet(Request *_req, Command _cmd, short _dest) : data(NULL), staticData(false), dynamicData(false), arrayData(false), addr(_req->paddr), size(_req->size), dest(_dest), - addrValid(_req->validPaddr), sizeValid(_req->validSize), + addrSizeValid(_req->validPaddr), srcValid(false), req(_req), coherence(NULL), senderState(NULL), cmd(_cmd), - time(curTick), result(Unknown) + result(Unknown) + { + flags = 0; + } + + /** Alternate constructor if you are trying to create a packet with + * a request that is for a whole block, not the address from the req. + * this allows for overriding the size/addr of the req.*/ + Packet(Request *_req, Command _cmd, short _dest, int _blkSize) + : data(NULL), staticData(false), dynamicData(false), arrayData(false), + addr(_req->paddr & ~(_blkSize - 1)), size(_blkSize), + dest(_dest), + addrSizeValid(_req->validPaddr), srcValid(false), + req(_req), coherence(NULL), senderState(NULL), cmd(_cmd), + result(Unknown) { + flags = 0; } + /** Destructor. */ ~Packet() { deleteData(); } + /** Reinitialize packet address and size from the associated + * Request object, and reset other fields that may have been + * modified by a previous transaction. Typically called when a + * statically allocated Request/Packet pair is reused for + * multiple transactions. */ + void reinitFromRequest() { + assert(req->validPaddr); + addr = req->paddr; + size = req->size; + addrSizeValid = true; + result = Unknown; + if (dynamicData) { + deleteData(); + dynamicData = false; + arrayData = false; + } + } - /** Minimally reset a packet so something like simple cpu can reuse it. */ - void reset(); + /** Take a request packet and modify it in place to be suitable + * for returning as a response to that request. Used for timing + * accesses only. For atomic and functional accesses, the + * request packet is always implicitly passed back *without* + * modifying the command or destination fields, so this function + * should not be called. */ + void makeTimingResponse() { + assert(needsResponse()); + int icmd = (int)cmd; + icmd &= ~(IsRequest | NeedsResponse); + icmd |= IsResponse; + cmd = (Command)icmd; + dest = src; + srcValid = false; + } - void reinitFromRequest() { - if (req->validPaddr) setAddr(req->paddr); - if (req->validSize) setSize(req->size); + /** Take a request packet that has been returned as NACKED and modify it so + * that it can be sent out again. Only packets that need a response can be + * NACKED, so verify that that is true. */ + void reinitNacked() { + assert(needsResponse() && result == Nacked); + dest = Broadcast; + result = Unknown; } + /** Set the data pointer to the following value that should not be freed. */ template <typename T> void dataStatic(T *p); diff --git a/src/mem/page_table.cc b/src/mem/page_table.cc index c4e1ea193..a34a0393a 100644 --- a/src/mem/page_table.cc +++ b/src/mem/page_table.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Ron Dreslinski */ /** @@ -51,6 +54,9 @@ PageTable::PageTable(System *_system, Addr _pageSize) system(_system) { assert(isPowerOf2(pageSize)); + pTableCache[0].vaddr = 0; + pTableCache[1].vaddr = 0; + pTableCache[2].vaddr = 0; } PageTable::~PageTable() @@ -92,7 +98,7 @@ PageTable::allocate(Addr vaddr, int size) assert(pageOffset(vaddr) == 0); for (; size > 0; size -= pageSize, vaddr += pageSize) { - std::map<Addr,Addr>::iterator iter = pTable.find(vaddr); + m5::hash_map<Addr,Addr>::iterator iter = pTable.find(vaddr); if (iter != pTable.end()) { // already mapped @@ -100,6 +106,12 @@ PageTable::allocate(Addr vaddr, int size) } pTable[vaddr] = system->new_page(); + pTableCache[2].paddr = pTableCache[1].paddr; + pTableCache[2].vaddr = pTableCache[1].vaddr; + pTableCache[1].paddr = pTableCache[0].paddr; + pTableCache[1].vaddr = pTableCache[0].vaddr; + pTableCache[0].paddr = pTable[vaddr]; + pTableCache[0].vaddr = vaddr; } } @@ -109,7 +121,22 @@ bool PageTable::translate(Addr vaddr, Addr &paddr) { Addr page_addr = pageAlign(vaddr); - std::map<Addr,Addr>::iterator iter = pTable.find(page_addr); + paddr = 0; + + if (pTableCache[0].vaddr == vaddr) { + paddr = pTableCache[0].paddr; + return true; + } + if (pTableCache[1].vaddr == vaddr) { + paddr = pTableCache[1].paddr; + return true; + } + if (pTableCache[2].vaddr == vaddr) { + paddr = pTableCache[2].paddr; + return true; + } + + m5::hash_map<Addr,Addr>::iterator iter = pTable.find(page_addr); if (iter == pTable.end()) { return false; @@ -127,7 +154,7 @@ PageTable::translate(RequestPtr &req) assert(pageAlign(req->getVaddr() + req->getSize() - 1) == pageAlign(req->getVaddr())); if (!translate(req->getVaddr(), paddr)) { - return genMachineCheckFault(); + return genPageTableFault(req->getVaddr()); } req->setPaddr(paddr); return page_check(req->getPaddr(), req->getSize()); diff --git a/src/mem/page_table.hh b/src/mem/page_table.hh index 26248261a..494c0ce9a 100644 --- a/src/mem/page_table.hh +++ b/src/mem/page_table.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ /** @@ -35,9 +37,9 @@ #define __PAGE_TABLE__ #include <string> -#include <map> #include "arch/isa_traits.hh" +#include "base/hashmap.hh" #include "base/trace.hh" #include "mem/request.hh" #include "mem/packet.hh" @@ -51,7 +53,14 @@ class System; class PageTable { protected: - std::map<Addr,Addr> pTable; + m5::hash_map<Addr,Addr> pTable; + + struct cacheElement { + Addr paddr; + Addr vaddr; + } ; + + struct cacheElement pTableCache[3]; const Addr pageSize; const Addr offsetMask; diff --git a/src/mem/physical.cc b/src/mem/physical.cc index 26dbef0cd..2d66602ab 100644 --- a/src/mem/physical.cc +++ b/src/mem/physical.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ron Dreslinski */ #include <sys/types.h> @@ -139,8 +141,7 @@ Tick PhysicalMemory::doAtomicAccess(Packet *pkt) { doFunctionalAccess(pkt); - pkt->time = curTick + lat; - return curTick + lat; + return lat; } void @@ -172,9 +173,9 @@ PhysicalMemory::doFunctionalAccess(Packet *pkt) } Port * -PhysicalMemory::getPort(const std::string &if_name) +PhysicalMemory::getPort(const std::string &if_name, int idx) { - if (if_name == "") { + if (if_name == "port" && idx == -1) { if (port != NULL) panic("PhysicalMemory::getPort: additional port requested to memory!"); port = new MemoryPort(name() + "-port", this); diff --git a/src/mem/physical.hh b/src/mem/physical.hh index 2ced79045..50fa75ed3 100644 --- a/src/mem/physical.hh +++ b/src/mem/physical.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ron Dreslinski */ /* @file @@ -106,7 +108,7 @@ class PhysicalMemory : public MemObject public: int deviceBlockSize(); void getAddressRanges(AddrRangeList &resp, AddrRangeList &snoop); - virtual Port *getPort(const std::string &if_name); + virtual Port *getPort(const std::string &if_name, int idx = -1); void virtual init(); // fast back-door memory access for vtophys(), remote gdb, etc. diff --git a/src/mem/port.cc b/src/mem/port.cc index 651cb739a..bec9d2274 100644 --- a/src/mem/port.cc +++ b/src/mem/port.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ /** @@ -45,13 +47,12 @@ Port::setPeer(Port *port) void Port::blobHelper(Addr addr, uint8_t *p, int size, Packet::Command cmd) { - Request req(false); + Request req; Packet pkt(&req, cmd, Packet::Broadcast); for (ChunkGenerator gen(addr, size, peerBlockSize()); !gen.done(); gen.next()) { - req.setPaddr(gen.addr()); - req.setSize(gen.size()); + req.setPhys(gen.addr(), gen.size(), 0); pkt.reinitFromRequest(); pkt.dataStatic(p); sendFunctional(&pkt); diff --git a/src/mem/port.hh b/src/mem/port.hh index f9103865e..17b1f4a00 100644 --- a/src/mem/port.hh +++ b/src/mem/port.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ron Dreslinski */ /** @@ -72,10 +74,19 @@ class Port private: /** Descriptive name (for DPRINTF output) */ - const std::string portName; + mutable std::string portName; + + /** A pointer to the peer port. Ports always come in pairs, that way they + can use a standardized interface to communicate between different + memory objects. */ + Port *peer; public: + Port() + : peer(NULL) + { } + /** * Constructor. * @@ -83,7 +94,7 @@ class Port * of memory system object to which the port belongs. */ Port(const std::string &_name) - : portName(_name) + : portName(_name), peer(NULL) { } /** Return port name (for DPRINTF). */ @@ -92,22 +103,14 @@ class Port virtual ~Port() {}; // mey be better to use subclasses & RTTI? - /** Holds the ports status. Keeps track if it is blocked, or has - calculated a range change. */ + /** Holds the ports status. Currently just that a range recomputation needs + * to be done. */ enum Status { - Blocked, - Unblocked, RangeChange }; - private: - - /** A pointer to the peer port. Ports always come in pairs, that way they - can use a standardized interface to communicate between different - memory objects. */ - Port *peer; - - public: + void setName(const std::string &name) + { portName = name; } /** Function to set the pointer for the peer port. @todo should be called by the configuration stuff (python). @@ -140,7 +143,7 @@ class Port wait. This shouldn't be valid for response paths (IO Devices). so it is set to panic if it isn't already defined. */ - virtual Packet *recvRetry() { panic("??"); } + virtual void recvRetry() { panic("??"); } /** Called by a peer port in order to determine the block size of the device connected to this port. It sometimes doesn't make sense for @@ -165,16 +168,17 @@ class Port port receive function. @return This function returns if the send was succesful in it's recieve. If it was a failure, then the port will wait for a recvRetry - at which point it can issue a successful sendTiming. This is used in + at which point it can possibly issue a successful sendTiming. This is used in case a cache has a higher priority request come in while waiting for the bus to arbitrate. */ bool sendTiming(Packet *pkt) { return peer->recvTiming(pkt); } - /** Function called by the associated device to send an atomic access, - an access in which the data is moved and the state is updated in one - cycle, without interleaving with other memory accesses. - */ + /** Function called by the associated device to send an atomic + * access, an access in which the data is moved and the state is + * updated in one cycle, without interleaving with other memory + * accesses. Returns estimated latency of access. + */ Tick sendAtomic(Packet *pkt) { return peer->recvAtomic(pkt); } @@ -194,7 +198,7 @@ class Port /** When a timing access doesn't return a success, some time later the Retry will be sent. */ - Packet *sendRetry() { return peer->recvRetry(); } + void sendRetry() { return peer->recvRetry(); } /** Called by the associated device if it wishes to find out the blocksize of the device on attached to the peer port. @@ -252,6 +256,14 @@ class FunctionalPort : public Port virtual void recvFunctional(Packet *pkt) { panic("FuncPort is UniDir"); } virtual void recvStatusChange(Status status) {} + /** a write function that also does an endian conversion. */ + template <typename T> + inline void writeHtoG(Addr addr, T d); + + /** a read function that also does an endian conversion. */ + template <typename T> + inline T readGtoH(Addr addr); + template <typename T> inline void write(Addr addr, T d) { diff --git a/src/mem/port_impl.hh b/src/mem/port_impl.hh new file mode 100644 index 000000000..e9a159293 --- /dev/null +++ b/src/mem/port_impl.hh @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + */ + +#include "arch/isa_specific.hh" +#include "arch/isa_traits.hh" +#include "mem/port.hh" +#include "sim/byteswap.hh" + +template <typename T> +void +FunctionalPort::writeHtoG(Addr addr, T d) +{ + d = TheISA::htog(d); + writeBlob(addr, (uint8_t*)&d, sizeof(T)); +} + + +template <typename T> +T +FunctionalPort::readGtoH(Addr addr) +{ + T d; + readBlob(addr, (uint8_t*)&d, sizeof(T)); + return TheISA::gtoh(d); +} + diff --git a/src/mem/request.hh b/src/mem/request.hh index 10550e859..a1524f807 100644 --- a/src/mem/request.hh +++ b/src/mem/request.hh @@ -24,6 +24,10 @@ * 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. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + * Ali Saidi */ /** @@ -40,6 +44,7 @@ class Request; typedef Request* RequestPtr; + /** The request is a Load locked/store conditional. */ const unsigned LOCKED = 0x001; /** The virtual address is also the physical address. */ @@ -58,142 +63,174 @@ const unsigned PF_EXCLUSIVE = 0x100; const unsigned EVICT_NEXT = 0x200; /** The request should ignore unaligned access faults */ const unsigned NO_ALIGN_FAULT = 0x400; +/** The request was an instruction read. */ +const unsigned INST_READ = 0x800; class Request { - //@todo Make Accesor functions, make these private. - public: - /** Constructor, needs a bool to signify if it is/isn't Cpu Request. */ - Request(bool isCpu); - - /** reset the request to it's initial state so it can be reused.*/ - void resetAll(bool isCpu); - - /** reset the request's addrs times, etc, so but not everything to same - * time. */ - void resetMin(); - -//First non-cpu request fields private: - /** The physical address of the request. */ + /** + * The physical address of the request. Valid only if validPaddr + * is set. */ Addr paddr; - /** Wether or not paddr is valid (has been written yet). */ - bool validPaddr; - /** The size of the request. */ + /** + * The size of the request. This field must be set when vaddr or + * paddr is written via setVirt() or setPhys(), so it is always + * valid as long as one of the address fields is valid. */ int size; - /** Wether or not size is valid (has been written yet). */ - bool validSize; - - /** The time this request was started. Used to calculate latencies. */ - Tick time; - /** Wether or not time is valid (has been written yet). */ - bool validTime; - - /** Destination address if this is a block copy. */ - Addr copyDest; - /** Wether or not copyDest is valid (has been written yet). */ - bool validCopyDest; /** Flag structure for the request. */ uint32_t flags; -//Accsesors for non-cpu request fields - public: - /** Accesor for paddr. */ - Addr getPaddr(); - /** Accesor for paddr. */ - void setPaddr(Addr _paddr); - - /** Accesor for size. */ - int getSize(); - /** Accesor for size. */ - void setSize(int _size); - - /** Accesor for time. */ - Tick getTime(); - /** Accesor for time. */ - void setTime(Tick _time); - - /** Accesor for copy dest. */ - Addr getCopyDest(); - /** Accesor for copy dest. */ - void setCopyDest(Addr _copyDest); - - /** Accesor for flags. */ - uint32_t getFlags(); - /** Accesor for paddr. */ - void setFlags(uint32_t _flags); - -//Now cpu-request fields - private: - /** Bool to signify if this is a cpuRequest. */ - bool cpuReq; - - /** The virtual address of the request. */ - Addr vaddr; - /** Wether or not the vaddr is valid. */ - bool validVaddr; + /** + * The time this request was started. Used to calculate + * latencies. This field is set to curTick any time paddr or vaddr + * is written. */ + Tick time; /** The address space ID. */ int asid; - /** Wether or not the asid is valid. */ - bool validAsid; + /** The virtual address of the request. */ + Addr vaddr; /** The return value of store conditional. */ uint64_t scResult; - /** Wether or not the sc result is valid. */ - bool validScResult; - /** The cpu number for statistics. */ + /** The cpu number (for statistics, typically). */ int cpuNum; - /** Wether or not the cpu number is valid. */ - bool validCpuNum; - - /** The requesting thread id. */ + /** The requesting thread id (for statistics, typically). */ int threadNum; - /** Wether or not the thread id is valid. */ - bool validThreadNum; /** program counter of initiating access; for tracing/debugging */ Addr pc; - /** Wether or not the pc is valid. */ + + /** Whether or not paddr is valid (has been written yet). */ + bool validPaddr; + /** Whether or not the asid & vaddr are valid. */ + bool validAsidVaddr; + /** Whether or not the sc result is valid. */ + bool validScResult; + /** Whether or not the cpu number & thread ID are valid. */ + bool validCpuAndThreadNums; + /** Whether or not the pc is valid. */ bool validPC; -//Accessor Functions for cpu request fields public: - /** Accesor function to determine if this is a cpu request or not.*/ - bool isCpuRequest(); - - /** Accesor function for vaddr.*/ - Addr getVaddr(); - /** Accesor function for vaddr.*/ - void setVaddr(Addr _vaddr); - - /** Accesor function for asid.*/ - int getAsid(); - /** Accesor function for asid.*/ - void setAsid(int _asid); - - /** Accesor function for store conditional return value.*/ - uint64_t getScResult(); - /** Accesor function for store conditional return value.*/ - void setScResult(uint64_t _scResult); - - /** Accesor function for cpu number.*/ - int getCpuNum(); - /** Accesor function for cpu number.*/ - void setCpuNum(int _cpuNum); - - /** Accesor function for thread number.*/ - int getThreadNum(); - /** Accesor function for thread number.*/ - void setThreadNum(int _threadNum); - - /** Accesor function for pc.*/ - Addr getPC(); - /** Accesor function for pc.*/ - void setPC(Addr _pc); + /** Minimal constructor. No fields are initialized. */ + Request() + : validPaddr(false), validAsidVaddr(false), + validScResult(false), validCpuAndThreadNums(false), validPC(false) + {} + + /** + * Constructor for physical (e.g. device) requests. Initializes + * just physical address, size, flags, and timestamp (to curTick). + * These fields are adequate to perform a request. */ + Request(Addr _paddr, int _size, int _flags) + : validCpuAndThreadNums(false) + { setPhys(_paddr, _size, _flags); } + + Request(int _asid, Addr _vaddr, int _size, int _flags, Addr _pc, + int _cpuNum, int _threadNum) + { + setThreadContext(_cpuNum, _threadNum); + setVirt(_asid, _vaddr, _size, _flags, _pc); + } + + /** + * Set up CPU and thread numbers. */ + void setThreadContext(int _cpuNum, int _threadNum) + { + cpuNum = _cpuNum; + threadNum = _threadNum; + validCpuAndThreadNums = true; + } + + /** + * Set up a physical (e.g. device) request in a previously + * allocated Request object. */ + void setPhys(Addr _paddr, int _size, int _flags) + { + paddr = _paddr; + size = _size; + flags = _flags; + time = curTick; + validPaddr = true; + validAsidVaddr = false; + validPC = false; + validScResult = false; + } + + /** + * Set up a virtual (e.g., CPU) request in a previously + * allocated Request object. */ + void setVirt(int _asid, Addr _vaddr, int _size, int _flags, Addr _pc) + { + asid = _asid; + vaddr = _vaddr; + size = _size; + flags = _flags; + pc = _pc; + time = curTick; + validPaddr = false; + validAsidVaddr = true; + validPC = true; + validScResult = false; + } + + /** Set just the physical address. This should only be used to + * record the result of a translation, and thus the vaddr must be + * valid before this method is called. Otherwise, use setPhys() + * to guarantee that the size and flags are also set. + */ + void setPaddr(Addr _paddr) + { + assert(validAsidVaddr); + paddr = _paddr; + validPaddr = true; + } + + /** Accessor for paddr. */ + Addr getPaddr() { assert(validPaddr); return paddr; } + + /** Accessor for size. */ + int getSize() { assert(validPaddr || validAsidVaddr); return size; } + /** Accessor for time. */ + Tick getTime() { assert(validPaddr || validAsidVaddr); return time; } + + /** Accessor for flags. */ + uint32_t getFlags() { assert(validPaddr || validAsidVaddr); return flags; } + /** Accessor for paddr. */ + void setFlags(uint32_t _flags) + { assert(validPaddr || validAsidVaddr); flags = _flags; } + + /** Accessor function for vaddr.*/ + Addr getVaddr() { assert(validAsidVaddr); return vaddr; } + + /** Accessor function for asid.*/ + int getAsid() { assert(validAsidVaddr); return asid; } + + /** Accessor function to check if sc result is valid. */ + bool scResultValid() { return validScResult; } + /** Accessor function for store conditional return value.*/ + uint64_t getScResult() { assert(validScResult); return scResult; } + /** Accessor function for store conditional return value.*/ + void setScResult(uint64_t _scResult) + { scResult = _scResult; validScResult = true; } + + /** Accessor function for cpu number.*/ + int getCpuNum() { assert(validCpuAndThreadNums); return cpuNum; } + /** Accessor function for thread number.*/ + int getThreadNum() { assert(validCpuAndThreadNums); return threadNum; } + + /** Accessor function for pc.*/ + Addr getPC() { assert(validPC); return pc; } + + /** Accessor Function to Check Cacheability. */ + bool isUncacheable() { return getFlags() & UNCACHEABLE; } + + bool isInstRead() { return getFlags() & INST_READ; } friend class Packet; }; diff --git a/src/mem/translating_port.cc b/src/mem/translating_port.cc index ee4d277b6..d2c854086 100644 --- a/src/mem/translating_port.cc +++ b/src/mem/translating_port.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ron Dreslinski + * Steve Reinhardt */ #include <string> diff --git a/src/mem/translating_port.hh b/src/mem/translating_port.hh index d078158a3..7354278ba 100644 --- a/src/mem/translating_port.hh +++ b/src/mem/translating_port.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ron Dreslinski + * Ali Saidi */ #ifndef __MEM_TRANSLATING_PROT_HH__ diff --git a/src/mem/vport.cc b/src/mem/vport.cc index cc569acf3..cd297bb8e 100644 --- a/src/mem/vport.cc +++ b/src/mem/vport.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @@ -40,8 +42,8 @@ VirtualPort::readBlob(Addr addr, uint8_t *p, int size) for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); gen.next()) { - if (xc) - paddr = TheISA::vtophys(xc,gen.addr()); + if (tc) + paddr = TheISA::vtophys(tc,gen.addr()); else paddr = TheISA::vtophys(gen.addr()); @@ -57,8 +59,8 @@ VirtualPort::writeBlob(Addr addr, uint8_t *p, int size) for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); gen.next()) { - if (xc) - paddr = TheISA::vtophys(xc,gen.addr()); + if (tc) + paddr = TheISA::vtophys(tc,gen.addr()); else paddr = TheISA::vtophys(gen.addr()); diff --git a/src/mem/vport.hh b/src/mem/vport.hh index 0f3b1f09e..697c8e5f3 100644 --- a/src/mem/vport.hh +++ b/src/mem/vport.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ /** @@ -36,13 +38,13 @@ #ifndef __MEM_VPORT_HH__ #define __MEM_VPORT_HH__ -#include "mem/port.hh" +#include "mem/port_impl.hh" #include "config/full_system.hh" #include "arch/vtophys.hh" /** A class that translates a virtual address to a physical address and then - * calls the above read/write functions. If an execution context is provided the + * calls the above read/write functions. If a thread context is provided the * address can alway be translated, If not it can only be translated if it is a * simple address masking operation (such as alpha super page accesses). */ @@ -50,18 +52,19 @@ class VirtualPort : public FunctionalPort { private: - ExecContext *xc; + ThreadContext *tc; public: - VirtualPort(const std::string &_name, ExecContext *_xc = NULL) - : FunctionalPort(_name), xc(_xc) + VirtualPort(const std::string &_name, ThreadContext *_tc = NULL) + : FunctionalPort(_name), tc(_tc) {} - /** Return true if we have an exec context. This is used to prevent someone - * from accidently deleting the cpus statically allocated vport. - * @return true if an execution context isn't valid + /** Return true if we have an thread context. This is used to + * prevent someone from accidently deleting the cpus statically + * allocated vport. + * @return true if a thread context isn't valid */ - bool nullExecContext() { return xc != NULL; } + bool nullThreadContext() { return tc != NULL; } /** Version of readblob that translates virt->phys and deals * with page boundries. */ diff --git a/src/python/SConscript b/src/python/SConscript index 4407e403d..3a9def9a8 100644 --- a/src/python/SConscript +++ b/src/python/SConscript @@ -25,183 +25,82 @@ # 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. +# +# Authors: Steve Reinhardt +# Nathan Binkert import os, os.path, re, sys +from zipfile import PyZipFile -Import('env') +# handy function for path joins +def join(*args): + return os.path.normpath(os.path.join(*args)) -import scons_helper - -def WriteEmbeddedPyFile(target, source, path, name, ext, filename): - if isinstance(source, str): - source = file(source, 'r') - - if isinstance(target, str): - target = file(target, 'w') - - print >>target, "AddModule(%s, %s, %s, %s, '''\\" % \ - (`path`, `name`, `ext`, `filename`) - - for line in source: - line = line - # escape existing backslashes - line = line.replace('\\', '\\\\') - # escape existing triple quotes - line = line.replace("'''", r"\'\'\'") - - print >>target, line, - - print >>target, "''')" - print >>target - -def WriteCFile(target, source, name): - if isinstance(source, str): - source = file(source, 'r') - - if isinstance(target, str): - target = file(target, 'w') - - print >>target, 'const char %s_string[] = {' % name - - count = 0 - from array import array - try: - while True: - foo = array('B') - foo.fromfile(source, 10000) - l = [ str(i) for i in foo.tolist() ] - count += len(l) - for i in xrange(0,9999,20): - print >>target, ','.join(l[i:i+20]) + ',' - except EOFError: - l = [ str(i) for i in foo.tolist() ] - count += len(l) - for i in xrange(0,len(l),20): - print >>target, ','.join(l[i:i+20]) + ',' - print >>target, ','.join(l[i:]) + ',' - - print >>target, '};' - print >>target, 'const int %s_length = %d;' % (name, count) - print >>target - -def splitpath(path): - dir,file = os.path.split(path) - path = [] - assert(file) - while dir: - dir,base = os.path.split(dir) - path.insert(0, base) - return path, file - -def MakeEmbeddedPyFile(target, source, env): - target = file(str(target[0]), 'w') - - tree = {} - for src in source: - src = str(src) - path,pyfile = splitpath(src) - node = tree - for dir in path: - if not node.has_key(dir): - node[dir] = { } - node = node[dir] - - name,ext = pyfile.split('.') - if name == '__init__': - node['.hasinit'] = True - node[pyfile] = (src,name,ext,src) - - done = False - while not done: - done = True - for name,entry in tree.items(): - if not isinstance(entry, dict): continue - if entry.has_key('.hasinit'): continue - - done = False - del tree[name] - for key,val in entry.iteritems(): - if tree.has_key(key): - raise NameError, \ - "dir already has %s can't add it again" % key - tree[key] = val - - files = [] - def populate(node, path = []): - names = node.keys() - names.sort() - for name in names: - if name == '.hasinit': - continue - - entry = node[name] - if isinstance(entry, dict): - if not entry.has_key('.hasinit'): - raise NameError, 'package directory missing __init__.py' - populate(entry, path + [ name ]) - else: - pyfile,name,ext,filename = entry - files.append((pyfile, path, name, ext, filename)) - populate(tree) - - for pyfile, path, name, ext, filename in files: - WriteEmbeddedPyFile(target, pyfile, path, name, ext, filename) +Import('env') +# This SConscript is in charge of collecting .py files and generating +# a zip archive that is appended to the m5 binary. + +# List of files & directories to include in the zip file. To include +# a package, list only the root directory of the package, not any +# internal .py files (else they will get the path stripped off when +# they are imported into the zip file). +pyzip_files = [] + +# List of additional files on which the zip archive depends, but which +# are not included in pyzip_files... i.e. individual .py files within +# a package. +pyzip_dep_files = [] + +# Add the specified package to the zip archive. Adds the directory to +# pyzip_files and all included .py files to pyzip_dep_files. +def addPkg(pkgdir): + pyzip_files.append(pkgdir) + origdir = os.getcwd() + srcdir = join(Dir('.').srcnode().abspath, pkgdir) + os.chdir(srcdir) + for path, dirs, files in os.walk('.'): + for i,dir in enumerate(dirs): + if dir == 'SCCS': + del dirs[i] + break + + for f in files: + if f.endswith('.py'): + pyzip_dep_files.append(join(pkgdir, path, f)) + + os.chdir(origdir) + +# Generate Python file that contains a dict specifying the current +# build_env flags. def MakeDefinesPyFile(target, source, env): f = file(str(target[0]), 'w') - print >>f, "import __main__" - print >>f, "__main__.m5_build_env = ", + print >>f, "m5_build_env = ", print >>f, source[0] f.close() -CFileCounter = 0 -def MakePythonCFile(target, source, env): - global CFileCounter - target = file(str(target[0]), 'w') - - print >>target, '''\ -#include "base/embedfile.hh" - -namespace { -''' - for src in source: - src = str(src) - fname = os.path.basename(src) - name = 'embedded_file%d' % CFileCounter - CFileCounter += 1 - WriteCFile(target, src, name) - print >>target, '''\ -EmbedMap %(name)s("%(fname)s", - %(name)s_string, %(name)s_length); - -''' % locals() - print >>target, '''\ - -/* namespace */ } -''' - -# base list of .py files to embed -embedded_py_files = [ os.path.join(env['ROOT'], 'util/pbs/jobfile.py') ] -# add all .py files in python/m5 -objpath = os.path.join(env['SRCDIR'], 'python', 'm5') -for root, dirs, files in os.walk(objpath, topdown=True): - for i,dir in enumerate(dirs): - if dir == 'SCCS': - del dirs[i] - break - - assert(root.startswith(objpath)) - for f in files: - if f.endswith('.py'): - embedded_py_files.append(os.path.join(root, f)) - -embedfile_hh = os.path.join(env['SRCDIR'], 'base/embedfile.hh') - optionDict = dict([(opt, env[opt]) for opt in env.ExportOptions]) -env.Command('defines.py', Value(optionDict), MakeDefinesPyFile) - -env.Command('embedded_py.py', embedded_py_files, MakeEmbeddedPyFile) -env.Depends('embedded_py.cc', embedfile_hh) -env.Command('embedded_py.cc', - ['string_importer.py', 'defines.py', 'embedded_py.py'], - MakePythonCFile) +env.Command('m5/defines.py', Value(optionDict), MakeDefinesPyFile) + +# Now specify the packages & files for the zip archive. +addPkg('m5') +pyzip_files.append('m5/defines.py') +pyzip_files.append(join(env['ROOT'], 'util/pbs/jobfile.py')) + +env.Command(['swig/cc_main_wrap.cc', 'm5/cc_main.py'], + 'swig/cc_main.i', + '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} ' + '-o ${TARGETS[0]} $SOURCES') + +pyzip_dep_files.append('m5/cc_main.py') + +# Action function to build the zip archive. Uses the PyZipFile module +# included in the standard Python library. +def buildPyZip(target, source, env): + pzf = PyZipFile(str(target[0]), 'w') + for s in source: + pzf.writepy(str(s)) + +# Add the zip file target to the environment. +env.Command('m5py.zip', pyzip_files, buildPyZip) +env.Depends('m5py.zip', pyzip_dep_files) diff --git a/src/python/m5/__init__.py b/src/python/m5/__init__.py index 9bb68a090..828165d15 100644 --- a/src/python/m5/__init__.py +++ b/src/python/m5/__init__.py @@ -23,24 +23,30 @@ # 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. +# +# Authors: Nathan Binkert +# Steve Reinhardt + +import sys, os, time, atexit, optparse + +# import the SWIG-wrapped main C++ functions +import cc_main +# import a few SWIG-wrapped items (those that are likely to be used +# directly by user scripts) completely into this module for +# convenience +from cc_main import simulate, SimLoopExitEvent, setCheckpointDir -import sys, os +# import the m5 compile options +import defines # define this here so we can use it right away if necessary def panic(string): print >>sys.stderr, 'panic:', string sys.exit(1) -def m5execfile(f, global_dict): - # copy current sys.path - oldpath = sys.path[:] - # push file's directory onto front of path - sys.path.insert(0, os.path.abspath(os.path.dirname(f))) - execfile(f, global_dict) - # restore original path - sys.path = oldpath - -# Prepend given directory to system module search path. +# Prepend given directory to system module search path. We may not +# need this anymore if we can structure our config library more like a +# Python package. def AddToPath(path): # if it's a relative path and we know what directory the current # python script is in, make the path relative to that directory. @@ -51,24 +57,236 @@ def AddToPath(path): # so place the new dir right after that. sys.path.insert(1, path) -# find the m5 compile options: must be specified as a dict in -# __main__.m5_build_env. -import __main__ -if not hasattr(__main__, 'm5_build_env'): - panic("__main__ must define m5_build_env") + +# The m5 module's pointer to the parsed options object +options = None + + +# User should call this function after calling parse_args() to pass +# parsed standard option values back into the m5 module for +# processing. +def setStandardOptions(_options): + # Set module global var + global options + options = _options + # tell C++ about output directory + cc_main.setOutputDir(options.outdir) + +# Callback to set trace flags. Not necessarily the best way to do +# things in the long run (particularly if we change how these global +# options are handled). +def setTraceFlags(option, opt_str, value, parser): + objects.Trace.flags = value + +def setTraceStart(option, opt_str, value, parser): + objects.Trace.start = value + +def setTraceFile(option, opt_str, value, parser): + objects.Trace.file = value + +def noPCSymbol(option, opt_str, value, parser): + objects.ExecutionTrace.pc_symbol = False + +def noPrintCycle(option, opt_str, value, parser): + objects.ExecutionTrace.print_cycle = False + +def noPrintOpclass(option, opt_str, value, parser): + objects.ExecutionTrace.print_opclass = False + +def noPrintThread(option, opt_str, value, parser): + objects.ExecutionTrace.print_thread = False + +def noPrintEA(option, opt_str, value, parser): + objects.ExecutionTrace.print_effaddr = False + +def noPrintData(option, opt_str, value, parser): + objects.ExecutionTrace.print_data = False + +def printFetchseq(option, opt_str, value, parser): + objects.ExecutionTrace.print_fetchseq = True + +def printCpseq(option, opt_str, value, parser): + objects.ExecutionTrace.print_cpseq = True + +def dumpOnExit(option, opt_str, value, parser): + objects.Trace.dump_on_exit = True + +def debugBreak(option, opt_str, value, parser): + objects.Debug.break_cycles = value + +def statsTextFile(option, opt_str, value, parser): + objects.Statistics.text_file = value + +# Standard optparse options. Need to be explicitly included by the +# user script when it calls optparse.OptionParser(). +standardOptions = [ + optparse.make_option("--outdir", type="string", default="."), + optparse.make_option("--traceflags", type="string", action="callback", + callback=setTraceFlags), + optparse.make_option("--tracestart", type="int", action="callback", + callback=setTraceStart), + optparse.make_option("--tracefile", type="string", action="callback", + callback=setTraceFile), + optparse.make_option("--nopcsymbol", + action="callback", callback=noPCSymbol, + help="Disable PC symbols in trace output"), + optparse.make_option("--noprintcycle", + action="callback", callback=noPrintCycle, + help="Don't print cycle numbers in trace output"), + optparse.make_option("--noprintopclass", + action="callback", callback=noPrintOpclass, + help="Don't print op class type in trace output"), + optparse.make_option("--noprintthread", + action="callback", callback=noPrintThread, + help="Don't print thread number in trace output"), + optparse.make_option("--noprinteffaddr", + action="callback", callback=noPrintEA, + help="Don't print effective address in trace output"), + optparse.make_option("--noprintdata", + action="callback", callback=noPrintData, + help="Don't print result data in trace output"), + optparse.make_option("--printfetchseq", + action="callback", callback=printFetchseq, + help="Print fetch sequence numbers in trace output"), + optparse.make_option("--printcpseq", + action="callback", callback=printCpseq, + help="Print correct path sequence numbers in trace output"), + optparse.make_option("--dumponexit", + action="callback", callback=dumpOnExit, + help="Dump trace buffer on exit"), + optparse.make_option("--debugbreak", type="int", metavar="CYCLE", + action="callback", callback=debugBreak, + help="Cycle to create a breakpoint"), + optparse.make_option("--statsfile", type="string", action="callback", + callback=statsTextFile, metavar="FILE", + help="Sets the output file for the statistics") + ] # make a SmartDict out of the build options for our local use import smartdict build_env = smartdict.SmartDict() -build_env.update(__main__.m5_build_env) +build_env.update(defines.m5_build_env) # make a SmartDict out of the OS environment too env = smartdict.SmartDict() env.update(os.environ) -# import the main m5 config code -from config import * -# import the built-in object definitions -from objects import * +# Function to provide to C++ so it can look up instances based on paths +def resolveSimObject(name): + obj = config.instanceDict[name] + return obj.getCCObject() + +# The final hook to generate .ini files. Called from the user script +# once the config is built. +def instantiate(root): + config.ticks_per_sec = float(root.clock.frequency) + # ugly temporary hack to get output to config.ini + sys.stdout = file(os.path.join(options.outdir, 'config.ini'), 'w') + root.print_ini() + sys.stdout.close() # close config.ini + sys.stdout = sys.__stdout__ # restore to original + cc_main.loadIniFile(resolveSimObject) # load config.ini into C++ + root.createCCObject() + root.connectPorts() + cc_main.finalInit() + noDot = True # temporary until we fix dot + if not noDot: + dot = pydot.Dot() + instance.outputDot(dot) + dot.orientation = "portrait" + dot.size = "8.5,11" + dot.ranksep="equally" + dot.rank="samerank" + dot.write("config.dot") + dot.write_ps("config.ps") + +# Export curTick to user script. +def curTick(): + return cc_main.cvar.curTick + +# register our C++ exit callback function with Python +atexit.register(cc_main.doExitCleanup) + +# This import allows user scripts to reference 'm5.objects.Foo' after +# just doing an 'import m5' (without an 'import m5.objects'). May not +# matter since most scripts will probably 'from m5.objects import *'. +import objects + +def doQuiesce(root): + quiesce = cc_main.createCountedQuiesce() + unready_objects = root.startQuiesce(quiesce, True) + # If we've got some objects that can't quiesce immediately, then simulate + if unready_objects > 0: + quiesce.setCount(unready_objects) + simulate() + cc_main.cleanupCountedQuiesce(quiesce) + +def resume(root): + root.resume() + +def checkpoint(root): + if not isinstance(root, objects.Root): + raise TypeError, "Object is not a root object. Checkpoint must be called on a root object." + doQuiesce(root) + print "Writing checkpoint" + cc_main.serializeAll() + resume(root) + +def restoreCheckpoint(root): + print "Restoring from checkpoint" + cc_main.unserializeAll() + +def changeToAtomic(system): + if not isinstance(system, objects.Root) and not isinstance(system, System): + raise TypeError, "Object is not a root or system object. Checkpoint must be " + "called on a root object." + doQuiesce(system) + print "Changing memory mode to atomic" + system.changeTiming(cc_main.SimObject.Atomic) + resume(system) + +def changeToTiming(system): + if not isinstance(system, objects.Root) and not isinstance(system, System): + raise TypeError, "Object is not a root or system object. Checkpoint must be " + "called on a root object." + doQuiesce(system) + print "Changing memory mode to timing" + system.changeTiming(cc_main.SimObject.Timing) + resume(system) + +def switchCpus(cpuList): + if not isinstance(cpuList, list): + raise RuntimeError, "Must pass a list to this function" + for i in cpuList: + if not isinstance(i, tuple): + raise RuntimeError, "List must have tuples of (oldCPU,newCPU)" + + [old_cpus, new_cpus] = zip(*cpuList) + + for cpu in old_cpus: + if not isinstance(cpu, objects.BaseCPU): + raise TypeError, "%s is not of type BaseCPU", cpu + for cpu in new_cpus: + if not isinstance(cpu, objects.BaseCPU): + raise TypeError, "%s is not of type BaseCPU", cpu + # Quiesce all of the individual CPUs + quiesce = cc_main.createCountedQuiesce() + unready_cpus = 0 + for old_cpu in old_cpus: + unready_cpus += old_cpu.startQuiesce(quiesce, False) + # If we've got some objects that can't quiesce immediately, then simulate + if unready_cpus > 0: + quiesce.setCount(unready_cpus) + simulate() + cc_main.cleanupCountedQuiesce(quiesce) + # Now all of the CPUs are ready to be switched out + for old_cpu in old_cpus: + old_cpu._ccObject.switchOut() + index = 0 + print "Switching CPUs" + for new_cpu in new_cpus: + new_cpu.takeOverFrom(old_cpus[index]) + new_cpu._ccObject.resume() + index += 1 diff --git a/src/python/m5/config.py b/src/python/m5/config.py index 1e25e0d09..6f2873d40 100644 --- a/src/python/m5/config.py +++ b/src/python/m5/config.py @@ -1,4 +1,4 @@ -# Copyright (c) 2004-2005 The Regents of The University of Michigan +# Copyright (c) 2004-2006 The Regents of The University of Michigan # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -23,12 +23,14 @@ # 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. +# +# Authors: Steve Reinhardt +# Nathan Binkert -from __future__ import generators -import os, re, sys, types, inspect +import os, re, sys, types, inspect, copy import m5 -panic = m5.panic +from m5 import panic, cc_main from convert import * from multidict import multidict @@ -82,60 +84,24 @@ class Singleton(type): # # Once a set of Python objects have been instantiated in a hierarchy, # calling 'instantiate(obj)' (where obj is the root of the hierarchy) -# will generate a .ini file. See simple-4cpu.py for an example -# (corresponding to m5-test/simple-4cpu.ini). +# will generate a .ini file. # ##################################################################### -##################################################################### -# -# ConfigNode/SimObject classes -# -# The Python class hierarchy rooted by ConfigNode (which is the base -# class of SimObject, which in turn is the base class of all other M5 -# SimObject classes) has special attribute behavior. In general, an -# object in this hierarchy has three categories of attribute-like -# things: -# -# 1. Regular Python methods and variables. These must start with an -# underscore to be treated normally. -# -# 2. SimObject parameters. These values are stored as normal Python -# attributes, but all assignments to these attributes are checked -# against the pre-defined set of parameters stored in the class's -# _params dictionary. Assignments to attributes that do not -# correspond to predefined parameters, or that are not of the correct -# type, incur runtime errors. +# dict to look up SimObjects based on path +instanceDict = {} + +############################# # -# 3. Hierarchy children. The child nodes of a ConfigNode are stored -# in the node's _children dictionary, but can be accessed using the -# Python attribute dot-notation (just as they are printed out by the -# simulator). Children cannot be created using attribute assigment; -# they must be added by specifying the parent node in the child's -# constructor or using the '+=' operator. - -# The SimObject parameters are the most complex, for a few reasons. -# First, both parameter descriptions and parameter values are -# inherited. Thus parameter description lookup must go up the -# inheritance chain like normal attribute lookup, but this behavior -# must be explicitly coded since the lookup occurs in each class's -# _params attribute. Second, because parameter values can be set -# on SimObject classes (to implement default values), the parameter -# checking behavior must be enforced on class attribute assignments as -# well as instance attribute assignments. Finally, because we allow -# class specialization via inheritance (e.g., see the L1Cache class in -# the simple-4cpu.py example), we must do parameter checking even on -# class instantiation. To provide all these features, we use a -# metaclass to define most of the SimObject parameter behavior for -# this class hierarchy. +# Utility methods # -##################################################################### +############################# def isSimObject(value): return isinstance(value, SimObject) -def isSimObjSequence(value): - if not isinstance(value, (list, tuple)): +def isSimObjectSequence(value): + if not isinstance(value, (list, tuple)) or len(value) == 0: return False for val in value: @@ -144,48 +110,76 @@ def isSimObjSequence(value): return True +def isSimObjectOrSequence(value): + return isSimObject(value) or isSimObjectSequence(value) + def isNullPointer(value): return isinstance(value, NullSimObject) -# The metaclass for ConfigNode (and thus for everything that derives -# from ConfigNode, including SimObject). This class controls how new -# classes that derive from ConfigNode are instantiated, and provides -# inherited class behavior (just like a class controls how instances -# of that class are instantiated, and provides inherited instance -# behavior). +# Apply method to object. +# applyMethod(obj, 'meth', <args>) is equivalent to obj.meth(<args>) +def applyMethod(obj, meth, *args, **kwargs): + return getattr(obj, meth)(*args, **kwargs) + +# If the first argument is an (non-sequence) object, apply the named +# method with the given arguments. If the first argument is a +# sequence, apply the method to each element of the sequence (a la +# 'map'). +def applyOrMap(objOrSeq, meth, *args, **kwargs): + if not isinstance(objOrSeq, (list, tuple)): + return applyMethod(objOrSeq, meth, *args, **kwargs) + else: + return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq] + + +# The metaclass for SimObject. This class controls how new classes +# that derive from SimObject are instantiated, and provides inherited +# class behavior (just like a class controls how instances of that +# class are instantiated, and provides inherited instance behavior). class MetaSimObject(type): # Attributes that can be set only at initialization time init_keywords = { 'abstract' : types.BooleanType, 'type' : types.StringType } # Attributes that can be set any time - keywords = { 'check' : types.FunctionType, - 'children' : types.ListType } + keywords = { 'check' : types.FunctionType } # __new__ is called before __init__, and is where the statements # in the body of the class definition get loaded into the class's - # __dict__. We intercept this to filter out parameter assignments + # __dict__. We intercept this to filter out parameter & port assignments # and only allow "private" attributes to be passed to the base # __new__ (starting with underscore). def __new__(mcls, name, bases, dict): - # Copy "private" attributes (including special methods such as __new__) - # to the official dict. Everything else goes in _init_dict to be + # Copy "private" attributes, functions, and classes to the + # official dict. Everything else goes in _init_dict to be # filtered in __init__. cls_dict = {} + value_dict = {} for key,val in dict.items(): - if key.startswith('_'): + if key.startswith('_') or isinstance(val, (types.FunctionType, + types.TypeType)): cls_dict[key] = val - del dict[key] - cls_dict['_init_dict'] = dict + else: + # must be a param/port setting + value_dict[key] = val + cls_dict['_value_dict'] = value_dict return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) - # initialization + # subclass initialization def __init__(cls, name, bases, dict): + # calls type.__init__()... I think that's a no-op, but leave + # it here just in case it's not. super(MetaSimObject, cls).__init__(name, bases, dict) # initialize required attributes - cls._params = multidict() - cls._values = multidict() - cls._anon_subclass_counter = 0 + + # class-only attributes + cls._params = multidict() # param descriptions + cls._ports = multidict() # port descriptions + + # class or instance attributes + cls._values = multidict() # param values + cls._port_map = multidict() # port bindings + cls._instantiated = False # really instantiated, cloned, or subclassed # We don't support multiple inheritance. If you want to, you # must fix multidict to deal with it properly. @@ -194,35 +188,34 @@ class MetaSimObject(type): base = bases[0] + # Set up general inheritance via multidicts. A subclass will + # inherit all its settings from the base class. The only time + # the following is not true is when we define the SimObject + # class itself (in which case the multidicts have no parent). if isinstance(base, MetaSimObject): cls._params.parent = base._params + cls._ports.parent = base._ports cls._values.parent = base._values - - # If your parent has a value in it that's a config node, clone - # it. Do this now so if we update any of the values' - # attributes we are updating the clone and not the original. - for key,val in base._values.iteritems(): - - # don't clone if (1) we're about to overwrite it with - # a local setting or (2) we've already cloned a copy - # from an earlier (more derived) base - if cls._init_dict.has_key(key) or cls._values.has_key(key): - continue - - if isSimObject(val): - cls._values[key] = val() - elif isSimObjSequence(val) and len(val): - cls._values[key] = [ v() for v in val ] - - # now process remaining _init_dict items - for key,val in cls._init_dict.items(): - if isinstance(val, (types.FunctionType, types.TypeType)): - type.__setattr__(cls, key, val) - + cls._port_map.parent = base._port_map + # mark base as having been subclassed + base._instantiated = True + + # Now process the _value_dict items. They could be defining + # new (or overriding existing) parameters or ports, setting + # class keywords (e.g., 'abstract'), or setting parameter + # values or port bindings. The first 3 can only be set when + # the class is defined, so we handle them here. The others + # can be set later too, so just emulate that by calling + # setattr(). + for key,val in cls._value_dict.items(): # param descriptions - elif isinstance(val, ParamDesc): + if isinstance(val, ParamDesc): cls._new_param(key, val) + # port objects + elif isinstance(val, Port): + cls._ports[key] = val + # init-time-only keywords elif cls.init_keywords.has_key(key): cls._set_keyword(key, val, cls.init_keywords[key]) @@ -256,10 +249,19 @@ class MetaSimObject(type): cls._set_keyword(attr, value, cls.keywords[attr]) return - # must be SimObject param + if cls._ports.has_key(attr): + self._ports[attr].connect(self, attr, value) + return + + if isSimObjectOrSequence(value) and cls._instantiated: + raise RuntimeError, \ + "cannot set SimObject parameter '%s' after\n" \ + " class %s has been instantiated or subclassed" \ + % (attr, cls.__name__) + + # check for param param = cls._params.get(attr, None) if param: - # It's ok: set attribute by delegating to 'object' class. try: cls._values[attr] = param.convert(value) except Exception, e: @@ -267,12 +269,12 @@ class MetaSimObject(type): (e, cls.__name__, attr, value) e.args = (msg, ) raise - # I would love to get rid of this - elif isSimObject(value) or isSimObjSequence(value): - cls._values[attr] = value + elif isSimObjectOrSequence(value): + # if RHS is a SimObject, it's an implicit child assignment + cls._values[attr] = value else: raise AttributeError, \ - "Class %s has no parameter %s" % (cls.__name__, attr) + "Class %s has no parameter \'%s\'" % (cls.__name__, attr) def __getattr__(cls, attr): if cls._values.has_key(attr): @@ -281,7 +283,7 @@ class MetaSimObject(type): raise AttributeError, \ "object '%s' has no attribute '%s'" % (cls.__name__, attr) -# The ConfigNode class is the root of the special hierarchy. Most of +# The SimObject class is the root of the special hierarchy. Most of # the code in this class deals with the configuration hierarchy itself # (parent/child node relationships). class SimObject(object): @@ -289,29 +291,79 @@ class SimObject(object): # get this metaclass. __metaclass__ = MetaSimObject - def __init__(self, _value_parent = None, **kwargs): + # Initialize new instance. For objects with SimObject-valued + # children, we need to recursively clone the classes represented + # by those param values as well in a consistent "deep copy"-style + # fashion. That is, we want to make sure that each instance is + # cloned only once, and that if there are multiple references to + # the same original object, we end up with the corresponding + # cloned references all pointing to the same cloned instance. + def __init__(self, **kwargs): + ancestor = kwargs.get('_ancestor') + memo_dict = kwargs.get('_memo') + if memo_dict is None: + # prepare to memoize any recursively instantiated objects + memo_dict = {} + elif ancestor: + # memoize me now to avoid problems with recursive calls + memo_dict[ancestor] = self + + if not ancestor: + ancestor = self.__class__ + ancestor._instantiated = True + + # initialize required attributes + self._parent = None self._children = {} - if _value_parent and type(_value_parent) != type(self): - # this was called as a type conversion rather than a clone - raise TypeError, "Cannot convert %s to %s" % \ - (_value_parent.__class__.__name__, self.__class__.__name__) - if not _value_parent: - _value_parent = self.__class__ - # clone values - self._values = multidict(_value_parent._values) - for key,val in _value_parent._values.iteritems(): + self._ccObject = None # pointer to C++ object + self._instantiated = False # really "cloned" + + # Inherit parameter values from class using multidict so + # individual value settings can be overridden. + self._values = multidict(ancestor._values) + # clone SimObject-valued parameters + for key,val in ancestor._values.iteritems(): if isSimObject(val): - setattr(self, key, val()) - elif isSimObjSequence(val) and len(val): - setattr(self, key, [ v() for v in val ]) + setattr(self, key, val(_memo=memo_dict)) + elif isSimObjectSequence(val) and len(val): + setattr(self, key, [ v(_memo=memo_dict) for v in val ]) + # clone port references. no need to use a multidict here + # since we will be creating new references for all ports. + self._port_map = {} + for key,val in ancestor._port_map.iteritems(): + self._port_map[key] = applyOrMap(val, 'clone', memo_dict) # apply attribute assignments from keyword args, if any for key,val in kwargs.iteritems(): setattr(self, key, val) + # "Clone" the current instance by creating another instance of + # this instance's class, but that inherits its parameter values + # and port mappings from the current instance. If we're in a + # "deep copy" recursive clone, check the _memo dict to see if + # we've already cloned this instance. def __call__(self, **kwargs): - return self.__class__(_value_parent = self, **kwargs) + memo_dict = kwargs.get('_memo') + if memo_dict is None: + # no memo_dict: must be top-level clone operation. + # this is only allowed at the root of a hierarchy + if self._parent: + raise RuntimeError, "attempt to clone object %s " \ + "not at the root of a tree (parent = %s)" \ + % (self, self._parent) + # create a new dict and use that. + memo_dict = {} + kwargs['_memo'] = memo_dict + elif memo_dict.has_key(self): + # clone already done & memoized + return memo_dict[self] + return self.__class__(_ancestor = self, **kwargs) def __getattr__(self, attr): + if self._ports.has_key(attr): + # return reference that can be assigned to another port + # via __setattr__ + return self._ports[attr].makeRef(self, attr) + if self._values.has_key(attr): return self._values[attr] @@ -326,10 +378,19 @@ class SimObject(object): object.__setattr__(self, attr, value) return + if self._ports.has_key(attr): + # set up port connection + self._ports[attr].connect(self, attr, value) + return + + if isSimObjectOrSequence(value) and self._instantiated: + raise RuntimeError, \ + "cannot set SimObject parameter '%s' after\n" \ + " instance been cloned %s" % (attr, `self`) + # must be SimObject param param = self._params.get(attr, None) if param: - # It's ok: set attribute by delegating to 'object' class. try: value = param.convert(value) except Exception, e: @@ -337,8 +398,7 @@ class SimObject(object): (e, self.__class__.__name__, attr, value) e.args = (msg, ) raise - # I would love to get rid of this - elif isSimObject(value) or isSimObjSequence(value): + elif isSimObjectOrSequence(value): pass else: raise AttributeError, "Class %s has no parameter %s" \ @@ -349,7 +409,7 @@ class SimObject(object): if isSimObject(value): value.set_path(self, attr) - elif isSimObjSequence(value): + elif isSimObjectSequence(value): value = SimObjVector(value) [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)] @@ -376,13 +436,13 @@ class SimObject(object): self._children[name] = value def set_path(self, parent, name): - if not hasattr(self, '_parent'): + if not self._parent: self._parent = parent self._name = name parent.add_child(name, self) def path(self): - if not hasattr(self, '_parent'): + if not self._parent: return 'root' ppath = self._parent.path() if ppath == 'root': @@ -423,6 +483,8 @@ class SimObject(object): def print_ini(self): print '[' + self.path() + ']' # .ini section header + instanceDict[self.path()] = self + if hasattr(self, 'type') and not isinstance(self, ParamContext): print 'type=%s' % self.type @@ -454,6 +516,60 @@ class SimObject(object): for child in child_names: self._children[child].print_ini() + # Call C++ to create C++ object corresponding to this object and + # (recursively) all its children + def createCCObject(self): + self.getCCObject() # force creation + for child in self._children.itervalues(): + child.createCCObject() + + # Get C++ object corresponding to this object, calling C++ if + # necessary to construct it. Does *not* recursively create + # children. + def getCCObject(self): + if not self._ccObject: + self._ccObject = -1 # flag to catch cycles in recursion + self._ccObject = cc_main.createSimObject(self.path()) + elif self._ccObject == -1: + raise RuntimeError, "%s: recursive call to getCCObject()" \ + % self.path() + return self._ccObject + + # Create C++ port connections corresponding to the connections in + # _port_map (& recursively for all children) + def connectPorts(self): + for portRef in self._port_map.itervalues(): + applyOrMap(portRef, 'ccConnect') + for child in self._children.itervalues(): + child.connectPorts() + + def startQuiesce(self, quiesce_event, recursive): + count = 0 + # ParamContexts don't serialize + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + if self._ccObject.quiesce(quiesce_event): + count = 1 + if recursive: + for child in self._children.itervalues(): + count += child.startQuiesce(quiesce_event, True) + return count + + def resume(self): + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + self._ccObject.resume() + for child in self._children.itervalues(): + child.resume() + + def changeTiming(self, mode): + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + self._ccObject.setMemoryMode(mode) + for child in self._children.itervalues(): + child.changeTiming(mode) + + def takeOverFrom(self, old_cpu): + cpu_ptr = cc_main.convertToBaseCPUPtr(old_cpu._ccObject) + self._ccObject.takeOverFrom(cpu_ptr) + # generate output file for 'dot' to display as a pretty graph. # this code is currently broken. def outputDot(self, dot): @@ -544,9 +660,9 @@ class BaseProxy(object): if self._search_up: while not done: - try: obj = obj._parent - except: break - + obj = obj._parent + if not obj: + break result, done = self.find(obj) if not done: @@ -662,16 +778,16 @@ Self = ProxyFactory(search_self = True, search_up = False) # # Parameter description classes # -# The _params dictionary in each class maps parameter names to -# either a Param or a VectorParam object. These objects contain the +# The _params dictionary in each class maps parameter names to either +# a Param or a VectorParam object. These objects contain the # parameter description string, the parameter type, and the default -# value (loaded from the PARAM section of the .odesc files). The -# _convert() method on these objects is used to force whatever value -# is assigned to the parameter to the appropriate type. +# value (if any). The convert() method on these objects is used to +# force whatever value is assigned to the parameter to the appropriate +# type. # # Note that the default values are loaded into the class's attribute # space when the parameter dictionary is initialized (in -# MetaConfigNode._setparams()); after that point they aren't used. +# MetaSimObject._new_param()); after that point they aren't used. # ##################################################################### @@ -725,7 +841,7 @@ class ParamDesc(object): def __getattr__(self, attr): if attr == 'ptype': try: - ptype = eval(self.ptype_str, m5.__dict__) + ptype = eval(self.ptype_str, m5.objects.__dict__) if not isinstance(ptype, type): panic("Param qualifier is not a type: %s" % self.ptype) self.ptype = ptype @@ -772,7 +888,7 @@ class VectorParamDesc(ParamDesc): if isinstance(value, (list, tuple)): # list: coerce each element into new list tmp_list = [ ParamDesc.convert(self, v) for v in value ] - if isSimObjSequence(tmp_list): + if isSimObjectSequence(tmp_list): return SimObjVector(tmp_list) else: return VectorParamValue(tmp_list) @@ -794,7 +910,7 @@ class ParamFactory(object): # E.g., Param.Int(5, "number of widgets") def __call__(self, *args, **kwargs): - caller_frame = inspect.stack()[1][0] + caller_frame = inspect.currentframe().f_back ptype = None try: ptype = eval(self.ptype_str, @@ -1066,7 +1182,10 @@ class EthernetAddr(ParamValue): def __str__(self): if self.value == NextEthernetAddr: - return self.addr + if hasattr(self, 'addr'): + return self.addr + else: + return "NextEthernetAddr (unresolved)" else: return self.value @@ -1285,24 +1404,108 @@ MaxAddr = Addr.max MaxTick = Tick.max AllMemory = AddrRange(0, MaxAddr) + +##################################################################### +# +# Port objects +# +# Ports are used to interconnect objects in the memory system. +# ##################################################################### -# The final hook to generate .ini files. Called from configuration -# script once config is built. -def instantiate(root): - global ticks_per_sec - ticks_per_sec = float(root.clock.frequency) - root.print_ini() - noDot = True # temporary until we fix dot - if not noDot: - dot = pydot.Dot() - instance.outputDot(dot) - dot.orientation = "portrait" - dot.size = "8.5,11" - dot.ranksep="equally" - dot.rank="samerank" - dot.write("config.dot") - dot.write_ps("config.ps") +# Port reference: encapsulates a reference to a particular port on a +# particular SimObject. +class PortRef(object): + def __init__(self, simobj, name, isVec): + assert(isSimObject(simobj)) + self.simobj = simobj + self.name = name + self.index = -1 + self.isVec = isVec # is this a vector port? + self.peer = None # not associated with another port yet + self.ccConnected = False # C++ port connection done? + + # Set peer port reference. Called via __setattr__ as a result of + # a port assignment, e.g., "obj1.port1 = obj2.port2". + def setPeer(self, other): + if self.isVec: + curMap = self.simobj._port_map.get(self.name, []) + self.index = len(curMap) + curMap.append(other) + else: + curMap = self.simobj._port_map.get(self.name) + if curMap and not self.isVec: + print "warning: overwriting port", self.simobj, self.name + curMap = other + self.simobj._port_map[self.name] = curMap + self.peer = other + + def clone(self, memo): + newRef = copy.copy(self) + assert(isSimObject(newRef.simobj)) + newRef.simobj = newRef.simobj(_memo=memo) + # Tricky: if I'm the *second* PortRef in the pair to be + # cloned, then my peer is still in the middle of its clone + # method, and thus hasn't returned to its owner's + # SimObject.__init__ to get installed in _port_map. As a + # result I have no way of finding the *new* peer object. So I + # mark myself as "waiting" for my peer, and I let the *first* + # PortRef clone call set up both peer pointers after I return. + newPeer = newRef.simobj._port_map.get(self.name) + if newPeer: + if self.isVec: + assert(self.index != -1) + newPeer = newPeer[self.index] + # other guy is all set up except for his peer pointer + assert(newPeer.peer == -1) # peer must be waiting for handshake + newPeer.peer = newRef + newRef.peer = newPeer + else: + # other guy is in clone; just wait for him to do the work + newRef.peer = -1 # mark as waiting for handshake + return newRef + + # Call C++ to create corresponding port connection between C++ objects + def ccConnect(self): + if self.ccConnected: # already done this + return + peer = self.peer + cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index, + peer.simobj.getCCObject(), peer.name, peer.index) + self.ccConnected = True + peer.ccConnected = True + +# Port description object. Like a ParamDesc object, this represents a +# logical port in the SimObject class, not a particular port on a +# SimObject instance. The latter are represented by PortRef objects. +class Port(object): + def __init__(self, desc): + self.desc = desc + self.isVec = False + + # Generate a PortRef for this port on the given SimObject with the + # given name + def makeRef(self, simobj, name): + return PortRef(simobj, name, self.isVec) + + # Connect an instance of this port (on the given SimObject with + # the given name) with the port described by the supplied PortRef + def connect(self, simobj, name, ref): + if not isinstance(ref, PortRef): + raise TypeError, \ + "assigning non-port reference port '%s'" % name + myRef = self.makeRef(simobj, name) + myRef.setPeer(ref) + ref.setPeer(myRef) + +# VectorPort description object. Like Port, but represents a vector +# of connections (e.g., as on a Bus). +class VectorPort(Port): + def __init__(self, desc): + Port.__init__(self, desc) + self.isVec = True + +##################################################################### # __all__ defines the list of symbols that get exported when # 'from config import *' is invoked. Try to keep this reasonably @@ -1319,5 +1522,6 @@ __all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam', 'NetworkBandwidth', 'MemoryBandwidth', 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 'Null', 'NULL', - 'NextEthernetAddr', 'instantiate'] + 'NextEthernetAddr', + 'Port', 'VectorPort'] diff --git a/src/python/m5/convert.py b/src/python/m5/convert.py index 73181e985..580a579bc 100644 --- a/src/python/m5/convert.py +++ b/src/python/m5/convert.py @@ -23,6 +23,8 @@ # 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. +# +# Authors: Nathan Binkert # metric prefixes exa = 1.0e18 diff --git a/src/python/m5/multidict.py b/src/python/m5/multidict.py index fd40ebbbd..34fc3139b 100644 --- a/src/python/m5/multidict.py +++ b/src/python/m5/multidict.py @@ -23,13 +23,15 @@ # 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. +# +# Authors: Nathan Binkert __all__ = [ 'multidict' ] class multidict(object): __nodefault = object() def __init__(self, parent = {}, **kwargs): - self.dict = dict(**kwargs) + self.local = dict(**kwargs) self.parent = parent self.deleted = {} @@ -40,11 +42,11 @@ class multidict(object): return `dict(self.items())` def __contains__(self, key): - return self.dict.has_key(key) or self.parent.has_key(key) + return self.local.has_key(key) or self.parent.has_key(key) def __delitem__(self, key): try: - del self.dict[key] + del self.local[key] except KeyError, e: if key in self.parent: self.deleted[key] = True @@ -53,11 +55,11 @@ class multidict(object): def __setitem__(self, key, value): self.deleted.pop(key, False) - self.dict[key] = value + self.local[key] = value def __getitem__(self, key): try: - return self.dict[key] + return self.local[key] except KeyError, e: if not self.deleted.get(key, False) and key in self.parent: return self.parent[key] @@ -65,15 +67,15 @@ class multidict(object): raise KeyError, e def __len__(self): - return len(self.dict) + len(self.parent) + return len(self.local) + len(self.parent) def next(self): - for key,value in self.dict.items(): + for key,value in self.local.items(): yield key,value if self.parent: for key,value in self.parent.next(): - if key not in self.dict and key not in self.deleted: + if key not in self.local and key not in self.deleted: yield key,value def has_key(self, key): @@ -114,22 +116,22 @@ class multidict(object): return self[key] except KeyError: self.deleted.pop(key, False) - self.dict[key] = default + self.local[key] = default return default def _dump(self): print 'multidict dump' node = self while isinstance(node, multidict): - print ' ', node.dict + print ' ', node.local node = node.parent def _dumpkey(self, key): values = [] node = self while isinstance(node, multidict): - if key in node.dict: - values.append(node.dict[key]) + if key in node.local: + values.append(node.local[key]) node = node.parent print key, values diff --git a/src/python/m5/objects/AlphaConsole.py b/src/python/m5/objects/AlphaConsole.py index 68e6089ab..329b8c5bd 100644 --- a/src/python/m5/objects/AlphaConsole.py +++ b/src/python/m5/objects/AlphaConsole.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice class AlphaConsole(BasicPioDevice): diff --git a/src/python/m5/objects/AlphaTLB.py b/src/python/m5/objects/AlphaTLB.py index 5edf8e13d..11c1792ee 100644 --- a/src/python/m5/objects/AlphaTLB.py +++ b/src/python/m5/objects/AlphaTLB.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class AlphaTLB(SimObject): type = 'AlphaTLB' abstract = True diff --git a/src/python/m5/objects/BadDevice.py b/src/python/m5/objects/BadDevice.py index 9cb9a8f03..186b733fa 100644 --- a/src/python/m5/objects/BadDevice.py +++ b/src/python/m5/objects/BadDevice.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice class BadDevice(BasicPioDevice): diff --git a/src/python/m5/objects/BaseCPU.py b/src/python/m5/objects/BaseCPU.py index 49cb2a8f3..2e78578df 100644 --- a/src/python/m5/objects/BaseCPU.py +++ b/src/python/m5/objects/BaseCPU.py @@ -1,4 +1,6 @@ -from m5 import * +from m5 import build_env +from m5.config import * + class BaseCPU(SimObject): type = 'BaseCPU' abstract = True diff --git a/src/python/m5/objects/BaseCache.py b/src/python/m5/objects/BaseCache.py index 79d21572a..497b2b038 100644 --- a/src/python/m5/objects/BaseCache.py +++ b/src/python/m5/objects/BaseCache.py @@ -1,29 +1,26 @@ -from m5 import * -from BaseMem import BaseMem +from m5.config import * +from MemObject import MemObject class Prefetch(Enum): vals = ['none', 'tagged', 'stride', 'ghb'] -class BaseCache(BaseMem): +class BaseCache(MemObject): type = 'BaseCache' adaptive_compression = Param.Bool(False, "Use an adaptive compression scheme") assoc = Param.Int("associativity") block_size = Param.Int("block size in bytes") + latency = Param.Int("Latency") compressed_bus = Param.Bool(False, "This cache connects to a compressed memory") compression_latency = Param.Latency('0ns', "Latency in cycles of compression algorithm") do_copy = Param.Bool(False, "perform fast copies in the cache") hash_delay = Param.Int(1, "time in cycles of hash access") - in_bus = Param.Bus(NULL, "incoming bus object") lifo = Param.Bool(False, "whether this NIC partition should use LIFO repl. policy") max_miss_count = Param.Counter(0, "number of misses to handle before calling exit") - mem_trace = Param.MemTraceWriter(NULL, - "memory trace writer to record accesses") mshrs = Param.Int("number of MSHRs (max outstanding requests)") - out_bus = Param.Bus("outgoing bus object") prioritizeRequests = Param.Bool(False, "always service demand misses first") protocol = Param.CoherenceProtocol(NULL, "coherence protocol to use") @@ -63,3 +60,6 @@ class BaseCache(BaseMem): "Use the CPU ID to seperate calculations of prefetches") prefetch_data_accesses_only = Param.Bool(False, "Only prefetch on data not on instruction accesses") + hit_latency = Param.Int(1,"Hit Latency of the cache") + cpu_side = Port("Port on side closer to CPU") + mem_side = Port("Port on side closer to MEM") diff --git a/src/python/m5/objects/Bridge.py b/src/python/m5/objects/Bridge.py index ada715ce9..c9e673afb 100644 --- a/src/python/m5/objects/Bridge.py +++ b/src/python/m5/objects/Bridge.py @@ -1,8 +1,10 @@ -from m5 import * +from m5.config import * from MemObject import MemObject class Bridge(MemObject): type = 'Bridge' + side_a = Port('Side A port') + side_b = Port('Side B port') queue_size_a = Param.Int(16, "The number of requests to buffer") queue_size_b = Param.Int(16, "The number of requests to buffer") delay = Param.Latency('0ns', "The latency of this bridge") diff --git a/src/python/m5/objects/Bus.py b/src/python/m5/objects/Bus.py index 8c5397281..019e15034 100644 --- a/src/python/m5/objects/Bus.py +++ b/src/python/m5/objects/Bus.py @@ -1,6 +1,7 @@ -from m5 import * +from m5.config import * from MemObject import MemObject class Bus(MemObject): type = 'Bus' + port = VectorPort("vector port for connecting devices") bus_id = Param.Int(0, "blah") diff --git a/src/python/m5/objects/CoherenceProtocol.py b/src/python/m5/objects/CoherenceProtocol.py index 7013000d6..64b6cbacf 100644 --- a/src/python/m5/objects/CoherenceProtocol.py +++ b/src/python/m5/objects/CoherenceProtocol.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Coherence(Enum): vals = ['uni', 'msi', 'mesi', 'mosi', 'moesi'] class CoherenceProtocol(SimObject): diff --git a/src/python/m5/objects/Device.py b/src/python/m5/objects/Device.py index 2a71bbc65..222f750da 100644 --- a/src/python/m5/objects/Device.py +++ b/src/python/m5/objects/Device.py @@ -1,9 +1,10 @@ -from m5 import * +from m5.config import * from MemObject import MemObject class PioDevice(MemObject): type = 'PioDevice' abstract = True + pio = Port("Programmed I/O port") platform = Param.Platform(Parent.any, "Platform this device is part of") system = Param.System(Parent.any, "System this device is part of") @@ -16,3 +17,4 @@ class BasicPioDevice(PioDevice): class DmaDevice(PioDevice): type = 'DmaDevice' abstract = True + dma = Port("DMA port") diff --git a/src/python/m5/objects/DiskImage.py b/src/python/m5/objects/DiskImage.py index 0d55e9329..70d8b2e45 100644 --- a/src/python/m5/objects/DiskImage.py +++ b/src/python/m5/objects/DiskImage.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class DiskImage(SimObject): type = 'DiskImage' abstract = True diff --git a/src/python/m5/objects/Ethernet.py b/src/python/m5/objects/Ethernet.py index 4286c71c8..418670592 100644 --- a/src/python/m5/objects/Ethernet.py +++ b/src/python/m5/objects/Ethernet.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * from Device import DmaDevice from Pci import PciDevice diff --git a/src/python/m5/objects/FUPool.py b/src/python/m5/objects/FUPool.py new file mode 100644 index 000000000..cbf1089cf --- /dev/null +++ b/src/python/m5/objects/FUPool.py @@ -0,0 +1,5 @@ +from m5.config import * + +class FUPool(SimObject): + type = 'FUPool' + FUList = VectorParam.FUDesc("list of FU's for this pool") diff --git a/src/python/m5/objects/FuncUnit.py b/src/python/m5/objects/FuncUnit.py new file mode 100644 index 000000000..f61590ae9 --- /dev/null +++ b/src/python/m5/objects/FuncUnit.py @@ -0,0 +1,17 @@ +from m5.config import * + +class OpType(Enum): + vals = ['(null)', 'IntAlu', 'IntMult', 'IntDiv', 'FloatAdd', + 'FloatCmp', 'FloatCvt', 'FloatMult', 'FloatDiv', 'FloatSqrt', + 'MemRead', 'MemWrite', 'IprAccess', 'InstPrefetch'] + +class OpDesc(SimObject): + type = 'OpDesc' + issueLat = Param.Int(1, "cycles until another can be issued") + opClass = Param.OpType("type of operation") + opLat = Param.Int(1, "cycles until result is available") + +class FUDesc(SimObject): + type = 'FUDesc' + count = Param.Int("number of these FU's available") + opList = VectorParam.OpDesc("operation classes for this FU type") diff --git a/src/python/m5/objects/Ide.py b/src/python/m5/objects/Ide.py index 2403e6d36..9ee578177 100644 --- a/src/python/m5/objects/Ide.py +++ b/src/python/m5/objects/Ide.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Pci import PciDevice class IdeID(Enum): vals = ['master', 'slave'] diff --git a/src/python/m5/objects/IntrControl.py b/src/python/m5/objects/IntrControl.py index 66c82c182..514c3fc62 100644 --- a/src/python/m5/objects/IntrControl.py +++ b/src/python/m5/objects/IntrControl.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class IntrControl(SimObject): type = 'IntrControl' cpu = Param.BaseCPU(Parent.any, "the cpu") diff --git a/src/python/m5/objects/MemObject.py b/src/python/m5/objects/MemObject.py index 4d68243e6..d957dae17 100644 --- a/src/python/m5/objects/MemObject.py +++ b/src/python/m5/objects/MemObject.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class MemObject(SimObject): type = 'MemObject' diff --git a/src/python/m5/objects/MemTest.py b/src/python/m5/objects/MemTest.py index 34299faf0..9916d7cb4 100644 --- a/src/python/m5/objects/MemTest.py +++ b/src/python/m5/objects/MemTest.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class MemTest(SimObject): type = 'MemTest' cache = Param.BaseCache("L1 cache") diff --git a/src/python/m5/objects/O3CPU.py b/src/python/m5/objects/O3CPU.py new file mode 100644 index 000000000..4ecfa8fbd --- /dev/null +++ b/src/python/m5/objects/O3CPU.py @@ -0,0 +1,98 @@ +from m5 import build_env +from m5.config import * +from BaseCPU import BaseCPU + +class DerivO3CPU(BaseCPU): + type = 'DerivO3CPU' + activity = Param.Unsigned("Initial count") + numThreads = Param.Unsigned("number of HW thread contexts") + + checker = Param.BaseCPU(NULL, "checker") + + cachePorts = Param.Unsigned("Cache Ports") + + decodeToFetchDelay = Param.Unsigned("Decode to fetch delay") + renameToFetchDelay = Param.Unsigned("Rename to fetch delay") + iewToFetchDelay = Param.Unsigned("Issue/Execute/Writeback to fetch " + "delay") + commitToFetchDelay = Param.Unsigned("Commit to fetch delay") + fetchWidth = Param.Unsigned("Fetch width") + + renameToDecodeDelay = Param.Unsigned("Rename to decode delay") + iewToDecodeDelay = Param.Unsigned("Issue/Execute/Writeback to decode " + "delay") + commitToDecodeDelay = Param.Unsigned("Commit to decode delay") + fetchToDecodeDelay = Param.Unsigned("Fetch to decode delay") + decodeWidth = Param.Unsigned("Decode width") + + iewToRenameDelay = Param.Unsigned("Issue/Execute/Writeback to rename " + "delay") + commitToRenameDelay = Param.Unsigned("Commit to rename delay") + decodeToRenameDelay = Param.Unsigned("Decode to rename delay") + renameWidth = Param.Unsigned("Rename width") + + commitToIEWDelay = Param.Unsigned("Commit to " + "Issue/Execute/Writeback delay") + renameToIEWDelay = Param.Unsigned("Rename to " + "Issue/Execute/Writeback delay") + issueToExecuteDelay = Param.Unsigned("Issue to execute delay (internal " + "to the IEW stage)") + issueWidth = Param.Unsigned("Issue width") + executeWidth = Param.Unsigned("Execute width") + executeIntWidth = Param.Unsigned("Integer execute width") + executeFloatWidth = Param.Unsigned("Floating point execute width") + executeBranchWidth = Param.Unsigned("Branch execute width") + executeMemoryWidth = Param.Unsigned("Memory execute width") + fuPool = Param.FUPool(NULL, "Functional Unit pool") + + iewToCommitDelay = Param.Unsigned("Issue/Execute/Writeback to commit " + "delay") + renameToROBDelay = Param.Unsigned("Rename to reorder buffer delay") + commitWidth = Param.Unsigned("Commit width") + squashWidth = Param.Unsigned("Squash width") + trapLatency = Param.Tick("Trap latency") + fetchTrapLatency = Param.Tick("Fetch trap latency") + + predType = Param.String("Branch predictor type ('local', 'tournament')") + localPredictorSize = Param.Unsigned("Size of local predictor") + localCtrBits = Param.Unsigned("Bits per counter") + localHistoryTableSize = Param.Unsigned("Size of local history table") + localHistoryBits = Param.Unsigned("Bits for the local history") + globalPredictorSize = Param.Unsigned("Size of global predictor") + globalCtrBits = Param.Unsigned("Bits per counter") + globalHistoryBits = Param.Unsigned("Bits of history") + choicePredictorSize = Param.Unsigned("Size of choice predictor") + choiceCtrBits = Param.Unsigned("Bits of choice counters") + + BTBEntries = Param.Unsigned("Number of BTB entries") + BTBTagSize = Param.Unsigned("Size of the BTB tags, in bits") + + RASSize = Param.Unsigned("RAS size") + + LQEntries = Param.Unsigned("Number of load queue entries") + SQEntries = Param.Unsigned("Number of store queue entries") + LFSTSize = Param.Unsigned("Last fetched store table size") + SSITSize = Param.Unsigned("Store set ID table size") + + numRobs = Param.Unsigned("Number of Reorder Buffers"); + + numPhysIntRegs = Param.Unsigned("Number of physical integer registers") + numPhysFloatRegs = Param.Unsigned("Number of physical floating point " + "registers") + numIQEntries = Param.Unsigned("Number of instruction queue entries") + numROBEntries = Param.Unsigned("Number of reorder buffer entries") + + instShiftAmt = Param.Unsigned("Number of bits to shift instructions by") + + function_trace = Param.Bool(False, "Enable function trace") + function_trace_start = Param.Tick(0, "Cycle to start function trace") + + smtNumFetchingThreads = Param.Unsigned("SMT Number of Fetching Threads") + smtFetchPolicy = Param.String("SMT Fetch policy") + smtLSQPolicy = Param.String("SMT LSQ Sharing Policy") + smtLSQThreshold = Param.String("SMT LSQ Threshold Sharing Parameter") + smtIQPolicy = Param.String("SMT IQ Sharing Policy") + smtIQThreshold = Param.String("SMT IQ Threshold Sharing Parameter") + smtROBPolicy = Param.String("SMT ROB Sharing Policy") + smtROBThreshold = Param.String("SMT ROB Threshold Sharing Parameter") + smtCommitPolicy = Param.String("SMT Commit Policy") diff --git a/src/python/m5/objects/OzoneCPU.py b/src/python/m5/objects/OzoneCPU.py new file mode 100644 index 000000000..8aff89203 --- /dev/null +++ b/src/python/m5/objects/OzoneCPU.py @@ -0,0 +1,88 @@ +from m5 import build_env +from m5.config import * +from BaseCPU import BaseCPU + +class DerivOzoneCPU(BaseCPU): + type = 'DerivOzoneCPU' + + numThreads = Param.Unsigned("number of HW thread contexts") + + checker = Param.BaseCPU("Checker CPU") + + width = Param.Unsigned("Width") + frontEndWidth = Param.Unsigned("Front end width") + backEndWidth = Param.Unsigned("Back end width") + backEndSquashLatency = Param.Unsigned("Back end squash latency") + backEndLatency = Param.Unsigned("Back end latency") + maxInstBufferSize = Param.Unsigned("Maximum instruction buffer size") + maxOutstandingMemOps = Param.Unsigned("Maximum number of outstanding memory operations") + decodeToFetchDelay = Param.Unsigned("Decode to fetch delay") + renameToFetchDelay = Param.Unsigned("Rename to fetch delay") + iewToFetchDelay = Param.Unsigned("Issue/Execute/Writeback to fetch " + "delay") + commitToFetchDelay = Param.Unsigned("Commit to fetch delay") + fetchWidth = Param.Unsigned("Fetch width") + + renameToDecodeDelay = Param.Unsigned("Rename to decode delay") + iewToDecodeDelay = Param.Unsigned("Issue/Execute/Writeback to decode " + "delay") + commitToDecodeDelay = Param.Unsigned("Commit to decode delay") + fetchToDecodeDelay = Param.Unsigned("Fetch to decode delay") + decodeWidth = Param.Unsigned("Decode width") + + iewToRenameDelay = Param.Unsigned("Issue/Execute/Writeback to rename " + "delay") + commitToRenameDelay = Param.Unsigned("Commit to rename delay") + decodeToRenameDelay = Param.Unsigned("Decode to rename delay") + renameWidth = Param.Unsigned("Rename width") + + commitToIEWDelay = Param.Unsigned("Commit to " + "Issue/Execute/Writeback delay") + renameToIEWDelay = Param.Unsigned("Rename to " + "Issue/Execute/Writeback delay") + issueToExecuteDelay = Param.Unsigned("Issue to execute delay (internal " + "to the IEW stage)") + issueWidth = Param.Unsigned("Issue width") + executeWidth = Param.Unsigned("Execute width") + executeIntWidth = Param.Unsigned("Integer execute width") + executeFloatWidth = Param.Unsigned("Floating point execute width") + executeBranchWidth = Param.Unsigned("Branch execute width") + executeMemoryWidth = Param.Unsigned("Memory execute width") + + iewToCommitDelay = Param.Unsigned("Issue/Execute/Writeback to commit " + "delay") + renameToROBDelay = Param.Unsigned("Rename to reorder buffer delay") + commitWidth = Param.Unsigned("Commit width") + squashWidth = Param.Unsigned("Squash width") + + predType = Param.String("Type of branch predictor ('local', 'tournament')") + localPredictorSize = Param.Unsigned("Size of local predictor") + localCtrBits = Param.Unsigned("Bits per counter") + localHistoryTableSize = Param.Unsigned("Size of local history table") + localHistoryBits = Param.Unsigned("Bits for the local history") + globalPredictorSize = Param.Unsigned("Size of global predictor") + globalCtrBits = Param.Unsigned("Bits per counter") + globalHistoryBits = Param.Unsigned("Bits of history") + choicePredictorSize = Param.Unsigned("Size of choice predictor") + choiceCtrBits = Param.Unsigned("Bits of choice counters") + + BTBEntries = Param.Unsigned("Number of BTB entries") + BTBTagSize = Param.Unsigned("Size of the BTB tags, in bits") + + RASSize = Param.Unsigned("RAS size") + + LQEntries = Param.Unsigned("Number of load queue entries") + SQEntries = Param.Unsigned("Number of store queue entries") + LFSTSize = Param.Unsigned("Last fetched store table size") + SSITSize = Param.Unsigned("Store set ID table size") + + numPhysIntRegs = Param.Unsigned("Number of physical integer registers") + numPhysFloatRegs = Param.Unsigned("Number of physical floating point " + "registers") + numIQEntries = Param.Unsigned("Number of instruction queue entries") + numROBEntries = Param.Unsigned("Number of reorder buffer entries") + + instShiftAmt = Param.Unsigned("Number of bits to shift instructions by") + + function_trace = Param.Bool(False, "Enable function trace") + function_trace_start = Param.Tick(0, "Cycle to start function trace") diff --git a/src/python/m5/objects/Pci.py b/src/python/m5/objects/Pci.py index 85cefcd44..9e1e91b13 100644 --- a/src/python/m5/objects/Pci.py +++ b/src/python/m5/objects/Pci.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice, DmaDevice class PciConfigData(SimObject): diff --git a/src/python/m5/objects/PhysicalMemory.py b/src/python/m5/objects/PhysicalMemory.py index c59910093..9cc7510a2 100644 --- a/src/python/m5/objects/PhysicalMemory.py +++ b/src/python/m5/objects/PhysicalMemory.py @@ -1,8 +1,9 @@ -from m5 import * +from m5.config import * from MemObject import * class PhysicalMemory(MemObject): type = 'PhysicalMemory' + port = Port("the access port") range = Param.AddrRange("Device Address") file = Param.String('', "memory mapped file") latency = Param.Latency(Parent.clock, "latency of an access") diff --git a/src/python/m5/objects/Platform.py b/src/python/m5/objects/Platform.py index 4da0ffab4..89fee9991 100644 --- a/src/python/m5/objects/Platform.py +++ b/src/python/m5/objects/Platform.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Platform(SimObject): type = 'Platform' abstract = True diff --git a/src/python/m5/objects/Process.py b/src/python/m5/objects/Process.py index 60b00229e..0091d8654 100644 --- a/src/python/m5/objects/Process.py +++ b/src/python/m5/objects/Process.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Process(SimObject): type = 'Process' abstract = True diff --git a/src/python/m5/objects/Repl.py b/src/python/m5/objects/Repl.py index afd256082..8e9f1094f 100644 --- a/src/python/m5/objects/Repl.py +++ b/src/python/m5/objects/Repl.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class Repl(SimObject): type = 'Repl' abstract = True diff --git a/src/python/m5/objects/Root.py b/src/python/m5/objects/Root.py index 205a93c76..373475a7a 100644 --- a/src/python/m5/objects/Root.py +++ b/src/python/m5/objects/Root.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Serialize import Serialize from Statistics import Statistics from Trace import Trace diff --git a/src/python/m5/objects/SimConsole.py b/src/python/m5/objects/SimConsole.py index df3061908..9e1452c6d 100644 --- a/src/python/m5/objects/SimConsole.py +++ b/src/python/m5/objects/SimConsole.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class ConsoleListener(SimObject): type = 'ConsoleListener' port = Param.TcpPort(3456, "listen port") diff --git a/src/python/m5/objects/SimpleDisk.py b/src/python/m5/objects/SimpleDisk.py index e34155ace..44ef709af 100644 --- a/src/python/m5/objects/SimpleDisk.py +++ b/src/python/m5/objects/SimpleDisk.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * class SimpleDisk(SimObject): type = 'SimpleDisk' disk = Param.DiskImage("Disk Image") diff --git a/src/python/m5/objects/AlphaFullCPU.py b/src/python/m5/objects/SimpleOzoneCPU.py index 48989d057..5d968cab0 100644 --- a/src/python/m5/objects/AlphaFullCPU.py +++ b/src/python/m5/objects/SimpleOzoneCPU.py @@ -1,14 +1,21 @@ -from m5 import * +from m5 import build_env +from m5.config import * from BaseCPU import BaseCPU -class DerivAlphaFullCPU(BaseCPU): - type = 'DerivAlphaFullCPU' +class SimpleOzoneCPU(BaseCPU): + type = 'SimpleOzoneCPU' numThreads = Param.Unsigned("number of HW thread contexts") if not build_env['FULL_SYSTEM']: mem = Param.FunctionalMemory(NULL, "memory") + width = Param.Unsigned("Width") + frontEndWidth = Param.Unsigned("Front end width") + backEndWidth = Param.Unsigned("Back end width") + backEndSquashLatency = Param.Unsigned("Back end squash latency") + backEndLatency = Param.Unsigned("Back end latency") + maxInstBufferSize = Param.Unsigned("Maximum instruction buffer size") decodeToFetchDelay = Param.Unsigned("Decode to fetch delay") renameToFetchDelay = Param.Unsigned("Rename to fetch delay") iewToFetchDelay = Param.Unsigned("Issue/Execute/Writeback to fetch " @@ -48,15 +55,15 @@ class DerivAlphaFullCPU(BaseCPU): commitWidth = Param.Unsigned("Commit width") squashWidth = Param.Unsigned("Squash width") - local_predictor_size = Param.Unsigned("Size of local predictor") - local_ctr_bits = Param.Unsigned("Bits per counter") - local_history_table_size = Param.Unsigned("Size of local history table") - local_history_bits = Param.Unsigned("Bits for the local history") - global_predictor_size = Param.Unsigned("Size of global predictor") - global_ctr_bits = Param.Unsigned("Bits per counter") - global_history_bits = Param.Unsigned("Bits of history") - choice_predictor_size = Param.Unsigned("Size of choice predictor") - choice_ctr_bits = Param.Unsigned("Bits of choice counters") + localPredictorSize = Param.Unsigned("Size of local predictor") + localCtrBits = Param.Unsigned("Bits per counter") + localHistoryTableSize = Param.Unsigned("Size of local history table") + localHistoryBits = Param.Unsigned("Bits for the local history") + globalPredictorSize = Param.Unsigned("Size of global predictor") + globalCtrBits = Param.Unsigned("Bits per counter") + globalHistoryBits = Param.Unsigned("Bits of history") + choicePredictorSize = Param.Unsigned("Size of choice predictor") + choiceCtrBits = Param.Unsigned("Bits of choice counters") BTBEntries = Param.Unsigned("Number of BTB entries") BTBTagSize = Param.Unsigned("Size of the BTB tags, in bits") diff --git a/src/python/m5/objects/System.py b/src/python/m5/objects/System.py index 622b5a870..9a1e1d690 100644 --- a/src/python/m5/objects/System.py +++ b/src/python/m5/objects/System.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * class System(SimObject): type = 'System' @@ -7,8 +8,6 @@ class System(SimObject): boot_cpu_frequency = Param.Frequency(Self.cpu[0].clock.frequency, "boot processor frequency") init_param = Param.UInt64(0, "numerical value to pass into simulator") - bin = Param.Bool(False, "is this system binned") - binned_fns = VectorParam.String([], "functions broken down and binned") boot_osflags = Param.String("a", "boot flags to pass to the kernel") kernel = Param.String("file that contains the kernel code") readfile = Param.String("", "file to read startup script from") diff --git a/src/python/m5/objects/Tsunami.py b/src/python/m5/objects/Tsunami.py index 27ea0bce8..4613571d8 100644 --- a/src/python/m5/objects/Tsunami.py +++ b/src/python/m5/objects/Tsunami.py @@ -1,4 +1,4 @@ -from m5 import * +from m5.config import * from Device import BasicPioDevice from Platform import Platform diff --git a/src/python/m5/objects/Uart.py b/src/python/m5/objects/Uart.py index 54754aeb9..8e1fd1a37 100644 --- a/src/python/m5/objects/Uart.py +++ b/src/python/m5/objects/Uart.py @@ -1,4 +1,5 @@ -from m5 import * +from m5 import build_env +from m5.config import * from Device import BasicPioDevice class Uart(BasicPioDevice): diff --git a/src/python/m5/smartdict.py b/src/python/m5/smartdict.py index cd38d7326..d85dbd517 100644 --- a/src/python/m5/smartdict.py +++ b/src/python/m5/smartdict.py @@ -23,6 +23,8 @@ # 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. +# +# Authors: Nathan Binkert # The SmartDict class fixes a couple of issues with using the content # of os.environ or similar dicts of strings as Python variables: diff --git a/src/sim/async.hh b/src/sim/async.hh index e0190a133..50ae73040 100644 --- a/src/sim/async.hh +++ b/src/sim/async.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __ASYNC_HH__ diff --git a/src/sim/builder.cc b/src/sim/builder.cc index 7dc118985..9074cc899 100644 --- a/src/sim/builder.cc +++ b/src/sim/builder.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <assert.h> @@ -31,17 +33,14 @@ #include "base/inifile.hh" #include "base/misc.hh" #include "sim/builder.hh" -#include "sim/configfile.hh" -#include "sim/config_node.hh" #include "sim/host.hh" #include "sim/sim_object.hh" #include "sim/root.hh" using namespace std; -SimObjectBuilder::SimObjectBuilder(ConfigNode *_configNode) - : ParamContext(_configNode->getPath(), NoAutoInit), - configNode(_configNode) +SimObjectBuilder::SimObjectBuilder(const std::string &_iniSection) + : ParamContext(_iniSection, NoAutoInit) { } @@ -76,8 +75,7 @@ SimObjectBuilder::parseParams(IniFile &iniFile) void SimObjectBuilder::printErrorProlog(ostream &os) { - ccprintf(os, "Error creating object '%s' of type '%s':\n", - iniSection, configNode->getType()); + ccprintf(os, "Error creating object '%s':\n", iniSection); } @@ -110,9 +108,13 @@ SimObjectClass::SimObjectClass(const string &className, CreateFunc createFunc) // // SimObject * -SimObjectClass::createObject(IniFile &configDB, ConfigNode *configNode) +SimObjectClass::createObject(IniFile &configDB, const std::string &iniSection) { - const string &type = configNode->getType(); + string type; + if (!configDB.find(iniSection, "type", type)) { + // no C++ type associated with this object + return NULL; + } // look up className to get appropriate createFunc if (classMap->find(type) == classMap->end()) @@ -123,7 +125,7 @@ SimObjectClass::createObject(IniFile &configDB, ConfigNode *configNode) // call createFunc with config hierarchy node to get object // builder instance (context with parameters for object creation) - SimObjectBuilder *objectBuilder = (*createFunc)(configNode); + SimObjectBuilder *objectBuilder = (*createFunc)(iniSection); assert(objectBuilder != NULL); @@ -164,7 +166,7 @@ SimObjectClass::describeAllClasses(ostream &os) os << "[" << className << "]\n"; // create dummy object builder just to instantiate parameters - SimObjectBuilder *objectBuilder = (*createFunc)(NULL); + SimObjectBuilder *objectBuilder = (*createFunc)(""); // now get the object builder to describe ite params objectBuilder->describeParams(os); diff --git a/src/sim/builder.hh b/src/sim/builder.hh index b32e00e76..2997fe5c3 100644 --- a/src/sim/builder.hh +++ b/src/sim/builder.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __BUILDER_HH__ @@ -53,14 +55,8 @@ class SimObject; // class SimObjectBuilder : public ParamContext { - private: - // The corresponding node in the configuration hierarchy. - // (optional: may be null if the created object is not in the - // hierarchy) - ConfigNode *configNode; - public: - SimObjectBuilder(ConfigNode *_configNode); + SimObjectBuilder(const std::string &_iniSection); virtual ~SimObjectBuilder(); @@ -75,9 +71,6 @@ class SimObjectBuilder : public ParamContext // configuration hierarchy node label and position) virtual const std::string &getInstanceName() { return iniSection; } - // return the configuration hierarchy node for this context. - virtual ConfigNode *getConfigNode() { return configNode; } - // Create the actual SimObject corresponding to the parameter // values in this context. This function is overridden in derived // classes to call a specific constructor for a particular @@ -123,7 +116,7 @@ class SimObjectClass // for the object (specified by the second string argument), and // an optional config hierarchy node (specified by the third // argument). A pointer to the new SimObjectBuilder is returned. - typedef SimObjectBuilder *(*CreateFunc)(ConfigNode *configNode); + typedef SimObjectBuilder *(*CreateFunc)(const std::string &iniSection); static std::map<std::string,CreateFunc> *classMap; @@ -135,7 +128,8 @@ class SimObjectClass // create SimObject given name of class and pointer to // configuration hierarchy node - static SimObject *createObject(IniFile &configDB, ConfigNode *configNode); + static SimObject *createObject(IniFile &configDB, + const std::string &iniSection); // print descriptions of all parameters registered with all // SimObject classes @@ -154,15 +148,15 @@ class OBJ_CLASS##Builder : public SimObjectBuilder \ #define END_DECLARE_SIM_OBJECT_PARAMS(OBJ_CLASS) \ \ - OBJ_CLASS##Builder(ConfigNode *configNode); \ + OBJ_CLASS##Builder(const std::string &iniSection); \ virtual ~OBJ_CLASS##Builder() {} \ \ OBJ_CLASS *create(); \ }; #define BEGIN_INIT_SIM_OBJECT_PARAMS(OBJ_CLASS) \ -OBJ_CLASS##Builder::OBJ_CLASS##Builder(ConfigNode *configNode) \ - : SimObjectBuilder(configNode), + OBJ_CLASS##Builder::OBJ_CLASS##Builder(const std::string &iSec) \ + : SimObjectBuilder(iSec), #define END_INIT_SIM_OBJECT_PARAMS(OBJ_CLASS) \ @@ -174,9 +168,9 @@ OBJ_CLASS *OBJ_CLASS##Builder::create() #define REGISTER_SIM_OBJECT(CLASS_NAME, OBJ_CLASS) \ SimObjectBuilder * \ -new##OBJ_CLASS##Builder(ConfigNode *configNode) \ +new##OBJ_CLASS##Builder(const std::string &iniSection) \ { \ - return new OBJ_CLASS##Builder(configNode); \ + return new OBJ_CLASS##Builder(iniSection); \ } \ \ SimObjectClass the##OBJ_CLASS##Class(CLASS_NAME, \ diff --git a/src/sim/byteswap.hh b/src/sim/byteswap.hh index a8c5da9d7..f1f244150 100644 --- a/src/sim/byteswap.hh +++ b/src/sim/byteswap.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Ali Saidi + * Nathan Binkert */ //The purpose of this file is to provide endainness conversion utility @@ -33,6 +36,7 @@ #ifndef __SIM_BYTE_SWAP_HH__ #define __SIM_BYTE_SWAP_HH__ +#include "base/misc.hh" #include "sim/host.hh" // This lets us figure out what the byte order of the host system is @@ -46,6 +50,10 @@ #include <machine/endian.h> #endif +#if defined(__APPLE__) +#include <libkern/OSByteOrder.h> +#endif + //These functions actually perform the swapping for parameters //of various bit lengths static inline uint64_t @@ -53,6 +61,8 @@ swap_byte64(uint64_t x) { #if defined(linux) return bswap_64(x); +#elif defined(__APPLE__) + return OSSwapInt64(x); #else return (uint64_t)((((uint64_t)(x) & 0xff) << 56) | ((uint64_t)(x) & 0xff00ULL) << 40 | @@ -70,6 +80,8 @@ swap_byte32(uint32_t x) { #if defined(linux) return bswap_32(x); +#elif defined(__APPLE__) + return OSSwapInt32(x); #else return (uint32_t)(((uint32_t)(x) & 0xff) << 24 | ((uint32_t)(x) & 0xff00) << 8 | ((uint32_t)(x) & 0xff0000) >> 8 | @@ -82,31 +94,31 @@ swap_byte16(uint16_t x) { #if defined(linux) return bswap_16(x); +#elif defined(__APPLE__) + return OSSwapInt16(x); #else return (uint16_t)(((uint16_t)(x) & 0xff) << 8 | ((uint16_t)(x) & 0xff00) >> 8); #endif } -//This lets the compiler figure out how to call the swap_byte functions above -//for different data types. -static inline uint64_t swap_byte(uint64_t x) {return swap_byte64(x);} -static inline int64_t swap_byte(int64_t x) {return swap_byte64((uint64_t)x);} -static inline uint32_t swap_byte(uint32_t x) {return swap_byte32(x);} -static inline int32_t swap_byte(int32_t x) {return swap_byte32((uint32_t)x);} -//This is to prevent the following two functions from compiling on -//64bit machines. It won't detect everything, so it should be changed. -#ifndef __x86_64__ -static inline long swap_byte(long x) {return swap_byte32((long)x);} -static inline unsigned long swap_byte(unsigned long x) - { return swap_byte32((unsigned long)x);} -#endif -static inline uint16_t swap_byte(uint16_t x) {return swap_byte32(x);} -static inline int16_t swap_byte(int16_t x) {return swap_byte16((uint16_t)x);} -static inline uint8_t swap_byte(uint8_t x) {return x;} -static inline int8_t swap_byte(int8_t x) {return x;} -static inline double swap_byte(double x) {return swap_byte64((uint64_t)x);} -static inline float swap_byte(float x) {return swap_byte32((uint32_t)x);} +// This function lets the compiler figure out how to call the +// swap_byte functions above for different data types. Since the +// sizeof() values are known at compiel time, it should inline to a +// direct call to the right swap_byteNN() function. +template <typename T> +static inline T swap_byte(T x) { + if (sizeof(T) == 8) + return swap_byte64((uint64_t)x); + else if (sizeof(T) == 4) + return swap_byte32((uint32_t)x); + else if (sizeof(T) == 2) + return swap_byte16((uint16_t)x); + else if (sizeof(T) == 1) + return x; + else + panic("Can't byte-swap values larger than 64 bits"); +} //The conversion functions with fixed endianness on both ends don't need to //be in a namespace diff --git a/src/sim/debug.cc b/src/sim/debug.cc index 09e5346a8..be9566836 100644 --- a/src/sim/debug.cc +++ b/src/sim/debug.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <sys/types.h> @@ -124,12 +127,12 @@ DebugContext::checkParams() // handy function to schedule DebugBreakEvent on main event queue // (callable from debugger) // -extern "C" void sched_break_cycle(Tick when) +void sched_break_cycle(Tick when) { new DebugBreakEvent(&mainEventQueue, when); } -extern "C" void eventq_dump() +void eventq_dump() { mainEventQueue.dump(); } diff --git a/src/sim/debug.hh b/src/sim/debug.hh index 75b261d80..79792234b 100644 --- a/src/sim/debug.hh +++ b/src/sim/debug.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __DEBUG_HH__ diff --git a/src/sim/eventq.cc b/src/sim/eventq.cc index 4bfd6face..6ae838897 100644 --- a/src/sim/eventq.cc +++ b/src/sim/eventq.cc @@ -24,6 +24,10 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + * Steve Raasch */ #include <assert.h> @@ -98,7 +102,7 @@ EventQueue::remove(Event *event) prev->next = curr->next; } -void +Event * EventQueue::serviceOne() { Event *event = head; @@ -106,13 +110,20 @@ EventQueue::serviceOne() head = event->next; // handle action - if (!event->squashed()) + if (!event->squashed()) { event->process(); - else + if (event->isExitEvent()) { + assert(!event->getFlags(Event::AutoDelete)); // would be silly + return event; + } + } else { event->clearFlags(Event::Squashed); + } if (event->getFlags(Event::AutoDelete) && !event->scheduled()) delete event; + + return NULL; } diff --git a/src/sim/eventq.hh b/src/sim/eventq.hh index 5fc73bb53..430473df3 100644 --- a/src/sim/eventq.hh +++ b/src/sim/eventq.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ /* @file @@ -87,7 +90,8 @@ class Event : public Serializable, public FastAlloc Squashed = 0x1, Scheduled = 0x2, AutoDelete = 0x4, - AutoSerialize = 0x8 + AutoSerialize = 0x8, + IsExitEvent = 0x10 }; bool getFlags(Flags f) const { return (_flags & f) == f; } @@ -211,6 +215,9 @@ class Event : public Serializable, public FastAlloc /// Check whether the event is squashed bool squashed() { return getFlags(Squashed); } + /// See if this is a SimExitEvent (without resorting to RTTI) + bool isExitEvent() { return getFlags(IsExitEvent); } + /// Get the time that the event is scheduled Tick when() const { return _when; } @@ -295,7 +302,7 @@ class EventQueue : public Serializable void reschedule(Event *ev); Tick nextTick() { return head->when(); } - void serviceOne(); + Event *serviceOne(); // process all events up to the given timestamp. we inline a // quick test to see if there are any events to process; if so, diff --git a/src/sim/faults.cc b/src/sim/faults.cc index cb095f852..650b728f7 100644 --- a/src/sim/faults.cc +++ b/src/sim/faults.cc @@ -24,29 +24,32 @@ * 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. + * + * Authors: Nathan Binkert + * Gabe Black */ #include "base/misc.hh" #include "sim/faults.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #if !FULL_SYSTEM -void FaultBase::invoke(ExecContext * xc) +void FaultBase::invoke(ThreadContext * tc) { - fatal("fault (%s) detected @ PC 0x%08p", name(), xc->readPC()); + fatal("fault (%s) detected @ PC 0x%08p", name(), tc->readPC()); } #else -void FaultBase::invoke(ExecContext * xc) +void FaultBase::invoke(ThreadContext * tc) { - DPRINTF(Fault, "Fault %s at PC: %#x\n", name(), xc->readPC()); - xc->getCpuPtr()->recordEvent(csprintf("Fault %s", name())); + DPRINTF(Fault, "Fault %s at PC: %#x\n", name(), tc->readPC()); + tc->getCpuPtr()->recordEvent(csprintf("Fault %s", name())); - assert(!xc->misspeculating()); + assert(!tc->misspeculating()); } #endif -void UnimpFault::invoke(ExecContext * xc) +void UnimpFault::invoke(ThreadContext * tc) { panic("Unimpfault: %s\n", panicStr.c_str()); } diff --git a/src/sim/faults.hh b/src/sim/faults.hh index 9b3bc9103..00264d8fc 100644 --- a/src/sim/faults.hh +++ b/src/sim/faults.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Gabe Black */ #ifndef __FAULTS_HH__ @@ -33,7 +36,7 @@ #include "sim/stats.hh" #include "config/full_system.hh" -class ExecContext; +class ThreadContext; class FaultBase; typedef RefCountingPtr<FaultBase> Fault; @@ -51,11 +54,7 @@ class FaultBase : public RefCounted { public: virtual FaultName name() = 0; -#if FULL_SYSTEM - virtual void invoke(ExecContext * xc); -#else - virtual void invoke(ExecContext * xc); -#endif + virtual void invoke(ThreadContext * tc); // template<typename T> // bool isA() {return dynamic_cast<T *>(this);} virtual bool isMachineCheckFault() {return false;} @@ -74,7 +73,7 @@ class UnimpFault : public FaultBase { } FaultName name() {return "Unimplemented simulator feature";} - void invoke(ExecContext * xc); + void invoke(ThreadContext * tc); }; #endif // __FAULTS_HH__ diff --git a/src/sim/host.hh b/src/sim/host.hh index 48c977331..9c79580b1 100644 --- a/src/sim/host.hh +++ b/src/sim/host.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ /** @@ -54,6 +56,8 @@ typedef int64_t Counter; */ typedef int64_t Tick; +const Tick MaxTick = (1LL << 62); + /** * Address type * This will probably be moved somewhere else in the near future. diff --git a/src/sim/main.cc b/src/sim/main.cc index aecc171ed..3eb7fa95d 100644 --- a/src/sim/main.cc +++ b/src/sim/main.cc @@ -24,24 +24,30 @@ * 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. + * + * Authors: Steve Raasch + * Nathan Binkert + * Steve Reinhardt */ /// /// @file sim/main.cc /// +#include <Python.h> // must be before system headers... see Python docs + #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <libgen.h> #include <stdlib.h> #include <signal.h> +#include <getopt.h> #include <list> #include <string> #include <vector> -#include "base/copyright.hh" -#include "base/embedfile.hh" +#include "base/callback.hh" #include "base/inifile.hh" #include "base/misc.hh" #include "base/output.hh" @@ -51,11 +57,12 @@ #include "base/time.hh" #include "cpu/base.hh" #include "cpu/smt.hh" -#include "python/pyconfig.hh" +#include "mem/mem_object.hh" +#include "mem/port.hh" #include "sim/async.hh" #include "sim/builder.hh" -#include "sim/configfile.hh" #include "sim/host.hh" +#include "sim/serialize.hh" #include "sim/sim_events.hh" #include "sim/sim_exit.hh" #include "sim/sim_object.hh" @@ -119,31 +126,39 @@ showBriefHelp(ostream &out) ccprintf(out, "Usage:\n"); ccprintf(out, -"%s [-d <dir>] [-E <var>[=<val>]] [-I <dir>] [-P <python>]\n" -" [--<var>=<val>] <config file>\n" +"%s [-p <path>] [-i ] [-h] <config file>\n" "\n" -" -d set the output directory to <dir>\n" -" -E set the environment variable <var> to <val> (or 'True')\n" -" -I add the directory <dir> to python's path\n" -" -P execute <python> directly in the configuration\n" -" --var=val set the python variable <var> to '<val>'\n" -" <configfile> config file name (ends in .py)\n\n", +" -p, --path <path> prepends <path> to PYTHONPATH instead of using\n" +" built-in zip archive. Useful when developing/debugging\n" +" changes to built-in Python libraries, as the new Python\n" +" can be tested without building a new m5 binary.\n\n" +" -i, --interactive forces entry into interactive mode after the supplied\n" +" script is executed (just like the -i option to the\n" +" Python interpreter).\n\n" +" -h Prints this help\n\n" +" <configfile> config file name which ends in .py. (Normally you can\n" +" run <configfile> --help to get help on that config files\n" +" parameters.\n\n", prog); - ccprintf(out, "%s -X\n -X extract embedded files\n\n", prog); - ccprintf(out, "%s -h\n -h print short help\n\n", prog); } +const char *briefCopyright = +"Copyright (c) 2001-2006\n" +"The Regents of The University of Michigan\n" +"All Rights Reserved\n"; + /// Print welcome message. void sayHello(ostream &out) { - extern const char *compileDate; // from date.cc + extern const char *compileDate; // from date.cc ccprintf(out, "M5 Simulator System\n"); // display copyright ccprintf(out, "%s\n", briefCopyright); - ccprintf(out, "M5 compiled on %d\n", compileDate); + ccprintf(out, "M5 compiled %d\n", compileDate); + ccprintf(out, "M5 started %s\n", Time::start); char *host = getenv("HOSTNAME"); if (!host) @@ -151,66 +166,19 @@ sayHello(ostream &out) if (host) ccprintf(out, "M5 executing on %s\n", host); - - ccprintf(out, "M5 simulation started %s\n", Time::start); -} - -/// -/// Echo the command line for posterity in such a way that it can be -/// used to rerun the same simulation (given the same .ini files). -/// -void -echoCommandLine(int argc, char **argv, ostream &out) -{ - out << "command line: " << argv[0]; - for (int i = 1; i < argc; i++) { - string arg(argv[i]); - - out << ' '; - - // If the arg contains spaces, we need to quote it. - // The rest of this is overkill to make it look purty. - - // print dashes first outside quotes - int non_dash_pos = arg.find_first_not_of("-"); - out << arg.substr(0, non_dash_pos); // print dashes - string body = arg.substr(non_dash_pos); // the rest - - // if it's an assignment, handle the lhs & rhs separately - int eq_pos = body.find("="); - if (eq_pos == string::npos) { - out << quote(body); - } - else { - string lhs(body.substr(0, eq_pos)); - string rhs(body.substr(eq_pos + 1)); - - out << quote(lhs) << "=" << quote(rhs); - } - } - out << endl << endl; } -char * -getOptionString(int &index, int argc, char **argv) -{ - char *option = argv[index] + 2; - if (*option != '\0') - return option; - - // We didn't find an argument, it must be in the next variable. - if (++index >= argc) - panic("option string for option '%s' not found", argv[index - 1]); - return argv[index]; -} +extern "C" { void init_cc_main(); } int main(int argc, char **argv) { - // Save off program name + // Saze off program name myProgName = argv[0]; + sayHello(cerr); + signal(SIGFPE, SIG_IGN); // may occur on misspeculated paths signal(SIGTRAP, SIG_IGN); signal(SIGUSR1, dumpStatsHandler); // dump intermediate stats @@ -218,181 +186,309 @@ main(int argc, char **argv) signal(SIGINT, exitNowHandler); // dump final stats and exit signal(SIGABRT, abortHandler); - bool configfile_found = false; - PythonConfig pyconfig; - string outdir; + Py_SetProgramName(argv[0]); + + // default path to m5 python code is the currently executing + // file... Python ZipImporter will find embedded zip archive + char *pythonpath = argv[0]; + + bool interactive = false; + bool show_help = false; + bool getopt_done = false; + int opt_index = 0; + + static struct option long_options[] = { + {"python", 1, 0, 'p'}, + {"interactive", 0, 0, 'i'}, + {"help", 0, 0, 'h'}, + {0,0,0,0} + }; + + do { + switch (getopt_long(argc, argv, "+p:ih", long_options, &opt_index)) { + // -p <path> prepends <path> to PYTHONPATH instead of + // using built-in zip archive. Useful when + // developing/debugging changes to built-in Python + // libraries, as the new Python can be tested without + // building a new m5 binary. + case 'p': + pythonpath = optarg; + break; + + // -i forces entry into interactive mode after the + // supplied script is executed (just like the -i option to + // the Python interpreter). + case 'i': + interactive = true; + break; + + case 'h': + show_help = true; + break; + case -1: + getopt_done = true; + break; + + default: + fatal("Unrecognized option %c\n", optopt); + } + } while (!getopt_done); - if (argc < 2) { + if (show_help) { showBriefHelp(cerr); exit(1); } - sayHello(cerr); + // Fix up argc & argv to hide arguments we just processed. + // getopt() sets optind to the index of the first non-processed + // argv element. + argc -= optind; + argv += optind; - // Parse command-line options. - // Since most of the complex options are handled through the - // config database, we don't mess with getopts, and just parse - // manually. - for (int i = 1; i < argc; ++i) { - char *arg_str = argv[i]; - - // if arg starts with '--', parse as a special python option - // of the format --<python var>=<string value>, if the arg - // starts with '-', it should be a simulator option with a - // format similar to getopt. In any other case, treat the - // option as a configuration file name and load it. - if (arg_str[0] == '-' && arg_str[1] == '-') { - string str = &arg_str[2]; - string var, val; - - if (!split_first(str, var, val, '=')) - panic("Could not parse configuration argument '%s'\n" - "Expecting --<variable>=<value>\n", arg_str); - - pyconfig.setVariable(var, val); - } else if (arg_str[0] == '-') { - char *option; - string var, val; - - // switch on second char - switch (arg_str[1]) { - case 'd': - outdir = getOptionString(i, argc, argv); - break; - - case 'h': - showBriefHelp(cerr); - exit(1); - - case 'E': - option = getOptionString(i, argc, argv); - if (!split_first(option, var, val, '=')) - val = "True"; - - if (setenv(var.c_str(), val.c_str(), true) == -1) - panic("setenv: %s\n", strerror(errno)); - break; - - case 'I': - option = getOptionString(i, argc, argv); - pyconfig.addPath(option); - break; - - case 'P': - option = getOptionString(i, argc, argv); - pyconfig.writeLine(option); - break; - - case 'X': { - list<EmbedFile> lst; - EmbedMap::all(lst); - list<EmbedFile>::iterator i = lst.begin(); - list<EmbedFile>::iterator end = lst.end(); - - while (i != end) { - cprintf("Embedded File: %s\n", i->name); - cout.write(i->data, i->length); - ++i; - } - - return 0; - } - - default: - showBriefHelp(cerr); - panic("invalid argument '%s'\n", arg_str); - } - } else { - string file(arg_str); - string base, ext; + // Set up PYTHONPATH to make sure the m5 module is found + string newpath(pythonpath); + + char *oldpath = getenv("PYTHONPATH"); + if (oldpath != NULL) { + newpath += ":"; + newpath += oldpath; + } - if (!split_last(file, base, ext, '.') || ext != "py") - panic("Config file '%s' must end in '.py'\n", file); + if (setenv("PYTHONPATH", newpath.c_str(), true) == -1) + fatal("setenv: %s\n", strerror(errno)); - pyconfig.load(file); - configfile_found = true; + // initialize embedded Python interpreter + Py_Initialize(); + PySys_SetArgv(argc, argv); + + // initialize SWIG 'cc_main' module + init_cc_main(); + + if (argc > 0) { + // extra arg(s): first is script file, remaining ones are args + // to script file + char *filename = argv[0]; + FILE *fp = fopen(filename, "r"); + if (!fp) { + fatal("cannot open file '%s'\n", filename); } + + PyRun_AnyFile(fp, filename); + } else { + // no script file argument... force interactive prompt + interactive = true; } - if (outdir.empty()) { - char *env = getenv("OUTPUT_DIR"); - outdir = env ? env : "."; + if (interactive) { + // The following code to import readline was copied from Python + // 2.4.3's Modules/main.c. + // Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 + // Python Software Foundation; All Rights Reserved + // We should only enable this if we're actually using an + // interactive prompt. + PyObject *v; + v = PyImport_ImportModule("readline"); + if (v == NULL) + PyErr_Clear(); + else + Py_DECREF(v); + + PyRun_InteractiveLoop(stdin, "stdin"); } - simout.setDirectory(outdir); + // clean up Python intepreter. + Py_Finalize(); +} - char *env = getenv("CONFIG_OUTPUT"); - if (!env) - env = "config.out"; - configStream = simout.find(env); - if (!configfile_found) - panic("no configuration file specified!"); +void +setOutputDir(const string &dir) +{ + simout.setDirectory(dir); +} + + +IniFile inifile; + +SimObject * +createSimObject(const string &name) +{ + return SimObjectClass::createObject(inifile, name); +} + + +/** + * Pointer to the Python function that maps names to SimObjects. + */ +PyObject *resolveFunc = NULL; + +/** + * Convert a pointer to the Python object that SWIG wraps around a C++ + * SimObject pointer back to the actual C++ pointer. See main.i. + */ +extern "C" SimObject *convertSwigSimObjectPtr(PyObject *); + + +SimObject * +resolveSimObject(const string &name) +{ + PyObject *pyPtr = PyEval_CallFunction(resolveFunc, "(s)", name.c_str()); + if (pyPtr == NULL) { + PyErr_Print(); + panic("resolveSimObject: failure on call to Python for %s", name); + } + + SimObject *simObj = convertSwigSimObjectPtr(pyPtr); + if (simObj == NULL) + panic("resolveSimObject: failure on pointer conversion for %s", name); + + return simObj; +} + + +/** + * Load config.ini into C++ database. Exported to Python via SWIG; + * invoked from m5.instantiate(). + */ +void +loadIniFile(PyObject *_resolveFunc) +{ + resolveFunc = _resolveFunc; + configStream = simout.find("config.out"); // The configuration database is now complete; start processing it. - IniFile inifile; - if (!pyconfig.output(inifile)) - panic("Error processing python code"); + inifile.load("config.ini"); // Initialize statistics database Stats::InitSimStats(); +} - // Now process the configuration hierarchy and create the SimObjects. - ConfigHierarchy configHierarchy(inifile); - configHierarchy.build(); - configHierarchy.createSimObjects(); +/** + * Look up a MemObject port. Helper function for connectPorts(). + */ +Port * +lookupPort(SimObject *so, const std::string &name, int i) +{ + MemObject *mo = dynamic_cast<MemObject *>(so); + if (mo == NULL) { + warn("error casting SimObject %s to MemObject", so->name()); + return NULL; + } + + Port *p = mo->getPort(name, i); + if (p == NULL) + warn("error looking up port %s on object %s", name, so->name()); + return p; +} + + +/** + * Connect the described MemObject ports. Called from Python via SWIG. + */ +int +connectPorts(SimObject *o1, const std::string &name1, int i1, + SimObject *o2, const std::string &name2, int i2) +{ + Port *p1 = lookupPort(o1, name1, i1); + Port *p2 = lookupPort(o2, name2, i2); + + if (p1 == NULL || p2 == NULL) { + warn("connectPorts: port lookup error"); + return 0; + } + + p1->setPeer(p2); + p2->setPeer(p1); + + return 1; +} + +/** + * Do final initialization steps after object construction but before + * start of simulation. + */ +void +finalInit() +{ // Parse and check all non-config-hierarchy parameters. ParamContext::parseAllContexts(inifile); ParamContext::checkAllContexts(); - // Print hello message to stats file if it's actually a file. If - // it's not (i.e. it's cout or cerr) then we already did it above. - if (simout.isFile(*outputStream)) - sayHello(*outputStream); - - // Echo command line and all parameter settings to stats file as well. - echoCommandLine(argc, argv, *outputStream); + // Echo all parameter settings to stats file as well. ParamContext::showAllContexts(*configStream); - // Any objects that can't connect themselves until after construction should - // do so now - SimObject::connectAll(); - // Do a second pass to finish initializing the sim objects SimObject::initAll(); // Restore checkpointed state, if any. +#if 0 configHierarchy.unserializeSimObjects(); - - // Done processing the configuration database. - // Check for unreferenced entries. - if (inifile.printUnreferenced()) - panic("unreferenced sections/entries in the intermediate ini file"); +#endif SimObject::regAllStats(); - // uncomment the following to get PC-based execution-time profile -#ifdef DO_PROFILE - init_profile((char *)&_init, (char *)&_fini); -#endif - // Check to make sure that the stats package is properly initialized Stats::check(); // Reset to put the stats in a consistent state. Stats::reset(); - warn("Entering event queue. Starting simulation...\n"); SimStartup(); - while (!mainEventQueue.empty()) { +} + + +/** Simulate for num_cycles additional cycles. If num_cycles is -1 + * (the default), do not limit simulation; some other event must + * terminate the loop. Exported to Python via SWIG. + * @return The SimLoopExitEvent that caused the loop to exit. + */ +SimLoopExitEvent * +simulate(Tick num_cycles = -1) +{ + warn("Entering event queue @ %d. Starting simulation...\n", curTick); + + // Fix up num_cycles. Special default value -1 means simulate + // "forever"... schedule event at MaxTick just to be safe. + // Otherwise it's a delta for additional cycles to simulate past + // curTick, and thus must be non-negative. + if (num_cycles == -1) + num_cycles = MaxTick; + else if (num_cycles < 0) + fatal("simulate: num_cycles must be >= 0 (was %d)\n", num_cycles); + else + num_cycles = curTick + num_cycles; + + Event *limit_event = new SimLoopExitEvent(num_cycles, + "simulate() limit reached"); + + while (1) { + // there should always be at least one event (the SimLoopExitEvent + // we just scheduled) in the queue + assert(!mainEventQueue.empty()); assert(curTick <= mainEventQueue.nextTick() && "event scheduled in the past"); // forward current cycle to the time of the first event on the // queue curTick = mainEventQueue.nextTick(); - mainEventQueue.serviceOne(); + Event *exit_event = mainEventQueue.serviceOne(); + if (exit_event != NULL) { + // hit some kind of exit event; return to Python + // event must be subclass of SimLoopExitEvent... + SimLoopExitEvent *se_event = dynamic_cast<SimLoopExitEvent *>(exit_event); + if (se_event == NULL) + panic("Bogus exit event class!"); + + // if we didn't hit limit_event, delete it + if (se_event != limit_event) { + assert(limit_event->scheduled()); + limit_event->deschedule(); + delete limit_event; + } + + return se_event; + } if (async_event) { async_event = false; @@ -412,7 +508,7 @@ main(int argc, char **argv) if (async_exit) { async_exit = false; - new SimExitEvent("User requested STOP"); + exitSimLoop("user interrupt received"); } if (async_io || async_alarm) { @@ -423,11 +519,78 @@ main(int argc, char **argv) } } - // This should never happen... every conceivable way for the - // simulation to terminate (hit max cycles/insts, signal, - // simulated system halts/exits) generates an exit event, so we - // should never run out of events on the queue. - exitNow("no events on event loop! All CPUs must be idle.", 1); + // not reached... only exit is return on SimLoopExitEvent +} + +Event * +createCountedQuiesce() +{ + return new CountedQuiesceEvent(); +} + +void +cleanupCountedQuiesce(Event *counted_quiesce) +{ + CountedQuiesceEvent *event = + dynamic_cast<CountedQuiesceEvent *>(counted_quiesce); + if (event == NULL) { + fatal("Called cleanupCountedQuiesce() on an event that was not " + "a CountedQuiesceEvent."); + } + assert(event->getCount() == 0); + delete event; +} + +void +serializeAll() +{ + Serializable::serializeAll(); +} + +void +unserializeAll() +{ + Serializable::unserializeAll(); +} + +/** + * Queue of C++ callbacks to invoke on simulator exit. + */ +CallbackQueue exitCallbacks; + +/** + * Register an exit callback. + */ +void +registerExitCallback(Callback *callback) +{ + exitCallbacks.add(callback); +} + +BaseCPU * +convertToBaseCPUPtr(SimObject *obj) +{ + BaseCPU *ptr = dynamic_cast<BaseCPU *>(obj); + + if (ptr == NULL) + warn("Casting to BaseCPU pointer failed"); + return ptr; +} + +/** + * Do C++ simulator exit processing. Exported to SWIG to be invoked + * when simulator terminates via Python's atexit mechanism. + */ +void +doExitCleanup() +{ + exitCallbacks.process(); + exitCallbacks.clear(); + + cout.flush(); + + ParamContext::cleanupAllContexts(); - return 0; + // print simulation stats + Stats::DumpNow(); } diff --git a/src/sim/param.cc b/src/sim/param.cc index 8998d7d77..b1c50946b 100644 --- a/src/sim/param.cc +++ b/src/sim/param.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #include <algorithm> @@ -37,8 +39,6 @@ #include "base/range.hh" #include "base/str.hh" #include "base/trace.hh" -#include "sim/config_node.hh" -#include "sim/configfile.hh" #include "sim/param.hh" #include "sim/sim_object.hh" @@ -519,7 +519,9 @@ parseSimObjectParam(ParamContext *context, const string &s, SimObject *&value) obj = NULL; } else { - obj = context->resolveSimObject(s); + // defined in main.cc + extern SimObject *resolveSimObject(const string &); + obj = resolveSimObject(s); if (obj == NULL) return false; @@ -694,22 +696,6 @@ ParamContext::printErrorProlog(ostream &os) } // -// Resolve an object name to a SimObject pointer. The object will be -// created as a side-effect if necessary. If the name contains a -// colon (e.g., "iq:IQ"), then the object is local (invisible to -// outside this context). If there is no colon, the name needs to be -// resolved through the configuration hierarchy (only possible for -// SimObjectBuilder objects, which return non-NULL for configNode()). -// -SimObject * -ParamContext::resolveSimObject(const string &name) -{ - ConfigNode *n = getConfigNode(); - return n ? n->resolveSimObject(name) : NULL; -} - - -// // static method: call parseParams() on all registered contexts // void diff --git a/src/sim/param.hh b/src/sim/param.hh index 4a1b8bda1..1bc55c125 100644 --- a/src/sim/param.hh +++ b/src/sim/param.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Steve Reinhardt */ #ifndef __SIM_PARAM_HH__ @@ -34,10 +36,10 @@ #include <string> #include <vector> -#include "sim/configfile.hh" #include "sim/startup.hh" // forward decls +class IniFile; class BaseParam; class SimObject; @@ -130,18 +132,10 @@ class ParamContext : protected StartupCallback // print context information for parameter error virtual void printErrorProlog(std::ostream &); - // resolve a SimObject name in this context to an object pointer. - virtual SimObject *resolveSimObject(const std::string &name); - // generate the name for this instance of this context (used as a // prefix to create unique names in resolveSimObject() virtual const std::string &getInstanceName() { return iniSection; } - // return the configuration hierarchy node for this context. Bare - // ParamContext objects have no corresponding node, so the default - // implementation returns NULL. - virtual ConfigNode *getConfigNode() { return NULL; } - // Parse all parameters registered with all ParamContext objects. static void parseAllContexts(IniFile &iniFile); diff --git a/src/sim/process.cc b/src/sim/process.cc index 1da525093..f989300a3 100644 --- a/src/sim/process.cc +++ b/src/sim/process.cc @@ -24,6 +24,10 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Ali Saidi */ #include <unistd.h> @@ -36,7 +40,7 @@ #include "base/loader/symtab.hh" #include "base/statistics.hh" #include "config/full_system.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/page_table.hh" #include "mem/physical.hh" #include "mem/translating_port.hh" @@ -46,6 +50,20 @@ #include "sim/syscall_emul.hh" #include "sim/system.hh" +#include "arch/isa_specific.hh" +#if THE_ISA == ALPHA_ISA +#include "arch/alpha/linux/process.hh" +#include "arch/alpha/tru64/process.hh" +#elif THE_ISA == SPARC_ISA +#include "arch/sparc/linux/process.hh" +#include "arch/sparc/solaris/process.hh" +#elif THE_ISA == MIPS_ISA +#include "arch/mips/linux/process.hh" +#else +#error "THE_ISA not set" +#endif + + using namespace std; using namespace TheISA; @@ -130,11 +148,11 @@ Process::openOutputFile(const string &filename) int -Process::registerExecContext(ExecContext *xc) +Process::registerThreadContext(ThreadContext *tc) { // add to list - int myIndex = execContexts.size(); - execContexts.push_back(xc); + int myIndex = threadContexts.size(); + threadContexts.push_back(tc); // return CPU number to caller return myIndex; @@ -143,14 +161,14 @@ Process::registerExecContext(ExecContext *xc) void Process::startup() { - if (execContexts.empty()) + if (threadContexts.empty()) fatal("Process %s is not associated with any CPUs!\n", name()); - // first exec context for this process... initialize & enable - ExecContext *xc = execContexts[0]; + // first thread context for this process... initialize & enable + ThreadContext *tc = threadContexts[0]; // mark this context as active so it will start ticking. - xc->activate(0); + tc->activate(0); Port *mem_port; mem_port = system->physmem->getPort("functional"); @@ -160,14 +178,14 @@ Process::startup() } void -Process::replaceExecContext(ExecContext *xc, int xcIndex) +Process::replaceThreadContext(ThreadContext *tc, int tcIndex) { - if (xcIndex >= execContexts.size()) { - panic("replaceExecContext: bad xcIndex, %d >= %d\n", - xcIndex, execContexts.size()); + if (tcIndex >= threadContexts.size()) { + panic("replaceThreadContext: bad tcIndex, %d >= %d\n", + tcIndex, threadContexts.size()); } - execContexts[xcIndex] = xc; + threadContexts[tcIndex] = tc; } // map simulator fd sim_fd to target fd tgt_fd @@ -308,11 +326,10 @@ LiveProcess::argsInit(int intSize, int pageSize) // set bottom of stack stack_min = stack_base - space_needed; // align it - stack_min &= ~(intSize-1); + stack_min = roundDown(stack_min, pageSize); stack_size = stack_base - stack_min; // map memory - pTable->allocate(roundDown(stack_min, pageSize), - roundUp(stack_size, pageSize)); + pTable->allocate(stack_min, roundUp(stack_size, pageSize)); // map out initial stack contents Addr argv_array_base = stack_min + intSize; // room for argc @@ -334,20 +351,23 @@ LiveProcess::argsInit(int intSize, int pageSize) copyStringArray(argv, argv_array_base, arg_data_base, initVirtMem); copyStringArray(envp, envp_array_base, env_data_base, initVirtMem); - execContexts[0]->setIntReg(ArgumentReg0, argc); - execContexts[0]->setIntReg(ArgumentReg1, argv_array_base); - execContexts[0]->setIntReg(StackPointerReg, stack_min); + threadContexts[0]->setIntReg(ArgumentReg0, argc); + threadContexts[0]->setIntReg(ArgumentReg1, argv_array_base); + threadContexts[0]->setIntReg(StackPointerReg, stack_min); Addr prog_entry = objFile->entryPoint(); - execContexts[0]->setPC(prog_entry); - execContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); - execContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); + threadContexts[0]->setPC(prog_entry); + threadContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); + +#if THE_ISA != ALPHA_ISA //e.g. MIPS or Sparc + threadContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); +#endif num_processes++; } void -LiveProcess::syscall(int64_t callnum, ExecContext *xc) +LiveProcess::syscall(int64_t callnum, ThreadContext *tc) { num_syscalls++; @@ -355,7 +375,135 @@ LiveProcess::syscall(int64_t callnum, ExecContext *xc) if (desc == NULL) fatal("Syscall %d out of range", callnum); - desc->doSyscall(callnum, this, xc); + desc->doSyscall(callnum, this, tc); +} + +LiveProcess * +LiveProcess::create(const std::string &nm, System *system, int stdin_fd, + int stdout_fd, int stderr_fd, std::string executable, + std::vector<std::string> &argv, + std::vector<std::string> &envp) +{ + LiveProcess *process = NULL; + + ObjectFile *objFile = createObjectFile(executable); + if (objFile == NULL) { + fatal("Can't load object file %s", executable); + } + +#if THE_ISA == ALPHA_ISA + if (objFile->getArch() != ObjectFile::Alpha) + fatal("Object file architecture does not match compiled ISA (Alpha)."); + switch (objFile->getOpSys()) { + case ObjectFile::Tru64: + process = new AlphaTru64Process(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + + case ObjectFile::Linux: + process = new AlphaLinuxProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + + default: + fatal("Unknown/unsupported operating system."); + } +#elif THE_ISA == SPARC_ISA + if (objFile->getArch() != ObjectFile::SPARC) + fatal("Object file architecture does not match compiled ISA (SPARC)."); + switch (objFile->getOpSys()) { + case ObjectFile::Linux: + process = new SparcLinuxProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + + + case ObjectFile::Solaris: + process = new SparcSolarisProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + default: + fatal("Unknown/unsupported operating system."); + } +#elif THE_ISA == MIPS_ISA + if (objFile->getArch() != ObjectFile::Mips) + fatal("Object file architecture does not match compiled ISA (MIPS)."); + switch (objFile->getOpSys()) { + case ObjectFile::Linux: + process = new MipsLinuxProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp); + break; + + default: + fatal("Unknown/unsupported operating system."); + } +#else +#error "THE_ISA not set" +#endif + + + if (process == NULL) + fatal("Unknown error creating process object."); + return process; +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(LiveProcess) + + VectorParam<string> cmd; + Param<string> executable; + Param<string> input; + Param<string> output; + VectorParam<string> env; + SimObjectParam<System *> system; + +END_DECLARE_SIM_OBJECT_PARAMS(LiveProcess) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(LiveProcess) + + INIT_PARAM(cmd, "command line (executable plus arguments)"), + INIT_PARAM(executable, "executable (overrides cmd[0] if set)"), + INIT_PARAM(input, "filename for stdin (dflt: use sim stdin)"), + INIT_PARAM(output, "filename for stdout/stderr (dflt: use sim stdout)"), + INIT_PARAM(env, "environment settings"), + INIT_PARAM(system, "system") + +END_INIT_SIM_OBJECT_PARAMS(LiveProcess) + + +CREATE_SIM_OBJECT(LiveProcess) +{ + string in = input; + string out = output; + + // initialize file descriptors to default: same as simulator + int stdin_fd, stdout_fd, stderr_fd; + + if (in == "stdin" || in == "cin") + stdin_fd = STDIN_FILENO; + else + stdin_fd = Process::openInputFile(input); + + if (out == "stdout" || out == "cout") + stdout_fd = STDOUT_FILENO; + else if (out == "stderr" || out == "cerr") + stdout_fd = STDERR_FILENO; + else + stdout_fd = Process::openOutputFile(out); + + stderr_fd = (stdout_fd != STDOUT_FILENO) ? stdout_fd : STDERR_FILENO; + + return LiveProcess::create(getInstanceName(), system, + stdin_fd, stdout_fd, stderr_fd, + (string)executable == "" ? cmd[0] : executable, + cmd, env); } -DEFINE_SIM_OBJECT_CLASS_NAME("LiveProcess", LiveProcess); + +REGISTER_SIM_OBJECT("LiveProcess", LiveProcess) diff --git a/src/sim/process.hh b/src/sim/process.hh index 807bf330f..763deb100 100644 --- a/src/sim/process.hh +++ b/src/sim/process.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __PROCESS_HH__ @@ -43,8 +46,7 @@ #include "base/statistics.hh" #include "sim/sim_object.hh" -class CPUExecContext; -class ExecContext; +class ThreadContext; class SyscallDesc; class PageTable; class TranslatingPort; @@ -62,24 +64,24 @@ class Process : public SimObject /// running on. System *system; - // have we initialized an execution context from this process? If + // have we initialized a thread context from this process? If // yes, subsequent contexts are assumed to be for dynamically // created threads and are not initialized. bool initialContextLoaded; - // execution contexts associated with this process - std::vector<ExecContext *> execContexts; + // thread contexts associated with this process + std::vector<ThreadContext *> threadContexts; // number of CPUs (esxec contexts, really) assigned to this process. - unsigned int numCpus() { return execContexts.size(); } + unsigned int numCpus() { return threadContexts.size(); } // record of blocked context struct WaitRec { Addr waitChan; - ExecContext *waitingContext; + ThreadContext *waitingContext; - WaitRec(Addr chan, ExecContext *ctx) + WaitRec(Addr chan, ThreadContext *ctx) : waitChan(chan), waitingContext(ctx) { } }; @@ -140,12 +142,12 @@ class Process : public SimObject // override of virtual SimObject method: register statistics virtual void regStats(); - // register an execution context for this process. - // returns xc's cpu number (index into execContexts[]) - int registerExecContext(ExecContext *xc); + // register a thread context for this process. + // returns tc's cpu number (index into threadContexts[]) + int registerThreadContext(ThreadContext *tc); - void replaceExecContext(ExecContext *xc, int xcIndex); + void replaceThreadContext(ThreadContext *tc, int tcIndex); // map simulator fd sim_fd to target fd tgt_fd void dup_fd(int sim_fd, int tgt_fd); @@ -159,7 +161,7 @@ class Process : public SimObject // look up simulator fd for given target fd int sim_fd(int tgt_fd); - virtual void syscall(int64_t callnum, ExecContext *xc) = 0; + virtual void syscall(int64_t callnum, ThreadContext *tc) = 0; }; // @@ -181,9 +183,19 @@ class LiveProcess : public Process virtual void argsInit(int intSize, int pageSize); public: - virtual void syscall(int64_t callnum, ExecContext *xc); + virtual void syscall(int64_t callnum, ThreadContext *tc); virtual SyscallDesc* getDesc(int callnum) = 0; + + // this function is used to create the LiveProcess object, since + // we can't tell which subclass of LiveProcess to use until we + // open and look at the object file. + static LiveProcess *create(const std::string &nm, + System *_system, + int stdin_fd, int stdout_fd, int stderr_fd, + std::string executable, + std::vector<std::string> &argv, + std::vector<std::string> &envp); }; diff --git a/src/sim/pseudo_inst.cc b/src/sim/pseudo_inst.cc index 7897b5c8b..b2854e491 100644 --- a/src/sim/pseudo_inst.cc +++ b/src/sim/pseudo_inst.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <errno.h> @@ -36,7 +38,8 @@ #include "arch/vtophys.hh" #include "cpu/base.hh" #include "cpu/sampler/sampler.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" +#include "cpu/quiesce_event.hh" #include "kern/kernel_stats.hh" #include "sim/param.hh" #include "sim/serialize.hh" @@ -61,89 +64,94 @@ namespace AlphaPseudo bool doQuiesce; void - arm(ExecContext *xc) + arm(ThreadContext *tc) { - xc->getCpuPtr()->kernelStats->arm(); + if (tc->getKernelStats()) + tc->getKernelStats()->arm(); } void - quiesce(ExecContext *xc) + quiesce(ThreadContext *tc) { if (!doQuiesce) return; - xc->suspend(); - xc->getCpuPtr()->kernelStats->quiesce(); + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); } void - quiesceNs(ExecContext *xc, uint64_t ns) + quiesceNs(ThreadContext *tc, uint64_t ns) { if (!doQuiesce || ns == 0) return; - Event *quiesceEvent = xc->getQuiesceEvent(); + EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent(); if (quiesceEvent->scheduled()) quiesceEvent->reschedule(curTick + Clock::Int::ns * ns); else quiesceEvent->schedule(curTick + Clock::Int::ns * ns); - xc->suspend(); - xc->getCpuPtr()->kernelStats->quiesce(); + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); } void - quiesceCycles(ExecContext *xc, uint64_t cycles) + quiesceCycles(ThreadContext *tc, uint64_t cycles) { if (!doQuiesce || cycles == 0) return; - Event *quiesceEvent = xc->getQuiesceEvent(); + EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent(); if (quiesceEvent->scheduled()) quiesceEvent->reschedule(curTick + - xc->getCpuPtr()->cycles(cycles)); + tc->getCpuPtr()->cycles(cycles)); else quiesceEvent->schedule(curTick + - xc->getCpuPtr()->cycles(cycles)); + tc->getCpuPtr()->cycles(cycles)); - xc->suspend(); - xc->getCpuPtr()->kernelStats->quiesce(); + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); } uint64_t - quiesceTime(ExecContext *xc) + quiesceTime(ThreadContext *tc) { - return (xc->readLastActivate() - xc->readLastSuspend()) / Clock::Int::ns; + return (tc->readLastActivate() - tc->readLastSuspend()) / Clock::Int::ns; } void - ivlb(ExecContext *xc) + ivlb(ThreadContext *tc) { - xc->getCpuPtr()->kernelStats->ivlb(); + if (tc->getKernelStats()) + tc->getKernelStats()->ivlb(); } void - ivle(ExecContext *xc) + ivle(ThreadContext *tc) { } void - m5exit_old(ExecContext *xc) + m5exit_old(ThreadContext *tc) { - SimExit(curTick, "m5_exit_old instruction encountered"); + exitSimLoop(curTick, "m5_exit_old instruction encountered"); } void - m5exit(ExecContext *xc, Tick delay) + m5exit(ThreadContext *tc, Tick delay) { Tick when = curTick + delay * Clock::Int::ns; - SimExit(when, "m5_exit instruction encountered"); + exitSimLoop(when, "m5_exit instruction encountered"); } void - resetstats(ExecContext *xc, Tick delay, Tick period) + resetstats(ThreadContext *tc, Tick delay, Tick period) { if (!doStatisticsInsts) return; @@ -157,7 +165,7 @@ namespace AlphaPseudo } void - dumpstats(ExecContext *xc, Tick delay, Tick period) + dumpstats(ThreadContext *tc, Tick delay, Tick period) { if (!doStatisticsInsts) return; @@ -171,19 +179,19 @@ namespace AlphaPseudo } void - addsymbol(ExecContext *xc, Addr addr, Addr symbolAddr) + addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr) { char symb[100]; - CopyStringOut(xc, symb, symbolAddr, 100); + CopyStringOut(tc, symb, symbolAddr, 100); std::string symbol(symb); DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr); - xc->getSystemPtr()->kernelSymtab->insert(addr,symbol); + tc->getSystemPtr()->kernelSymtab->insert(addr,symbol); } void - dumpresetstats(ExecContext *xc, Tick delay, Tick period) + dumpresetstats(ThreadContext *tc, Tick delay, Tick period) { if (!doStatisticsInsts) return; @@ -197,22 +205,16 @@ namespace AlphaPseudo } void - m5checkpoint(ExecContext *xc, Tick delay, Tick period) + m5checkpoint(ThreadContext *tc, Tick delay, Tick period) { if (!doCheckpointInsts) return; - - - Tick when = curTick + delay * Clock::Int::ns; - Tick repeat = period * Clock::Int::ns; - - Checkpoint::setup(when, repeat); } uint64_t - readfile(ExecContext *xc, Addr vaddr, uint64_t len, uint64_t offset) + readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset) { - const string &file = xc->getCpuPtr()->system->params()->readfile; + const string &file = tc->getCpuPtr()->system->params()->readfile; if (file.empty()) { return ULL(0); } @@ -239,7 +241,7 @@ namespace AlphaPseudo } close(fd); - CopyIn(xc, vaddr, buf, result); + CopyIn(tc, vaddr, buf, result); delete [] buf; return result; } @@ -271,12 +273,12 @@ namespace AlphaPseudo doCheckpointInsts = __checkpoint; } - void debugbreak(ExecContext *xc) + void debugbreak(ThreadContext *tc) { debug_break(); } - void switchcpu(ExecContext *xc) + void switchcpu(ThreadContext *tc) { if (SampCPU) SampCPU->switchCPUs(); diff --git a/src/sim/pseudo_inst.hh b/src/sim/pseudo_inst.hh index 4dd427c99..5e5b7d95f 100644 --- a/src/sim/pseudo_inst.hh +++ b/src/sim/pseudo_inst.hh @@ -24,9 +24,11 @@ * 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. + * + * Authors: Nathan Binkert */ -class ExecContext; +class ThreadContext; //We need the "Tick" data type from here #include "sim/host.hh" @@ -42,21 +44,21 @@ namespace AlphaPseudo extern bool doCheckpointInsts; extern bool doQuiesce; - void arm(ExecContext *xc); - void quiesce(ExecContext *xc); - void quiesceNs(ExecContext *xc, uint64_t ns); - void quiesceCycles(ExecContext *xc, uint64_t cycles); - uint64_t quiesceTime(ExecContext *xc); - void ivlb(ExecContext *xc); - void ivle(ExecContext *xc); - void m5exit(ExecContext *xc, Tick delay); - void m5exit_old(ExecContext *xc); - void resetstats(ExecContext *xc, Tick delay, Tick period); - void dumpstats(ExecContext *xc, Tick delay, Tick period); - void dumpresetstats(ExecContext *xc, Tick delay, Tick period); - void m5checkpoint(ExecContext *xc, Tick delay, Tick period); - uint64_t readfile(ExecContext *xc, Addr vaddr, uint64_t len, uint64_t offset); - void debugbreak(ExecContext *xc); - void switchcpu(ExecContext *xc); - void addsymbol(ExecContext *xc, Addr addr, Addr symbolAddr); + void arm(ThreadContext *tc); + void quiesce(ThreadContext *tc); + void quiesceNs(ThreadContext *tc, uint64_t ns); + void quiesceCycles(ThreadContext *tc, uint64_t cycles); + uint64_t quiesceTime(ThreadContext *tc); + void ivlb(ThreadContext *tc); + void ivle(ThreadContext *tc); + void m5exit(ThreadContext *tc, Tick delay); + void m5exit_old(ThreadContext *tc); + void resetstats(ThreadContext *tc, Tick delay, Tick period); + void dumpstats(ThreadContext *tc, Tick delay, Tick period); + void dumpresetstats(ThreadContext *tc, Tick delay, Tick period); + void m5checkpoint(ThreadContext *tc, Tick delay, Tick period); + uint64_t readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset); + void debugbreak(ThreadContext *tc); + void switchcpu(ThreadContext *tc); + void addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr); } diff --git a/src/sim/root.cc b/src/sim/root.cc index 6348ec104..ec5e2f7e2 100644 --- a/src/sim/root.cc +++ b/src/sim/root.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <cstring> @@ -37,6 +40,7 @@ #include "sim/builder.hh" #include "sim/host.hh" #include "sim/sim_events.hh" +#include "sim/sim_exit.hh" #include "sim/sim_object.hh" #include "sim/root.hh" @@ -96,7 +100,7 @@ void Root::startup() { if (max_tick != 0) - new SimExitEvent(curTick + max_tick, "reached maximum cycle count"); + exitSimLoop(curTick + max_tick, "reached maximum cycle count"); if (progress_interval != 0) new ProgressEvent(&mainEventQueue, progress_interval); diff --git a/src/sim/serialize.cc b/src/sim/serialize.cc index c4ef124bb..7450d7b7e 100644 --- a/src/sim/serialize.cc +++ b/src/sim/serialize.cc @@ -24,6 +24,10 @@ * 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. + * + * Authors: Nathan Binkert + * Erik Hallnor + * Steve Reinhardt */ #include <sys/time.h> @@ -41,7 +45,6 @@ #include "base/output.hh" #include "base/str.hh" #include "base/trace.hh" -#include "sim/config_node.hh" #include "sim/eventq.hh" #include "sim/param.hh" #include "sim/serialize.hh" @@ -241,56 +244,41 @@ Serializable::serializeAll() globals.serialize(outstream); SimObject::serializeAll(outstream); - - assert(Serializable::ckptPrevCount + 1 == Serializable::ckptCount); - Serializable::ckptPrevCount++; - if (ckptMaxCount && ++ckptCount >= ckptMaxCount) - SimExit(curTick + 1, "Maximum number of checkpoints dropped"); - } - void -Serializable::unserializeGlobals(Checkpoint *cp) +Serializable::unserializeAll() { - globals.unserialize(cp); -} - - -class SerializeEvent : public Event -{ - protected: - Tick repeat; - - public: - SerializeEvent(Tick _when, Tick _repeat); - virtual void process(); - virtual void serialize(std::ostream &os) - { - panic("Cannot serialize the SerializeEvent"); - } + string dir = Checkpoint::dir(); + string cpt_file = dir + Checkpoint::baseFilename; + string section = ""; -}; + DPRINTFR(Config, "Loading checkpoint dir '%s'\n", + dir); + Checkpoint *cp = new Checkpoint(dir, section); + unserializeGlobals(cp); -SerializeEvent::SerializeEvent(Tick _when, Tick _repeat) - : Event(&mainEventQueue, Serialize_Pri), repeat(_repeat) -{ - setFlags(AutoDelete); - schedule(_when); + SimObject::unserializeAll(cp); } void -SerializeEvent::process() +Serializable::unserializeGlobals(Checkpoint *cp) { - Serializable::serializeAll(); - if (repeat) - schedule(curTick + repeat); + globals.unserialize(cp); } const char *Checkpoint::baseFilename = "m5.cpt"; static string checkpointDirBase; +void +setCheckpointDir(const std::string &name) +{ + checkpointDirBase = name; + if (checkpointDirBase[checkpointDirBase.size() - 1] != '/') + checkpointDirBase += "/"; +} + string Checkpoint::dir() { @@ -301,75 +289,11 @@ Checkpoint::dir() } void -Checkpoint::setup(Tick when, Tick period) -{ - new SerializeEvent(when, period); -} - -class SerializeParamContext : public ParamContext -{ - private: - SerializeEvent *event; - - public: - SerializeParamContext(const string §ion); - ~SerializeParamContext(); - void checkParams(); -}; - -SerializeParamContext serialParams("serialize"); - -Param<string> serialize_dir(&serialParams, "dir", - "dir to stick checkpoint in " - "(sprintf format with cycle #)"); - -Param<Counter> serialize_cycle(&serialParams, - "cycle", - "cycle to serialize", - 0); - -Param<Counter> serialize_period(&serialParams, - "period", - "period to repeat serializations", - 0); - -Param<int> serialize_count(&serialParams, "count", - "maximum number of checkpoints to drop"); - -SerializeParamContext::SerializeParamContext(const string §ion) - : ParamContext(section), event(NULL) -{ } - -SerializeParamContext::~SerializeParamContext() -{ -} - -void -SerializeParamContext::checkParams() -{ - checkpointDirBase = simout.resolve(serialize_dir); - - // guarantee that directory ends with a '/' - if (checkpointDirBase[checkpointDirBase.size() - 1] != '/') - checkpointDirBase += "/"; - - if (serialize_cycle > 0) - Checkpoint::setup(serialize_cycle, serialize_period); - - Serializable::ckptMaxCount = serialize_count; -} - -void debug_serialize() { Serializable::serializeAll(); } -void -debug_serialize(Tick when) -{ - new SerializeEvent(when, 0); -} //////////////////////////////////////////////////////////////////////// // @@ -439,9 +363,8 @@ Serializable::create(Checkpoint *cp, const std::string §ion) } -Checkpoint::Checkpoint(const std::string &cpt_dir, const std::string &path, - const ConfigNode *_configNode) - : db(new IniFile), basePath(path), configNode(_configNode), cptDir(cpt_dir) +Checkpoint::Checkpoint(const std::string &cpt_dir, const std::string &path) + : db(new IniFile), basePath(path), cptDir(cpt_dir) { string filename = cpt_dir + "/" + Checkpoint::baseFilename; if (!db->load(filename)) { @@ -467,9 +390,6 @@ Checkpoint::findObj(const std::string §ion, const std::string &entry, if (!db->find(section, entry, path)) return false; - if ((value = configNode->resolveSimObject(path)) != NULL) - return true; - if ((value = objMap[path]) != NULL) return true; diff --git a/src/sim/serialize.hh b/src/sim/serialize.hh index d8f5f8fc5..a80dc99e4 100644 --- a/src/sim/serialize.hh +++ b/src/sim/serialize.hh @@ -24,6 +24,10 @@ * 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. + * + * Authors: Nathan Binkert + * Erik Hallnor + * Steve Reinhardt */ /* @file @@ -39,8 +43,8 @@ #include <map> #include "sim/host.hh" -#include "sim/configfile.hh" +class IniFile; class Serializable; class Checkpoint; @@ -123,6 +127,7 @@ class Serializable static int ckptMaxCount; static int ckptPrevCount; static void serializeAll(); + static void unserializeAll(); static void unserializeGlobals(Checkpoint *cp); }; @@ -174,7 +179,7 @@ class SerializableClass // an optional config hierarchy node (specified by the third // argument). A pointer to the new SerializableBuilder is returned. typedef Serializable *(*CreateFunc)(Checkpoint *cp, - const std::string §ion); + const std::string §ion); static std::map<std::string,CreateFunc> *classMap; @@ -188,7 +193,7 @@ class SerializableClass // create Serializable given name of class and pointer to // configuration hierarchy node static Serializable *createObject(Checkpoint *cp, - const std::string §ion); + const std::string §ion); }; // @@ -200,18 +205,19 @@ class SerializableClass SerializableClass the##OBJ_CLASS##Class(CLASS_NAME, \ OBJ_CLASS::createForUnserialize); +void +setCheckpointName(const std::string &name); + class Checkpoint { private: IniFile *db; const std::string basePath; - const ConfigNode *configNode; std::map<std::string, Serializable*> objMap; public: - Checkpoint(const std::string &cpt_dir, const std::string &path, - const ConfigNode *_configNode); + Checkpoint(const std::string &cpt_dir, const std::string &path); const std::string cptDir; @@ -235,9 +241,6 @@ class Checkpoint // Filename for base checkpoint file within directory. static const char *baseFilename; - - // Set up a checkpoint creation event or series of events. - static void setup(Tick when, Tick period = 0); }; #endif // __SERIALIZE_HH__ diff --git a/src/sim/sim_events.cc b/src/sim/sim_events.cc index c2bdca9df..97f7ae03c 100644 --- a/src/sim/sim_events.cc +++ b/src/sim/sim_events.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <string> @@ -43,26 +45,45 @@ using namespace std; // handle termination event // void -SimExitEvent::process() +SimLoopExitEvent::process() { - // This event does not autodelete because exitNow may be called, - // and the function will never be allowed to finish. - if (theQueue() == &mainEventQueue) { - string _cause = cause; - int _code = code; - delete this; - exitNow(_cause, _code); - } else { - new SimExitEvent(cause, code); + // if this got scheduled on a different queue (e.g. the committed + // instruction queue) then make a corresponding event on the main + // queue. + if (theQueue() != &mainEventQueue) { + exitSimLoop(cause, code); delete this; } + + // otherwise do nothing... the IsExitEvent flag takes care of + // exiting the simulation loop and returning this object to Python } const char * -SimExitEvent::description() +SimLoopExitEvent::description() +{ + return "simulation loop exit"; +} + +void +exitSimLoop(Tick when, const std::string &message, int exit_code) +{ + new SimLoopExitEvent(when, message, exit_code); +} + +void +exitSimLoop(const std::string &message, int exit_code) +{ + exitSimLoop(curTick, message, exit_code); +} + +void +CountedQuiesceEvent::process() { - return "simulation termination"; + if (--count == 0) { + exitSimLoop("Finished quiesce"); + } } // @@ -88,7 +109,7 @@ void CountedExitEvent::process() { if (--downCounter == 0) { - new SimExitEvent(cause, 0); + exitSimLoop(cause, 0); } } @@ -117,7 +138,7 @@ CheckSwapEvent::process() if (swap < 100) { cerr << "\a\aAborting Simulation! Inadequate swap space!\n\n"; - new SimExitEvent("Lack of swap space"); + exitSimLoop("Lack of swap space"); } schedule(curTick + interval); diff --git a/src/sim/sim_events.hh b/src/sim/sim_events.hh index c93914457..50368f258 100644 --- a/src/sim/sim_events.hh +++ b/src/sim/sim_events.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __SIM_SIM_EVENTS_HH__ @@ -34,7 +36,7 @@ // // Event to terminate simulation at a particular cycle/instruction // -class SimExitEvent : public Event +class SimLoopExitEvent : public Event { private: // string explaining why we're terminating @@ -42,30 +44,45 @@ class SimExitEvent : public Event int code; public: - SimExitEvent(const std::string &_cause, int c = 0) - : Event(&mainEventQueue, Sim_Exit_Pri), cause(_cause), - code(c) - { schedule(curTick); } + // Default constructor. Only really used for derived classes. + SimLoopExitEvent() + : Event(&mainEventQueue, Sim_Exit_Pri) + { } - SimExitEvent(Tick _when, const std::string &_cause, int c = 0) + SimLoopExitEvent(Tick _when, const std::string &_cause, int c = 0) : Event(&mainEventQueue, Sim_Exit_Pri), cause(_cause), code(c) - { schedule(_when); } + { setFlags(IsExitEvent); schedule(_when); } - SimExitEvent(EventQueue *q, const std::string &_cause, int c = 0) + SimLoopExitEvent(EventQueue *q, + Tick _when, const std::string &_cause, int c = 0) : Event(q, Sim_Exit_Pri), cause(_cause), code(c) - { schedule(curTick); } + { setFlags(IsExitEvent); schedule(_when); } - SimExitEvent(EventQueue *q, Tick _when, const std::string &_cause, - int c = 0) - : Event(q, Sim_Exit_Pri), cause(_cause), code(c) - { schedule(_when); } + std::string getCause() { return cause; } + int getCode() { return code; } void process(); // process event virtual const char *description(); }; +class CountedQuiesceEvent : public SimLoopExitEvent +{ + private: + // Count down to quiescing + int count; + public: + CountedQuiesceEvent() + : count(0) + { } + void process(); + + void setCount(int _count) { count = _count; } + + int getCount() { return count; } +}; + // // Event class to terminate simulation after 'n' related events have // occurred using a shared counter: used to terminate when *all* diff --git a/src/sim/sim_exit.hh b/src/sim/sim_exit.hh index f14256933..545bf4ae0 100644 --- a/src/sim/sim_exit.hh +++ b/src/sim/sim_exit.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #ifndef __SIM_EXIT_HH__ @@ -33,12 +36,23 @@ #include "sim/host.hh" +// forward declaration class Callback; +/// Register a callback to be called when Python exits. Defined in +/// sim/main.cc. void registerExitCallback(Callback *); -void exitNow(const std::string &cause, int exit_code); -void exitNow(const char *cause, int exit_code); -void SimExit(Tick when, const char *message); +/// Schedule an event to exit the simulation loop (returning to +/// Python) at the indicated tick. The message and exit_code +/// parameters are saved in the SimLoopExitEvent to indicate why the +/// exit occurred. +void exitSimLoop(Tick when, const std::string &message, int exit_code = 0); + +/// Schedule an event to exit the simulation loop (returning to +/// Python) at the end of the current cycle (curTick). The message +/// and exit_code parameters are saved in the SimLoopExitEvent to +/// indicate why the exit occurred. +void exitSimLoop(const std::string &cause, int exit_code = 0); #endif // __SIM_EXIT_HH__ diff --git a/src/sim/sim_object.cc b/src/sim/sim_object.cc index 17d58ba4f..551555b25 100644 --- a/src/sim/sim_object.cc +++ b/src/sim/sim_object.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ #include <assert.h> @@ -35,7 +38,6 @@ #include "base/trace.hh" #include "base/stats/events.hh" #include "base/serializer.hh" -#include "sim/configfile.hh" #include "sim/host.hh" #include "sim/sim_object.hh" #include "sim/stats.hh" @@ -71,6 +73,7 @@ SimObject::SimObject(Params *p) doRecordEvent = !Stats::event_ignore.match(name()); simObjectList.push_back(this); + state = Atomic; } // @@ -86,6 +89,7 @@ SimObject::SimObject(const string &_name) doRecordEvent = !Stats::event_ignore.match(name()); simObjectList.push_back(this); + state = Atomic; } void @@ -217,6 +221,24 @@ SimObject::serializeAll(ostream &os) } } +void +SimObject::unserializeAll(Checkpoint *cp) +{ + SimObjectList::reverse_iterator ri = simObjectList.rbegin(); + SimObjectList::reverse_iterator rend = simObjectList.rend(); + + for (; ri != rend; ++ri) { + SimObject *obj = *ri; + DPRINTFR(Config, "Unserializing '%s'\n", + obj->name()); + if(cp->sectionExists(obj->name())) + obj->unserialize(cp, obj->name()); + else + warn("Not unserializing '%s': no section found in checkpoint.\n", + obj->name()); + } +} + #ifdef DEBUG // // static function: flag which objects should have the debugger break @@ -234,7 +256,6 @@ SimObject::debugObjectBreak(const string &objs) } } -extern "C" void debugObjectBreak(const char *objs) { @@ -249,10 +270,50 @@ SimObject::recordEvent(const std::string &stat) Stats::recordEvent(stat); } +bool +SimObject::quiesce(Event *quiesce_event) +{ + if (state != QuiescedAtomic && state != Atomic) { + panic("Must implement your own quiesce function if it is to be used " + "in timing mode!"); + } + state = QuiescedAtomic; + return false; +} + +void +SimObject::resume() +{ + if (state == QuiescedAtomic) { + state = Atomic; + } else if (state == QuiescedTiming) { + state = Timing; + } +} + +void +SimObject::setMemoryMode(State new_mode) +{ + assert(new_mode == Timing || new_mode == Atomic); + if (state == QuiescedAtomic && new_mode == Timing) { + state = QuiescedTiming; + } else if (state == QuiescedTiming && new_mode == Atomic) { + state = QuiescedAtomic; + } else { + state = new_mode; + } +} + +void +SimObject::switchOut() +{ + panic("Unimplemented!"); +} + void -SimObject::drain(Serializer *serializer) +SimObject::takeOverFrom(BaseCPU *cpu) { - serializer->signalDrained(); + panic("Unimplemented!"); } DEFINE_SIM_OBJECT_CLASS_NAME("SimObject", SimObject) diff --git a/src/sim/sim_object.hh b/src/sim/sim_object.hh index 76aba7ea1..e0b21782f 100644 --- a/src/sim/sim_object.hh +++ b/src/sim/sim_object.hh @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ /* @file @@ -41,7 +44,8 @@ #include "sim/serialize.hh" #include "sim/startup.hh" -class Serializer; +class BaseCPU; +class Event; /* * Abstract superclass for simulation objects. Represents things that @@ -55,15 +59,26 @@ class SimObject : public Serializable, protected StartupCallback std::string name; }; + enum State { + Atomic, + Timing, + Quiescing, + QuiescedAtomic, + QuiescedTiming + }; + protected: Params *_params; + State state; + + void changeState(State new_state) { state = new_state; } public: const Params *params() const { return _params; } - private: - friend class Serializer; + State getState() { return state; } + private: typedef std::vector<SimObject *> SimObjectList; // list of all instantiated simulation objects @@ -97,13 +112,16 @@ class SimObject : public Serializable, protected StartupCallback // static: call nameOut() & serialize() on all SimObjects static void serializeAll(std::ostream &); + static void unserializeAll(Checkpoint *cp); // Methods to drain objects in order to take checkpoints // Or switch from timing -> atomic memory model - virtual void drain(Serializer *serializer); - virtual void resume() { return;} ; - virtual void serializationComplete() - { assert(0 && "Unimplemented"); }; + // Quiesce returns true if the SimObject cannot quiesce immediately. + virtual bool quiesce(Event *quiesce_event); + virtual void resume(); + virtual void setMemoryMode(State new_mode); + virtual void switchOut(); + virtual void takeOverFrom(BaseCPU *cpu); #ifdef DEBUG public: diff --git a/src/sim/startup.cc b/src/sim/startup.cc index 683e6c746..838352107 100644 --- a/src/sim/startup.cc +++ b/src/sim/startup.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Kevin Lim */ #include <list> diff --git a/src/sim/startup.hh b/src/sim/startup.hh index 3c9b654f1..cfc4450f4 100644 --- a/src/sim/startup.hh +++ b/src/sim/startup.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __SIM_STARTUP_HH__ diff --git a/src/sim/stat_control.cc b/src/sim/stat_control.cc index 85c405b7f..041830ab7 100644 --- a/src/sim/stat_control.cc +++ b/src/sim/stat_control.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ // This file will contain default statistics for the simulator that @@ -219,8 +221,7 @@ SetupEvent(int flags, Tick when, Tick repeat) /* namespace Stats */ } -extern "C" void -debugDumpStats() +void debugDumpStats() { Stats::DumpNow(); } diff --git a/src/sim/stat_control.hh b/src/sim/stat_control.hh index a22ce76af..fb369f640 100644 --- a/src/sim/stat_control.hh +++ b/src/sim/stat_control.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __SIM_STAT_CONTROL_HH__ diff --git a/src/sim/stats.hh b/src/sim/stats.hh index 8e97d041f..97251283d 100644 --- a/src/sim/stats.hh +++ b/src/sim/stats.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __SIM_STATS_HH__ diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc index ed0da628e..e72890612 100644 --- a/src/sim/syscall_emul.cc +++ b/src/sim/syscall_emul.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Steve Reinhardt + * Ali Saidi */ #include <fcntl.h> @@ -35,37 +38,37 @@ #include "sim/syscall_emul.hh" #include "base/chunk_generator.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "cpu/base.hh" #include "mem/page_table.hh" #include "sim/process.hh" -#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" using namespace std; using namespace TheISA; void -SyscallDesc::doSyscall(int callnum, Process *process, ExecContext *xc) +SyscallDesc::doSyscall(int callnum, Process *process, ThreadContext *tc) { DPRINTFR(SyscallVerbose, "%d: %s: syscall %s called w/arguments %d,%d,%d,%d\n", - curTick,xc->getCpuPtr()->name(), name, - xc->getSyscallArg(0),xc->getSyscallArg(1), - xc->getSyscallArg(2),xc->getSyscallArg(3)); + curTick,tc->getCpuPtr()->name(), name, + tc->getSyscallArg(0),tc->getSyscallArg(1), + tc->getSyscallArg(2),tc->getSyscallArg(3)); - SyscallReturn retval = (*funcPtr)(this, callnum, process, xc); + SyscallReturn retval = (*funcPtr)(this, callnum, process, tc); DPRINTFR(SyscallVerbose, "%d: %s: syscall %s returns %d\n", - curTick,xc->getCpuPtr()->name(), name, retval.value()); + curTick,tc->getCpuPtr()->name(), name, retval.value()); if (!(flags & SyscallDesc::SuppressReturnValue)) - xc->setSyscallReturn(retval); + tc->setSyscallReturn(retval); } SyscallReturn unimplementedFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { fatal("syscall %s (#%d) unimplemented.", desc->name, callnum); @@ -75,10 +78,10 @@ unimplementedFunc(SyscallDesc *desc, int callnum, Process *process, SyscallReturn ignoreFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { warn("ignoring syscall %s(%d, %d, ...)", desc->name, - xc->getSyscallArg(0), xc->getSyscallArg(1)); + tc->getSyscallArg(0), tc->getSyscallArg(1)); return 0; } @@ -86,28 +89,30 @@ ignoreFunc(SyscallDesc *desc, int callnum, Process *process, SyscallReturn exitFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - new SimExitEvent("target called exit()", xc->getSyscallArg(0) & 0xff); + if (tc->exit()) { + exitSimLoop("target called exit()", tc->getSyscallArg(0) & 0xff); + } return 1; } SyscallReturn -getpagesizeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +getpagesizeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { return (int)VMPageSize; } SyscallReturn -obreakFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +obreakFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { Addr junk; // change brk addr to first arg - Addr new_brk = xc->getSyscallArg(0); + Addr new_brk = tc->getSyscallArg(0); if (new_brk != 0) { for (ChunkGenerator gen(p->brk_point, new_brk - p->brk_point, VMPageSize); !gen.done(); gen.next()) { @@ -123,9 +128,9 @@ obreakFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) SyscallReturn -closeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +closeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int target_fd = xc->getSyscallArg(0); + int target_fd = tc->getSyscallArg(0); int status = close(p->sim_fd(target_fd)); if (status >= 0) p->free_fd(target_fd); @@ -134,28 +139,28 @@ closeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) SyscallReturn -readFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +readFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int fd = p->sim_fd(xc->getSyscallArg(0)); - int nbytes = xc->getSyscallArg(2); - BufferArg bufArg(xc->getSyscallArg(1), nbytes); + int fd = p->sim_fd(tc->getSyscallArg(0)); + int nbytes = tc->getSyscallArg(2); + BufferArg bufArg(tc->getSyscallArg(1), nbytes); int bytes_read = read(fd, bufArg.bufferPtr(), nbytes); if (bytes_read != -1) - bufArg.copyOut(xc->getMemPort()); + bufArg.copyOut(tc->getMemPort()); return bytes_read; } SyscallReturn -writeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +writeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int fd = p->sim_fd(xc->getSyscallArg(0)); - int nbytes = xc->getSyscallArg(2); - BufferArg bufArg(xc->getSyscallArg(1), nbytes); + int fd = p->sim_fd(tc->getSyscallArg(0)); + int nbytes = tc->getSyscallArg(2); + BufferArg bufArg(tc->getSyscallArg(1), nbytes); - bufArg.copyIn(xc->getMemPort()); + bufArg.copyIn(tc->getMemPort()); int bytes_written = write(fd, bufArg.bufferPtr(), nbytes); @@ -166,11 +171,11 @@ writeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) SyscallReturn -lseekFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +lseekFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int fd = p->sim_fd(xc->getSyscallArg(0)); - uint64_t offs = xc->getSyscallArg(1); - int whence = xc->getSyscallArg(2); + int fd = p->sim_fd(tc->getSyscallArg(0)); + uint64_t offs = tc->getSyscallArg(1); + int whence = tc->getSyscallArg(2); off_t result = lseek(fd, offs, whence); @@ -179,7 +184,7 @@ lseekFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) SyscallReturn -munmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +munmapFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { // given that we don't really implement mmap, munmap is really easy return 0; @@ -189,24 +194,24 @@ munmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) const char *hostname = "m5.eecs.umich.edu"; SyscallReturn -gethostnameFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +gethostnameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - int name_len = xc->getSyscallArg(1); - BufferArg name(xc->getSyscallArg(0), name_len); + int name_len = tc->getSyscallArg(1); + BufferArg name(tc->getSyscallArg(0), name_len); strncpy((char *)name.bufferPtr(), hostname, name_len); - name.copyOut(xc->getMemPort()); + name.copyOut(tc->getMemPort()); return 0; } SyscallReturn -unlinkFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +unlinkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return (TheISA::IntReg)-EFAULT; int result = unlink(path.c_str()); @@ -214,16 +219,16 @@ unlinkFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) } SyscallReturn -renameFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +renameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { string old_name; - if (!xc->getMemPort()->tryReadString(old_name, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(old_name, tc->getSyscallArg(0))) return -EFAULT; string new_name; - if (!xc->getMemPort()->tryReadString(new_name, xc->getSyscallArg(1))) + if (!tc->getMemPort()->tryReadString(new_name, tc->getSyscallArg(1))) return -EFAULT; int64_t result = rename(old_name.c_str(), new_name.c_str()); @@ -231,45 +236,45 @@ renameFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) } SyscallReturn -truncateFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +truncateFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; - off_t length = xc->getSyscallArg(1); + off_t length = tc->getSyscallArg(1); int result = truncate(path.c_str(), length); return (result == -1) ? -errno : result; } SyscallReturn -ftruncateFunc(SyscallDesc *desc, int num, Process *process, ExecContext *xc) +ftruncateFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc) { - int fd = process->sim_fd(xc->getSyscallArg(0)); + int fd = process->sim_fd(tc->getSyscallArg(0)); if (fd < 0) return -EBADF; - off_t length = xc->getSyscallArg(1); + off_t length = tc->getSyscallArg(1); int result = ftruncate(fd, length); return (result == -1) ? -errno : result; } SyscallReturn -chownFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +chownFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; /* XXX endianess */ - uint32_t owner = xc->getSyscallArg(1); + uint32_t owner = tc->getSyscallArg(1); uid_t hostOwner = owner; - uint32_t group = xc->getSyscallArg(2); + uint32_t group = tc->getSyscallArg(2); gid_t hostGroup = group; int result = chown(path.c_str(), hostOwner, hostGroup); @@ -277,17 +282,17 @@ chownFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) } SyscallReturn -fchownFunc(SyscallDesc *desc, int num, Process *process, ExecContext *xc) +fchownFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc) { - int fd = process->sim_fd(xc->getSyscallArg(0)); + int fd = process->sim_fd(tc->getSyscallArg(0)); if (fd < 0) return -EBADF; /* XXX endianess */ - uint32_t owner = xc->getSyscallArg(1); + uint32_t owner = tc->getSyscallArg(1); uid_t hostOwner = owner; - uint32_t group = xc->getSyscallArg(2); + uint32_t group = tc->getSyscallArg(2); gid_t hostGroup = group; int result = fchown(fd, hostOwner, hostGroup); @@ -297,14 +302,14 @@ fchownFunc(SyscallDesc *desc, int num, Process *process, ExecContext *xc) SyscallReturn fcntlFunc(SyscallDesc *desc, int num, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); + int fd = tc->getSyscallArg(0); if (fd < 0 || process->sim_fd(fd) < 0) return -EBADF; - int cmd = xc->getSyscallArg(1); + int cmd = tc->getSyscallArg(1); switch (cmd) { case 0: // F_DUPFD // if we really wanted to support this, we'd need to do it @@ -338,8 +343,37 @@ fcntlFunc(SyscallDesc *desc, int num, Process *process, } SyscallReturn +fcntl64Func(SyscallDesc *desc, int num, Process *process, + ThreadContext *tc) +{ + int fd = tc->getSyscallArg(0); + + if (fd < 0 || process->sim_fd(fd) < 0) + return -EBADF; + + int cmd = tc->getSyscallArg(1); + switch (cmd) { + case 33: //F_GETLK64 + warn("fcntl64(%d, F_GETLK64) not supported, error returned\n", fd); + return -EMFILE; + + case 34: // F_SETLK64 + case 35: // F_SETLKW64 + warn("fcntl64(%d, F_SETLK(W)64) not supported, error returned\n", fd); + return -EMFILE; + + default: + // not sure if this is totally valid, but we'll pass it through + // to the underlying OS + warn("fcntl64(%d, %d) passed through to host\n", fd, cmd); + return fcntl(process->sim_fd(fd), cmd); + // return 0; + } +} + +SyscallReturn pipePseudoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { int fds[2], sim_fds[2]; int pipe_retval = pipe(fds); @@ -354,99 +388,99 @@ pipePseudoFunc(SyscallDesc *desc, int callnum, Process *process, // Alpha Linux convention for pipe() is that fd[0] is returned as // the return value of the function, and fd[1] is returned in r20. - xc->setIntReg(SyscallPseudoReturnReg, sim_fds[1]); + tc->setIntReg(SyscallPseudoReturnReg, sim_fds[1]); return sim_fds[0]; } SyscallReturn getpidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Make up a PID. There's no interprocess communication in // fake_syscall mode, so there's no way for a process to know it's // not getting a unique value. - xc->setIntReg(SyscallPseudoReturnReg, 99); + tc->setIntReg(SyscallPseudoReturnReg, 99); return 100; } SyscallReturn getuidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Make up a UID and EUID... it shouldn't matter, and we want the // simulation to be deterministic. // EUID goes in r20. - xc->setIntReg(SyscallPseudoReturnReg, 100); //EUID + tc->setIntReg(SyscallPseudoReturnReg, 100); //EUID return 100; // UID } SyscallReturn getgidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Get current group ID. EGID goes in r20. - xc->setIntReg(SyscallPseudoReturnReg, 100); //EGID + tc->setIntReg(SyscallPseudoReturnReg, 100); //EGID return 100; } SyscallReturn setuidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // can't fathom why a benchmark would call this. - warn("Ignoring call to setuid(%d)\n", xc->getSyscallArg(0)); + warn("Ignoring call to setuid(%d)\n", tc->getSyscallArg(0)); return 0; } SyscallReturn getpidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { // Make up a PID. There's no interprocess communication in // fake_syscall mode, so there's no way for a process to know it's // not getting a unique value. - xc->setIntReg(SyscallPseudoReturnReg, 99); //PID + tc->setIntReg(SyscallPseudoReturnReg, 99); //PID return 100; } SyscallReturn getppidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 99; } SyscallReturn getuidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 100; // UID } SyscallReturn geteuidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 100; // UID } SyscallReturn getgidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 100; } SyscallReturn getegidFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { return 100; } diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh index 00f016410..a3990e2fd 100644 --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -24,13 +24,17 @@ * 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. + * + * Authors: Steve Reinhardt + * Kevin Lim + * Korey Sewell */ #ifndef __SIM_SYSCALL_EMUL_HH__ #define __SIM_SYSCALL_EMUL_HH__ -#define BSD_HOST (defined(__APPLE__) || defined(__OpenBSD__) || \ - defined(__FreeBSD__)) +#define NO_STAT64 (defined(__APPLE__) || defined(__OpenBSD__) || \ + defined(__FreeBSD__) || defined(__CYGWIN__)) /// /// @file syscall_emul.hh @@ -51,7 +55,7 @@ #include "base/misc.hh" #include "base/trace.hh" #include "cpu/base.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/translating_port.hh" #include "mem/page_table.hh" #include "sim/process.hh" @@ -65,7 +69,7 @@ class SyscallDesc { /// Typedef for target syscall handler functions. typedef SyscallReturn (*FuncPtr)(SyscallDesc *, int num, - Process *, ExecContext *); + Process *, ThreadContext *); const char *name; //!< Syscall name (e.g., "open"). FuncPtr funcPtr; //!< Pointer to emulation function. @@ -75,7 +79,7 @@ class SyscallDesc { enum Flags { /// Don't set return regs according to funcPtr return value. /// Used for syscalls with non-standard return conventions - /// that explicitly set the ExecContext regs (e.g., + /// that explicitly set the ThreadContext regs (e.g., /// sigreturn). SuppressReturnValue = 1 }; @@ -87,7 +91,7 @@ class SyscallDesc { } /// Emulate the syscall. Public interface for calling through funcPtr. - void doSyscall(int callnum, Process *proc, ExecContext *xc); + void doSyscall(int callnum, Process *proc, ThreadContext *tc); }; @@ -169,129 +173,133 @@ class TypedBufferArg : public BaseBufferArg /// Handler for unimplemented syscalls that we haven't thought about. SyscallReturn unimplementedFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Handler for unimplemented syscalls that we never intend to /// implement (signal handling, etc.) and should not affect the correct /// behavior of the program. Print a warning only if the appropriate /// trace flag is enabled. Return success to the target program. SyscallReturn ignoreFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target exit() handler: terminate simulation. SyscallReturn exitFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getpagesize() handler. SyscallReturn getpagesizeFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target obreak() handler: set brk address. SyscallReturn obreakFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target close() handler. SyscallReturn closeFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target read() handler. SyscallReturn readFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target write() handler. SyscallReturn writeFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target lseek() handler. SyscallReturn lseekFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target munmap() handler. SyscallReturn munmapFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target gethostname() handler. SyscallReturn gethostnameFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target unlink() handler. SyscallReturn unlinkFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target rename() handler. SyscallReturn renameFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target truncate() handler. SyscallReturn truncateFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target ftruncate() handler. SyscallReturn ftruncateFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target chown() handler. SyscallReturn chownFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target fchown() handler. SyscallReturn fchownFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target fnctl() handler. SyscallReturn fcntlFunc(SyscallDesc *desc, int num, - Process *process, ExecContext *xc); + Process *process, ThreadContext *tc); + +/// Target fcntl64() handler. +SyscallReturn fcntl64Func(SyscallDesc *desc, int num, + Process *process, ThreadContext *tc); /// Target setuid() handler. SyscallReturn setuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getpid() handler. SyscallReturn getpidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getuid() handler. SyscallReturn getuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getgid() handler. SyscallReturn getgidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getppid() handler. SyscallReturn getppidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target geteuid() handler. SyscallReturn geteuidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getegid() handler. SyscallReturn getegidFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Pseudo Funcs - These functions use a different return convension, /// returning a second value in a register other than the normal return register SyscallReturn pipePseudoFunc(SyscallDesc *desc, int num, - Process *process, ExecContext *xc); + Process *process, ThreadContext *tc); /// Target getpidPseudo() handler. SyscallReturn getpidPseudoFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getuidPseudo() handler. SyscallReturn getuidPseudoFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// Target getgidPseudo() handler. SyscallReturn getgidPseudoFunc(SyscallDesc *desc, int num, - Process *p, ExecContext *xc); + Process *p, ThreadContext *tc); /// This struct is used to build an target-OS-dependent table that @@ -335,10 +343,10 @@ getElapsedTime(T1 &sec, T2 &usec) template <class OS> SyscallReturn ioctlFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); - unsigned req = xc->getSyscallArg(1); + int fd = tc->getSyscallArg(0); + unsigned req = tc->getSyscallArg(1); DPRINTF(SyscallVerbose, "ioctl(%d, 0x%x, ...)\n", fd, req); @@ -360,7 +368,7 @@ ioctlFunc(SyscallDesc *desc, int callnum, Process *process, default: fatal("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ 0x%llx\n", - fd, req, xc->readPC()); + fd, req, tc->readPC()); } } @@ -368,11 +376,11 @@ ioctlFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn openFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; if (path == "/dev/sysdev0") { @@ -382,8 +390,8 @@ openFunc(SyscallDesc *desc, int callnum, Process *process, return -ENOENT; } - int tgtFlags = xc->getSyscallArg(1); - int mode = xc->getSyscallArg(2); + int tgtFlags = tc->getSyscallArg(1); + int mode = tc->getSyscallArg(2); int hostFlags = 0; // translate open flags @@ -415,14 +423,14 @@ openFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn chmodFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; - uint32_t mode = xc->getSyscallArg(1); + uint32_t mode = tc->getSyscallArg(1); mode_t hostMode = 0; // XXX translate mode flags via OS::something??? @@ -441,15 +449,15 @@ chmodFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn fchmodFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); + int fd = tc->getSyscallArg(0); if (fd < 0 || process->sim_fd(fd) < 0) { // doesn't map to any simulator fd: not a valid target fd return -EBADF; } - uint32_t mode = xc->getSyscallArg(1); + uint32_t mode = tc->getSyscallArg(1); mode_t hostMode = 0; // XXX translate mode flags via OS::someting??? @@ -468,11 +476,11 @@ fchmodFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn statFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; struct stat hostBuf; @@ -481,7 +489,7 @@ statFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -491,15 +499,15 @@ statFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn fstat64Func(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); + int fd = tc->getSyscallArg(0); if (fd < 0 || process->sim_fd(fd) < 0) { // doesn't map to any simulator fd: not a valid target fd return -EBADF; } -#if BSD_HOST +#if NO_STAT64 struct stat hostBuf; int result = fstat(process->sim_fd(fd), &hostBuf); #else @@ -510,7 +518,7 @@ fstat64Func(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStat64Buf(xc->getMemPort(), fd, xc->getSyscallArg(1), &hostBuf); + OS::copyOutStat64Buf(tc->getMemPort(), fd, tc->getSyscallArg(1), &hostBuf); return 0; } @@ -520,11 +528,11 @@ fstat64Func(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn lstatFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; struct stat hostBuf; @@ -533,7 +541,7 @@ lstatFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -542,14 +550,14 @@ lstatFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn lstat64Func(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; -#if BSD_HOST +#if NO_STAT64 struct stat hostBuf; int result = lstat(path.c_str(), &hostBuf); #else @@ -560,7 +568,7 @@ lstat64Func(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStat64Buf(xc->getMemPort(), -1, xc->getSyscallArg(1), &hostBuf); + OS::copyOutStat64Buf(tc->getMemPort(), -1, tc->getSyscallArg(1), &hostBuf); return 0; } @@ -569,9 +577,9 @@ lstat64Func(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn fstatFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = process->sim_fd(xc->getSyscallArg(0)); + int fd = process->sim_fd(tc->getSyscallArg(0)); DPRINTF(SyscallVerbose, "fstat(%d, ...)\n", fd); @@ -584,7 +592,7 @@ fstatFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -594,11 +602,11 @@ fstatFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn statfsFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; struct statfs hostBuf; @@ -607,7 +615,7 @@ statfsFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatfsBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatfsBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -617,9 +625,9 @@ statfsFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn fstatfsFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = process->sim_fd(xc->getSyscallArg(0)); + int fd = process->sim_fd(tc->getSyscallArg(0)); if (fd < 0) return -EBADF; @@ -630,7 +638,7 @@ fstatfsFunc(SyscallDesc *desc, int callnum, Process *process, if (result < 0) return -errno; - OS::copyOutStatfsBuf(xc->getMemPort(), xc->getSyscallArg(1), &hostBuf); + OS::copyOutStatfsBuf(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); return 0; } @@ -640,17 +648,17 @@ fstatfsFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn writevFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int fd = xc->getSyscallArg(0); + int fd = tc->getSyscallArg(0); if (fd < 0 || process->sim_fd(fd) < 0) { // doesn't map to any simulator fd: not a valid target fd return -EBADF; } - TranslatingPort *p = xc->getMemPort(); - uint64_t tiov_base = xc->getSyscallArg(1); - size_t count = xc->getSyscallArg(2); + TranslatingPort *p = tc->getMemPort(); + uint64_t tiov_base = tc->getSyscallArg(1); + size_t count = tc->getSyscallArg(2); struct iovec hiov[count]; for (int i = 0; i < count; ++i) { @@ -692,14 +700,14 @@ writevFunc(SyscallDesc *desc, int callnum, Process *process, /// anything else. template <class OS> SyscallReturn -mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) +mmapFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { - Addr start = xc->getSyscallArg(0); - uint64_t length = xc->getSyscallArg(1); - // int prot = xc->getSyscallArg(2); - int flags = xc->getSyscallArg(3); - // int fd = p->sim_fd(xc->getSyscallArg(4)); - // int offset = xc->getSyscallArg(5); + Addr start = tc->getSyscallArg(0); + uint64_t length = tc->getSyscallArg(1); + // int prot = tc->getSyscallArg(2); + int flags = tc->getSyscallArg(3); + // int fd = p->sim_fd(tc->getSyscallArg(4)); + // int offset = tc->getSyscallArg(5); if ((start % TheISA::VMPageSize) != 0 || (length % TheISA::VMPageSize) != 0) { @@ -721,7 +729,7 @@ mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) if (!(flags & OS::TGT_MAP_ANONYMOUS)) { warn("allowing mmap of file @ fd %d. " - "This will break if not /dev/zero.", xc->getSyscallArg(4)); + "This will break if not /dev/zero.", tc->getSyscallArg(4)); } return start; @@ -731,10 +739,10 @@ mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc) template <class OS> SyscallReturn getrlimitFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - unsigned resource = xc->getSyscallArg(0); - TypedBufferArg<typename OS::rlimit> rlp(xc->getSyscallArg(1)); + unsigned resource = tc->getSyscallArg(0); + TypedBufferArg<typename OS::rlimit> rlp(tc->getSyscallArg(1)); switch (resource) { case OS::TGT_RLIMIT_STACK: @@ -751,7 +759,7 @@ getrlimitFunc(SyscallDesc *desc, int callnum, Process *process, break; } - rlp.copyOut(xc->getMemPort()); + rlp.copyOut(tc->getMemPort()); return 0; } @@ -759,16 +767,16 @@ getrlimitFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn gettimeofdayFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - TypedBufferArg<typename OS::timeval> tp(xc->getSyscallArg(0)); + TypedBufferArg<typename OS::timeval> tp(tc->getSyscallArg(0)); getElapsedTime(tp->tv_sec, tp->tv_usec); tp->tv_sec += seconds_since_epoch; tp->tv_sec = htog(tp->tv_sec); tp->tv_usec = htog(tp->tv_usec); - tp.copyOut(xc->getMemPort()); + tp.copyOut(tc->getMemPort()); return 0; } @@ -778,15 +786,15 @@ gettimeofdayFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn utimesFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { std::string path; - if (!xc->getMemPort()->tryReadString(path, xc->getSyscallArg(0))) + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) return -EFAULT; - TypedBufferArg<typename OS::timeval [2]> tp(xc->getSyscallArg(1)); - tp.copyIn(xc->getMemPort()); + TypedBufferArg<typename OS::timeval [2]> tp(tc->getSyscallArg(1)); + tp.copyIn(tc->getMemPort()); struct timeval hostTimeval[2]; for (int i = 0; i < 2; ++i) @@ -805,10 +813,10 @@ utimesFunc(SyscallDesc *desc, int callnum, Process *process, template <class OS> SyscallReturn getrusageFunc(SyscallDesc *desc, int callnum, Process *process, - ExecContext *xc) + ThreadContext *tc) { - int who = xc->getSyscallArg(0); // THREAD, SELF, or CHILDREN - TypedBufferArg<typename OS::rusage> rup(xc->getSyscallArg(1)); + int who = tc->getSyscallArg(0); // THREAD, SELF, or CHILDREN + TypedBufferArg<typename OS::rusage> rup(tc->getSyscallArg(1)); if (who != OS::TGT_RUSAGE_SELF) { // don't really handle THREAD or CHILDREN, but just warn and @@ -838,7 +846,7 @@ getrusageFunc(SyscallDesc *desc, int callnum, Process *process, rup->ru_nvcsw = 0; rup->ru_nivcsw = 0; - rup.copyOut(xc->getMemPort()); + rup.copyOut(tc->getMemPort()); return 0; } diff --git a/src/sim/system.cc b/src/sim/system.cc index 89f39491e..89e7b8542 100644 --- a/src/sim/system.cc +++ b/src/sim/system.cc @@ -1,8 +1,41 @@ +/* + * Copyright (c) 2003-2006 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Lisa Hsu + * Nathan Binkert + * Ali Saidi + */ + #include "arch/isa_traits.hh" #include "base/loader/object_file.hh" #include "base/loader/symtab.hh" #include "base/trace.hh" -#include "cpu/exec_context.hh" +#include "cpu/thread_context.hh" #include "mem/mem_object.hh" #include "mem/physical.hh" #include "sim/builder.hh" @@ -86,8 +119,6 @@ System::System(Params *p) DPRINTF(Loader, "Kernel end = %#x\n", kernelEnd); DPRINTF(Loader, "Kernel entry = %#x\n", kernelEntry); DPRINTF(Loader, "Kernel loaded...\n"); - - kernelBinning = new Kernel::Binning(this); #endif // FULL_SYSTEM // increment the number of running systms @@ -99,8 +130,6 @@ System::~System() #if FULL_SYSTEM delete kernelSymtab; delete kernel; - - delete kernelBinning; #else panic("System::fixFuncEventAddr needs to be rewritten " "to work with syscall emulation"); @@ -115,26 +144,26 @@ int rgdb_wait = -1; #endif // FULL_SYSTEM int -System::registerExecContext(ExecContext *xc, int id) +System::registerThreadContext(ThreadContext *tc, int id) { if (id == -1) { - for (id = 0; id < execContexts.size(); id++) { - if (!execContexts[id]) + for (id = 0; id < threadContexts.size(); id++) { + if (!threadContexts[id]) break; } } - if (execContexts.size() <= id) - execContexts.resize(id + 1); + if (threadContexts.size() <= id) + threadContexts.resize(id + 1); - if (execContexts[id]) + if (threadContexts[id]) panic("Cannot have two CPUs with the same id (%d)\n", id); - execContexts[id] = xc; + threadContexts[id] = tc; numcpus++; #if FULL_SYSTEM - RemoteGDB *rgdb = new RemoteGDB(this, xc); + RemoteGDB *rgdb = new RemoteGDB(this, tc); GDBListener *gdbl = new GDBListener(rgdb, 7000 + id); gdbl->listen(); /** @@ -158,21 +187,21 @@ void System::startup() { int i; - for (i = 0; i < execContexts.size(); i++) - execContexts[i]->activate(0); + for (i = 0; i < threadContexts.size(); i++) + threadContexts[i]->activate(0); } void -System::replaceExecContext(ExecContext *xc, int id) +System::replaceThreadContext(ThreadContext *tc, int id) { - if (id >= execContexts.size()) { - panic("replaceExecContext: bad id, %d >= %d\n", - id, execContexts.size()); + if (id >= threadContexts.size()) { + panic("replaceThreadContext: bad id, %d >= %d\n", + id, threadContexts.size()); } - execContexts[id] = xc; + threadContexts[id] = tc; #if FULL_SYSTEM - remoteGDB[id]->replaceExecContext(xc); + remoteGDB[id]->replaceThreadContext(tc); #endif // FULL_SYSTEM } @@ -187,19 +216,9 @@ System::new_page() #endif void -System::regStats() -{ -#if FULL_SYSTEM - kernelBinning->regStats(name() + ".kern"); -#endif // FULL_SYSTEM -} - -void System::serialize(ostream &os) { #if FULL_SYSTEM - kernelBinning->serialize(os); - kernelSymtab->serialize("kernel_symtab", os); #endif // FULL_SYSTEM } @@ -209,8 +228,6 @@ void System::unserialize(Checkpoint *cp, const string §ion) { #if FULL_SYSTEM - kernelBinning->unserialize(cp, section); - kernelSymtab->unserialize("kernel_symtab", cp, section); #endif // FULL_SYSTEM } @@ -226,7 +243,6 @@ System::printSystems() } } -extern "C" void printSystems() { diff --git a/src/sim/system.hh b/src/sim/system.hh index 7e21bd587..059dc92dc 100644 --- a/src/sim/system.hh +++ b/src/sim/system.hh @@ -24,6 +24,10 @@ * 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu + * Nathan Binkert */ #ifndef __SYSTEM_HH__ @@ -44,7 +48,7 @@ #endif class BaseCPU; -class ExecContext; +class ThreadContext; class ObjectFile; class PhysicalMemory; @@ -52,7 +56,6 @@ class PhysicalMemory; class Platform; class GDBListener; class RemoteGDB; -namespace Kernel { class Binning; } #endif class System : public SimObject @@ -61,12 +64,12 @@ class System : public SimObject PhysicalMemory *physmem; PCEventQueue pcEventQueue; - std::vector<ExecContext *> execContexts; + std::vector<ThreadContext *> threadContexts; int numcpus; int getNumCPUs() { - if (numcpus != execContexts.size()) + if (numcpus != threadContexts.size()) panic("cpu array not fully populated!"); return numcpus; @@ -96,8 +99,6 @@ class System : public SimObject /** Entry point in the kernel to start at */ Addr kernelEntry; - Kernel::Binning *kernelBinning; - #else int page_ptr; @@ -157,9 +158,6 @@ class System : public SimObject Tick boot_cpu_frequency; std::string boot_osflags; uint64_t init_param; - bool bin; - std::vector<std::string> binned_fns; - bool bin_int; std::string kernel_path; std::string readfile; @@ -204,10 +202,9 @@ class System : public SimObject #endif // FULL_SYSTEM - int registerExecContext(ExecContext *xc, int xcIndex); - void replaceExecContext(ExecContext *xc, int xcIndex); + int registerThreadContext(ThreadContext *tc, int tcIndex); + void replaceThreadContext(ThreadContext *tc, int tcIndex); - void regStats(); void serialize(std::ostream &os); void unserialize(Checkpoint *cp, const std::string §ion); diff --git a/src/sim/vptr.hh b/src/sim/vptr.hh index cc57e63f0..bcc22f0ca 100644 --- a/src/sim/vptr.hh +++ b/src/sim/vptr.hh @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #ifndef __ARCH_ALPHA_VPTR_HH__ @@ -32,7 +34,7 @@ #include "arch/vtophys.hh" #include "arch/isa_traits.hh" -class ExecContext; +class ThreadContext; template <class T> class VPtr @@ -41,17 +43,17 @@ class VPtr typedef T Type; private: - ExecContext *xc; + ThreadContext *tc; Addr ptr; public: - ExecContext *GetXC() const { return xc; } + ThreadContext *GetTC() const { return tc; } Addr GetPointer() const { return ptr; } public: - explicit VPtr(ExecContext *_xc, Addr p = 0) : xc(_xc), ptr(p) { } + explicit VPtr(ThreadContext *_tc, Addr p = 0) : tc(_tc), ptr(p) { } template <class U> - VPtr(const VPtr<U> &vp) : xc(vp.GetXC()), ptr(vp.GetPointer()) {} + VPtr(const VPtr<U> &vp) : tc(vp.GetTC()), ptr(vp.GetPointer()) {} ~VPtr() {} bool operator!() const @@ -88,7 +90,7 @@ class VPtr template <class U> const VPtr<T> &operator=(const VPtr<U> &vp) { - xc = vp.GetXC(); + tc = vp.GetTC(); ptr = vp.GetPointer(); return *this; @@ -97,7 +99,7 @@ class VPtr operator T *() { panic("Needs to be rewritten\n"); -/* void *addr = vtomem(xc, ptr, sizeof(T)); +/* void *addr = vtomem(tc, ptr, sizeof(T)); return (T *)addr; */ } @@ -105,7 +107,7 @@ class VPtr T *operator->() { panic("Needs to be rewritten\n"); -/* void *addr = vtomem(xc, ptr, sizeof(T)); +/* void *addr = vtomem(tc, ptr, sizeof(T)); return (T *)addr; */ } @@ -113,7 +115,7 @@ class VPtr T &operator*() { panic("Needs to be rewritten\n"); -/* void *addr = vtomem(xc, ptr, sizeof(T)); +/* void *addr = vtomem(tc, ptr, sizeof(T)); return *(T *)addr; */ } diff --git a/src/unittest/bitvectest.cc b/src/unittest/bitvectest.cc index 1b8c332f5..440a150a3 100644 --- a/src/unittest/bitvectest.cc +++ b/src/unittest/bitvectest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream.h> diff --git a/src/unittest/circletest.cc b/src/unittest/circletest.cc index bb15f8c64..f072cf044 100644 --- a/src/unittest/circletest.cc +++ b/src/unittest/circletest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <fcntl.h> diff --git a/src/unittest/cprintftest.cc b/src/unittest/cprintftest.cc index 361f84028..9c3eb0cd6 100644 --- a/src/unittest/cprintftest.cc +++ b/src/unittest/cprintftest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/unittest/genini.py b/src/unittest/genini.py index 2af81fe2b..a969a6ee4 100755 --- a/src/unittest/genini.py +++ b/src/unittest/genini.py @@ -24,6 +24,8 @@ # 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. +# +# Authors: Nathan Binkert import getopt, os, os.path, sys from os.path import join as joinpath, realpath diff --git a/src/unittest/initest.cc b/src/unittest/initest.cc index 0c5ac2343..8f53fce5c 100644 --- a/src/unittest/initest.cc +++ b/src/unittest/initest.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt */ #include <iostream> diff --git a/src/unittest/lru_test.cc b/src/unittest/lru_test.cc index 2829163de..d10eb1dd0 100644 --- a/src/unittest/lru_test.cc +++ b/src/unittest/lru_test.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Dave Greene + * Nathan Binkert */ #include <iostream> diff --git a/src/unittest/nmtest.cc b/src/unittest/nmtest.cc index e9c20d19d..b6b74e08d 100644 --- a/src/unittest/nmtest.cc +++ b/src/unittest/nmtest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/unittest/offtest.cc b/src/unittest/offtest.cc index d3f035b73..ebfb2515c 100644 --- a/src/unittest/offtest.cc +++ b/src/unittest/offtest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <sys/types.h> diff --git a/src/unittest/paramtest.cc b/src/unittest/paramtest.cc index cb31c49d5..e513ab981 100644 --- a/src/unittest/paramtest.cc +++ b/src/unittest/paramtest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ // diff --git a/src/unittest/rangetest.cc b/src/unittest/rangetest.cc index 41d438f48..b7a68ab44 100644 --- a/src/unittest/rangetest.cc +++ b/src/unittest/rangetest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/unittest/sized_test.cc b/src/unittest/sized_test.cc index 86cd13e5b..f1bf7528f 100644 --- a/src/unittest/sized_test.cc +++ b/src/unittest/sized_test.cc @@ -24,6 +24,9 @@ * 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. + * + * Authors: Dave Greene + * Nathan Binkert */ #include <iostream> diff --git a/src/unittest/stattest.cc b/src/unittest/stattest.cc index b944eff45..4e504fde9 100644 --- a/src/unittest/stattest.cc +++ b/src/unittest/stattest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iomanip> @@ -48,10 +50,10 @@ Tick ticksPerSecond = ULL(2000000000); Scalar<> s1; Scalar<> s2; Average<> s3; -Scalar<MainBin> s4; -Vector<MainBin> s5; -Distribution<MainBin> s6; -Vector<MainBin> s7; +Scalar<> s4; +Vector<> s5; +Distribution<> s6; +Vector<> s7; AverageVector<> s8; StandardDeviation<> s9; AverageDeviation<> s10; @@ -70,9 +72,6 @@ Value f5; Formula f6; Formula f7; -MainBin bin1("bin1"); -MainBin bin2("bin2"); - ostream *outputStream = &cout; double @@ -301,8 +300,6 @@ main(int argc, char *argv[]) check(); reset(); - bin1.activate(); - s16[1][0] = 1; s16[0][1] = 3; s16[0][0] = 2; @@ -493,7 +490,6 @@ main(int argc, char *argv[]) s6.sample(8); s6.sample(9); - bin2.activate(); s6.sample(10); s6.sample(10); s6.sample(10); diff --git a/src/unittest/strnumtest.cc b/src/unittest/strnumtest.cc index a80dd7c36..ea28e35df 100644 --- a/src/unittest/strnumtest.cc +++ b/src/unittest/strnumtest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream.h> diff --git a/src/unittest/symtest.cc b/src/unittest/symtest.cc index 5fba71736..f0142b923 100644 --- a/src/unittest/symtest.cc +++ b/src/unittest/symtest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream.h> diff --git a/src/unittest/tokentest.cc b/src/unittest/tokentest.cc index 7f27d58fe..128346b5b 100644 --- a/src/unittest/tokentest.cc +++ b/src/unittest/tokentest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <iostream> diff --git a/src/unittest/tracetest.cc b/src/unittest/tracetest.cc index 866b67d9b..b1343aac3 100644 --- a/src/unittest/tracetest.cc +++ b/src/unittest/tracetest.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include "sim/host.hh" diff --git a/util/ccdrv/devtime.c b/util/ccdrv/devtime.c index 62b0e2592..d8be97bb4 100644 --- a/util/ccdrv/devtime.c +++ b/util/ccdrv/devtime.c @@ -24,6 +24,8 @@ * 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. + * + * Authors: Ali Saidi */ #include <linux/module.h> diff --git a/util/m5/Makefile b/util/m5/Makefile index 518542322..a98092e47 100644 --- a/util/m5/Makefile +++ b/util/m5/Makefile @@ -23,6 +23,9 @@ # 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. +# +# Authors: Nathan Binkert +# Ali Saidi ### If we are not compiling on an alpha, we must use cross tools ### ifneq ($(shell uname -m), alpha) diff --git a/util/m5/m5.c b/util/m5/m5.c index 6fdbc0500..c9d7d5a47 100644 --- a/util/m5/m5.c +++ b/util/m5/m5.c @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ #include <inttypes.h> diff --git a/util/m5/m5op.S b/util/m5/m5op.S index a53c45277..a55532c90 100644 --- a/util/m5/m5op.S +++ b/util/m5/m5op.S @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ #define m5_op 0x01 diff --git a/util/m5/m5op.h b/util/m5/m5op.h index 34ac7760d..f96c5097a 100644 --- a/util/m5/m5op.h +++ b/util/m5/m5op.h @@ -24,6 +24,9 @@ * 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. + * + * Authors: Nathan Binkert + * Ali Saidi */ #ifndef __M5OP_H__ diff --git a/util/oprofile-top.py b/util/oprofile-top.py index c6e35344c..aea9dfadd 100755 --- a/util/oprofile-top.py +++ b/util/oprofile-top.py @@ -25,6 +25,9 @@ # 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. +# +# Authors: Ali Saidi +# Nathan Binkert # Parse sampled function profile output (quick hack). @@ -25,6 +25,8 @@ # 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. +# +# Authors: Steve Reinhardt import sys import os diff --git a/util/rundiff b/util/rundiff index 9376e4b9e..c34bb53a3 100755 --- a/util/rundiff +++ b/util/rundiff @@ -25,6 +25,9 @@ # 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. +# +# Authors: Nathan Binkert +# Steve Reinhardt # Diff two streams. # @@ -36,7 +39,7 @@ # "filename" is a pipe (|). Thus to compare the instruction traces # from two versions of m5 (m5a and m5b), you can do this: # -# rundiff 'm5a --trace:flags=InstExec |' 'm5b --trace:flags=InstExec |' +# rundiff 'm5a --traceflags=InstExec |' 'm5b --traceflags=InstExec |' # use strict; diff --git a/util/stats/db.py b/util/stats/db.py index c0e7796eb..e1198d438 100644 --- a/util/stats/db.py +++ b/util/stats/db.py @@ -23,6 +23,8 @@ # 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. +# +# Authors: Nathan Binkert import MySQLdb, re, string @@ -135,10 +137,6 @@ class Database(object): self.allRunIds = {} self.allRunNames = {} - self.allBins = [] - self.allBinIds = {} - self.allBinNames = {} - self.allFormulas = {} self.stattop = {} @@ -147,7 +145,6 @@ class Database(object): self.mode = 'sum'; self.runs = None - self.bins = None self.ticks = None self.method = 'sum' self._method = type(self).sum @@ -218,11 +215,6 @@ class Database(object): self.allRunIds[run.run] = run self.allRunNames[run.name] = run - self.query('select * from bins') - for id,name in self.cursor.fetchall(): - self.allBinIds[int(id)] = name - self.allBinNames[name] = int(id) - self.query('select sd_stat,sd_x,sd_y,sd_name,sd_descr from subdata') for result in self.cursor.fetchall(): subdata = SubData(result) @@ -245,18 +237,6 @@ class Database(object): self.allStatIds[stat.stat] = stat self.allStatNames[stat.name] = stat - # Name: listbins - # Desc: Prints all bins matching regex argument, if no argument - # is given all bins are returned - def listBins(self, regex='.*'): - print '%-50s %-10s' % ('bin name', 'id') - print '-' * 61 - names = self.allBinNames.keys() - names.sort() - for name in names: - id = self.allBinNames[name] - print '%-50s %-10d' % (name, id) - # Name: listruns # Desc: Prints all runs matching a given user, if no argument # is given all runs are returned @@ -360,39 +340,10 @@ class Database(object): ret.append(stat) return ret - def getBin(self, bins): - if type(bins) is not list: - bins = [ bins ] - - ret = [] - for bin in bins: - if type(bin) is int: - ret.append(bin) - elif type(bin) is str: - ret.append(self.allBinNames[bin]) - else: - for name,id in self.allBinNames.items(): - if bin.match(name): - ret.append(id) - - return ret - - def getNotBin(self, bin): - map = {} - for bin in getBin(bin): - map[bin] = 1 - - ret = [] - for bin in self.allBinIds.keys(): - if not map.has_key(bin): - ret.append(bin) - - return ret - ######################################### # get the data # - def inner(self, op, stat, bins, ticks, group=False): + def query(self, op, stat, ticks, group=False): sql = 'select ' sql += 'dt_stat as stat, ' sql += 'dt_run as run, ' @@ -414,10 +365,6 @@ class Database(object): val = ' or '.join([ 'dt_run=%d' % r for r in self.runs ]) sql += ' and (%s)' % val - if bins != None and len(bins): - val = ' or '.join([ 'dt_bin=%d' % b for b in bins ]) - sql += ' and (%s)' % val - if ticks != None and len(ticks): val = ' or '.join([ 'dt_tick=%d' % s for s in ticks ]) sql += ' and (%s)' % val @@ -427,35 +374,21 @@ class Database(object): sql += ',dt_tick' return sql - def outer(self, op_out, op_in, stat, bins, ticks): - sql = self.inner(op_in, stat, bins, ticks, True) - sql = 'select stat,run,x,y,%s(data) from (%s) as tb ' % (op_out, sql) - sql += 'group by stat,run,x,y' - return sql - # Name: sum - # Desc: given a run, a stat and an array of samples and bins, - # sum all the bins and then get the standard deviation of the - # samples for non-binned runs. This will just return the average - # of samples, however a bin array still must be passed - def sum(self, stat, bins, ticks): - return self.inner('sum', stat, bins, ticks) + # Desc: given a run, a stat and an array of samples, total the samples + def sum(self, *args, **kwargs): + return self.query('sum', *args, **kwargs) # Name: avg - # Desc: given a run, a stat and an array of samples and bins, - # sum all the bins and then average the samples for non-binned - # runs this will just return the average of samples, however - # a bin array still must be passed - def avg(self, stat, bins, ticks): - return self.outer('avg', 'sum', stat, bins, ticks) + # Desc: given a run, a stat and an array of samples, average the samples + def avg(self, stat, ticks): + return self.query('avg', *args, **kwargs) # Name: stdev - # Desc: given a run, a stat and an array of samples and bins, - # sum all the bins and then get the standard deviation of the - # samples for non-binned runs. This will just return the average - # of samples, however a bin array still must be passed - def stdev(self, stat, bins, ticks): - return self.outer('stddev', 'sum', stat, bins, ticks) + # Desc: given a run, a stat and an array of samples, get the standard + # deviation + def stdev(self, stat, ticks): + return self.query('stddev', *args, **kwargs) def __setattr__(self, attr, value): super(Database, self).__setattr__(attr, value) @@ -471,12 +404,10 @@ class Database(object): else: raise AttributeError, "can only set get to: sum | avg | stdev" - def data(self, stat, bins=None, ticks=None): - if bins is None: - bins = self.bins + def data(self, stat, ticks=None): if ticks is None: ticks = self.ticks - sql = self._method(self, stat, bins, ticks) + sql = self._method(self, stat, ticks) self.query(sql) runs = {} diff --git a/util/stats/dbinit.py b/util/stats/dbinit.py index ffae88cdb..036941675 100644 --- a/util/stats/dbinit.py +++ b/util/stats/dbinit.py @@ -23,6 +23,8 @@ # 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. +# +# Authors: Nathan Binkert import MySQLdb @@ -98,28 +100,6 @@ class MyDB(object): ) TYPE=InnoDB''') # - # We keep the bin names separate so that the data table doesn't get - # huge since bin names are frequently repeated. - # - # COLUMNS: - # 'id' is the unique bin identifer. - # 'name' is the string name for the bin. - # - # INDEXES: - # 'bin' is indexed to get the name of a bin when data is retrieved - # via the data table. - # 'name' is indexed to get the bin id for a named bin when you want - # to search the data table based on a specific bin. - # - self.query(''' - CREATE TABLE bins( - bn_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, - bn_name VARCHAR(255) NOT NULL, - PRIMARY KEY(bn_id), - UNIQUE (bn_name) - ) TYPE=InnoDB''') - - # # The stat table gives us all of the data for a particular stat. # # COLUMNS: @@ -220,14 +200,12 @@ class MyDB(object): # 'run' is the run that the data was generated from. Details up in # the run table # 'tick' is a timestamp generated by the simulator. - # 'bin' is the name of the bin that the data was generated in, if - # any. # 'data' is the actual stat value. # # INDEXES: # 'stat' is indexed so that a user can find all of the data for a # particular stat. It is not unique, because that specific stat - # can be found in many runs, bins, and samples, in addition to + # can be found in many runs and samples, in addition to # having entries for the mulidimensional cases. # 'run' is indexed to allow a user to remove all of the data for a # particular execution run. It can also be used to allow the @@ -240,11 +218,10 @@ class MyDB(object): dt_y SMALLINT NOT NULL, dt_run SMALLINT UNSIGNED NOT NULL, dt_tick BIGINT UNSIGNED NOT NULL, - dt_bin SMALLINT UNSIGNED NOT NULL, dt_data DOUBLE NOT NULL, INDEX (dt_stat), INDEX (dt_run), - UNIQUE (dt_stat,dt_x,dt_y,dt_run,dt_tick,dt_bin) + UNIQUE (dt_stat,dt_x,dt_y,dt_run,dt_tick) ) TYPE=InnoDB;''') # @@ -396,12 +373,6 @@ class MyDB(object): WHERE dt_stat IS NULL''') self.query(''' - DELETE bins - FROM bins - LEFT JOIN data ON bn_id=dt_bin - WHERE dt_bin IS NULL''') - - self.query(''' DELETE events FROM events LEFT JOIN runs ON ev_run=rn_id diff --git a/util/stats/display.py b/util/stats/display.py index fbcff5c70..cb825f493 100644 --- a/util/stats/display.py +++ b/util/stats/display.py @@ -23,6 +23,8 @@ # 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. +# +# Authors: Nathan Binkert class Value: def __init__(self, value, precision, percent = False): diff --git a/util/stats/info.py b/util/stats/info.py index 4cb55f564..193159b4c 100644 --- a/util/stats/info.py +++ b/util/stats/info.py @@ -23,6 +23,8 @@ # 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. +# +# Authors: Nathan Binkert from __future__ import division import operator, re, types @@ -174,7 +176,7 @@ def WrapValue(value): class Statistic(object): def __getattr__(self, attr): if attr in ('data', 'x', 'y'): - result = self.source.data(self, self.bins, self.ticks) + result = self.source.data(self, self.ticks) self.data = result.data self.x = result.x self.y = result.y @@ -183,7 +185,7 @@ class Statistic(object): def __setattr__(self, attr, value): if attr == 'stat': raise AttributeError, '%s is read only' % stat - if attr in ('source', 'bins', 'ticks'): + if attr in ('source', 'ticks'): if getattr(self, attr) != value: if hasattr(self, 'data'): delattr(self, 'data') @@ -759,7 +761,6 @@ def NewStat(source, data): stat = Formula() stat.__dict__['source'] = source - stat.__dict__['bins'] = None stat.__dict__['ticks'] = None stat.__dict__.update(data.__dict__) diff --git a/util/stats/orderdict.py b/util/stats/orderdict.py index 816355ae2..3f755d299 100644 --- a/util/stats/orderdict.py +++ b/util/stats/orderdict.py @@ -23,6 +23,8 @@ # 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. +# +# Authors: Nathan Binkert __all__ = [ 'orderdict' ] diff --git a/util/stats/output.py b/util/stats/output.py index abfb8d901..03c100edc 100644 --- a/util/stats/output.py +++ b/util/stats/output.py @@ -29,24 +29,16 @@ from chart import ChartOptions class StatOutput(ChartOptions): - def __init__(self, jobfile, info, stat=None, binstats=None): + def __init__(self, jobfile, info, stat=None): super(StatOutput, self).__init__() self.jobfile = jobfile self.stat = stat - self.binstats = None self.invert = False self.info = info - def printdata(self, name, bin = None, printmode = 'G'): + def display(self, name, printmode = 'G'): import info - if bin: - print '%s %s stats' % (name, bin) - - if self.binstats: - for stat in self.binstats: - stat.bins = bin - if printmode == 'G': valformat = '%g' elif printmode != 'F' and value > 1e6: @@ -70,16 +62,6 @@ class StatOutput(ChartOptions): valstring = ', '.join([ valformat % val for val in value ]) print '%-50s %s' % (job.name + ':', valstring) - def display(self, name, binned = False, printmode = 'G'): - if binned and self.binstats: - self.printdata(name, 'kernel', printmode) - self.printdata(name, 'idle', printmode) - self.printdata(name, 'user', printmode) - self.printdata(name, 'interrupt', printmode) - - print '%s total stats' % name - self.printdata(name, printmode=printmode) - def graph(self, name, graphdir, proxy=None): from os.path import expanduser, isdir, join as joinpath from barchart import BarChart diff --git a/util/stats/print.py b/util/stats/print.py index 2572fd4e7..1c83fa87d 100644 --- a/util/stats/print.py +++ b/util/stats/print.py @@ -23,6 +23,8 @@ # 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. +# +# Authors: Nathan Binkert all = False descriptions = False diff --git a/util/stats/profile.py b/util/stats/profile.py index f28c5867c..6959e0e34 100644 --- a/util/stats/profile.py +++ b/util/stats/profile.py @@ -23,6 +23,8 @@ # 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. +# +# Authors: Nathan Binkert from orderdict import orderdict import output diff --git a/util/stats/stats.py b/util/stats/stats.py index 08281287f..2aa0d4e0b 100755 --- a/util/stats/stats.py +++ b/util/stats/stats.py @@ -25,6 +25,8 @@ # 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. +# +# Authors: Nathan Binkert from __future__ import division import re, sys, math @@ -37,7 +39,6 @@ Usage: %s [-E] [-F] [ -G <get> ] [-d <db> ] [-g <graphdir> ] [-h <host>] [-p] commands extra parameters description ----------- ------------------ --------------------------------------- - bins [regex] List bins (only matching regex) formula <formula> Evaluated formula specified formulas [regex] List formulas (only matching regex) runs none List all runs in database @@ -140,16 +141,6 @@ def commands(options, command, args): return - if command == 'bins': - if len(args) == 0: - source.listBins() - elif len(args) == 1: - source.listBins(args[0]) - else: - raise CommandException - - return - if command == 'formulas': if len(args) == 0: source.listFormulas() @@ -279,7 +270,7 @@ def commands(options, command, args): if options.graph: output.graph(stat.name, options.graphdir) else: - output.display(stat.name, options.binned, options.printmode) + output.display(stat.name, options.printmode) return @@ -299,22 +290,10 @@ def commands(options, command, args): if options.graph: output.graph(command, options.graphdir, proxy) else: - output.display(command, options.binned, options.printmode) - - if command == 'usertime': - import copy - user = copy.copy(system.run0.numCycles) - user.bins = 'user' - - output.stat = user / system.run0.numCycles - output.ylabel = 'User Fraction' - - display() - return + output.display(command, options.printmode) if command == 'ticks': output.stat = system.run0.numCycles - output.binstats = [ system.run0.numCycles ] display() return @@ -401,7 +380,6 @@ def commands(options, command, args): if command == 'mpkb': output.stat = misses / (bytes / 1024) - output.binstats = [ misses ] output.ylabel = 'Misses / KB' display() return @@ -409,7 +387,6 @@ def commands(options, command, args): if command == 'ipkb': interrupts = system.run0.kern.faults[4] output.stat = interrupts / kbytes - output.binstats = [ interrupts ] output.ylabel = 'Interrupts / KB' display() return @@ -446,7 +423,6 @@ if __name__ == '__main__': options.runs = None options.system = 'client' options.method = None - options.binned = False options.graph = False options.ticks = False options.printmode = 'G' @@ -454,10 +430,8 @@ if __name__ == '__main__': options.jobfile = None options.all = False - opts, args = getopts(sys.argv[1:], '-BEFJad:g:h:j:m:pr:s:u:T:') + opts, args = getopts(sys.argv[1:], '-EFJad:g:h:j:m:pr:s:u:T:') for o,a in opts: - if o == '-B': - options.binned = True if o == '-E': options.printmode = 'E' if o == '-F': diff --git a/util/tap/tap.cc b/util/tap/tap.cc index f4d0aadf2..b026e0220 100644 --- a/util/tap/tap.cc +++ b/util/tap/tap.cc @@ -24,6 +24,8 @@ * 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. + * + * Authors: Nathan Binkert */ extern "C" { diff --git a/util/tracediff b/util/tracediff index f11431293..a7efc260d 100755 --- a/util/tracediff +++ b/util/tracediff @@ -1,5 +1,5 @@ #! /usr/bin/env perl -# Copyright (c) 2003-2005 The Regents of The University of Michigan +# Copyright (c) 2003-2006 The Regents of The University of Michigan # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -36,7 +36,7 @@ # If you want to pass different arguments to the two instances of m5, # you can embed them in the simulator arguments like this: # -# % tracediff "m5.opt --foo.bar=1" "m5.opt --foo.bar=2" [common args] +# % tracediff "m5.opt --option1" "m5.opt --option2" [common args] # if (@ARGV < 2) { |