Adding gem5 source to svn.
git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1819 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
61
simulators/gem5/src/mem/AbstractMemory.py
Normal file
61
simulators/gem5/src/mem/AbstractMemory.py
Normal file
@ -0,0 +1,61 @@
|
||||
# Copyright (c) 2012 ARM Limited
|
||||
# All rights reserved.
|
||||
#
|
||||
# The license below extends only to copyright in the software and shall
|
||||
# not be construed as granting a license to any other intellectual
|
||||
# property including but not limited to intellectual property relating
|
||||
# to a hardware implementation of the functionality of the software
|
||||
# licensed hereunder. You may use the software subject to the license
|
||||
# terms below provided that you ensure that this notice is replicated
|
||||
# unmodified and in its entirety in all distributions of the software,
|
||||
# modified or unmodified, in source code or in binary form.
|
||||
#
|
||||
# Copyright (c) 2005-2008 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
|
||||
# Andreas Hansson
|
||||
|
||||
from m5.params import *
|
||||
from MemObject import MemObject
|
||||
|
||||
class AbstractMemory(MemObject):
|
||||
type = 'AbstractMemory'
|
||||
abstract = True
|
||||
range = Param.AddrRange(AddrRange('128MB'), "Address range")
|
||||
file = Param.String('', "Memory-mapped file")
|
||||
null = Param.Bool(False, "Do not store data, always return zero")
|
||||
zero = Param.Bool(False, "Initialize memory with zeros")
|
||||
|
||||
# All memories are passed to the global physical memory, and
|
||||
# certain memories may be excluded from the global address map,
|
||||
# e.g. by the testers that use shadow memories as a reference
|
||||
in_addr_map = Param.Bool(True, "Memory part of the global address map")
|
||||
|
||||
# Should the bootloader include this memory when passing
|
||||
# configuration information about the physical memory layout to
|
||||
# the kernel, e.g. using ATAG or ACPI
|
||||
conf_table_reported = Param.Bool(False, "Report to configuration table")
|
||||
42
simulators/gem5/src/mem/Bridge.py
Normal file
42
simulators/gem5/src/mem/Bridge.py
Normal file
@ -0,0 +1,42 @@
|
||||
# Copyright (c) 2006-2007 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
|
||||
|
||||
from m5.params import *
|
||||
from MemObject import MemObject
|
||||
|
||||
class Bridge(MemObject):
|
||||
type = 'Bridge'
|
||||
slave = SlavePort('Slave port')
|
||||
master = MasterPort('Master port')
|
||||
req_size = Param.Int(16, "The number of requests to buffer")
|
||||
resp_size = Param.Int(16, "The number of requests to buffer")
|
||||
delay = Param.Latency('0ns', "The latency of this bridge")
|
||||
nack_delay = Param.Latency('0ns', "The latency of this bridge")
|
||||
write_ack = Param.Bool(False, "Should this bridge ack writes")
|
||||
ranges = VectorParam.AddrRange([AllMemory],
|
||||
"Address ranges to pass through the bridge")
|
||||
72
simulators/gem5/src/mem/Bus.py
Normal file
72
simulators/gem5/src/mem/Bus.py
Normal file
@ -0,0 +1,72 @@
|
||||
# Copyright (c) 2012 ARM Limited
|
||||
# All rights reserved.
|
||||
#
|
||||
# The license below extends only to copyright in the software and shall
|
||||
# not be construed as granting a license to any other intellectual
|
||||
# property including but not limited to intellectual property relating
|
||||
# to a hardware implementation of the functionality of the software
|
||||
# licensed hereunder. You may use the software subject to the license
|
||||
# terms below provided that you ensure that this notice is replicated
|
||||
# unmodified and in its entirety in all distributions of the software,
|
||||
# modified or unmodified, in source code or in binary form.
|
||||
#
|
||||
# Copyright (c) 2005-2008 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
|
||||
# Andreas Hansson
|
||||
|
||||
from MemObject import MemObject
|
||||
from m5.params import *
|
||||
|
||||
class BaseBus(MemObject):
|
||||
type = 'BaseBus'
|
||||
abstract = True
|
||||
slave = VectorSlavePort("vector port for connecting masters")
|
||||
master = VectorMasterPort("vector port for connecting slaves")
|
||||
clock = Param.Clock("1GHz", "bus clock speed")
|
||||
header_cycles = Param.Int(1, "cycles of overhead per transaction")
|
||||
width = Param.Int(64, "bus width (bytes)")
|
||||
block_size = Param.Int(64, "The default block size if not set by " \
|
||||
"any connected module")
|
||||
|
||||
# The default port can be left unconnected, or be used to connect
|
||||
# a default slave port
|
||||
default = MasterPort("Port for connecting an optional default slave")
|
||||
|
||||
# The default port can be used unconditionally, or based on
|
||||
# address range, in which case it may overlap with other
|
||||
# ports. The default range is always checked first, thus creating
|
||||
# a two-level hierarchical lookup. This is useful e.g. for the PCI
|
||||
# bus configuration.
|
||||
use_default_range = Param.Bool(False, "Perform address mapping for " \
|
||||
"the default port")
|
||||
|
||||
class NoncoherentBus(BaseBus):
|
||||
type = 'NoncoherentBus'
|
||||
|
||||
class CoherentBus(BaseBus):
|
||||
type = 'CoherentBus'
|
||||
97
simulators/gem5/src/mem/CommMonitor.py
Normal file
97
simulators/gem5/src/mem/CommMonitor.py
Normal file
@ -0,0 +1,97 @@
|
||||
# Copyright (c) 2012 ARM Limited
|
||||
# All rights reserved.
|
||||
#
|
||||
# The license below extends only to copyright in the software and shall
|
||||
# not be construed as granting a license to any other intellectual
|
||||
# property including but not limited to intellectual property relating
|
||||
# to a hardware implementation of the functionality of the software
|
||||
# licensed hereunder. You may use the software subject to the license
|
||||
# terms below provided that you ensure that this notice is replicated
|
||||
# unmodified and in its entirety in all distributions of the software,
|
||||
# modified or unmodified, in source code or in binary form.
|
||||
#
|
||||
# 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: Thomas Grass
|
||||
# Andreas Hansson
|
||||
|
||||
from m5.params import *
|
||||
from MemObject import MemObject
|
||||
|
||||
# The communication monitor will most typically be used in combination
|
||||
# with periodic dumping and resetting of stats using schedStatEvent
|
||||
class CommMonitor(MemObject):
|
||||
type = 'CommMonitor'
|
||||
|
||||
# one port in each direction
|
||||
master = MasterPort("Master port")
|
||||
slave = SlavePort("Slave port")
|
||||
|
||||
# control the sample period window length of this monitor
|
||||
sample_period = Param.Clock("1ms", "Sample period for histograms")
|
||||
|
||||
# for each histogram, set the number of bins and enable the user
|
||||
# to disable the measurement, reads and writes use the same
|
||||
# parameters
|
||||
|
||||
# histogram of burst length of packets (not using sample period)
|
||||
burst_length_bins = Param.Unsigned('20', "# bins in burst length " \
|
||||
"histograms")
|
||||
disable_burst_length_hists = Param.Bool(False, "Disable burst length " \
|
||||
"histograms")
|
||||
|
||||
# bandwidth per sample period
|
||||
bandwidth_bins = Param.Unsigned('20', "# bins in bandwidth histograms")
|
||||
disable_bandwidth_hists = Param.Bool(False, "Disable bandwidth histograms")
|
||||
|
||||
# latency from request to response (not using sample period)
|
||||
latency_bins = Param.Unsigned('20', "# bins in latency histograms")
|
||||
disable_latency_hists = Param.Bool(False, "Disable latency histograms")
|
||||
|
||||
# inter transaction time (ITT) distributions in uniformly sized
|
||||
# bins up to the maximum, independently for read-to-read,
|
||||
# write-to-write and the combined request-to-request that does not
|
||||
# separate read and write requests
|
||||
itt_bins = Param.Unsigned('20', "# bins in ITT distributions")
|
||||
itt_max_bin = Param.Latency('100ns', "Max bin of ITT distributions")
|
||||
disable_itt_dists = Param.Bool(False, "Disable ITT distributions")
|
||||
|
||||
# outstanding requests (that did not yet get a response) per
|
||||
# sample period
|
||||
outstanding_bins = Param.Unsigned('20', "# bins in outstanding " \
|
||||
"requests histograms")
|
||||
disable_outstanding_hists = Param.Bool(False, "Disable outstanding " \
|
||||
"requests histograms")
|
||||
|
||||
# transactions (requests) observed per sample period
|
||||
transaction_bins = Param.Unsigned('20', "# bins in transaction " \
|
||||
"count histograms")
|
||||
disable_transaction_hists = Param.Bool(False, "Disable transaction count " \
|
||||
"histograms")
|
||||
|
||||
# address distributions (heatmaps) with associated address masks
|
||||
# to selectively only look at certain bits of the address
|
||||
read_addr_mask = Param.Addr(MaxAddr, "Address mask for read address")
|
||||
write_addr_mask = Param.Addr(MaxAddr, "Address mask for write address")
|
||||
disable_addr_dists = Param.Bool(True, "Disable address distributions")
|
||||
33
simulators/gem5/src/mem/MemObject.py
Normal file
33
simulators/gem5/src/mem/MemObject.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2006-2007 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
|
||||
|
||||
from m5.SimObject import SimObject
|
||||
|
||||
class MemObject(SimObject):
|
||||
type = 'MemObject'
|
||||
abstract = True
|
||||
91
simulators/gem5/src/mem/SConscript
Normal file
91
simulators/gem5/src/mem/SConscript
Normal file
@ -0,0 +1,91 @@
|
||||
# -*- 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: Nathan Binkert
|
||||
|
||||
Import('*')
|
||||
|
||||
SimObject('Bridge.py')
|
||||
SimObject('Bus.py')
|
||||
SimObject('CommMonitor.py')
|
||||
SimObject('MemObject.py')
|
||||
|
||||
Source('bridge.cc')
|
||||
Source('bus.cc')
|
||||
Source('coherent_bus.cc')
|
||||
Source('comm_monitor.cc')
|
||||
Source('mem_object.cc')
|
||||
Source('mport.cc')
|
||||
Source('noncoherent_bus.cc')
|
||||
Source('packet.cc')
|
||||
Source('port.cc')
|
||||
Source('packet_queue.cc')
|
||||
Source('tport.cc')
|
||||
Source('port_proxy.cc')
|
||||
Source('fs_translating_port_proxy.cc')
|
||||
Source('se_translating_port_proxy.cc')
|
||||
|
||||
if env['TARGET_ISA'] != 'no':
|
||||
SimObject('AbstractMemory.py')
|
||||
SimObject('SimpleMemory.py')
|
||||
Source('abstract_mem.cc')
|
||||
Source('simple_mem.cc')
|
||||
Source('page_table.cc')
|
||||
Source('physical.cc')
|
||||
|
||||
DebugFlag('BaseBus')
|
||||
DebugFlag('BusAddrRanges')
|
||||
DebugFlag('CoherentBus')
|
||||
DebugFlag('NoncoherentBus')
|
||||
CompoundFlag('Bus', ['BaseBus', 'BusAddrRanges', 'CoherentBus',
|
||||
'NoncoherentBus'])
|
||||
|
||||
DebugFlag('BusBridge')
|
||||
DebugFlag('CommMonitor')
|
||||
DebugFlag('LLSC')
|
||||
DebugFlag('MMU')
|
||||
DebugFlag('MemoryAccess')
|
||||
DebugFlag('PacketQueue')
|
||||
|
||||
DebugFlag('ProtocolTrace')
|
||||
DebugFlag('RubyCache')
|
||||
DebugFlag('RubyCacheTrace')
|
||||
DebugFlag('RubyDma')
|
||||
DebugFlag('RubyGenerated')
|
||||
DebugFlag('RubyMemory')
|
||||
DebugFlag('RubyNetwork')
|
||||
DebugFlag('RubyPort')
|
||||
DebugFlag('RubyQueue')
|
||||
DebugFlag('RubySequencer')
|
||||
DebugFlag('RubySlicc')
|
||||
DebugFlag('RubySystem')
|
||||
DebugFlag('RubyTester')
|
||||
|
||||
CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester',
|
||||
'RubyGenerated', 'RubySlicc', 'RubySystem', 'RubyCache',
|
||||
'RubyMemory', 'RubyDma', 'RubyPort', 'RubySequencer', 'RubyCacheTrace'])
|
||||
49
simulators/gem5/src/mem/SimpleMemory.py
Normal file
49
simulators/gem5/src/mem/SimpleMemory.py
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright (c) 2012 ARM Limited
|
||||
# All rights reserved.
|
||||
#
|
||||
# The license below extends only to copyright in the software and shall
|
||||
# not be construed as granting a license to any other intellectual
|
||||
# property including but not limited to intellectual property relating
|
||||
# to a hardware implementation of the functionality of the software
|
||||
# licensed hereunder. You may use the software subject to the license
|
||||
# terms below provided that you ensure that this notice is replicated
|
||||
# unmodified and in its entirety in all distributions of the software,
|
||||
# modified or unmodified, in source code or in binary form.
|
||||
#
|
||||
# Copyright (c) 2005-2008 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
|
||||
# Andreas Hansson
|
||||
|
||||
from m5.params import *
|
||||
from AbstractMemory import *
|
||||
|
||||
class SimpleMemory(AbstractMemory):
|
||||
type = 'SimpleMemory'
|
||||
port = VectorSlavePort("Slave ports")
|
||||
latency = Param.Latency('30ns', "Request to response latency")
|
||||
latency_var = Param.Latency('0ns', "Request to response latency variance")
|
||||
589
simulators/gem5/src/mem/abstract_mem.cc
Normal file
589
simulators/gem5/src/mem/abstract_mem.cc
Normal file
@ -0,0 +1,589 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* Copyright (c) 2001-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
|
||||
* Ali Saidi
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "arch/registers.hh"
|
||||
#include "config/the_isa.hh"
|
||||
#include "debug/LLSC.hh"
|
||||
#include "debug/MemoryAccess.hh"
|
||||
#include "mem/abstract_mem.hh"
|
||||
#include "mem/packet_access.hh"
|
||||
#include "sim/system.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
AbstractMemory::AbstractMemory(const Params *p) :
|
||||
MemObject(p), range(params()->range), pmemAddr(NULL),
|
||||
confTableReported(p->conf_table_reported), inAddrMap(p->in_addr_map),
|
||||
_system(NULL)
|
||||
{
|
||||
if (size() % TheISA::PageBytes != 0)
|
||||
panic("Memory Size not divisible by page size\n");
|
||||
|
||||
if (params()->null)
|
||||
return;
|
||||
|
||||
if (params()->file == "") {
|
||||
int map_flags = MAP_ANON | MAP_PRIVATE;
|
||||
pmemAddr = (uint8_t *)mmap(NULL, size(),
|
||||
PROT_READ | PROT_WRITE, map_flags, -1, 0);
|
||||
} else {
|
||||
int map_flags = MAP_PRIVATE;
|
||||
int fd = open(params()->file.c_str(), O_RDONLY);
|
||||
long _size = lseek(fd, 0, SEEK_END);
|
||||
if (_size != range.size()) {
|
||||
warn("Specified size %d does not match file %s %d\n", range.size(),
|
||||
params()->file, _size);
|
||||
range = RangeSize(range.start, _size);
|
||||
}
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
pmemAddr = (uint8_t *)mmap(NULL, roundUp(_size, sysconf(_SC_PAGESIZE)),
|
||||
PROT_READ | PROT_WRITE, map_flags, fd, 0);
|
||||
}
|
||||
|
||||
if (pmemAddr == (void *)MAP_FAILED) {
|
||||
perror("mmap");
|
||||
if (params()->file == "")
|
||||
fatal("Could not mmap!\n");
|
||||
else
|
||||
fatal("Could not find file: %s\n", params()->file);
|
||||
}
|
||||
|
||||
//If requested, initialize all the memory to 0
|
||||
if (p->zero)
|
||||
memset(pmemAddr, 0, size());
|
||||
}
|
||||
|
||||
|
||||
AbstractMemory::~AbstractMemory()
|
||||
{
|
||||
if (pmemAddr)
|
||||
munmap((char*)pmemAddr, size());
|
||||
}
|
||||
|
||||
void
|
||||
AbstractMemory::regStats()
|
||||
{
|
||||
using namespace Stats;
|
||||
|
||||
assert(system());
|
||||
|
||||
bytesRead
|
||||
.init(system()->maxMasters())
|
||||
.name(name() + ".bytes_read")
|
||||
.desc("Number of bytes read from this memory")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
bytesRead.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
bytesInstRead
|
||||
.init(system()->maxMasters())
|
||||
.name(name() + ".bytes_inst_read")
|
||||
.desc("Number of instructions bytes read from this memory")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
bytesInstRead.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
bytesWritten
|
||||
.init(system()->maxMasters())
|
||||
.name(name() + ".bytes_written")
|
||||
.desc("Number of bytes written to this memory")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
bytesWritten.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
numReads
|
||||
.init(system()->maxMasters())
|
||||
.name(name() + ".num_reads")
|
||||
.desc("Number of read requests responded to by this memory")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
numReads.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
numWrites
|
||||
.init(system()->maxMasters())
|
||||
.name(name() + ".num_writes")
|
||||
.desc("Number of write requests responded to by this memory")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
numWrites.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
numOther
|
||||
.init(system()->maxMasters())
|
||||
.name(name() + ".num_other")
|
||||
.desc("Number of other requests responded to by this memory")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
numOther.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
bwRead
|
||||
.name(name() + ".bw_read")
|
||||
.desc("Total read bandwidth from this memory (bytes/s)")
|
||||
.precision(0)
|
||||
.prereq(bytesRead)
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
bwRead.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
|
||||
bwInstRead
|
||||
.name(name() + ".bw_inst_read")
|
||||
.desc("Instruction read bandwidth from this memory (bytes/s)")
|
||||
.precision(0)
|
||||
.prereq(bytesInstRead)
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
bwInstRead.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
bwWrite
|
||||
.name(name() + ".bw_write")
|
||||
.desc("Write bandwidth from this memory (bytes/s)")
|
||||
.precision(0)
|
||||
.prereq(bytesWritten)
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
bwWrite.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
bwTotal
|
||||
.name(name() + ".bw_total")
|
||||
.desc("Total bandwidth to/from this memory (bytes/s)")
|
||||
.precision(0)
|
||||
.prereq(bwTotal)
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system()->maxMasters(); i++) {
|
||||
bwTotal.subname(i, system()->getMasterName(i));
|
||||
}
|
||||
bwRead = bytesRead / simSeconds;
|
||||
bwInstRead = bytesInstRead / simSeconds;
|
||||
bwWrite = bytesWritten / simSeconds;
|
||||
bwTotal = (bytesRead + bytesWritten) / simSeconds;
|
||||
}
|
||||
|
||||
Range<Addr>
|
||||
AbstractMemory::getAddrRange()
|
||||
{
|
||||
return range;
|
||||
}
|
||||
|
||||
// Add load-locked to tracking list. Should only be called if the
|
||||
// operation is a load and the LLSC flag is set.
|
||||
void
|
||||
AbstractMemory::trackLoadLocked(PacketPtr pkt)
|
||||
{
|
||||
Request *req = pkt->req;
|
||||
Addr paddr = LockedAddr::mask(req->getPaddr());
|
||||
|
||||
// first we check if we already have a locked addr for this
|
||||
// xc. Since each xc only gets one, we just update the
|
||||
// existing record with the new address.
|
||||
list<LockedAddr>::iterator i;
|
||||
|
||||
for (i = lockedAddrList.begin(); i != lockedAddrList.end(); ++i) {
|
||||
if (i->matchesContext(req)) {
|
||||
DPRINTF(LLSC, "Modifying lock record: context %d addr %#x\n",
|
||||
req->contextId(), paddr);
|
||||
i->addr = paddr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no record for this xc: need to allocate a new one
|
||||
DPRINTF(LLSC, "Adding lock record: context %d addr %#x\n",
|
||||
req->contextId(), paddr);
|
||||
lockedAddrList.push_front(LockedAddr(req));
|
||||
}
|
||||
|
||||
|
||||
// Called on *writes* only... both regular stores and
|
||||
// store-conditional operations. Check for conventional stores which
|
||||
// conflict with locked addresses, and for success/failure of store
|
||||
// conditionals.
|
||||
bool
|
||||
AbstractMemory::checkLockedAddrList(PacketPtr pkt)
|
||||
{
|
||||
Request *req = pkt->req;
|
||||
Addr paddr = LockedAddr::mask(req->getPaddr());
|
||||
bool isLLSC = pkt->isLLSC();
|
||||
|
||||
// Initialize return value. Non-conditional stores always
|
||||
// succeed. Assume conditional stores will fail until proven
|
||||
// otherwise.
|
||||
bool success = !isLLSC;
|
||||
|
||||
// Iterate over list. Note that there could be multiple matching
|
||||
// records, as more than one context could have done a load locked
|
||||
// to this location.
|
||||
list<LockedAddr>::iterator i = lockedAddrList.begin();
|
||||
|
||||
while (i != lockedAddrList.end()) {
|
||||
|
||||
if (i->addr == paddr) {
|
||||
// we have a matching address
|
||||
|
||||
if (isLLSC && i->matchesContext(req)) {
|
||||
// it's a store conditional, and as far as the memory
|
||||
// system can tell, the requesting context's lock is
|
||||
// still valid.
|
||||
DPRINTF(LLSC, "StCond success: context %d addr %#x\n",
|
||||
req->contextId(), paddr);
|
||||
success = true;
|
||||
}
|
||||
|
||||
// Get rid of our record of this lock and advance to next
|
||||
DPRINTF(LLSC, "Erasing lock record: context %d addr %#x\n",
|
||||
i->contextId, paddr);
|
||||
i = lockedAddrList.erase(i);
|
||||
}
|
||||
else {
|
||||
// no match: advance to next record
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (isLLSC) {
|
||||
req->setExtraData(success ? 1 : 0);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
#if TRACING_ON
|
||||
|
||||
#define CASE(A, T) \
|
||||
case sizeof(T): \
|
||||
DPRINTF(MemoryAccess,"%s of size %i on address 0x%x data 0x%x\n", \
|
||||
A, pkt->getSize(), pkt->getAddr(), pkt->get<T>()); \
|
||||
break
|
||||
|
||||
|
||||
#define TRACE_PACKET(A) \
|
||||
do { \
|
||||
switch (pkt->getSize()) { \
|
||||
CASE(A, uint64_t); \
|
||||
CASE(A, uint32_t); \
|
||||
CASE(A, uint16_t); \
|
||||
CASE(A, uint8_t); \
|
||||
default: \
|
||||
DPRINTF(MemoryAccess, "%s of size %i on address 0x%x\n", \
|
||||
A, pkt->getSize(), pkt->getAddr()); \
|
||||
DDUMP(MemoryAccess, pkt->getPtr<uint8_t>(), pkt->getSize());\
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define TRACE_PACKET(A)
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
AbstractMemory::access(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->getAddr() >= range.start &&
|
||||
(pkt->getAddr() + pkt->getSize() - 1) <= range.end);
|
||||
|
||||
if (pkt->memInhibitAsserted()) {
|
||||
DPRINTF(MemoryAccess, "mem inhibited on 0x%x: not responding\n",
|
||||
pkt->getAddr());
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *hostAddr = pmemAddr + pkt->getAddr() - range.start;
|
||||
|
||||
if (pkt->cmd == MemCmd::SwapReq) {
|
||||
TheISA::IntReg overwrite_val;
|
||||
bool overwrite_mem;
|
||||
uint64_t condition_val64;
|
||||
uint32_t condition_val32;
|
||||
|
||||
if (!pmemAddr)
|
||||
panic("Swap only works if there is real memory (i.e. null=False)");
|
||||
assert(sizeof(TheISA::IntReg) >= pkt->getSize());
|
||||
|
||||
overwrite_mem = true;
|
||||
// keep a copy of our possible write value, and copy what is at the
|
||||
// memory address into the packet
|
||||
std::memcpy(&overwrite_val, pkt->getPtr<uint8_t>(), pkt->getSize());
|
||||
std::memcpy(pkt->getPtr<uint8_t>(), hostAddr, pkt->getSize());
|
||||
|
||||
if (pkt->req->isCondSwap()) {
|
||||
if (pkt->getSize() == sizeof(uint64_t)) {
|
||||
condition_val64 = pkt->req->getExtraData();
|
||||
overwrite_mem = !std::memcmp(&condition_val64, hostAddr,
|
||||
sizeof(uint64_t));
|
||||
} else if (pkt->getSize() == sizeof(uint32_t)) {
|
||||
condition_val32 = (uint32_t)pkt->req->getExtraData();
|
||||
overwrite_mem = !std::memcmp(&condition_val32, hostAddr,
|
||||
sizeof(uint32_t));
|
||||
} else
|
||||
panic("Invalid size for conditional read/write\n");
|
||||
}
|
||||
|
||||
if (overwrite_mem)
|
||||
std::memcpy(hostAddr, &overwrite_val, pkt->getSize());
|
||||
|
||||
assert(!pkt->req->isInstFetch());
|
||||
TRACE_PACKET("Read/Write");
|
||||
numOther[pkt->req->masterId()]++;
|
||||
} else if (pkt->isRead()) {
|
||||
assert(!pkt->isWrite());
|
||||
if (pkt->isLLSC()) {
|
||||
trackLoadLocked(pkt);
|
||||
}
|
||||
if (pmemAddr)
|
||||
memcpy(pkt->getPtr<uint8_t>(), hostAddr, pkt->getSize());
|
||||
TRACE_PACKET(pkt->req->isInstFetch() ? "IFetch" : "Read");
|
||||
numReads[pkt->req->masterId()]++;
|
||||
bytesRead[pkt->req->masterId()] += pkt->getSize();
|
||||
if (pkt->req->isInstFetch())
|
||||
bytesInstRead[pkt->req->masterId()] += pkt->getSize();
|
||||
} else if (pkt->isWrite()) {
|
||||
if (writeOK(pkt)) {
|
||||
if (pmemAddr)
|
||||
memcpy(hostAddr, pkt->getPtr<uint8_t>(), pkt->getSize());
|
||||
assert(!pkt->req->isInstFetch());
|
||||
TRACE_PACKET("Write");
|
||||
numWrites[pkt->req->masterId()]++;
|
||||
bytesWritten[pkt->req->masterId()] += pkt->getSize();
|
||||
}
|
||||
} else if (pkt->isInvalidate()) {
|
||||
// no need to do anything
|
||||
} else {
|
||||
panic("unimplemented");
|
||||
}
|
||||
|
||||
if (pkt->needsResponse()) {
|
||||
pkt->makeResponse();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AbstractMemory::functionalAccess(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->getAddr() >= range.start &&
|
||||
(pkt->getAddr() + pkt->getSize() - 1) <= range.end);
|
||||
|
||||
uint8_t *hostAddr = pmemAddr + pkt->getAddr() - range.start;
|
||||
|
||||
if (pkt->isRead()) {
|
||||
if (pmemAddr)
|
||||
memcpy(pkt->getPtr<uint8_t>(), hostAddr, pkt->getSize());
|
||||
TRACE_PACKET("Read");
|
||||
pkt->makeResponse();
|
||||
} else if (pkt->isWrite()) {
|
||||
if (pmemAddr)
|
||||
memcpy(hostAddr, pkt->getPtr<uint8_t>(), pkt->getSize());
|
||||
TRACE_PACKET("Write");
|
||||
pkt->makeResponse();
|
||||
} else if (pkt->isPrint()) {
|
||||
Packet::PrintReqState *prs =
|
||||
dynamic_cast<Packet::PrintReqState*>(pkt->senderState);
|
||||
assert(prs);
|
||||
// Need to call printLabels() explicitly since we're not going
|
||||
// through printObj().
|
||||
prs->printLabels();
|
||||
// Right now we just print the single byte at the specified address.
|
||||
ccprintf(prs->os, "%s%#x\n", prs->curPrefix(), *hostAddr);
|
||||
} else {
|
||||
panic("AbstractMemory: unimplemented functional command %s",
|
||||
pkt->cmdString());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AbstractMemory::serialize(ostream &os)
|
||||
{
|
||||
if (!pmemAddr)
|
||||
return;
|
||||
|
||||
gzFile compressedMem;
|
||||
string filename = name() + ".physmem";
|
||||
long _size = range.size();
|
||||
|
||||
SERIALIZE_SCALAR(filename);
|
||||
SERIALIZE_SCALAR(_size);
|
||||
|
||||
// write memory file
|
||||
string thefile = Checkpoint::dir() + "/" + filename.c_str();
|
||||
int fd = creat(thefile.c_str(), 0664);
|
||||
if (fd < 0) {
|
||||
perror("creat");
|
||||
fatal("Can't open physical memory checkpoint file '%s'\n", filename);
|
||||
}
|
||||
|
||||
compressedMem = gzdopen(fd, "wb");
|
||||
if (compressedMem == NULL)
|
||||
fatal("Insufficient memory to allocate compression state for %s\n",
|
||||
filename);
|
||||
|
||||
if (gzwrite(compressedMem, pmemAddr, size()) != (int)size()) {
|
||||
fatal("Write failed on physical memory checkpoint file '%s'\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
if (gzclose(compressedMem))
|
||||
fatal("Close failed on physical memory checkpoint file '%s'\n",
|
||||
filename);
|
||||
|
||||
list<LockedAddr>::iterator i = lockedAddrList.begin();
|
||||
|
||||
vector<Addr> lal_addr;
|
||||
vector<int> lal_cid;
|
||||
while (i != lockedAddrList.end()) {
|
||||
lal_addr.push_back(i->addr);
|
||||
lal_cid.push_back(i->contextId);
|
||||
i++;
|
||||
}
|
||||
arrayParamOut(os, "lal_addr", lal_addr);
|
||||
arrayParamOut(os, "lal_cid", lal_cid);
|
||||
}
|
||||
|
||||
void
|
||||
AbstractMemory::unserialize(Checkpoint *cp, const string §ion)
|
||||
{
|
||||
if (!pmemAddr)
|
||||
return;
|
||||
|
||||
gzFile compressedMem;
|
||||
long *tempPage;
|
||||
long *pmem_current;
|
||||
uint64_t curSize;
|
||||
uint32_t bytesRead;
|
||||
const uint32_t chunkSize = 16384;
|
||||
|
||||
string filename;
|
||||
|
||||
UNSERIALIZE_SCALAR(filename);
|
||||
|
||||
filename = cp->cptDir + "/" + filename;
|
||||
|
||||
// mmap memoryfile
|
||||
int fd = open(filename.c_str(), O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
fatal("Can't open physical memory checkpoint file '%s'", filename);
|
||||
}
|
||||
|
||||
compressedMem = gzdopen(fd, "rb");
|
||||
if (compressedMem == NULL)
|
||||
fatal("Insufficient memory to allocate compression state for %s\n",
|
||||
filename);
|
||||
|
||||
// unmap file that was mmapped in the constructor
|
||||
// This is done here to make sure that gzip and open don't muck with our
|
||||
// nice large space of memory before we reallocate it
|
||||
munmap((char*)pmemAddr, size());
|
||||
|
||||
long _size;
|
||||
UNSERIALIZE_SCALAR(_size);
|
||||
if (_size > params()->range.size())
|
||||
fatal("Memory size has changed! size %lld, param size %lld\n",
|
||||
_size, params()->range.size());
|
||||
|
||||
pmemAddr = (uint8_t *)mmap(NULL, size(),
|
||||
PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (pmemAddr == (void *)MAP_FAILED) {
|
||||
perror("mmap");
|
||||
fatal("Could not mmap physical memory!\n");
|
||||
}
|
||||
|
||||
curSize = 0;
|
||||
tempPage = (long*)malloc(chunkSize);
|
||||
if (tempPage == NULL)
|
||||
fatal("Unable to malloc memory to read file %s\n", filename);
|
||||
|
||||
/* Only copy bytes that are non-zero, so we don't give the VM system hell */
|
||||
while (curSize < size()) {
|
||||
bytesRead = gzread(compressedMem, tempPage, chunkSize);
|
||||
if (bytesRead == 0)
|
||||
break;
|
||||
|
||||
assert(bytesRead % sizeof(long) == 0);
|
||||
|
||||
for (uint32_t x = 0; x < bytesRead / sizeof(long); x++)
|
||||
{
|
||||
if (*(tempPage+x) != 0) {
|
||||
pmem_current = (long*)(pmemAddr + curSize + x * sizeof(long));
|
||||
*pmem_current = *(tempPage+x);
|
||||
}
|
||||
}
|
||||
curSize += bytesRead;
|
||||
}
|
||||
|
||||
free(tempPage);
|
||||
|
||||
if (gzclose(compressedMem))
|
||||
fatal("Close failed on physical memory checkpoint file '%s'\n",
|
||||
filename);
|
||||
|
||||
vector<Addr> lal_addr;
|
||||
vector<int> lal_cid;
|
||||
arrayParamIn(cp, section, "lal_addr", lal_addr);
|
||||
arrayParamIn(cp, section, "lal_cid", lal_cid);
|
||||
for(int i = 0; i < lal_addr.size(); i++)
|
||||
lockedAddrList.push_front(LockedAddr(lal_addr[i], lal_cid[i]));
|
||||
}
|
||||
273
simulators/gem5/src/mem/abstract_mem.hh
Normal file
273
simulators/gem5/src/mem/abstract_mem.hh
Normal file
@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* Copyright (c) 2001-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
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* AbstractMemory declaration
|
||||
*/
|
||||
|
||||
#ifndef __ABSTRACT_MEMORY_HH__
|
||||
#define __ABSTRACT_MEMORY_HH__
|
||||
|
||||
#include "mem/mem_object.hh"
|
||||
#include "params/AbstractMemory.hh"
|
||||
#include "sim/stats.hh"
|
||||
|
||||
|
||||
class System;
|
||||
|
||||
/**
|
||||
* An abstract memory represents a contiguous block of physical
|
||||
* memory, with an associated address range, and also provides basic
|
||||
* functionality for reading and writing this memory without any
|
||||
* timing information. It is a MemObject since any subclass must have
|
||||
* at least one slave port.
|
||||
*/
|
||||
class AbstractMemory : public MemObject
|
||||
{
|
||||
protected:
|
||||
|
||||
// Address range of this memory
|
||||
Range<Addr> range;
|
||||
|
||||
// Pointer to host memory used to implement this memory
|
||||
uint8_t* pmemAddr;
|
||||
|
||||
// Enable specific memories to be reported to the configuration table
|
||||
bool confTableReported;
|
||||
|
||||
// Should the memory appear in the global address map
|
||||
bool inAddrMap;
|
||||
|
||||
class LockedAddr {
|
||||
|
||||
public:
|
||||
// on alpha, minimum LL/SC granularity is 16 bytes, so lower
|
||||
// bits need to masked off.
|
||||
static const Addr Addr_Mask = 0xf;
|
||||
|
||||
static Addr mask(Addr paddr) { return (paddr & ~Addr_Mask); }
|
||||
|
||||
Addr addr; // locked address
|
||||
int contextId; // locking hw context
|
||||
|
||||
// check for matching execution context
|
||||
bool matchesContext(Request *req)
|
||||
{
|
||||
return (contextId == req->contextId());
|
||||
}
|
||||
|
||||
LockedAddr(Request *req) : addr(mask(req->getPaddr())),
|
||||
contextId(req->contextId())
|
||||
{
|
||||
}
|
||||
// constructor for unserialization use
|
||||
LockedAddr(Addr _addr, int _cid) : addr(_addr), contextId(_cid)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::list<LockedAddr> lockedAddrList;
|
||||
|
||||
// helper function for checkLockedAddrs(): we really want to
|
||||
// inline a quick check for an empty locked addr list (hopefully
|
||||
// the common case), and do the full list search (if necessary) in
|
||||
// this out-of-line function
|
||||
bool checkLockedAddrList(PacketPtr pkt);
|
||||
|
||||
// Record the address of a load-locked operation so that we can
|
||||
// clear the execution context's lock flag if a matching store is
|
||||
// performed
|
||||
void trackLoadLocked(PacketPtr pkt);
|
||||
|
||||
// Compare a store address with any locked addresses so we can
|
||||
// clear the lock flag appropriately. Return value set to 'false'
|
||||
// if store operation should be suppressed (because it was a
|
||||
// conditional store and the address was no longer locked by the
|
||||
// requesting execution context), 'true' otherwise. Note that
|
||||
// this method must be called on *all* stores since even
|
||||
// non-conditional stores must clear any matching lock addresses.
|
||||
bool writeOK(PacketPtr pkt) {
|
||||
Request *req = pkt->req;
|
||||
if (lockedAddrList.empty()) {
|
||||
// no locked addrs: nothing to check, store_conditional fails
|
||||
bool isLLSC = pkt->isLLSC();
|
||||
if (isLLSC) {
|
||||
req->setExtraData(0);
|
||||
}
|
||||
return !isLLSC; // only do write if not an sc
|
||||
} else {
|
||||
// iterate over list...
|
||||
return checkLockedAddrList(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
/** Number of total bytes read from this memory */
|
||||
Stats::Vector bytesRead;
|
||||
/** Number of instruction bytes read from this memory */
|
||||
Stats::Vector bytesInstRead;
|
||||
/** Number of bytes written to this memory */
|
||||
Stats::Vector bytesWritten;
|
||||
/** Number of read requests */
|
||||
Stats::Vector numReads;
|
||||
/** Number of write requests */
|
||||
Stats::Vector numWrites;
|
||||
/** Number of other requests */
|
||||
Stats::Vector numOther;
|
||||
/** Read bandwidth from this memory */
|
||||
Stats::Formula bwRead;
|
||||
/** Read bandwidth from this memory */
|
||||
Stats::Formula bwInstRead;
|
||||
/** Write bandwidth from this memory */
|
||||
Stats::Formula bwWrite;
|
||||
/** Total bandwidth from this memory */
|
||||
Stats::Formula bwTotal;
|
||||
|
||||
/** Pointor to the System object.
|
||||
* This is used for getting the number of masters in the system which is
|
||||
* needed when registering stats
|
||||
*/
|
||||
System *_system;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Prevent copying
|
||||
AbstractMemory(const AbstractMemory&);
|
||||
|
||||
// Prevent assignment
|
||||
AbstractMemory& operator=(const AbstractMemory&);
|
||||
|
||||
public:
|
||||
|
||||
typedef AbstractMemoryParams Params;
|
||||
|
||||
AbstractMemory(const Params* p);
|
||||
virtual ~AbstractMemory();
|
||||
|
||||
/** read the system pointer
|
||||
* Implemented for completeness with the setter
|
||||
* @return pointer to the system object */
|
||||
System* system() const { return _system; }
|
||||
|
||||
/** Set the system pointer on this memory
|
||||
* This can't be done via a python parameter because the system needs
|
||||
* pointers to all the memories and the reverse would create a cycle in the
|
||||
* object graph. An init() this is set.
|
||||
* @param sys system pointer to set
|
||||
*/
|
||||
void system(System *sys) { _system = sys; }
|
||||
|
||||
const Params *
|
||||
params() const
|
||||
{
|
||||
return dynamic_cast<const Params *>(_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address range
|
||||
*
|
||||
* @return a single contigous address range
|
||||
*/
|
||||
Range<Addr> getAddrRange();
|
||||
|
||||
/**
|
||||
* Get the memory size.
|
||||
*
|
||||
* @return the size of the memory
|
||||
*/
|
||||
uint64_t size() { return range.size(); }
|
||||
|
||||
/**
|
||||
* Get the start address.
|
||||
*
|
||||
* @return the start address of the memory
|
||||
*/
|
||||
Addr start() { return range.start; }
|
||||
|
||||
/**
|
||||
* Should this memory be passed to the kernel and part of the OS
|
||||
* physical memory layout.
|
||||
*
|
||||
* @return if this memory is reported
|
||||
*/
|
||||
bool isConfReported() const { return confTableReported; }
|
||||
|
||||
/**
|
||||
* Some memories are used as shadow memories or should for other
|
||||
* reasons not be part of the global address map.
|
||||
*
|
||||
* @return if this memory is part of the address map
|
||||
*/
|
||||
bool isInAddrMap() const { return inAddrMap; }
|
||||
|
||||
/**
|
||||
* Perform an untimed memory access and update all the state
|
||||
* (e.g. locked addresses) and statistics accordingly. The packet
|
||||
* is turned into a response if required.
|
||||
*
|
||||
* @param pkt Packet performing the access
|
||||
*/
|
||||
void access(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Perform an untimed memory read or write without changing
|
||||
* anything but the memory itself. No stats are affected by this
|
||||
* access. In addition to normal accesses this also facilitates
|
||||
* print requests.
|
||||
*
|
||||
* @param pkt Packet performing the access
|
||||
*/
|
||||
void functionalAccess(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Register Statistics
|
||||
*/
|
||||
virtual void regStats();
|
||||
|
||||
virtual void serialize(std::ostream &os);
|
||||
virtual void unserialize(Checkpoint *cp, const std::string §ion);
|
||||
|
||||
};
|
||||
|
||||
#endif //__ABSTRACT_MEMORY_HH__
|
||||
455
simulators/gem5/src/mem/bridge.cc
Normal file
455
simulators/gem5/src/mem/bridge.cc
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Steve Reinhardt
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Implementation of a memory-mapped bus bridge that connects a master
|
||||
* and a slave through a request and response queue.
|
||||
*/
|
||||
|
||||
#include "base/trace.hh"
|
||||
#include "debug/BusBridge.hh"
|
||||
#include "mem/bridge.hh"
|
||||
#include "params/Bridge.hh"
|
||||
|
||||
Bridge::BridgeSlavePort::BridgeSlavePort(const std::string &_name,
|
||||
Bridge* _bridge,
|
||||
BridgeMasterPort& _masterPort,
|
||||
int _delay, int _nack_delay,
|
||||
int _resp_limit,
|
||||
std::vector<Range<Addr> > _ranges)
|
||||
: SlavePort(_name, _bridge), bridge(_bridge), masterPort(_masterPort),
|
||||
delay(_delay), nackDelay(_nack_delay),
|
||||
ranges(_ranges.begin(), _ranges.end()),
|
||||
outstandingResponses(0), inRetry(false),
|
||||
respQueueLimit(_resp_limit), sendEvent(*this)
|
||||
{
|
||||
}
|
||||
|
||||
Bridge::BridgeMasterPort::BridgeMasterPort(const std::string &_name,
|
||||
Bridge* _bridge,
|
||||
BridgeSlavePort& _slavePort,
|
||||
int _delay, int _req_limit)
|
||||
: MasterPort(_name, _bridge), bridge(_bridge), slavePort(_slavePort),
|
||||
delay(_delay), inRetry(false), reqQueueLimit(_req_limit),
|
||||
sendEvent(*this)
|
||||
{
|
||||
}
|
||||
|
||||
Bridge::Bridge(Params *p)
|
||||
: MemObject(p),
|
||||
slavePort(p->name + "-slave", this, masterPort, p->delay,
|
||||
p->nack_delay, p->resp_size, p->ranges),
|
||||
masterPort(p->name + "-master", this, slavePort, p->delay, p->req_size),
|
||||
ackWrites(p->write_ack), _params(p)
|
||||
{
|
||||
if (ackWrites)
|
||||
panic("No support for acknowledging writes\n");
|
||||
}
|
||||
|
||||
MasterPort&
|
||||
Bridge::getMasterPort(const std::string &if_name, int idx)
|
||||
{
|
||||
if (if_name == "master")
|
||||
return masterPort;
|
||||
else
|
||||
// pass it along to our super class
|
||||
return MemObject::getMasterPort(if_name, idx);
|
||||
}
|
||||
|
||||
SlavePort&
|
||||
Bridge::getSlavePort(const std::string &if_name, int idx)
|
||||
{
|
||||
if (if_name == "slave")
|
||||
return slavePort;
|
||||
else
|
||||
// pass it along to our super class
|
||||
return MemObject::getSlavePort(if_name, idx);
|
||||
}
|
||||
|
||||
void
|
||||
Bridge::init()
|
||||
{
|
||||
// make sure both sides are connected and have the same block size
|
||||
if (!slavePort.isConnected() || !masterPort.isConnected())
|
||||
fatal("Both ports of bus bridge are not connected to a bus.\n");
|
||||
|
||||
if (slavePort.peerBlockSize() != masterPort.peerBlockSize())
|
||||
fatal("Slave port size %d, master port size %d \n " \
|
||||
"Busses don't have the same block size... Not supported.\n",
|
||||
slavePort.peerBlockSize(), masterPort.peerBlockSize());
|
||||
|
||||
// notify the master side of our address ranges
|
||||
slavePort.sendRangeChange();
|
||||
}
|
||||
|
||||
bool
|
||||
Bridge::BridgeSlavePort::respQueueFull()
|
||||
{
|
||||
return outstandingResponses == respQueueLimit;
|
||||
}
|
||||
|
||||
bool
|
||||
Bridge::BridgeMasterPort::reqQueueFull()
|
||||
{
|
||||
return requestQueue.size() == reqQueueLimit;
|
||||
}
|
||||
|
||||
bool
|
||||
Bridge::BridgeMasterPort::recvTimingResp(PacketPtr pkt)
|
||||
{
|
||||
// all checks are done when the request is accepted on the slave
|
||||
// side, so we are guaranteed to have space for the response
|
||||
DPRINTF(BusBridge, "recvTiming: response %s addr 0x%x\n",
|
||||
pkt->cmdString(), pkt->getAddr());
|
||||
|
||||
DPRINTF(BusBridge, "Request queue size: %d\n", requestQueue.size());
|
||||
|
||||
slavePort.queueForSendTiming(pkt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Bridge::BridgeSlavePort::recvTimingReq(PacketPtr pkt)
|
||||
{
|
||||
DPRINTF(BusBridge, "recvTiming: request %s addr 0x%x\n",
|
||||
pkt->cmdString(), pkt->getAddr());
|
||||
|
||||
DPRINTF(BusBridge, "Response queue size: %d outresp: %d\n",
|
||||
responseQueue.size(), outstandingResponses);
|
||||
|
||||
if (masterPort.reqQueueFull()) {
|
||||
DPRINTF(BusBridge, "Request queue full, nacking\n");
|
||||
nackRequest(pkt);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pkt->needsResponse()) {
|
||||
if (respQueueFull()) {
|
||||
DPRINTF(BusBridge,
|
||||
"Response queue full, no space for response, nacking\n");
|
||||
DPRINTF(BusBridge,
|
||||
"queue size: %d outstanding resp: %d\n",
|
||||
responseQueue.size(), outstandingResponses);
|
||||
nackRequest(pkt);
|
||||
return true;
|
||||
} else {
|
||||
DPRINTF(BusBridge, "Request Needs response, reserving space\n");
|
||||
assert(outstandingResponses != respQueueLimit);
|
||||
++outstandingResponses;
|
||||
}
|
||||
}
|
||||
|
||||
masterPort.queueForSendTiming(pkt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Bridge::BridgeSlavePort::nackRequest(PacketPtr pkt)
|
||||
{
|
||||
// Nack the packet
|
||||
pkt->makeTimingResponse();
|
||||
pkt->setNacked();
|
||||
|
||||
// The Nack packets are stored in the response queue just like any
|
||||
// other response, but they do not occupy any space as this is
|
||||
// tracked by the outstandingResponses, this guarantees space for
|
||||
// the Nack packets, but implicitly means we have an (unrealistic)
|
||||
// unbounded Nack queue.
|
||||
|
||||
// put it on the list to send
|
||||
Tick readyTime = curTick() + nackDelay;
|
||||
DeferredResponse resp(pkt, readyTime, true);
|
||||
|
||||
// nothing on the list, add it and we're done
|
||||
if (responseQueue.empty()) {
|
||||
assert(!sendEvent.scheduled());
|
||||
bridge->schedule(sendEvent, readyTime);
|
||||
responseQueue.push_back(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(sendEvent.scheduled() || inRetry);
|
||||
|
||||
// does it go at the end?
|
||||
if (readyTime >= responseQueue.back().ready) {
|
||||
responseQueue.push_back(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
// ok, somewhere in the middle, fun
|
||||
std::list<DeferredResponse>::iterator i = responseQueue.begin();
|
||||
std::list<DeferredResponse>::iterator end = responseQueue.end();
|
||||
std::list<DeferredResponse>::iterator begin = responseQueue.begin();
|
||||
bool done = false;
|
||||
|
||||
while (i != end && !done) {
|
||||
if (readyTime < (*i).ready) {
|
||||
if (i == begin)
|
||||
bridge->reschedule(sendEvent, readyTime);
|
||||
responseQueue.insert(i, resp);
|
||||
done = true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
assert(done);
|
||||
}
|
||||
|
||||
void
|
||||
Bridge::BridgeMasterPort::queueForSendTiming(PacketPtr pkt)
|
||||
{
|
||||
Tick readyTime = curTick() + delay;
|
||||
|
||||
// If we expect to see a response, we need to restore the source
|
||||
// and destination field that is potentially changed by a second
|
||||
// bus
|
||||
if (!pkt->memInhibitAsserted() && pkt->needsResponse()) {
|
||||
// Update the sender state so we can deal with the response
|
||||
// appropriately
|
||||
RequestState *req_state = new RequestState(pkt);
|
||||
pkt->senderState = req_state;
|
||||
}
|
||||
|
||||
// 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
|
||||
// should already be an event scheduled for sending the head
|
||||
// packet.
|
||||
if (requestQueue.empty()) {
|
||||
bridge->schedule(sendEvent, readyTime);
|
||||
}
|
||||
|
||||
assert(requestQueue.size() != reqQueueLimit);
|
||||
|
||||
requestQueue.push_back(DeferredRequest(pkt, readyTime));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Bridge::BridgeSlavePort::queueForSendTiming(PacketPtr pkt)
|
||||
{
|
||||
// This is a response for a request we forwarded earlier. The
|
||||
// corresponding request state should be stored in the packet's
|
||||
// senderState field.
|
||||
RequestState *req_state = dynamic_cast<RequestState*>(pkt->senderState);
|
||||
assert(req_state != NULL);
|
||||
// set up new packet dest & senderState based on values saved
|
||||
// from original request
|
||||
req_state->fixResponse(pkt);
|
||||
|
||||
// the bridge assumes that at least one bus has set the
|
||||
// destination field of the packet
|
||||
assert(pkt->isDestValid());
|
||||
DPRINTF(BusBridge, "response, new dest %d\n", pkt->getDest());
|
||||
delete req_state;
|
||||
|
||||
Tick readyTime = curTick() + delay;
|
||||
|
||||
// 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
|
||||
// should already be an event scheduled for sending the head
|
||||
// packet.
|
||||
if (responseQueue.empty()) {
|
||||
bridge->schedule(sendEvent, readyTime);
|
||||
}
|
||||
responseQueue.push_back(DeferredResponse(pkt, readyTime));
|
||||
}
|
||||
|
||||
void
|
||||
Bridge::BridgeMasterPort::trySend()
|
||||
{
|
||||
assert(!requestQueue.empty());
|
||||
|
||||
DeferredRequest req = requestQueue.front();
|
||||
|
||||
assert(req.ready <= curTick());
|
||||
|
||||
PacketPtr pkt = req.pkt;
|
||||
|
||||
DPRINTF(BusBridge, "trySend request: addr 0x%x\n", pkt->getAddr());
|
||||
|
||||
if (sendTimingReq(pkt)) {
|
||||
// send successful
|
||||
requestQueue.pop_front();
|
||||
|
||||
// If there are more packets to send, schedule event to try again.
|
||||
if (!requestQueue.empty()) {
|
||||
req = requestQueue.front();
|
||||
DPRINTF(BusBridge, "Scheduling next send\n");
|
||||
bridge->schedule(sendEvent,
|
||||
std::max(req.ready, curTick() + 1));
|
||||
}
|
||||
} else {
|
||||
inRetry = true;
|
||||
}
|
||||
|
||||
DPRINTF(BusBridge, "trySend: request queue size: %d\n",
|
||||
requestQueue.size());
|
||||
}
|
||||
|
||||
void
|
||||
Bridge::BridgeSlavePort::trySend()
|
||||
{
|
||||
assert(!responseQueue.empty());
|
||||
|
||||
DeferredResponse resp = responseQueue.front();
|
||||
|
||||
assert(resp.ready <= curTick());
|
||||
|
||||
PacketPtr pkt = resp.pkt;
|
||||
|
||||
DPRINTF(BusBridge, "trySend response: dest %d addr 0x%x\n",
|
||||
pkt->getDest(), pkt->getAddr());
|
||||
|
||||
bool was_nacked_here = resp.nackedHere;
|
||||
|
||||
if (sendTimingResp(pkt)) {
|
||||
DPRINTF(BusBridge, " successful\n");
|
||||
// send successful
|
||||
responseQueue.pop_front();
|
||||
|
||||
if (!was_nacked_here) {
|
||||
assert(outstandingResponses != 0);
|
||||
--outstandingResponses;
|
||||
}
|
||||
|
||||
// If there are more packets to send, schedule event to try again.
|
||||
if (!responseQueue.empty()) {
|
||||
resp = responseQueue.front();
|
||||
DPRINTF(BusBridge, "Scheduling next send\n");
|
||||
bridge->schedule(sendEvent,
|
||||
std::max(resp.ready, curTick() + 1));
|
||||
}
|
||||
} else {
|
||||
DPRINTF(BusBridge, " unsuccessful\n");
|
||||
inRetry = true;
|
||||
}
|
||||
|
||||
DPRINTF(BusBridge, "trySend: queue size: %d outstanding resp: %d\n",
|
||||
responseQueue.size(), outstandingResponses);
|
||||
}
|
||||
|
||||
void
|
||||
Bridge::BridgeMasterPort::recvRetry()
|
||||
{
|
||||
inRetry = false;
|
||||
Tick nextReady = requestQueue.front().ready;
|
||||
if (nextReady <= curTick())
|
||||
trySend();
|
||||
else
|
||||
bridge->schedule(sendEvent, nextReady);
|
||||
}
|
||||
|
||||
void
|
||||
Bridge::BridgeSlavePort::recvRetry()
|
||||
{
|
||||
inRetry = false;
|
||||
Tick nextReady = responseQueue.front().ready;
|
||||
if (nextReady <= curTick())
|
||||
trySend();
|
||||
else
|
||||
bridge->schedule(sendEvent, nextReady);
|
||||
}
|
||||
|
||||
Tick
|
||||
Bridge::BridgeSlavePort::recvAtomic(PacketPtr pkt)
|
||||
{
|
||||
return delay + masterPort.sendAtomic(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
Bridge::BridgeSlavePort::recvFunctional(PacketPtr pkt)
|
||||
{
|
||||
std::list<DeferredResponse>::iterator i;
|
||||
|
||||
pkt->pushLabel(name());
|
||||
|
||||
// check the response queue
|
||||
for (i = responseQueue.begin(); i != responseQueue.end(); ++i) {
|
||||
if (pkt->checkFunctional((*i).pkt)) {
|
||||
pkt->makeResponse();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// also check the master port's request queue
|
||||
if (masterPort.checkFunctional(pkt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pkt->popLabel();
|
||||
|
||||
// fall through if pkt still not satisfied
|
||||
masterPort.sendFunctional(pkt);
|
||||
}
|
||||
|
||||
bool
|
||||
Bridge::BridgeMasterPort::checkFunctional(PacketPtr pkt)
|
||||
{
|
||||
bool found = false;
|
||||
std::list<DeferredRequest>::iterator i = requestQueue.begin();
|
||||
|
||||
while(i != requestQueue.end() && !found) {
|
||||
if (pkt->checkFunctional((*i).pkt)) {
|
||||
pkt->makeResponse();
|
||||
found = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
AddrRangeList
|
||||
Bridge::BridgeSlavePort::getAddrRanges()
|
||||
{
|
||||
return ranges;
|
||||
}
|
||||
|
||||
Bridge *
|
||||
BridgeParams::create()
|
||||
{
|
||||
return new Bridge(this);
|
||||
}
|
||||
420
simulators/gem5/src/mem/bridge.hh
Normal file
420
simulators/gem5/src/mem/bridge.hh
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Steve Reinhardt
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Declaration of a memory-mapped bus bridge that connects a master
|
||||
* and a slave through a request and response queue.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_BRIDGE_HH__
|
||||
#define __MEM_BRIDGE_HH__
|
||||
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
#include "base/types.hh"
|
||||
#include "mem/mem_object.hh"
|
||||
#include "mem/packet.hh"
|
||||
#include "mem/port.hh"
|
||||
#include "params/Bridge.hh"
|
||||
#include "sim/eventq.hh"
|
||||
|
||||
/**
|
||||
* A bridge is used to interface two different busses (or in general a
|
||||
* memory-mapped master and slave), with buffering for requests and
|
||||
* responses. The bridge has a fixed delay for packets passing through
|
||||
* it and responds to a fixed set of address ranges.
|
||||
*
|
||||
* The bridge comprises a slave port and a master port, that buffer
|
||||
* outgoing responses and requests respectively. Buffer space is
|
||||
* reserved when a request arrives, also reserving response space
|
||||
* before forwarding the request. An incoming request is always
|
||||
* accepted (recvTiming returns true), but is potentially NACKed if
|
||||
* there is no request space or response space.
|
||||
*/
|
||||
class Bridge : public MemObject
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* A bridge request state stores packets along with their sender
|
||||
* state and original source. It has enough information to also
|
||||
* restore the response once it comes back to the bridge.
|
||||
*/
|
||||
class RequestState : public Packet::SenderState
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
Packet::SenderState *origSenderState;
|
||||
PortID origSrc;
|
||||
|
||||
RequestState(PacketPtr _pkt)
|
||||
: origSenderState(_pkt->senderState),
|
||||
origSrc(_pkt->getSrc())
|
||||
{ }
|
||||
|
||||
void fixResponse(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->senderState == this);
|
||||
pkt->setDest(origSrc);
|
||||
pkt->senderState = origSenderState;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A deferred request stores a packet along with its scheduled
|
||||
* transmission time, and whether we can expect to see a response
|
||||
* or not.
|
||||
*/
|
||||
class DeferredRequest
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
Tick ready;
|
||||
PacketPtr pkt;
|
||||
bool expectResponse;
|
||||
|
||||
DeferredRequest(PacketPtr _pkt, Tick t)
|
||||
: ready(t), pkt(_pkt), expectResponse(_pkt->needsResponse())
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* A deferred response stores a packet along with its scheduled
|
||||
* transmission time. It also contains information of whether the
|
||||
* bridge NACKed the packet to be able to correctly maintain
|
||||
* counters of outstanding responses.
|
||||
*/
|
||||
class DeferredResponse {
|
||||
|
||||
public:
|
||||
|
||||
Tick ready;
|
||||
PacketPtr pkt;
|
||||
bool nackedHere;
|
||||
|
||||
DeferredResponse(PacketPtr _pkt, Tick t, bool nack = false)
|
||||
: ready(t), pkt(_pkt), nackedHere(nack)
|
||||
{ }
|
||||
};
|
||||
|
||||
// Forward declaration to allow the slave port to have a pointer
|
||||
class BridgeMasterPort;
|
||||
|
||||
/**
|
||||
* The port on the side that receives requests and sends
|
||||
* responses. The slave port has a set of address ranges that it
|
||||
* is responsible for. The slave port also has a buffer for the
|
||||
* responses not yet sent.
|
||||
*/
|
||||
class BridgeSlavePort : public SlavePort
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
/** A pointer to the bridge to which this port belongs. */
|
||||
Bridge *bridge;
|
||||
|
||||
/**
|
||||
* Master port on the other side of the bridge
|
||||
* (connected to the other bus).
|
||||
*/
|
||||
BridgeMasterPort& masterPort;
|
||||
|
||||
/** Minimum request delay though this bridge. */
|
||||
Tick delay;
|
||||
|
||||
/** Min delay to respond with a nack. */
|
||||
Tick nackDelay;
|
||||
|
||||
/** Address ranges to pass through the bridge */
|
||||
AddrRangeList ranges;
|
||||
|
||||
/**
|
||||
* Response packet queue. Response packets are held in this
|
||||
* queue for a specified delay to model the processing delay
|
||||
* of the bridge.
|
||||
*/
|
||||
std::list<DeferredResponse> responseQueue;
|
||||
|
||||
/** Counter to track the outstanding responses. */
|
||||
unsigned int outstandingResponses;
|
||||
|
||||
/** If we're waiting for a retry to happen. */
|
||||
bool inRetry;
|
||||
|
||||
/** Max queue size for reserved responses. */
|
||||
unsigned int respQueueLimit;
|
||||
|
||||
/**
|
||||
* Is this side blocked from accepting new response packets.
|
||||
*
|
||||
* @return true if the reserved space has reached the set limit
|
||||
*/
|
||||
bool respQueueFull();
|
||||
|
||||
/**
|
||||
* Turn the request packet into a NACK response and put it in
|
||||
* the response queue and schedule its transmission.
|
||||
*
|
||||
* @param pkt the request packet to NACK
|
||||
*/
|
||||
void nackRequest(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Handle send event, scheduled when the packet at the head of
|
||||
* the response queue is ready to transmit (for timing
|
||||
* accesses only).
|
||||
*/
|
||||
void trySend();
|
||||
|
||||
/**
|
||||
* Private class for scheduling sending of responses from the
|
||||
* response queue.
|
||||
*/
|
||||
class SendEvent : public Event
|
||||
{
|
||||
BridgeSlavePort& port;
|
||||
|
||||
public:
|
||||
SendEvent(BridgeSlavePort& p) : port(p) {}
|
||||
virtual void process() { port.trySend(); }
|
||||
virtual const char *description() const { return "bridge send"; }
|
||||
};
|
||||
|
||||
/** Send event for the response queue. */
|
||||
SendEvent sendEvent;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for the BridgeSlavePort.
|
||||
*
|
||||
* @param _name the port name including the owner
|
||||
* @param _bridge the structural owner
|
||||
* @param _masterPort the master port on the other side of the bridge
|
||||
* @param _delay the delay from seeing a response to sending it
|
||||
* @param _nack_delay the delay from a NACK to sending the response
|
||||
* @param _resp_limit the size of the response queue
|
||||
* @param _ranges a number of address ranges to forward
|
||||
*/
|
||||
BridgeSlavePort(const std::string &_name, Bridge *_bridge,
|
||||
BridgeMasterPort& _masterPort, int _delay,
|
||||
int _nack_delay, int _resp_limit,
|
||||
std::vector<Range<Addr> > _ranges);
|
||||
|
||||
/**
|
||||
* Queue a response packet to be sent out later and also schedule
|
||||
* a send if necessary.
|
||||
*
|
||||
* @param pkt a response to send out after a delay
|
||||
*/
|
||||
void queueForSendTiming(PacketPtr pkt);
|
||||
|
||||
protected:
|
||||
|
||||
/** When receiving a timing request from the peer port,
|
||||
pass it to the bridge. */
|
||||
virtual bool recvTimingReq(PacketPtr pkt);
|
||||
|
||||
/** When receiving a retry request from the peer port,
|
||||
pass it to the bridge. */
|
||||
virtual void recvRetry();
|
||||
|
||||
/** When receiving a Atomic requestfrom the peer port,
|
||||
pass it to the bridge. */
|
||||
virtual Tick recvAtomic(PacketPtr pkt);
|
||||
|
||||
/** When receiving a Functional request from the peer port,
|
||||
pass it to the bridge. */
|
||||
virtual void recvFunctional(PacketPtr pkt);
|
||||
|
||||
/** When receiving a address range request the peer port,
|
||||
pass it to the bridge. */
|
||||
virtual AddrRangeList getAddrRanges();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Port on the side that forwards requests and receives
|
||||
* responses. The master port has a buffer for the requests not
|
||||
* yet sent.
|
||||
*/
|
||||
class BridgeMasterPort : public MasterPort
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
/** A pointer to the bridge to which this port belongs. */
|
||||
Bridge* bridge;
|
||||
|
||||
/**
|
||||
* Pointer to the slave port on the other side of the bridge
|
||||
* (connected to the other bus).
|
||||
*/
|
||||
BridgeSlavePort& slavePort;
|
||||
|
||||
/** Minimum delay though this bridge. */
|
||||
Tick delay;
|
||||
|
||||
/**
|
||||
* Request packet queue. Request packets are held in this
|
||||
* queue for a specified delay to model the processing delay
|
||||
* of the bridge.
|
||||
*/
|
||||
std::list<DeferredRequest> requestQueue;
|
||||
|
||||
/** If we're waiting for a retry to happen. */
|
||||
bool inRetry;
|
||||
|
||||
/** Max queue size for request packets */
|
||||
unsigned int reqQueueLimit;
|
||||
|
||||
/**
|
||||
* Handle send event, scheduled when the packet at the head of
|
||||
* the outbound queue is ready to transmit (for timing
|
||||
* accesses only).
|
||||
*/
|
||||
void trySend();
|
||||
|
||||
/**
|
||||
* Private class for scheduling sending of requests from the
|
||||
* request queue.
|
||||
*/
|
||||
class SendEvent : public Event
|
||||
{
|
||||
BridgeMasterPort& port;
|
||||
|
||||
public:
|
||||
SendEvent(BridgeMasterPort& p) : port(p) {}
|
||||
virtual void process() { port.trySend(); }
|
||||
virtual const char *description() const { return "bridge send"; }
|
||||
};
|
||||
|
||||
/** Send event for the request queue. */
|
||||
SendEvent sendEvent;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for the BridgeMasterPort.
|
||||
*
|
||||
* @param _name the port name including the owner
|
||||
* @param _bridge the structural owner
|
||||
* @param _slavePort the slave port on the other side of the bridge
|
||||
* @param _delay the delay from seeing a request to sending it
|
||||
* @param _req_limit the size of the request queue
|
||||
*/
|
||||
BridgeMasterPort(const std::string &_name, Bridge *_bridge,
|
||||
BridgeSlavePort& _slavePort, int _delay,
|
||||
int _req_limit);
|
||||
|
||||
/**
|
||||
* Is this side blocked from accepting new request packets.
|
||||
*
|
||||
* @return true if the occupied space has reached the set limit
|
||||
*/
|
||||
bool reqQueueFull();
|
||||
|
||||
/**
|
||||
* Queue a request packet to be sent out later and also schedule
|
||||
* a send if necessary.
|
||||
*
|
||||
* @param pkt a request to send out after a delay
|
||||
*/
|
||||
void queueForSendTiming(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Check a functional request against the packets in our
|
||||
* request queue.
|
||||
*
|
||||
* @param pkt packet to check against
|
||||
*
|
||||
* @return true if we find a match
|
||||
*/
|
||||
bool checkFunctional(PacketPtr pkt);
|
||||
|
||||
protected:
|
||||
|
||||
/** When receiving a timing request from the peer port,
|
||||
pass it to the bridge. */
|
||||
virtual bool recvTimingResp(PacketPtr pkt);
|
||||
|
||||
/** When receiving a retry request from the peer port,
|
||||
pass it to the bridge. */
|
||||
virtual void recvRetry();
|
||||
};
|
||||
|
||||
/** Slave port of the bridge. */
|
||||
BridgeSlavePort slavePort;
|
||||
|
||||
/** Master port of the bridge. */
|
||||
BridgeMasterPort masterPort;
|
||||
|
||||
/** If this bridge should acknowledge writes. */
|
||||
bool ackWrites;
|
||||
|
||||
public:
|
||||
typedef BridgeParams Params;
|
||||
|
||||
protected:
|
||||
Params *_params;
|
||||
|
||||
public:
|
||||
const Params *params() const { return _params; }
|
||||
|
||||
virtual MasterPort& getMasterPort(const std::string& if_name,
|
||||
int idx = -1);
|
||||
virtual SlavePort& getSlavePort(const std::string& if_name, int idx = -1);
|
||||
|
||||
virtual void init();
|
||||
|
||||
Bridge(Params *p);
|
||||
};
|
||||
|
||||
#endif //__MEM_BUS_HH__
|
||||
456
simulators/gem5/src/mem/bus.cc
Normal file
456
simulators/gem5/src/mem/bus.cc
Normal file
@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Andreas Hansson
|
||||
* William Wang
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of a bus object.
|
||||
*/
|
||||
|
||||
#include "base/misc.hh"
|
||||
#include "base/trace.hh"
|
||||
#include "debug/Bus.hh"
|
||||
#include "debug/BusAddrRanges.hh"
|
||||
#include "mem/bus.hh"
|
||||
|
||||
BaseBus::BaseBus(const BaseBusParams *p)
|
||||
: MemObject(p), clock(p->clock),
|
||||
headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
|
||||
drainEvent(NULL), busIdleEvent(this), inRetry(false),
|
||||
defaultPortID(InvalidPortID),
|
||||
useDefaultRange(p->use_default_range),
|
||||
defaultBlockSize(p->block_size),
|
||||
cachedBlockSize(0), cachedBlockSizeValid(false)
|
||||
{
|
||||
//width, clock period, and header cycles must be positive
|
||||
if (width <= 0)
|
||||
fatal("Bus width must be positive\n");
|
||||
if (clock <= 0)
|
||||
fatal("Bus clock period must be positive\n");
|
||||
if (headerCycles <= 0)
|
||||
fatal("Number of header cycles must be positive\n");
|
||||
}
|
||||
|
||||
BaseBus::~BaseBus()
|
||||
{
|
||||
for (MasterPortIter m = masterPorts.begin(); m != masterPorts.end();
|
||||
++m) {
|
||||
delete *m;
|
||||
}
|
||||
|
||||
for (SlavePortIter s = slavePorts.begin(); s != slavePorts.end();
|
||||
++s) {
|
||||
delete *s;
|
||||
}
|
||||
}
|
||||
|
||||
MasterPort &
|
||||
BaseBus::getMasterPort(const std::string &if_name, int idx)
|
||||
{
|
||||
if (if_name == "master" && idx < masterPorts.size()) {
|
||||
// the master port index translates directly to the vector position
|
||||
return *masterPorts[idx];
|
||||
} else if (if_name == "default") {
|
||||
return *masterPorts[defaultPortID];
|
||||
} else {
|
||||
return MemObject::getMasterPort(if_name, idx);
|
||||
}
|
||||
}
|
||||
|
||||
SlavePort &
|
||||
BaseBus::getSlavePort(const std::string &if_name, int idx)
|
||||
{
|
||||
if (if_name == "slave" && idx < slavePorts.size()) {
|
||||
// the slave port index translates directly to the vector position
|
||||
return *slavePorts[idx];
|
||||
} else {
|
||||
return MemObject::getSlavePort(if_name, idx);
|
||||
}
|
||||
}
|
||||
|
||||
Tick
|
||||
BaseBus::calcPacketTiming(PacketPtr pkt)
|
||||
{
|
||||
// determine the current time rounded to the closest following
|
||||
// clock edge
|
||||
Tick now = curTick();
|
||||
if (now % clock != 0) {
|
||||
now = ((now / clock) + 1) * clock;
|
||||
}
|
||||
|
||||
Tick headerTime = now + headerCycles * clock;
|
||||
|
||||
// The packet will be sent. Figure out how long it occupies the bus, and
|
||||
// how much of that time is for the first "word", aka bus width.
|
||||
int numCycles = 0;
|
||||
if (pkt->hasData()) {
|
||||
// If a packet has data, it needs ceil(size/width) cycles to send it
|
||||
int dataSize = pkt->getSize();
|
||||
numCycles += dataSize/width;
|
||||
if (dataSize % width)
|
||||
numCycles++;
|
||||
}
|
||||
|
||||
// The first word will be delivered after the current tick, the delivery
|
||||
// of the address if any, and one bus cycle to deliver the data
|
||||
pkt->firstWordTime = headerTime + clock;
|
||||
|
||||
pkt->finishTime = headerTime + numCycles * clock;
|
||||
|
||||
return headerTime;
|
||||
}
|
||||
|
||||
void BaseBus::occupyBus(Tick until)
|
||||
{
|
||||
if (until == 0) {
|
||||
// shortcut for express snoop packets
|
||||
return;
|
||||
}
|
||||
|
||||
tickNextIdle = until;
|
||||
reschedule(busIdleEvent, tickNextIdle, true);
|
||||
|
||||
DPRINTF(BaseBus, "The bus is now occupied from tick %d to %d\n",
|
||||
curTick(), tickNextIdle);
|
||||
}
|
||||
|
||||
bool
|
||||
BaseBus::isOccupied(Port* port)
|
||||
{
|
||||
// first we see if the next idle tick is in the future, next the
|
||||
// bus is considered occupied if there are ports on the retry list
|
||||
// and we are not in a retry with the current port
|
||||
if (tickNextIdle > curTick() ||
|
||||
(!retryList.empty() && !(inRetry && port == retryList.front()))) {
|
||||
addToRetryList(port);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
BaseBus::succeededTiming(Tick busy_time)
|
||||
{
|
||||
// occupy the bus accordingly
|
||||
occupyBus(busy_time);
|
||||
|
||||
// if a retrying port succeeded, also take it off the retry list
|
||||
if (inRetry) {
|
||||
DPRINTF(BaseBus, "Remove retry from list %s\n",
|
||||
retryList.front()->name());
|
||||
retryList.pop_front();
|
||||
inRetry = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaseBus::releaseBus()
|
||||
{
|
||||
// releasing the bus means we should now be idle
|
||||
assert(curTick() >= tickNextIdle);
|
||||
|
||||
// bus is now idle, so if someone is waiting we can retry
|
||||
if (!retryList.empty()) {
|
||||
// note that we block (return false on recvTiming) both
|
||||
// because the bus is busy and because the destination is
|
||||
// busy, and in the latter case the bus may be released before
|
||||
// we see a retry from the destination
|
||||
retryWaiting();
|
||||
}
|
||||
|
||||
//If we weren't able to drain before, we might be able to now.
|
||||
if (drainEvent && retryList.empty() && curTick() >= tickNextIdle) {
|
||||
drainEvent->process();
|
||||
// Clear the drain event once we're done with it.
|
||||
drainEvent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaseBus::retryWaiting()
|
||||
{
|
||||
// this should never be called with an empty retry list
|
||||
assert(!retryList.empty());
|
||||
|
||||
// send a retry to the port at the head of the retry list
|
||||
inRetry = true;
|
||||
|
||||
// note that we might have blocked on the receiving port being
|
||||
// busy (rather than the bus itself) and now call retry before the
|
||||
// destination called retry on the bus
|
||||
retryList.front()->sendRetry();
|
||||
|
||||
// If inRetry is still true, sendTiming wasn't called in zero time
|
||||
// (e.g. the cache does this)
|
||||
if (inRetry) {
|
||||
retryList.pop_front();
|
||||
inRetry = false;
|
||||
|
||||
//Bring tickNextIdle up to the present
|
||||
while (tickNextIdle < curTick())
|
||||
tickNextIdle += clock;
|
||||
|
||||
//Burn a cycle for the missed grant.
|
||||
tickNextIdle += clock;
|
||||
|
||||
reschedule(busIdleEvent, tickNextIdle, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaseBus::recvRetry()
|
||||
{
|
||||
// we got a retry from a peer that we tried to send something to
|
||||
// and failed, but we sent it on the account of someone else, and
|
||||
// that source port should be on our retry list, however if the
|
||||
// bus is released before this happens and the retry (from the bus
|
||||
// point of view) is successful then this no longer holds and we
|
||||
// could in fact have an empty retry list
|
||||
if (retryList.empty())
|
||||
return;
|
||||
|
||||
// if the bus isn't busy
|
||||
if (curTick() >= tickNextIdle) {
|
||||
// note that we do not care who told us to retry at the moment, we
|
||||
// merely let the first one on the retry list go
|
||||
retryWaiting();
|
||||
}
|
||||
}
|
||||
|
||||
PortID
|
||||
BaseBus::findPort(Addr addr)
|
||||
{
|
||||
/* An interval tree would be a better way to do this. --ali. */
|
||||
PortID dest_id = checkPortCache(addr);
|
||||
if (dest_id != InvalidPortID)
|
||||
return dest_id;
|
||||
|
||||
// Check normal port ranges
|
||||
PortIter i = portMap.find(RangeSize(addr,1));
|
||||
if (i != portMap.end()) {
|
||||
dest_id = i->second;
|
||||
updatePortCache(dest_id, i->first.start, i->first.end);
|
||||
return dest_id;
|
||||
}
|
||||
|
||||
// Check if this matches the default range
|
||||
if (useDefaultRange) {
|
||||
AddrRangeIter a_end = defaultRange.end();
|
||||
for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) {
|
||||
if (*i == addr) {
|
||||
DPRINTF(BusAddrRanges, " found addr %#llx on default\n",
|
||||
addr);
|
||||
return defaultPortID;
|
||||
}
|
||||
}
|
||||
} else if (defaultPortID != InvalidPortID) {
|
||||
DPRINTF(BusAddrRanges, "Unable to find destination for addr %#llx, "
|
||||
"will use default port\n", addr);
|
||||
return defaultPortID;
|
||||
}
|
||||
|
||||
// we should use the range for the default port and it did not
|
||||
// match, or the default port is not set
|
||||
fatal("Unable to find destination for addr %#llx on bus %s\n", addr,
|
||||
name());
|
||||
}
|
||||
|
||||
/** Function called by the port when the bus is receiving a range change.*/
|
||||
void
|
||||
BaseBus::recvRangeChange(PortID master_port_id)
|
||||
{
|
||||
AddrRangeList ranges;
|
||||
AddrRangeIter iter;
|
||||
|
||||
if (inRecvRangeChange.count(master_port_id))
|
||||
return;
|
||||
inRecvRangeChange.insert(master_port_id);
|
||||
|
||||
DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n",
|
||||
master_port_id);
|
||||
|
||||
clearPortCache();
|
||||
if (master_port_id == defaultPortID) {
|
||||
defaultRange.clear();
|
||||
// Only try to update these ranges if the user set a default responder.
|
||||
if (useDefaultRange) {
|
||||
AddrRangeList ranges =
|
||||
masterPorts[master_port_id]->getSlavePort().getAddrRanges();
|
||||
for(iter = ranges.begin(); iter != ranges.end(); iter++) {
|
||||
defaultRange.push_back(*iter);
|
||||
DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n",
|
||||
iter->start, iter->end);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
assert(master_port_id < masterPorts.size() && master_port_id >= 0);
|
||||
MasterPort *port = masterPorts[master_port_id];
|
||||
|
||||
// Clean out any previously existent ids
|
||||
for (PortIter portIter = portMap.begin();
|
||||
portIter != portMap.end(); ) {
|
||||
if (portIter->second == master_port_id)
|
||||
portMap.erase(portIter++);
|
||||
else
|
||||
portIter++;
|
||||
}
|
||||
|
||||
ranges = port->getSlavePort().getAddrRanges();
|
||||
|
||||
for (iter = ranges.begin(); iter != ranges.end(); iter++) {
|
||||
DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n",
|
||||
iter->start, iter->end, master_port_id);
|
||||
if (portMap.insert(*iter, master_port_id) == portMap.end()) {
|
||||
PortID conflict_id = portMap.find(*iter)->second;
|
||||
fatal("%s has two ports with same range:\n\t%s\n\t%s\n",
|
||||
name(),
|
||||
masterPorts[master_port_id]->getSlavePort().name(),
|
||||
masterPorts[conflict_id]->getSlavePort().name());
|
||||
}
|
||||
}
|
||||
}
|
||||
DPRINTF(BusAddrRanges, "port list has %d entries\n", portMap.size());
|
||||
|
||||
// tell all our neighbouring master ports that our address range
|
||||
// has changed
|
||||
for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
|
||||
++p)
|
||||
(*p)->sendRangeChange();
|
||||
|
||||
inRecvRangeChange.erase(master_port_id);
|
||||
}
|
||||
|
||||
AddrRangeList
|
||||
BaseBus::getAddrRanges()
|
||||
{
|
||||
AddrRangeList ranges;
|
||||
|
||||
DPRINTF(BusAddrRanges, "received address range request, returning:\n");
|
||||
|
||||
for (AddrRangeIter dflt_iter = defaultRange.begin();
|
||||
dflt_iter != defaultRange.end(); dflt_iter++) {
|
||||
ranges.push_back(*dflt_iter);
|
||||
DPRINTF(BusAddrRanges, " -- Dflt: %#llx : %#llx\n",dflt_iter->start,
|
||||
dflt_iter->end);
|
||||
}
|
||||
for (PortIter portIter = portMap.begin();
|
||||
portIter != portMap.end(); portIter++) {
|
||||
bool subset = false;
|
||||
for (AddrRangeIter dflt_iter = defaultRange.begin();
|
||||
dflt_iter != defaultRange.end(); dflt_iter++) {
|
||||
if ((portIter->first.start < dflt_iter->start &&
|
||||
portIter->first.end >= dflt_iter->start) ||
|
||||
(portIter->first.start < dflt_iter->end &&
|
||||
portIter->first.end >= dflt_iter->end))
|
||||
fatal("Devices can not set ranges that itersect the default set\
|
||||
but are not a subset of the default set.\n");
|
||||
if (portIter->first.start >= dflt_iter->start &&
|
||||
portIter->first.end <= dflt_iter->end) {
|
||||
subset = true;
|
||||
DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n",
|
||||
portIter->first.start, portIter->first.end);
|
||||
}
|
||||
}
|
||||
if (!subset) {
|
||||
ranges.push_back(portIter->first);
|
||||
DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n",
|
||||
portIter->first.start, portIter->first.end);
|
||||
}
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
unsigned
|
||||
BaseBus::findBlockSize()
|
||||
{
|
||||
if (cachedBlockSizeValid)
|
||||
return cachedBlockSize;
|
||||
|
||||
unsigned max_bs = 0;
|
||||
|
||||
for (MasterPortConstIter m = masterPorts.begin(); m != masterPorts.end();
|
||||
++m) {
|
||||
unsigned tmp_bs = (*m)->peerBlockSize();
|
||||
if (tmp_bs > max_bs)
|
||||
max_bs = tmp_bs;
|
||||
}
|
||||
|
||||
for (SlavePortConstIter s = slavePorts.begin(); s != slavePorts.end();
|
||||
++s) {
|
||||
unsigned tmp_bs = (*s)->peerBlockSize();
|
||||
if (tmp_bs > max_bs)
|
||||
max_bs = tmp_bs;
|
||||
}
|
||||
if (max_bs == 0)
|
||||
max_bs = defaultBlockSize;
|
||||
|
||||
if (max_bs != 64)
|
||||
warn_once("Blocksize found to not be 64... hmm... probably not.\n");
|
||||
cachedBlockSize = max_bs;
|
||||
cachedBlockSizeValid = true;
|
||||
return max_bs;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
BaseBus::drain(Event * de)
|
||||
{
|
||||
//We should check that we're not "doing" anything, and that noone is
|
||||
//waiting. We might be idle but have someone waiting if the device we
|
||||
//contacted for a retry didn't actually retry.
|
||||
if (!retryList.empty() || (curTick() < tickNextIdle &&
|
||||
busIdleEvent.scheduled())) {
|
||||
drainEvent = de;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
BaseBus::startup()
|
||||
{
|
||||
if (tickNextIdle < curTick())
|
||||
tickNextIdle = (curTick() / clock) * clock + clock;
|
||||
}
|
||||
290
simulators/gem5/src/mem/bus.hh
Normal file
290
simulators/gem5/src/mem/bus.hh
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Ron Dreslinski
|
||||
* Ali Saidi
|
||||
* Andreas Hansson
|
||||
* William Wang
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Declaration of an abstract bus base class.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_BUS_HH__
|
||||
#define __MEM_BUS_HH__
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
#include "base/range.hh"
|
||||
#include "base/range_map.hh"
|
||||
#include "base/types.hh"
|
||||
#include "mem/mem_object.hh"
|
||||
#include "params/BaseBus.hh"
|
||||
|
||||
/**
|
||||
* The base bus contains the common elements of the non-coherent and
|
||||
* coherent bus. It is an abstract class that does not have any of the
|
||||
* functionality relating to the actual reception and transmission of
|
||||
* packets, as this is left for the subclasses.
|
||||
*
|
||||
* The BaseBus is responsible for the basic flow control (busy or
|
||||
* not), the administration of retries, and the address decoding.
|
||||
*/
|
||||
class BaseBus : public MemObject
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
/** the clock speed for the bus */
|
||||
int clock;
|
||||
/** cycles of overhead per transaction */
|
||||
int headerCycles;
|
||||
/** the width of the bus in bytes */
|
||||
int width;
|
||||
/** the next tick at which the bus will be idle */
|
||||
Tick tickNextIdle;
|
||||
|
||||
Event * drainEvent;
|
||||
|
||||
typedef range_map<Addr, PortID>::iterator PortIter;
|
||||
range_map<Addr, PortID> portMap;
|
||||
|
||||
AddrRangeList defaultRange;
|
||||
|
||||
/**
|
||||
* Determine if the bus is to be considered occupied when being
|
||||
* presented with a packet from a specific port. If so, the port
|
||||
* in question is also added to the retry list.
|
||||
*
|
||||
* @param port Source port on the bus presenting the packet
|
||||
*
|
||||
* @return True if the bus is to be considered occupied
|
||||
*/
|
||||
bool isOccupied(Port* port);
|
||||
|
||||
/**
|
||||
* Deal with a destination port accepting a packet by potentially
|
||||
* removing the source port from the retry list (if retrying) and
|
||||
* occupying the bus accordingly.
|
||||
*
|
||||
* @param busy_time Time to spend as a result of a successful send
|
||||
*/
|
||||
void succeededTiming(Tick busy_time);
|
||||
|
||||
/** Timing function called by port when it is once again able to process
|
||||
* requests. */
|
||||
void recvRetry();
|
||||
|
||||
/**
|
||||
* Function called by the port when the bus is recieving a range change.
|
||||
*
|
||||
* @param master_port_id id of the port that received the change
|
||||
*/
|
||||
void recvRangeChange(PortID master_port_id);
|
||||
|
||||
/** Find which port connected to this bus (if any) should be given a packet
|
||||
* with this address.
|
||||
* @param addr Address to find port for.
|
||||
* @return id of port that the packet should be sent out of.
|
||||
*/
|
||||
PortID findPort(Addr addr);
|
||||
|
||||
// Cache for the findPort function storing recently used ports from portMap
|
||||
struct PortCache {
|
||||
bool valid;
|
||||
PortID id;
|
||||
Addr start;
|
||||
Addr end;
|
||||
};
|
||||
|
||||
PortCache portCache[3];
|
||||
|
||||
// Checks the cache and returns the id of the port that has the requested
|
||||
// address within its range
|
||||
inline PortID checkPortCache(Addr addr) {
|
||||
if (portCache[0].valid && addr >= portCache[0].start &&
|
||||
addr < portCache[0].end) {
|
||||
return portCache[0].id;
|
||||
}
|
||||
if (portCache[1].valid && addr >= portCache[1].start &&
|
||||
addr < portCache[1].end) {
|
||||
return portCache[1].id;
|
||||
}
|
||||
if (portCache[2].valid && addr >= portCache[2].start &&
|
||||
addr < portCache[2].end) {
|
||||
return portCache[2].id;
|
||||
}
|
||||
|
||||
return InvalidPortID;
|
||||
}
|
||||
|
||||
// Clears the earliest entry of the cache and inserts a new port entry
|
||||
inline void updatePortCache(short id, Addr start, Addr end) {
|
||||
portCache[2].valid = portCache[1].valid;
|
||||
portCache[2].id = portCache[1].id;
|
||||
portCache[2].start = portCache[1].start;
|
||||
portCache[2].end = portCache[1].end;
|
||||
|
||||
portCache[1].valid = portCache[0].valid;
|
||||
portCache[1].id = portCache[0].id;
|
||||
portCache[1].start = portCache[0].start;
|
||||
portCache[1].end = portCache[0].end;
|
||||
|
||||
portCache[0].valid = true;
|
||||
portCache[0].id = id;
|
||||
portCache[0].start = start;
|
||||
portCache[0].end = end;
|
||||
}
|
||||
|
||||
// Clears the cache. Needs to be called in constructor.
|
||||
inline void clearPortCache() {
|
||||
portCache[2].valid = false;
|
||||
portCache[1].valid = false;
|
||||
portCache[0].valid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the address ranges the bus is responsible for.
|
||||
*
|
||||
* @return a list of non-overlapping address ranges
|
||||
*/
|
||||
AddrRangeList getAddrRanges();
|
||||
|
||||
/** Calculate the timing parameters for the packet. Updates the
|
||||
* firstWordTime and finishTime fields of the packet object.
|
||||
* Returns the tick at which the packet header is completed (which
|
||||
* will be all that is sent if the target rejects the packet).
|
||||
*/
|
||||
Tick calcPacketTiming(PacketPtr pkt);
|
||||
|
||||
/** Occupy the bus until until */
|
||||
void occupyBus(Tick until);
|
||||
|
||||
/**
|
||||
* Release the bus after being occupied and return to an idle
|
||||
* state where we proceed to send a retry to any potential waiting
|
||||
* port, or drain if asked to do so.
|
||||
*/
|
||||
void releaseBus();
|
||||
|
||||
/**
|
||||
* Send a retry to the port at the head of the retryList. The
|
||||
* caller must ensure that the list is not empty.
|
||||
*/
|
||||
void retryWaiting();
|
||||
|
||||
/**
|
||||
* Ask everyone on the bus what their size is
|
||||
*
|
||||
* @return the max of all the sizes
|
||||
*/
|
||||
unsigned findBlockSize();
|
||||
|
||||
// event used to schedule a release of the bus
|
||||
EventWrapper<BaseBus, &BaseBus::releaseBus> busIdleEvent;
|
||||
|
||||
bool inRetry;
|
||||
std::set<PortID> inRecvRangeChange;
|
||||
|
||||
/** The master and slave ports of the bus */
|
||||
std::vector<SlavePort*> slavePorts;
|
||||
std::vector<MasterPort*> masterPorts;
|
||||
|
||||
/** Convenience typedefs. */
|
||||
typedef std::vector<SlavePort*>::iterator SlavePortIter;
|
||||
typedef std::vector<MasterPort*>::iterator MasterPortIter;
|
||||
typedef std::vector<SlavePort*>::const_iterator SlavePortConstIter;
|
||||
typedef std::vector<MasterPort*>::const_iterator MasterPortConstIter;
|
||||
|
||||
/** An array of pointers to ports that retry should be called on because the
|
||||
* original send failed for whatever reason.*/
|
||||
std::list<Port*> retryList;
|
||||
|
||||
void addToRetryList(Port* port)
|
||||
{
|
||||
if (!inRetry) {
|
||||
// The device wasn't retrying a packet, or wasn't at an
|
||||
// appropriate time.
|
||||
retryList.push_back(port);
|
||||
} else {
|
||||
if (!retryList.empty() && port == retryList.front()) {
|
||||
// The device was retrying a packet. It didn't work,
|
||||
// so we'll leave it at the head of the retry list.
|
||||
inRetry = false;
|
||||
} else {
|
||||
// We are in retry, but not for this port, put it at
|
||||
// the end.
|
||||
retryList.push_back(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Port that handles requests that don't match any of the interfaces.*/
|
||||
PortID defaultPortID;
|
||||
|
||||
/** If true, use address range provided by default device. Any
|
||||
address not handled by another port and not in default device's
|
||||
range will cause a fatal error. If false, just send all
|
||||
addresses not handled by another port to default device. */
|
||||
bool useDefaultRange;
|
||||
|
||||
unsigned defaultBlockSize;
|
||||
unsigned cachedBlockSize;
|
||||
bool cachedBlockSizeValid;
|
||||
|
||||
BaseBus(const BaseBusParams *p);
|
||||
|
||||
virtual ~BaseBus();
|
||||
|
||||
public:
|
||||
|
||||
/** A function used to return the port associated with this bus object. */
|
||||
virtual MasterPort& getMasterPort(const std::string& if_name, int idx = -1);
|
||||
virtual SlavePort& getSlavePort(const std::string& if_name, int idx = -1);
|
||||
|
||||
virtual void startup();
|
||||
|
||||
unsigned int drain(Event *de);
|
||||
|
||||
};
|
||||
|
||||
#endif //__MEM_BUS_HH__
|
||||
64
simulators/gem5/src/mem/cache/BaseCache.py
vendored
Normal file
64
simulators/gem5/src/mem/cache/BaseCache.py
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
# Copyright (c) 2005-2007 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
|
||||
|
||||
from m5.params import *
|
||||
from m5.proxy import *
|
||||
from MemObject import MemObject
|
||||
from Prefetcher import BasePrefetcher
|
||||
|
||||
|
||||
class BaseCache(MemObject):
|
||||
type = 'BaseCache'
|
||||
assoc = Param.Int("associativity")
|
||||
block_size = Param.Int("block size in bytes")
|
||||
latency = Param.Latency("Latency")
|
||||
hash_delay = Param.Int(1, "time in cycles of hash access")
|
||||
max_miss_count = Param.Counter(0,
|
||||
"number of misses to handle before calling exit")
|
||||
mshrs = Param.Int("number of MSHRs (max outstanding requests)")
|
||||
prioritizeRequests = Param.Bool(False,
|
||||
"always service demand misses first")
|
||||
repl = Param.Repl(NULL, "replacement policy")
|
||||
size = Param.MemorySize("capacity in bytes")
|
||||
forward_snoops = Param.Bool(True,
|
||||
"forward snoops from mem side to cpu side")
|
||||
is_top_level = Param.Bool(False, "Is this cache at the top level (e.g. L1)")
|
||||
subblock_size = Param.Int(0,
|
||||
"Size of subblock in IIC used for compression")
|
||||
tgts_per_mshr = Param.Int("max number of accesses per MSHR")
|
||||
trace_addr = Param.Addr(0, "address to trace")
|
||||
two_queue = Param.Bool(False,
|
||||
"whether the lifo should have two queue replacement")
|
||||
write_buffers = Param.Int(8, "number of write buffers")
|
||||
prefetch_on_access = Param.Bool(False,
|
||||
"notify the hardware prefetcher on every access (not just misses)")
|
||||
prefetcher = Param.BasePrefetcher(NULL,"Prefetcher attached to cache")
|
||||
cpu_side = SlavePort("Port on side closer to CPU")
|
||||
mem_side = MasterPort("Port on side closer to MEM")
|
||||
addr_ranges = VectorParam.AddrRange([AllMemory], "The address range for the CPU-side port")
|
||||
system = Param.System(Parent.any, "System we belong to")
|
||||
48
simulators/gem5/src/mem/cache/SConscript
vendored
Normal file
48
simulators/gem5/src/mem/cache/SConscript
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- 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: Nathan Binkert
|
||||
|
||||
Import('*')
|
||||
|
||||
if env['TARGET_ISA'] == 'no':
|
||||
Return()
|
||||
|
||||
SimObject('BaseCache.py')
|
||||
|
||||
Source('base.cc')
|
||||
Source('cache.cc')
|
||||
Source('blk.cc')
|
||||
Source('builder.cc')
|
||||
Source('mshr.cc')
|
||||
Source('mshr_queue.cc')
|
||||
|
||||
DebugFlag('Cache')
|
||||
DebugFlag('CachePort')
|
||||
DebugFlag('CacheRepl')
|
||||
DebugFlag('HWPrefetch')
|
||||
760
simulators/gem5/src/mem/cache/base.cc
vendored
Normal file
760
simulators/gem5/src/mem/cache/base.cc
vendored
Normal file
@ -0,0 +1,760 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved.
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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 "cpu/base.hh"
|
||||
#include "cpu/smt.hh"
|
||||
#include "debug/Cache.hh"
|
||||
#include "mem/cache/base.hh"
|
||||
#include "mem/cache/mshr.hh"
|
||||
#include "sim/full_system.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
BaseCache::CacheSlavePort::CacheSlavePort(const std::string &_name,
|
||||
BaseCache *_cache,
|
||||
const std::string &_label)
|
||||
: QueuedSlavePort(_name, _cache, queue), queue(*_cache, *this, _label),
|
||||
blocked(false), mustSendRetry(false), sendRetryEvent(this)
|
||||
{
|
||||
}
|
||||
|
||||
BaseCache::BaseCache(const Params *p)
|
||||
: MemObject(p),
|
||||
mshrQueue("MSHRs", p->mshrs, 4, MSHRQueue_MSHRs),
|
||||
writeBuffer("write buffer", p->write_buffers, p->mshrs+1000,
|
||||
MSHRQueue_WriteBuffer),
|
||||
blkSize(p->block_size),
|
||||
hitLatency(p->latency),
|
||||
numTarget(p->tgts_per_mshr),
|
||||
forwardSnoops(p->forward_snoops),
|
||||
isTopLevel(p->is_top_level),
|
||||
blocked(0),
|
||||
noTargetMSHR(NULL),
|
||||
missCount(p->max_miss_count),
|
||||
drainEvent(NULL),
|
||||
addrRanges(p->addr_ranges.begin(), p->addr_ranges.end()),
|
||||
system(p->system)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
BaseCache::CacheSlavePort::setBlocked()
|
||||
{
|
||||
assert(!blocked);
|
||||
DPRINTF(CachePort, "Cache port %s blocking new requests\n", name());
|
||||
blocked = true;
|
||||
}
|
||||
|
||||
void
|
||||
BaseCache::CacheSlavePort::clearBlocked()
|
||||
{
|
||||
assert(blocked);
|
||||
DPRINTF(CachePort, "Cache port %s accepting new requests\n", name());
|
||||
blocked = false;
|
||||
if (mustSendRetry) {
|
||||
DPRINTF(CachePort, "Cache port %s sending retry\n", name());
|
||||
mustSendRetry = false;
|
||||
// @TODO: need to find a better time (next bus cycle?)
|
||||
owner.schedule(sendRetryEvent, curTick() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BaseCache::init()
|
||||
{
|
||||
if (!cpuSidePort->isConnected() || !memSidePort->isConnected())
|
||||
fatal("Cache ports on %s are not connected\n", name());
|
||||
cpuSidePort->sendRangeChange();
|
||||
}
|
||||
|
||||
MasterPort &
|
||||
BaseCache::getMasterPort(const std::string &if_name, int idx)
|
||||
{
|
||||
if (if_name == "mem_side") {
|
||||
return *memSidePort;
|
||||
} else {
|
||||
return MemObject::getMasterPort(if_name, idx);
|
||||
}
|
||||
}
|
||||
|
||||
SlavePort &
|
||||
BaseCache::getSlavePort(const std::string &if_name, int idx)
|
||||
{
|
||||
if (if_name == "cpu_side") {
|
||||
return *cpuSidePort;
|
||||
} else {
|
||||
return MemObject::getSlavePort(if_name, idx);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaseCache::regStats()
|
||||
{
|
||||
using namespace Stats;
|
||||
|
||||
// Hit statistics
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
hits[access_idx]
|
||||
.init(system->maxMasters())
|
||||
.name(name() + "." + cstr + "_hits")
|
||||
.desc("number of " + cstr + " hits")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
hits[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
// These macros make it easier to sum the right subset of commands and
|
||||
// to change the subset of commands that are considered "demand" vs
|
||||
// "non-demand"
|
||||
#define SUM_DEMAND(s) \
|
||||
(s[MemCmd::ReadReq] + s[MemCmd::WriteReq] + s[MemCmd::ReadExReq])
|
||||
|
||||
// should writebacks be included here? prior code was inconsistent...
|
||||
#define SUM_NON_DEMAND(s) \
|
||||
(s[MemCmd::SoftPFReq] + s[MemCmd::HardPFReq])
|
||||
|
||||
demandHits
|
||||
.name(name() + ".demand_hits")
|
||||
.desc("number of demand (read+write) hits")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandHits = SUM_DEMAND(hits);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandHits.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallHits
|
||||
.name(name() + ".overall_hits")
|
||||
.desc("number of overall hits")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallHits = demandHits + SUM_NON_DEMAND(hits);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallHits.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// Miss statistics
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
misses[access_idx]
|
||||
.init(system->maxMasters())
|
||||
.name(name() + "." + cstr + "_misses")
|
||||
.desc("number of " + cstr + " misses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
misses[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandMisses
|
||||
.name(name() + ".demand_misses")
|
||||
.desc("number of demand (read+write) misses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandMisses = SUM_DEMAND(misses);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandMisses.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallMisses
|
||||
.name(name() + ".overall_misses")
|
||||
.desc("number of overall misses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallMisses = demandMisses + SUM_NON_DEMAND(misses);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallMisses.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// Miss latency statistics
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
missLatency[access_idx]
|
||||
.init(system->maxMasters())
|
||||
.name(name() + "." + cstr + "_miss_latency")
|
||||
.desc("number of " + cstr + " miss cycles")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
missLatency[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandMissLatency
|
||||
.name(name() + ".demand_miss_latency")
|
||||
.desc("number of demand (read+write) miss cycles")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandMissLatency = SUM_DEMAND(missLatency);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandMissLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallMissLatency
|
||||
.name(name() + ".overall_miss_latency")
|
||||
.desc("number of overall miss cycles")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallMissLatency = demandMissLatency + SUM_NON_DEMAND(missLatency);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallMissLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// access formulas
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
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];
|
||||
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
accesses[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandAccesses
|
||||
.name(name() + ".demand_accesses")
|
||||
.desc("number of demand (read+write) accesses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandAccesses = demandHits + demandMisses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandAccesses.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallAccesses
|
||||
.name(name() + ".overall_accesses")
|
||||
.desc("number of overall (read+write) accesses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallAccesses = overallHits + overallMisses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallAccesses.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// miss rate formulas
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
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];
|
||||
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
missRate[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandMissRate
|
||||
.name(name() + ".demand_miss_rate")
|
||||
.desc("miss rate for demand accesses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandMissRate = demandMisses / demandAccesses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandMissRate.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallMissRate
|
||||
.name(name() + ".overall_miss_rate")
|
||||
.desc("miss rate for overall accesses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallMissRate = overallMisses / overallAccesses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallMissRate.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// miss latency formulas
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
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];
|
||||
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
avgMissLatency[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandAvgMissLatency
|
||||
.name(name() + ".demand_avg_miss_latency")
|
||||
.desc("average overall miss latency")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandAvgMissLatency = demandMissLatency / demandMisses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandAvgMissLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallAvgMissLatency
|
||||
.name(name() + ".overall_avg_miss_latency")
|
||||
.desc("average overall miss latency")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallAvgMissLatency = overallMissLatency / overallMisses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallAvgMissLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
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")
|
||||
;
|
||||
|
||||
writebacks
|
||||
.init(system->maxMasters())
|
||||
.name(name() + ".writebacks")
|
||||
.desc("number of writebacks")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
writebacks.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// MSHR statistics
|
||||
// MSHR hit statistics
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
mshr_hits[access_idx]
|
||||
.init(system->maxMasters())
|
||||
.name(name() + "." + cstr + "_mshr_hits")
|
||||
.desc("number of " + cstr + " MSHR hits")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
mshr_hits[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandMshrHits
|
||||
.name(name() + ".demand_mshr_hits")
|
||||
.desc("number of demand (read+write) MSHR hits")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandMshrHits = SUM_DEMAND(mshr_hits);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandMshrHits.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallMshrHits
|
||||
.name(name() + ".overall_mshr_hits")
|
||||
.desc("number of overall MSHR hits")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallMshrHits = demandMshrHits + SUM_NON_DEMAND(mshr_hits);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallMshrHits.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// MSHR miss statistics
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
mshr_misses[access_idx]
|
||||
.init(system->maxMasters())
|
||||
.name(name() + "." + cstr + "_mshr_misses")
|
||||
.desc("number of " + cstr + " MSHR misses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
mshr_misses[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandMshrMisses
|
||||
.name(name() + ".demand_mshr_misses")
|
||||
.desc("number of demand (read+write) MSHR misses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandMshrMisses = SUM_DEMAND(mshr_misses);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandMshrMisses.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallMshrMisses
|
||||
.name(name() + ".overall_mshr_misses")
|
||||
.desc("number of overall MSHR misses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallMshrMisses = demandMshrMisses + SUM_NON_DEMAND(mshr_misses);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallMshrMisses.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// MSHR miss latency statistics
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
mshr_miss_latency[access_idx]
|
||||
.init(system->maxMasters())
|
||||
.name(name() + "." + cstr + "_mshr_miss_latency")
|
||||
.desc("number of " + cstr + " MSHR miss cycles")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
mshr_miss_latency[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandMshrMissLatency
|
||||
.name(name() + ".demand_mshr_miss_latency")
|
||||
.desc("number of demand (read+write) MSHR miss cycles")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandMshrMissLatency = SUM_DEMAND(mshr_miss_latency);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandMshrMissLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallMshrMissLatency
|
||||
.name(name() + ".overall_mshr_miss_latency")
|
||||
.desc("number of overall MSHR miss cycles")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallMshrMissLatency =
|
||||
demandMshrMissLatency + SUM_NON_DEMAND(mshr_miss_latency);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallMshrMissLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// MSHR uncacheable statistics
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
mshr_uncacheable[access_idx]
|
||||
.init(system->maxMasters())
|
||||
.name(name() + "." + cstr + "_mshr_uncacheable")
|
||||
.desc("number of " + cstr + " MSHR uncacheable")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
mshr_uncacheable[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
overallMshrUncacheable
|
||||
.name(name() + ".overall_mshr_uncacheable_misses")
|
||||
.desc("number of overall MSHR uncacheable misses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallMshrUncacheable =
|
||||
SUM_DEMAND(mshr_uncacheable) + SUM_NON_DEMAND(mshr_uncacheable);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallMshrUncacheable.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// MSHR miss latency statistics
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
mshr_uncacheable_lat[access_idx]
|
||||
.init(system->maxMasters())
|
||||
.name(name() + "." + cstr + "_mshr_uncacheable_latency")
|
||||
.desc("number of " + cstr + " MSHR uncacheable cycles")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
mshr_uncacheable_lat[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
overallMshrUncacheableLatency
|
||||
.name(name() + ".overall_mshr_uncacheable_latency")
|
||||
.desc("number of overall MSHR uncacheable cycles")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallMshrUncacheableLatency =
|
||||
SUM_DEMAND(mshr_uncacheable_lat) +
|
||||
SUM_NON_DEMAND(mshr_uncacheable_lat);
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallMshrUncacheableLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
#if 0
|
||||
// MSHR access formulas
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
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 < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
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] / accesses[access_idx];
|
||||
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
mshrMissRate[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandMshrMissRate
|
||||
.name(name() + ".demand_mshr_miss_rate")
|
||||
.desc("mshr miss rate for demand accesses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandMshrMissRate = demandMshrMisses / demandAccesses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandMshrMissRate.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallMshrMissRate
|
||||
.name(name() + ".overall_mshr_miss_rate")
|
||||
.desc("mshr miss rate for overall accesses")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallMshrMissRate = overallMshrMisses / overallAccesses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallMshrMissRate.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// mshrMiss latency formulas
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
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];
|
||||
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
avgMshrMissLatency[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
demandAvgMshrMissLatency
|
||||
.name(name() + ".demand_avg_mshr_miss_latency")
|
||||
.desc("average overall mshr miss latency")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
demandAvgMshrMissLatency = demandMshrMissLatency / demandMshrMisses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
demandAvgMshrMissLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
overallAvgMshrMissLatency
|
||||
.name(name() + ".overall_avg_mshr_miss_latency")
|
||||
.desc("average overall mshr miss latency")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallAvgMshrMissLatency = overallMshrMissLatency / overallMshrMisses;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallAvgMshrMissLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
// mshrUncacheable latency formulas
|
||||
for (int access_idx = 0; access_idx < MemCmd::NUM_MEM_CMDS; ++access_idx) {
|
||||
MemCmd cmd(access_idx);
|
||||
const string &cstr = cmd.toString();
|
||||
|
||||
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];
|
||||
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
avgMshrUncacheableLatency[access_idx].subname(i, system->getMasterName(i));
|
||||
}
|
||||
}
|
||||
|
||||
overallAvgMshrUncacheableLatency
|
||||
.name(name() + ".overall_avg_mshr_uncacheable_latency")
|
||||
.desc("average overall mshr uncacheable latency")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
overallAvgMshrUncacheableLatency = overallMshrUncacheableLatency / overallMshrUncacheable;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
overallAvgMshrUncacheableLatency.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
mshr_cap_events
|
||||
.init(system->maxMasters())
|
||||
.name(name() + ".mshr_cap_events")
|
||||
.desc("number of times MSHR cap was activated")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
mshr_cap_events.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
//software prefetching stats
|
||||
soft_prefetch_mshr_full
|
||||
.init(system->maxMasters())
|
||||
.name(name() + ".soft_prefetch_mshr_full")
|
||||
.desc("number of mshr full events for SW prefetching instrutions")
|
||||
.flags(total | nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < system->maxMasters(); i++) {
|
||||
soft_prefetch_mshr_full.subname(i, system->getMasterName(i));
|
||||
}
|
||||
|
||||
mshr_no_allocate_misses
|
||||
.name(name() +".no_allocate_misses")
|
||||
.desc("Number of misses that were no-allocate")
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
unsigned int
|
||||
BaseCache::drain(Event *de)
|
||||
{
|
||||
int count = memSidePort->drain(de) + cpuSidePort->drain(de);
|
||||
|
||||
// Set status
|
||||
if (count != 0) {
|
||||
drainEvent = de;
|
||||
|
||||
changeState(SimObject::Draining);
|
||||
return count;
|
||||
}
|
||||
|
||||
changeState(SimObject::Drained);
|
||||
return 0;
|
||||
}
|
||||
583
simulators/gem5/src/mem/cache/base.hh
vendored
Normal file
583
simulators/gem5/src/mem/cache/base.hh
vendored
Normal file
@ -0,0 +1,583 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved.
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Steve Reinhardt
|
||||
* Ron Dreslinski
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Declares a basic cache interface BaseCache.
|
||||
*/
|
||||
|
||||
#ifndef __BASE_CACHE_HH__
|
||||
#define __BASE_CACHE_HH__
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/misc.hh"
|
||||
#include "base/statistics.hh"
|
||||
#include "base/trace.hh"
|
||||
#include "base/types.hh"
|
||||
#include "debug/Cache.hh"
|
||||
#include "debug/CachePort.hh"
|
||||
#include "mem/cache/mshr_queue.hh"
|
||||
#include "mem/mem_object.hh"
|
||||
#include "mem/packet.hh"
|
||||
#include "mem/qport.hh"
|
||||
#include "mem/request.hh"
|
||||
#include "params/BaseCache.hh"
|
||||
#include "sim/eventq.hh"
|
||||
#include "sim/full_system.hh"
|
||||
#include "sim/sim_exit.hh"
|
||||
#include "sim/system.hh"
|
||||
|
||||
class MSHR;
|
||||
/**
|
||||
* A basic cache interface. Implements some common functions for speed.
|
||||
*/
|
||||
class BaseCache : public MemObject
|
||||
{
|
||||
/**
|
||||
* Indexes to enumerate the MSHR queues.
|
||||
*/
|
||||
enum MSHRQueueIndex {
|
||||
MSHRQueue_MSHRs,
|
||||
MSHRQueue_WriteBuffer
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Reasons for caches to be blocked.
|
||||
*/
|
||||
enum BlockedCause {
|
||||
Blocked_NoMSHRs = MSHRQueue_MSHRs,
|
||||
Blocked_NoWBBuffers = MSHRQueue_WriteBuffer,
|
||||
Blocked_NoTargets,
|
||||
NUM_BLOCKED_CAUSES
|
||||
};
|
||||
|
||||
/**
|
||||
* Reasons for cache to request a bus.
|
||||
*/
|
||||
enum RequestCause {
|
||||
Request_MSHR = MSHRQueue_MSHRs,
|
||||
Request_WB = MSHRQueue_WriteBuffer,
|
||||
Request_PF,
|
||||
NUM_REQUEST_CAUSES
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* A cache master port is used for the memory-side port of the
|
||||
* cache, and in addition to the basic timing port that only sends
|
||||
* response packets through a transmit list, it also offers the
|
||||
* ability to schedule and send request packets (requests &
|
||||
* writebacks). The send event is scheduled through requestBus,
|
||||
* and the sendDeferredPacket of the timing port is modified to
|
||||
* consider both the transmit list and the requests from the MSHR.
|
||||
*/
|
||||
class CacheMasterPort : public QueuedMasterPort
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Schedule a send of a request packet (from the MSHR). Note
|
||||
* that we could already have a retry or a transmit list of
|
||||
* responses outstanding.
|
||||
*/
|
||||
void requestBus(RequestCause cause, Tick time)
|
||||
{
|
||||
DPRINTF(CachePort, "Asserting bus request for cause %d\n", cause);
|
||||
queue.schedSendEvent(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the transmissions of a response packet at a given
|
||||
* point in time.
|
||||
*
|
||||
* @param pkt response packet
|
||||
* @param when time to send the response
|
||||
*/
|
||||
void respond(PacketPtr pkt, Tick time) {
|
||||
queue.schedSendTiming(pkt, time, true);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
CacheMasterPort(const std::string &_name, BaseCache *_cache,
|
||||
MasterPacketQueue &_queue) :
|
||||
QueuedMasterPort(_name, _cache, _queue)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Memory-side port always snoops.
|
||||
*
|
||||
* @return always true
|
||||
*/
|
||||
virtual bool isSnooping() const { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* A cache slave port is used for the CPU-side port of the cache,
|
||||
* and it is basically a simple timing port that uses a transmit
|
||||
* list for responses to the CPU (or connected master). In
|
||||
* addition, it has the functionality to block the port for
|
||||
* incoming requests. If blocked, the port will issue a retry once
|
||||
* unblocked.
|
||||
*/
|
||||
class CacheSlavePort : public QueuedSlavePort
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/** Do not accept any new requests. */
|
||||
void setBlocked();
|
||||
|
||||
/** Return to normal operation and accept new requests. */
|
||||
void clearBlocked();
|
||||
|
||||
/**
|
||||
* Schedule the transmissions of a response packet at a given
|
||||
* point in time.
|
||||
*
|
||||
* @param pkt response packet
|
||||
* @param when time to send the response
|
||||
*/
|
||||
void respond(PacketPtr pkt, Tick time) {
|
||||
queue.schedSendTiming(pkt, time);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
CacheSlavePort(const std::string &_name, BaseCache *_cache,
|
||||
const std::string &_label);
|
||||
|
||||
/** A normal packet queue used to store responses. */
|
||||
SlavePacketQueue queue;
|
||||
|
||||
bool blocked;
|
||||
|
||||
bool mustSendRetry;
|
||||
|
||||
private:
|
||||
|
||||
EventWrapper<Port, &Port::sendRetry> sendRetryEvent;
|
||||
|
||||
};
|
||||
|
||||
CacheSlavePort *cpuSidePort;
|
||||
CacheMasterPort *memSidePort;
|
||||
|
||||
protected:
|
||||
|
||||
/** Miss status registers */
|
||||
MSHRQueue mshrQueue;
|
||||
|
||||
/** Write/writeback buffer */
|
||||
MSHRQueue writeBuffer;
|
||||
|
||||
MSHR *allocateBufferInternal(MSHRQueue *mq, Addr addr, int size,
|
||||
PacketPtr pkt, Tick time, bool requestBus)
|
||||
{
|
||||
MSHR *mshr = mq->allocate(addr, size, pkt, time, order++);
|
||||
|
||||
if (mq->isFull()) {
|
||||
setBlocked((BlockedCause)mq->index);
|
||||
}
|
||||
|
||||
if (requestBus) {
|
||||
requestMemSideBus((RequestCause)mq->index, time);
|
||||
}
|
||||
|
||||
return mshr;
|
||||
}
|
||||
|
||||
void markInServiceInternal(MSHR *mshr, PacketPtr pkt)
|
||||
{
|
||||
MSHRQueue *mq = mshr->queue;
|
||||
bool wasFull = mq->isFull();
|
||||
mq->markInService(mshr, pkt);
|
||||
if (wasFull && !mq->isFull()) {
|
||||
clearBlocked((BlockedCause)mq->index);
|
||||
}
|
||||
}
|
||||
|
||||
/** Block size of this cache */
|
||||
const unsigned blkSize;
|
||||
|
||||
/**
|
||||
* The latency of a hit in this device.
|
||||
*/
|
||||
int hitLatency;
|
||||
|
||||
/** The number of targets for each MSHR. */
|
||||
const int numTarget;
|
||||
|
||||
/** Do we forward snoops from mem side port through to cpu side port? */
|
||||
bool forwardSnoops;
|
||||
|
||||
/** Is this cache a toplevel cache (e.g. L1, I/O cache). If so we should
|
||||
* never try to forward ownership and similar optimizations to the cpu
|
||||
* side */
|
||||
bool isTopLevel;
|
||||
|
||||
/**
|
||||
* Bit vector of the blocking reasons for the access path.
|
||||
* @sa #BlockedCause
|
||||
*/
|
||||
uint8_t blocked;
|
||||
|
||||
/** Increasing order number assigned to each incoming request. */
|
||||
uint64_t order;
|
||||
|
||||
/** Stores time the cache blocked for statistics. */
|
||||
Tick blockedCycle;
|
||||
|
||||
/** Pointer to the MSHR that has no targets. */
|
||||
MSHR *noTargetMSHR;
|
||||
|
||||
/** The number of misses to trigger an exit event. */
|
||||
Counter missCount;
|
||||
|
||||
/** The drain event. */
|
||||
Event *drainEvent;
|
||||
|
||||
/**
|
||||
* The address range to which the cache responds on the CPU side.
|
||||
* Normally this is all possible memory addresses. */
|
||||
AddrRangeList addrRanges;
|
||||
|
||||
public:
|
||||
/** System we are currently operating in. */
|
||||
System *system;
|
||||
|
||||
// Statistics
|
||||
/**
|
||||
* @addtogroup CacheStatistics
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Number of hits per thread for each type of command. @sa Packet::Command */
|
||||
Stats::Vector hits[MemCmd::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[MemCmd::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[MemCmd::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[MemCmd::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[MemCmd::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[MemCmd::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;
|
||||
|
||||
/** 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[MemCmd::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[MemCmd::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[MemCmd::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[MemCmd::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[MemCmd::NUM_MEM_CMDS];
|
||||
/** Total cycle latency of overall MSHR misses. */
|
||||
Stats::Formula overallMshrUncacheableLatency;
|
||||
|
||||
#if 0
|
||||
/** The total number of MSHR accesses per command and thread. */
|
||||
Stats::Formula mshrAccesses[MemCmd::NUM_MEM_CMDS];
|
||||
/** The total number of demand MSHR accesses. */
|
||||
Stats::Formula demandMshrAccesses;
|
||||
/** The total number of MSHR accesses. */
|
||||
Stats::Formula overallMshrAccesses;
|
||||
#endif
|
||||
|
||||
/** The miss rate in the MSHRs pre command and thread. */
|
||||
Stats::Formula mshrMissRate[MemCmd::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[MemCmd::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[MemCmd::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;
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register stats for this object.
|
||||
*/
|
||||
virtual void regStats();
|
||||
|
||||
public:
|
||||
typedef BaseCacheParams Params;
|
||||
BaseCache(const Params *p);
|
||||
~BaseCache() {}
|
||||
|
||||
virtual void init();
|
||||
|
||||
virtual MasterPort &getMasterPort(const std::string &if_name, int idx = -1);
|
||||
virtual SlavePort &getSlavePort(const std::string &if_name, int idx = -1);
|
||||
|
||||
/**
|
||||
* Query block size of a cache.
|
||||
* @return The block size
|
||||
*/
|
||||
unsigned
|
||||
getBlockSize() const
|
||||
{
|
||||
return blkSize;
|
||||
}
|
||||
|
||||
|
||||
Addr blockAlign(Addr addr) const { return (addr & ~(Addr(blkSize - 1))); }
|
||||
|
||||
|
||||
const AddrRangeList &getAddrRanges() const { return addrRanges; }
|
||||
|
||||
MSHR *allocateMissBuffer(PacketPtr pkt, Tick time, bool requestBus)
|
||||
{
|
||||
assert(!pkt->req->isUncacheable());
|
||||
return allocateBufferInternal(&mshrQueue,
|
||||
blockAlign(pkt->getAddr()), blkSize,
|
||||
pkt, time, requestBus);
|
||||
}
|
||||
|
||||
MSHR *allocateWriteBuffer(PacketPtr pkt, Tick time, bool requestBus)
|
||||
{
|
||||
assert(pkt->isWrite() && !pkt->isRead());
|
||||
return allocateBufferInternal(&writeBuffer,
|
||||
pkt->getAddr(), pkt->getSize(),
|
||||
pkt, time, requestBus);
|
||||
}
|
||||
|
||||
MSHR *allocateUncachedReadBuffer(PacketPtr pkt, Tick time, bool requestBus)
|
||||
{
|
||||
assert(pkt->req->isUncacheable());
|
||||
assert(pkt->isRead());
|
||||
return allocateBufferInternal(&mshrQueue,
|
||||
pkt->getAddr(), pkt->getSize(),
|
||||
pkt, time, requestBus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the cache is blocked for accesses.
|
||||
*/
|
||||
bool isBlocked()
|
||||
{
|
||||
return blocked != 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();
|
||||
cpuSidePort->setBlocked();
|
||||
}
|
||||
blocked |= flag;
|
||||
DPRINTF(Cache,"Blocking for cause %d, mask=%d\n", cause, blocked);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
DPRINTF(Cache,"Unblocking for cause %d, mask=%d\n", cause, blocked);
|
||||
if (blocked == 0) {
|
||||
blocked_cycles[cause] += curTick() - blockedCycle;
|
||||
cpuSidePort->clearBlocked();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 requestMemSideBus(RequestCause cause, Tick time)
|
||||
{
|
||||
memSidePort->requestBus(cause, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the master bus request for the given cause.
|
||||
* @param cause The request reason to clear.
|
||||
*/
|
||||
void deassertMemSideBusRequest(RequestCause cause)
|
||||
{
|
||||
// Obsolete... we no longer signal bus requests explicitly so
|
||||
// we can't deassert them. Leaving this in as a no-op since
|
||||
// the prefetcher calls it to indicate that it no longer wants
|
||||
// to request a prefetch, and someday that might be
|
||||
// interesting again.
|
||||
}
|
||||
|
||||
virtual unsigned int drain(Event *de);
|
||||
|
||||
virtual bool inCache(Addr addr) = 0;
|
||||
|
||||
virtual bool inMissQueue(Addr addr) = 0;
|
||||
|
||||
void incMissCount(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->req->masterId() < system->maxMasters());
|
||||
misses[pkt->cmdToIndex()][pkt->req->masterId()]++;
|
||||
|
||||
if (missCount) {
|
||||
--missCount;
|
||||
if (missCount == 0)
|
||||
exitSimLoop("A cache reached the maximum miss count");
|
||||
}
|
||||
}
|
||||
void incHitCount(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->req->masterId() < system->maxMasters());
|
||||
hits[pkt->cmdToIndex()][pkt->req->masterId()]++;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //__BASE_CACHE_HH__
|
||||
41
simulators/gem5/src/mem/cache/blk.cc
vendored
Normal file
41
simulators/gem5/src/mem/cache/blk.cc
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2007 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 "base/cprintf.hh"
|
||||
#include "mem/cache/blk.hh"
|
||||
|
||||
void
|
||||
CacheBlkPrintWrapper::print(std::ostream &os, int verbosity,
|
||||
const std::string &prefix) const
|
||||
{
|
||||
ccprintf(os, "%sblk %c%c%c\n", prefix,
|
||||
blk->isValid() ? 'V' : '-',
|
||||
blk->isWritable() ? 'E' : '-',
|
||||
blk->isDirty() ? 'M' : '-');
|
||||
}
|
||||
|
||||
292
simulators/gem5/src/mem/cache/blk.hh
vendored
Normal file
292
simulators/gem5/src/mem/cache/blk.hh
vendored
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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 <list>
|
||||
|
||||
#include "base/printable.hh"
|
||||
#include "mem/packet.hh"
|
||||
#include "mem/request.hh"
|
||||
#include "sim/core.hh" // for Tick
|
||||
|
||||
/**
|
||||
* Cache block status bit assignments
|
||||
*/
|
||||
enum CacheBlkStatusBits {
|
||||
/** valid, readable */
|
||||
BlkValid = 0x01,
|
||||
/** write permission */
|
||||
BlkWritable = 0x02,
|
||||
/** read permission (yes, block can be valid but not readable) */
|
||||
BlkReadable = 0x04,
|
||||
/** dirty (modified) */
|
||||
BlkDirty = 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;
|
||||
|
||||
/** whether this block has been touched */
|
||||
bool isTouched;
|
||||
|
||||
/** Number of references to this block since it was brought in. */
|
||||
int refCount;
|
||||
|
||||
/** holds the source requestor ID for this block. */
|
||||
int srcMasterId;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Represents that the indicated thread context has a "lock" on
|
||||
* the block, in the LL/SC sense.
|
||||
*/
|
||||
class Lock {
|
||||
public:
|
||||
int contextId; // locking context
|
||||
|
||||
// check for matching execution context
|
||||
bool matchesContext(Request *req)
|
||||
{
|
||||
return (contextId == req->contextId());
|
||||
}
|
||||
|
||||
Lock(Request *req)
|
||||
: contextId(req->contextId())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** List of thread contexts that have performed a load-locked (LL)
|
||||
* on the block since the last store. */
|
||||
std::list<Lock> lockList;
|
||||
|
||||
public:
|
||||
|
||||
CacheBlk()
|
||||
: asid(-1), tag(0), data(0) ,size(0), status(0), whenReady(0),
|
||||
set(-1), isTouched(false), refCount(0),
|
||||
srcMasterId(Request::invldMasterId)
|
||||
{}
|
||||
|
||||
/**
|
||||
* 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 State needed_bits = BlkWritable | BlkValid;
|
||||
return (status & needed_bits) == needed_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the read permissions of this block. Note that a block
|
||||
* can be valid but not readable if there is an outstanding write
|
||||
* upgrade miss.
|
||||
* @return True if the block is readable.
|
||||
*/
|
||||
bool isReadable() const
|
||||
{
|
||||
const State needed_bits = BlkReadable | BlkValid;
|
||||
return (status & needed_bits) == needed_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that a block is valid.
|
||||
* @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 isDirty() const
|
||||
{
|
||||
return (status & BlkDirty) != 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 wasPrefetched() const
|
||||
{
|
||||
return (status & BlkHWPrefetched) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track the fact that a local locked was issued to the block. If
|
||||
* multiple LLs get issued from the same context we could have
|
||||
* redundant records on the list, but that's OK, as they'll all
|
||||
* get blown away at the next store.
|
||||
*/
|
||||
void trackLoadLocked(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isLLSC());
|
||||
lockList.push_front(Lock(pkt->req));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the list of valid load locks. Should be called whenever
|
||||
* block is written to or invalidated.
|
||||
*/
|
||||
void clearLoadLocks() { lockList.clear(); }
|
||||
|
||||
/**
|
||||
* Handle interaction of load-locked operations and stores.
|
||||
* @return True if write should proceed, false otherwise. Returns
|
||||
* false only in the case of a failed store conditional.
|
||||
*/
|
||||
bool checkWrite(PacketPtr pkt)
|
||||
{
|
||||
Request *req = pkt->req;
|
||||
if (pkt->isLLSC()) {
|
||||
// it's a store conditional... have to check for matching
|
||||
// load locked.
|
||||
bool success = false;
|
||||
|
||||
for (std::list<Lock>::iterator i = lockList.begin();
|
||||
i != lockList.end(); ++i)
|
||||
{
|
||||
if (i->matchesContext(req)) {
|
||||
// it's a store conditional, and as far as the memory
|
||||
// system can tell, the requesting context's lock is
|
||||
// still valid.
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
req->setExtraData(success ? 1 : 0);
|
||||
clearLoadLocks();
|
||||
return success;
|
||||
} else {
|
||||
// for *all* stores (conditional or otherwise) we have to
|
||||
// clear the list of load-locks as they're all invalid now.
|
||||
clearLoadLocks();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple class to provide virtual print() method on cache blocks
|
||||
* without allocating a vtable pointer for every single cache block.
|
||||
* Just wrap the CacheBlk object in an instance of this before passing
|
||||
* to a function that requires a Printable object.
|
||||
*/
|
||||
class CacheBlkPrintWrapper : public Printable
|
||||
{
|
||||
CacheBlk *blk;
|
||||
public:
|
||||
CacheBlkPrintWrapper(CacheBlk *_blk) : blk(_blk) {}
|
||||
virtual ~CacheBlkPrintWrapper() {}
|
||||
void print(std::ostream &o, int verbosity = 0,
|
||||
const std::string &prefix = "") const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //__CACHE_BLK_HH__
|
||||
136
simulators/gem5/src/mem/cache/builder.cc
vendored
Normal file
136
simulators/gem5/src/mem/cache/builder.cc
vendored
Normal file
@ -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
|
||||
* Nathan Binkert
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Simobject instatiation of caches.
|
||||
*/
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "config/the_isa.hh"
|
||||
#include "mem/cache/base.hh"
|
||||
#include "mem/cache/cache.hh"
|
||||
#include "mem/config/cache.hh"
|
||||
#include "params/BaseCache.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
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define BUILD_CACHE(TAGS, tags) \
|
||||
do { \
|
||||
Cache<TAGS> *retval = \
|
||||
new Cache<TAGS>(this, tags); \
|
||||
return retval; \
|
||||
} while (0)
|
||||
|
||||
#define BUILD_CACHE_PANIC(x) do { \
|
||||
panic("%s not compiled into M5", x); \
|
||||
} while (0)
|
||||
|
||||
#if defined(USE_CACHE_FALRU)
|
||||
#define BUILD_FALRU_CACHE do { \
|
||||
FALRU *tags = new FALRU(block_size, size, latency); \
|
||||
BUILD_CACHE(FALRU, tags); \
|
||||
} while (0)
|
||||
#else
|
||||
#define BUILD_FALRU_CACHE BUILD_CACHE_PANIC("falru cache")
|
||||
#endif
|
||||
|
||||
#if defined(USE_CACHE_LRU)
|
||||
#define BUILD_LRU_CACHE do { \
|
||||
LRU *tags = new LRU(numSets, block_size, assoc, latency); \
|
||||
BUILD_CACHE(LRU, tags); \
|
||||
} while (0)
|
||||
#else
|
||||
#define BUILD_LRU_CACHE BUILD_CACHE_PANIC("lru cache")
|
||||
#endif
|
||||
|
||||
#if defined(USE_CACHE_IIC)
|
||||
#define BUILD_IIC_CACHE do { \
|
||||
IIC *tags = new IIC(iic_params); \
|
||||
BUILD_CACHE(IIC, tags); \
|
||||
} while (0)
|
||||
#else
|
||||
#define BUILD_IIC_CACHE BUILD_CACHE_PANIC("iic")
|
||||
#endif
|
||||
|
||||
#define BUILD_CACHES do { \
|
||||
if (repl == NULL) { \
|
||||
if (numSets == 1) { \
|
||||
BUILD_FALRU_CACHE; \
|
||||
} else { \
|
||||
BUILD_LRU_CACHE; \
|
||||
} \
|
||||
} else { \
|
||||
BUILD_IIC_CACHE; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
BaseCache *
|
||||
BaseCacheParams::create()
|
||||
{
|
||||
int numSets = size / (assoc * block_size);
|
||||
if (subblock_size == 0) {
|
||||
subblock_size = block_size;
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
BUILD_CACHES;
|
||||
return NULL;
|
||||
}
|
||||
71
simulators/gem5/src/mem/cache/cache.cc
vendored
Normal file
71
simulators/gem5/src/mem/cache/cache.cc
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#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
|
||||
|
||||
#include "mem/cache/cache_impl.hh"
|
||||
|
||||
// Template Instantiations
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
|
||||
#if defined(USE_CACHE_FALRU)
|
||||
template class Cache<FALRU>;
|
||||
#endif
|
||||
|
||||
#if defined(USE_CACHE_IIC)
|
||||
template class Cache<IIC>;
|
||||
#endif
|
||||
|
||||
#if defined(USE_CACHE_LRU)
|
||||
template class Cache<LRU>;
|
||||
#endif
|
||||
|
||||
#endif //DOXYGEN_SHOULD_SKIP_THIS
|
||||
382
simulators/gem5/src/mem/cache/cache.hh
vendored
Normal file
382
simulators/gem5/src/mem/cache/cache.hh
vendored
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved.
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Ron Dreslinski
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Describes a cache based on template policies.
|
||||
*/
|
||||
|
||||
#ifndef __CACHE_HH__
|
||||
#define __CACHE_HH__
|
||||
|
||||
#include "base/misc.hh" // fatal, panic, and warn
|
||||
#include "mem/cache/base.hh"
|
||||
#include "mem/cache/blk.hh"
|
||||
#include "mem/cache/mshr.hh"
|
||||
#include "sim/eventq.hh"
|
||||
|
||||
//Forward decleration
|
||||
class BasePrefetcher;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
template <class TagStore>
|
||||
class Cache : public BaseCache
|
||||
{
|
||||
public:
|
||||
/** Define the type of cache block to use. */
|
||||
typedef typename TagStore::BlkType BlkType;
|
||||
/** A typedef for a list of BlkType pointers. */
|
||||
typedef typename TagStore::BlkList BlkList;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The CPU-side port extends the base cache slave port with access
|
||||
* functions for functional, atomic and timing requests.
|
||||
*/
|
||||
class CpuSidePort : public CacheSlavePort
|
||||
{
|
||||
private:
|
||||
|
||||
// a pointer to our specific cache implementation
|
||||
Cache<TagStore> *cache;
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool recvTimingSnoopResp(PacketPtr pkt);
|
||||
|
||||
virtual bool recvTimingReq(PacketPtr pkt);
|
||||
|
||||
virtual Tick recvAtomic(PacketPtr pkt);
|
||||
|
||||
virtual void recvFunctional(PacketPtr pkt);
|
||||
|
||||
virtual unsigned deviceBlockSize() const
|
||||
{ return cache->getBlockSize(); }
|
||||
|
||||
virtual AddrRangeList getAddrRanges();
|
||||
|
||||
public:
|
||||
|
||||
CpuSidePort(const std::string &_name, Cache<TagStore> *_cache,
|
||||
const std::string &_label);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Override the default behaviour of sendDeferredPacket to enable
|
||||
* the memory-side cache port to also send requests based on the
|
||||
* current MSHR status. This queue has a pointer to our specific
|
||||
* cache implementation and is used by the MemSidePort.
|
||||
*/
|
||||
class MemSidePacketQueue : public MasterPacketQueue
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
Cache<TagStore> &cache;
|
||||
|
||||
public:
|
||||
|
||||
MemSidePacketQueue(Cache<TagStore> &cache, MasterPort &port,
|
||||
const std::string &label) :
|
||||
MasterPacketQueue(cache, port, label), cache(cache) { }
|
||||
|
||||
/**
|
||||
* Override the normal sendDeferredPacket and do not only
|
||||
* consider the transmit list (used for responses), but also
|
||||
* requests.
|
||||
*/
|
||||
virtual void sendDeferredPacket();
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The memory-side port extends the base cache master port with
|
||||
* access functions for functional, atomic and timing snoops.
|
||||
*/
|
||||
class MemSidePort : public CacheMasterPort
|
||||
{
|
||||
private:
|
||||
|
||||
/** The cache-specific queue. */
|
||||
MemSidePacketQueue _queue;
|
||||
|
||||
// a pointer to our specific cache implementation
|
||||
Cache<TagStore> *cache;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void recvTimingSnoopReq(PacketPtr pkt);
|
||||
|
||||
virtual bool recvTimingResp(PacketPtr pkt);
|
||||
|
||||
virtual Tick recvAtomicSnoop(PacketPtr pkt);
|
||||
|
||||
virtual void recvFunctionalSnoop(PacketPtr pkt);
|
||||
|
||||
virtual unsigned deviceBlockSize() const
|
||||
{ return cache->getBlockSize(); }
|
||||
|
||||
public:
|
||||
|
||||
MemSidePort(const std::string &_name, Cache<TagStore> *_cache,
|
||||
const std::string &_label);
|
||||
};
|
||||
|
||||
/** Tag and data Storage */
|
||||
TagStore *tags;
|
||||
|
||||
/** Prefetcher */
|
||||
BasePrefetcher *prefetcher;
|
||||
|
||||
/** Temporary cache block for occasional transitory use */
|
||||
BlkType *tempBlock;
|
||||
|
||||
/**
|
||||
* This cache should allocate a block on a line-sized write miss.
|
||||
*/
|
||||
const bool doFastWrites;
|
||||
|
||||
/**
|
||||
* Notify the prefetcher on every access, not just misses.
|
||||
*/
|
||||
const bool prefetchOnAccess;
|
||||
|
||||
/**
|
||||
* @todo this is a temporary workaround until the 4-phase code is committed.
|
||||
* upstream caches need this packet until true is returned, so hold it for
|
||||
* deletion until a subsequent call
|
||||
*/
|
||||
std::vector<PacketPtr> pendingDelete;
|
||||
|
||||
/**
|
||||
* Does all the processing necessary to perform the provided request.
|
||||
* @param pkt The memory request to perform.
|
||||
* @param lat The latency of the access.
|
||||
* @param writebacks List for any writebacks that need to be performed.
|
||||
* @param update True if the replacement data should be updated.
|
||||
* @return Boolean indicating whether the request was satisfied.
|
||||
*/
|
||||
bool access(PacketPtr pkt, BlkType *&blk,
|
||||
int &lat, PacketList &writebacks);
|
||||
|
||||
/**
|
||||
*Handle doing the Compare and Swap function for SPARC.
|
||||
*/
|
||||
void cmpAndSwap(BlkType *blk, PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Find a block frame for new block at address addr, assuming that
|
||||
* the block is not currently in the cache. Append writebacks if
|
||||
* any to provided packet list. Return free block frame. May
|
||||
* return NULL if there are no replaceable blocks at the moment.
|
||||
*/
|
||||
BlkType *allocateBlock(Addr addr, PacketList &writebacks);
|
||||
|
||||
/**
|
||||
* Populates a cache block and handles all outstanding requests for the
|
||||
* satisfied fill request. This version takes two memory requests. One
|
||||
* contains the fill data, the other is an optional target to satisfy.
|
||||
* @param pkt The memory request with the fill data.
|
||||
* @param blk The cache block if it already exists.
|
||||
* @param writebacks List for any writebacks that need to be performed.
|
||||
* @return Pointer to the new cache block.
|
||||
*/
|
||||
BlkType *handleFill(PacketPtr pkt, BlkType *blk,
|
||||
PacketList &writebacks);
|
||||
|
||||
void satisfyCpuSideRequest(PacketPtr pkt, BlkType *blk,
|
||||
bool deferred_response = false,
|
||||
bool pending_downgrade = false);
|
||||
bool satisfyMSHR(MSHR *mshr, PacketPtr pkt, BlkType *blk);
|
||||
|
||||
void doTimingSupplyResponse(PacketPtr req_pkt, uint8_t *blk_data,
|
||||
bool already_copied, bool pending_inval);
|
||||
|
||||
/**
|
||||
* Sets the blk to the new state.
|
||||
* @param blk The cache block being snooped.
|
||||
* @param new_state The new coherence state for the block.
|
||||
*/
|
||||
void handleSnoop(PacketPtr ptk, BlkType *blk,
|
||||
bool is_timing, bool is_deferred, bool pending_inval);
|
||||
|
||||
/**
|
||||
* Create a writeback request for the given block.
|
||||
* @param blk The block to writeback.
|
||||
* @return The writeback request for the block.
|
||||
*/
|
||||
PacketPtr writebackBlk(BlkType *blk);
|
||||
|
||||
public:
|
||||
/** Instantiates a basic cache object. */
|
||||
Cache(const Params *p, TagStore *tags);
|
||||
|
||||
void regStats();
|
||||
|
||||
/**
|
||||
* Performs the access specified by the request.
|
||||
* @param pkt The request to perform.
|
||||
* @return The result of the access.
|
||||
*/
|
||||
bool timingAccess(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Performs the access specified by the request.
|
||||
* @param pkt The request to perform.
|
||||
* @return The result of the access.
|
||||
*/
|
||||
Tick atomicAccess(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Performs the access specified by the request.
|
||||
* @param pkt The request to perform.
|
||||
* @param fromCpuSide from the CPU side port or the memory side port
|
||||
*/
|
||||
void functionalAccess(PacketPtr pkt, bool fromCpuSide);
|
||||
|
||||
/**
|
||||
* Handles a response (cache line fill/write ack) from the bus.
|
||||
* @param pkt The request being responded to.
|
||||
*/
|
||||
void handleResponse(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Snoops bus transactions to maintain coherence.
|
||||
* @param pkt The current bus transaction.
|
||||
*/
|
||||
void snoopTiming(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Snoop for the provided request in the cache and return the estimated
|
||||
* time of completion.
|
||||
* @param pkt The memory request to snoop
|
||||
* @return The estimated completion time.
|
||||
*/
|
||||
Tick snoopAtomic(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Squash all requests associated with specified thread.
|
||||
* intended for use by I-cache.
|
||||
* @param threadNum The thread to squash.
|
||||
*/
|
||||
void squash(int threadNum);
|
||||
|
||||
/**
|
||||
* Generate an appropriate downstream bus request packet for the
|
||||
* given parameters.
|
||||
* @param cpu_pkt The upstream request that needs to be satisfied.
|
||||
* @param blk The block currently in the cache corresponding to
|
||||
* cpu_pkt (NULL if none).
|
||||
* @param needsExclusive Indicates that an exclusive copy is required
|
||||
* even if the request in cpu_pkt doesn't indicate that.
|
||||
* @return A new Packet containing the request, or NULL if the
|
||||
* current request in cpu_pkt should just be forwarded on.
|
||||
*/
|
||||
PacketPtr getBusPacket(PacketPtr cpu_pkt, BlkType *blk,
|
||||
bool needsExclusive);
|
||||
|
||||
/**
|
||||
* Return the next MSHR to service, either a pending miss from the
|
||||
* mshrQueue, a buffered write from the write buffer, or something
|
||||
* from the prefetcher. This function is responsible for
|
||||
* prioritizing among those sources on the fly.
|
||||
*/
|
||||
MSHR *getNextMSHR();
|
||||
|
||||
/**
|
||||
* Selects an outstanding request to service. Called when the
|
||||
* cache gets granted the downstream bus in timing mode.
|
||||
* @return The request to service, NULL if none found.
|
||||
*/
|
||||
PacketPtr getTimingPacket();
|
||||
|
||||
/**
|
||||
* 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 pkt The request that was sent on the bus.
|
||||
*/
|
||||
void markInService(MSHR *mshr, PacketPtr pkt = 0);
|
||||
|
||||
/**
|
||||
* Return whether there are any outstanding misses.
|
||||
*/
|
||||
bool outstandingMisses() const
|
||||
{
|
||||
return mshrQueue.allocated != 0;
|
||||
}
|
||||
|
||||
CacheBlk *findBlock(Addr addr) {
|
||||
return tags->findBlock(addr);
|
||||
}
|
||||
|
||||
bool inCache(Addr addr) {
|
||||
return (tags->findBlock(addr) != 0);
|
||||
}
|
||||
|
||||
bool inMissQueue(Addr addr) {
|
||||
return (mshrQueue.findMatch(addr) != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find next request ready time from among possible sources.
|
||||
*/
|
||||
Tick nextMSHRReadyTime();
|
||||
|
||||
/** serialize the state of the caches
|
||||
* We currently don't support checkpointing cache state, so this panics.
|
||||
*/
|
||||
virtual void serialize(std::ostream &os);
|
||||
void unserialize(Checkpoint *cp, const std::string §ion);
|
||||
};
|
||||
|
||||
#endif // __CACHE_HH__
|
||||
1737
simulators/gem5/src/mem/cache/cache_impl.hh
vendored
Normal file
1737
simulators/gem5/src/mem/cache/cache_impl.hh
vendored
Normal file
File diff suppressed because it is too large
Load Diff
463
simulators/gem5/src/mem/cache/mshr.cc
vendored
Normal file
463
simulators/gem5/src/mem/cache/mshr.cc
vendored
Normal file
@ -0,0 +1,463 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2005 The Regents of The University of Michigan
|
||||
* Copyright (c) 2010 Advanced Micro Devices, Inc.
|
||||
* 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 <algorithm>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/misc.hh"
|
||||
#include "base/types.hh"
|
||||
#include "debug/Cache.hh"
|
||||
#include "mem/cache/cache.hh"
|
||||
#include "mem/cache/mshr.hh"
|
||||
#include "sim/core.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
MSHR::MSHR()
|
||||
{
|
||||
inService = false;
|
||||
ntargets = 0;
|
||||
threadNum = InvalidThreadID;
|
||||
targets = new TargetList();
|
||||
deferredTargets = new TargetList();
|
||||
}
|
||||
|
||||
|
||||
MSHR::TargetList::TargetList()
|
||||
: needsExclusive(false), hasUpgrade(false)
|
||||
{}
|
||||
|
||||
|
||||
inline void
|
||||
MSHR::TargetList::add(PacketPtr pkt, Tick readyTime,
|
||||
Counter order, Target::Source source, bool markPending)
|
||||
{
|
||||
if (source != Target::FromSnoop) {
|
||||
if (pkt->needsExclusive()) {
|
||||
needsExclusive = true;
|
||||
}
|
||||
|
||||
// StoreCondReq is effectively an upgrade if it's in an MSHR
|
||||
// since it would have been failed already if we didn't have a
|
||||
// read-only copy
|
||||
if (pkt->isUpgrade() || pkt->cmd == MemCmd::StoreCondReq) {
|
||||
hasUpgrade = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (markPending) {
|
||||
MSHR *mshr = dynamic_cast<MSHR*>(pkt->senderState);
|
||||
if (mshr != NULL) {
|
||||
assert(!mshr->downstreamPending);
|
||||
mshr->downstreamPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
push_back(Target(pkt, readyTime, order, source, markPending));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
replaceUpgrade(PacketPtr pkt)
|
||||
{
|
||||
if (pkt->cmd == MemCmd::UpgradeReq) {
|
||||
pkt->cmd = MemCmd::ReadExReq;
|
||||
DPRINTF(Cache, "Replacing UpgradeReq with ReadExReq\n");
|
||||
} else if (pkt->cmd == MemCmd::SCUpgradeReq) {
|
||||
pkt->cmd = MemCmd::SCUpgradeFailReq;
|
||||
DPRINTF(Cache, "Replacing SCUpgradeReq with SCUpgradeFailReq\n");
|
||||
} else if (pkt->cmd == MemCmd::StoreCondReq) {
|
||||
pkt->cmd = MemCmd::StoreCondFailReq;
|
||||
DPRINTF(Cache, "Replacing StoreCondReq with StoreCondFailReq\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MSHR::TargetList::replaceUpgrades()
|
||||
{
|
||||
if (!hasUpgrade)
|
||||
return;
|
||||
|
||||
Iterator end_i = end();
|
||||
for (Iterator i = begin(); i != end_i; ++i) {
|
||||
replaceUpgrade(i->pkt);
|
||||
}
|
||||
|
||||
hasUpgrade = false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MSHR::TargetList::clearDownstreamPending()
|
||||
{
|
||||
Iterator end_i = end();
|
||||
for (Iterator i = begin(); i != end_i; ++i) {
|
||||
if (i->markedPending) {
|
||||
MSHR *mshr = dynamic_cast<MSHR*>(i->pkt->senderState);
|
||||
if (mshr != NULL) {
|
||||
mshr->clearDownstreamPending();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MSHR::TargetList::checkFunctional(PacketPtr pkt)
|
||||
{
|
||||
Iterator end_i = end();
|
||||
for (Iterator i = begin(); i != end_i; ++i) {
|
||||
if (pkt->checkFunctional(i->pkt)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MSHR::TargetList::
|
||||
print(std::ostream &os, int verbosity, const std::string &prefix) const
|
||||
{
|
||||
ConstIterator end_i = end();
|
||||
for (ConstIterator i = begin(); i != end_i; ++i) {
|
||||
const char *s;
|
||||
switch (i->source) {
|
||||
case Target::FromCPU:
|
||||
s = "FromCPU";
|
||||
break;
|
||||
case Target::FromSnoop:
|
||||
s = "FromSnoop";
|
||||
break;
|
||||
case Target::FromPrefetcher:
|
||||
s = "FromPrefetcher";
|
||||
break;
|
||||
default:
|
||||
s = "";
|
||||
break;
|
||||
}
|
||||
ccprintf(os, "%s%s: ", prefix, s);
|
||||
i->pkt->print(os, verbosity, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MSHR::allocate(Addr _addr, int _size, PacketPtr target,
|
||||
Tick whenReady, Counter _order)
|
||||
{
|
||||
addr = _addr;
|
||||
size = _size;
|
||||
readyTime = whenReady;
|
||||
order = _order;
|
||||
assert(target);
|
||||
isForward = false;
|
||||
_isUncacheable = target->req->isUncacheable();
|
||||
inService = false;
|
||||
downstreamPending = false;
|
||||
threadNum = 0;
|
||||
ntargets = 1;
|
||||
assert(targets->isReset());
|
||||
// Don't know of a case where we would allocate a new MSHR for a
|
||||
// snoop (mem-side request), so set source according to request here
|
||||
Target::Source source = (target->cmd == MemCmd::HardPFReq) ?
|
||||
Target::FromPrefetcher : Target::FromCPU;
|
||||
targets->add(target, whenReady, _order, source, true);
|
||||
assert(deferredTargets->isReset());
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MSHR::clearDownstreamPending()
|
||||
{
|
||||
assert(downstreamPending);
|
||||
downstreamPending = false;
|
||||
// recursively clear flag on any MSHRs we will be forwarding
|
||||
// responses to
|
||||
targets->clearDownstreamPending();
|
||||
}
|
||||
|
||||
bool
|
||||
MSHR::markInService(PacketPtr pkt)
|
||||
{
|
||||
assert(!inService);
|
||||
if (isForwardNoResponse()) {
|
||||
// we just forwarded the request packet & don't expect a
|
||||
// response, so get rid of it
|
||||
assert(getNumTargets() == 1);
|
||||
popTarget();
|
||||
return true;
|
||||
}
|
||||
inService = true;
|
||||
pendingDirty = (targets->needsExclusive ||
|
||||
(!pkt->sharedAsserted() && pkt->memInhibitAsserted()));
|
||||
postInvalidate = postDowngrade = false;
|
||||
|
||||
if (!downstreamPending) {
|
||||
// let upstream caches know that the request has made it to a
|
||||
// level where it's going to get a response
|
||||
targets->clearDownstreamPending();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MSHR::deallocate()
|
||||
{
|
||||
assert(targets->empty());
|
||||
targets->resetFlags();
|
||||
assert(deferredTargets->isReset());
|
||||
assert(ntargets == 0);
|
||||
inService = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a target to an MSHR
|
||||
*/
|
||||
void
|
||||
MSHR::allocateTarget(PacketPtr pkt, Tick whenReady, Counter _order)
|
||||
{
|
||||
// if there's a request already in service for this MSHR, we will
|
||||
// have to defer the new target until after the response if any of
|
||||
// the following are true:
|
||||
// - there are other targets already deferred
|
||||
// - there's a pending invalidate to be applied after the response
|
||||
// comes back (but before this target is processed)
|
||||
// - this target requires an exclusive block and either we're not
|
||||
// getting an exclusive block back or we have already snooped
|
||||
// another read request that will downgrade our exclusive block
|
||||
// to shared
|
||||
|
||||
// assume we'd never issue a prefetch when we've got an
|
||||
// outstanding miss
|
||||
assert(pkt->cmd != MemCmd::HardPFReq);
|
||||
|
||||
if (inService &&
|
||||
(!deferredTargets->empty() || hasPostInvalidate() ||
|
||||
(pkt->needsExclusive() &&
|
||||
(!isPendingDirty() || hasPostDowngrade() || isForward)))) {
|
||||
// need to put on deferred list
|
||||
if (hasPostInvalidate())
|
||||
replaceUpgrade(pkt);
|
||||
deferredTargets->add(pkt, whenReady, _order, Target::FromCPU, true);
|
||||
} else {
|
||||
// No request outstanding, or still OK to append to
|
||||
// outstanding request: append to regular target list. Only
|
||||
// mark pending if current request hasn't been issued yet
|
||||
// (isn't in service).
|
||||
targets->add(pkt, whenReady, _order, Target::FromCPU, !inService);
|
||||
}
|
||||
|
||||
++ntargets;
|
||||
}
|
||||
|
||||
bool
|
||||
MSHR::handleSnoop(PacketPtr pkt, Counter _order)
|
||||
{
|
||||
if (!inService || (pkt->isExpressSnoop() && downstreamPending)) {
|
||||
// Request has not been issued yet, or it's been issued
|
||||
// locally but is buffered unissued at some downstream cache
|
||||
// which is forwarding us this snoop. Either way, the packet
|
||||
// we're snooping logically precedes this MSHR's request, so
|
||||
// the snoop has no impact on the MSHR, but must be processed
|
||||
// in the standard way by the cache. The only exception is
|
||||
// that if we're an L2+ cache buffering an UpgradeReq from a
|
||||
// higher-level cache, and the snoop is invalidating, then our
|
||||
// buffered upgrades must be converted to read exclusives,
|
||||
// since the upper-level cache no longer has a valid copy.
|
||||
// That is, even though the upper-level cache got out on its
|
||||
// local bus first, some other invalidating transaction
|
||||
// reached the global bus before the upgrade did.
|
||||
if (pkt->needsExclusive()) {
|
||||
targets->replaceUpgrades();
|
||||
deferredTargets->replaceUpgrades();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// From here on down, the request issued by this MSHR logically
|
||||
// precedes the request we're snooping.
|
||||
if (pkt->needsExclusive()) {
|
||||
// snooped request still precedes the re-request we'll have to
|
||||
// issue for deferred targets, if any...
|
||||
deferredTargets->replaceUpgrades();
|
||||
}
|
||||
|
||||
if (hasPostInvalidate()) {
|
||||
// a prior snoop has already appended an invalidation, so
|
||||
// logically we don't have the block anymore; no need for
|
||||
// further snooping.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isPendingDirty() || pkt->isInvalidate()) {
|
||||
// We need to save and replay the packet in two cases:
|
||||
// 1. We're awaiting an exclusive copy, so ownership is pending,
|
||||
// and we need to respond after we receive data.
|
||||
// 2. It's an invalidation (e.g., UpgradeReq), and we need
|
||||
// to forward the snoop up the hierarchy after the current
|
||||
// transaction completes.
|
||||
|
||||
// Actual target device (typ. a memory) will delete the
|
||||
// packet on reception, so we need to save a copy here.
|
||||
PacketPtr cp_pkt = new Packet(pkt, true);
|
||||
targets->add(cp_pkt, curTick(), _order, Target::FromSnoop,
|
||||
downstreamPending && targets->needsExclusive);
|
||||
++ntargets;
|
||||
|
||||
if (isPendingDirty()) {
|
||||
pkt->assertMemInhibit();
|
||||
pkt->setSupplyExclusive();
|
||||
}
|
||||
|
||||
if (pkt->needsExclusive()) {
|
||||
// This transaction will take away our pending copy
|
||||
postInvalidate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pkt->needsExclusive()) {
|
||||
// This transaction will get a read-shared copy, downgrading
|
||||
// our copy if we had an exclusive one
|
||||
postDowngrade = true;
|
||||
pkt->assertShared();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MSHR::promoteDeferredTargets()
|
||||
{
|
||||
assert(targets->empty());
|
||||
if (deferredTargets->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// swap targets & deferredTargets lists
|
||||
TargetList *tmp = targets;
|
||||
targets = deferredTargets;
|
||||
deferredTargets = tmp;
|
||||
|
||||
assert(targets->size() == ntargets);
|
||||
|
||||
// clear deferredTargets flags
|
||||
deferredTargets->resetFlags();
|
||||
|
||||
order = targets->front().order;
|
||||
readyTime = std::max(curTick(), targets->front().readyTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MSHR::handleFill(Packet *pkt, CacheBlk *blk)
|
||||
{
|
||||
if (!pkt->sharedAsserted()
|
||||
&& !(hasPostInvalidate() || hasPostDowngrade())
|
||||
&& deferredTargets->needsExclusive) {
|
||||
// We got an exclusive response, but we have deferred targets
|
||||
// which are waiting to request an exclusive copy (not because
|
||||
// of a pending invalidate). This can happen if the original
|
||||
// request was for a read-only (non-exclusive) block, but we
|
||||
// got an exclusive copy anyway because of the E part of the
|
||||
// MOESI/MESI protocol. Since we got the exclusive copy
|
||||
// there's no need to defer the targets, so move them up to
|
||||
// the regular target list.
|
||||
assert(!targets->needsExclusive);
|
||||
targets->needsExclusive = true;
|
||||
// if any of the deferred targets were upper-level cache
|
||||
// requests marked downstreamPending, need to clear that
|
||||
assert(!downstreamPending); // not pending here anymore
|
||||
deferredTargets->clearDownstreamPending();
|
||||
// this clears out deferredTargets too
|
||||
targets->splice(targets->end(), *deferredTargets);
|
||||
deferredTargets->resetFlags();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MSHR::checkFunctional(PacketPtr pkt)
|
||||
{
|
||||
// For printing, we treat the MSHR as a whole as single entity.
|
||||
// For other requests, we iterate over the individual targets
|
||||
// since that's where the actual data lies.
|
||||
if (pkt->isPrint()) {
|
||||
pkt->checkFunctional(this, addr, size, NULL);
|
||||
return false;
|
||||
} else {
|
||||
return (targets->checkFunctional(pkt) ||
|
||||
deferredTargets->checkFunctional(pkt));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MSHR::print(std::ostream &os, int verbosity, const std::string &prefix) const
|
||||
{
|
||||
ccprintf(os, "%s[%x:%x] %s %s %s state: %s %s %s %s\n",
|
||||
prefix, addr, addr+size-1,
|
||||
isForward ? "Forward" : "",
|
||||
isForwardNoResponse() ? "ForwNoResp" : "",
|
||||
needsExclusive() ? "Excl" : "",
|
||||
_isUncacheable ? "Unc" : "",
|
||||
inService ? "InSvc" : "",
|
||||
downstreamPending ? "DwnPend" : "",
|
||||
hasPostInvalidate() ? "PostInv" : "",
|
||||
hasPostDowngrade() ? "PostDowngr" : "");
|
||||
|
||||
ccprintf(os, "%s Targets:\n", prefix);
|
||||
targets->print(os, verbosity, prefix + " ");
|
||||
if (!deferredTargets->empty()) {
|
||||
ccprintf(os, "%s Deferred Targets:\n", prefix);
|
||||
deferredTargets->print(os, verbosity, prefix + " ");
|
||||
}
|
||||
}
|
||||
|
||||
MSHR::~MSHR()
|
||||
{
|
||||
}
|
||||
283
simulators/gem5/src/mem/cache/mshr.hh
vendored
Normal file
283
simulators/gem5/src/mem/cache/mshr.hh
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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 <list>
|
||||
|
||||
#include "base/printable.hh"
|
||||
#include "mem/packet.hh"
|
||||
|
||||
class CacheBlk;
|
||||
class MSHRQueue;
|
||||
|
||||
/**
|
||||
* 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 Packet::SenderState, public Printable
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
class Target {
|
||||
public:
|
||||
|
||||
enum Source {
|
||||
FromCPU,
|
||||
FromSnoop,
|
||||
FromPrefetcher
|
||||
};
|
||||
|
||||
Tick recvTime; //!< Time when request was received (for stats)
|
||||
Tick readyTime; //!< Time when request is ready to be serviced
|
||||
Counter order; //!< Global order (for memory consistency mgmt)
|
||||
PacketPtr pkt; //!< Pending request packet.
|
||||
Source source; //!< Did request come from cpu, memory, or prefetcher?
|
||||
bool markedPending; //!< Did we mark upstream MSHR
|
||||
//!< as downstreamPending?
|
||||
|
||||
Target(PacketPtr _pkt, Tick _readyTime, Counter _order,
|
||||
Source _source, bool _markedPending)
|
||||
: recvTime(curTick()), readyTime(_readyTime), order(_order),
|
||||
pkt(_pkt), source(_source), markedPending(_markedPending)
|
||||
{}
|
||||
};
|
||||
|
||||
class TargetList : public std::list<Target> {
|
||||
/** Target list iterator. */
|
||||
typedef std::list<Target>::iterator Iterator;
|
||||
typedef std::list<Target>::const_iterator ConstIterator;
|
||||
|
||||
public:
|
||||
bool needsExclusive;
|
||||
bool hasUpgrade;
|
||||
|
||||
TargetList();
|
||||
void resetFlags() { needsExclusive = hasUpgrade = false; }
|
||||
bool isReset() { return !needsExclusive && !hasUpgrade; }
|
||||
void add(PacketPtr pkt, Tick readyTime, Counter order,
|
||||
Target::Source source, bool markPending);
|
||||
void replaceUpgrades();
|
||||
void clearDownstreamPending();
|
||||
bool checkFunctional(PacketPtr pkt);
|
||||
void print(std::ostream &os, int verbosity,
|
||||
const std::string &prefix) const;
|
||||
};
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Pointer to queue containing this MSHR. */
|
||||
MSHRQueue *queue;
|
||||
|
||||
/** Cycle when ready to issue */
|
||||
Tick readyTime;
|
||||
|
||||
/** Order number assigned by the miss queue. */
|
||||
Counter order;
|
||||
|
||||
/** Address of the request. */
|
||||
Addr addr;
|
||||
|
||||
/** Size of the request. */
|
||||
int size;
|
||||
|
||||
/** True if the request has been sent to the bus. */
|
||||
bool inService;
|
||||
|
||||
/** True if the request is just a simple forward from an upper level */
|
||||
bool isForward;
|
||||
|
||||
/** True if we need to get an exclusive copy of the block. */
|
||||
bool needsExclusive() const { return targets->needsExclusive; }
|
||||
|
||||
/** True if the request is uncacheable */
|
||||
bool _isUncacheable;
|
||||
|
||||
bool downstreamPending;
|
||||
|
||||
/** The pending* and post* flags are only valid if inService is
|
||||
* true. Using the accessor functions lets us detect if these
|
||||
* flags are accessed improperly.
|
||||
*/
|
||||
|
||||
/** Will we have a dirty copy after this request? */
|
||||
bool pendingDirty;
|
||||
bool isPendingDirty() const {
|
||||
assert(inService); return pendingDirty;
|
||||
}
|
||||
|
||||
/** Did we snoop an invalidate while waiting for data? */
|
||||
bool postInvalidate;
|
||||
bool hasPostInvalidate() const {
|
||||
assert(inService); return postInvalidate;
|
||||
}
|
||||
|
||||
/** Did we snoop a read while waiting for data? */
|
||||
bool postDowngrade;
|
||||
bool hasPostDowngrade() const {
|
||||
assert(inService); return postDowngrade;
|
||||
}
|
||||
|
||||
/** Thread number of the miss. */
|
||||
ThreadID threadNum;
|
||||
/** The number of currently allocated targets. */
|
||||
unsigned short ntargets;
|
||||
|
||||
|
||||
/** Data buffer (if needed). Currently used only for pending
|
||||
* upgrade handling. */
|
||||
uint8_t *data;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
TargetList *deferredTargets;
|
||||
|
||||
public:
|
||||
|
||||
bool isUncacheable() { return _isUncacheable; }
|
||||
|
||||
/**
|
||||
* 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 pkt The original miss.
|
||||
*/
|
||||
void allocate(Addr addr, int size, PacketPtr pkt,
|
||||
Tick when, Counter _order);
|
||||
|
||||
bool markInService(PacketPtr pkt);
|
||||
|
||||
void clearDownstreamPending();
|
||||
|
||||
/**
|
||||
* Mark this MSHR as free.
|
||||
*/
|
||||
void deallocate();
|
||||
|
||||
/**
|
||||
* Add a request to the list of targets.
|
||||
* @param target The target.
|
||||
*/
|
||||
void allocateTarget(PacketPtr target, Tick when, Counter order);
|
||||
bool handleSnoop(PacketPtr target, Counter order);
|
||||
|
||||
/** A simple constructor. */
|
||||
MSHR();
|
||||
/** A simple destructor. */
|
||||
~MSHR();
|
||||
|
||||
/**
|
||||
* Returns the current number of allocated targets.
|
||||
* @return The current number of allocated targets.
|
||||
*/
|
||||
int getNumTargets() const { return ntargets; }
|
||||
|
||||
/**
|
||||
* Returns a pointer to the target list.
|
||||
* @return a pointer to the target list.
|
||||
*/
|
||||
TargetList *getTargetList() { return targets; }
|
||||
|
||||
/**
|
||||
* Returns true if there are targets left.
|
||||
* @return true if there are targets
|
||||
*/
|
||||
bool hasTargets() const { return !targets->empty(); }
|
||||
|
||||
/**
|
||||
* Returns a reference to the first target.
|
||||
* @return A pointer to the first target.
|
||||
*/
|
||||
Target *getTarget() const
|
||||
{
|
||||
assert(hasTargets());
|
||||
return &targets->front();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop first target.
|
||||
*/
|
||||
void popTarget()
|
||||
{
|
||||
--ntargets;
|
||||
targets->pop_front();
|
||||
}
|
||||
|
||||
bool isForwardNoResponse() const
|
||||
{
|
||||
if (getNumTargets() != 1)
|
||||
return false;
|
||||
Target *tgt = getTarget();
|
||||
return tgt->source == Target::FromCPU && !tgt->pkt->needsResponse();
|
||||
}
|
||||
|
||||
bool promoteDeferredTargets();
|
||||
|
||||
void handleFill(Packet *pkt, CacheBlk *blk);
|
||||
|
||||
bool checkFunctional(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Prints the contents of this MSHR for debugging.
|
||||
*/
|
||||
void print(std::ostream &os,
|
||||
int verbosity = 0,
|
||||
const std::string &prefix = "") const;
|
||||
};
|
||||
|
||||
#endif //__MSHR_HH__
|
||||
247
simulators/gem5/src/mem/cache/mshr_queue.cc
vendored
Normal file
247
simulators/gem5/src/mem/cache/mshr_queue.cc
vendored
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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 MSHRQueue class functions.
|
||||
*/
|
||||
|
||||
#include "mem/cache/mshr_queue.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
MSHRQueue::MSHRQueue(const std::string &_label,
|
||||
int num_entries, int reserve, int _index)
|
||||
: label(_label),
|
||||
numEntries(num_entries + reserve - 1), numReserve(reserve),
|
||||
index(_index)
|
||||
{
|
||||
allocated = 0;
|
||||
inServiceEntries = 0;
|
||||
registers = new MSHR[numEntries];
|
||||
for (int i = 0; i < numEntries; ++i) {
|
||||
registers[i].queue = this;
|
||||
freeList.push_back(®isters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
MSHRQueue::~MSHRQueue()
|
||||
{
|
||||
delete [] registers;
|
||||
}
|
||||
|
||||
MSHR *
|
||||
MSHRQueue::findMatch(Addr addr) 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, 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;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MSHRQueue::checkFunctional(PacketPtr pkt, Addr blk_addr)
|
||||
{
|
||||
pkt->pushLabel(label);
|
||||
MSHR::ConstIterator i = allocatedList.begin();
|
||||
MSHR::ConstIterator end = allocatedList.end();
|
||||
for (; i != end; ++i) {
|
||||
MSHR *mshr = *i;
|
||||
if (mshr->addr == blk_addr && mshr->checkFunctional(pkt)) {
|
||||
pkt->popLabel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
pkt->popLabel();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
MSHR *
|
||||
MSHRQueue::findPending(Addr addr, int size) const
|
||||
{
|
||||
MSHR::ConstIterator i = readyList.begin();
|
||||
MSHR::ConstIterator end = readyList.end();
|
||||
for (; i != end; ++i) {
|
||||
MSHR *mshr = *i;
|
||||
if (mshr->addr < addr) {
|
||||
if (mshr->addr + mshr->size > addr) {
|
||||
return mshr;
|
||||
}
|
||||
} else {
|
||||
if (addr + size > mshr->addr) {
|
||||
return mshr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
MSHR::Iterator
|
||||
MSHRQueue::addToReadyList(MSHR *mshr)
|
||||
{
|
||||
if (readyList.empty() || readyList.back()->readyTime <= mshr->readyTime) {
|
||||
return readyList.insert(readyList.end(), mshr);
|
||||
}
|
||||
|
||||
MSHR::Iterator i = readyList.begin();
|
||||
MSHR::Iterator end = readyList.end();
|
||||
for (; i != end; ++i) {
|
||||
if ((*i)->readyTime > mshr->readyTime) {
|
||||
return readyList.insert(i, mshr);
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return end; // keep stupid compilers happy
|
||||
}
|
||||
|
||||
|
||||
MSHR *
|
||||
MSHRQueue::allocate(Addr addr, int size, PacketPtr &pkt,
|
||||
Tick when, Counter order)
|
||||
{
|
||||
assert(!freeList.empty());
|
||||
MSHR *mshr = freeList.front();
|
||||
assert(mshr->getNumTargets() == 0);
|
||||
freeList.pop_front();
|
||||
|
||||
mshr->allocate(addr, size, pkt, when, order);
|
||||
mshr->allocIter = allocatedList.insert(allocatedList.end(), mshr);
|
||||
mshr->readyIter = addToReadyList(mshr);
|
||||
|
||||
allocated += 1;
|
||||
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--;
|
||||
if (mshr->inService) {
|
||||
inServiceEntries--;
|
||||
} else {
|
||||
readyList.erase(mshr->readyIter);
|
||||
}
|
||||
mshr->deallocate();
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
MSHRQueue::moveToFront(MSHR *mshr)
|
||||
{
|
||||
if (!mshr->inService) {
|
||||
assert(mshr == *(mshr->readyIter));
|
||||
readyList.erase(mshr->readyIter);
|
||||
mshr->readyIter = readyList.insert(readyList.begin(), mshr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MSHRQueue::markInService(MSHR *mshr, PacketPtr pkt)
|
||||
{
|
||||
if (mshr->markInService(pkt)) {
|
||||
deallocate(mshr);
|
||||
} else {
|
||||
readyList.erase(mshr->readyIter);
|
||||
inServiceEntries += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MSHRQueue::markPending(MSHR *mshr)
|
||||
{
|
||||
assert(mshr->inService);
|
||||
mshr->inService = false;
|
||||
--inServiceEntries;
|
||||
/**
|
||||
* @ todo might want to add rerequests to front of pending list for
|
||||
* performance.
|
||||
*/
|
||||
mshr->readyIter = addToReadyList(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()) {
|
||||
mshr->popTarget();
|
||||
assert(0/*target->req->threadId()*/ == threadNum);
|
||||
}
|
||||
assert(!mshr->hasTargets());
|
||||
assert(mshr->ntargets==0);
|
||||
if (!mshr->inService) {
|
||||
i = deallocateOne(mshr);
|
||||
} else {
|
||||
//mshr->pkt->flags &= ~CACHE_LINE_FILL;
|
||||
++i;
|
||||
}
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
214
simulators/gem5/src/mem/cache/mshr_queue.hh
vendored
Normal file
214
simulators/gem5/src/mem/cache/mshr_queue.hh
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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 __MEM__CACHE__MISS__MSHR_QUEUE_HH__
|
||||
#define __MEM__CACHE__MISS__MSHR_QUEUE_HH__
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "mem/cache/mshr.hh"
|
||||
#include "mem/packet.hh"
|
||||
|
||||
/**
|
||||
* A Class for maintaining a list of pending and allocated memory requests.
|
||||
*/
|
||||
class MSHRQueue
|
||||
{
|
||||
private:
|
||||
/** Local label (for functional print requests) */
|
||||
const std::string label;
|
||||
|
||||
/** MSHR storage. */
|
||||
MSHR *registers;
|
||||
/** Holds pointers to all allocated entries. */
|
||||
MSHR::List allocatedList;
|
||||
/** Holds pointers to entries that haven't been sent to the bus. */
|
||||
MSHR::List readyList;
|
||||
/** Holds non allocated entries. */
|
||||
MSHR::List freeList;
|
||||
|
||||
// Parameters
|
||||
/**
|
||||
* The total number of entries in this queue. This number is set as the
|
||||
* number of entries requested plus (numReserve - 1). This allows for
|
||||
* the same number of effective entries while still maintaining the reserve.
|
||||
*/
|
||||
const int numEntries;
|
||||
|
||||
/**
|
||||
* The number of entries to hold in reserve. This is needed because copy
|
||||
* operations can allocate upto 4 entries at one time.
|
||||
*/
|
||||
const int numReserve;
|
||||
|
||||
MSHR::Iterator addToReadyList(MSHR *mshr);
|
||||
|
||||
|
||||
public:
|
||||
/** The number of allocated entries. */
|
||||
int allocated;
|
||||
/** The number of entries that have been forwarded to the bus. */
|
||||
int inServiceEntries;
|
||||
/** The index of this queue within the cache (MSHR queue vs. write
|
||||
* buffer). */
|
||||
const int index;
|
||||
|
||||
/**
|
||||
* Create a queue with a given number of entries.
|
||||
* @param num_entrys The number of entries in this queue.
|
||||
* @param reserve The minimum number of entries needed to satisfy
|
||||
* any access.
|
||||
*/
|
||||
MSHRQueue(const std::string &_label, int num_entries, int reserve,
|
||||
int index);
|
||||
|
||||
/** Destructor */
|
||||
~MSHRQueue();
|
||||
|
||||
/**
|
||||
* Find the first MSHR that matches the provided address.
|
||||
* @param addr The address to find.
|
||||
* @return Pointer to the matching MSHR, null if not found.
|
||||
*/
|
||||
MSHR *findMatch(Addr addr) const;
|
||||
|
||||
/**
|
||||
* Find and return all the matching entries in the provided vector.
|
||||
* @param addr The address to find.
|
||||
* @param matches The vector to return pointers to the matching entries.
|
||||
* @return True if any matches are found, false otherwise.
|
||||
* @todo Typedef the vector??
|
||||
*/
|
||||
bool findMatches(Addr addr, std::vector<MSHR*>& matches) const;
|
||||
|
||||
/**
|
||||
* Find any pending requests that overlap the given request.
|
||||
* @param pkt The request to find.
|
||||
* @return A pointer to the earliest matching MSHR.
|
||||
*/
|
||||
MSHR *findPending(Addr addr, int size) const;
|
||||
|
||||
bool checkFunctional(PacketPtr pkt, Addr blk_addr);
|
||||
|
||||
/**
|
||||
* Allocates a new MSHR for the request and size. This places the request
|
||||
* as the first target in the MSHR.
|
||||
* @param pkt 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 entries.
|
||||
*/
|
||||
MSHR *allocate(Addr addr, int size, PacketPtr &pkt,
|
||||
Tick when, Counter order);
|
||||
|
||||
/**
|
||||
* Removes the given MSHR from the queue. This places the MSHR on the
|
||||
* free list.
|
||||
* @param mshr
|
||||
*/
|
||||
void deallocate(MSHR *mshr);
|
||||
|
||||
/**
|
||||
* 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 entry to move.
|
||||
*/
|
||||
void moveToFront(MSHR *mshr);
|
||||
|
||||
/**
|
||||
* Mark the given MSHR as in service. This removes the MSHR from the
|
||||
* readyList. Deallocates the MSHR if it does not expect a response.
|
||||
* @param mshr The MSHR to mark in service.
|
||||
*/
|
||||
void markInService(MSHR *mshr, PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Mark an in service entry as pending, used to resend a request.
|
||||
* @param mshr The MSHR to resend.
|
||||
*/
|
||||
void markPending(MSHR *mshr);
|
||||
|
||||
/**
|
||||
* Squash outstanding requests with the given thread number. If a request
|
||||
* is in service, just squashes the targets.
|
||||
* @param threadNum 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 !readyList.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are no free entries.
|
||||
* @return True if this queue is full.
|
||||
*/
|
||||
bool isFull() const
|
||||
{
|
||||
return (allocated > numEntries - numReserve);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MSHR at the head of the readyList.
|
||||
* @return The next request to service.
|
||||
*/
|
||||
MSHR *getNextMSHR() const
|
||||
{
|
||||
if (readyList.empty() || readyList.front()->readyTime > curTick()) {
|
||||
return NULL;
|
||||
}
|
||||
return readyList.front();
|
||||
}
|
||||
|
||||
Tick nextMSHRReadyTime() const
|
||||
{
|
||||
return readyList.empty() ? MaxTick : readyList.front()->readyTime;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__MEM__CACHE__MISS__MSHR_QUEUE_HH__
|
||||
38
simulators/gem5/src/mem/cache/prefetch/Prefetcher.py
vendored
Normal file
38
simulators/gem5/src/mem/cache/prefetch/Prefetcher.py
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
from m5.SimObject import SimObject
|
||||
from m5.params import *
|
||||
from m5.proxy import *
|
||||
|
||||
class BasePrefetcher(SimObject):
|
||||
type = 'BasePrefetcher'
|
||||
abstract = True
|
||||
size = Param.Int(100,
|
||||
"Number of entries in the hardware prefetch queue")
|
||||
cross_pages = Param.Bool(False,
|
||||
"Allow prefetches to cross virtual page boundaries")
|
||||
serial_squash = Param.Bool(False,
|
||||
"Squash prefetches with a later time on a subsequent miss")
|
||||
degree = Param.Int(1,
|
||||
"Degree of the prefetch depth")
|
||||
latency = Param.Latency('10t',
|
||||
"Latency of the prefetcher")
|
||||
use_master_id = Param.Bool(True,
|
||||
"Use the master id to separate calculations of prefetches")
|
||||
data_accesses_only = Param.Bool(False,
|
||||
"Only prefetch on data not on instruction accesses")
|
||||
sys = Param.System(Parent.any, "System this device belongs to")
|
||||
|
||||
class GHBPrefetcher(BasePrefetcher):
|
||||
type = 'GHBPrefetcher'
|
||||
cxx_class = 'GHBPrefetcher'
|
||||
|
||||
class StridePrefetcher(BasePrefetcher):
|
||||
type = 'StridePrefetcher'
|
||||
cxx_class = 'StridePrefetcher'
|
||||
|
||||
class TaggedPrefetcher(BasePrefetcher):
|
||||
type = 'TaggedPrefetcher'
|
||||
cxx_class = 'TaggedPrefetcher'
|
||||
|
||||
|
||||
|
||||
|
||||
41
simulators/gem5/src/mem/cache/prefetch/SConscript
vendored
Normal file
41
simulators/gem5/src/mem/cache/prefetch/SConscript
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# -*- 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: Nathan Binkert
|
||||
|
||||
Import('*')
|
||||
|
||||
if env['TARGET_ISA'] == 'no':
|
||||
Return()
|
||||
SimObject('Prefetcher.py')
|
||||
|
||||
Source('base.cc')
|
||||
Source('ghb.cc')
|
||||
Source('stride.cc')
|
||||
Source('tagged.cc')
|
||||
|
||||
283
simulators/gem5/src/mem/cache/prefetch/base.cc
vendored
Normal file
283
simulators/gem5/src/mem/cache/prefetch/base.cc
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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 <list>
|
||||
|
||||
#include "arch/isa_traits.hh"
|
||||
#include "base/trace.hh"
|
||||
#include "config/the_isa.hh"
|
||||
#include "debug/HWPrefetch.hh"
|
||||
#include "mem/cache/prefetch/base.hh"
|
||||
#include "mem/cache/base.hh"
|
||||
#include "mem/request.hh"
|
||||
#include "sim/system.hh"
|
||||
|
||||
BasePrefetcher::BasePrefetcher(const Params *p)
|
||||
: SimObject(p), size(p->size), latency(p->latency), degree(p->degree),
|
||||
useMasterId(p->use_master_id), pageStop(!p->cross_pages),
|
||||
serialSquash(p->serial_squash), onlyData(p->data_accesses_only),
|
||||
system(p->sys), masterId(system->getMasterId(name()))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
BasePrefetcher::setCache(BaseCache *_cache)
|
||||
{
|
||||
cache = _cache;
|
||||
blkSize = cache->getBlockSize();
|
||||
}
|
||||
|
||||
void
|
||||
BasePrefetcher::regStats()
|
||||
{
|
||||
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")
|
||||
;
|
||||
}
|
||||
|
||||
inline bool
|
||||
BasePrefetcher::inCache(Addr addr)
|
||||
{
|
||||
if (cache->inCache(addr)) {
|
||||
pfCacheHit++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
BasePrefetcher::inMissQueue(Addr addr)
|
||||
{
|
||||
if (cache->inMissQueue(addr)) {
|
||||
pfMSHRHit++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PacketPtr
|
||||
BasePrefetcher::getPacket()
|
||||
{
|
||||
DPRINTF(HWPrefetch, "Requesting a hw_pf to issue\n");
|
||||
|
||||
if (pf.empty()) {
|
||||
DPRINTF(HWPrefetch, "No HW_PF found\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PacketPtr pkt = *pf.begin();
|
||||
while (!pf.empty()) {
|
||||
pkt = *pf.begin();
|
||||
pf.pop_front();
|
||||
|
||||
Addr blk_addr = pkt->getAddr() & ~(Addr)(blkSize-1);
|
||||
|
||||
if (!inCache(blk_addr) && !inMissQueue(blk_addr))
|
||||
// we found a prefetch, return it
|
||||
break;
|
||||
|
||||
DPRINTF(HWPrefetch, "addr 0x%x in cache, skipping\n", pkt->getAddr());
|
||||
delete pkt->req;
|
||||
delete pkt;
|
||||
|
||||
if (pf.empty()) {
|
||||
cache->deassertMemSideBusRequest(BaseCache::Request_PF);
|
||||
return NULL; // None left, all were in cache
|
||||
}
|
||||
}
|
||||
|
||||
pfIssued++;
|
||||
assert(pkt != NULL);
|
||||
DPRINTF(HWPrefetch, "returning 0x%x\n", pkt->getAddr());
|
||||
return pkt;
|
||||
}
|
||||
|
||||
|
||||
Tick
|
||||
BasePrefetcher::notify(PacketPtr &pkt, Tick time)
|
||||
{
|
||||
if (!pkt->req->isUncacheable() && !(pkt->req->isInstFetch() && onlyData)) {
|
||||
// Calculate the blk address
|
||||
Addr blk_addr = pkt->getAddr() & ~(Addr)(blkSize-1);
|
||||
|
||||
// Check if miss is in pfq, if so remove it
|
||||
std::list<PacketPtr>::iterator iter = inPrefetch(blk_addr);
|
||||
if (iter != pf.end()) {
|
||||
DPRINTF(HWPrefetch, "Saw a miss to a queued prefetch addr: "
|
||||
"0x%x, removing it\n", blk_addr);
|
||||
pfRemovedMSHR++;
|
||||
delete (*iter)->req;
|
||||
delete (*iter);
|
||||
iter = pf.erase(iter);
|
||||
if (pf.empty())
|
||||
cache->deassertMemSideBusRequest(BaseCache::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();
|
||||
if (iter != pf.begin())
|
||||
iter--;
|
||||
while (!pf.empty() && ((*iter)->time >= time)) {
|
||||
pfSquashed++;
|
||||
DPRINTF(HWPrefetch, "Squashing old prefetch addr: 0x%x\n",
|
||||
(*iter)->getAddr());
|
||||
delete (*iter)->req;
|
||||
delete (*iter);
|
||||
iter = pf.erase(iter);
|
||||
if (iter != pf.begin())
|
||||
iter--;
|
||||
}
|
||||
if (pf.empty())
|
||||
cache->deassertMemSideBusRequest(BaseCache::Request_PF);
|
||||
}
|
||||
|
||||
|
||||
std::list<Addr> addresses;
|
||||
std::list<Tick> delays;
|
||||
calculatePrefetch(pkt, addresses, delays);
|
||||
|
||||
std::list<Addr>::iterator addrIter = addresses.begin();
|
||||
std::list<Tick>::iterator delayIter = delays.begin();
|
||||
for (; addrIter != addresses.end(); ++addrIter, ++delayIter) {
|
||||
Addr addr = *addrIter;
|
||||
|
||||
pfIdentified++;
|
||||
|
||||
DPRINTF(HWPrefetch, "Found a pf candidate addr: 0x%x, "
|
||||
"inserting into prefetch queue with delay %d time %d\n",
|
||||
addr, *delayIter, time);
|
||||
|
||||
// Check if it is already in the pf buffer
|
||||
if (inPrefetch(addr) != pf.end()) {
|
||||
pfBufferHit++;
|
||||
DPRINTF(HWPrefetch, "Prefetch addr already in pf buffer\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// create a prefetch memreq
|
||||
Request *prefetchReq = new Request(*addrIter, blkSize, 0, masterId);
|
||||
PacketPtr prefetch =
|
||||
new Packet(prefetchReq, MemCmd::HardPFReq);
|
||||
prefetch->allocate();
|
||||
prefetch->req->setThreadContext(pkt->req->contextId(),
|
||||
pkt->req->threadId());
|
||||
|
||||
prefetch->time = time + (*delayIter); // @todo ADD LATENCY HERE
|
||||
|
||||
// We just remove the head if we are full
|
||||
if (pf.size() == size) {
|
||||
pfRemovedFull++;
|
||||
PacketPtr old_pkt = *pf.begin();
|
||||
DPRINTF(HWPrefetch, "Prefetch queue full, "
|
||||
"removing oldest 0x%x\n", old_pkt->getAddr());
|
||||
delete old_pkt->req;
|
||||
delete old_pkt;
|
||||
pf.pop_front();
|
||||
}
|
||||
|
||||
pf.push_back(prefetch);
|
||||
}
|
||||
}
|
||||
|
||||
return pf.empty() ? 0 : pf.front()->time;
|
||||
}
|
||||
|
||||
std::list<PacketPtr>::iterator
|
||||
BasePrefetcher::inPrefetch(Addr address)
|
||||
{
|
||||
// Guaranteed to only be one match, we always check before inserting
|
||||
std::list<PacketPtr>::iterator iter;
|
||||
for (iter = pf.begin(); iter != pf.end(); iter++) {
|
||||
if (((*iter)->getAddr() & ~(Addr)(blkSize-1)) == address) {
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
return pf.end();
|
||||
}
|
||||
|
||||
bool
|
||||
BasePrefetcher::samePage(Addr a, Addr b)
|
||||
{
|
||||
return roundDown(a, TheISA::VMPageSize) == roundDown(b, TheISA::VMPageSize);
|
||||
}
|
||||
|
||||
|
||||
152
simulators/gem5/src/mem/cache/prefetch/base.hh
vendored
Normal file
152
simulators/gem5/src/mem/cache/prefetch/base.hh
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 <list>
|
||||
|
||||
#include "base/statistics.hh"
|
||||
#include "mem/packet.hh"
|
||||
#include "params/BaseCache.hh"
|
||||
#include "sim/sim_object.hh"
|
||||
|
||||
class BaseCache;
|
||||
|
||||
class BasePrefetcher : public SimObject
|
||||
{
|
||||
protected:
|
||||
|
||||
/** The Prefetch Queue. */
|
||||
std::list<PacketPtr> pf;
|
||||
|
||||
// PARAMETERS
|
||||
|
||||
/** The number of MSHRs in the Prefetch Queue. */
|
||||
const unsigned size;
|
||||
|
||||
/** Pointr to the parent cache. */
|
||||
BaseCache* cache;
|
||||
|
||||
/** The block size of the parent cache. */
|
||||
int blkSize;
|
||||
|
||||
/** The latency before a prefetch is issued */
|
||||
Tick latency;
|
||||
|
||||
/** The number of prefetches to issue */
|
||||
unsigned degree;
|
||||
|
||||
/** If patterns should be found per context id */
|
||||
bool useMasterId;
|
||||
/** Do we prefetch across page boundaries. */
|
||||
bool pageStop;
|
||||
|
||||
/** Do we remove prefetches with later times than a new miss.*/
|
||||
bool serialSquash;
|
||||
|
||||
/** Do we prefetch on only data reads, or on inst reads as well. */
|
||||
bool onlyData;
|
||||
|
||||
/** System we belong to */
|
||||
System* system;
|
||||
|
||||
/** Request id for prefetches */
|
||||
MasterID masterId;
|
||||
|
||||
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();
|
||||
|
||||
public:
|
||||
typedef BasePrefetcherParams Params;
|
||||
BasePrefetcher(const Params *p);
|
||||
|
||||
virtual ~BasePrefetcher() {}
|
||||
|
||||
void setCache(BaseCache *_cache);
|
||||
|
||||
/**
|
||||
* Notify prefetcher of cache access (may be any access or just
|
||||
* misses, depending on cache parameters.)
|
||||
* @retval Time of next prefetch availability, or 0 if none.
|
||||
*/
|
||||
Tick notify(PacketPtr &pkt, Tick time);
|
||||
|
||||
bool inCache(Addr addr);
|
||||
|
||||
bool inMissQueue(Addr addr);
|
||||
|
||||
PacketPtr getPacket();
|
||||
|
||||
bool havePending()
|
||||
{
|
||||
return !pf.empty();
|
||||
}
|
||||
|
||||
Tick nextPrefetchReadyTime()
|
||||
{
|
||||
return pf.empty() ? MaxTick : pf.front()->time;
|
||||
}
|
||||
|
||||
virtual void calculatePrefetch(PacketPtr &pkt,
|
||||
std::list<Addr> &addresses,
|
||||
std::list<Tick> &delays) = 0;
|
||||
|
||||
std::list<PacketPtr>::iterator inPrefetch(Addr address);
|
||||
|
||||
/**
|
||||
* Utility function: are addresses a and b on the same VM page?
|
||||
*/
|
||||
bool samePage(Addr a, Addr b);
|
||||
public:
|
||||
const Params*
|
||||
params() const
|
||||
{
|
||||
return dynamic_cast<const Params *>(_params);
|
||||
}
|
||||
|
||||
};
|
||||
#endif //__MEM_CACHE_PREFETCH_BASE_PREFETCHER_HH__
|
||||
75
simulators/gem5/src/mem/cache/prefetch/ghb.cc
vendored
Normal file
75
simulators/gem5/src/mem/cache/prefetch/ghb.cc
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 implementation.
|
||||
*/
|
||||
|
||||
#include "base/trace.hh"
|
||||
#include "debug/HWPrefetch.hh"
|
||||
#include "mem/cache/prefetch/ghb.hh"
|
||||
|
||||
void
|
||||
GHBPrefetcher::calculatePrefetch(PacketPtr &pkt, std::list<Addr> &addresses,
|
||||
std::list<Tick> &delays)
|
||||
{
|
||||
Addr blk_addr = pkt->getAddr() & ~(Addr)(blkSize-1);
|
||||
int master_id = useMasterId ? pkt->req->masterId() : 0;
|
||||
assert(master_id < Max_Masters);
|
||||
|
||||
int new_stride = blk_addr - lastMissAddr[master_id];
|
||||
int old_stride = lastMissAddr[master_id] - secondLastMissAddr[master_id];
|
||||
|
||||
secondLastMissAddr[master_id] = lastMissAddr[master_id];
|
||||
lastMissAddr[master_id] = blk_addr;
|
||||
|
||||
if (new_stride == old_stride) {
|
||||
for (int d = 1; d <= degree; d++) {
|
||||
Addr new_addr = blk_addr + d * new_stride;
|
||||
if (pageStop && !samePage(blk_addr, new_addr)) {
|
||||
// Spanned the page, so now stop
|
||||
pfSpanPage += degree - d + 1;
|
||||
return;
|
||||
} else {
|
||||
addresses.push_back(new_addr);
|
||||
delays.push_back(latency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GHBPrefetcher*
|
||||
GHBPrefetcherParams::create()
|
||||
{
|
||||
return new GHBPrefetcher(this);
|
||||
}
|
||||
63
simulators/gem5/src/mem/cache/prefetch/ghb.hh
vendored
Normal file
63
simulators/gem5/src/mem/cache/prefetch/ghb.hh
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_CACHE_PREFETCH_GHB_PREFETCHER_HH__
|
||||
#define __MEM_CACHE_PREFETCH_GHB_PREFETCHER_HH__
|
||||
|
||||
#include "mem/cache/prefetch/base.hh"
|
||||
#include "params/GHBPrefetcher.hh"
|
||||
|
||||
class GHBPrefetcher : public BasePrefetcher
|
||||
{
|
||||
protected:
|
||||
|
||||
static const int Max_Masters = 64;
|
||||
|
||||
Addr secondLastMissAddr[Max_Masters];
|
||||
Addr lastMissAddr[Max_Masters];
|
||||
|
||||
public:
|
||||
GHBPrefetcher(const Params *p)
|
||||
: BasePrefetcher(p)
|
||||
{
|
||||
}
|
||||
|
||||
~GHBPrefetcher() {}
|
||||
|
||||
void calculatePrefetch(PacketPtr &pkt, std::list<Addr> &addresses,
|
||||
std::list<Tick> &delays);
|
||||
};
|
||||
|
||||
#endif // __MEM_CACHE_PREFETCH_GHB_PREFETCHER_HH__
|
||||
136
simulators/gem5/src/mem/cache/prefetch/stride.cc
vendored
Normal file
136
simulators/gem5/src/mem/cache/prefetch/stride.cc
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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 "base/trace.hh"
|
||||
#include "debug/HWPrefetch.hh"
|
||||
#include "mem/cache/prefetch/stride.hh"
|
||||
|
||||
void
|
||||
StridePrefetcher::calculatePrefetch(PacketPtr &pkt, std::list<Addr> &addresses,
|
||||
std::list<Tick> &delays)
|
||||
{
|
||||
if (!pkt->req->hasPC()) {
|
||||
DPRINTF(HWPrefetch, "ignoring request with no PC");
|
||||
return;
|
||||
}
|
||||
|
||||
Addr blk_addr = pkt->getAddr() & ~(Addr)(blkSize-1);
|
||||
MasterID master_id = useMasterId ? pkt->req->masterId() : 0;
|
||||
Addr pc = pkt->req->getPC();
|
||||
assert(master_id < Max_Contexts);
|
||||
std::list<StrideEntry*> &tab = table[master_id];
|
||||
|
||||
/* Scan Table for instAddr Match */
|
||||
std::list<StrideEntry*>::iterator iter;
|
||||
for (iter = tab.begin(); iter != tab.end(); iter++) {
|
||||
if ((*iter)->instAddr == pc)
|
||||
break;
|
||||
}
|
||||
|
||||
if (iter != tab.end()) {
|
||||
// Hit in table
|
||||
|
||||
int new_stride = blk_addr - (*iter)->missAddr;
|
||||
bool stride_match = (new_stride == (*iter)->stride);
|
||||
|
||||
if (stride_match && new_stride != 0) {
|
||||
if ((*iter)->confidence < Max_Conf)
|
||||
(*iter)->confidence++;
|
||||
} else {
|
||||
(*iter)->stride = new_stride;
|
||||
if ((*iter)->confidence > Min_Conf)
|
||||
(*iter)->confidence = 0;
|
||||
}
|
||||
|
||||
DPRINTF(HWPrefetch, "hit: PC %x blk_addr %x stride %d (%s), conf %d\n",
|
||||
pc, blk_addr, new_stride, stride_match ? "match" : "change",
|
||||
(*iter)->confidence);
|
||||
|
||||
(*iter)->missAddr = blk_addr;
|
||||
|
||||
if ((*iter)->confidence <= 0)
|
||||
return;
|
||||
|
||||
for (int d = 1; d <= degree; d++) {
|
||||
Addr new_addr = blk_addr + d * new_stride;
|
||||
if (pageStop && !samePage(blk_addr, new_addr)) {
|
||||
// Spanned the page, so now stop
|
||||
pfSpanPage += degree - d + 1;
|
||||
return;
|
||||
} else {
|
||||
DPRINTF(HWPrefetch, " queuing prefetch to %x @ %d\n",
|
||||
new_addr, latency);
|
||||
addresses.push_back(new_addr);
|
||||
delays.push_back(latency);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Miss in table
|
||||
// Find lowest confidence and replace
|
||||
|
||||
DPRINTF(HWPrefetch, "miss: PC %x blk_addr %x\n", pc, blk_addr);
|
||||
|
||||
if (tab.size() >= 256) { //set default table size is 256
|
||||
std::list<StrideEntry*>::iterator min_pos = tab.begin();
|
||||
int min_conf = (*min_pos)->confidence;
|
||||
for (iter = min_pos, ++iter; iter != tab.end(); ++iter) {
|
||||
if ((*iter)->confidence < min_conf){
|
||||
min_pos = iter;
|
||||
min_conf = (*iter)->confidence;
|
||||
}
|
||||
}
|
||||
DPRINTF(HWPrefetch, " replacing PC %x\n", (*min_pos)->instAddr);
|
||||
|
||||
// free entry and delete it
|
||||
delete *min_pos;
|
||||
tab.erase(min_pos);
|
||||
}
|
||||
|
||||
StrideEntry *new_entry = new StrideEntry;
|
||||
new_entry->instAddr = pc;
|
||||
new_entry->missAddr = blk_addr;
|
||||
new_entry->stride = 0;
|
||||
new_entry->confidence = 0;
|
||||
tab.push_back(new_entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StridePrefetcher*
|
||||
StridePrefetcherParams::create()
|
||||
{
|
||||
return new StridePrefetcher(this);
|
||||
}
|
||||
81
simulators/gem5/src/mem/cache/prefetch/stride.hh
vendored
Normal file
81
simulators/gem5/src/mem/cache/prefetch/stride.hh
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_CACHE_PREFETCH_STRIDE_PREFETCHER_HH__
|
||||
#define __MEM_CACHE_PREFETCH_STRIDE_PREFETCHER_HH__
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "mem/cache/prefetch/base.hh"
|
||||
#include "params/StridePrefetcher.hh"
|
||||
|
||||
class StridePrefetcher : public BasePrefetcher
|
||||
{
|
||||
protected:
|
||||
|
||||
static const int Max_Contexts = 64;
|
||||
|
||||
// These constants need to be changed with the type of the
|
||||
// 'confidence' field below.
|
||||
static const int Max_Conf = INT_MAX;
|
||||
static const int Min_Conf = INT_MIN;
|
||||
|
||||
class StrideEntry
|
||||
{
|
||||
public:
|
||||
Addr instAddr;
|
||||
Addr missAddr;
|
||||
int stride;
|
||||
int confidence;
|
||||
};
|
||||
|
||||
Addr *lastMissAddr[Max_Contexts];
|
||||
|
||||
std::list<StrideEntry*> table[Max_Contexts];
|
||||
|
||||
public:
|
||||
|
||||
StridePrefetcher(const Params *p)
|
||||
: BasePrefetcher(p)
|
||||
{
|
||||
}
|
||||
|
||||
~StridePrefetcher() {}
|
||||
|
||||
void calculatePrefetch(PacketPtr &pkt, std::list<Addr> &addresses,
|
||||
std::list<Tick> &delays);
|
||||
};
|
||||
|
||||
#endif // __MEM_CACHE_PREFETCH_STRIDE_PREFETCHER_HH__
|
||||
68
simulators/gem5/src/mem/cache/prefetch/tagged.cc
vendored
Normal file
68
simulators/gem5/src/mem/cache/prefetch/tagged.cc
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "mem/cache/prefetch/tagged.hh"
|
||||
|
||||
TaggedPrefetcher::TaggedPrefetcher(const Params *p)
|
||||
: BasePrefetcher(p)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TaggedPrefetcher::
|
||||
calculatePrefetch(PacketPtr &pkt, std::list<Addr> &addresses,
|
||||
std::list<Tick> &delays)
|
||||
{
|
||||
Addr blkAddr = pkt->getAddr() & ~(Addr)(blkSize-1);
|
||||
|
||||
for (int d = 1; d <= degree; d++) {
|
||||
Addr newAddr = blkAddr + d*(blkSize);
|
||||
if (pageStop && !samePage(blkAddr, newAddr)) {
|
||||
// Spanned the page, so now stop
|
||||
pfSpanPage += degree - d + 1;
|
||||
return;
|
||||
} else {
|
||||
addresses.push_back(newAddr);
|
||||
delays.push_back(latency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TaggedPrefetcher*
|
||||
TaggedPrefetcherParams::create()
|
||||
{
|
||||
return new TaggedPrefetcher(this);
|
||||
}
|
||||
55
simulators/gem5/src/mem/cache/prefetch/tagged.hh
vendored
Normal file
55
simulators/gem5/src/mem/cache/prefetch/tagged.hh
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_CACHE_PREFETCH_TAGGED_PREFETCHER_HH__
|
||||
#define __MEM_CACHE_PREFETCH_TAGGED_PREFETCHER_HH__
|
||||
|
||||
#include "mem/cache/prefetch/base.hh"
|
||||
#include "params/TaggedPrefetcher.hh"
|
||||
|
||||
|
||||
class TaggedPrefetcher : public BasePrefetcher
|
||||
{
|
||||
public:
|
||||
|
||||
TaggedPrefetcher(const Params *p);
|
||||
|
||||
~TaggedPrefetcher() {}
|
||||
|
||||
void calculatePrefetch(PacketPtr &pkt, std::list<Addr> &addresses,
|
||||
std::list<Tick> &delays);
|
||||
};
|
||||
|
||||
#endif // __MEM_CACHE_PREFETCH_TAGGED_PREFETCHER_HH__
|
||||
46
simulators/gem5/src/mem/cache/tags/SConscript
vendored
Normal file
46
simulators/gem5/src/mem/cache/tags/SConscript
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
# -*- 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: Nathan Binkert
|
||||
|
||||
Import('*')
|
||||
|
||||
if env['TARGET_ISA'] == 'no':
|
||||
Return()
|
||||
|
||||
Source('base.cc')
|
||||
Source('fa_lru.cc')
|
||||
Source('iic.cc')
|
||||
Source('lru.cc')
|
||||
Source('cacheset.cc')
|
||||
|
||||
SimObject('iic_repl/Repl.py')
|
||||
Source('iic_repl/gen.cc')
|
||||
|
||||
DebugFlag('IIC')
|
||||
DebugFlag('IICMore')
|
||||
111
simulators/gem5/src/mem/cache/tags/base.cc
vendored
Normal file
111
simulators/gem5/src/mem/cache/tags/base.cc
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 "cpu/smt.hh" //maxThreadsPerCPU
|
||||
#include "mem/cache/tags/base.hh"
|
||||
#include "mem/cache/base.hh"
|
||||
#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.")
|
||||
;
|
||||
|
||||
occupancies
|
||||
.init(cache->system->maxMasters())
|
||||
.name(name + ".occ_blocks")
|
||||
.desc("Average occupied blocks per requestor")
|
||||
.flags(nozero | nonan)
|
||||
;
|
||||
for (int i = 0; i < cache->system->maxMasters(); i++) {
|
||||
occupancies.subname(i, cache->system->getMasterName(i));
|
||||
}
|
||||
|
||||
avgOccs
|
||||
.name(name + ".occ_percent")
|
||||
.desc("Average percentage of cache occupancy")
|
||||
.flags(nozero | total)
|
||||
;
|
||||
for (int i = 0; i < cache->system->maxMasters(); i++) {
|
||||
avgOccs.subname(i, cache->system->getMasterName(i));
|
||||
}
|
||||
|
||||
avgOccs = occupancies / Stats::constant(numBlocks);
|
||||
|
||||
registerExitCallback(new BaseTagsCallback(this));
|
||||
}
|
||||
160
simulators/gem5/src/mem/cache/tags/base.hh
vendored
Normal file
160
simulators/gem5/src/mem/cache/tags/base.hh
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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/callback.hh"
|
||||
#include "base/statistics.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;
|
||||
|
||||
/** the number of blocks in the cache */
|
||||
unsigned numBlocks;
|
||||
|
||||
// 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;
|
||||
|
||||
/** Average occupancy of each requestor using the cache */
|
||||
Stats::AverageVector occupancies;
|
||||
|
||||
/** Average occ % of each requestor using the cache */
|
||||
Stats::Formula avgOccs;
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
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() {}
|
||||
|
||||
/**
|
||||
*iterated through all blocks and clear all locks
|
||||
*Needed to clear all lock tracking at once
|
||||
*/
|
||||
virtual void clearLocks() {}
|
||||
};
|
||||
|
||||
class BaseTagsCallback : public Callback
|
||||
{
|
||||
BaseTags *tags;
|
||||
public:
|
||||
BaseTagsCallback(BaseTags *t) : tags(t) {}
|
||||
virtual void process() { tags->cleanupRefs(); };
|
||||
};
|
||||
|
||||
#endif //__BASE_TAGS_HH__
|
||||
68
simulators/gem5/src/mem/cache/tags/cacheset.cc
vendored
Normal file
68
simulators/gem5/src/mem/cache/tags/cacheset.cc
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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
|
||||
*/
|
||||
|
||||
|
||||
#include "mem/cache/tags/cacheset.hh"
|
||||
|
||||
CacheBlk*
|
||||
CacheSet::findBlk(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(CacheBlk *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;
|
||||
CacheBlk *next = blk;
|
||||
|
||||
do {
|
||||
assert(i < assoc);
|
||||
// swap blks[i] and next
|
||||
CacheBlk *tmp = blks[i];
|
||||
blks[i] = next;
|
||||
next = tmp;
|
||||
++i;
|
||||
} while (next != blk);
|
||||
}
|
||||
|
||||
71
simulators/gem5/src/mem/cache/tags/cacheset.hh
vendored
Normal file
71
simulators/gem5/src/mem/cache/tags/cacheset.hh
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 an associative set
|
||||
*/
|
||||
|
||||
#ifndef __CACHESET_HH__
|
||||
#define __CACHESET_HH__
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "mem/cache/blk.hh" // base class
|
||||
|
||||
/**
|
||||
* 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. */
|
||||
CacheBlk **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.
|
||||
*/
|
||||
CacheBlk* findBlk(Addr tag) const;
|
||||
|
||||
/**
|
||||
* Move the given block to the head of the list.
|
||||
* @param blk The block to move.
|
||||
*/
|
||||
void moveToHead(CacheBlk *blk);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
296
simulators/gem5/src/mem/cache/tags/fa_lru.cc
vendored
Normal file
296
simulators/gem5/src/mem/cache/tags/fa_lru.cc
vendored
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* 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 <cassert>
|
||||
#include <sstream>
|
||||
|
||||
#include "base/intmath.hh"
|
||||
#include "base/misc.hh"
|
||||
#include "mem/cache/tags/fa_lru.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
FALRU::FALRU(unsigned _blkSize, unsigned _size, unsigned hit_latency)
|
||||
: blkSize(_blkSize), size(_size), 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;
|
||||
numBlocks = size/blkSize;
|
||||
|
||||
blks = new FALRUBlk[numBlocks];
|
||||
head = &(blks[0]);
|
||||
tail = &(blks[numBlocks-1]);
|
||||
|
||||
head->prev = NULL;
|
||||
head->next = &(blks[1]);
|
||||
head->inCache = cacheMask;
|
||||
|
||||
tail->prev = &(blks[numBlocks-2]);
|
||||
tail->next = NULL;
|
||||
tail->inCache = 0;
|
||||
|
||||
unsigned index = (1 << 17) / blkSize;
|
||||
unsigned j = 0;
|
||||
int flags = cacheMask;
|
||||
for (unsigned i = 1; i < numBlocks - 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 == numBlocks);
|
||||
//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 (unsigned i = 0; i <= numCaches; ++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;
|
||||
}
|
||||
|
||||
void
|
||||
FALRU::invalidateBlk(FALRU::BlkType *blk)
|
||||
{
|
||||
if (blk) {
|
||||
blk->status = 0;
|
||||
blk->isTouched = false;
|
||||
tagsInUse--;
|
||||
}
|
||||
}
|
||||
|
||||
FALRUBlk*
|
||||
FALRU::accessBlock(Addr addr, int &lat, int context_src, 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 (unsigned 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 (unsigned i = 0; i <= numCaches; ++i) {
|
||||
misses[i]++;
|
||||
}
|
||||
}
|
||||
if (inCache) {
|
||||
*inCache = tmp_in_cache;
|
||||
}
|
||||
|
||||
lat = hitLatency;
|
||||
//assert(check());
|
||||
return blk;
|
||||
}
|
||||
|
||||
|
||||
FALRUBlk*
|
||||
FALRU::findBlock(Addr addr) const
|
||||
{
|
||||
Addr blkAddr = blkAlign(addr);
|
||||
FALRUBlk* blk = hashLookup(blkAddr);
|
||||
|
||||
if (blk && blk->isValid()) {
|
||||
assert(blk->tag == blkAddr);
|
||||
} else {
|
||||
blk = NULL;
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
|
||||
FALRUBlk*
|
||||
FALRU::findVictim(Addr addr, PacketList &writebacks)
|
||||
{
|
||||
FALRUBlk * blk = tail;
|
||||
assert(blk->inCache == 0);
|
||||
moveToHead(blk);
|
||||
tagHash.erase(blk->tag);
|
||||
tagHash[blkAlign(addr)] = 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::insertBlock(Addr addr, FALRU::BlkType *blk, int context_src)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
FALRU::moveToHead(FALRUBlk *blk)
|
||||
{
|
||||
int updateMask = blk->inCache ^ cacheMask;
|
||||
for (unsigned 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;
|
||||
}
|
||||
|
||||
void
|
||||
FALRU::clearLocks()
|
||||
{
|
||||
for (int i = 0; i < numBlocks; i++){
|
||||
blks[i].clearLoadLocks();
|
||||
}
|
||||
}
|
||||
291
simulators/gem5/src/mem/cache/tags/fa_lru.hh
vendored
Normal file
291
simulators/gem5/src/mem/cache/tags/fa_lru.hh
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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 __MEM_CACHE_TAGS_FA_LRU_HH__
|
||||
#define __MEM_CACHE_TAGS_FA_LRU_HH__
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "base/hashmap.hh"
|
||||
#include "mem/cache/tags/base.hh"
|
||||
#include "mem/cache/blk.hh"
|
||||
#include "mem/packet.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 unsigned blkSize;
|
||||
/** The size of the cache. */
|
||||
const unsigned size;
|
||||
/** The hit latency of the cache. */
|
||||
const unsigned 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. */
|
||||
unsigned 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(unsigned blkSize, unsigned size, unsigned hit_latency);
|
||||
|
||||
/**
|
||||
* Register the stats for this object.
|
||||
* @param name The name to prepend to the stats name.
|
||||
*/
|
||||
void regStats(const std::string &name);
|
||||
|
||||
/**
|
||||
* Invalidate a cache block.
|
||||
* @param blk The block to invalidate.
|
||||
*/
|
||||
void invalidateBlk(BlkType *blk);
|
||||
|
||||
/**
|
||||
* Access block and update replacement data. May not succeed, in which case
|
||||
* NULL pointer is returned. This has all the implications of a cache
|
||||
* access and should only be used as such.
|
||||
* Returns the access latency and inCache 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* accessBlock(Addr addr, int &lat, int context_src, 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) const;
|
||||
|
||||
/**
|
||||
* Find a replacement block for the address provided.
|
||||
* @param pkt The request to a find a replacement candidate for.
|
||||
* @param writebacks List for any writebacks to be performed.
|
||||
* @return The block to place the replacement in.
|
||||
*/
|
||||
FALRUBlk* findVictim(Addr addr, PacketList & writebacks);
|
||||
|
||||
void insertBlock(Addr addr, BlkType *blk, int context_src);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
unsigned
|
||||
getBlockSize() const
|
||||
{
|
||||
return blkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the subblock size of this cache, always the block size.
|
||||
* @return The block size.
|
||||
*/
|
||||
unsigned
|
||||
getSubBlockSize() const
|
||||
{
|
||||
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.
|
||||
* @return The tag.
|
||||
*/
|
||||
Addr extractTag(Addr addr) 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);
|
||||
}
|
||||
|
||||
/**
|
||||
*iterated through all blocks and clear all locks
|
||||
*Needed to clear all lock tracking at once
|
||||
*/
|
||||
virtual void clearLocks();
|
||||
};
|
||||
|
||||
#endif // __MEM_CACHE_TAGS_FA_LRU_HH__
|
||||
648
simulators/gem5/src/mem/cache/tags/iic.cc
vendored
Normal file
648
simulators/gem5/src/mem/cache/tags/iic.cc
vendored
Normal file
@ -0,0 +1,648 @@
|
||||
/*
|
||||
* 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 <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/intmath.hh"
|
||||
#include "base/trace.hh"
|
||||
#include "debug/Cache.hh"
|
||||
#include "debug/IIC.hh"
|
||||
#include "debug/IICMore.hh"
|
||||
#include "mem/cache/tags/iic.hh"
|
||||
#include "mem/cache/base.hh"
|
||||
#include "sim/core.hh"
|
||||
|
||||
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),
|
||||
numTags(hashSets * assoc + params.size/blkSize -1),
|
||||
numSecondary(params.size/blkSize),
|
||||
tagNull(numTags),
|
||||
primaryBound(hashSets * assoc)
|
||||
{
|
||||
// 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;
|
||||
numBlocks = params.size/subSize;
|
||||
|
||||
// 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.
|
||||
unsigned data_index = 0;
|
||||
dataStore = new uint8_t[(numBlocks + numTags) * blkSize];
|
||||
dataBlks = new uint8_t*[numBlocks];
|
||||
for (unsigned 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];
|
||||
|
||||
unsigned blkIndex = 0;
|
||||
// allocate and init sets
|
||||
sets = new IICSet[hashSets];
|
||||
for (unsigned i = 0; i < hashSets; ++i) {
|
||||
sets[i].assoc = assoc;
|
||||
sets[i].tags = new IICTag*[assoc];
|
||||
sets[i].chain_ptr = tagNull;
|
||||
|
||||
for (unsigned 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 (unsigned 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->regStatsWithSuffix(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")
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
IICTag*
|
||||
IIC::accessBlock(Addr addr, int &lat, int context_src)
|
||||
{
|
||||
Addr tag = extractTag(addr);
|
||||
unsigned set = hash(addr);
|
||||
int set_lat;
|
||||
|
||||
unsigned long chain_ptr = tagNull;
|
||||
|
||||
if (PROFILE_IIC)
|
||||
setAccess.sample(set);
|
||||
|
||||
IICTag *tag_ptr = sets[set].findTag(tag, chain_ptr);
|
||||
set_lat = 1;
|
||||
if (tag_ptr == NULL && chain_ptr != tagNull) {
|
||||
int secondary_depth;
|
||||
tag_ptr = secondaryChain(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(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) const
|
||||
{
|
||||
Addr tag = extractTag(addr);
|
||||
unsigned set = hash(addr);
|
||||
|
||||
unsigned long chain_ptr = tagNull;
|
||||
|
||||
IICTag *tag_ptr = sets[set].findTag(tag, chain_ptr);
|
||||
if (tag_ptr == NULL && chain_ptr != tagNull) {
|
||||
int secondary_depth;
|
||||
tag_ptr = secondaryChain(tag, chain_ptr, &secondary_depth);
|
||||
}
|
||||
return tag_ptr;
|
||||
}
|
||||
|
||||
|
||||
IICTag*
|
||||
IIC::findVictim(Addr addr, PacketList &writebacks)
|
||||
{
|
||||
DPRINTF(IIC, "Finding Replacement for %x\n", addr);
|
||||
unsigned set = hash(addr);
|
||||
IICTag *tag_ptr;
|
||||
unsigned long *tmp_data = new unsigned long[numSub];
|
||||
|
||||
// Get a enough subblocks for a full cache line
|
||||
for (unsigned 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 (unsigned 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::insertBlock(Addr addr, BlkType* blk, int context_src)
|
||||
{
|
||||
}
|
||||
|
||||
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->isDirty() ? "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->isDirty()) {
|
||||
/* PacketPtr 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, Request::wbMasterId);
|
||||
PacketPtr writeback = new Packet(writebackReq, MemCmd::Writeback);
|
||||
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)
|
||||
{
|
||||
unsigned long data_ptr;
|
||||
|
||||
/* 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((double)hashSets)/::log((double)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(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].isValid())) {
|
||||
*_depth = depth;
|
||||
return &tagStore[chain_ptr];
|
||||
}
|
||||
depth++;
|
||||
chain_ptr = tagStore[chain_ptr].chain_ptr;
|
||||
}
|
||||
*_depth = depth;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
IIC::invalidateBlk(IIC::BlkType *tag_ptr)
|
||||
{
|
||||
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::clearLocks()
|
||||
{
|
||||
for (int i = 0; i < numTags; i++){
|
||||
tagStore[i].clearLoadLocks();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IIC::cleanupRefs()
|
||||
{
|
||||
for (unsigned i = 0; i < numTags; ++i) {
|
||||
if (tagStore[i].isValid()) {
|
||||
totalRefs += tagStore[i].refCount;
|
||||
++sampledRefs;
|
||||
}
|
||||
}
|
||||
}
|
||||
508
simulators/gem5/src/mem/cache/tags/iic.hh
vendored
Normal file
508
simulators/gem5/src/mem/cache/tags/iic.hh
vendored
Normal file
@ -0,0 +1,508 @@
|
||||
/*
|
||||
* 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 "base/statistics.hh"
|
||||
#include "mem/cache/tags/iic_repl/repl.hh"
|
||||
#include "mem/cache/tags/base.hh"
|
||||
#include "mem/cache/blk.hh"
|
||||
#include "mem/packet.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( 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 unsigned hashSets;
|
||||
/** The block size in bytes. */
|
||||
const unsigned blkSize;
|
||||
/** The associativity of the primary table. */
|
||||
const unsigned assoc;
|
||||
/** The base hit latency. */
|
||||
const unsigned hitLatency;
|
||||
/** The subblock size, used for compression. */
|
||||
const unsigned subSize;
|
||||
|
||||
/** The number of subblocks */
|
||||
const unsigned numSub;
|
||||
/** The number of bytes used by data pointers */
|
||||
const unsigned trivialSize;
|
||||
|
||||
/** The amount to shift address to get the tag. */
|
||||
const unsigned tagShift;
|
||||
/** The mask to get block offset bits. */
|
||||
const unsigned blkMask;
|
||||
|
||||
/** The amount to shift to get the subblock number. */
|
||||
const unsigned subShift;
|
||||
/** The mask to get the correct subblock number. */
|
||||
const unsigned subMask;
|
||||
|
||||
/** The latency of a hash lookup. */
|
||||
const unsigned hashDelay;
|
||||
/** The total number of tags in primary and secondary. */
|
||||
const unsigned numTags;
|
||||
/** The number of tags in the secondary tag store. */
|
||||
const unsigned numSecondary;
|
||||
|
||||
/** The Null tag pointer. */
|
||||
const unsigned tagNull;
|
||||
/** The last tag in the primary table. */
|
||||
const unsigned 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. */
|
||||
unsigned size;
|
||||
/** The number of sets in the primary table. */
|
||||
unsigned numSets;
|
||||
/** The block size in bytes. */
|
||||
unsigned blkSize;
|
||||
/** The associativity of the primary table. */
|
||||
unsigned assoc;
|
||||
/** The number of cycles for each hash lookup. */
|
||||
unsigned hashDelay;
|
||||
/** The number of cycles to read the data. */
|
||||
unsigned hitLatency;
|
||||
/** The replacement policy. */
|
||||
Repl *rp;
|
||||
/** The subblock size in bytes. */
|
||||
unsigned 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.
|
||||
*/
|
||||
unsigned
|
||||
getBlockSize() const
|
||||
{
|
||||
return blkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the subblock size.
|
||||
* @return The subblock size.
|
||||
*/
|
||||
unsigned
|
||||
getSubBlockSize() const
|
||||
{
|
||||
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.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate a block.
|
||||
* @param blk The block to invalidate.
|
||||
*/
|
||||
void invalidateBlk(BlkType *blk);
|
||||
|
||||
/**
|
||||
* Access block and update replacement data. May not succeed, in which case
|
||||
* NULL pointer is returned. This has all the implications of a cache
|
||||
* access and should only be used as such.
|
||||
* Returns the access latency and inCache flags 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* accessBlock(Addr addr, int &lat, int context_src);
|
||||
|
||||
/**
|
||||
* 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) const;
|
||||
|
||||
/**
|
||||
* Find a replacement block for the address provided.
|
||||
* @param pkt The request to a find a replacement candidate for.
|
||||
* @param writebacks List for any writebacks to be performed.
|
||||
* @return The block to place the replacement in.
|
||||
*/
|
||||
IICTag* findVictim(Addr addr, PacketList &writebacks);
|
||||
|
||||
void insertBlock(Addr addr, BlkType *blk, int context_src);
|
||||
/**
|
||||
*iterated through all blocks and clear all locks
|
||||
*Needed to clear all lock tracking at once
|
||||
*/
|
||||
virtual void clearLocks();
|
||||
|
||||
/**
|
||||
* 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(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__
|
||||
|
||||
39
simulators/gem5/src/mem/cache/tags/iic_repl/Repl.py
vendored
Normal file
39
simulators/gem5/src/mem/cache/tags/iic_repl/Repl.py
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright (c) 2005-2008 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
|
||||
|
||||
from m5.SimObject import SimObject
|
||||
from m5.params import *
|
||||
class Repl(SimObject):
|
||||
type = 'Repl'
|
||||
abstract = True
|
||||
|
||||
class GenRepl(Repl):
|
||||
type = 'GenRepl'
|
||||
fresh_res = Param.Int("Fresh pool residency time")
|
||||
num_pools = Param.Int("Number of priority pools")
|
||||
pool_res = Param.Int("Pool residency time")
|
||||
246
simulators/gem5/src/mem/cache/tags/iic_repl/gen.cc
vendored
Normal file
246
simulators/gem5/src/mem/cache/tags/iic_repl/gen.cc
vendored
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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 "base/types.hh"
|
||||
#include "mem/cache/tags/iic_repl/gen.hh"
|
||||
#include "mem/cache/tags/iic.hh"
|
||||
#include "params/GenRepl.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
GenRepl::GenRepl(const Params *p) // fix this, should be set by cache
|
||||
: Repl(p), num_pools(p->num_pools), fresh_res(p->fresh_res),
|
||||
pool_res(p->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::regStatsWithSuffix(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;
|
||||
}
|
||||
|
||||
GenRepl *
|
||||
GenReplParams::create()
|
||||
{
|
||||
return new GenRepl(this);
|
||||
}
|
||||
241
simulators/gem5/src/mem/cache/tags/iic_repl/gen.hh
vendored
Normal file
241
simulators/gem5/src/mem/cache/tags/iic_repl/gen.hh
vendored
Normal file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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/iic_repl/repl.hh"
|
||||
#include "params/GenRepl.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 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;
|
||||
/** The array of pools. */
|
||||
GenPool *pools;
|
||||
|
||||
// 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;
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
typedef GenReplParams Params;
|
||||
GenRepl(const Params *p);
|
||||
|
||||
/**
|
||||
* 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 regStatsWithSuffix(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__ */
|
||||
124
simulators/gem5/src/mem/cache/tags/iic_repl/repl.hh
vendored
Normal file
124
simulators/gem5/src/mem/cache/tags/iic_repl/repl.hh
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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 <list>
|
||||
#include <string>
|
||||
|
||||
#include "base/types.hh"
|
||||
#include "cpu/smt.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;
|
||||
|
||||
Repl (const Params *params)
|
||||
: SimObject(params)
|
||||
{
|
||||
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 regStatsWithSuffix(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 */
|
||||
236
simulators/gem5/src/mem/cache/tags/lru.cc
vendored
Normal file
236
simulators/gem5/src/mem/cache/tags/lru.cc
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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 "base/intmath.hh"
|
||||
#include "debug/CacheRepl.hh"
|
||||
#include "mem/cache/tags/cacheset.hh"
|
||||
#include "mem/cache/tags/lru.hh"
|
||||
#include "mem/cache/base.hh"
|
||||
#include "sim/core.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// create and initialize a LRU/MRU cache structure
|
||||
LRU::LRU(unsigned _numSets, unsigned _blkSize, unsigned _assoc,
|
||||
unsigned _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");
|
||||
}
|
||||
|
||||
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 BlkType[numSets * assoc];
|
||||
// allocate data storage in one big chunk
|
||||
numBlocks = numSets * assoc;
|
||||
dataBlks = new uint8_t[numBlocks * blkSize];
|
||||
|
||||
unsigned blkIndex = 0; // index into blks array
|
||||
for (unsigned i = 0; i < numSets; ++i) {
|
||||
sets[i].assoc = assoc;
|
||||
|
||||
sets[i].blks = new BlkType*[assoc];
|
||||
|
||||
// link in the data blocks
|
||||
for (unsigned j = 0; j < assoc; ++j) {
|
||||
// locate next cache block
|
||||
BlkType *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->isTouched = false;
|
||||
blk->size = blkSize;
|
||||
sets[i].blks[j]=blk;
|
||||
blk->set = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LRU::~LRU()
|
||||
{
|
||||
delete [] dataBlks;
|
||||
delete [] blks;
|
||||
delete [] sets;
|
||||
}
|
||||
|
||||
LRU::BlkType*
|
||||
LRU::accessBlock(Addr addr, int &lat, int master_id)
|
||||
{
|
||||
Addr tag = extractTag(addr);
|
||||
unsigned set = extractSet(addr);
|
||||
BlkType *blk = sets[set].findBlk(tag);
|
||||
lat = hitLatency;
|
||||
if (blk != NULL) {
|
||||
// move this block to head of the MRU list
|
||||
sets[set].moveToHead(blk);
|
||||
DPRINTF(CacheRepl, "set %x: moving blk %x to MRU\n",
|
||||
set, regenerateBlkAddr(tag, set));
|
||||
if (blk->whenReady > curTick()
|
||||
&& blk->whenReady - curTick() > hitLatency) {
|
||||
lat = blk->whenReady - curTick();
|
||||
}
|
||||
blk->refCount += 1;
|
||||
}
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
|
||||
LRU::BlkType*
|
||||
LRU::findBlock(Addr addr) const
|
||||
{
|
||||
Addr tag = extractTag(addr);
|
||||
unsigned set = extractSet(addr);
|
||||
BlkType *blk = sets[set].findBlk(tag);
|
||||
return blk;
|
||||
}
|
||||
|
||||
LRU::BlkType*
|
||||
LRU::findVictim(Addr addr, PacketList &writebacks)
|
||||
{
|
||||
unsigned set = extractSet(addr);
|
||||
// grab a replacement candidate
|
||||
BlkType *blk = sets[set].blks[assoc-1];
|
||||
|
||||
if (blk->isValid()) {
|
||||
DPRINTF(CacheRepl, "set %x: selecting blk %x for replacement\n",
|
||||
set, regenerateBlkAddr(blk->tag, set));
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
|
||||
void
|
||||
LRU::insertBlock(Addr addr, BlkType *blk, int master_id)
|
||||
{
|
||||
if (!blk->isTouched) {
|
||||
tagsInUse++;
|
||||
blk->isTouched = true;
|
||||
if (!warmedUp && tagsInUse.value() >= warmupBound) {
|
||||
warmedUp = true;
|
||||
warmupCycle = curTick();
|
||||
}
|
||||
}
|
||||
|
||||
// If we're replacing a block that was previously valid update
|
||||
// stats for it. This can't be done in findBlock() because a
|
||||
// found block might not actually be replaced there if the
|
||||
// coherence protocol says it can't be.
|
||||
if (blk->isValid()) {
|
||||
replacements[0]++;
|
||||
totalRefs += blk->refCount;
|
||||
++sampledRefs;
|
||||
blk->refCount = 0;
|
||||
|
||||
// deal with evicted block
|
||||
assert(blk->srcMasterId < cache->system->maxMasters());
|
||||
occupancies[blk->srcMasterId]--;
|
||||
}
|
||||
|
||||
// Set tag for new block. Caller is responsible for setting status.
|
||||
blk->tag = extractTag(addr);
|
||||
|
||||
// deal with what we are bringing in
|
||||
assert(master_id < cache->system->maxMasters());
|
||||
occupancies[master_id]++;
|
||||
blk->srcMasterId = master_id;
|
||||
|
||||
unsigned set = extractSet(addr);
|
||||
sets[set].moveToHead(blk);
|
||||
}
|
||||
|
||||
void
|
||||
LRU::invalidateBlk(BlkType *blk)
|
||||
{
|
||||
if (blk) {
|
||||
if (blk->isValid()) {
|
||||
tagsInUse--;
|
||||
assert(blk->srcMasterId < cache->system->maxMasters());
|
||||
occupancies[blk->srcMasterId]--;
|
||||
blk->srcMasterId = Request::invldMasterId;
|
||||
}
|
||||
blk->status = 0;
|
||||
blk->isTouched = false;
|
||||
blk->clearLoadLocks();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LRU::clearLocks()
|
||||
{
|
||||
for (int i = 0; i < numBlocks; i++){
|
||||
blks[i].clearLoadLocks();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LRU::cleanupRefs()
|
||||
{
|
||||
for (unsigned i = 0; i < numSets*assoc; ++i) {
|
||||
if (blks[i].isValid()) {
|
||||
totalRefs += blks[i].refCount;
|
||||
++sampledRefs;
|
||||
}
|
||||
}
|
||||
}
|
||||
239
simulators/gem5/src/mem/cache/tags/lru.hh
vendored
Normal file
239
simulators/gem5/src/mem/cache/tags/lru.hh
vendored
Normal file
@ -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 LRU tag store.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_CACHE_TAGS_LRU_HH__
|
||||
#define __MEM_CACHE_TAGS_LRU_HH__
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
|
||||
#include "mem/cache/tags/base.hh"
|
||||
#include "mem/cache/blk.hh"
|
||||
#include "mem/packet.hh"
|
||||
|
||||
class BaseCache;
|
||||
class CacheSet;
|
||||
|
||||
|
||||
/**
|
||||
* A LRU cache tag store.
|
||||
*/
|
||||
class LRU : public BaseTags
|
||||
{
|
||||
public:
|
||||
/** Typedef the block type used in this tag store. */
|
||||
typedef CacheBlk BlkType;
|
||||
/** Typedef for a list of pointers to the local block class. */
|
||||
typedef std::list<BlkType*> BlkList;
|
||||
|
||||
protected:
|
||||
/** The number of sets in the cache. */
|
||||
const unsigned numSets;
|
||||
/** The number of bytes in a block. */
|
||||
const unsigned blkSize;
|
||||
/** The associativity of the cache. */
|
||||
const unsigned assoc;
|
||||
/** The hit latency. */
|
||||
const unsigned hitLatency;
|
||||
|
||||
/** The cache sets. */
|
||||
CacheSet *sets;
|
||||
|
||||
/** The cache blocks. */
|
||||
BlkType *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(unsigned _numSets, unsigned _blkSize, unsigned _assoc,
|
||||
unsigned _hit_latency);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~LRU();
|
||||
|
||||
/**
|
||||
* Return the block size.
|
||||
* @return the block size.
|
||||
*/
|
||||
unsigned
|
||||
getBlockSize() const
|
||||
{
|
||||
return blkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the subblock size. In the case of LRU it is always the block
|
||||
* size.
|
||||
* @return The block size.
|
||||
*/
|
||||
unsigned
|
||||
getSubBlockSize() const
|
||||
{
|
||||
return blkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the given block.
|
||||
* @param blk The block to invalidate.
|
||||
*/
|
||||
void invalidateBlk(BlkType *blk);
|
||||
|
||||
/**
|
||||
* Access block and update replacement data. May not succeed, in which case
|
||||
* NULL pointer is returned. This has all the implications of a cache
|
||||
* access and should only be used as such. 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.
|
||||
*/
|
||||
BlkType* accessBlock(Addr addr, int &lat, int context_src);
|
||||
|
||||
/**
|
||||
* Finds the given address in the cache, do not update replacement data.
|
||||
* i.e. This is a no-side-effect find of a block.
|
||||
* @param addr The address to find.
|
||||
* @param asid The address space ID.
|
||||
* @return Pointer to the cache block if found.
|
||||
*/
|
||||
BlkType* findBlock(Addr addr) const;
|
||||
|
||||
/**
|
||||
* Find a block to evict for the address provided.
|
||||
* @param addr The addr to a find a replacement candidate for.
|
||||
* @param writebacks List for any writebacks to be performed.
|
||||
* @return The candidate block.
|
||||
*/
|
||||
BlkType* findVictim(Addr addr, PacketList &writebacks);
|
||||
|
||||
/**
|
||||
* Insert the new block into the cache. For LRU this means inserting into
|
||||
* the MRU position of the set.
|
||||
* @param addr The address to update.
|
||||
* @param blk The block to update.
|
||||
*/
|
||||
void insertBlock(Addr addr, BlkType *blk, int context_src);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
*iterated through all blocks and clear all locks
|
||||
*Needed to clear all lock tracking at once
|
||||
*/
|
||||
virtual void clearLocks();
|
||||
|
||||
/**
|
||||
* Called at end of simulation to complete average block reference stats.
|
||||
*/
|
||||
virtual void cleanupRefs();
|
||||
};
|
||||
|
||||
#endif // __MEM_CACHE_TAGS_LRU_HH__
|
||||
493
simulators/gem5/src/mem/coherent_bus.cc
Normal file
493
simulators/gem5/src/mem/coherent_bus.cc
Normal file
@ -0,0 +1,493 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Andreas Hansson
|
||||
* William Wang
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of a bus object.
|
||||
*/
|
||||
|
||||
#include "base/misc.hh"
|
||||
#include "base/trace.hh"
|
||||
#include "debug/BusAddrRanges.hh"
|
||||
#include "debug/CoherentBus.hh"
|
||||
#include "mem/coherent_bus.hh"
|
||||
|
||||
CoherentBus::CoherentBus(const CoherentBusParams *p)
|
||||
: BaseBus(p)
|
||||
{
|
||||
// create the ports based on the size of the master and slave
|
||||
// vector ports, and the presence of the default port, the ports
|
||||
// are enumerated starting from zero
|
||||
for (int i = 0; i < p->port_master_connection_count; ++i) {
|
||||
std::string portName = csprintf("%s-p%d", name(), i);
|
||||
MasterPort* bp = new CoherentBusMasterPort(portName, *this, i);
|
||||
masterPorts.push_back(bp);
|
||||
}
|
||||
|
||||
// see if we have a default slave device connected and if so add
|
||||
// our corresponding master port
|
||||
if (p->port_default_connection_count) {
|
||||
defaultPortID = masterPorts.size();
|
||||
std::string portName = csprintf("%s-default", name());
|
||||
MasterPort* bp = new CoherentBusMasterPort(portName, *this,
|
||||
defaultPortID);
|
||||
masterPorts.push_back(bp);
|
||||
}
|
||||
|
||||
// create the slave ports, once again starting at zero
|
||||
for (int i = 0; i < p->port_slave_connection_count; ++i) {
|
||||
std::string portName = csprintf("%s-p%d", name(), i);
|
||||
SlavePort* bp = new CoherentBusSlavePort(portName, *this, i);
|
||||
slavePorts.push_back(bp);
|
||||
}
|
||||
|
||||
clearPortCache();
|
||||
}
|
||||
|
||||
void
|
||||
CoherentBus::init()
|
||||
{
|
||||
// iterate over our slave ports and determine which of our
|
||||
// neighbouring master ports are snooping and add them as snoopers
|
||||
for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
|
||||
++p) {
|
||||
if ((*p)->getMasterPort().isSnooping()) {
|
||||
DPRINTF(BusAddrRanges, "Adding snooping master %s\n",
|
||||
(*p)->getMasterPort().name());
|
||||
snoopPorts.push_back(*p);
|
||||
}
|
||||
}
|
||||
|
||||
if (snoopPorts.empty())
|
||||
warn("CoherentBus %s has no snooping ports attached!\n", name());
|
||||
}
|
||||
|
||||
bool
|
||||
CoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
|
||||
{
|
||||
// determine the source port based on the id
|
||||
SlavePort *src_port = slavePorts[slave_port_id];
|
||||
|
||||
// test if the bus should be considered occupied for the current
|
||||
// port, and exclude express snoops from the check
|
||||
if (!pkt->isExpressSnoop() && isOccupied(src_port)) {
|
||||
DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
return false;
|
||||
}
|
||||
|
||||
DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
|
||||
// set the source port for routing of the response
|
||||
pkt->setSrc(slave_port_id);
|
||||
|
||||
Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
|
||||
Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
|
||||
|
||||
// uncacheable requests need never be snooped
|
||||
if (!pkt->req->isUncacheable()) {
|
||||
// the packet is a memory-mapped request and should be
|
||||
// broadcasted to our snoopers but the source
|
||||
forwardTiming(pkt, slave_port_id);
|
||||
}
|
||||
|
||||
// remember if we add an outstanding req so we can undo it if
|
||||
// necessary, if the packet needs a response, we should add it
|
||||
// as outstanding and express snoops never fail so there is
|
||||
// not need to worry about them
|
||||
bool add_outstanding = !pkt->isExpressSnoop() && pkt->needsResponse();
|
||||
|
||||
// keep track that we have an outstanding request packet
|
||||
// matching this request, this is used by the coherency
|
||||
// mechanism in determining what to do with snoop responses
|
||||
// (in recvTimingSnoop)
|
||||
if (add_outstanding) {
|
||||
// we should never have an exsiting request outstanding
|
||||
assert(outstandingReq.find(pkt->req) == outstandingReq.end());
|
||||
outstandingReq.insert(pkt->req);
|
||||
}
|
||||
|
||||
// since it is a normal request, determine the destination
|
||||
// based on the address and attempt to send the packet
|
||||
bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
|
||||
|
||||
if (!success) {
|
||||
// inhibited packets should never be forced to retry
|
||||
assert(!pkt->memInhibitAsserted());
|
||||
|
||||
// if it was added as outstanding and the send failed, then
|
||||
// erase it again
|
||||
if (add_outstanding)
|
||||
outstandingReq.erase(pkt->req);
|
||||
|
||||
DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
|
||||
addToRetryList(src_port);
|
||||
occupyBus(headerFinishTime);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
succeededTiming(packetFinishTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
|
||||
{
|
||||
// determine the source port based on the id
|
||||
MasterPort *src_port = masterPorts[master_port_id];
|
||||
|
||||
// test if the bus should be considered occupied for the current
|
||||
// port
|
||||
if (isOccupied(src_port)) {
|
||||
DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
return false;
|
||||
}
|
||||
|
||||
DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
|
||||
calcPacketTiming(pkt);
|
||||
Tick packetFinishTime = pkt->finishTime;
|
||||
|
||||
// the packet is a normal response to a request that we should
|
||||
// have seen passing through the bus
|
||||
assert(outstandingReq.find(pkt->req) != outstandingReq.end());
|
||||
|
||||
// remove it as outstanding
|
||||
outstandingReq.erase(pkt->req);
|
||||
|
||||
// send the packet to the destination through one of our slave
|
||||
// ports, as determined by the destination field
|
||||
bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
|
||||
|
||||
// currently it is illegal to block responses... can lead to
|
||||
// deadlock
|
||||
assert(success);
|
||||
|
||||
succeededTiming(packetFinishTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CoherentBus::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
|
||||
{
|
||||
DPRINTF(CoherentBus, "recvTimingSnoopReq: src %s %s 0x%x\n",
|
||||
masterPorts[master_port_id]->name(), pkt->cmdString(),
|
||||
pkt->getAddr());
|
||||
|
||||
// we should only see express snoops from caches
|
||||
assert(pkt->isExpressSnoop());
|
||||
|
||||
// set the source port for routing of the response
|
||||
pkt->setSrc(master_port_id);
|
||||
|
||||
// forward to all snoopers
|
||||
forwardTiming(pkt, InvalidPortID);
|
||||
|
||||
// a snoop request came from a connected slave device (one of
|
||||
// our master ports), and if it is not coming from the slave
|
||||
// device responsible for the address range something is
|
||||
// wrong, hence there is nothing further to do as the packet
|
||||
// would be going back to where it came from
|
||||
assert(master_port_id == findPort(pkt->getAddr()));
|
||||
|
||||
// this is an express snoop and is never forced to retry
|
||||
assert(!inRetry);
|
||||
}
|
||||
|
||||
bool
|
||||
CoherentBus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
|
||||
{
|
||||
// determine the source port based on the id
|
||||
SlavePort* src_port = slavePorts[slave_port_id];
|
||||
|
||||
// test if the bus should be considered occupied for the current
|
||||
// port
|
||||
if (isOccupied(src_port)) {
|
||||
DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
return false;
|
||||
}
|
||||
|
||||
DPRINTF(CoherentBus, "recvTimingSnoop: src %s %s 0x%x\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
|
||||
// get the destination from the packet
|
||||
PortID dest = pkt->getDest();
|
||||
|
||||
// responses are never express snoops
|
||||
assert(!pkt->isExpressSnoop());
|
||||
|
||||
calcPacketTiming(pkt);
|
||||
Tick packetFinishTime = pkt->finishTime;
|
||||
|
||||
// determine if the response is from a snoop request we
|
||||
// created as the result of a normal request (in which case it
|
||||
// should be in the outstandingReq), or if we merely forwarded
|
||||
// someone else's snoop request
|
||||
if (outstandingReq.find(pkt->req) == outstandingReq.end()) {
|
||||
// this is a snoop response to a snoop request we
|
||||
// forwarded, e.g. coming from the L1 and going to the L2
|
||||
// this should be forwarded as a snoop response
|
||||
bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoopResp(pkt);
|
||||
assert(success);
|
||||
} else {
|
||||
// we got a snoop response on one of our slave ports,
|
||||
// i.e. from a coherent master connected to the bus, and
|
||||
// since we created the snoop request as part of
|
||||
// recvTiming, this should now be a normal response again
|
||||
outstandingReq.erase(pkt->req);
|
||||
|
||||
// this is a snoop response from a coherent master, with a
|
||||
// destination field set on its way through the bus as
|
||||
// request, hence it should never go back to where the
|
||||
// snoop response came from, but instead to where the
|
||||
// original request came from
|
||||
assert(slave_port_id != dest);
|
||||
|
||||
// as a normal response, it should go back to a master
|
||||
// through one of our slave ports
|
||||
bool success M5_VAR_USED = slavePorts[dest]->sendTimingResp(pkt);
|
||||
|
||||
// currently it is illegal to block responses... can lead
|
||||
// to deadlock
|
||||
assert(success);
|
||||
}
|
||||
|
||||
succeededTiming(packetFinishTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CoherentBus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id)
|
||||
{
|
||||
for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
|
||||
SlavePort *p = *s;
|
||||
// we could have gotten this request from a snooping master
|
||||
// (corresponding to our own slave port that is also in
|
||||
// snoopPorts) and should not send it back to where it came
|
||||
// from
|
||||
if (exclude_slave_port_id == InvalidPortID ||
|
||||
p->getId() != exclude_slave_port_id) {
|
||||
// cache is not allowed to refuse snoop
|
||||
p->sendTimingSnoopReq(pkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tick
|
||||
CoherentBus::recvAtomic(PacketPtr pkt, PortID slave_port_id)
|
||||
{
|
||||
DPRINTF(CoherentBus, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
|
||||
slavePorts[slave_port_id]->name(), pkt->getAddr(),
|
||||
pkt->cmdString());
|
||||
|
||||
MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
|
||||
Tick snoop_response_latency = 0;
|
||||
|
||||
// uncacheable requests need never be snooped
|
||||
if (!pkt->req->isUncacheable()) {
|
||||
// forward to all snoopers but the source
|
||||
std::pair<MemCmd, Tick> snoop_result =
|
||||
forwardAtomic(pkt, slave_port_id);
|
||||
snoop_response_cmd = snoop_result.first;
|
||||
snoop_response_latency = snoop_result.second;
|
||||
}
|
||||
|
||||
// even if we had a snoop response, we must continue and also
|
||||
// perform the actual request at the destination
|
||||
PortID dest_id = findPort(pkt->getAddr());
|
||||
|
||||
// forward the request to the appropriate destination
|
||||
Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt);
|
||||
|
||||
// if we got a response from a snooper, restore it here
|
||||
if (snoop_response_cmd != MemCmd::InvalidCmd) {
|
||||
// no one else should have responded
|
||||
assert(!pkt->isResponse());
|
||||
pkt->cmd = snoop_response_cmd;
|
||||
response_latency = snoop_response_latency;
|
||||
}
|
||||
|
||||
pkt->finishTime = curTick() + response_latency;
|
||||
return response_latency;
|
||||
}
|
||||
|
||||
Tick
|
||||
CoherentBus::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id)
|
||||
{
|
||||
DPRINTF(CoherentBus, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n",
|
||||
masterPorts[master_port_id]->name(), pkt->getAddr(),
|
||||
pkt->cmdString());
|
||||
|
||||
// forward to all snoopers
|
||||
std::pair<MemCmd, Tick> snoop_result =
|
||||
forwardAtomic(pkt, InvalidPortID);
|
||||
MemCmd snoop_response_cmd = snoop_result.first;
|
||||
Tick snoop_response_latency = snoop_result.second;
|
||||
|
||||
if (snoop_response_cmd != MemCmd::InvalidCmd)
|
||||
pkt->cmd = snoop_response_cmd;
|
||||
|
||||
pkt->finishTime = curTick() + snoop_response_latency;
|
||||
return snoop_response_latency;
|
||||
}
|
||||
|
||||
std::pair<MemCmd, Tick>
|
||||
CoherentBus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id)
|
||||
{
|
||||
// the packet may be changed on snoops, record the original
|
||||
// command to enable us to restore it between snoops so that
|
||||
// additional snoops can take place properly
|
||||
MemCmd orig_cmd = pkt->cmd;
|
||||
MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
|
||||
Tick snoop_response_latency = 0;
|
||||
|
||||
for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
|
||||
SlavePort *p = *s;
|
||||
// we could have gotten this request from a snooping master
|
||||
// (corresponding to our own slave port that is also in
|
||||
// snoopPorts) and should not send it back to where it came
|
||||
// from
|
||||
if (exclude_slave_port_id == InvalidPortID ||
|
||||
p->getId() != exclude_slave_port_id) {
|
||||
Tick latency = p->sendAtomicSnoop(pkt);
|
||||
// in contrast to a functional access, we have to keep on
|
||||
// going as all snoopers must be updated even if we get a
|
||||
// response
|
||||
if (pkt->isResponse()) {
|
||||
// response from snoop agent
|
||||
assert(pkt->cmd != orig_cmd);
|
||||
assert(pkt->memInhibitAsserted());
|
||||
// should only happen once
|
||||
assert(snoop_response_cmd == MemCmd::InvalidCmd);
|
||||
// save response state
|
||||
snoop_response_cmd = pkt->cmd;
|
||||
snoop_response_latency = latency;
|
||||
// restore original packet state for remaining snoopers
|
||||
pkt->cmd = orig_cmd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the packet is restored as part of the loop and any potential
|
||||
// snoop response is part of the returned pair
|
||||
return std::make_pair(snoop_response_cmd, snoop_response_latency);
|
||||
}
|
||||
|
||||
void
|
||||
CoherentBus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
|
||||
{
|
||||
if (!pkt->isPrint()) {
|
||||
// don't do DPRINTFs on PrintReq as it clutters up the output
|
||||
DPRINTF(CoherentBus,
|
||||
"recvFunctional: packet src %s addr 0x%x cmd %s\n",
|
||||
slavePorts[slave_port_id]->name(), pkt->getAddr(),
|
||||
pkt->cmdString());
|
||||
}
|
||||
|
||||
// uncacheable requests need never be snooped
|
||||
if (!pkt->req->isUncacheable()) {
|
||||
// forward to all snoopers but the source
|
||||
forwardFunctional(pkt, slave_port_id);
|
||||
}
|
||||
|
||||
// there is no need to continue if the snooping has found what we
|
||||
// were looking for and the packet is already a response
|
||||
if (!pkt->isResponse()) {
|
||||
PortID dest_id = findPort(pkt->getAddr());
|
||||
|
||||
masterPorts[dest_id]->sendFunctional(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CoherentBus::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id)
|
||||
{
|
||||
if (!pkt->isPrint()) {
|
||||
// don't do DPRINTFs on PrintReq as it clutters up the output
|
||||
DPRINTF(CoherentBus,
|
||||
"recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n",
|
||||
masterPorts[master_port_id]->name(), pkt->getAddr(),
|
||||
pkt->cmdString());
|
||||
}
|
||||
|
||||
// forward to all snoopers
|
||||
forwardFunctional(pkt, InvalidPortID);
|
||||
}
|
||||
|
||||
void
|
||||
CoherentBus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
|
||||
{
|
||||
for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
|
||||
SlavePort *p = *s;
|
||||
// we could have gotten this request from a snooping master
|
||||
// (corresponding to our own slave port that is also in
|
||||
// snoopPorts) and should not send it back to where it came
|
||||
// from
|
||||
if (exclude_slave_port_id == InvalidPortID ||
|
||||
p->getId() != exclude_slave_port_id)
|
||||
p->sendFunctionalSnoop(pkt);
|
||||
|
||||
// if we get a response we are done
|
||||
if (pkt->isResponse()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CoherentBus *
|
||||
CoherentBusParams::create()
|
||||
{
|
||||
return new CoherentBus(this);
|
||||
}
|
||||
290
simulators/gem5/src/mem/coherent_bus.hh
Normal file
290
simulators/gem5/src/mem/coherent_bus.hh
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Ron Dreslinski
|
||||
* Ali Saidi
|
||||
* Andreas Hansson
|
||||
* William Wang
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Declaration of a coherent bus.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_COHERENT_BUS_HH__
|
||||
#define __MEM_COHERENT_BUS_HH__
|
||||
|
||||
#include "mem/bus.hh"
|
||||
#include "params/CoherentBus.hh"
|
||||
|
||||
/**
|
||||
* A coherent bus connects a number of (potentially) snooping masters
|
||||
* and slaves, and routes the request and response packets based on
|
||||
* the address, and also forwards all requests to the snoopers and
|
||||
* deals with the snoop responses.
|
||||
*
|
||||
* The coherent bus can be used as a template for modelling QPI,
|
||||
* HyperTransport, ACE and coherent OCP buses, and is typically used
|
||||
* for the L1-to-L2 buses and as the main system interconnect.
|
||||
*/
|
||||
class CoherentBus : public BaseBus
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Declaration of the coherent bus slave port type, one will be
|
||||
* instantiated for each of the master ports connecting to the
|
||||
* bus.
|
||||
*/
|
||||
class CoherentBusSlavePort : public SlavePort
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
/** A reference to the bus to which this port belongs. */
|
||||
CoherentBus &bus;
|
||||
|
||||
public:
|
||||
|
||||
CoherentBusSlavePort(const std::string &_name,
|
||||
CoherentBus &_bus, PortID _id)
|
||||
: SlavePort(_name, &_bus, _id), bus(_bus)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* When receiving a timing request, pass it to the bus.
|
||||
*/
|
||||
virtual bool recvTimingReq(PacketPtr pkt)
|
||||
{ return bus.recvTimingReq(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving a timing snoop response, pass it to the bus.
|
||||
*/
|
||||
virtual bool recvTimingSnoopResp(PacketPtr pkt)
|
||||
{ return bus.recvTimingSnoopResp(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving an atomic request, pass it to the bus.
|
||||
*/
|
||||
virtual Tick recvAtomic(PacketPtr pkt)
|
||||
{ return bus.recvAtomic(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving a functional request, pass it to the bus.
|
||||
*/
|
||||
virtual void recvFunctional(PacketPtr pkt)
|
||||
{ bus.recvFunctional(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving a retry, pass it to the bus.
|
||||
*/
|
||||
virtual void recvRetry()
|
||||
{ panic("Bus slave ports always succeed and should never retry.\n"); }
|
||||
|
||||
/**
|
||||
* Return the union of all adress ranges seen by this bus.
|
||||
*/
|
||||
virtual AddrRangeList getAddrRanges()
|
||||
{ return bus.getAddrRanges(); }
|
||||
|
||||
/**
|
||||
* Get the maximum block size as seen by the bus.
|
||||
*/
|
||||
virtual unsigned deviceBlockSize() const
|
||||
{ return bus.findBlockSize(); }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Declaration of the coherent bus master port type, one will be
|
||||
* instantiated for each of the slave interfaces connecting to the
|
||||
* bus.
|
||||
*/
|
||||
class CoherentBusMasterPort : public MasterPort
|
||||
{
|
||||
private:
|
||||
/** A reference to the bus to which this port belongs. */
|
||||
CoherentBus &bus;
|
||||
|
||||
public:
|
||||
|
||||
CoherentBusMasterPort(const std::string &_name,
|
||||
CoherentBus &_bus, PortID _id)
|
||||
: MasterPort(_name, &_bus, _id), bus(_bus)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Determine if this port should be considered a snooper. For
|
||||
* a coherent bus master port this is always true.
|
||||
*
|
||||
* @return a boolean that is true if this port is snooping
|
||||
*/
|
||||
virtual bool isSnooping() const
|
||||
{ return true; }
|
||||
|
||||
/**
|
||||
* When receiving a timing response, pass it to the bus.
|
||||
*/
|
||||
virtual bool recvTimingResp(PacketPtr pkt)
|
||||
{ return bus.recvTimingResp(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving a timing snoop request, pass it to the bus.
|
||||
*/
|
||||
virtual void recvTimingSnoopReq(PacketPtr pkt)
|
||||
{ return bus.recvTimingSnoopReq(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving an atomic snoop request, pass it to the bus.
|
||||
*/
|
||||
virtual Tick recvAtomicSnoop(PacketPtr pkt)
|
||||
{ return bus.recvAtomicSnoop(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving a functional snoop request, pass it to the bus.
|
||||
*/
|
||||
virtual void recvFunctionalSnoop(PacketPtr pkt)
|
||||
{ bus.recvFunctionalSnoop(pkt, id); }
|
||||
|
||||
/** When reciving a range change from the peer port (at id),
|
||||
pass it to the bus. */
|
||||
virtual void recvRangeChange()
|
||||
{ bus.recvRangeChange(id); }
|
||||
|
||||
/** When reciving a retry from the peer port (at id),
|
||||
pass it to the bus. */
|
||||
virtual void recvRetry()
|
||||
{ bus.recvRetry(); }
|
||||
|
||||
// Ask the bus to ask everyone on the bus what their block size is and
|
||||
// take the max of it. This might need to be changed a bit if we ever
|
||||
// support multiple block sizes.
|
||||
virtual unsigned deviceBlockSize() const
|
||||
{ return bus.findBlockSize(); }
|
||||
|
||||
};
|
||||
|
||||
std::vector<SlavePort*> snoopPorts;
|
||||
|
||||
/**
|
||||
* Store the outstanding requests so we can determine which ones
|
||||
* we generated and which ones were merely forwarded. This is used
|
||||
* in the coherent bus when coherency responses come back.
|
||||
*/
|
||||
std::set<RequestPtr> outstandingReq;
|
||||
|
||||
/** Function called by the port when the bus is recieving a Timing
|
||||
request packet.*/
|
||||
bool recvTimingReq(PacketPtr pkt, PortID slave_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving a Timing
|
||||
response packet.*/
|
||||
bool recvTimingResp(PacketPtr pkt, PortID master_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving a timing
|
||||
snoop request.*/
|
||||
void recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving a timing
|
||||
snoop response.*/
|
||||
bool recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id);
|
||||
|
||||
/**
|
||||
* Forward a timing packet to our snoopers, potentially excluding
|
||||
* one of the connected coherent masters to avoid sending a packet
|
||||
* back to where it came from.
|
||||
*
|
||||
* @param pkt Packet to forward
|
||||
* @param exclude_slave_port_id Id of slave port to exclude
|
||||
*/
|
||||
void forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving a Atomic
|
||||
transaction.*/
|
||||
Tick recvAtomic(PacketPtr pkt, PortID slave_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving an
|
||||
atomic snoop transaction.*/
|
||||
Tick recvAtomicSnoop(PacketPtr pkt, PortID master_port_id);
|
||||
|
||||
/**
|
||||
* Forward an atomic packet to our snoopers, potentially excluding
|
||||
* one of the connected coherent masters to avoid sending a packet
|
||||
* back to where it came from.
|
||||
*
|
||||
* @param pkt Packet to forward
|
||||
* @param exclude_slave_port_id Id of slave port to exclude
|
||||
*
|
||||
* @return a pair containing the snoop response and snoop latency
|
||||
*/
|
||||
std::pair<MemCmd, Tick> forwardAtomic(PacketPtr pkt,
|
||||
PortID exclude_slave_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving a Functional
|
||||
transaction.*/
|
||||
void recvFunctional(PacketPtr pkt, PortID slave_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving a functional
|
||||
snoop transaction.*/
|
||||
void recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id);
|
||||
|
||||
/**
|
||||
* Forward a functional packet to our snoopers, potentially
|
||||
* excluding one of the connected coherent masters to avoid
|
||||
* sending a packet back to where it came from.
|
||||
*
|
||||
* @param pkt Packet to forward
|
||||
* @param exclude_slave_port_id Id of slave port to exclude
|
||||
*/
|
||||
void forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id);
|
||||
|
||||
public:
|
||||
|
||||
virtual void init();
|
||||
|
||||
CoherentBus(const CoherentBusParams *p);
|
||||
};
|
||||
|
||||
#endif //__MEM_COHERENT_BUS_HH__
|
||||
538
simulators/gem5/src/mem/comm_monitor.cc
Normal file
538
simulators/gem5/src/mem/comm_monitor.cc
Normal file
@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Thomas Grass
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
#include "debug/CommMonitor.hh"
|
||||
#include "mem/comm_monitor.hh"
|
||||
#include "sim/stats.hh"
|
||||
|
||||
CommMonitor::CommMonitor(Params* params)
|
||||
: MemObject(params),
|
||||
masterPort(name() + "-master", *this),
|
||||
slavePort(name() + "-slave", *this),
|
||||
samplePeriodicEvent(this),
|
||||
samplePeriodTicks(params->sample_period),
|
||||
readAddrMask(params->read_addr_mask),
|
||||
writeAddrMask(params->write_addr_mask),
|
||||
stats(params)
|
||||
{
|
||||
// keep track of the sample period both in ticks and absolute time
|
||||
samplePeriod.setTick(params->sample_period);
|
||||
|
||||
DPRINTF(CommMonitor,
|
||||
"Created monitor %s with sample period %d ticks (%f s)\n",
|
||||
name(), samplePeriodTicks, samplePeriod);
|
||||
}
|
||||
|
||||
CommMonitor*
|
||||
CommMonitorParams::create()
|
||||
{
|
||||
return new CommMonitor(this);
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::init()
|
||||
{
|
||||
// make sure both sides of the monitor are connected
|
||||
if (!slavePort.isConnected() || !masterPort.isConnected())
|
||||
fatal("Communication monitor is not connected on both sides.\n");
|
||||
}
|
||||
|
||||
MasterPort&
|
||||
CommMonitor::getMasterPort(const std::string& if_name, int idx)
|
||||
{
|
||||
if (if_name == "master") {
|
||||
return masterPort;
|
||||
} else {
|
||||
return MemObject::getMasterPort(if_name, idx);
|
||||
}
|
||||
}
|
||||
|
||||
SlavePort&
|
||||
CommMonitor::getSlavePort(const std::string& if_name, int idx)
|
||||
{
|
||||
if (if_name == "slave") {
|
||||
return slavePort;
|
||||
} else {
|
||||
return MemObject::getSlavePort(if_name, idx);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::recvFunctional(PacketPtr pkt)
|
||||
{
|
||||
masterPort.sendFunctional(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::recvFunctionalSnoop(PacketPtr pkt)
|
||||
{
|
||||
slavePort.sendFunctionalSnoop(pkt);
|
||||
}
|
||||
|
||||
Tick
|
||||
CommMonitor::recvAtomic(PacketPtr pkt)
|
||||
{
|
||||
return masterPort.sendAtomic(pkt);
|
||||
}
|
||||
|
||||
Tick
|
||||
CommMonitor::recvAtomicSnoop(PacketPtr pkt)
|
||||
{
|
||||
return slavePort.sendAtomicSnoop(pkt);
|
||||
}
|
||||
|
||||
bool
|
||||
CommMonitor::recvTimingReq(PacketPtr pkt)
|
||||
{
|
||||
// should always see a request
|
||||
assert(pkt->isRequest());
|
||||
|
||||
// Store relevant fields of packet, because packet may be modified
|
||||
// or even deleted when sendTiming() is called.
|
||||
bool isRead = pkt->isRead();
|
||||
bool isWrite = pkt->isWrite();
|
||||
unsigned size = pkt->getSize();
|
||||
Addr addr = pkt->getAddr();
|
||||
bool needsResponse = pkt->needsResponse();
|
||||
bool memInhibitAsserted = pkt->memInhibitAsserted();
|
||||
Packet::SenderState* senderState = pkt->senderState;
|
||||
|
||||
// If a cache miss is served by a cache, a monitor near the memory
|
||||
// would see a request which needs a response, but this response
|
||||
// would be inhibited and not come back from the memory. Therefore
|
||||
// we additionally have to check the inhibit flag.
|
||||
if (needsResponse && !memInhibitAsserted && !stats.disableLatencyHists) {
|
||||
pkt->senderState = new CommMonitorSenderState(senderState,
|
||||
curTick());
|
||||
}
|
||||
|
||||
// Attempt to send the packet (always succeeds for inhibited
|
||||
// packets)
|
||||
bool successful = masterPort.sendTimingReq(pkt);
|
||||
|
||||
// If not successful, restore the sender state
|
||||
if (!successful && needsResponse && !stats.disableLatencyHists) {
|
||||
delete pkt->senderState;
|
||||
pkt->senderState = senderState;
|
||||
}
|
||||
|
||||
if (successful && isRead) {
|
||||
DPRINTF(CommMonitor, "Forwarded read request\n");
|
||||
|
||||
// Increment number of observed read transactions
|
||||
if (!stats.disableTransactionHists) {
|
||||
++stats.readTrans;
|
||||
}
|
||||
|
||||
// Get sample of burst length
|
||||
if (!stats.disableBurstLengthHists) {
|
||||
stats.readBurstLengthHist.sample(size);
|
||||
}
|
||||
|
||||
// Sample the masked address
|
||||
if (!stats.disableAddrDists) {
|
||||
stats.readAddrDist.sample(addr & readAddrMask);
|
||||
}
|
||||
|
||||
// If it needs a response increment number of outstanding read
|
||||
// requests
|
||||
if (!stats.disableOutstandingHists && needsResponse) {
|
||||
++stats.outstandingReadReqs;
|
||||
}
|
||||
|
||||
if (!stats.disableITTDists) {
|
||||
// Sample value of read-read inter transaction time
|
||||
if (stats.timeOfLastRead != 0) {
|
||||
stats.ittReadRead.sample(curTick() - stats.timeOfLastRead);
|
||||
}
|
||||
stats.timeOfLastRead = curTick();
|
||||
|
||||
// Sample value of req-req inter transaction time
|
||||
if (stats.timeOfLastReq != 0) {
|
||||
stats.ittReqReq.sample(curTick() - stats.timeOfLastReq);
|
||||
}
|
||||
stats.timeOfLastReq = curTick();
|
||||
}
|
||||
} else if (successful && isWrite) {
|
||||
DPRINTF(CommMonitor, "Forwarded write request\n");
|
||||
|
||||
// Same as for reads
|
||||
if (!stats.disableTransactionHists) {
|
||||
++stats.writeTrans;
|
||||
}
|
||||
|
||||
if (!stats.disableBurstLengthHists) {
|
||||
stats.writeBurstLengthHist.sample(size);
|
||||
}
|
||||
|
||||
// Update the bandwidth stats on the request
|
||||
if (!stats.disableBandwidthHists) {
|
||||
stats.writtenBytes += size;
|
||||
stats.totalWrittenBytes += size;
|
||||
}
|
||||
|
||||
// Sample the masked write address
|
||||
if (!stats.disableAddrDists) {
|
||||
stats.writeAddrDist.sample(addr & writeAddrMask);
|
||||
}
|
||||
|
||||
if (!stats.disableOutstandingHists && needsResponse) {
|
||||
++stats.outstandingWriteReqs;
|
||||
}
|
||||
|
||||
if (!stats.disableITTDists) {
|
||||
// Sample value of write-to-write inter transaction time
|
||||
if (stats.timeOfLastWrite != 0) {
|
||||
stats.ittWriteWrite.sample(curTick() - stats.timeOfLastWrite);
|
||||
}
|
||||
stats.timeOfLastWrite = curTick();
|
||||
|
||||
// Sample value of req-to-req inter transaction time
|
||||
if (stats.timeOfLastReq != 0) {
|
||||
stats.ittReqReq.sample(curTick() - stats.timeOfLastReq);
|
||||
}
|
||||
stats.timeOfLastReq = curTick();
|
||||
}
|
||||
} else if (successful) {
|
||||
DPRINTF(CommMonitor, "Forwarded non read/write request\n");
|
||||
}
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
bool
|
||||
CommMonitor::recvTimingResp(PacketPtr pkt)
|
||||
{
|
||||
// should always see responses
|
||||
assert(pkt->isResponse());
|
||||
|
||||
// Store relevant fields of packet, because packet may be modified
|
||||
// or even deleted when sendTiming() is called.
|
||||
bool isRead = pkt->isRead();
|
||||
bool isWrite = pkt->isWrite();
|
||||
unsigned size = pkt->getSize();
|
||||
Tick latency = 0;
|
||||
CommMonitorSenderState* commReceivedState =
|
||||
dynamic_cast<CommMonitorSenderState*>(pkt->senderState);
|
||||
|
||||
if (!stats.disableLatencyHists) {
|
||||
// Restore initial sender state
|
||||
if (commReceivedState == NULL)
|
||||
panic("Monitor got a response without monitor sender state\n");
|
||||
|
||||
// Restore the sate
|
||||
pkt->senderState = commReceivedState->origSenderState;
|
||||
}
|
||||
|
||||
// Attempt to send the packet
|
||||
bool successful = slavePort.sendTimingResp(pkt);
|
||||
|
||||
if (!stats.disableLatencyHists) {
|
||||
// If packet successfully send, sample value of latency,
|
||||
// afterwards delete sender state, otherwise restore state
|
||||
if (successful) {
|
||||
latency = curTick() - commReceivedState->transmitTime;
|
||||
DPRINTF(CommMonitor, "Latency: %d\n", latency);
|
||||
delete commReceivedState;
|
||||
} else {
|
||||
// Don't delete anything and let the packet look like we
|
||||
// did not touch it
|
||||
pkt->senderState = commReceivedState;
|
||||
}
|
||||
}
|
||||
|
||||
if (successful && isRead) {
|
||||
// Decrement number of outstanding read requests
|
||||
DPRINTF(CommMonitor, "Received read response\n");
|
||||
if (!stats.disableOutstandingHists) {
|
||||
assert(stats.outstandingReadReqs != 0);
|
||||
--stats.outstandingReadReqs;
|
||||
}
|
||||
|
||||
if (!stats.disableLatencyHists) {
|
||||
stats.readLatencyHist.sample(latency);
|
||||
}
|
||||
|
||||
// Update the bandwidth stats based on responses for reads
|
||||
if (!stats.disableBandwidthHists) {
|
||||
stats.readBytes += size;
|
||||
stats.totalReadBytes += size;
|
||||
}
|
||||
|
||||
} else if (successful && isWrite) {
|
||||
// Decrement number of outstanding write requests
|
||||
DPRINTF(CommMonitor, "Received write response\n");
|
||||
if (!stats.disableOutstandingHists) {
|
||||
assert(stats.outstandingWriteReqs != 0);
|
||||
--stats.outstandingWriteReqs;
|
||||
}
|
||||
|
||||
if (!stats.disableLatencyHists) {
|
||||
stats.writeLatencyHist.sample(latency);
|
||||
}
|
||||
} else if (successful) {
|
||||
DPRINTF(CommMonitor, "Received non read/write response\n");
|
||||
}
|
||||
return successful;
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::recvTimingSnoopReq(PacketPtr pkt)
|
||||
{
|
||||
slavePort.sendTimingSnoopReq(pkt);
|
||||
}
|
||||
|
||||
bool
|
||||
CommMonitor::recvTimingSnoopResp(PacketPtr pkt)
|
||||
{
|
||||
return masterPort.sendTimingSnoopResp(pkt);
|
||||
}
|
||||
|
||||
bool
|
||||
CommMonitor::isSnooping() const
|
||||
{
|
||||
return slavePort.getMasterPort().isSnooping();
|
||||
}
|
||||
|
||||
unsigned
|
||||
CommMonitor::deviceBlockSizeMaster()
|
||||
{
|
||||
return slavePort.peerBlockSize();
|
||||
}
|
||||
|
||||
unsigned
|
||||
CommMonitor::deviceBlockSizeSlave()
|
||||
{
|
||||
return masterPort.peerBlockSize();
|
||||
}
|
||||
|
||||
AddrRangeList
|
||||
CommMonitor::getAddrRanges()
|
||||
{
|
||||
return masterPort.getSlavePort().getAddrRanges();
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::recvRetryMaster()
|
||||
{
|
||||
slavePort.sendRetry();
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::recvRetrySlave()
|
||||
{
|
||||
masterPort.sendRetry();
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::recvRangeChange()
|
||||
{
|
||||
slavePort.sendRangeChange();
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::regStats()
|
||||
{
|
||||
// Initialise all the monitor stats
|
||||
using namespace Stats;
|
||||
|
||||
stats.readBurstLengthHist
|
||||
.init(params()->burst_length_bins)
|
||||
.name(name() + ".readBurstLengthHist")
|
||||
.desc("Histogram of burst lengths of transmitted packets")
|
||||
.flags(stats.disableBurstLengthHists ? nozero : pdf);
|
||||
|
||||
stats.writeBurstLengthHist
|
||||
.init(params()->burst_length_bins)
|
||||
.name(name() + ".writeBurstLengthHist")
|
||||
.desc("Histogram of burst lengths of transmitted packets")
|
||||
.flags(stats.disableBurstLengthHists ? nozero : pdf);
|
||||
|
||||
// Stats based on received responses
|
||||
stats.readBandwidthHist
|
||||
.init(params()->bandwidth_bins)
|
||||
.name(name() + ".readBandwidthHist")
|
||||
.desc("Histogram of read bandwidth per sample period (bytes/s)")
|
||||
.flags(stats.disableBandwidthHists ? nozero : pdf);
|
||||
|
||||
stats.averageReadBW
|
||||
.name(name() + ".averageReadBandwidth")
|
||||
.desc("Average read bandwidth (bytes/s)")
|
||||
.flags(stats.disableBandwidthHists ? nozero : pdf);
|
||||
|
||||
stats.totalReadBytes
|
||||
.name(name() + ".totalReadBytes")
|
||||
.desc("Number of bytes read")
|
||||
.flags(stats.disableBandwidthHists ? nozero : pdf);
|
||||
|
||||
stats.averageReadBW = stats.totalReadBytes / simSeconds;
|
||||
|
||||
// Stats based on successfully sent requests
|
||||
stats.writeBandwidthHist
|
||||
.init(params()->bandwidth_bins)
|
||||
.name(name() + ".writeBandwidthHist")
|
||||
.desc("Histogram of write bandwidth (bytes/s)")
|
||||
.flags(stats.disableBandwidthHists ? (pdf | nozero) : pdf);
|
||||
|
||||
stats.averageWriteBW
|
||||
.name(name() + ".averageWriteBandwidth")
|
||||
.desc("Average write bandwidth (bytes/s)")
|
||||
.flags(stats.disableBandwidthHists ? nozero : pdf);
|
||||
|
||||
stats.totalWrittenBytes
|
||||
.name(name() + ".totalWrittenBytes")
|
||||
.desc("Number of bytes written")
|
||||
.flags(stats.disableBandwidthHists ? nozero : pdf);
|
||||
|
||||
stats.averageWriteBW = stats.totalWrittenBytes / simSeconds;
|
||||
|
||||
stats.readLatencyHist
|
||||
.init(params()->latency_bins)
|
||||
.name(name() + ".readLatencyHist")
|
||||
.desc("Read request-response latency")
|
||||
.flags(stats.disableLatencyHists ? nozero : pdf);
|
||||
|
||||
stats.writeLatencyHist
|
||||
.init(params()->latency_bins)
|
||||
.name(name() + ".writeLatencyHist")
|
||||
.desc("Write request-response latency")
|
||||
.flags(stats.disableLatencyHists ? nozero : pdf);
|
||||
|
||||
stats.ittReadRead
|
||||
.init(1, params()->itt_max_bin, params()->itt_max_bin /
|
||||
params()->itt_bins)
|
||||
.name(name() + ".ittReadRead")
|
||||
.desc("Read-to-read inter transaction time")
|
||||
.flags(stats.disableITTDists ? nozero : pdf);
|
||||
|
||||
stats.ittWriteWrite
|
||||
.init(1, params()->itt_max_bin, params()->itt_max_bin /
|
||||
params()->itt_bins)
|
||||
.name(name() + ".ittWriteWrite")
|
||||
.desc("Write-to-write inter transaction time")
|
||||
.flags(stats.disableITTDists ? nozero : pdf);
|
||||
|
||||
stats.ittReqReq
|
||||
.init(1, params()->itt_max_bin, params()->itt_max_bin /
|
||||
params()->itt_bins)
|
||||
.name(name() + ".ittReqReq")
|
||||
.desc("Request-to-request inter transaction time")
|
||||
.flags(stats.disableITTDists ? nozero : pdf);
|
||||
|
||||
stats.outstandingReadsHist
|
||||
.init(params()->outstanding_bins)
|
||||
.name(name() + ".outstandingReadsHist")
|
||||
.desc("Outstanding read transactions")
|
||||
.flags(stats.disableOutstandingHists ? nozero : pdf);
|
||||
|
||||
stats.outstandingWritesHist
|
||||
.init(params()->outstanding_bins)
|
||||
.name(name() + ".outstandingWritesHist")
|
||||
.desc("Outstanding write transactions")
|
||||
.flags(stats.disableOutstandingHists ? nozero : pdf);
|
||||
|
||||
stats.readTransHist
|
||||
.init(params()->transaction_bins)
|
||||
.name(name() + ".readTransHist")
|
||||
.desc("Histogram of read transactions per sample period")
|
||||
.flags(stats.disableTransactionHists ? nozero : pdf);
|
||||
|
||||
stats.writeTransHist
|
||||
.init(params()->transaction_bins)
|
||||
.name(name() + ".writeTransHist")
|
||||
.desc("Histogram of read transactions per sample period")
|
||||
.flags(stats.disableTransactionHists ? nozero : pdf);
|
||||
|
||||
stats.readAddrDist
|
||||
.init(0)
|
||||
.name(name() + ".readAddrDist")
|
||||
.desc("Read address distribution")
|
||||
.flags(stats.disableAddrDists ? nozero : pdf);
|
||||
|
||||
stats.writeAddrDist
|
||||
.init(0)
|
||||
.name(name() + ".writeAddrDist")
|
||||
.desc("Write address distribution")
|
||||
.flags(stats.disableAddrDists ? nozero : pdf);
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::samplePeriodic()
|
||||
{
|
||||
// the periodic stats update runs on the granularity of sample
|
||||
// periods, but in combination with this there may also be a
|
||||
// external resets and dumps of the stats (through schedStatEvent)
|
||||
// causing the stats themselves to capture less than a sample
|
||||
// period
|
||||
|
||||
// only capture if we have not reset the stats during the last
|
||||
// sample period
|
||||
if (simTicks.value() >= samplePeriodTicks) {
|
||||
if (!stats.disableTransactionHists) {
|
||||
stats.readTransHist.sample(stats.readTrans);
|
||||
stats.writeTransHist.sample(stats.writeTrans);
|
||||
}
|
||||
|
||||
if (!stats.disableBandwidthHists) {
|
||||
stats.readBandwidthHist.sample(stats.readBytes / samplePeriod);
|
||||
stats.writeBandwidthHist.sample(stats.writtenBytes / samplePeriod);
|
||||
}
|
||||
|
||||
if (!stats.disableOutstandingHists) {
|
||||
stats.outstandingReadsHist.sample(stats.outstandingReadReqs);
|
||||
stats.outstandingWritesHist.sample(stats.outstandingWriteReqs);
|
||||
}
|
||||
}
|
||||
|
||||
// reset the sampled values
|
||||
stats.readTrans = 0;
|
||||
stats.writeTrans = 0;
|
||||
|
||||
stats.readBytes = 0;
|
||||
stats.writtenBytes = 0;
|
||||
|
||||
schedule(samplePeriodicEvent, curTick() + samplePeriodTicks);
|
||||
}
|
||||
|
||||
void
|
||||
CommMonitor::startup()
|
||||
{
|
||||
schedule(samplePeriodicEvent, curTick() + samplePeriodTicks);
|
||||
}
|
||||
432
simulators/gem5/src/mem/comm_monitor.hh
Normal file
432
simulators/gem5/src/mem/comm_monitor.hh
Normal file
@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Thomas Grass
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
#ifndef __MEM_COMM_MONITOR_HH__
|
||||
#define __MEM_COMM_MONITOR_HH__
|
||||
|
||||
#include "base/statistics.hh"
|
||||
#include "base/time.hh"
|
||||
#include "mem/mem_object.hh"
|
||||
#include "params/CommMonitor.hh"
|
||||
|
||||
/**
|
||||
* The communication monitor is a MemObject which can monitor statistics of
|
||||
* the communication happening between two ports in the memory system.
|
||||
*
|
||||
* Currently the following stats are implemented: Histograms of read/write
|
||||
* transactions, read/write burst lengths, read/write bandwidth,
|
||||
* outstanding read/write requests, read latency and inter transaction time
|
||||
* (read-read, write-write, read/write-read/write). Furthermore it allows
|
||||
* to capture the number of accesses to an address over time ("heat map").
|
||||
* All stats can be disabled from Python.
|
||||
*/
|
||||
class CommMonitor : public MemObject
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/** Parameters of communication monitor */
|
||||
typedef CommMonitorParams Params;
|
||||
const Params* params() const
|
||||
{ return reinterpret_cast<const Params*>(_params); }
|
||||
|
||||
/**
|
||||
* Constructor based on the Python params
|
||||
*
|
||||
* @param params Python parameters
|
||||
*/
|
||||
CommMonitor(Params* params);
|
||||
|
||||
/** Destructor */
|
||||
~CommMonitor() { }
|
||||
|
||||
virtual MasterPort& getMasterPort(const std::string& if_name,
|
||||
int idx = -1);
|
||||
|
||||
virtual SlavePort& getSlavePort(const std::string& if_name,
|
||||
int idx = -1);
|
||||
|
||||
virtual void init();
|
||||
|
||||
/** Register statistics */
|
||||
void regStats();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Sender state class for the monitor so that we can annotate
|
||||
* packets with a transmit time and receive time.
|
||||
*/
|
||||
class CommMonitorSenderState : public Packet::SenderState
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a new sender state and remember the original one
|
||||
* so that we can implement a stack.
|
||||
*
|
||||
* @param _origSenderState Sender state to remember
|
||||
* @param _transmitTime Time of packet transmission
|
||||
*/
|
||||
CommMonitorSenderState(SenderState* _origSenderState,
|
||||
Tick _transmitTime)
|
||||
: origSenderState(_origSenderState), transmitTime(_transmitTime)
|
||||
{ }
|
||||
|
||||
/** Destructor */
|
||||
~CommMonitorSenderState() { }
|
||||
|
||||
/** Pointer to old sender state of packet */
|
||||
SenderState* origSenderState;
|
||||
|
||||
/** Tick when request is transmitted */
|
||||
Tick transmitTime;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the master port of the communication monitor. All recv
|
||||
* functions call a function in CommMonitor, where the
|
||||
* send function of the slave port is called. Besides this, these
|
||||
* functions can also perform actions for capturing statistics.
|
||||
*/
|
||||
class MonitorMasterPort : public MasterPort
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
MonitorMasterPort(const std::string& _name, CommMonitor& _mon)
|
||||
: MasterPort(_name, &_mon), mon(_mon)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
|
||||
void recvFunctionalSnoop(PacketPtr pkt)
|
||||
{
|
||||
mon.recvFunctionalSnoop(pkt);
|
||||
}
|
||||
|
||||
Tick recvAtomicSnoop(PacketPtr pkt)
|
||||
{
|
||||
return mon.recvAtomicSnoop(pkt);
|
||||
}
|
||||
|
||||
bool recvTimingResp(PacketPtr pkt)
|
||||
{
|
||||
return mon.recvTimingResp(pkt);
|
||||
}
|
||||
|
||||
void recvTimingSnoopReq(PacketPtr pkt)
|
||||
{
|
||||
mon.recvTimingSnoopReq(pkt);
|
||||
}
|
||||
|
||||
void recvRangeChange()
|
||||
{
|
||||
mon.recvRangeChange();
|
||||
}
|
||||
|
||||
bool isSnooping() const
|
||||
{
|
||||
return mon.isSnooping();
|
||||
}
|
||||
|
||||
unsigned deviceBlockSize() const
|
||||
{
|
||||
return mon.deviceBlockSizeMaster();
|
||||
}
|
||||
|
||||
void recvRetry()
|
||||
{
|
||||
mon.recvRetryMaster();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CommMonitor& mon;
|
||||
|
||||
};
|
||||
|
||||
/** Instance of master port, facing the memory side */
|
||||
MonitorMasterPort masterPort;
|
||||
|
||||
/**
|
||||
* This is the slave port of the communication monitor. All recv
|
||||
* functions call a function in CommMonitor, where the
|
||||
* send function of the master port is called. Besides this, these
|
||||
* functions can also perform actions for capturing statistics.
|
||||
*/
|
||||
class MonitorSlavePort : public SlavePort
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
MonitorSlavePort(const std::string& _name, CommMonitor& _mon)
|
||||
: SlavePort(_name, &_mon), mon(_mon)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
|
||||
void recvFunctional(PacketPtr pkt)
|
||||
{
|
||||
mon.recvFunctional(pkt);
|
||||
}
|
||||
|
||||
Tick recvAtomic(PacketPtr pkt)
|
||||
{
|
||||
return mon.recvAtomic(pkt);
|
||||
}
|
||||
|
||||
bool recvTimingReq(PacketPtr pkt)
|
||||
{
|
||||
return mon.recvTimingReq(pkt);
|
||||
}
|
||||
|
||||
bool recvTimingSnoopResp(PacketPtr pkt)
|
||||
{
|
||||
return mon.recvTimingSnoopResp(pkt);
|
||||
}
|
||||
|
||||
unsigned deviceBlockSize() const
|
||||
{
|
||||
return mon.deviceBlockSizeSlave();
|
||||
}
|
||||
|
||||
AddrRangeList getAddrRanges()
|
||||
{
|
||||
return mon.getAddrRanges();
|
||||
}
|
||||
|
||||
void recvRetry()
|
||||
{
|
||||
mon.recvRetrySlave();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CommMonitor& mon;
|
||||
|
||||
};
|
||||
|
||||
/** Instance of slave port, i.e. on the CPU side */
|
||||
MonitorSlavePort slavePort;
|
||||
|
||||
void recvFunctional(PacketPtr pkt);
|
||||
|
||||
void recvFunctionalSnoop(PacketPtr pkt);
|
||||
|
||||
Tick recvAtomic(PacketPtr pkt);
|
||||
|
||||
Tick recvAtomicSnoop(PacketPtr pkt);
|
||||
|
||||
bool recvTimingReq(PacketPtr pkt);
|
||||
|
||||
bool recvTimingResp(PacketPtr pkt);
|
||||
|
||||
void recvTimingSnoopReq(PacketPtr pkt);
|
||||
|
||||
bool recvTimingSnoopResp(PacketPtr pkt);
|
||||
|
||||
unsigned deviceBlockSizeMaster();
|
||||
|
||||
unsigned deviceBlockSizeSlave();
|
||||
|
||||
AddrRangeList getAddrRanges();
|
||||
|
||||
bool isSnooping() const;
|
||||
|
||||
void recvRetryMaster();
|
||||
|
||||
void recvRetrySlave();
|
||||
|
||||
void recvRangeChange();
|
||||
|
||||
void periodicTraceDump();
|
||||
|
||||
/** Stats declarations, all in a struct for convenience. */
|
||||
struct MonitorStats
|
||||
{
|
||||
|
||||
/** Disable flag for burst length historgrams **/
|
||||
bool disableBurstLengthHists;
|
||||
|
||||
/** Histogram of read burst lengths */
|
||||
Stats::Histogram readBurstLengthHist;
|
||||
|
||||
/** Histogram of write burst lengths */
|
||||
Stats::Histogram writeBurstLengthHist;
|
||||
|
||||
/** Disable flag for the bandwidth histograms */
|
||||
bool disableBandwidthHists;
|
||||
|
||||
/**
|
||||
* Histogram for read bandwidth per sample window. The
|
||||
* internal counter is an unsigned int rather than a stat.
|
||||
*/
|
||||
unsigned int readBytes;
|
||||
Stats::Histogram readBandwidthHist;
|
||||
Stats::Formula averageReadBW;
|
||||
Stats::Scalar totalReadBytes;
|
||||
|
||||
/**
|
||||
* Histogram for write bandwidth per sample window. The
|
||||
* internal counter is an unsigned int rather than a stat.
|
||||
*/
|
||||
unsigned int writtenBytes;
|
||||
Stats::Histogram writeBandwidthHist;
|
||||
Stats::Formula averageWriteBW;
|
||||
Stats::Scalar totalWrittenBytes;
|
||||
|
||||
/** Disable flag for latency histograms. */
|
||||
bool disableLatencyHists;
|
||||
|
||||
/** Histogram of read request-to-response latencies */
|
||||
Stats::Histogram readLatencyHist;
|
||||
|
||||
/** Histogram of write request-to-response latencies */
|
||||
Stats::Histogram writeLatencyHist;
|
||||
|
||||
/** Disable flag for ITT distributions. */
|
||||
bool disableITTDists;
|
||||
|
||||
/**
|
||||
* Inter transaction time (ITT) distributions. There are
|
||||
* histograms of the time between two read, write or arbitrary
|
||||
* accesses. The time of a request is the tick at which the
|
||||
* request is forwarded by the monitor.
|
||||
*/
|
||||
Stats::Distribution ittReadRead;
|
||||
Stats::Distribution ittWriteWrite;
|
||||
Stats::Distribution ittReqReq;
|
||||
Tick timeOfLastRead;
|
||||
Tick timeOfLastWrite;
|
||||
Tick timeOfLastReq;
|
||||
|
||||
/** Disable flag for outstanding histograms. */
|
||||
bool disableOutstandingHists;
|
||||
|
||||
/**
|
||||
* Histogram of outstanding read requests. Counter for
|
||||
* outstanding read requests is an unsigned integer because
|
||||
* it should not be reset when stats are reset.
|
||||
*/
|
||||
Stats::Histogram outstandingReadsHist;
|
||||
unsigned int outstandingReadReqs;
|
||||
|
||||
/**
|
||||
* Histogram of outstanding write requests. Counter for
|
||||
* outstanding write requests is an unsigned integer because
|
||||
* it should not be reset when stats are reset.
|
||||
*/
|
||||
Stats::Histogram outstandingWritesHist;
|
||||
unsigned int outstandingWriteReqs;
|
||||
|
||||
/** Disable flag for transaction histograms. */
|
||||
bool disableTransactionHists;
|
||||
|
||||
/** Histogram of number of read transactions per time bin */
|
||||
Stats::Histogram readTransHist;
|
||||
unsigned int readTrans;
|
||||
|
||||
/** Histogram of number of timing write transactions per time bin */
|
||||
Stats::Histogram writeTransHist;
|
||||
unsigned int writeTrans;
|
||||
|
||||
/** Disable flag for address distributions. */
|
||||
bool disableAddrDists;
|
||||
|
||||
/**
|
||||
* Histogram of number of read accesses to addresses over
|
||||
* time.
|
||||
*/
|
||||
Stats::SparseHistogram readAddrDist;
|
||||
|
||||
/**
|
||||
* Histogram of number of write accesses to addresses over
|
||||
* time.
|
||||
*/
|
||||
Stats::SparseHistogram writeAddrDist;
|
||||
|
||||
/**
|
||||
* Create the monitor stats and initialise all the members
|
||||
* that are not statistics themselves, but used to control the
|
||||
* stats or track values during a sample period.
|
||||
*/
|
||||
MonitorStats(const CommMonitorParams* params) :
|
||||
disableBurstLengthHists(params->disable_burst_length_hists),
|
||||
disableBandwidthHists(params->disable_bandwidth_hists),
|
||||
readBytes(0), writtenBytes(0),
|
||||
disableLatencyHists(params->disable_latency_hists),
|
||||
disableITTDists(params->disable_itt_dists),
|
||||
timeOfLastRead(0), timeOfLastWrite(0), timeOfLastReq(0),
|
||||
disableOutstandingHists(params->disable_outstanding_hists),
|
||||
outstandingReadReqs(0), outstandingWriteReqs(0),
|
||||
disableTransactionHists(params->disable_transaction_hists),
|
||||
readTrans(0), writeTrans(0),
|
||||
disableAddrDists(params->disable_addr_dists)
|
||||
{ }
|
||||
|
||||
};
|
||||
|
||||
/** This function is called periodically at the end of each time bin */
|
||||
void samplePeriodic();
|
||||
|
||||
/** Schedule the first periodic event */
|
||||
void startup();
|
||||
|
||||
/** Periodic event called at the end of each simulation time bin */
|
||||
EventWrapper<CommMonitor, &CommMonitor::samplePeriodic> samplePeriodicEvent;
|
||||
|
||||
/** Length of simulation time bin*/
|
||||
Tick samplePeriodTicks;
|
||||
Time samplePeriod;
|
||||
|
||||
/** Address mask for sources of read accesses to be captured */
|
||||
Addr readAddrMask;
|
||||
|
||||
/** Address mask for sources of write accesses to be captured */
|
||||
Addr writeAddrMask;
|
||||
|
||||
/** Instantiate stats */
|
||||
MonitorStats stats;
|
||||
};
|
||||
|
||||
#endif //__MEM_COMM_MONITOR_HH__
|
||||
39
simulators/gem5/src/mem/config/cache.hh
Normal file
39
simulators/gem5/src/mem/config/cache.hh
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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_IIC 1
|
||||
162
simulators/gem5/src/mem/fs_translating_port_proxy.cc
Normal file
162
simulators/gem5/src/mem/fs_translating_port_proxy.cc
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) 2011 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Port object definitions.
|
||||
*/
|
||||
|
||||
#include "base/chunk_generator.hh"
|
||||
#include "cpu/base.hh"
|
||||
#include "cpu/thread_context.hh"
|
||||
#include "mem/fs_translating_port_proxy.hh"
|
||||
|
||||
using namespace TheISA;
|
||||
|
||||
FSTranslatingPortProxy::FSTranslatingPortProxy(ThreadContext *tc)
|
||||
: PortProxy(tc->getCpuPtr()->getDataPort()), _tc(tc)
|
||||
{
|
||||
}
|
||||
|
||||
FSTranslatingPortProxy::FSTranslatingPortProxy(MasterPort &port)
|
||||
: PortProxy(port), _tc(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
FSTranslatingPortProxy::~FSTranslatingPortProxy()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
FSTranslatingPortProxy::readBlob(Addr addr, uint8_t *p, int size) const
|
||||
{
|
||||
Addr paddr;
|
||||
for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done();
|
||||
gen.next())
|
||||
{
|
||||
if (_tc)
|
||||
paddr = TheISA::vtophys(_tc,gen.addr());
|
||||
else
|
||||
paddr = TheISA::vtophys(gen.addr());
|
||||
|
||||
PortProxy::readBlob(paddr, p, gen.size());
|
||||
p += gen.size();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FSTranslatingPortProxy::writeBlob(Addr addr, uint8_t *p, int size) const
|
||||
{
|
||||
Addr paddr;
|
||||
for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done();
|
||||
gen.next())
|
||||
{
|
||||
if (_tc)
|
||||
paddr = TheISA::vtophys(_tc,gen.addr());
|
||||
else
|
||||
paddr = TheISA::vtophys(gen.addr());
|
||||
|
||||
PortProxy::writeBlob(paddr, p, gen.size());
|
||||
p += gen.size();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FSTranslatingPortProxy::memsetBlob(Addr address, uint8_t v, int size) const
|
||||
{
|
||||
Addr paddr;
|
||||
for (ChunkGenerator gen(address, size, TheISA::PageBytes); !gen.done();
|
||||
gen.next())
|
||||
{
|
||||
if (_tc)
|
||||
paddr = TheISA::vtophys(_tc,gen.addr());
|
||||
else
|
||||
paddr = TheISA::vtophys(gen.addr());
|
||||
|
||||
PortProxy::memsetBlob(paddr, v, gen.size());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen)
|
||||
{
|
||||
uint8_t *dst = (uint8_t *)dest;
|
||||
tc->getVirtProxy().readBlob(src, dst, cplen);
|
||||
}
|
||||
|
||||
void
|
||||
CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen)
|
||||
{
|
||||
uint8_t *src = (uint8_t *)source;
|
||||
tc->getVirtProxy().writeBlob(dest, src, cplen);
|
||||
}
|
||||
|
||||
void
|
||||
CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen)
|
||||
{
|
||||
char *start = dst;
|
||||
FSTranslatingPortProxy &vp = tc->getVirtProxy();
|
||||
|
||||
bool foundNull = false;
|
||||
while ((dst - start + 1) < maxlen && !foundNull) {
|
||||
vp.readBlob(vaddr++, (uint8_t*)dst, 1);
|
||||
if (*dst == '\0')
|
||||
foundNull = true;
|
||||
dst++;
|
||||
}
|
||||
|
||||
if (!foundNull)
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
CopyStringIn(ThreadContext *tc, char *src, Addr vaddr)
|
||||
{
|
||||
FSTranslatingPortProxy &vp = tc->getVirtProxy();
|
||||
for (ChunkGenerator gen(vaddr, strlen(src), TheISA::PageBytes); !gen.done();
|
||||
gen.next())
|
||||
{
|
||||
vp.writeBlob(gen.addr(), (uint8_t*)src, gen.size());
|
||||
src += gen.size();
|
||||
}
|
||||
}
|
||||
104
simulators/gem5/src/mem/fs_translating_port_proxy.hh
Normal file
104
simulators/gem5/src/mem/fs_translating_port_proxy.hh
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2011 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* TranslatingPortProxy Object Declaration for FS.
|
||||
*
|
||||
* Port proxies are used when non structural entities need access to
|
||||
* the memory system. Proxy objects replace the previous
|
||||
* FunctionalPort, TranslatingPort and VirtualPort objects, which
|
||||
* provided the same functionality as the proxies, but were instances
|
||||
* of ports not corresponding to real structural ports of the
|
||||
* simulated system. Via the port proxies all the accesses go through
|
||||
* an actual port and thus are transparent to a potentially
|
||||
* distributed memory and automatically adhere to the memory map of
|
||||
* the system.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_FS_PORT_PROXY_HH__
|
||||
#define __MEM_FS_PORT_PROXY_HH__
|
||||
|
||||
#include "arch/vtophys.hh"
|
||||
#include "mem/port_proxy.hh"
|
||||
|
||||
/**
|
||||
* A TranslatingPortProxy in FS mode translates a virtual address to a
|
||||
* physical address and then calls the read/write functions of the
|
||||
* port. 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).
|
||||
*/
|
||||
class FSTranslatingPortProxy : public PortProxy
|
||||
{
|
||||
private:
|
||||
ThreadContext* _tc;
|
||||
|
||||
public:
|
||||
|
||||
FSTranslatingPortProxy(ThreadContext* tc);
|
||||
|
||||
FSTranslatingPortProxy(MasterPort &port);
|
||||
|
||||
virtual ~FSTranslatingPortProxy();
|
||||
|
||||
/** Version of readblob that translates virt->phys and deals
|
||||
* with page boundries. */
|
||||
virtual void readBlob(Addr addr, uint8_t *p, int size) const;
|
||||
|
||||
/** Version of writeBlob that translates virt->phys and deals
|
||||
* with page boundries. */
|
||||
virtual void writeBlob(Addr addr, uint8_t *p, int size) const;
|
||||
|
||||
/**
|
||||
* Fill size bytes starting at addr with byte value val.
|
||||
*/
|
||||
virtual void memsetBlob(Addr address, uint8_t v, int size) const;
|
||||
};
|
||||
|
||||
void CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen);
|
||||
void CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen);
|
||||
void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen);
|
||||
void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr);
|
||||
|
||||
#endif //__MEM_FS_PORT_PROXY_HH__
|
||||
61
simulators/gem5/src/mem/mem_object.cc
Normal file
61
simulators/gem5/src/mem/mem_object.cc
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Steve Reinhardt
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
#include "mem/mem_object.hh"
|
||||
|
||||
MemObject::MemObject(const Params *params)
|
||||
: SimObject(params)
|
||||
{
|
||||
}
|
||||
|
||||
MasterPort&
|
||||
MemObject::getMasterPort(const std::string& if_name, int idx)
|
||||
{
|
||||
fatal("%s does not have any master port named %s\n", name(), if_name);
|
||||
}
|
||||
|
||||
SlavePort&
|
||||
MemObject::getSlavePort(const std::string& if_name, int idx)
|
||||
{
|
||||
fatal("%s does not have any slave port named %s\n", name(), if_name);
|
||||
}
|
||||
92
simulators/gem5/src/mem/mem_object.hh
Normal file
92
simulators/gem5/src/mem/mem_object.hh
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Ron Dreslinski
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* MemObject declaration.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_MEM_OBJECT_HH__
|
||||
#define __MEM_MEM_OBJECT_HH__
|
||||
|
||||
#include "mem/port.hh"
|
||||
#include "params/MemObject.hh"
|
||||
#include "sim/sim_object.hh"
|
||||
|
||||
/**
|
||||
* The MemObject class extends the SimObject with accessor functions
|
||||
* to get its master and slave ports.
|
||||
*/
|
||||
class MemObject : public SimObject
|
||||
{
|
||||
public:
|
||||
typedef MemObjectParams Params;
|
||||
const Params *params() const
|
||||
{ return dynamic_cast<const Params *>(_params); }
|
||||
|
||||
MemObject(const Params *params);
|
||||
|
||||
/**
|
||||
* Get a master port with a given name and index.
|
||||
*
|
||||
* @param if_name Port name
|
||||
* @param idx Index in the case of a VectorPort
|
||||
*
|
||||
* @return A reference to the given port
|
||||
*/
|
||||
virtual MasterPort& getMasterPort(const std::string& if_name,
|
||||
int idx = -1);
|
||||
|
||||
/**
|
||||
* Get a slave port with a given name and index.
|
||||
*
|
||||
* @param if_name Port name
|
||||
* @param idx Index in the case of a VectorPort
|
||||
*
|
||||
* @return A reference to the given port
|
||||
*/
|
||||
virtual SlavePort& getSlavePort(const std::string& if_name,
|
||||
int idx = -1);
|
||||
};
|
||||
|
||||
#endif //__MEM_MEM_OBJECT_HH__
|
||||
54
simulators/gem5/src/mem/mport.cc
Normal file
54
simulators/gem5/src/mem/mport.cc
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* Copyright (c) 2008 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: Gabe Black
|
||||
*/
|
||||
|
||||
#include "mem/mport.hh"
|
||||
|
||||
Tick
|
||||
MessageSlavePort::recvAtomic(PacketPtr pkt)
|
||||
{
|
||||
if (pkt->cmd == MemCmd::MessageReq) {
|
||||
return recvMessage(pkt);
|
||||
} else {
|
||||
panic("%s received unexpected atomic command %s from %s.\n",
|
||||
name(), pkt->cmd.toString(), getMasterPort().name());
|
||||
}
|
||||
}
|
||||
99
simulators/gem5/src/mem/mport.hh
Normal file
99
simulators/gem5/src/mem/mport.hh
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* Copyright (c) 2008 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: Gabe Black
|
||||
*/
|
||||
|
||||
#ifndef __MEM_MPORT_HH__
|
||||
#define __MEM_MPORT_HH__
|
||||
|
||||
#include "mem/mem_object.hh"
|
||||
#include "mem/tport.hh"
|
||||
|
||||
/*
|
||||
* This file defines a port class which is used for sending and receiving
|
||||
* messages. These messages are atomic units which don't interact and
|
||||
* should be smaller than a cache block. This class is based on
|
||||
* the underpinnings of SimpleTimingPort, but it tweaks some of the external
|
||||
* functions.
|
||||
*/
|
||||
class MessageSlavePort : public SimpleTimingPort
|
||||
{
|
||||
|
||||
public:
|
||||
MessageSlavePort(const std::string &name, MemObject *owner) :
|
||||
SimpleTimingPort(name, owner)
|
||||
{}
|
||||
|
||||
virtual ~MessageSlavePort()
|
||||
{}
|
||||
|
||||
protected:
|
||||
|
||||
Tick recvAtomic(PacketPtr pkt);
|
||||
|
||||
virtual Tick recvMessage(PacketPtr pkt) = 0;
|
||||
};
|
||||
|
||||
class MessageMasterPort : public QueuedMasterPort
|
||||
{
|
||||
public:
|
||||
|
||||
MessageMasterPort(const std::string &name, MemObject *owner) :
|
||||
QueuedMasterPort(name, owner, queue), queue(*owner, *this)
|
||||
{}
|
||||
|
||||
virtual ~MessageMasterPort()
|
||||
{}
|
||||
|
||||
bool recvTimingResp(PacketPtr pkt) { recvResponse(pkt); return true; }
|
||||
|
||||
protected:
|
||||
|
||||
/** A packet queue for outgoing packets. */
|
||||
MasterPacketQueue queue;
|
||||
|
||||
// Accept and ignore responses.
|
||||
virtual Tick recvResponse(PacketPtr pkt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
209
simulators/gem5/src/mem/noncoherent_bus.cc
Normal file
209
simulators/gem5/src/mem/noncoherent_bus.cc
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Andreas Hansson
|
||||
* William Wang
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of a bus object.
|
||||
*/
|
||||
|
||||
#include "base/misc.hh"
|
||||
#include "base/trace.hh"
|
||||
#include "debug/Bus.hh"
|
||||
#include "debug/BusAddrRanges.hh"
|
||||
#include "debug/NoncoherentBus.hh"
|
||||
#include "mem/noncoherent_bus.hh"
|
||||
|
||||
NoncoherentBus::NoncoherentBus(const NoncoherentBusParams *p)
|
||||
: BaseBus(p)
|
||||
{
|
||||
// create the ports based on the size of the master and slave
|
||||
// vector ports, and the presence of the default port, the ports
|
||||
// are enumerated starting from zero
|
||||
for (int i = 0; i < p->port_master_connection_count; ++i) {
|
||||
std::string portName = csprintf("%s-p%d", name(), i);
|
||||
MasterPort* bp = new NoncoherentBusMasterPort(portName, *this, i);
|
||||
masterPorts.push_back(bp);
|
||||
}
|
||||
|
||||
// see if we have a default slave device connected and if so add
|
||||
// our corresponding master port
|
||||
if (p->port_default_connection_count) {
|
||||
defaultPortID = masterPorts.size();
|
||||
std::string portName = csprintf("%s-default", name());
|
||||
MasterPort* bp = new NoncoherentBusMasterPort(portName, *this,
|
||||
defaultPortID);
|
||||
masterPorts.push_back(bp);
|
||||
}
|
||||
|
||||
// create the slave ports, once again starting at zero
|
||||
for (int i = 0; i < p->port_slave_connection_count; ++i) {
|
||||
std::string portName = csprintf("%s-p%d", name(), i);
|
||||
SlavePort* bp = new NoncoherentBusSlavePort(portName, *this, i);
|
||||
slavePorts.push_back(bp);
|
||||
}
|
||||
|
||||
clearPortCache();
|
||||
}
|
||||
|
||||
bool
|
||||
NoncoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
|
||||
{
|
||||
// determine the source port based on the id
|
||||
SlavePort *src_port = slavePorts[slave_port_id];
|
||||
|
||||
// we should never see express snoops on a non-coherent bus
|
||||
assert(!pkt->isExpressSnoop());
|
||||
|
||||
// test if the bus should be considered occupied for the current
|
||||
// port
|
||||
if (isOccupied(src_port)) {
|
||||
DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x BUSY\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
return false;
|
||||
}
|
||||
|
||||
DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
|
||||
// set the source port for routing of the response
|
||||
pkt->setSrc(slave_port_id);
|
||||
|
||||
Tick headerFinishTime = calcPacketTiming(pkt);
|
||||
Tick packetFinishTime = pkt->finishTime;
|
||||
|
||||
// since it is a normal request, determine the destination
|
||||
// based on the address and attempt to send the packet
|
||||
bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
|
||||
|
||||
if (!success) {
|
||||
// inhibited packets should never be forced to retry
|
||||
assert(!pkt->memInhibitAsserted());
|
||||
|
||||
DPRINTF(NoncoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
|
||||
addToRetryList(src_port);
|
||||
occupyBus(headerFinishTime);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
succeededTiming(packetFinishTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
NoncoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
|
||||
{
|
||||
// determine the source port based on the id
|
||||
MasterPort *src_port = masterPorts[master_port_id];
|
||||
|
||||
// test if the bus should be considered occupied for the current
|
||||
// port
|
||||
if (isOccupied(src_port)) {
|
||||
DPRINTF(NoncoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
return false;
|
||||
}
|
||||
|
||||
DPRINTF(NoncoherentBus, "recvTimingResp: src %s %s 0x%x\n",
|
||||
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
||||
|
||||
calcPacketTiming(pkt);
|
||||
Tick packetFinishTime = pkt->finishTime;
|
||||
|
||||
// send the packet to the destination through one of our slave
|
||||
// ports, as determined by the destination field
|
||||
bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
|
||||
|
||||
// currently it is illegal to block responses... can lead to
|
||||
// deadlock
|
||||
assert(success);
|
||||
|
||||
succeededTiming(packetFinishTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Tick
|
||||
NoncoherentBus::recvAtomic(PacketPtr pkt, PortID slave_port_id)
|
||||
{
|
||||
DPRINTF(NoncoherentBus, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
|
||||
slavePorts[slave_port_id]->name(), pkt->getAddr(),
|
||||
pkt->cmdString());
|
||||
|
||||
// determine the destination port
|
||||
PortID dest_id = findPort(pkt->getAddr());
|
||||
|
||||
// forward the request to the appropriate destination
|
||||
Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt);
|
||||
|
||||
pkt->finishTime = curTick() + response_latency;
|
||||
return response_latency;
|
||||
}
|
||||
|
||||
void
|
||||
NoncoherentBus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
|
||||
{
|
||||
if (!pkt->isPrint()) {
|
||||
// don't do DPRINTFs on PrintReq as it clutters up the output
|
||||
DPRINTF(NoncoherentBus,
|
||||
"recvFunctional: packet src %s addr 0x%x cmd %s\n",
|
||||
slavePorts[slave_port_id]->name(), pkt->getAddr(),
|
||||
pkt->cmdString());
|
||||
}
|
||||
|
||||
// determine the destination port
|
||||
PortID dest_id = findPort(pkt->getAddr());
|
||||
|
||||
// forward the request to the appropriate destination
|
||||
masterPorts[dest_id]->sendFunctional(pkt);
|
||||
}
|
||||
|
||||
NoncoherentBus*
|
||||
NoncoherentBusParams::create()
|
||||
{
|
||||
return new NoncoherentBus(this);
|
||||
}
|
||||
201
simulators/gem5/src/mem/noncoherent_bus.hh
Normal file
201
simulators/gem5/src/mem/noncoherent_bus.hh
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Ron Dreslinski
|
||||
* Ali Saidi
|
||||
* Andreas Hansson
|
||||
* William Wang
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Declaration of a non-coherent bus.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_NONCOHERENT_BUS_HH__
|
||||
#define __MEM_NONCOHERENT_BUS_HH__
|
||||
|
||||
#include "mem/bus.hh"
|
||||
#include "params/NoncoherentBus.hh"
|
||||
|
||||
/**
|
||||
* A non-coherent bus connects a number of non-snooping masters and
|
||||
* slaves, and routes the request and response packets based on the
|
||||
* address. The request packets issued by the master connected to a
|
||||
* non-coherent bus could still snoop in caches attached to a coherent
|
||||
* bus, as is the case with the I/O bus and memory bus in most system
|
||||
* configurations. No snoops will, however, reach any master on the
|
||||
* non-coherent bus itself.
|
||||
*
|
||||
* The non-coherent bus can be used as a template for modelling PCI,
|
||||
* PCIe, and non-coherent AMBA and OCP buses, and is typically used
|
||||
* for the I/O buses.
|
||||
*/
|
||||
class NoncoherentBus : public BaseBus
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Declaration of the non-coherent bus slave port type, one will
|
||||
* be instantiated for each of the master ports connecting to the
|
||||
* bus.
|
||||
*/
|
||||
class NoncoherentBusSlavePort : public SlavePort
|
||||
{
|
||||
private:
|
||||
|
||||
/** A reference to the bus to which this port belongs. */
|
||||
NoncoherentBus &bus;
|
||||
|
||||
public:
|
||||
|
||||
NoncoherentBusSlavePort(const std::string &_name,
|
||||
NoncoherentBus &_bus, PortID _id)
|
||||
: SlavePort(_name, &_bus, _id), bus(_bus)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* When receiving a timing request, pass it to the bus.
|
||||
*/
|
||||
virtual bool recvTimingReq(PacketPtr pkt)
|
||||
{ return bus.recvTimingReq(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving an atomic request, pass it to the bus.
|
||||
*/
|
||||
virtual Tick recvAtomic(PacketPtr pkt)
|
||||
{ return bus.recvAtomic(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving a functional request, pass it to the bus.
|
||||
*/
|
||||
virtual void recvFunctional(PacketPtr pkt)
|
||||
{ bus.recvFunctional(pkt, id); }
|
||||
|
||||
/**
|
||||
* When receiving a retry, pass it to the bus.
|
||||
*/
|
||||
virtual void recvRetry()
|
||||
{ panic("Bus slave ports always succeed and should never retry.\n"); }
|
||||
|
||||
/**
|
||||
* Return the union of all adress ranges seen by this bus.
|
||||
*/
|
||||
virtual AddrRangeList getAddrRanges()
|
||||
{ return bus.getAddrRanges(); }
|
||||
|
||||
/**
|
||||
* Get the maximum block size as seen by the bus.
|
||||
*/
|
||||
virtual unsigned deviceBlockSize() const
|
||||
{ return bus.findBlockSize(); }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Declaration of the bus master port type, one will be
|
||||
* instantiated for each of the slave ports connecting to the
|
||||
* bus.
|
||||
*/
|
||||
class NoncoherentBusMasterPort : public MasterPort
|
||||
{
|
||||
private:
|
||||
|
||||
/** A reference to the bus to which this port belongs. */
|
||||
NoncoherentBus &bus;
|
||||
|
||||
public:
|
||||
|
||||
NoncoherentBusMasterPort(const std::string &_name,
|
||||
NoncoherentBus &_bus, PortID _id)
|
||||
: MasterPort(_name, &_bus, _id), bus(_bus)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* When receiving a timing response, pass it to the bus.
|
||||
*/
|
||||
virtual bool recvTimingResp(PacketPtr pkt)
|
||||
{ return bus.recvTimingResp(pkt, id); }
|
||||
|
||||
/** When reciving a range change from the peer port (at id),
|
||||
pass it to the bus. */
|
||||
virtual void recvRangeChange()
|
||||
{ bus.recvRangeChange(id); }
|
||||
|
||||
/** When reciving a retry from the peer port (at id),
|
||||
pass it to the bus. */
|
||||
virtual void recvRetry()
|
||||
{ bus.recvRetry(); }
|
||||
|
||||
/**
|
||||
* Get the maximum block size as seen by the bus.
|
||||
*/
|
||||
virtual unsigned deviceBlockSize() const
|
||||
{ return bus.findBlockSize(); }
|
||||
|
||||
};
|
||||
|
||||
/** Function called by the port when the bus is recieving a Timing
|
||||
request packet.*/
|
||||
bool recvTimingReq(PacketPtr pkt, PortID slave_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving a Timing
|
||||
response packet.*/
|
||||
bool recvTimingResp(PacketPtr pkt, PortID master_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving a Atomic
|
||||
transaction.*/
|
||||
Tick recvAtomic(PacketPtr pkt, PortID slave_port_id);
|
||||
|
||||
/** Function called by the port when the bus is recieving a Functional
|
||||
transaction.*/
|
||||
void recvFunctional(PacketPtr pkt, PortID slave_port_id);
|
||||
|
||||
public:
|
||||
|
||||
NoncoherentBus(const NoncoherentBusParams *p);
|
||||
|
||||
};
|
||||
|
||||
#endif //__MEM_NONCOHERENT_BUS_HH__
|
||||
385
simulators/gem5/src/mem/packet.cc
Normal file
385
simulators/gem5/src/mem/packet.cc
Normal file
@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Copyright (c) 2011 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* Copyright (c) 2006 The Regents of The University of Michigan
|
||||
* Copyright (c) 2010 Advanced Micro Devices, Inc.
|
||||
* 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
|
||||
* Steve Reinhardt
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of the Packet Class, a packet is a transaction occuring
|
||||
* between a single level of the memory heirarchy (ie L1->L2).
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "base/cprintf.hh"
|
||||
#include "base/misc.hh"
|
||||
#include "base/trace.hh"
|
||||
#include "mem/packet.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// The one downside to bitsets is that static initializers can get ugly.
|
||||
#define SET1(a1) (1 << (a1))
|
||||
#define SET2(a1, a2) (SET1(a1) | SET1(a2))
|
||||
#define SET3(a1, a2, a3) (SET2(a1, a2) | SET1(a3))
|
||||
#define SET4(a1, a2, a3, a4) (SET3(a1, a2, a3) | SET1(a4))
|
||||
#define SET5(a1, a2, a3, a4, a5) (SET4(a1, a2, a3, a4) | SET1(a5))
|
||||
#define SET6(a1, a2, a3, a4, a5, a6) (SET5(a1, a2, a3, a4, a5) | SET1(a6))
|
||||
|
||||
const MemCmd::CommandInfo
|
||||
MemCmd::commandInfo[] =
|
||||
{
|
||||
/* InvalidCmd */
|
||||
{ 0, InvalidCmd, "InvalidCmd" },
|
||||
/* ReadReq */
|
||||
{ SET3(IsRead, IsRequest, NeedsResponse), ReadResp, "ReadReq" },
|
||||
/* ReadResp */
|
||||
{ SET3(IsRead, IsResponse, HasData), InvalidCmd, "ReadResp" },
|
||||
/* ReadRespWithInvalidate */
|
||||
{ SET4(IsRead, IsResponse, HasData, IsInvalidate),
|
||||
InvalidCmd, "ReadRespWithInvalidate" },
|
||||
/* WriteReq */
|
||||
{ SET5(IsWrite, NeedsExclusive, IsRequest, NeedsResponse, HasData),
|
||||
WriteResp, "WriteReq" },
|
||||
/* WriteResp */
|
||||
{ SET3(IsWrite, NeedsExclusive, IsResponse), InvalidCmd, "WriteResp" },
|
||||
/* Writeback */
|
||||
{ SET4(IsWrite, NeedsExclusive, IsRequest, HasData),
|
||||
InvalidCmd, "Writeback" },
|
||||
/* SoftPFReq */
|
||||
{ SET4(IsRead, IsRequest, IsSWPrefetch, NeedsResponse),
|
||||
SoftPFResp, "SoftPFReq" },
|
||||
/* HardPFReq */
|
||||
{ SET4(IsRead, IsRequest, IsHWPrefetch, NeedsResponse),
|
||||
HardPFResp, "HardPFReq" },
|
||||
/* SoftPFResp */
|
||||
{ SET4(IsRead, IsResponse, IsSWPrefetch, HasData),
|
||||
InvalidCmd, "SoftPFResp" },
|
||||
/* HardPFResp */
|
||||
{ SET4(IsRead, IsResponse, IsHWPrefetch, HasData),
|
||||
InvalidCmd, "HardPFResp" },
|
||||
/* WriteInvalidateReq (currently unused, see packet.hh) */
|
||||
{ SET6(IsWrite, NeedsExclusive, IsInvalidate,
|
||||
IsRequest, HasData, NeedsResponse),
|
||||
WriteInvalidateResp, "WriteInvalidateReq" },
|
||||
/* WriteInvalidateResp (currently unused, see packet.hh) */
|
||||
{ SET3(IsWrite, NeedsExclusive, IsResponse),
|
||||
InvalidCmd, "WriteInvalidateResp" },
|
||||
/* UpgradeReq */
|
||||
{ SET5(IsInvalidate, NeedsExclusive, IsUpgrade, IsRequest, NeedsResponse),
|
||||
UpgradeResp, "UpgradeReq" },
|
||||
/* SCUpgradeReq: response could be UpgradeResp or UpgradeFailResp */
|
||||
{ SET6(IsInvalidate, NeedsExclusive, IsUpgrade, IsLlsc,
|
||||
IsRequest, NeedsResponse),
|
||||
UpgradeResp, "SCUpgradeReq" },
|
||||
/* UpgradeResp */
|
||||
{ SET3(NeedsExclusive, IsUpgrade, IsResponse),
|
||||
InvalidCmd, "UpgradeResp" },
|
||||
/* SCUpgradeFailReq: generates UpgradeFailResp ASAP */
|
||||
{ SET5(IsInvalidate, NeedsExclusive, IsLlsc,
|
||||
IsRequest, NeedsResponse),
|
||||
UpgradeFailResp, "SCUpgradeFailReq" },
|
||||
/* UpgradeFailResp */
|
||||
{ SET2(NeedsExclusive, IsResponse),
|
||||
InvalidCmd, "UpgradeFailResp" },
|
||||
/* ReadExReq */
|
||||
{ SET5(IsRead, NeedsExclusive, IsInvalidate, IsRequest, NeedsResponse),
|
||||
ReadExResp, "ReadExReq" },
|
||||
/* ReadExResp */
|
||||
{ SET4(IsRead, NeedsExclusive, IsResponse, HasData),
|
||||
InvalidCmd, "ReadExResp" },
|
||||
/* LoadLockedReq: note that we use plain ReadResp as response, so that
|
||||
* we can also use ReadRespWithInvalidate when needed */
|
||||
{ SET4(IsRead, IsLlsc, IsRequest, NeedsResponse),
|
||||
ReadResp, "LoadLockedReq" },
|
||||
/* StoreCondReq */
|
||||
{ SET6(IsWrite, NeedsExclusive, IsLlsc,
|
||||
IsRequest, NeedsResponse, HasData),
|
||||
StoreCondResp, "StoreCondReq" },
|
||||
/* StoreCondFailReq: generates failing StoreCondResp ASAP */
|
||||
{ SET6(IsWrite, NeedsExclusive, IsLlsc,
|
||||
IsRequest, NeedsResponse, HasData),
|
||||
StoreCondResp, "StoreCondFailReq" },
|
||||
/* StoreCondResp */
|
||||
{ SET4(IsWrite, NeedsExclusive, IsLlsc, IsResponse),
|
||||
InvalidCmd, "StoreCondResp" },
|
||||
/* SwapReq -- for Swap ldstub type operations */
|
||||
{ SET6(IsRead, IsWrite, NeedsExclusive, IsRequest, HasData, NeedsResponse),
|
||||
SwapResp, "SwapReq" },
|
||||
/* SwapResp -- for Swap ldstub type operations */
|
||||
{ SET5(IsRead, IsWrite, NeedsExclusive, IsResponse, HasData),
|
||||
InvalidCmd, "SwapResp" },
|
||||
/* IntReq -- for interrupts */
|
||||
{ SET4(IsWrite, IsRequest, NeedsResponse, HasData),
|
||||
MessageResp, "MessageReq" },
|
||||
/* IntResp -- for interrupts */
|
||||
{ SET2(IsWrite, IsResponse), InvalidCmd, "MessageResp" },
|
||||
/* NetworkNackError -- nacked at network layer (not by protocol) */
|
||||
{ SET2(IsResponse, IsError), InvalidCmd, "NetworkNackError" },
|
||||
/* InvalidDestError -- packet dest field invalid */
|
||||
{ SET2(IsResponse, IsError), InvalidCmd, "InvalidDestError" },
|
||||
/* BadAddressError -- memory address invalid */
|
||||
{ SET2(IsResponse, IsError), InvalidCmd, "BadAddressError" },
|
||||
/* FunctionalReadError */
|
||||
{ SET3(IsRead, IsResponse, IsError), InvalidCmd, "FunctionalReadError" },
|
||||
/* FunctionalWriteError */
|
||||
{ SET3(IsWrite, IsResponse, IsError), InvalidCmd, "FunctionalWriteError" },
|
||||
/* PrintReq */
|
||||
{ SET2(IsRequest, IsPrint), InvalidCmd, "PrintReq" },
|
||||
/* Flush Request */
|
||||
{ SET3(IsRequest, IsFlush, NeedsExclusive), InvalidCmd, "FlushReq" },
|
||||
/* Invalidation Request */
|
||||
{ SET3(NeedsExclusive, IsInvalidate, IsRequest),
|
||||
InvalidCmd, "InvalidationReq" },
|
||||
};
|
||||
|
||||
bool
|
||||
Packet::checkFunctional(Printable *obj, Addr addr, int size, uint8_t *data)
|
||||
{
|
||||
Addr func_start = getAddr();
|
||||
Addr func_end = getAddr() + getSize() - 1;
|
||||
Addr val_start = addr;
|
||||
Addr val_end = val_start + size - 1;
|
||||
|
||||
if (func_start > val_end || val_start > func_end) {
|
||||
// no intersection
|
||||
return false;
|
||||
}
|
||||
|
||||
// check print first since it doesn't require data
|
||||
if (isPrint()) {
|
||||
dynamic_cast<PrintReqState*>(senderState)->printObj(obj);
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there's no data, there's no need to look further
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// offset of functional request into supplied value (could be
|
||||
// negative if partial overlap)
|
||||
int offset = func_start - val_start;
|
||||
|
||||
if (isRead()) {
|
||||
if (func_start >= val_start && func_end <= val_end) {
|
||||
allocate();
|
||||
memcpy(getPtr<uint8_t>(), data + offset, getSize());
|
||||
return true;
|
||||
} else {
|
||||
// Offsets and sizes to copy in case of partial overlap
|
||||
int func_offset;
|
||||
int val_offset;
|
||||
int overlap_size;
|
||||
|
||||
// calculate offsets and copy sizes for the two byte arrays
|
||||
if (val_start < func_start && val_end <= func_end) {
|
||||
val_offset = func_start - val_start;
|
||||
func_offset = 0;
|
||||
overlap_size = val_end - func_start;
|
||||
} else if (val_start >= func_start && val_end > func_end) {
|
||||
val_offset = 0;
|
||||
func_offset = val_start - func_start;
|
||||
overlap_size = func_end - val_start;
|
||||
} else if (val_start >= func_start && val_end <= func_end) {
|
||||
val_offset = 0;
|
||||
func_offset = val_start - func_start;
|
||||
overlap_size = size;
|
||||
} else {
|
||||
panic("BUG: Missed a case for a partial functional request");
|
||||
}
|
||||
|
||||
// Figure out how much of the partial overlap should be copied
|
||||
// into the packet and not overwrite previously found bytes.
|
||||
if (bytesValidStart == 0 && bytesValidEnd == 0) {
|
||||
// No bytes have been copied yet, just set indices
|
||||
// to found range
|
||||
bytesValidStart = func_offset;
|
||||
bytesValidEnd = func_offset + overlap_size;
|
||||
} else {
|
||||
// Some bytes have already been copied. Use bytesValid
|
||||
// indices and offset values to figure out how much data
|
||||
// to copy and where to copy it to.
|
||||
|
||||
// Indice overlap conditions to check
|
||||
int a = func_offset - bytesValidStart;
|
||||
int b = (func_offset + overlap_size) - bytesValidEnd;
|
||||
int c = func_offset - bytesValidEnd;
|
||||
int d = (func_offset + overlap_size) - bytesValidStart;
|
||||
|
||||
if (a >= 0 && b <= 0) {
|
||||
// bytes already in pkt data array are superset of
|
||||
// found bytes, will not copy any bytes
|
||||
overlap_size = 0;
|
||||
} else if (a < 0 && d >= 0 && b <= 0) {
|
||||
// found bytes will move bytesValidStart towards 0
|
||||
overlap_size = bytesValidStart - func_offset;
|
||||
bytesValidStart = func_offset;
|
||||
} else if (b > 0 && c <= 0 && a >= 0) {
|
||||
// found bytes will move bytesValidEnd
|
||||
// towards end of pkt data array
|
||||
overlap_size =
|
||||
(func_offset + overlap_size) - bytesValidEnd;
|
||||
val_offset += bytesValidEnd - func_offset;
|
||||
func_offset = bytesValidEnd;
|
||||
bytesValidEnd += overlap_size;
|
||||
} else if (a < 0 && b > 0) {
|
||||
// Found bytes are superset of copied range. Will move
|
||||
// bytesValidStart towards 0 and bytesValidEnd towards
|
||||
// end of pkt data array. Need to break copy into two
|
||||
// pieces so as to not overwrite previously found data.
|
||||
|
||||
// copy the first half
|
||||
uint8_t *dest = getPtr<uint8_t>() + func_offset;
|
||||
uint8_t *src = data + val_offset;
|
||||
memcpy(dest, src, (bytesValidStart - func_offset));
|
||||
|
||||
// re-calc the offsets and indices to do the copy
|
||||
// required for the second half
|
||||
val_offset += (bytesValidEnd - func_offset);
|
||||
bytesValidStart = func_offset;
|
||||
overlap_size =
|
||||
(func_offset + overlap_size) - bytesValidEnd;
|
||||
func_offset = bytesValidEnd;
|
||||
bytesValidEnd += overlap_size;
|
||||
} else if ((c > 0 && b > 0)
|
||||
|| (a < 0 && d < 0)) {
|
||||
// region to be copied is discontiguous! Not supported.
|
||||
panic("BUG: Discontiguous bytes found"
|
||||
"for functional copying!");
|
||||
}
|
||||
}
|
||||
assert(bytesValidEnd <= getSize());
|
||||
|
||||
// copy partial data into the packet's data array
|
||||
uint8_t *dest = getPtr<uint8_t>() + func_offset;
|
||||
uint8_t *src = data + val_offset;
|
||||
memcpy(dest, src, overlap_size);
|
||||
|
||||
// check if we're done filling the functional access
|
||||
bool done = (bytesValidStart == 0) && (bytesValidEnd == getSize());
|
||||
return done;
|
||||
}
|
||||
} else if (isWrite()) {
|
||||
if (offset >= 0) {
|
||||
memcpy(data + offset, getPtr<uint8_t>(),
|
||||
(min(func_end, val_end) - func_start) + 1);
|
||||
} else {
|
||||
// val_start > func_start
|
||||
memcpy(data, getPtr<uint8_t>() - offset,
|
||||
(min(func_end, val_end) - val_start) + 1);
|
||||
}
|
||||
} else {
|
||||
panic("Don't know how to handle command %s\n", cmdString());
|
||||
}
|
||||
|
||||
// keep going with request by default
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Packet::print(ostream &o, const int verbosity, const string &prefix) const
|
||||
{
|
||||
ccprintf(o, "%s[%x:%x] %s\n", prefix,
|
||||
getAddr(), getAddr() + getSize() - 1, cmdString());
|
||||
}
|
||||
|
||||
Packet::PrintReqState::PrintReqState(ostream &_os, int _verbosity)
|
||||
: curPrefixPtr(new string("")), os(_os), verbosity(_verbosity)
|
||||
{
|
||||
labelStack.push_back(LabelStackEntry("", curPrefixPtr));
|
||||
}
|
||||
|
||||
Packet::PrintReqState::~PrintReqState()
|
||||
{
|
||||
labelStack.pop_back();
|
||||
assert(labelStack.empty());
|
||||
delete curPrefixPtr;
|
||||
}
|
||||
|
||||
Packet::PrintReqState::
|
||||
LabelStackEntry::LabelStackEntry(const string &_label, string *_prefix)
|
||||
: label(_label), prefix(_prefix), labelPrinted(false)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Packet::PrintReqState::pushLabel(const string &lbl, const string &prefix)
|
||||
{
|
||||
labelStack.push_back(LabelStackEntry(lbl, curPrefixPtr));
|
||||
curPrefixPtr = new string(*curPrefixPtr);
|
||||
*curPrefixPtr += prefix;
|
||||
}
|
||||
|
||||
void
|
||||
Packet::PrintReqState::popLabel()
|
||||
{
|
||||
delete curPrefixPtr;
|
||||
curPrefixPtr = labelStack.back().prefix;
|
||||
labelStack.pop_back();
|
||||
assert(!labelStack.empty());
|
||||
}
|
||||
|
||||
void
|
||||
Packet::PrintReqState::printLabels()
|
||||
{
|
||||
if (!labelStack.back().labelPrinted) {
|
||||
LabelStack::iterator i = labelStack.begin();
|
||||
LabelStack::iterator end = labelStack.end();
|
||||
while (i != end) {
|
||||
if (!i->labelPrinted) {
|
||||
ccprintf(os, "%s%s\n", *(i->prefix), i->label);
|
||||
i->labelPrinted = true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Packet::PrintReqState::printObj(Printable *obj)
|
||||
{
|
||||
printLabels();
|
||||
obj->print(os, verbosity, curPrefix());
|
||||
}
|
||||
872
simulators/gem5/src/mem/packet.hh
Normal file
872
simulators/gem5/src/mem/packet.hh
Normal file
@ -0,0 +1,872 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* Copyright (c) 2006 The Regents of The University of Michigan
|
||||
* Copyright (c) 2010 Advanced Micro Devices, Inc.
|
||||
* 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
|
||||
* Ali Saidi
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Declaration of the Packet class.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_PACKET_HH__
|
||||
#define __MEM_PACKET_HH__
|
||||
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include <list>
|
||||
|
||||
#include "base/cast.hh"
|
||||
#include "base/compiler.hh"
|
||||
#include "base/flags.hh"
|
||||
#include "base/misc.hh"
|
||||
#include "base/printable.hh"
|
||||
#include "base/types.hh"
|
||||
#include "mem/request.hh"
|
||||
#include "sim/core.hh"
|
||||
|
||||
class Packet;
|
||||
typedef Packet *PacketPtr;
|
||||
typedef uint8_t* PacketDataPtr;
|
||||
typedef std::list<PacketPtr> PacketList;
|
||||
|
||||
class MemCmd
|
||||
{
|
||||
friend class Packet;
|
||||
|
||||
public:
|
||||
/**
|
||||
* List of all commands associated with a packet.
|
||||
*/
|
||||
enum Command
|
||||
{
|
||||
InvalidCmd,
|
||||
ReadReq,
|
||||
ReadResp,
|
||||
ReadRespWithInvalidate,
|
||||
WriteReq,
|
||||
WriteResp,
|
||||
Writeback,
|
||||
SoftPFReq,
|
||||
HardPFReq,
|
||||
SoftPFResp,
|
||||
HardPFResp,
|
||||
// WriteInvalidateReq transactions used to be generated by the
|
||||
// DMA ports when writing full blocks to memory, however, it
|
||||
// is not used anymore since we put the I/O cache in place to
|
||||
// deal with partial block writes. Hence, WriteInvalidateReq
|
||||
// and WriteInvalidateResp are currently unused. The
|
||||
// implication is that the I/O cache does read-exclusive
|
||||
// operations on every full-cache-block DMA, and ultimately
|
||||
// this needs to be fixed.
|
||||
WriteInvalidateReq,
|
||||
WriteInvalidateResp,
|
||||
UpgradeReq,
|
||||
SCUpgradeReq, // Special "weak" upgrade for StoreCond
|
||||
UpgradeResp,
|
||||
SCUpgradeFailReq, // Failed SCUpgradeReq in MSHR (never sent)
|
||||
UpgradeFailResp, // Valid for SCUpgradeReq only
|
||||
ReadExReq,
|
||||
ReadExResp,
|
||||
LoadLockedReq,
|
||||
StoreCondReq,
|
||||
StoreCondFailReq, // Failed StoreCondReq in MSHR (never sent)
|
||||
StoreCondResp,
|
||||
SwapReq,
|
||||
SwapResp,
|
||||
MessageReq,
|
||||
MessageResp,
|
||||
// Error responses
|
||||
// @TODO these should be classified as responses rather than
|
||||
// requests; coding them as requests initially for backwards
|
||||
// compatibility
|
||||
NetworkNackError, // nacked at network layer (not by protocol)
|
||||
InvalidDestError, // packet dest field invalid
|
||||
BadAddressError, // memory address invalid
|
||||
FunctionalReadError, // unable to fulfill functional read
|
||||
FunctionalWriteError, // unable to fulfill functional write
|
||||
// Fake simulator-only commands
|
||||
PrintReq, // Print state matching address
|
||||
FlushReq, //request for a cache flush
|
||||
InvalidationReq, // request for address to be invalidated from lsq
|
||||
NUM_MEM_CMDS
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* List of command attributes.
|
||||
*/
|
||||
enum Attribute
|
||||
{
|
||||
IsRead, //!< Data flows from responder to requester
|
||||
IsWrite, //!< Data flows from requester to responder
|
||||
IsUpgrade,
|
||||
IsInvalidate,
|
||||
NeedsExclusive, //!< Requires exclusive copy to complete in-cache
|
||||
IsRequest, //!< Issued by requester
|
||||
IsResponse, //!< Issue by responder
|
||||
NeedsResponse, //!< Requester needs response from target
|
||||
IsSWPrefetch,
|
||||
IsHWPrefetch,
|
||||
IsLlsc, //!< Alpha/MIPS LL or SC access
|
||||
HasData, //!< There is an associated payload
|
||||
IsError, //!< Error response
|
||||
IsPrint, //!< Print state matching address (for debugging)
|
||||
IsFlush, //!< Flush the address from caches
|
||||
NUM_COMMAND_ATTRIBUTES
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure that defines attributes and other data associated
|
||||
* with a Command.
|
||||
*/
|
||||
struct CommandInfo
|
||||
{
|
||||
/// Set of attribute flags.
|
||||
const std::bitset<NUM_COMMAND_ATTRIBUTES> attributes;
|
||||
/// Corresponding response for requests; InvalidCmd if no
|
||||
/// response is applicable.
|
||||
const Command response;
|
||||
/// String representation (for printing)
|
||||
const std::string str;
|
||||
};
|
||||
|
||||
/// Array to map Command enum to associated info.
|
||||
static const CommandInfo commandInfo[];
|
||||
|
||||
private:
|
||||
|
||||
Command cmd;
|
||||
|
||||
bool
|
||||
testCmdAttrib(MemCmd::Attribute attrib) const
|
||||
{
|
||||
return commandInfo[cmd].attributes[attrib] != 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool isRead() const { return testCmdAttrib(IsRead); }
|
||||
bool isWrite() const { return testCmdAttrib(IsWrite); }
|
||||
bool isUpgrade() const { return testCmdAttrib(IsUpgrade); }
|
||||
bool isRequest() const { return testCmdAttrib(IsRequest); }
|
||||
bool isResponse() const { return testCmdAttrib(IsResponse); }
|
||||
bool needsExclusive() const { return testCmdAttrib(NeedsExclusive); }
|
||||
bool needsResponse() const { return testCmdAttrib(NeedsResponse); }
|
||||
bool isInvalidate() const { return testCmdAttrib(IsInvalidate); }
|
||||
bool hasData() const { return testCmdAttrib(HasData); }
|
||||
bool isReadWrite() const { return isRead() && isWrite(); }
|
||||
bool isLLSC() const { return testCmdAttrib(IsLlsc); }
|
||||
bool isError() const { return testCmdAttrib(IsError); }
|
||||
bool isPrint() const { return testCmdAttrib(IsPrint); }
|
||||
bool isFlush() const { return testCmdAttrib(IsFlush); }
|
||||
|
||||
const Command
|
||||
responseCommand() const
|
||||
{
|
||||
return commandInfo[cmd].response;
|
||||
}
|
||||
|
||||
/// Return the string to a cmd given by idx.
|
||||
const std::string &toString() const { return commandInfo[cmd].str; }
|
||||
int toInt() const { return (int)cmd; }
|
||||
|
||||
MemCmd(Command _cmd) : cmd(_cmd) { }
|
||||
MemCmd(int _cmd) : cmd((Command)_cmd) { }
|
||||
MemCmd() : cmd(InvalidCmd) { }
|
||||
|
||||
bool operator==(MemCmd c2) const { return (cmd == c2.cmd); }
|
||||
bool operator!=(MemCmd c2) const { return (cmd != c2.cmd); }
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 Printable
|
||||
{
|
||||
public:
|
||||
typedef uint32_t FlagsType;
|
||||
typedef ::Flags<FlagsType> Flags;
|
||||
|
||||
private:
|
||||
static const FlagsType PUBLIC_FLAGS = 0x00000000;
|
||||
static const FlagsType PRIVATE_FLAGS = 0x00007F0F;
|
||||
static const FlagsType COPY_FLAGS = 0x0000000F;
|
||||
|
||||
static const FlagsType SHARED = 0x00000001;
|
||||
// Special control flags
|
||||
/// Special timing-mode atomic snoop for multi-level coherence.
|
||||
static const FlagsType EXPRESS_SNOOP = 0x00000002;
|
||||
/// Does supplier have exclusive copy?
|
||||
/// Useful for multi-level coherence.
|
||||
static const FlagsType SUPPLY_EXCLUSIVE = 0x00000004;
|
||||
// Snoop response flags
|
||||
static const FlagsType MEM_INHIBIT = 0x00000008;
|
||||
/// Are the 'addr' and 'size' fields valid?
|
||||
static const FlagsType VALID_ADDR = 0x00000100;
|
||||
static const FlagsType VALID_SIZE = 0x00000200;
|
||||
/// Is the data pointer set to a value that shouldn't be freed
|
||||
/// when the packet is destroyed?
|
||||
static const FlagsType STATIC_DATA = 0x00001000;
|
||||
/// The data pointer points to a value that should be freed when
|
||||
/// the packet is destroyed.
|
||||
static const FlagsType DYNAMIC_DATA = 0x00002000;
|
||||
/// the data pointer points to an array (thus delete []) needs to
|
||||
/// be called on it rather than simply delete.
|
||||
static const FlagsType ARRAY_DATA = 0x00004000;
|
||||
/// suppress the error if this packet encounters a functional
|
||||
/// access failure.
|
||||
static const FlagsType SUPPRESS_FUNC_ERROR = 0x00008000;
|
||||
|
||||
Flags flags;
|
||||
|
||||
public:
|
||||
typedef MemCmd::Command Command;
|
||||
|
||||
/// The command field of the packet.
|
||||
MemCmd cmd;
|
||||
|
||||
/// A pointer to the original request.
|
||||
RequestPtr req;
|
||||
|
||||
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.
|
||||
*/
|
||||
PacketDataPtr data;
|
||||
|
||||
/// The address of the request. This address could be virtual or
|
||||
/// physical, depending on the system configuration.
|
||||
Addr addr;
|
||||
|
||||
/// The size of the request or transfer.
|
||||
unsigned size;
|
||||
|
||||
/**
|
||||
* Source port identifier set on a request packet to enable
|
||||
* appropriate routing of the responses. The source port
|
||||
* identifier is set by any multiplexing component, e.g. a bus, as
|
||||
* the timing responses need this information to be routed back to
|
||||
* the appropriate port at a later point in time. The field can be
|
||||
* updated (over-written) as the request packet passes through
|
||||
* additional multiplexing components, and it is their
|
||||
* responsibility to remember the original source port identifier,
|
||||
* for example by using an appropriate sender state. The latter is
|
||||
* done in the cache and bridge.
|
||||
*/
|
||||
PortID src;
|
||||
|
||||
/**
|
||||
* Destination port identifier that is present on all response
|
||||
* packets that passed through a multiplexing component as a
|
||||
* request packet. The source port identifier is turned into a
|
||||
* destination port identifier when the packet is turned into a
|
||||
* response, and the destination is used, e.g. by the bus, to
|
||||
* select the appropriate path through the interconnect.
|
||||
*/
|
||||
PortID dest;
|
||||
|
||||
/**
|
||||
* The original value of the command field. Only valid when the
|
||||
* current command field is an error condition; in that case, the
|
||||
* previous contents of the command field are copied here. This
|
||||
* field is *not* set on non-error responses.
|
||||
*/
|
||||
MemCmd origCmd;
|
||||
|
||||
/**
|
||||
* These values specify the range of bytes found that satisfy a
|
||||
* functional read.
|
||||
*/
|
||||
uint16_t bytesValidStart;
|
||||
uint16_t bytesValidEnd;
|
||||
|
||||
public:
|
||||
/// Used to calculate latencies for each packet.
|
||||
Tick time;
|
||||
|
||||
/// The time at which the packet will be fully transmitted
|
||||
Tick finishTime;
|
||||
|
||||
/// The time at which the first chunk of the packet will be transmitted
|
||||
Tick firstWordTime;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
struct SenderState
|
||||
{
|
||||
virtual ~SenderState() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Object used to maintain state of a PrintReq. The senderState
|
||||
* field of a PrintReq should always be of this type.
|
||||
*/
|
||||
class PrintReqState : public SenderState
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* An entry in the label stack.
|
||||
*/
|
||||
struct LabelStackEntry
|
||||
{
|
||||
const std::string label;
|
||||
std::string *prefix;
|
||||
bool labelPrinted;
|
||||
LabelStackEntry(const std::string &_label, std::string *_prefix);
|
||||
};
|
||||
|
||||
typedef std::list<LabelStackEntry> LabelStack;
|
||||
LabelStack labelStack;
|
||||
|
||||
std::string *curPrefixPtr;
|
||||
|
||||
public:
|
||||
std::ostream &os;
|
||||
const int verbosity;
|
||||
|
||||
PrintReqState(std::ostream &os, int verbosity = 0);
|
||||
~PrintReqState();
|
||||
|
||||
/**
|
||||
* Returns the current line prefix.
|
||||
*/
|
||||
const std::string &curPrefix() { return *curPrefixPtr; }
|
||||
|
||||
/**
|
||||
* Push a label onto the label stack, and prepend the given
|
||||
* prefix string onto the current prefix. Labels will only be
|
||||
* printed if an object within the label's scope is printed.
|
||||
*/
|
||||
void pushLabel(const std::string &lbl,
|
||||
const std::string &prefix = " ");
|
||||
|
||||
/**
|
||||
* Pop a label off the label stack.
|
||||
*/
|
||||
void popLabel();
|
||||
|
||||
/**
|
||||
* Print all of the pending unprinted labels on the
|
||||
* stack. Called by printObj(), so normally not called by
|
||||
* users unless bypassing printObj().
|
||||
*/
|
||||
void printLabels();
|
||||
|
||||
/**
|
||||
* Print a Printable object to os, because it matched the
|
||||
* address on a PrintReq.
|
||||
*/
|
||||
void printObj(Printable *obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* This packet's sender state. Devices should use dynamic_cast<>
|
||||
* to cast to the state appropriate to the sender. The intent of
|
||||
* this variable is to allow a device to attach extra information
|
||||
* to a request. A response packet must return the sender state
|
||||
* that was attached to the original request (even if a new packet
|
||||
* is created).
|
||||
*/
|
||||
SenderState *senderState;
|
||||
|
||||
/// Return the string name of the cmd field (for debugging and
|
||||
/// tracing).
|
||||
const std::string &cmdString() const { return cmd.toString(); }
|
||||
|
||||
/// Return the index of this command.
|
||||
inline int cmdToIndex() const { return cmd.toInt(); }
|
||||
|
||||
bool isRead() const { return cmd.isRead(); }
|
||||
bool isWrite() const { return cmd.isWrite(); }
|
||||
bool isUpgrade() const { return cmd.isUpgrade(); }
|
||||
bool isRequest() const { return cmd.isRequest(); }
|
||||
bool isResponse() const { return cmd.isResponse(); }
|
||||
bool needsExclusive() const { return cmd.needsExclusive(); }
|
||||
bool needsResponse() const { return cmd.needsResponse(); }
|
||||
bool isInvalidate() const { return cmd.isInvalidate(); }
|
||||
bool hasData() const { return cmd.hasData(); }
|
||||
bool isReadWrite() const { return cmd.isReadWrite(); }
|
||||
bool isLLSC() const { return cmd.isLLSC(); }
|
||||
bool isError() const { return cmd.isError(); }
|
||||
bool isPrint() const { return cmd.isPrint(); }
|
||||
bool isFlush() const { return cmd.isFlush(); }
|
||||
|
||||
// Snoop flags
|
||||
void assertMemInhibit() { flags.set(MEM_INHIBIT); }
|
||||
bool memInhibitAsserted() { return flags.isSet(MEM_INHIBIT); }
|
||||
void assertShared() { flags.set(SHARED); }
|
||||
bool sharedAsserted() { return flags.isSet(SHARED); }
|
||||
|
||||
// Special control flags
|
||||
void setExpressSnoop() { flags.set(EXPRESS_SNOOP); }
|
||||
bool isExpressSnoop() { return flags.isSet(EXPRESS_SNOOP); }
|
||||
void setSupplyExclusive() { flags.set(SUPPLY_EXCLUSIVE); }
|
||||
void clearSupplyExclusive() { flags.clear(SUPPLY_EXCLUSIVE); }
|
||||
bool isSupplyExclusive() { return flags.isSet(SUPPLY_EXCLUSIVE); }
|
||||
void setSuppressFuncError() { flags.set(SUPPRESS_FUNC_ERROR); }
|
||||
bool suppressFuncError() { return flags.isSet(SUPPRESS_FUNC_ERROR); }
|
||||
|
||||
// Network error conditions... encapsulate them as methods since
|
||||
// their encoding keeps changing (from result field to command
|
||||
// field, etc.)
|
||||
void
|
||||
setNacked()
|
||||
{
|
||||
assert(isResponse());
|
||||
cmd = MemCmd::NetworkNackError;
|
||||
}
|
||||
|
||||
void
|
||||
setBadAddress()
|
||||
{
|
||||
assert(isResponse());
|
||||
cmd = MemCmd::BadAddressError;
|
||||
}
|
||||
|
||||
bool wasNacked() const { return cmd == MemCmd::NetworkNackError; }
|
||||
bool hadBadAddress() const { return cmd == MemCmd::BadAddressError; }
|
||||
void copyError(Packet *pkt) { assert(pkt->isError()); cmd = pkt->cmd; }
|
||||
|
||||
bool isSrcValid() const { return src != InvalidPortID; }
|
||||
/// Accessor function to get the source index of the packet.
|
||||
PortID getSrc() const { assert(isSrcValid()); return src; }
|
||||
/// Accessor function to set the source index of the packet.
|
||||
void setSrc(PortID _src) { src = _src; }
|
||||
/// Reset source field, e.g. to retransmit packet on different bus.
|
||||
void clearSrc() { src = InvalidPortID; }
|
||||
|
||||
bool isDestValid() const { return dest != InvalidPortID; }
|
||||
/// Accessor function for the destination index of the packet.
|
||||
PortID getDest() const { assert(isDestValid()); return dest; }
|
||||
/// Accessor function to set the destination index of the packet.
|
||||
void setDest(PortID _dest) { dest = _dest; }
|
||||
/// Reset destination field, e.g. to turn a response into a request again.
|
||||
void clearDest() { dest = InvalidPortID; }
|
||||
|
||||
Addr getAddr() const { assert(flags.isSet(VALID_ADDR)); return addr; }
|
||||
unsigned getSize() const { assert(flags.isSet(VALID_SIZE)); return size; }
|
||||
Addr getOffset(int blkSize) const { return getAddr() & (Addr)(blkSize - 1); }
|
||||
|
||||
/**
|
||||
* It has been determined that the SC packet should successfully update
|
||||
* memory. Therefore, convert this SC packet to a normal write.
|
||||
*/
|
||||
void
|
||||
convertScToWrite()
|
||||
{
|
||||
assert(isLLSC());
|
||||
assert(isWrite());
|
||||
cmd = MemCmd::WriteReq;
|
||||
}
|
||||
|
||||
/**
|
||||
* When ruby is in use, Ruby will monitor the cache line and thus M5
|
||||
* phys memory should treat LL ops as normal reads.
|
||||
*/
|
||||
void
|
||||
convertLlToRead()
|
||||
{
|
||||
assert(isLLSC());
|
||||
assert(isRead());
|
||||
cmd = MemCmd::ReadReq;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 must be supplied.
|
||||
*/
|
||||
Packet(Request *_req, MemCmd _cmd)
|
||||
: cmd(_cmd), req(_req), data(NULL),
|
||||
src(InvalidPortID), dest(InvalidPortID),
|
||||
bytesValidStart(0), bytesValidEnd(0),
|
||||
time(curTick()), senderState(NULL)
|
||||
{
|
||||
if (req->hasPaddr()) {
|
||||
addr = req->getPaddr();
|
||||
flags.set(VALID_ADDR);
|
||||
}
|
||||
if (req->hasSize()) {
|
||||
size = req->getSize();
|
||||
flags.set(VALID_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, MemCmd _cmd, int _blkSize)
|
||||
: cmd(_cmd), req(_req), data(NULL),
|
||||
src(InvalidPortID), dest(InvalidPortID),
|
||||
bytesValidStart(0), bytesValidEnd(0),
|
||||
time(curTick()), senderState(NULL)
|
||||
{
|
||||
if (req->hasPaddr()) {
|
||||
addr = req->getPaddr() & ~(_blkSize - 1);
|
||||
flags.set(VALID_ADDR);
|
||||
}
|
||||
size = _blkSize;
|
||||
flags.set(VALID_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternate constructor for copying a packet. Copy all fields
|
||||
* *except* if the original packet's data was dynamic, don't copy
|
||||
* that, as we can't guarantee that the new packet's lifetime is
|
||||
* less than that of the original packet. In this case the new
|
||||
* packet should allocate its own data.
|
||||
*/
|
||||
Packet(Packet *pkt, bool clearFlags = false)
|
||||
: cmd(pkt->cmd), req(pkt->req),
|
||||
data(pkt->flags.isSet(STATIC_DATA) ? pkt->data : NULL),
|
||||
addr(pkt->addr), size(pkt->size), src(pkt->src), dest(pkt->dest),
|
||||
bytesValidStart(pkt->bytesValidStart), bytesValidEnd(pkt->bytesValidEnd),
|
||||
time(curTick()), senderState(pkt->senderState)
|
||||
{
|
||||
if (!clearFlags)
|
||||
flags.set(pkt->flags & COPY_FLAGS);
|
||||
|
||||
flags.set(pkt->flags & (VALID_ADDR|VALID_SIZE));
|
||||
flags.set(pkt->flags & STATIC_DATA);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* clean up packet variables
|
||||
*/
|
||||
~Packet()
|
||||
{
|
||||
// If this is a request packet for which there's no response,
|
||||
// delete the request object here, since the requester will
|
||||
// never get the chance.
|
||||
if (req && isRequest() && !needsResponse())
|
||||
delete req;
|
||||
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->hasPaddr());
|
||||
flags = 0;
|
||||
addr = req->getPaddr();
|
||||
size = req->getSize();
|
||||
time = req->time();
|
||||
|
||||
flags.set(VALID_ADDR|VALID_SIZE);
|
||||
deleteData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a request packet and modify it in place to be suitable for
|
||||
* returning as a response to that request. The source field is
|
||||
* turned into the destination, and subsequently cleared. Note
|
||||
* that the latter is not necessary for atomic requests, but
|
||||
* causes no harm as neither field is valid.
|
||||
*/
|
||||
void
|
||||
makeResponse()
|
||||
{
|
||||
assert(needsResponse());
|
||||
assert(isRequest());
|
||||
origCmd = cmd;
|
||||
cmd = cmd.responseCommand();
|
||||
|
||||
// responses are never express, even if the snoop that
|
||||
// triggered them was
|
||||
flags.clear(EXPRESS_SNOOP);
|
||||
|
||||
dest = src;
|
||||
clearSrc();
|
||||
}
|
||||
|
||||
void
|
||||
makeAtomicResponse()
|
||||
{
|
||||
makeResponse();
|
||||
}
|
||||
|
||||
void
|
||||
makeTimingResponse()
|
||||
{
|
||||
makeResponse();
|
||||
}
|
||||
|
||||
void
|
||||
setFunctionalResponseStatus(bool success)
|
||||
{
|
||||
if (!success) {
|
||||
if (isWrite()) {
|
||||
cmd = MemCmd::FunctionalWriteError;
|
||||
} else {
|
||||
cmd = MemCmd::FunctionalReadError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(wasNacked());
|
||||
cmd = origCmd;
|
||||
assert(needsResponse());
|
||||
clearDest();
|
||||
}
|
||||
|
||||
void
|
||||
setSize(unsigned size)
|
||||
{
|
||||
assert(!flags.isSet(VALID_SIZE));
|
||||
|
||||
this->size = size;
|
||||
flags.set(VALID_SIZE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the data pointer to the following value that should not be
|
||||
* freed.
|
||||
*/
|
||||
template <typename T>
|
||||
void
|
||||
dataStatic(T *p)
|
||||
{
|
||||
assert(flags.noneSet(STATIC_DATA|DYNAMIC_DATA|ARRAY_DATA));
|
||||
data = (PacketDataPtr)p;
|
||||
flags.set(STATIC_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the data pointer to a value that should have delete []
|
||||
* called on it.
|
||||
*/
|
||||
template <typename T>
|
||||
void
|
||||
dataDynamicArray(T *p)
|
||||
{
|
||||
assert(flags.noneSet(STATIC_DATA|DYNAMIC_DATA|ARRAY_DATA));
|
||||
data = (PacketDataPtr)p;
|
||||
flags.set(DYNAMIC_DATA|ARRAY_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the data pointer to a value that should have delete called
|
||||
* on it.
|
||||
*/
|
||||
template <typename T>
|
||||
void
|
||||
dataDynamic(T *p)
|
||||
{
|
||||
assert(flags.noneSet(STATIC_DATA|DYNAMIC_DATA|ARRAY_DATA));
|
||||
data = (PacketDataPtr)p;
|
||||
flags.set(DYNAMIC_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* get a pointer to the data ptr.
|
||||
*/
|
||||
template <typename T>
|
||||
T*
|
||||
getPtr(bool null_ok = false)
|
||||
{
|
||||
assert(null_ok || flags.isSet(STATIC_DATA|DYNAMIC_DATA));
|
||||
return (T*)data;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the value of what is pointed to in the packet.
|
||||
*/
|
||||
template <typename T>
|
||||
T get();
|
||||
|
||||
/**
|
||||
* set the value in the data pointer to v.
|
||||
*/
|
||||
template <typename T>
|
||||
void set(T v);
|
||||
|
||||
/**
|
||||
* Copy data into the packet from the provided pointer.
|
||||
*/
|
||||
void
|
||||
setData(uint8_t *p)
|
||||
{
|
||||
if (p != getPtr<uint8_t>())
|
||||
std::memcpy(getPtr<uint8_t>(), p, getSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data into the packet from the provided block pointer,
|
||||
* which is aligned to the given block size.
|
||||
*/
|
||||
void
|
||||
setDataFromBlock(uint8_t *blk_data, int blkSize)
|
||||
{
|
||||
setData(blk_data + getOffset(blkSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from the packet to the provided block pointer, which
|
||||
* is aligned to the given block size.
|
||||
*/
|
||||
void
|
||||
writeData(uint8_t *p)
|
||||
{
|
||||
std::memcpy(p, getPtr<uint8_t>(), getSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from the packet to the memory at the provided pointer.
|
||||
*/
|
||||
void
|
||||
writeDataToBlock(uint8_t *blk_data, int blkSize)
|
||||
{
|
||||
writeData(blk_data + getOffset(blkSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* delete the data pointed to in the data pointer. Ok to call to
|
||||
* matter how data was allocted.
|
||||
*/
|
||||
void
|
||||
deleteData()
|
||||
{
|
||||
if (flags.isSet(ARRAY_DATA))
|
||||
delete [] data;
|
||||
else if (flags.isSet(DYNAMIC_DATA))
|
||||
delete data;
|
||||
|
||||
flags.clear(STATIC_DATA|DYNAMIC_DATA|ARRAY_DATA);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
/** If there isn't data in the packet, allocate some. */
|
||||
void
|
||||
allocate()
|
||||
{
|
||||
if (data) {
|
||||
assert(flags.isSet(STATIC_DATA|DYNAMIC_DATA));
|
||||
return;
|
||||
}
|
||||
|
||||
assert(flags.noneSet(STATIC_DATA|DYNAMIC_DATA));
|
||||
flags.set(DYNAMIC_DATA|ARRAY_DATA);
|
||||
data = new uint8_t[getSize()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a functional request against a memory value represented
|
||||
* by a base/size pair and an associated data array. If the
|
||||
* functional request is a read, it may be satisfied by the memory
|
||||
* value. If the functional request is a write, it may update the
|
||||
* memory value.
|
||||
*/
|
||||
bool checkFunctional(Printable *obj, Addr base, int size, uint8_t *data);
|
||||
|
||||
/**
|
||||
* Check a functional request against a memory value stored in
|
||||
* another packet (i.e. an in-transit request or response).
|
||||
*/
|
||||
bool
|
||||
checkFunctional(PacketPtr other)
|
||||
{
|
||||
uint8_t *data = other->hasData() ? other->getPtr<uint8_t>() : NULL;
|
||||
return checkFunctional(other, other->getAddr(), other->getSize(),
|
||||
data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push label for PrintReq (safe to call unconditionally).
|
||||
*/
|
||||
void
|
||||
pushLabel(const std::string &lbl)
|
||||
{
|
||||
if (isPrint())
|
||||
safe_cast<PrintReqState*>(senderState)->pushLabel(lbl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop label for PrintReq (safe to call unconditionally).
|
||||
*/
|
||||
void
|
||||
popLabel()
|
||||
{
|
||||
if (isPrint())
|
||||
safe_cast<PrintReqState*>(senderState)->popLabel();
|
||||
}
|
||||
|
||||
void print(std::ostream &o, int verbosity = 0,
|
||||
const std::string &prefix = "") const;
|
||||
};
|
||||
|
||||
#endif //__MEM_PACKET_HH
|
||||
65
simulators/gem5/src/mem/packet_access.hh
Normal file
65
simulators/gem5/src/mem/packet_access.hh
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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
|
||||
* Nathan Binkert
|
||||
*/
|
||||
|
||||
#include "arch/isa_traits.hh"
|
||||
#include "base/bigint.hh"
|
||||
#include "config/the_isa.hh"
|
||||
#include "mem/packet.hh"
|
||||
#include "sim/byteswap.hh"
|
||||
|
||||
#ifndef __MEM_PACKET_ACCESS_HH__
|
||||
#define __MEM_PACKET_ACCESS_HH__
|
||||
// The memory system needs to have an endianness. This is the easiest
|
||||
// way to deal with it for now. At some point, we will have to remove
|
||||
// these functions and make the users do their own byte swapping since
|
||||
// the memory system does not in fact have an endianness.
|
||||
|
||||
/** return the value of what is pointed to in the packet. */
|
||||
template <typename T>
|
||||
inline T
|
||||
Packet::get()
|
||||
{
|
||||
assert(flags.isSet(STATIC_DATA|DYNAMIC_DATA));
|
||||
assert(sizeof(T) <= size);
|
||||
return TheISA::gtoh(*(T*)data);
|
||||
}
|
||||
|
||||
/** set the value in the data pointer to v. */
|
||||
template <typename T>
|
||||
inline void
|
||||
Packet::set(T v)
|
||||
{
|
||||
assert(flags.isSet(STATIC_DATA|DYNAMIC_DATA));
|
||||
assert(sizeof(T) <= size);
|
||||
*(T*)data = TheISA::htog(v);
|
||||
}
|
||||
|
||||
#endif //__MEM_PACKET_ACCESS_HH__
|
||||
236
simulators/gem5/src/mem/packet_queue.cc
Normal file
236
simulators/gem5/src/mem/packet_queue.cc
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved.
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
#include "debug/PacketQueue.hh"
|
||||
#include "mem/packet_queue.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
PacketQueue::PacketQueue(EventManager& _em, const std::string& _label)
|
||||
: em(_em), sendEvent(this), drainEvent(NULL), label(_label),
|
||||
waitingOnRetry(false)
|
||||
{
|
||||
}
|
||||
|
||||
PacketQueue::~PacketQueue()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
PacketQueue::retry()
|
||||
{
|
||||
DPRINTF(PacketQueue, "Queue %s received retry\n", name());
|
||||
assert(waitingOnRetry);
|
||||
sendDeferredPacket();
|
||||
}
|
||||
|
||||
bool
|
||||
PacketQueue::checkFunctional(PacketPtr pkt)
|
||||
{
|
||||
pkt->pushLabel(label);
|
||||
|
||||
DeferredPacketIterator i = transmitList.begin();
|
||||
DeferredPacketIterator end = transmitList.end();
|
||||
bool found = false;
|
||||
|
||||
while (!found && i != end) {
|
||||
// If the buffered packet contains data, and it overlaps the
|
||||
// current packet, then update data
|
||||
found = pkt->checkFunctional(i->pkt);
|
||||
++i;
|
||||
}
|
||||
|
||||
pkt->popLabel();
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void
|
||||
PacketQueue::schedSendEvent(Tick when)
|
||||
{
|
||||
// if we are waiting on a retry, do not schedule a send event, and
|
||||
// instead rely on retry being called
|
||||
if (waitingOnRetry) {
|
||||
assert(!sendEvent.scheduled());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sendEvent.scheduled()) {
|
||||
em.schedule(&sendEvent, when);
|
||||
} else if (sendEvent.when() > when) {
|
||||
em.reschedule(&sendEvent, when);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PacketQueue::schedSendTiming(PacketPtr pkt, Tick when, bool send_as_snoop)
|
||||
{
|
||||
assert(when > curTick());
|
||||
|
||||
// nothing on the list, or earlier than current front element,
|
||||
// schedule an event
|
||||
if (transmitList.empty() || when < transmitList.front().tick) {
|
||||
// note that currently we ignore a potentially outstanding retry
|
||||
// and could in theory put a new packet at the head of the
|
||||
// transmit list before retrying the existing packet
|
||||
transmitList.push_front(DeferredPacket(when, pkt, send_as_snoop));
|
||||
schedSendEvent(when);
|
||||
return;
|
||||
}
|
||||
|
||||
// list is non-empty and this belongs at the end
|
||||
if (when >= transmitList.back().tick) {
|
||||
transmitList.push_back(DeferredPacket(when, pkt, send_as_snoop));
|
||||
return;
|
||||
}
|
||||
|
||||
// this belongs in the middle somewhere, insertion sort
|
||||
DeferredPacketIterator i = transmitList.begin();
|
||||
++i; // already checked for insertion at front
|
||||
while (i != transmitList.end() && when >= i->tick)
|
||||
++i;
|
||||
transmitList.insert(i, DeferredPacket(when, pkt, send_as_snoop));
|
||||
}
|
||||
|
||||
void PacketQueue::trySendTiming()
|
||||
{
|
||||
assert(deferredPacketReady());
|
||||
|
||||
// take the next packet off the list here, as we might return to
|
||||
// ourselves through the sendTiming call below
|
||||
DeferredPacket dp = transmitList.front();
|
||||
transmitList.pop_front();
|
||||
|
||||
// use the appropriate implementation of sendTiming based on the
|
||||
// type of port associated with the queue, and whether the packet
|
||||
// is to be sent as a snoop or not
|
||||
waitingOnRetry = !sendTiming(dp.pkt, dp.sendAsSnoop);
|
||||
|
||||
if (waitingOnRetry) {
|
||||
// put the packet back at the front of the list (packet should
|
||||
// not have changed since it wasn't accepted)
|
||||
assert(!sendEvent.scheduled());
|
||||
transmitList.push_front(dp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PacketQueue::scheduleSend(Tick time)
|
||||
{
|
||||
// the next ready time is either determined by the next deferred packet,
|
||||
// or in the cache through the MSHR ready time
|
||||
Tick nextReady = std::min(deferredPacketReadyTime(), time);
|
||||
|
||||
if (nextReady != MaxTick) {
|
||||
// if the sendTiming caused someone else to call our
|
||||
// recvTiming we could already have an event scheduled, check
|
||||
if (!sendEvent.scheduled())
|
||||
em.schedule(&sendEvent, std::max(nextReady, curTick() + 1));
|
||||
} else {
|
||||
// no more to send, so if we're draining, we may be done
|
||||
if (drainEvent && !sendEvent.scheduled()) {
|
||||
drainEvent->process();
|
||||
drainEvent = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PacketQueue::sendDeferredPacket()
|
||||
{
|
||||
// try to send what is on the list, this will set waitingOnRetry
|
||||
// accordingly
|
||||
trySendTiming();
|
||||
|
||||
// if we succeeded and are not waiting for a retry, schedule the
|
||||
// next send
|
||||
if (!waitingOnRetry) {
|
||||
scheduleSend();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PacketQueue::processSendEvent()
|
||||
{
|
||||
assert(!waitingOnRetry);
|
||||
sendDeferredPacket();
|
||||
}
|
||||
|
||||
unsigned int
|
||||
PacketQueue::drain(Event *de)
|
||||
{
|
||||
if (transmitList.empty() && !sendEvent.scheduled())
|
||||
return 0;
|
||||
drainEvent = de;
|
||||
return 1;
|
||||
}
|
||||
|
||||
MasterPacketQueue::MasterPacketQueue(EventManager& _em, MasterPort& _masterPort,
|
||||
const std::string _label)
|
||||
: PacketQueue(_em, _label), masterPort(_masterPort)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
MasterPacketQueue::sendTiming(PacketPtr pkt, bool send_as_snoop)
|
||||
{
|
||||
// attempt to send the packet and return according to the outcome
|
||||
if (!send_as_snoop)
|
||||
return masterPort.sendTimingReq(pkt);
|
||||
else
|
||||
return masterPort.sendTimingSnoopResp(pkt);
|
||||
}
|
||||
|
||||
SlavePacketQueue::SlavePacketQueue(EventManager& _em, SlavePort& _slavePort,
|
||||
const std::string _label)
|
||||
: PacketQueue(_em, _label), slavePort(_slavePort)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
SlavePacketQueue::sendTiming(PacketPtr pkt, bool send_as_snoop)
|
||||
{
|
||||
// we should never have queued snoop requests
|
||||
assert(!send_as_snoop);
|
||||
return slavePort.sendTimingResp(pkt);
|
||||
}
|
||||
278
simulators/gem5/src/mem/packet_queue.hh
Normal file
278
simulators/gem5/src/mem/packet_queue.hh
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved.
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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
|
||||
* Andreas Hansson
|
||||
*/
|
||||
|
||||
#ifndef __MEM_PACKET_QUEUE_HH__
|
||||
#define __MEM_PACKET_QUEUE_HH__
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Declaration of a simple PacketQueue that is associated with
|
||||
* a port on which it attempts to send packets according to the time
|
||||
* stamp given to them at insertion. The packet queue is responsible
|
||||
* for the flow control of the port, but relies on the module
|
||||
* notifying the queue when a transfer ends.
|
||||
*/
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "mem/port.hh"
|
||||
#include "sim/eventq.hh"
|
||||
|
||||
/**
|
||||
* A packet queue is a class that holds deferred packets and later
|
||||
* sends them using the associated slave port or master port.
|
||||
*/
|
||||
class PacketQueue
|
||||
{
|
||||
private:
|
||||
/** A deferred packet, buffered to transmit later. */
|
||||
class DeferredPacket {
|
||||
public:
|
||||
Tick tick; ///< The tick when the packet is ready to transmit
|
||||
PacketPtr pkt; ///< Pointer to the packet to transmit
|
||||
bool sendAsSnoop; ///< Should it be sent as a snoop or not
|
||||
DeferredPacket(Tick t, PacketPtr p, bool send_as_snoop)
|
||||
: tick(t), pkt(p), sendAsSnoop(send_as_snoop)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::list<DeferredPacket> DeferredPacketList;
|
||||
typedef std::list<DeferredPacket>::iterator DeferredPacketIterator;
|
||||
|
||||
/** A list of outgoing timing response packets that haven't been
|
||||
* serviced yet. */
|
||||
DeferredPacketList transmitList;
|
||||
|
||||
/** The manager which is used for the event queue */
|
||||
EventManager& em;
|
||||
|
||||
/** This function attempts to send deferred packets. Scheduled to
|
||||
* be called in the future via SendEvent. */
|
||||
void processSendEvent();
|
||||
|
||||
/**
|
||||
* Event used to call processSendEvent.
|
||||
**/
|
||||
EventWrapper<PacketQueue, &PacketQueue::processSendEvent> sendEvent;
|
||||
|
||||
/** If we need to drain, keep the drain event around until we're done
|
||||
* here.*/
|
||||
Event *drainEvent;
|
||||
|
||||
protected:
|
||||
|
||||
/** Label to use for print request packets label stack. */
|
||||
const std::string label;
|
||||
|
||||
/** Remember whether we're awaiting a retry from the bus. */
|
||||
bool waitingOnRetry;
|
||||
|
||||
/** Check whether we have a packet ready to go on the transmit list. */
|
||||
bool deferredPacketReady()
|
||||
{ return !transmitList.empty() && transmitList.front().tick <= curTick(); }
|
||||
|
||||
Tick deferredPacketReadyTime()
|
||||
{ return transmitList.empty() ? MaxTick : transmitList.front().tick; }
|
||||
|
||||
/**
|
||||
* Attempt to send the packet at the head of the transmit
|
||||
* list. Caller must guarantee that the list is non-empty and that
|
||||
* the head packet is scheduled for curTick() (or earlier). Note
|
||||
* that a subclass of the PacketQueue can override this method and
|
||||
* thus change the behaviour (as done by the cache).
|
||||
*/
|
||||
virtual void sendDeferredPacket();
|
||||
|
||||
/**
|
||||
* Attempt to send the packet at the front of the transmit list,
|
||||
* and set waitingOnRetry accordingly. The packet is temporarily
|
||||
* taken off the list, but put back at the front if not
|
||||
* successfully sent.
|
||||
*/
|
||||
void trySendTiming();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
virtual bool sendTiming(PacketPtr pkt, bool send_as_snoop) = 0;
|
||||
|
||||
/**
|
||||
* Based on the transmit list, or the provided time, schedule a
|
||||
* send event if there are packets to send. If we are idle and
|
||||
* asked to drain then do so.
|
||||
*
|
||||
* @param time an alternative time for the next send event
|
||||
*/
|
||||
void scheduleSend(Tick time = MaxTick);
|
||||
|
||||
/**
|
||||
* Simple ports are generally used as slave ports (i.e. the
|
||||
* respond to requests) and thus do not expect to receive any
|
||||
* range changes (as the neighbouring port has a master role and
|
||||
* do not have any address ranges. A subclass can override the
|
||||
* default behaviuor if needed.
|
||||
*/
|
||||
virtual void recvRangeChange() { }
|
||||
|
||||
/**
|
||||
* Create a packet queue, linked to an event manager, and a label
|
||||
* that will be used for functional print request packets.
|
||||
*
|
||||
* @param _em Event manager used for scheduling this queue
|
||||
* @param _label Label to push on the label stack for print request packets
|
||||
*/
|
||||
PacketQueue(EventManager& _em, const std::string& _label);
|
||||
|
||||
/**
|
||||
* Virtual desctructor since the class may be used as a base class.
|
||||
*/
|
||||
virtual ~PacketQueue();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Provide a name to simplify debugging.
|
||||
*
|
||||
* @return A complete name, appended to module and port
|
||||
*/
|
||||
virtual const std::string name() const = 0;
|
||||
|
||||
/** Check the list of buffered packets against the supplied
|
||||
* functional request. */
|
||||
bool checkFunctional(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Schedule a send even if not already waiting for a retry. If the
|
||||
* requested time is before an already scheduled send event it
|
||||
* will be rescheduled.
|
||||
*
|
||||
* @param when
|
||||
*/
|
||||
void schedSendEvent(Tick when);
|
||||
|
||||
/**
|
||||
* Add a packet to the transmit list, and ensure that a
|
||||
* processSendEvent is called in the future.
|
||||
*
|
||||
* @param pkt Packet to send
|
||||
* @param when Absolute time (in ticks) to send packet
|
||||
* @param send_as_snoop Send the packet as a snoop or not
|
||||
*/
|
||||
void schedSendTiming(PacketPtr pkt, Tick when, bool send_as_snoop = false);
|
||||
|
||||
/**
|
||||
* Used by a port to notify the queue that a retry was received
|
||||
* and that the queue can proceed and retry sending the packet
|
||||
* that caused the wait.
|
||||
*/
|
||||
void retry();
|
||||
|
||||
/**
|
||||
* Hook for draining the packet queue.
|
||||
*
|
||||
* @param de An event which is used to signal back to the caller
|
||||
* @return A number indicating how many times process will be called
|
||||
*/
|
||||
unsigned int drain(Event *de);
|
||||
};
|
||||
|
||||
class MasterPacketQueue : public PacketQueue
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
MasterPort& masterPort;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create a master packet queue, linked to an event manager, a
|
||||
* master port, and a label that will be used for functional print
|
||||
* request packets.
|
||||
*
|
||||
* @param _em Event manager used for scheduling this queue
|
||||
* @param _masterPort Master port used to send the packets
|
||||
* @param _label Label to push on the label stack for print request packets
|
||||
*/
|
||||
MasterPacketQueue(EventManager& _em, MasterPort& _masterPort,
|
||||
const std::string _label = "MasterPacketQueue");
|
||||
|
||||
virtual ~MasterPacketQueue() { }
|
||||
|
||||
const std::string name() const
|
||||
{ return masterPort.name() + "-" + label; }
|
||||
|
||||
bool sendTiming(PacketPtr pkt, bool send_as_snoop);
|
||||
};
|
||||
|
||||
class SlavePacketQueue : public PacketQueue
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
SlavePort& slavePort;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create a slave packet queue, linked to an event manager, a
|
||||
* slave port, and a label that will be used for functional print
|
||||
* request packets.
|
||||
*
|
||||
* @param _em Event manager used for scheduling this queue
|
||||
* @param _slavePort Slave port used to send the packets
|
||||
* @param _label Label to push on the label stack for print request packets
|
||||
*/
|
||||
SlavePacketQueue(EventManager& _em, SlavePort& _slavePort,
|
||||
const std::string _label = "SlavePacketQueue");
|
||||
|
||||
virtual ~SlavePacketQueue() { }
|
||||
|
||||
const std::string name() const
|
||||
{ return slavePort.name() + "-" + label; }
|
||||
|
||||
bool sendTiming(PacketPtr pkt, bool send_as_snoop);
|
||||
|
||||
};
|
||||
|
||||
#endif // __MEM_PACKET_QUEUE_HH__
|
||||
234
simulators/gem5/src/mem/page_table.cc
Normal file
234
simulators/gem5/src/mem/page_table.cc
Normal file
@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (c) 2003 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
|
||||
* Ron Dreslinski
|
||||
* Ali Saidi
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definitions of page table.
|
||||
*/
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/bitfield.hh"
|
||||
#include "base/intmath.hh"
|
||||
#include "base/trace.hh"
|
||||
#include "config/the_isa.hh"
|
||||
#include "debug/MMU.hh"
|
||||
#include "mem/page_table.hh"
|
||||
#include "sim/faults.hh"
|
||||
#include "sim/sim_object.hh"
|
||||
|
||||
using namespace std;
|
||||
using namespace TheISA;
|
||||
|
||||
PageTable::PageTable(const std::string &__name, uint64_t _pid, Addr _pageSize)
|
||||
: pageSize(_pageSize), offsetMask(mask(floorLog2(_pageSize))),
|
||||
pid(_pid), _name(__name)
|
||||
{
|
||||
assert(isPowerOf2(pageSize));
|
||||
pTableCache[0].vaddr = 0;
|
||||
pTableCache[1].vaddr = 0;
|
||||
pTableCache[2].vaddr = 0;
|
||||
}
|
||||
|
||||
PageTable::~PageTable()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
PageTable::map(Addr vaddr, Addr paddr, int64_t size, bool clobber)
|
||||
{
|
||||
// starting address must be page aligned
|
||||
assert(pageOffset(vaddr) == 0);
|
||||
|
||||
DPRINTF(MMU, "Allocating Page: %#x-%#x\n", vaddr, vaddr+ size);
|
||||
|
||||
for (; size > 0; size -= pageSize, vaddr += pageSize, paddr += pageSize) {
|
||||
if (!clobber && (pTable.find(vaddr) != pTable.end())) {
|
||||
// already mapped
|
||||
fatal("PageTable::allocate: address 0x%x already mapped", vaddr);
|
||||
}
|
||||
|
||||
pTable[vaddr] = TheISA::TlbEntry(pid, vaddr, paddr);
|
||||
updateCache(vaddr, pTable[vaddr]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PageTable::remap(Addr vaddr, int64_t size, Addr new_vaddr)
|
||||
{
|
||||
assert(pageOffset(vaddr) == 0);
|
||||
assert(pageOffset(new_vaddr) == 0);
|
||||
|
||||
DPRINTF(MMU, "moving pages from vaddr %08p to %08p, size = %d\n", vaddr,
|
||||
new_vaddr, size);
|
||||
|
||||
for (; size > 0; size -= pageSize, vaddr += pageSize, new_vaddr += pageSize) {
|
||||
assert(pTable.find(vaddr) != pTable.end());
|
||||
|
||||
pTable[new_vaddr] = pTable[vaddr];
|
||||
pTable.erase(vaddr);
|
||||
pTable[new_vaddr].updateVaddr(new_vaddr);
|
||||
updateCache(new_vaddr, pTable[new_vaddr]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PageTable::unmap(Addr vaddr, int64_t size)
|
||||
{
|
||||
assert(pageOffset(vaddr) == 0);
|
||||
|
||||
DPRINTF(MMU, "Unmapping page: %#x-%#x\n", vaddr, vaddr+ size);
|
||||
|
||||
for (; size > 0; size -= pageSize, vaddr += pageSize) {
|
||||
assert(pTable.find(vaddr) != pTable.end());
|
||||
|
||||
pTable.erase(vaddr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
PageTable::isUnmapped(Addr vaddr, int64_t size)
|
||||
{
|
||||
// starting address must be page aligned
|
||||
assert(pageOffset(vaddr) == 0);
|
||||
|
||||
for (; size > 0; size -= pageSize, vaddr += pageSize) {
|
||||
if (pTable.find(vaddr) != pTable.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PageTable::lookup(Addr vaddr, TheISA::TlbEntry &entry)
|
||||
{
|
||||
Addr page_addr = pageAlign(vaddr);
|
||||
|
||||
if (pTableCache[0].vaddr == page_addr) {
|
||||
entry = pTableCache[0].entry;
|
||||
return true;
|
||||
}
|
||||
if (pTableCache[1].vaddr == page_addr) {
|
||||
entry = pTableCache[1].entry;
|
||||
return true;
|
||||
}
|
||||
if (pTableCache[2].vaddr == page_addr) {
|
||||
entry = pTableCache[2].entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
PTableItr iter = pTable.find(page_addr);
|
||||
|
||||
if (iter == pTable.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
updateCache(page_addr, iter->second);
|
||||
entry = iter->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PageTable::translate(Addr vaddr, Addr &paddr)
|
||||
{
|
||||
TheISA::TlbEntry entry;
|
||||
if (!lookup(vaddr, entry)) {
|
||||
DPRINTF(MMU, "Couldn't Translate: %#x\n", vaddr);
|
||||
return false;
|
||||
}
|
||||
paddr = pageOffset(vaddr) + entry.pageStart();
|
||||
DPRINTF(MMU, "Translating: %#x->%#x\n", vaddr, paddr);
|
||||
return true;
|
||||
}
|
||||
|
||||
Fault
|
||||
PageTable::translate(RequestPtr req)
|
||||
{
|
||||
Addr paddr;
|
||||
assert(pageAlign(req->getVaddr() + req->getSize() - 1)
|
||||
== pageAlign(req->getVaddr()));
|
||||
if (!translate(req->getVaddr(), paddr)) {
|
||||
return Fault(new GenericPageTableFault(req->getVaddr()));
|
||||
}
|
||||
req->setPaddr(paddr);
|
||||
if ((paddr & (pageSize - 1)) + req->getSize() > pageSize) {
|
||||
panic("Request spans page boundaries!\n");
|
||||
return NoFault;
|
||||
}
|
||||
return NoFault;
|
||||
}
|
||||
|
||||
void
|
||||
PageTable::serialize(std::ostream &os)
|
||||
{
|
||||
paramOut(os, "ptable.size", pTable.size());
|
||||
|
||||
PTable::size_type count = 0;
|
||||
|
||||
PTableItr iter = pTable.begin();
|
||||
PTableItr end = pTable.end();
|
||||
while (iter != end) {
|
||||
os << "\n[" << csprintf("%s.Entry%d", name(), count) << "]\n";
|
||||
|
||||
paramOut(os, "vaddr", iter->first);
|
||||
iter->second.serialize(os);
|
||||
|
||||
++iter;
|
||||
++count;
|
||||
}
|
||||
assert(count == pTable.size());
|
||||
}
|
||||
|
||||
void
|
||||
PageTable::unserialize(Checkpoint *cp, const std::string §ion)
|
||||
{
|
||||
int i = 0, count;
|
||||
paramIn(cp, section, "ptable.size", count);
|
||||
|
||||
pTable.clear();
|
||||
|
||||
while (i < count) {
|
||||
TheISA::TlbEntry *entry;
|
||||
Addr vaddr;
|
||||
|
||||
paramIn(cp, csprintf("%s.Entry%d", name(), i), "vaddr", vaddr);
|
||||
entry = new TheISA::TlbEntry();
|
||||
entry->unserialize(cp, csprintf("%s.Entry%d", name(), i));
|
||||
pTable[vaddr] = *entry;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
147
simulators/gem5/src/mem/page_table.hh
Normal file
147
simulators/gem5/src/mem/page_table.hh
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2003 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Declaration of a non-full system Page Table.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_PAGE_TABLE_HH__
|
||||
#define __MEM_PAGE_TABLE_HH__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "arch/isa_traits.hh"
|
||||
#include "arch/tlb.hh"
|
||||
#include "base/hashmap.hh"
|
||||
#include "base/types.hh"
|
||||
#include "config/the_isa.hh"
|
||||
#include "mem/request.hh"
|
||||
#include "sim/serialize.hh"
|
||||
|
||||
/**
|
||||
* Page Table Declaration.
|
||||
*/
|
||||
class PageTable
|
||||
{
|
||||
protected:
|
||||
typedef m5::hash_map<Addr, TheISA::TlbEntry> PTable;
|
||||
typedef PTable::iterator PTableItr;
|
||||
PTable pTable;
|
||||
|
||||
struct cacheElement {
|
||||
Addr vaddr;
|
||||
TheISA::TlbEntry entry;
|
||||
};
|
||||
|
||||
struct cacheElement pTableCache[3];
|
||||
|
||||
const Addr pageSize;
|
||||
const Addr offsetMask;
|
||||
|
||||
const uint64_t pid;
|
||||
const std::string _name;
|
||||
|
||||
public:
|
||||
|
||||
PageTable(const std::string &__name, uint64_t _pid,
|
||||
Addr _pageSize = TheISA::VMPageSize);
|
||||
|
||||
~PageTable();
|
||||
|
||||
// for DPRINTF compatibility
|
||||
const std::string name() const { return _name; }
|
||||
|
||||
Addr pageAlign(Addr a) { return (a & ~offsetMask); }
|
||||
Addr pageOffset(Addr a) { return (a & offsetMask); }
|
||||
|
||||
void map(Addr vaddr, Addr paddr, int64_t size, bool clobber = false);
|
||||
void remap(Addr vaddr, int64_t size, Addr new_vaddr);
|
||||
void unmap(Addr vaddr, int64_t size);
|
||||
|
||||
/**
|
||||
* Check if any pages in a region are already allocated
|
||||
* @param vaddr The starting virtual address of the region.
|
||||
* @param size The length of the region.
|
||||
* @return True if no pages in the region are mapped.
|
||||
*/
|
||||
bool isUnmapped(Addr vaddr, int64_t size);
|
||||
|
||||
/**
|
||||
* Lookup function
|
||||
* @param vaddr The virtual address.
|
||||
* @return entry The page table entry corresponding to vaddr.
|
||||
*/
|
||||
bool lookup(Addr vaddr, TheISA::TlbEntry &entry);
|
||||
|
||||
/**
|
||||
* Translate function
|
||||
* @param vaddr The virtual address.
|
||||
* @param paddr Physical address from translation.
|
||||
* @return True if translation exists
|
||||
*/
|
||||
bool translate(Addr vaddr, Addr &paddr);
|
||||
|
||||
/**
|
||||
* Simplified translate function (just check for translation)
|
||||
* @param vaddr The virtual address.
|
||||
* @return True if translation exists
|
||||
*/
|
||||
bool translate(Addr vaddr) { Addr dummy; return translate(vaddr, dummy); }
|
||||
|
||||
/**
|
||||
* Perform a translation on the memory request, fills in paddr
|
||||
* field of req.
|
||||
* @param req The memory request.
|
||||
*/
|
||||
Fault translate(RequestPtr req);
|
||||
|
||||
/**
|
||||
* Update the page table cache.
|
||||
* @param vaddr virtual address (page aligned) to check
|
||||
* @param pte page table entry to return
|
||||
*/
|
||||
inline void updateCache(Addr vaddr, TheISA::TlbEntry entry)
|
||||
{
|
||||
pTableCache[2].entry = pTableCache[1].entry;
|
||||
pTableCache[2].vaddr = pTableCache[1].vaddr;
|
||||
pTableCache[1].entry = pTableCache[0].entry;
|
||||
pTableCache[1].vaddr = pTableCache[0].vaddr;
|
||||
pTableCache[0].entry = entry;
|
||||
pTableCache[0].vaddr = vaddr;
|
||||
}
|
||||
|
||||
|
||||
void serialize(std::ostream &os);
|
||||
|
||||
void unserialize(Checkpoint *cp, const std::string §ion);
|
||||
};
|
||||
|
||||
#endif // __MEM_PAGE_TABLE_HH__
|
||||
126
simulators/gem5/src/mem/physical.cc
Normal file
126
simulators/gem5/src/mem/physical.cc
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Andreas Hansson
|
||||
*/
|
||||
|
||||
#include "debug/BusAddrRanges.hh"
|
||||
#include "mem/physical.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
PhysicalMemory::PhysicalMemory(const vector<AbstractMemory*>& _memories) :
|
||||
size(0)
|
||||
{
|
||||
for (vector<AbstractMemory*>::const_iterator m = _memories.begin();
|
||||
m != _memories.end(); ++m) {
|
||||
// only add the memory if it is part of the global address map
|
||||
if ((*m)->isInAddrMap()) {
|
||||
memories.push_back(*m);
|
||||
|
||||
// calculate the total size once and for all
|
||||
size += (*m)->size();
|
||||
|
||||
// add the range to our interval tree and make sure it does not
|
||||
// intersect an existing range
|
||||
if (addrMap.insert((*m)->getAddrRange(), *m) == addrMap.end())
|
||||
fatal("Memory address range for %s is overlapping\n",
|
||||
(*m)->name());
|
||||
}
|
||||
DPRINTF(BusAddrRanges,
|
||||
"Skipping memory %s that is not in global address map\n",
|
||||
(*m)->name());
|
||||
}
|
||||
rangeCache.invalidate();
|
||||
}
|
||||
|
||||
bool
|
||||
PhysicalMemory::isMemAddr(Addr addr) const
|
||||
{
|
||||
// see if the address is within the last matched range
|
||||
if (addr != rangeCache) {
|
||||
// lookup in the interval tree
|
||||
range_map<Addr, AbstractMemory*>::const_iterator r =
|
||||
addrMap.find(addr);
|
||||
if (r == addrMap.end()) {
|
||||
// not in the cache, and not in the tree
|
||||
return false;
|
||||
}
|
||||
// the range is in the tree, update the cache
|
||||
rangeCache = r->first;
|
||||
}
|
||||
|
||||
assert(addrMap.find(addr) != addrMap.end());
|
||||
|
||||
// either matched the cache or found in the tree
|
||||
return true;
|
||||
}
|
||||
|
||||
AddrRangeList
|
||||
PhysicalMemory::getConfAddrRanges() const
|
||||
{
|
||||
// this could be done once in the constructor, but since it is unlikely to
|
||||
// be called more than once the iteration should not be a problem
|
||||
AddrRangeList ranges;
|
||||
for (vector<AbstractMemory*>::const_iterator m = memories.begin();
|
||||
m != memories.end(); ++m) {
|
||||
if ((*m)->isConfReported()) {
|
||||
ranges.push_back((*m)->getAddrRange());
|
||||
}
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
void
|
||||
PhysicalMemory::access(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isRequest());
|
||||
Addr addr = pkt->getAddr();
|
||||
range_map<Addr, AbstractMemory*>::const_iterator m = addrMap.find(addr);
|
||||
assert(m != addrMap.end());
|
||||
m->second->access(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
PhysicalMemory::functionalAccess(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isRequest());
|
||||
Addr addr = pkt->getAddr();
|
||||
range_map<Addr, AbstractMemory*>::const_iterator m = addrMap.find(addr);
|
||||
assert(m != addrMap.end());
|
||||
m->second->functionalAccess(pkt);
|
||||
}
|
||||
121
simulators/gem5/src/mem/physical.hh
Normal file
121
simulators/gem5/src/mem/physical.hh
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Andreas Hansson
|
||||
*/
|
||||
|
||||
#ifndef __PHYSICAL_MEMORY_HH__
|
||||
#define __PHYSICAL_MEMORY_HH__
|
||||
|
||||
#include "base/range_map.hh"
|
||||
#include "mem/abstract_mem.hh"
|
||||
#include "mem/packet.hh"
|
||||
|
||||
/**
|
||||
* The physical memory encapsulates all memories in the system and
|
||||
* provides basic functionality for accessing those memories without
|
||||
* going through the memory system and interconnect.
|
||||
*/
|
||||
class PhysicalMemory
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
// Global address map
|
||||
range_map<Addr, AbstractMemory* > addrMap;
|
||||
|
||||
// a mutable cache for the last range that matched an address
|
||||
mutable Range<Addr> rangeCache;
|
||||
|
||||
// All address-mapped memories
|
||||
std::vector<AbstractMemory*> memories;
|
||||
|
||||
// The total memory size
|
||||
uint64_t size;
|
||||
|
||||
// Prevent copying
|
||||
PhysicalMemory(const PhysicalMemory&);
|
||||
|
||||
// Prevent assignment
|
||||
PhysicalMemory& operator=(const PhysicalMemory&);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create a physical memory object, wrapping a number of memories.
|
||||
*/
|
||||
PhysicalMemory(const std::vector<AbstractMemory*>& _memories);
|
||||
|
||||
/**
|
||||
* Nothing to destruct.
|
||||
*/
|
||||
~PhysicalMemory() { }
|
||||
|
||||
/**
|
||||
* Check if a physical address is within a range of a memory that
|
||||
* is part of the global address map.
|
||||
*
|
||||
* @param addr A physical address
|
||||
* @return Whether the address corresponds to a memory
|
||||
*/
|
||||
bool isMemAddr(Addr addr) const;
|
||||
|
||||
/**
|
||||
* Get the memory ranges for all memories that are to be reported
|
||||
* to the configuration table.
|
||||
*
|
||||
* @return All configuration table memory ranges
|
||||
*/
|
||||
AddrRangeList getConfAddrRanges() const;
|
||||
|
||||
/**
|
||||
* Get the total physical memory size.
|
||||
*
|
||||
* @return The sum of all memory sizes
|
||||
*/
|
||||
uint64_t totalSize() const { return size; }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void access(PacketPtr pkt);
|
||||
void functionalAccess(PacketPtr pkt);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //__PHYSICAL_MEMORY_HH__
|
||||
213
simulators/gem5/src/mem/port.cc
Normal file
213
simulators/gem5/src/mem/port.cc
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Steve Reinhardt
|
||||
* Andreas Hansson
|
||||
* William Wang
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Port object definitions.
|
||||
*/
|
||||
#include "base/trace.hh"
|
||||
#include "mem/mem_object.hh"
|
||||
#include "mem/port.hh"
|
||||
|
||||
Port::Port(const std::string &_name, MemObject& _owner, PortID _id)
|
||||
: portName(_name), id(_id), peer(NULL), owner(_owner)
|
||||
{
|
||||
}
|
||||
|
||||
Port::~Port()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Master port
|
||||
*/
|
||||
MasterPort::MasterPort(const std::string& name, MemObject* owner, PortID _id)
|
||||
: Port(name, *owner, _id), _slavePort(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
MasterPort::~MasterPort()
|
||||
{
|
||||
}
|
||||
|
||||
SlavePort&
|
||||
MasterPort::getSlavePort() const
|
||||
{
|
||||
if(_slavePort == NULL)
|
||||
panic("Cannot getSlavePort on master port %s that is not connected\n",
|
||||
name());
|
||||
|
||||
return *_slavePort;
|
||||
}
|
||||
|
||||
void
|
||||
MasterPort::bind(SlavePort& slave_port)
|
||||
{
|
||||
// master port keeps track of the slave port
|
||||
_slavePort = &slave_port;
|
||||
peer = &slave_port;
|
||||
|
||||
// slave port also keeps track of master port
|
||||
_slavePort->bind(*this);
|
||||
}
|
||||
|
||||
bool
|
||||
MasterPort::isConnected() const
|
||||
{
|
||||
return _slavePort != NULL;
|
||||
}
|
||||
|
||||
unsigned
|
||||
MasterPort::peerBlockSize() const
|
||||
{
|
||||
return _slavePort->deviceBlockSize();
|
||||
}
|
||||
|
||||
Tick
|
||||
MasterPort::sendAtomic(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isRequest());
|
||||
return _slavePort->recvAtomic(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
MasterPort::sendFunctional(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isRequest());
|
||||
return _slavePort->recvFunctional(pkt);
|
||||
}
|
||||
|
||||
bool
|
||||
MasterPort::sendTimingReq(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isRequest());
|
||||
return _slavePort->recvTimingReq(pkt);
|
||||
}
|
||||
|
||||
bool
|
||||
MasterPort::sendTimingSnoopResp(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isResponse());
|
||||
return _slavePort->recvTimingSnoopResp(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
MasterPort::printAddr(Addr a)
|
||||
{
|
||||
Request req(a, 1, 0, Request::funcMasterId);
|
||||
Packet pkt(&req, MemCmd::PrintReq);
|
||||
Packet::PrintReqState prs(std::cerr);
|
||||
pkt.senderState = &prs;
|
||||
|
||||
sendFunctional(&pkt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Slave port
|
||||
*/
|
||||
SlavePort::SlavePort(const std::string& name, MemObject* owner, PortID id)
|
||||
: Port(name, *owner, id), _masterPort(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
SlavePort::~SlavePort()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SlavePort::bind(MasterPort& master_port)
|
||||
{
|
||||
_masterPort = &master_port;
|
||||
peer = &master_port;
|
||||
}
|
||||
|
||||
MasterPort&
|
||||
SlavePort::getMasterPort() const
|
||||
{
|
||||
if (_masterPort == NULL)
|
||||
panic("Cannot getMasterPort on slave port %s that is not connected\n",
|
||||
name());
|
||||
|
||||
return *_masterPort;
|
||||
}
|
||||
|
||||
unsigned
|
||||
SlavePort::peerBlockSize() const
|
||||
{
|
||||
return _masterPort->deviceBlockSize();
|
||||
}
|
||||
|
||||
bool
|
||||
SlavePort::isConnected() const
|
||||
{
|
||||
return _masterPort != NULL;
|
||||
}
|
||||
|
||||
Tick
|
||||
SlavePort::sendAtomicSnoop(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isRequest());
|
||||
return _masterPort->recvAtomicSnoop(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
SlavePort::sendFunctionalSnoop(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isRequest());
|
||||
return _masterPort->recvFunctionalSnoop(pkt);
|
||||
}
|
||||
|
||||
bool
|
||||
SlavePort::sendTimingResp(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isResponse());
|
||||
return _masterPort->recvTimingResp(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
SlavePort::sendTimingSnoopReq(PacketPtr pkt)
|
||||
{
|
||||
assert(pkt->isRequest());
|
||||
_masterPort->recvTimingSnoopReq(pkt);
|
||||
}
|
||||
401
simulators/gem5/src/mem/port.hh
Normal file
401
simulators/gem5/src/mem/port.hh
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Ron Dreslinski
|
||||
* Andreas Hansson
|
||||
* William Wang
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Port Object Declaration.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_PORT_HH__
|
||||
#define __MEM_PORT_HH__
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "base/range.hh"
|
||||
#include "mem/packet.hh"
|
||||
|
||||
/**
|
||||
* This typedef is used to clean up getAddrRanges(). It's declared
|
||||
* outside the Port object since it's also used by some mem objects.
|
||||
* Eventually we should move this typedef to wherever Addr is
|
||||
* defined.
|
||||
*/
|
||||
|
||||
typedef std::list<Range<Addr> > AddrRangeList;
|
||||
typedef std::list<Range<Addr> >::iterator AddrRangeIter;
|
||||
|
||||
class MemObject;
|
||||
|
||||
/**
|
||||
* Ports are used to interface memory objects to each other. A port is
|
||||
* either a master or a slave and the connected peer is always of the
|
||||
* opposite role.
|
||||
*
|
||||
* Each port has a name and an owner, and enables three basic types of
|
||||
* accesses to the peer port: functional, atomic and timing.
|
||||
*/
|
||||
class Port
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
/** Descriptive name (for DPRINTF output) */
|
||||
std::string portName;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* A numeric identifier to distinguish ports in a vector, and set
|
||||
* to InvalidPortID in case this port is not part of a vector.
|
||||
*/
|
||||
const PortID id;
|
||||
|
||||
/** A pointer to the peer port. */
|
||||
Port* peer;
|
||||
|
||||
/** A reference to the MemObject that owns this port. */
|
||||
MemObject& owner;
|
||||
|
||||
/**
|
||||
* Abstract base class for ports
|
||||
*
|
||||
* @param _name Port name including the owners name
|
||||
* @param _owner The MemObject that is the structural owner of this port
|
||||
* @param _id A port identifier for vector ports
|
||||
*/
|
||||
Port(const std::string& _name, MemObject& _owner, PortID _id);
|
||||
|
||||
/**
|
||||
* Virtual destructor due to inheritance.
|
||||
*/
|
||||
virtual ~Port();
|
||||
|
||||
public:
|
||||
|
||||
/** Return port name (for DPRINTF). */
|
||||
const std::string name() const { return portName; }
|
||||
|
||||
/** Get the port id. */
|
||||
PortID getId() const { return id; }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Called by a peer port if sendTimingReq, sendTimingResp or
|
||||
* sendTimingSnoopResp was unsuccesful, and had to wait.
|
||||
*/
|
||||
virtual void recvRetry() = 0;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Send a retry to a peer port that previously attempted a
|
||||
* sendTimingReq, sendTimingResp or sendTimingSnoopResp which was
|
||||
* unsuccessful.
|
||||
*/
|
||||
void sendRetry() { return peer->recvRetry(); }
|
||||
|
||||
};
|
||||
|
||||
/** Forward declaration */
|
||||
class SlavePort;
|
||||
|
||||
/**
|
||||
* A MasterPort is a specialisation of a port. In addition to the
|
||||
* basic functionality of sending packets to its slave peer, it also
|
||||
* has functions specific to a master, e.g. to receive range changes
|
||||
* or determine if the port is snooping or not.
|
||||
*/
|
||||
class MasterPort : public Port
|
||||
{
|
||||
|
||||
friend class SlavePort;
|
||||
|
||||
private:
|
||||
|
||||
SlavePort* _slavePort;
|
||||
|
||||
public:
|
||||
|
||||
MasterPort(const std::string& name, MemObject* owner,
|
||||
PortID id = InvalidPortID);
|
||||
virtual ~MasterPort();
|
||||
|
||||
void bind(SlavePort& slave_port);
|
||||
SlavePort& getSlavePort() const;
|
||||
bool isConnected() const;
|
||||
|
||||
/**
|
||||
* Send an atomic request packet, where the data is moved and the
|
||||
* state is updated in zero time, without interleaving with other
|
||||
* memory accesses.
|
||||
*
|
||||
* @param pkt Packet to send.
|
||||
*
|
||||
* @return Estimated latency of access.
|
||||
*/
|
||||
Tick sendAtomic(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Send a functional request packet, where the data is instantly
|
||||
* updated everywhere in the memory system, without affecting the
|
||||
* current state of any block or moving the block.
|
||||
*
|
||||
* @param pkt Packet to send.
|
||||
*/
|
||||
void sendFunctional(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Attempt to send a timing request to the slave port by calling
|
||||
* its corresponding receive function. If the send does not
|
||||
* succeed, as indicated by the return value, then the sender must
|
||||
* wait for a recvRetry at which point it can re-issue a
|
||||
* sendTimingReq.
|
||||
*
|
||||
* @param pkt Packet to send.
|
||||
*
|
||||
* @return If the send was succesful or not.
|
||||
*/
|
||||
bool sendTimingReq(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Attempt to send a timing snoop response packet to the slave
|
||||
* port by calling its corresponding receive function. If the send
|
||||
* does not succeed, as indicated by the return value, then the
|
||||
* sender must wait for a recvRetry at which point it can re-issue
|
||||
* a sendTimingSnoopResp.
|
||||
*
|
||||
* @param pkt Packet to send.
|
||||
*/
|
||||
bool sendTimingSnoopResp(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Determine if this master port is snooping or not. The default
|
||||
* implementation returns false and thus tells the neighbour we
|
||||
* are not snooping. Any master port that wants to receive snoop
|
||||
* requests (e.g. a cache connected to a bus) has to override this
|
||||
* function.
|
||||
*
|
||||
* @return true if the port should be considered a snooper
|
||||
*/
|
||||
virtual bool isSnooping() const { return false; }
|
||||
|
||||
/**
|
||||
* Called by a peer port in order to determine the block size of
|
||||
* the owner of this port.
|
||||
*/
|
||||
virtual unsigned deviceBlockSize() const { return 0; }
|
||||
|
||||
/** Called by the associated device if it wishes to find out the blocksize
|
||||
of the device on attached to the peer port.
|
||||
*/
|
||||
unsigned peerBlockSize() const;
|
||||
|
||||
/** Inject a PrintReq for the given address to print the state of
|
||||
* that address throughout the memory system. For debugging.
|
||||
*/
|
||||
void printAddr(Addr a);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Receive an atomic snoop request packet from the slave port.
|
||||
*/
|
||||
virtual Tick recvAtomicSnoop(PacketPtr pkt)
|
||||
{
|
||||
panic("%s was not expecting an atomic snoop request\n", name());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a functional snoop request packet from the slave port.
|
||||
*/
|
||||
virtual void recvFunctionalSnoop(PacketPtr pkt)
|
||||
{
|
||||
panic("%s was not expecting a functional snoop request\n", name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a timing response from the slave port.
|
||||
*/
|
||||
virtual bool recvTimingResp(PacketPtr pkt) = 0;
|
||||
|
||||
/**
|
||||
* Receive a timing snoop request from the slave port.
|
||||
*/
|
||||
virtual void recvTimingSnoopReq(PacketPtr pkt)
|
||||
{
|
||||
panic("%s was not expecting a timing snoop request\n", name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to receive an address range change from the peer slave
|
||||
* port. the default implementation ignored the change and does
|
||||
* nothing. Override this function in a derived class if the owner
|
||||
* needs to be aware of he laesddress ranges, e.g. in an
|
||||
* interconnect component like a bus.
|
||||
*/
|
||||
virtual void recvRangeChange() { }
|
||||
};
|
||||
|
||||
/**
|
||||
* A SlavePort is a specialisation of a port. In addition to the
|
||||
* basic functionality of sending packets to its master peer, it also
|
||||
* has functions specific to a slave, e.g. to send range changes
|
||||
* and get the address ranges that the port responds to.
|
||||
*/
|
||||
class SlavePort : public Port
|
||||
{
|
||||
|
||||
friend class MasterPort;
|
||||
|
||||
private:
|
||||
|
||||
MasterPort* _masterPort;
|
||||
|
||||
public:
|
||||
|
||||
SlavePort(const std::string& name, MemObject* owner,
|
||||
PortID id = InvalidPortID);
|
||||
virtual ~SlavePort();
|
||||
|
||||
void bind(MasterPort& master_port);
|
||||
MasterPort& getMasterPort() const;
|
||||
bool isConnected() const;
|
||||
|
||||
/**
|
||||
* Send an atomic snoop request packet, where the data is moved
|
||||
* and the state is updated in zero time, without interleaving
|
||||
* with other memory accesses.
|
||||
*
|
||||
* @param pkt Snoop packet to send.
|
||||
*
|
||||
* @return Estimated latency of access.
|
||||
*/
|
||||
Tick sendAtomicSnoop(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Send a functional snoop request packet, where the data is
|
||||
* instantly updated everywhere in the memory system, without
|
||||
* affecting the current state of any block or moving the block.
|
||||
*
|
||||
* @param pkt Snoop packet to send.
|
||||
*/
|
||||
void sendFunctionalSnoop(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Attempt to send a timing response to the master port by calling
|
||||
* its corresponding receive function. If the send does not
|
||||
* succeed, as indicated by the return value, then the sender must
|
||||
* wait for a recvRetry at which point it can re-issue a
|
||||
* sendTimingResp.
|
||||
*
|
||||
* @param pkt Packet to send.
|
||||
*
|
||||
* @return If the send was succesful or not.
|
||||
*/
|
||||
bool sendTimingResp(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Attempt to send a timing snoop request packet to the master port
|
||||
* by calling its corresponding receive function. Snoop requests
|
||||
* always succeed and hence no return value is needed.
|
||||
*
|
||||
* @param pkt Packet to send.
|
||||
*/
|
||||
void sendTimingSnoopReq(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Called by a peer port in order to determine the block size of
|
||||
* the owner of this port.
|
||||
*/
|
||||
virtual unsigned deviceBlockSize() const { return 0; }
|
||||
|
||||
/** Called by the associated device if it wishes to find out the blocksize
|
||||
of the device on attached to the peer port.
|
||||
*/
|
||||
unsigned peerBlockSize() const;
|
||||
|
||||
/**
|
||||
* Called by the owner to send a range change
|
||||
*/
|
||||
void sendRangeChange() const { _masterPort->recvRangeChange(); }
|
||||
|
||||
/**
|
||||
* Get a list of the non-overlapping address ranges the owner is
|
||||
* responsible for. All slave ports must override this function
|
||||
* and return a populated list with at least one item.
|
||||
*
|
||||
* @return a list of ranges responded to
|
||||
*/
|
||||
virtual AddrRangeList getAddrRanges() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Receive an atomic request packet from the master port.
|
||||
*/
|
||||
virtual Tick recvAtomic(PacketPtr pkt) = 0;
|
||||
|
||||
/**
|
||||
* Receive a functional request packet from the master port.
|
||||
*/
|
||||
virtual void recvFunctional(PacketPtr pkt) = 0;
|
||||
|
||||
/**
|
||||
* Receive a timing request from the master port.
|
||||
*/
|
||||
virtual bool recvTimingReq(PacketPtr pkt) = 0;
|
||||
|
||||
/**
|
||||
* Receive a timing snoop response from the master port.
|
||||
*/
|
||||
virtual bool recvTimingSnoopResp(PacketPtr pkt)
|
||||
{
|
||||
panic("%s was not expecting a timing snoop response\n", name());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //__MEM_PORT_HH__
|
||||
68
simulators/gem5/src/mem/port_proxy.cc
Normal file
68
simulators/gem5/src/mem/port_proxy.cc
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Andreas Hansson
|
||||
*/
|
||||
|
||||
#include "base/chunk_generator.hh"
|
||||
#include "mem/port_proxy.hh"
|
||||
|
||||
void
|
||||
PortProxy::blobHelper(Addr addr, uint8_t *p, int size, MemCmd cmd) const
|
||||
{
|
||||
Request req;
|
||||
|
||||
for (ChunkGenerator gen(addr, size, _port.peerBlockSize());
|
||||
!gen.done(); gen.next()) {
|
||||
req.setPhys(gen.addr(), gen.size(), 0, Request::funcMasterId);
|
||||
Packet pkt(&req, cmd);
|
||||
pkt.dataStatic(p);
|
||||
_port.sendFunctional(&pkt);
|
||||
p += gen.size();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PortProxy::memsetBlob(Addr addr, uint8_t v, int size) const
|
||||
{
|
||||
// quick and dirty...
|
||||
uint8_t *buf = new uint8_t[size];
|
||||
|
||||
std::memset(buf, v, size);
|
||||
blobHelper(addr, buf, size, MemCmd::WriteReq);
|
||||
|
||||
delete [] buf;
|
||||
}
|
||||
179
simulators/gem5/src/mem/port_proxy.hh
Normal file
179
simulators/gem5/src/mem/port_proxy.hh
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 ARM Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
* not be construed as granting a license to any other intellectual
|
||||
* property including but not limited to intellectual property relating
|
||||
* to a hardware implementation of the functionality of the software
|
||||
* licensed hereunder. You may use the software subject to the license
|
||||
* terms below provided that you ensure that this notice is replicated
|
||||
* unmodified and in its entirety in all distributions of the software,
|
||||
* modified or unmodified, in source code or in binary form.
|
||||
*
|
||||
* 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: Andreas Hansson
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* PortProxy Object Declaration.
|
||||
*
|
||||
* Port proxies are used when non-structural entities need access to
|
||||
* the memory system (or structural entities that want to peak into
|
||||
* the memory system without making a real memory access).
|
||||
*
|
||||
* Proxy objects replace the previous FunctionalPort, TranslatingPort
|
||||
* and VirtualPort objects, which provided the same functionality as
|
||||
* the proxies, but were instances of ports not corresponding to real
|
||||
* structural ports of the simulated system. Via the port proxies all
|
||||
* the accesses go through an actual port (either the system port,
|
||||
* e.g. for processes or initialisation, or a the data port of the
|
||||
* CPU, e.g. for threads) and thus are transparent to a potentially
|
||||
* distributed memory and automatically adhere to the memory map of
|
||||
* the system.
|
||||
*/
|
||||
|
||||
#ifndef __MEM_PORT_PROXY_HH__
|
||||
#define __MEM_PORT_PROXY_HH__
|
||||
|
||||
#include "config/the_isa.hh"
|
||||
#if THE_ISA != NO_ISA
|
||||
#include "arch/isa_traits.hh"
|
||||
#endif
|
||||
|
||||
#include "mem/port.hh"
|
||||
#include "sim/byteswap.hh"
|
||||
|
||||
/**
|
||||
* This object is a proxy for a structural port, to be used for debug
|
||||
* accesses.
|
||||
*
|
||||
* This proxy object is used when non structural entities
|
||||
* (e.g. thread contexts, object file loaders) need access to the
|
||||
* memory system. It calls the corresponding functions on the underlying
|
||||
* structural port, and provides templatized convenience access functions.
|
||||
*
|
||||
* The addresses are interpreted as physical addresses.
|
||||
*
|
||||
* @sa SETranslatingProxy
|
||||
* @sa FSTranslatingProxy
|
||||
*/
|
||||
class PortProxy
|
||||
{
|
||||
private:
|
||||
|
||||
/** The actual physical port used by this proxy. */
|
||||
MasterPort &_port;
|
||||
|
||||
void blobHelper(Addr addr, uint8_t *p, int size, MemCmd cmd) const;
|
||||
|
||||
public:
|
||||
PortProxy(MasterPort &port) : _port(port) { }
|
||||
virtual ~PortProxy() { }
|
||||
|
||||
/**
|
||||
* Read size bytes memory at address and store in p.
|
||||
*/
|
||||
virtual void readBlob(Addr addr, uint8_t* p, int size) const
|
||||
{ blobHelper(addr, p, size, MemCmd::ReadReq); }
|
||||
|
||||
/**
|
||||
* Write size bytes from p to address.
|
||||
*/
|
||||
virtual void writeBlob(Addr addr, uint8_t* p, int size) const
|
||||
{ blobHelper(addr, p, size, MemCmd::WriteReq); }
|
||||
|
||||
/**
|
||||
* Fill size bytes starting at addr with byte value val.
|
||||
*/
|
||||
virtual void memsetBlob(Addr addr, uint8_t v, int size) const;
|
||||
|
||||
/**
|
||||
* Read sizeof(T) bytes from address and return as object T.
|
||||
*/
|
||||
template <typename T>
|
||||
T read(Addr address) const;
|
||||
|
||||
/**
|
||||
* Write object T to address. Writes sizeof(T) bytes.
|
||||
*/
|
||||
template <typename T>
|
||||
void write(Addr address, T data) const;
|
||||
|
||||
#if THE_ISA != NO_ISA
|
||||
/**
|
||||
* Read sizeof(T) bytes from address and return as object T.
|
||||
* Performs Guest to Host endianness transform.
|
||||
*/
|
||||
template <typename T>
|
||||
T readGtoH(Addr address) const;
|
||||
|
||||
/**
|
||||
* Write object T to address. Writes sizeof(T) bytes.
|
||||
* Performs Host to Guest endianness transform.
|
||||
*/
|
||||
template <typename T>
|
||||
void writeHtoG(Addr address, T data) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
T
|
||||
PortProxy::read(Addr address) const
|
||||
{
|
||||
T data;
|
||||
readBlob(address, (uint8_t*)&data, sizeof(T));
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
PortProxy::write(Addr address, T data) const
|
||||
{
|
||||
writeBlob(address, (uint8_t*)&data, sizeof(T));
|
||||
}
|
||||
|
||||
#if THE_ISA != NO_ISA
|
||||
template <typename T>
|
||||
T
|
||||
PortProxy::readGtoH(Addr address) const
|
||||
{
|
||||
T data;
|
||||
readBlob(address, (uint8_t*)&data, sizeof(T));
|
||||
return TheISA::gtoh(data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
PortProxy::writeHtoG(Addr address, T data) const
|
||||
{
|
||||
data = TheISA::htog(data);
|
||||
writeBlob(address, (uint8_t*)&data, sizeof(T));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __MEM_PORT_PROXY_HH__
|
||||
966
simulators/gem5/src/mem/protocol/MESI_CMP_directory-L1cache.sm
Normal file
966
simulators/gem5/src/mem/protocol/MESI_CMP_directory-L1cache.sm
Normal file
@ -0,0 +1,966 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999-2005 Mark D. Hill and David A. Wood
|
||||
* 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.
|
||||
*/
|
||||
|
||||
machine(L1Cache, "MESI Directory L1 Cache CMP")
|
||||
: Sequencer * sequencer,
|
||||
CacheMemory * L1IcacheMemory,
|
||||
CacheMemory * L1DcacheMemory,
|
||||
int l2_select_num_bits,
|
||||
int l1_request_latency = 2,
|
||||
int l1_response_latency = 2,
|
||||
int to_l2_latency = 1,
|
||||
bool send_evictions
|
||||
{
|
||||
// NODE L1 CACHE
|
||||
// From this node's L1 cache TO the network
|
||||
// a local L1 -> this L2 bank, currently ordered with directory forwarded requests
|
||||
MessageBuffer requestFromL1Cache, network="To", virtual_network="0", ordered="false", vnet_type="request";
|
||||
// a local L1 -> this L2 bank
|
||||
MessageBuffer responseFromL1Cache, network="To", virtual_network="1", ordered="false", vnet_type="response";
|
||||
MessageBuffer unblockFromL1Cache, network="To", virtual_network="2", ordered="false", vnet_type="unblock";
|
||||
|
||||
|
||||
// To this node's L1 cache FROM the network
|
||||
// a L2 bank -> this L1
|
||||
MessageBuffer requestToL1Cache, network="From", virtual_network="0", ordered="false", vnet_type="request";
|
||||
// a L2 bank -> this L1
|
||||
MessageBuffer responseToL1Cache, network="From", virtual_network="1", ordered="false", vnet_type="response";
|
||||
|
||||
// STATES
|
||||
state_declaration(State, desc="Cache states", default="L1Cache_State_I") {
|
||||
// Base states
|
||||
NP, AccessPermission:Invalid, desc="Not present in either cache";
|
||||
I, AccessPermission:Invalid, desc="a L1 cache entry Idle";
|
||||
S, AccessPermission:Read_Only, desc="a L1 cache entry Shared";
|
||||
E, AccessPermission:Read_Only, desc="a L1 cache entry Exclusive";
|
||||
M, AccessPermission:Read_Write, desc="a L1 cache entry Modified", format="!b";
|
||||
|
||||
// Transient States
|
||||
IS, AccessPermission:Busy, desc="L1 idle, issued GETS, have not seen response yet";
|
||||
IM, AccessPermission:Busy, desc="L1 idle, issued GETX, have not seen response yet";
|
||||
SM, AccessPermission:Read_Only, desc="L1 idle, issued GETX, have not seen response yet";
|
||||
IS_I, AccessPermission:Busy, desc="L1 idle, issued GETS, saw Inv before data because directory doesn't block on GETS hit";
|
||||
|
||||
M_I, AccessPermission:Busy, desc="L1 replacing, waiting for ACK";
|
||||
SINK_WB_ACK, AccessPermission:Busy, desc="This is to sink WB_Acks from L2";
|
||||
|
||||
}
|
||||
|
||||
// EVENTS
|
||||
enumeration(Event, desc="Cache events") {
|
||||
// L1 events
|
||||
Load, desc="Load request from the home processor";
|
||||
Ifetch, desc="I-fetch request from the home processor";
|
||||
Store, desc="Store request from the home processor";
|
||||
|
||||
Inv, desc="Invalidate request from L2 bank";
|
||||
|
||||
// internal generated request
|
||||
L1_Replacement, desc="L1 Replacement", format="!r";
|
||||
|
||||
// other requests
|
||||
Fwd_GETX, desc="GETX from other processor";
|
||||
Fwd_GETS, desc="GETS from other processor";
|
||||
Fwd_GET_INSTR, desc="GET_INSTR from other processor";
|
||||
|
||||
Data, desc="Data for processor";
|
||||
Data_Exclusive, desc="Data for processor";
|
||||
DataS_fromL1, desc="data for GETS request, need to unblock directory";
|
||||
Data_all_Acks, desc="Data for processor, all acks";
|
||||
|
||||
Ack, desc="Ack for processor";
|
||||
Ack_all, desc="Last ack for processor";
|
||||
|
||||
WB_Ack, desc="Ack for replacement";
|
||||
}
|
||||
|
||||
// TYPES
|
||||
|
||||
// CacheEntry
|
||||
structure(Entry, desc="...", interface="AbstractCacheEntry" ) {
|
||||
State CacheState, desc="cache state";
|
||||
DataBlock DataBlk, desc="data for the block";
|
||||
bool Dirty, default="false", desc="data is dirty";
|
||||
}
|
||||
|
||||
// TBE fields
|
||||
structure(TBE, desc="...") {
|
||||
Address Address, desc="Physical address for this TBE";
|
||||
State TBEState, desc="Transient state";
|
||||
DataBlock DataBlk, desc="Buffer for the data block";
|
||||
bool Dirty, default="false", desc="data is dirty";
|
||||
bool isPrefetch, desc="Set if this was caused by a prefetch";
|
||||
int pendingAcks, default="0", desc="number of pending acks";
|
||||
}
|
||||
|
||||
structure(TBETable, external="yes") {
|
||||
TBE lookup(Address);
|
||||
void allocate(Address);
|
||||
void deallocate(Address);
|
||||
bool isPresent(Address);
|
||||
}
|
||||
|
||||
TBETable L1_TBEs, template_hack="<L1Cache_TBE>";
|
||||
|
||||
MessageBuffer mandatoryQueue, ordered="false";
|
||||
|
||||
int l2_select_low_bit, default="RubySystem::getBlockSizeBits()";
|
||||
|
||||
void set_cache_entry(AbstractCacheEntry a);
|
||||
void unset_cache_entry();
|
||||
void set_tbe(TBE a);
|
||||
void unset_tbe();
|
||||
void wakeUpBuffers(Address a);
|
||||
|
||||
// inclusive cache returns L1 entries only
|
||||
Entry getCacheEntry(Address addr), return_by_pointer="yes" {
|
||||
Entry L1Dcache_entry := static_cast(Entry, "pointer", L1DcacheMemory[addr]);
|
||||
if(is_valid(L1Dcache_entry)) {
|
||||
return L1Dcache_entry;
|
||||
}
|
||||
|
||||
Entry L1Icache_entry := static_cast(Entry, "pointer", L1IcacheMemory[addr]);
|
||||
return L1Icache_entry;
|
||||
}
|
||||
|
||||
Entry getL1DCacheEntry(Address addr), return_by_pointer="yes" {
|
||||
Entry L1Dcache_entry := static_cast(Entry, "pointer", L1DcacheMemory[addr]);
|
||||
return L1Dcache_entry;
|
||||
}
|
||||
|
||||
Entry getL1ICacheEntry(Address addr), return_by_pointer="yes" {
|
||||
Entry L1Icache_entry := static_cast(Entry, "pointer", L1IcacheMemory[addr]);
|
||||
return L1Icache_entry;
|
||||
}
|
||||
|
||||
State getState(TBE tbe, Entry cache_entry, Address addr) {
|
||||
assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false);
|
||||
|
||||
if(is_valid(tbe)) {
|
||||
return tbe.TBEState;
|
||||
} else if (is_valid(cache_entry)) {
|
||||
return cache_entry.CacheState;
|
||||
}
|
||||
return State:NP;
|
||||
}
|
||||
|
||||
void setState(TBE tbe, Entry cache_entry, Address addr, State state) {
|
||||
assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false);
|
||||
|
||||
// MUST CHANGE
|
||||
if(is_valid(tbe)) {
|
||||
tbe.TBEState := state;
|
||||
}
|
||||
|
||||
if (is_valid(cache_entry)) {
|
||||
cache_entry.CacheState := state;
|
||||
}
|
||||
}
|
||||
|
||||
AccessPermission getAccessPermission(Address addr) {
|
||||
TBE tbe := L1_TBEs[addr];
|
||||
if(is_valid(tbe)) {
|
||||
DPRINTF(RubySlicc, "%s\n", L1Cache_State_to_permission(tbe.TBEState));
|
||||
return L1Cache_State_to_permission(tbe.TBEState);
|
||||
}
|
||||
|
||||
Entry cache_entry := getCacheEntry(addr);
|
||||
if(is_valid(cache_entry)) {
|
||||
DPRINTF(RubySlicc, "%s\n", L1Cache_State_to_permission(cache_entry.CacheState));
|
||||
return L1Cache_State_to_permission(cache_entry.CacheState);
|
||||
}
|
||||
|
||||
DPRINTF(RubySlicc, "%s\n", AccessPermission:NotPresent);
|
||||
return AccessPermission:NotPresent;
|
||||
}
|
||||
|
||||
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
|
||||
return getCacheEntry(addr).DataBlk;
|
||||
}
|
||||
|
||||
void setAccessPermission(Entry cache_entry, Address addr, State state) {
|
||||
if (is_valid(cache_entry)) {
|
||||
cache_entry.changePermission(L1Cache_State_to_permission(state));
|
||||
}
|
||||
}
|
||||
|
||||
Event mandatory_request_type_to_event(RubyRequestType type) {
|
||||
if (type == RubyRequestType:LD) {
|
||||
return Event:Load;
|
||||
} else if (type == RubyRequestType:IFETCH) {
|
||||
return Event:Ifetch;
|
||||
} else if ((type == RubyRequestType:ST) || (type == RubyRequestType:ATOMIC)) {
|
||||
return Event:Store;
|
||||
} else {
|
||||
error("Invalid RubyRequestType");
|
||||
}
|
||||
}
|
||||
|
||||
int getPendingAcks(TBE tbe) {
|
||||
return tbe.pendingAcks;
|
||||
}
|
||||
|
||||
out_port(requestIntraChipL1Network_out, RequestMsg, requestFromL1Cache);
|
||||
out_port(responseIntraChipL1Network_out, ResponseMsg, responseFromL1Cache);
|
||||
out_port(unblockNetwork_out, ResponseMsg, unblockFromL1Cache);
|
||||
|
||||
// Response IntraChip L1 Network - response msg to this L1 cache
|
||||
in_port(responseIntraChipL1Network_in, ResponseMsg, responseToL1Cache, rank = 2) {
|
||||
if (responseIntraChipL1Network_in.isReady()) {
|
||||
peek(responseIntraChipL1Network_in, ResponseMsg, block_on="Address") {
|
||||
assert(in_msg.Destination.isElement(machineID));
|
||||
|
||||
Entry cache_entry := getCacheEntry(in_msg.Address);
|
||||
TBE tbe := L1_TBEs[in_msg.Address];
|
||||
|
||||
if(in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE) {
|
||||
trigger(Event:Data_Exclusive, in_msg.Address, cache_entry, tbe);
|
||||
} else if(in_msg.Type == CoherenceResponseType:DATA) {
|
||||
if ((getState(tbe, cache_entry, in_msg.Address) == State:IS ||
|
||||
getState(tbe, cache_entry, in_msg.Address) == State:IS_I) &&
|
||||
machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache) {
|
||||
|
||||
trigger(Event:DataS_fromL1, in_msg.Address, cache_entry, tbe);
|
||||
|
||||
} else if ( (getPendingAcks(tbe) - in_msg.AckCount) == 0 ) {
|
||||
trigger(Event:Data_all_Acks, in_msg.Address, cache_entry, tbe);
|
||||
} else {
|
||||
trigger(Event:Data, in_msg.Address, cache_entry, tbe);
|
||||
}
|
||||
} else if (in_msg.Type == CoherenceResponseType:ACK) {
|
||||
if ( (getPendingAcks(tbe) - in_msg.AckCount) == 0 ) {
|
||||
trigger(Event:Ack_all, in_msg.Address, cache_entry, tbe);
|
||||
} else {
|
||||
trigger(Event:Ack, in_msg.Address, cache_entry, tbe);
|
||||
}
|
||||
} else if (in_msg.Type == CoherenceResponseType:WB_ACK) {
|
||||
trigger(Event:WB_Ack, in_msg.Address, cache_entry, tbe);
|
||||
} else {
|
||||
error("Invalid L1 response type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request InterChip network - request from this L1 cache to the shared L2
|
||||
in_port(requestIntraChipL1Network_in, RequestMsg, requestToL1Cache, rank = 1) {
|
||||
if(requestIntraChipL1Network_in.isReady()) {
|
||||
peek(requestIntraChipL1Network_in, RequestMsg, block_on="Address") {
|
||||
assert(in_msg.Destination.isElement(machineID));
|
||||
|
||||
Entry cache_entry := getCacheEntry(in_msg.Address);
|
||||
TBE tbe := L1_TBEs[in_msg.Address];
|
||||
|
||||
if (in_msg.Type == CoherenceRequestType:INV) {
|
||||
trigger(Event:Inv, in_msg.Address, cache_entry, tbe);
|
||||
} else if (in_msg.Type == CoherenceRequestType:GETX || in_msg.Type == CoherenceRequestType:UPGRADE) {
|
||||
// upgrade transforms to GETX due to race
|
||||
trigger(Event:Fwd_GETX, in_msg.Address, cache_entry, tbe);
|
||||
} else if (in_msg.Type == CoherenceRequestType:GETS) {
|
||||
trigger(Event:Fwd_GETS, in_msg.Address, cache_entry, tbe);
|
||||
} else if (in_msg.Type == CoherenceRequestType:GET_INSTR) {
|
||||
trigger(Event:Fwd_GET_INSTR, in_msg.Address, cache_entry, tbe);
|
||||
} else {
|
||||
error("Invalid forwarded request type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mandatory Queue betweens Node's CPU and it's L1 caches
|
||||
in_port(mandatoryQueue_in, RubyRequest, mandatoryQueue, desc="...", rank = 0) {
|
||||
if (mandatoryQueue_in.isReady()) {
|
||||
peek(mandatoryQueue_in, RubyRequest, block_on="LineAddress") {
|
||||
|
||||
// Check for data access to blocks in I-cache and ifetchs to blocks in D-cache
|
||||
|
||||
if (in_msg.Type == RubyRequestType:IFETCH) {
|
||||
// ** INSTRUCTION ACCESS ***
|
||||
|
||||
Entry L1Icache_entry := getL1ICacheEntry(in_msg.LineAddress);
|
||||
if (is_valid(L1Icache_entry)) {
|
||||
// The tag matches for the L1, so the L1 asks the L2 for it.
|
||||
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
|
||||
L1Icache_entry, L1_TBEs[in_msg.LineAddress]);
|
||||
} else {
|
||||
|
||||
// Check to see if it is in the OTHER L1
|
||||
Entry L1Dcache_entry := getL1DCacheEntry(in_msg.LineAddress);
|
||||
if (is_valid(L1Dcache_entry)) {
|
||||
// The block is in the wrong L1, put the request on the queue to the shared L2
|
||||
trigger(Event:L1_Replacement, in_msg.LineAddress,
|
||||
L1Dcache_entry, L1_TBEs[in_msg.LineAddress]);
|
||||
}
|
||||
|
||||
if (L1IcacheMemory.cacheAvail(in_msg.LineAddress)) {
|
||||
// L1 does't have the line, but we have space for it in the L1 so let's see if the L2 has it
|
||||
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
|
||||
L1Icache_entry, L1_TBEs[in_msg.LineAddress]);
|
||||
} else {
|
||||
// No room in the L1, so we need to make room in the L1
|
||||
trigger(Event:L1_Replacement, L1IcacheMemory.cacheProbe(in_msg.LineAddress),
|
||||
getL1ICacheEntry(L1IcacheMemory.cacheProbe(in_msg.LineAddress)),
|
||||
L1_TBEs[L1IcacheMemory.cacheProbe(in_msg.LineAddress)]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// *** DATA ACCESS ***
|
||||
Entry L1Dcache_entry := getL1DCacheEntry(in_msg.LineAddress);
|
||||
if (is_valid(L1Dcache_entry)) {
|
||||
// The tag matches for the L1, so the L1 ask the L2 for it
|
||||
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
|
||||
L1Dcache_entry, L1_TBEs[in_msg.LineAddress]);
|
||||
} else {
|
||||
|
||||
// Check to see if it is in the OTHER L1
|
||||
Entry L1Icache_entry := getL1ICacheEntry(in_msg.LineAddress);
|
||||
if (is_valid(L1Icache_entry)) {
|
||||
// The block is in the wrong L1, put the request on the queue to the shared L2
|
||||
trigger(Event:L1_Replacement, in_msg.LineAddress,
|
||||
L1Icache_entry, L1_TBEs[in_msg.LineAddress]);
|
||||
}
|
||||
|
||||
if (L1DcacheMemory.cacheAvail(in_msg.LineAddress)) {
|
||||
// L1 does't have the line, but we have space for it in the L1 let's see if the L2 has it
|
||||
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
|
||||
L1Dcache_entry, L1_TBEs[in_msg.LineAddress]);
|
||||
} else {
|
||||
// No room in the L1, so we need to make room in the L1
|
||||
trigger(Event:L1_Replacement, L1DcacheMemory.cacheProbe(in_msg.LineAddress),
|
||||
getL1DCacheEntry(L1DcacheMemory.cacheProbe(in_msg.LineAddress)),
|
||||
L1_TBEs[L1DcacheMemory.cacheProbe(in_msg.LineAddress)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ACTIONS
|
||||
action(a_issueGETS, "a", desc="Issue GETS") {
|
||||
peek(mandatoryQueue_in, RubyRequest) {
|
||||
enqueue(requestIntraChipL1Network_out, RequestMsg, latency=l1_request_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:GETS;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
DPRINTF(RubySlicc, "address: %s, destination: %s\n",
|
||||
address, out_msg.Destination);
|
||||
out_msg.MessageSize := MessageSizeType:Control;
|
||||
out_msg.Prefetch := in_msg.Prefetch;
|
||||
out_msg.AccessMode := in_msg.AccessMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(ai_issueGETINSTR, "ai", desc="Issue GETINSTR") {
|
||||
peek(mandatoryQueue_in, RubyRequest) {
|
||||
enqueue(requestIntraChipL1Network_out, RequestMsg, latency=l1_request_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:GET_INSTR;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
DPRINTF(RubySlicc, "address: %s, destination: %s\n",
|
||||
address, out_msg.Destination);
|
||||
out_msg.MessageSize := MessageSizeType:Control;
|
||||
out_msg.Prefetch := in_msg.Prefetch;
|
||||
out_msg.AccessMode := in_msg.AccessMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
action(b_issueGETX, "b", desc="Issue GETX") {
|
||||
peek(mandatoryQueue_in, RubyRequest) {
|
||||
enqueue(requestIntraChipL1Network_out, RequestMsg, latency=l1_request_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:GETX;
|
||||
out_msg.Requestor := machineID;
|
||||
DPRINTF(RubySlicc, "%s\n", machineID);
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
DPRINTF(RubySlicc, "address: %s, destination: %s\n",
|
||||
address, out_msg.Destination);
|
||||
out_msg.MessageSize := MessageSizeType:Control;
|
||||
out_msg.Prefetch := in_msg.Prefetch;
|
||||
out_msg.AccessMode := in_msg.AccessMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(c_issueUPGRADE, "c", desc="Issue GETX") {
|
||||
peek(mandatoryQueue_in, RubyRequest) {
|
||||
enqueue(requestIntraChipL1Network_out, RequestMsg, latency= l1_request_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:UPGRADE;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
DPRINTF(RubySlicc, "address: %s, destination: %s\n",
|
||||
address, out_msg.Destination);
|
||||
out_msg.MessageSize := MessageSizeType:Control;
|
||||
out_msg.Prefetch := in_msg.Prefetch;
|
||||
out_msg.AccessMode := in_msg.AccessMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(d_sendDataToRequestor, "d", desc="send data to requestor") {
|
||||
peek(requestIntraChipL1Network_in, RequestMsg) {
|
||||
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency=l1_response_latency) {
|
||||
assert(is_valid(cache_entry));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.DataBlk := cache_entry.DataBlk;
|
||||
out_msg.Dirty := cache_entry.Dirty;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(d2_sendDataToL2, "d2", desc="send data to the L2 cache because of M downgrade") {
|
||||
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency=l1_response_latency) {
|
||||
assert(is_valid(cache_entry));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.DataBlk := cache_entry.DataBlk;
|
||||
out_msg.Dirty := cache_entry.Dirty;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
|
||||
action(dt_sendDataToRequestor_fromTBE, "dt", desc="send data to requestor") {
|
||||
peek(requestIntraChipL1Network_in, RequestMsg) {
|
||||
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency=l1_response_latency) {
|
||||
assert(is_valid(tbe));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.DataBlk := tbe.DataBlk;
|
||||
out_msg.Dirty := tbe.Dirty;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(d2t_sendDataToL2_fromTBE, "d2t", desc="send data to the L2 cache") {
|
||||
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency=l1_response_latency) {
|
||||
assert(is_valid(tbe));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.DataBlk := tbe.DataBlk;
|
||||
out_msg.Dirty := tbe.Dirty;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
|
||||
action(e_sendAckToRequestor, "e", desc="send invalidate ack to requestor (could be L2 or L1)") {
|
||||
peek(requestIntraChipL1Network_in, RequestMsg) {
|
||||
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency=l1_response_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:ACK;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.MessageSize := MessageSizeType:Response_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(f_sendDataToL2, "f", desc="send data to the L2 cache") {
|
||||
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency=l1_response_latency) {
|
||||
assert(is_valid(cache_entry));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.DataBlk := cache_entry.DataBlk;
|
||||
out_msg.Dirty := cache_entry.Dirty;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Data;
|
||||
}
|
||||
}
|
||||
|
||||
action(ft_sendDataToL2_fromTBE, "ft", desc="send data to the L2 cache") {
|
||||
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency=l1_response_latency) {
|
||||
assert(is_valid(tbe));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.DataBlk := tbe.DataBlk;
|
||||
out_msg.Dirty := tbe.Dirty;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Data;
|
||||
}
|
||||
}
|
||||
|
||||
action(fi_sendInvAck, "fi", desc="send data to the L2 cache") {
|
||||
peek(requestIntraChipL1Network_in, RequestMsg) {
|
||||
enqueue(responseIntraChipL1Network_out, ResponseMsg, latency=l1_response_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:ACK;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.MessageSize := MessageSizeType:Response_Control;
|
||||
out_msg.AckCount := 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") {
|
||||
if (send_evictions) {
|
||||
DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address);
|
||||
sequencer.evictionCallback(address);
|
||||
}
|
||||
}
|
||||
|
||||
action(g_issuePUTX, "g", desc="send data to the L2 cache") {
|
||||
enqueue(requestIntraChipL1Network_out, RequestMsg, latency=l1_response_latency) {
|
||||
assert(is_valid(cache_entry));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:PUTX;
|
||||
out_msg.DataBlk := cache_entry.DataBlk;
|
||||
out_msg.Dirty := cache_entry.Dirty;
|
||||
out_msg.Requestor:= machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
if (cache_entry.Dirty) {
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Data;
|
||||
} else {
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(j_sendUnblock, "j", desc="send unblock to the L2 cache") {
|
||||
enqueue(unblockNetwork_out, ResponseMsg, latency=to_l2_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:UNBLOCK;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
out_msg.MessageSize := MessageSizeType:Response_Control;
|
||||
DPRINTF(RubySlicc, "%s\n", address);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
action(jj_sendExclusiveUnblock, "\j", desc="send unblock to the L2 cache") {
|
||||
enqueue(unblockNetwork_out, ResponseMsg, latency=to_l2_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:EXCLUSIVE_UNBLOCK;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(mapAddressToRange(address, MachineType:L2Cache,
|
||||
l2_select_low_bit, l2_select_num_bits));
|
||||
out_msg.MessageSize := MessageSizeType:Response_Control;
|
||||
DPRINTF(RubySlicc, "%s\n", address);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
action(h_load_hit, "h", desc="If not prefetch, notify sequencer the load completed.") {
|
||||
assert(is_valid(cache_entry));
|
||||
DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
|
||||
sequencer.readCallback(address, cache_entry.DataBlk);
|
||||
}
|
||||
|
||||
action(hh_store_hit, "\h", desc="If not prefetch, notify sequencer that store completed.") {
|
||||
assert(is_valid(cache_entry));
|
||||
DPRINTF(RubySlicc, "%s\n", cache_entry.DataBlk);
|
||||
sequencer.writeCallback(address, cache_entry.DataBlk);
|
||||
cache_entry.Dirty := true;
|
||||
}
|
||||
|
||||
action(i_allocateTBE, "i", desc="Allocate TBE (isPrefetch=0, number of invalidates=0)") {
|
||||
check_allocate(L1_TBEs);
|
||||
assert(is_valid(cache_entry));
|
||||
L1_TBEs.allocate(address);
|
||||
set_tbe(L1_TBEs[address]);
|
||||
tbe.isPrefetch := false;
|
||||
tbe.Dirty := cache_entry.Dirty;
|
||||
tbe.DataBlk := cache_entry.DataBlk;
|
||||
}
|
||||
|
||||
action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") {
|
||||
mandatoryQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(l_popRequestQueue, "l", desc="Pop incoming request queue and profile the delay within this virtual network") {
|
||||
profileMsgDelay(2, requestIntraChipL1Network_in.dequeue_getDelayCycles());
|
||||
}
|
||||
|
||||
action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue and profile the delay within this virtual network") {
|
||||
profileMsgDelay(3, responseIntraChipL1Network_in.dequeue_getDelayCycles());
|
||||
}
|
||||
|
||||
action(s_deallocateTBE, "s", desc="Deallocate TBE") {
|
||||
L1_TBEs.deallocate(address);
|
||||
unset_tbe();
|
||||
}
|
||||
|
||||
action(u_writeDataToL1Cache, "u", desc="Write data to cache") {
|
||||
peek(responseIntraChipL1Network_in, ResponseMsg) {
|
||||
assert(is_valid(cache_entry));
|
||||
cache_entry.DataBlk := in_msg.DataBlk;
|
||||
cache_entry.Dirty := in_msg.Dirty;
|
||||
}
|
||||
}
|
||||
|
||||
action(q_updateAckCount, "q", desc="Update ack count") {
|
||||
peek(responseIntraChipL1Network_in, ResponseMsg) {
|
||||
assert(is_valid(tbe));
|
||||
tbe.pendingAcks := tbe.pendingAcks - in_msg.AckCount;
|
||||
APPEND_TRANSITION_COMMENT(in_msg.AckCount);
|
||||
APPEND_TRANSITION_COMMENT(" p: ");
|
||||
APPEND_TRANSITION_COMMENT(tbe.pendingAcks);
|
||||
}
|
||||
}
|
||||
|
||||
action(ff_deallocateL1CacheBlock, "\f", desc="Deallocate L1 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") {
|
||||
if (L1DcacheMemory.isTagPresent(address)) {
|
||||
L1DcacheMemory.deallocate(address);
|
||||
} else {
|
||||
L1IcacheMemory.deallocate(address);
|
||||
}
|
||||
unset_cache_entry();
|
||||
}
|
||||
|
||||
action(oo_allocateL1DCacheBlock, "\o", desc="Set L1 D-cache tag equal to tag of block B.") {
|
||||
if (is_invalid(cache_entry)) {
|
||||
set_cache_entry(L1DcacheMemory.allocate(address, new Entry));
|
||||
}
|
||||
}
|
||||
|
||||
action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") {
|
||||
if (is_invalid(cache_entry)) {
|
||||
set_cache_entry(L1IcacheMemory.allocate(address, new Entry));
|
||||
}
|
||||
}
|
||||
|
||||
action(z_stallAndWaitMandatoryQueue, "\z", desc="recycle L1 request queue") {
|
||||
stall_and_wait(mandatoryQueue_in, address);
|
||||
}
|
||||
|
||||
action(kd_wakeUpDependents, "kd", desc="wake-up dependents") {
|
||||
wakeUpBuffers(address);
|
||||
}
|
||||
|
||||
action(uu_profileInstMiss, "\ui", desc="Profile the demand miss") {
|
||||
peek(mandatoryQueue_in, RubyRequest) {
|
||||
L1IcacheMemory.profileMiss(in_msg);
|
||||
}
|
||||
}
|
||||
|
||||
action(uu_profileDataMiss, "\ud", desc="Profile the demand miss") {
|
||||
peek(mandatoryQueue_in, RubyRequest) {
|
||||
L1DcacheMemory.profileMiss(in_msg);
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************
|
||||
// TRANSITIONS
|
||||
//*****************************************************
|
||||
|
||||
// Transitions for Load/Store/Replacement/WriteBack from transient states
|
||||
transition({IS, IM, IS_I, M_I, SM, SINK_WB_ACK}, {Load, Ifetch, Store, L1_Replacement}) {
|
||||
z_stallAndWaitMandatoryQueue;
|
||||
}
|
||||
|
||||
// Transitions from Idle
|
||||
transition({NP,I}, L1_Replacement) {
|
||||
ff_deallocateL1CacheBlock;
|
||||
}
|
||||
|
||||
transition({NP,I}, Load, IS) {
|
||||
oo_allocateL1DCacheBlock;
|
||||
i_allocateTBE;
|
||||
a_issueGETS;
|
||||
uu_profileDataMiss;
|
||||
k_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition({NP,I}, Ifetch, IS) {
|
||||
pp_allocateL1ICacheBlock;
|
||||
i_allocateTBE;
|
||||
ai_issueGETINSTR;
|
||||
uu_profileInstMiss;
|
||||
k_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition({NP,I}, Store, IM) {
|
||||
oo_allocateL1DCacheBlock;
|
||||
i_allocateTBE;
|
||||
b_issueGETX;
|
||||
uu_profileDataMiss;
|
||||
k_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition({NP, I}, Inv) {
|
||||
fi_sendInvAck;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
// Transitions from Shared
|
||||
transition(S, {Load,Ifetch}) {
|
||||
h_load_hit;
|
||||
k_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(S, Store, SM) {
|
||||
i_allocateTBE;
|
||||
c_issueUPGRADE;
|
||||
uu_profileDataMiss;
|
||||
k_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(S, L1_Replacement, I) {
|
||||
forward_eviction_to_cpu;
|
||||
ff_deallocateL1CacheBlock;
|
||||
}
|
||||
|
||||
transition(S, Inv, I) {
|
||||
forward_eviction_to_cpu;
|
||||
fi_sendInvAck;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
// Transitions from Exclusive
|
||||
|
||||
transition(E, {Load, Ifetch}) {
|
||||
h_load_hit;
|
||||
k_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(E, Store, M) {
|
||||
hh_store_hit;
|
||||
k_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(E, L1_Replacement, M_I) {
|
||||
// silent E replacement??
|
||||
forward_eviction_to_cpu;
|
||||
i_allocateTBE;
|
||||
g_issuePUTX; // send data, but hold in case forwarded request
|
||||
ff_deallocateL1CacheBlock;
|
||||
}
|
||||
|
||||
transition(E, Inv, I) {
|
||||
// don't send data
|
||||
forward_eviction_to_cpu;
|
||||
fi_sendInvAck;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(E, Fwd_GETX, I) {
|
||||
forward_eviction_to_cpu;
|
||||
d_sendDataToRequestor;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(E, {Fwd_GETS, Fwd_GET_INSTR}, S) {
|
||||
d_sendDataToRequestor;
|
||||
d2_sendDataToL2;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
// Transitions from Modified
|
||||
transition(M, {Load, Ifetch}) {
|
||||
h_load_hit;
|
||||
k_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(M, Store) {
|
||||
hh_store_hit;
|
||||
k_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(M, L1_Replacement, M_I) {
|
||||
forward_eviction_to_cpu;
|
||||
i_allocateTBE;
|
||||
g_issuePUTX; // send data, but hold in case forwarded request
|
||||
ff_deallocateL1CacheBlock;
|
||||
}
|
||||
|
||||
transition(M_I, WB_Ack, I) {
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
transition(M, Inv, I) {
|
||||
forward_eviction_to_cpu;
|
||||
f_sendDataToL2;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(M_I, Inv, SINK_WB_ACK) {
|
||||
ft_sendDataToL2_fromTBE;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(M, Fwd_GETX, I) {
|
||||
forward_eviction_to_cpu;
|
||||
d_sendDataToRequestor;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(M, {Fwd_GETS, Fwd_GET_INSTR}, S) {
|
||||
d_sendDataToRequestor;
|
||||
d2_sendDataToL2;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(M_I, Fwd_GETX, SINK_WB_ACK) {
|
||||
dt_sendDataToRequestor_fromTBE;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(M_I, {Fwd_GETS, Fwd_GET_INSTR}, SINK_WB_ACK) {
|
||||
dt_sendDataToRequestor_fromTBE;
|
||||
d2t_sendDataToL2_fromTBE;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
// Transitions from IS
|
||||
transition({IS, IS_I}, Inv, IS_I) {
|
||||
fi_sendInvAck;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(IS, Data_all_Acks, S) {
|
||||
u_writeDataToL1Cache;
|
||||
h_load_hit;
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
transition(IS_I, Data_all_Acks, I) {
|
||||
u_writeDataToL1Cache;
|
||||
h_load_hit;
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
transition(IS, DataS_fromL1, S) {
|
||||
u_writeDataToL1Cache;
|
||||
j_sendUnblock;
|
||||
h_load_hit;
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
transition(IS_I, DataS_fromL1, I) {
|
||||
u_writeDataToL1Cache;
|
||||
j_sendUnblock;
|
||||
h_load_hit;
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
// directory is blocked when sending exclusive data
|
||||
transition(IS_I, Data_Exclusive, E) {
|
||||
u_writeDataToL1Cache;
|
||||
h_load_hit;
|
||||
jj_sendExclusiveUnblock;
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
transition(IS, Data_Exclusive, E) {
|
||||
u_writeDataToL1Cache;
|
||||
h_load_hit;
|
||||
jj_sendExclusiveUnblock;
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
// Transitions from IM
|
||||
transition({IM, SM}, Inv, IM) {
|
||||
fi_sendInvAck;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(IM, Data, SM) {
|
||||
u_writeDataToL1Cache;
|
||||
q_updateAckCount;
|
||||
o_popIncomingResponseQueue;
|
||||
}
|
||||
|
||||
transition(IM, Data_all_Acks, M) {
|
||||
u_writeDataToL1Cache;
|
||||
hh_store_hit;
|
||||
jj_sendExclusiveUnblock;
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
// transitions from SM
|
||||
transition({SM, IM}, Ack) {
|
||||
q_updateAckCount;
|
||||
o_popIncomingResponseQueue;
|
||||
}
|
||||
|
||||
transition(SM, Ack_all, M) {
|
||||
jj_sendExclusiveUnblock;
|
||||
hh_store_hit;
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
transition(SINK_WB_ACK, Inv){
|
||||
fi_sendInvAck;
|
||||
l_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(SINK_WB_ACK, WB_Ack, I){
|
||||
s_deallocateTBE;
|
||||
o_popIncomingResponseQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
}
|
||||
1145
simulators/gem5/src/mem/protocol/MESI_CMP_directory-L2cache.sm
Normal file
1145
simulators/gem5/src/mem/protocol/MESI_CMP_directory-L2cache.sm
Normal file
File diff suppressed because it is too large
Load Diff
606
simulators/gem5/src/mem/protocol/MESI_CMP_directory-dir.sm
Normal file
606
simulators/gem5/src/mem/protocol/MESI_CMP_directory-dir.sm
Normal file
@ -0,0 +1,606 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999-2005 Mark D. Hill and David A. Wood
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id: MOESI_CMP_token-dir.sm 1.6 05/01/19 15:48:35-06:00 mikem@royal16.cs.wisc.edu $
|
||||
*/
|
||||
|
||||
// This file is copied from Yasuko Watanabe's prefetch / memory protocol
|
||||
// Copied here by aep 12/14/07
|
||||
|
||||
|
||||
machine(Directory, "MESI_CMP_filter_directory protocol")
|
||||
: DirectoryMemory * directory,
|
||||
MemoryControl * memBuffer,
|
||||
int to_mem_ctrl_latency = 1,
|
||||
int directory_latency = 6
|
||||
{
|
||||
|
||||
MessageBuffer requestToDir, network="From", virtual_network="0", ordered="false", vnet_type="request";
|
||||
MessageBuffer responseToDir, network="From", virtual_network="1", ordered="false", vnet_type="response";
|
||||
|
||||
MessageBuffer requestFromDir, network="To", virtual_network="0", ordered="false", vnet_type="request";
|
||||
MessageBuffer responseFromDir, network="To", virtual_network="1", ordered="false", vnet_type="response";
|
||||
|
||||
// STATES
|
||||
state_declaration(State, desc="Directory states", default="Directory_State_I") {
|
||||
// Base states
|
||||
I, AccessPermission:Read_Write, desc="dir is the owner and memory is up-to-date, all other copies are Invalid";
|
||||
ID, AccessPermission:Busy, desc="Intermediate state for DMA_READ when in I";
|
||||
ID_W, AccessPermission:Busy, desc="Intermediate state for DMA_WRITE when in I";
|
||||
|
||||
M, AccessPermission:Maybe_Stale, desc="memory copy may be stale, i.e. other modified copies may exist";
|
||||
IM, AccessPermission:Busy, desc="Intermediate State I>M";
|
||||
MI, AccessPermission:Busy, desc="Intermediate State M>I";
|
||||
M_DRD, AccessPermission:Busy, desc="Intermediate State when there is a dma read";
|
||||
M_DRDI, AccessPermission:Busy, desc="Intermediate State when there is a dma read";
|
||||
M_DWR, AccessPermission:Busy, desc="Intermediate State when there is a dma write";
|
||||
M_DWRI, AccessPermission:Busy, desc="Intermediate State when there is a dma write";
|
||||
}
|
||||
|
||||
// Events
|
||||
enumeration(Event, desc="Directory events") {
|
||||
Fetch, desc="A memory fetch arrives";
|
||||
Data, desc="writeback data arrives";
|
||||
Memory_Data, desc="Fetched data from memory arrives";
|
||||
Memory_Ack, desc="Writeback Ack from memory arrives";
|
||||
//added by SS for dma
|
||||
DMA_READ, desc="A DMA Read memory request";
|
||||
DMA_WRITE, desc="A DMA Write memory request";
|
||||
CleanReplacement, desc="Clean Replacement in L2 cache";
|
||||
|
||||
}
|
||||
|
||||
// TYPES
|
||||
|
||||
// DirectoryEntry
|
||||
structure(Entry, desc="...", interface="AbstractEntry") {
|
||||
State DirectoryState, desc="Directory state";
|
||||
DataBlock DataBlk, desc="data for the block";
|
||||
NetDest Sharers, desc="Sharers for this block";
|
||||
NetDest Owner, desc="Owner of this block";
|
||||
}
|
||||
|
||||
// TBE entries for DMA requests
|
||||
structure(TBE, desc="TBE entries for outstanding DMA requests") {
|
||||
Address PhysicalAddress, desc="physical address";
|
||||
State TBEState, desc="Transient State";
|
||||
DataBlock DataBlk, desc="Data to be written (DMA write only)";
|
||||
int Len, desc="...";
|
||||
}
|
||||
|
||||
structure(TBETable, external="yes") {
|
||||
TBE lookup(Address);
|
||||
void allocate(Address);
|
||||
void deallocate(Address);
|
||||
bool isPresent(Address);
|
||||
}
|
||||
|
||||
|
||||
// ** OBJECTS **
|
||||
|
||||
TBETable TBEs, template_hack="<Directory_TBE>";
|
||||
|
||||
void set_tbe(TBE tbe);
|
||||
void unset_tbe();
|
||||
void wakeUpBuffers(Address a);
|
||||
|
||||
Entry getDirectoryEntry(Address addr), return_by_pointer="yes" {
|
||||
Entry dir_entry := static_cast(Entry, "pointer", directory[addr]);
|
||||
|
||||
if (is_valid(dir_entry)) {
|
||||
return dir_entry;
|
||||
}
|
||||
|
||||
dir_entry := static_cast(Entry, "pointer",
|
||||
directory.allocate(addr, new Entry));
|
||||
return dir_entry;
|
||||
}
|
||||
|
||||
State getState(TBE tbe, Address addr) {
|
||||
if (is_valid(tbe)) {
|
||||
return tbe.TBEState;
|
||||
} else if (directory.isPresent(addr)) {
|
||||
return getDirectoryEntry(addr).DirectoryState;
|
||||
} else {
|
||||
return State:I;
|
||||
}
|
||||
}
|
||||
|
||||
void setState(TBE tbe, Address addr, State state) {
|
||||
|
||||
if (is_valid(tbe)) {
|
||||
tbe.TBEState := state;
|
||||
}
|
||||
|
||||
if (directory.isPresent(addr)) {
|
||||
|
||||
if (state == State:I) {
|
||||
assert(getDirectoryEntry(addr).Owner.count() == 0);
|
||||
assert(getDirectoryEntry(addr).Sharers.count() == 0);
|
||||
} else if (state == State:M) {
|
||||
assert(getDirectoryEntry(addr).Owner.count() == 1);
|
||||
assert(getDirectoryEntry(addr).Sharers.count() == 0);
|
||||
}
|
||||
|
||||
getDirectoryEntry(addr).DirectoryState := state;
|
||||
}
|
||||
}
|
||||
|
||||
AccessPermission getAccessPermission(Address addr) {
|
||||
TBE tbe := TBEs[addr];
|
||||
if(is_valid(tbe)) {
|
||||
DPRINTF(RubySlicc, "%s\n", Directory_State_to_permission(tbe.TBEState));
|
||||
return Directory_State_to_permission(tbe.TBEState);
|
||||
}
|
||||
|
||||
if(directory.isPresent(addr)) {
|
||||
DPRINTF(RubySlicc, "%s\n", Directory_State_to_permission(getDirectoryEntry(addr).DirectoryState));
|
||||
return Directory_State_to_permission(getDirectoryEntry(addr).DirectoryState);
|
||||
}
|
||||
|
||||
DPRINTF(RubySlicc, "%s\n", AccessPermission:NotPresent);
|
||||
return AccessPermission:NotPresent;
|
||||
}
|
||||
|
||||
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
|
||||
return getDirectoryEntry(addr).DataBlk;
|
||||
}
|
||||
|
||||
void setAccessPermission(Address addr, State state) {
|
||||
if (directory.isPresent(addr)) {
|
||||
getDirectoryEntry(addr).changePermission(Directory_State_to_permission(state));
|
||||
}
|
||||
}
|
||||
|
||||
bool isGETRequest(CoherenceRequestType type) {
|
||||
return (type == CoherenceRequestType:GETS) ||
|
||||
(type == CoherenceRequestType:GET_INSTR) ||
|
||||
(type == CoherenceRequestType:GETX);
|
||||
}
|
||||
|
||||
|
||||
// ** OUT_PORTS **
|
||||
out_port(responseNetwork_out, ResponseMsg, responseFromDir);
|
||||
out_port(memQueue_out, MemoryMsg, memBuffer);
|
||||
|
||||
// ** IN_PORTS **
|
||||
|
||||
in_port(requestNetwork_in, RequestMsg, requestToDir, rank = 0) {
|
||||
if (requestNetwork_in.isReady()) {
|
||||
peek(requestNetwork_in, RequestMsg) {
|
||||
assert(in_msg.Destination.isElement(machineID));
|
||||
if (isGETRequest(in_msg.Type)) {
|
||||
trigger(Event:Fetch, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceRequestType:DMA_READ) {
|
||||
trigger(Event:DMA_READ, makeLineAddress(in_msg.Address),
|
||||
TBEs[makeLineAddress(in_msg.Address)]);
|
||||
} else if (in_msg.Type == CoherenceRequestType:DMA_WRITE) {
|
||||
trigger(Event:DMA_WRITE, makeLineAddress(in_msg.Address),
|
||||
TBEs[makeLineAddress(in_msg.Address)]);
|
||||
} else {
|
||||
DPRINTF(RubySlicc, "%s\n", in_msg);
|
||||
error("Invalid message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(responseNetwork_in, ResponseMsg, responseToDir, rank = 1) {
|
||||
if (responseNetwork_in.isReady()) {
|
||||
peek(responseNetwork_in, ResponseMsg) {
|
||||
assert(in_msg.Destination.isElement(machineID));
|
||||
if (in_msg.Type == CoherenceResponseType:MEMORY_DATA) {
|
||||
trigger(Event:Data, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceResponseType:ACK) {
|
||||
trigger(Event:CleanReplacement, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else {
|
||||
DPRINTF(RubySlicc, "%s\n", in_msg.Type);
|
||||
error("Invalid message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// off-chip memory request/response is done
|
||||
in_port(memQueue_in, MemoryMsg, memBuffer, rank = 2) {
|
||||
if (memQueue_in.isReady()) {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
if (in_msg.Type == MemoryRequestType:MEMORY_READ) {
|
||||
trigger(Event:Memory_Data, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == MemoryRequestType:MEMORY_WB) {
|
||||
trigger(Event:Memory_Ack, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else {
|
||||
DPRINTF(RubySlicc, "%s\n", in_msg.Type);
|
||||
error("Invalid message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Actions
|
||||
action(a_sendAck, "a", desc="Send ack to L2") {
|
||||
peek(responseNetwork_in, ResponseMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:MEMORY_ACK;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.Sender);
|
||||
out_msg.MessageSize := MessageSizeType:Response_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(d_sendData, "d", desc="Send data to requestor") {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:MEMORY_DATA;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.OriginalRequestorMachId);
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Dirty := false;
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
action(aa_sendAck, "aa", desc="Send ack to L2") {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:MEMORY_ACK;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.OriginalRequestorMachId);
|
||||
out_msg.MessageSize := MessageSizeType:Response_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") {
|
||||
requestNetwork_in.dequeue();
|
||||
}
|
||||
|
||||
action(k_popIncomingResponseQueue, "k", desc="Pop incoming request queue") {
|
||||
responseNetwork_in.dequeue();
|
||||
}
|
||||
|
||||
action(l_popMemQueue, "q", desc="Pop off-chip request queue") {
|
||||
memQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(kd_wakeUpDependents, "kd", desc="wake-up dependents") {
|
||||
wakeUpBuffers(address);
|
||||
}
|
||||
|
||||
action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") {
|
||||
peek(requestNetwork_in, RequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_READ;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.OriginalRequestorMachId := in_msg.Requestor;
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
out_msg.Prefetch := in_msg.Prefetch;
|
||||
out_msg.DataBlk := getDirectoryEntry(in_msg.Address).DataBlk;
|
||||
|
||||
DPRINTF(RubySlicc, "%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(qw_queueMemoryWBRequest, "qw", desc="Queue off-chip writeback request") {
|
||||
peek(responseNetwork_in, ResponseMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_WB;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.OriginalRequestorMachId := in_msg.Sender;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
//out_msg.Prefetch := in_msg.Prefetch;
|
||||
|
||||
DPRINTF(RubySlicc, "%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(m_writeDataToMemory, "m", desc="Write dirty writeback to memory") {
|
||||
peek(responseNetwork_in, ResponseMsg) {
|
||||
getDirectoryEntry(in_msg.Address).DataBlk := in_msg.DataBlk;
|
||||
DPRINTF(RubySlicc, "Address: %s, Data Block: %s\n",
|
||||
in_msg.Address, in_msg.DataBlk);
|
||||
}
|
||||
}
|
||||
//added by SS for dma
|
||||
action(qf_queueMemoryFetchRequestDMA, "qfd", desc="Queue off-chip fetch request") {
|
||||
peek(requestNetwork_in, RequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_READ;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.OriginalRequestorMachId := machineID;
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
out_msg.DataBlk := getDirectoryEntry(address).DataBlk;
|
||||
DPRINTF(RubySlicc, "%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(p_popIncomingDMARequestQueue, "p", desc="Pop incoming DMA queue") {
|
||||
requestNetwork_in.dequeue();
|
||||
}
|
||||
|
||||
action(dr_sendDMAData, "dr", desc="Send Data to DMA controller from directory") {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.DataBlk := in_msg.DataBlk; // we send the entire data block and rely on the dma controller to split it up if need be
|
||||
out_msg.Destination.add(map_Address_to_DMA(address));
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(dw_writeDMAData, "dw", desc="DMA Write data to memory") {
|
||||
peek(requestNetwork_in, RequestMsg) {
|
||||
getDirectoryEntry(address).DataBlk.copyPartial(in_msg.DataBlk, addressOffset(in_msg.Address), in_msg.Len);
|
||||
}
|
||||
}
|
||||
|
||||
action(qw_queueMemoryWBRequest_partial, "qwp", desc="Queue off-chip writeback request") {
|
||||
peek(requestNetwork_in, RequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_WB;
|
||||
out_msg.OriginalRequestorMachId := machineID;
|
||||
//out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.DataBlk.copyPartial(in_msg.DataBlk, addressOffset(address), in_msg.Len);
|
||||
|
||||
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
//out_msg.Prefetch := in_msg.Prefetch;
|
||||
|
||||
DPRINTF(RubySlicc, "%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(da_sendDMAAck, "da", desc="Send Ack to DMA controller") {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:ACK;
|
||||
out_msg.Destination.add(map_Address_to_DMA(address));
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
|
||||
action(z_stallAndWaitRequest, "z", desc="recycle request queue") {
|
||||
stall_and_wait(requestNetwork_in, address);
|
||||
}
|
||||
|
||||
action(zz_recycleDMAQueue, "zz", desc="recycle DMA queue") {
|
||||
requestNetwork_in.recycle();
|
||||
}
|
||||
|
||||
|
||||
action(e_ownerIsRequestor, "e", desc="The owner is now the requestor") {
|
||||
peek(requestNetwork_in, RequestMsg) {
|
||||
getDirectoryEntry(address).Owner.clear();
|
||||
getDirectoryEntry(address).Owner.add(in_msg.Requestor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
action(inv_sendCacheInvalidate, "inv", desc="Invalidate a cache block") {
|
||||
peek(requestNetwork_in, RequestMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:INV;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination := getDirectoryEntry(address).Owner;
|
||||
out_msg.MessageSize := MessageSizeType:Response_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
action(drp_sendDMAData, "drp", desc="Send Data to DMA controller from incoming PUTX") {
|
||||
peek(responseNetwork_in, ResponseMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency=to_mem_ctrl_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.DataBlk := in_msg.DataBlk; // we send the entire data block and rely on the dma controller to split it up if need be
|
||||
out_msg.Destination.add(map_Address_to_DMA(address));
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(c_clearOwner, "c", desc="Clear the owner field") {
|
||||
getDirectoryEntry(address).Owner.clear();
|
||||
}
|
||||
|
||||
action(v_allocateTBE, "v", desc="Allocate TBE") {
|
||||
peek(requestNetwork_in, RequestMsg) {
|
||||
TBEs.allocate(address);
|
||||
set_tbe(TBEs[address]);
|
||||
tbe.DataBlk := in_msg.DataBlk;
|
||||
tbe.PhysicalAddress := in_msg.Address;
|
||||
tbe.Len := in_msg.Len;
|
||||
}
|
||||
}
|
||||
|
||||
action(dwt_writeDMADataFromTBE, "dwt", desc="DMA Write data to memory from TBE") {
|
||||
assert(is_valid(tbe));
|
||||
//getDirectoryEntry(address).DataBlk.copyPartial(tbe.DataBlk, tbe.Offset, tbe.Len);
|
||||
getDirectoryEntry(address).DataBlk.copyPartial(tbe.DataBlk, addressOffset(tbe.PhysicalAddress), tbe.Len);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
action(qw_queueMemoryWBRequest_partialTBE, "qwt", desc="Queue off-chip writeback request") {
|
||||
peek(responseNetwork_in, ResponseMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency=to_mem_ctrl_latency) {
|
||||
assert(is_valid(tbe));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_WB;
|
||||
out_msg.OriginalRequestorMachId := in_msg.Sender;
|
||||
//out_msg.DataBlk := in_msg.DataBlk;
|
||||
//out_msg.DataBlk.copyPartial(tbe.DataBlk, tbe.Offset, tbe.Len);
|
||||
out_msg.DataBlk.copyPartial(tbe.DataBlk, addressOffset(tbe.PhysicalAddress), tbe.Len);
|
||||
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
//out_msg.Prefetch := in_msg.Prefetch;
|
||||
|
||||
DPRINTF(RubySlicc, "%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(w_deallocateTBE, "w", desc="Deallocate TBE") {
|
||||
TBEs.deallocate(address);
|
||||
unset_tbe();
|
||||
}
|
||||
|
||||
|
||||
// TRANSITIONS
|
||||
|
||||
|
||||
transition(I, Fetch, IM) {
|
||||
qf_queueMemoryFetchRequest;
|
||||
e_ownerIsRequestor;
|
||||
j_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(IM, Memory_Data, M) {
|
||||
d_sendData;
|
||||
l_popMemQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
//added by SS
|
||||
transition(M, CleanReplacement, I) {
|
||||
c_clearOwner;
|
||||
a_sendAck;
|
||||
k_popIncomingResponseQueue;
|
||||
}
|
||||
|
||||
transition(M, Data, MI) {
|
||||
m_writeDataToMemory;
|
||||
qw_queueMemoryWBRequest;
|
||||
k_popIncomingResponseQueue;
|
||||
}
|
||||
|
||||
transition(MI, Memory_Ack, I) {
|
||||
c_clearOwner;
|
||||
aa_sendAck;
|
||||
l_popMemQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
|
||||
//added by SS for dma support
|
||||
transition(I, DMA_READ, ID) {
|
||||
qf_queueMemoryFetchRequestDMA;
|
||||
j_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(ID, Memory_Data, I) {
|
||||
dr_sendDMAData;
|
||||
l_popMemQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
transition(I, DMA_WRITE, ID_W) {
|
||||
dw_writeDMAData;
|
||||
qw_queueMemoryWBRequest_partial;
|
||||
j_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(ID_W, Memory_Ack, I) {
|
||||
da_sendDMAAck;
|
||||
l_popMemQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
transition({ID, ID_W, M_DRDI, M_DWRI, IM, MI}, {Fetch, Data} ) {
|
||||
z_stallAndWaitRequest;
|
||||
}
|
||||
|
||||
transition({ID, ID_W, M_DRD, M_DRDI, M_DWR, M_DWRI, IM, MI}, {DMA_WRITE, DMA_READ} ) {
|
||||
zz_recycleDMAQueue;
|
||||
}
|
||||
|
||||
|
||||
transition(M, DMA_READ, M_DRD) {
|
||||
inv_sendCacheInvalidate;
|
||||
j_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(M_DRD, Data, M_DRDI) {
|
||||
drp_sendDMAData;
|
||||
m_writeDataToMemory;
|
||||
qw_queueMemoryWBRequest;
|
||||
k_popIncomingResponseQueue;
|
||||
}
|
||||
|
||||
transition(M_DRDI, Memory_Ack, I) {
|
||||
aa_sendAck;
|
||||
c_clearOwner;
|
||||
l_popMemQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
transition(M, DMA_WRITE, M_DWR) {
|
||||
v_allocateTBE;
|
||||
inv_sendCacheInvalidate;
|
||||
j_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(M_DWR, Data, M_DWRI) {
|
||||
m_writeDataToMemory;
|
||||
qw_queueMemoryWBRequest_partialTBE;
|
||||
k_popIncomingResponseQueue;
|
||||
}
|
||||
|
||||
transition(M_DWRI, Memory_Ack, I) {
|
||||
dwt_writeDMADataFromTBE;
|
||||
aa_sendAck;
|
||||
c_clearOwner;
|
||||
da_sendDMAAck;
|
||||
w_deallocateTBE;
|
||||
l_popMemQueue;
|
||||
kd_wakeUpDependents;
|
||||
}
|
||||
|
||||
}
|
||||
146
simulators/gem5/src/mem/protocol/MESI_CMP_directory-dma.sm
Normal file
146
simulators/gem5/src/mem/protocol/MESI_CMP_directory-dma.sm
Normal file
@ -0,0 +1,146 @@
|
||||
|
||||
machine(DMA, "DMA Controller")
|
||||
: DMASequencer * dma_sequencer,
|
||||
int request_latency = 6
|
||||
{
|
||||
|
||||
MessageBuffer responseFromDir, network="From", virtual_network="1", ordered="true", vnet_type="response", no_vector="true";
|
||||
MessageBuffer reqToDirectory, network="To", virtual_network="0", ordered="false", vnet_type="request", no_vector="true";
|
||||
|
||||
state_declaration(State, desc="DMA states", default="DMA_State_READY") {
|
||||
READY, AccessPermission:Invalid, desc="Ready to accept a new request";
|
||||
BUSY_RD, AccessPermission:Busy, desc="Busy: currently processing a request";
|
||||
BUSY_WR, AccessPermission:Busy, desc="Busy: currently processing a request";
|
||||
}
|
||||
|
||||
enumeration(Event, desc="DMA events") {
|
||||
ReadRequest, desc="A new read request";
|
||||
WriteRequest, desc="A new write request";
|
||||
Data, desc="Data from a DMA memory read";
|
||||
Ack, desc="DMA write to memory completed";
|
||||
}
|
||||
|
||||
structure(DMASequencer, external="yes") {
|
||||
void ackCallback();
|
||||
void dataCallback(DataBlock);
|
||||
}
|
||||
|
||||
MessageBuffer mandatoryQueue, ordered="false", no_vector="true";
|
||||
State cur_state, no_vector="true";
|
||||
|
||||
State getState(Address addr) {
|
||||
return cur_state;
|
||||
}
|
||||
void setState(Address addr, State state) {
|
||||
cur_state := state;
|
||||
}
|
||||
|
||||
AccessPermission getAccessPermission(Address addr) {
|
||||
return AccessPermission:NotPresent;
|
||||
}
|
||||
|
||||
void setAccessPermission(Address addr, State state) {
|
||||
}
|
||||
|
||||
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
|
||||
error("DMA does not support get data block.");
|
||||
}
|
||||
|
||||
out_port(reqToDirectory_out, RequestMsg, reqToDirectory, desc="...");
|
||||
|
||||
in_port(dmaRequestQueue_in, SequencerMsg, mandatoryQueue, desc="...") {
|
||||
if (dmaRequestQueue_in.isReady()) {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
if (in_msg.Type == SequencerRequestType:LD ) {
|
||||
trigger(Event:ReadRequest, in_msg.LineAddress);
|
||||
} else if (in_msg.Type == SequencerRequestType:ST) {
|
||||
trigger(Event:WriteRequest, in_msg.LineAddress);
|
||||
} else {
|
||||
error("Invalid request type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(dmaResponseQueue_in, ResponseMsg, responseFromDir, desc="...") {
|
||||
if (dmaResponseQueue_in.isReady()) {
|
||||
peek( dmaResponseQueue_in, ResponseMsg) {
|
||||
if (in_msg.Type == CoherenceResponseType:ACK) {
|
||||
trigger(Event:Ack, makeLineAddress(in_msg.Address));
|
||||
} else if (in_msg.Type == CoherenceResponseType:DATA) {
|
||||
trigger(Event:Data, makeLineAddress(in_msg.Address));
|
||||
} else {
|
||||
error("Invalid response type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(s_sendReadRequest, "s", desc="Send a DMA read request to memory") {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
enqueue(reqToDirectory_out, RequestMsg, latency=request_latency) {
|
||||
out_msg.Address := in_msg.PhysicalAddress;
|
||||
out_msg.Type := CoherenceRequestType:DMA_READ;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Len := in_msg.Len;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(s_sendWriteRequest, "\s", desc="Send a DMA write request to memory") {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
enqueue(reqToDirectory_out, RequestMsg, latency=request_latency) {
|
||||
out_msg.Address := in_msg.PhysicalAddress;
|
||||
out_msg.Type := CoherenceRequestType:DMA_WRITE;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Len := in_msg.Len;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(a_ackCallback, "a", desc="Notify dma controller that write request completed") {
|
||||
dma_sequencer.ackCallback();
|
||||
}
|
||||
|
||||
action(d_dataCallback, "d", desc="Write data to dma sequencer") {
|
||||
peek (dmaResponseQueue_in, ResponseMsg) {
|
||||
dma_sequencer.dataCallback(in_msg.DataBlk);
|
||||
}
|
||||
}
|
||||
|
||||
action(p_popRequestQueue, "p", desc="Pop request queue") {
|
||||
dmaRequestQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(p_popResponseQueue, "\p", desc="Pop request queue") {
|
||||
dmaResponseQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(z_stall, "z", desc="dma is busy..stall") {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
transition(READY, ReadRequest, BUSY_RD) {
|
||||
s_sendReadRequest;
|
||||
p_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(READY, WriteRequest, BUSY_WR) {
|
||||
s_sendWriteRequest;
|
||||
p_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_RD, Data, READY) {
|
||||
d_dataCallback;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_WR, Ack, READY) {
|
||||
a_ackCallback;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
}
|
||||
87
simulators/gem5/src/mem/protocol/MESI_CMP_directory-msg.sm
Normal file
87
simulators/gem5/src/mem/protocol/MESI_CMP_directory-msg.sm
Normal file
@ -0,0 +1,87 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999-2005 Mark D. Hill and David A. Wood
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
// CoherenceRequestType
|
||||
enumeration(CoherenceRequestType, desc="...") {
|
||||
GETX, desc="Get eXclusive";
|
||||
UPGRADE, desc="UPGRADE to exclusive";
|
||||
GETS, desc="Get Shared";
|
||||
GET_INSTR, desc="Get Instruction";
|
||||
INV, desc="INValidate";
|
||||
PUTX, desc="replacement message";
|
||||
|
||||
WB_ACK, desc="Writeback ack";
|
||||
WB_NACK, desc="Writeback neg. ack";
|
||||
FWD, desc="Generic FWD";
|
||||
|
||||
DMA_READ, desc="DMA Read";
|
||||
DMA_WRITE, desc="DMA Write";
|
||||
}
|
||||
|
||||
// CoherenceResponseType
|
||||
enumeration(CoherenceResponseType, desc="...") {
|
||||
MEMORY_ACK, desc="Ack from memory controller";
|
||||
DATA, desc="Data";
|
||||
DATA_EXCLUSIVE, desc="Data";
|
||||
MEMORY_DATA, desc="Data";
|
||||
ACK, desc="Generic invalidate ack";
|
||||
WB_ACK, desc="writeback ack";
|
||||
UNBLOCK, desc="unblock";
|
||||
EXCLUSIVE_UNBLOCK, desc="exclusive unblock";
|
||||
INV, desc="Invalidate from directory";
|
||||
}
|
||||
|
||||
// RequestMsg
|
||||
structure(RequestMsg, desc="...", interface="NetworkMessage") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)";
|
||||
RubyAccessMode AccessMode, desc="user/supervisor access type";
|
||||
MachineID Requestor , desc="What component request";
|
||||
NetDest Destination, desc="What components receive the request, includes MachineType and num";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
DataBlock DataBlk, desc="Data for the cache line (if PUTX)";
|
||||
int Len;
|
||||
bool Dirty, default="false", desc="Dirty bit";
|
||||
PrefetchBit Prefetch, desc="Is this a prefetch request";
|
||||
}
|
||||
|
||||
// ResponseMsg
|
||||
structure(ResponseMsg, desc="...", interface="NetworkMessage") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)";
|
||||
MachineID Sender, desc="What component sent the data";
|
||||
NetDest Destination, desc="Node to whom the data is sent";
|
||||
DataBlock DataBlk, desc="Data for the cache line";
|
||||
bool Dirty, default="false", desc="Dirty bit";
|
||||
int AckCount, default="0", desc="number of acks in this message";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
protocol "MESI_CMP_directory";
|
||||
include "RubySlicc_interfaces.slicc";
|
||||
include "MESI_CMP_directory-msg.sm";
|
||||
include "MESI_CMP_directory-L1cache.sm";
|
||||
include "MESI_CMP_directory-L2cache.sm";
|
||||
include "MESI_CMP_directory-dir.sm";
|
||||
include "MESI_CMP_directory-dma.sm";
|
||||
481
simulators/gem5/src/mem/protocol/MI_example-cache.sm
Normal file
481
simulators/gem5/src/mem/protocol/MI_example-cache.sm
Normal file
@ -0,0 +1,481 @@
|
||||
|
||||
machine(L1Cache, "MI Example L1 Cache")
|
||||
: Sequencer * sequencer,
|
||||
CacheMemory * cacheMemory,
|
||||
int cache_response_latency = 12,
|
||||
int issue_latency = 2,
|
||||
bool send_evictions
|
||||
{
|
||||
|
||||
// NETWORK BUFFERS
|
||||
MessageBuffer requestFromCache, network="To", virtual_network="2", ordered="true", vnet_type="request";
|
||||
MessageBuffer responseFromCache, network="To", virtual_network="4", ordered="true", vnet_type="response";
|
||||
|
||||
MessageBuffer forwardToCache, network="From", virtual_network="3", ordered="true", vnet_type="forward";
|
||||
MessageBuffer responseToCache, network="From", virtual_network="4", ordered="true", vnet_type="response";
|
||||
|
||||
// STATES
|
||||
state_declaration(State, desc="Cache states") {
|
||||
I, AccessPermission:Invalid, desc="Not Present/Invalid";
|
||||
II, AccessPermission:Busy, desc="Not Present/Invalid, issued PUT";
|
||||
M, AccessPermission:Read_Write, desc="Modified";
|
||||
MI, AccessPermission:Busy, desc="Modified, issued PUT";
|
||||
MII, AccessPermission:Busy, desc="Modified, issued PUTX, received nack";
|
||||
|
||||
IS, AccessPermission:Busy, desc="Issued request for LOAD/IFETCH";
|
||||
IM, AccessPermission:Busy, desc="Issued request for STORE/ATOMIC";
|
||||
}
|
||||
|
||||
// EVENTS
|
||||
enumeration(Event, desc="Cache events") {
|
||||
// From processor
|
||||
|
||||
Load, desc="Load request from processor";
|
||||
Ifetch, desc="Ifetch request from processor";
|
||||
Store, desc="Store request from processor";
|
||||
|
||||
Data, desc="Data from network";
|
||||
Fwd_GETX, desc="Forward from network";
|
||||
|
||||
Inv, desc="Invalidate request from dir";
|
||||
|
||||
Replacement, desc="Replace a block";
|
||||
Writeback_Ack, desc="Ack from the directory for a writeback";
|
||||
Writeback_Nack, desc="Nack from the directory for a writeback";
|
||||
}
|
||||
|
||||
// STRUCTURE DEFINITIONS
|
||||
|
||||
MessageBuffer mandatoryQueue, ordered="false";
|
||||
|
||||
// CacheEntry
|
||||
structure(Entry, desc="...", interface="AbstractCacheEntry") {
|
||||
State CacheState, desc="cache state";
|
||||
bool Dirty, desc="Is the data dirty (different than memory)?";
|
||||
DataBlock DataBlk, desc="Data in the block";
|
||||
}
|
||||
|
||||
// TBE fields
|
||||
structure(TBE, desc="...") {
|
||||
State TBEState, desc="Transient state";
|
||||
DataBlock DataBlk, desc="data for the block, required for concurrent writebacks";
|
||||
}
|
||||
|
||||
structure(TBETable, external="yes") {
|
||||
TBE lookup(Address);
|
||||
void allocate(Address);
|
||||
void deallocate(Address);
|
||||
bool isPresent(Address);
|
||||
}
|
||||
|
||||
|
||||
// STRUCTURES
|
||||
TBETable TBEs, template_hack="<L1Cache_TBE>";
|
||||
|
||||
// PROTOTYPES
|
||||
void set_cache_entry(AbstractCacheEntry a);
|
||||
void unset_cache_entry();
|
||||
void set_tbe(TBE b);
|
||||
void unset_tbe();
|
||||
|
||||
Entry getCacheEntry(Address address), return_by_pointer="yes" {
|
||||
return static_cast(Entry, "pointer", cacheMemory.lookup(address));
|
||||
}
|
||||
|
||||
// FUNCTIONS
|
||||
Event mandatory_request_type_to_event(RubyRequestType type) {
|
||||
if (type == RubyRequestType:LD) {
|
||||
return Event:Load;
|
||||
} else if (type == RubyRequestType:IFETCH) {
|
||||
return Event:Ifetch;
|
||||
} else if ((type == RubyRequestType:ST) || (type == RubyRequestType:ATOMIC)) {
|
||||
return Event:Store;
|
||||
} else {
|
||||
error("Invalid RubyRequestType");
|
||||
}
|
||||
}
|
||||
|
||||
State getState(TBE tbe, Entry cache_entry, Address addr) {
|
||||
|
||||
if (is_valid(tbe)) {
|
||||
return tbe.TBEState;
|
||||
}
|
||||
else if (is_valid(cache_entry)) {
|
||||
return cache_entry.CacheState;
|
||||
}
|
||||
else {
|
||||
return State:I;
|
||||
}
|
||||
}
|
||||
|
||||
void setState(TBE tbe, Entry cache_entry, Address addr, State state) {
|
||||
|
||||
if (is_valid(tbe)) {
|
||||
tbe.TBEState := state;
|
||||
}
|
||||
|
||||
if (is_valid(cache_entry)) {
|
||||
cache_entry.CacheState := state;
|
||||
}
|
||||
}
|
||||
|
||||
AccessPermission getAccessPermission(Address addr) {
|
||||
TBE tbe := TBEs[addr];
|
||||
if(is_valid(tbe)) {
|
||||
return L1Cache_State_to_permission(tbe.TBEState);
|
||||
}
|
||||
|
||||
Entry cache_entry := getCacheEntry(addr);
|
||||
if(is_valid(cache_entry)) {
|
||||
return L1Cache_State_to_permission(cache_entry.CacheState);
|
||||
}
|
||||
|
||||
return AccessPermission:NotPresent;
|
||||
}
|
||||
|
||||
void setAccessPermission(Entry cache_entry, Address addr, State state) {
|
||||
if (is_valid(cache_entry)) {
|
||||
cache_entry.changePermission(L1Cache_State_to_permission(state));
|
||||
}
|
||||
}
|
||||
|
||||
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
|
||||
return getCacheEntry(addr).DataBlk;
|
||||
}
|
||||
|
||||
GenericMachineType getNondirectHitMachType(MachineID sender) {
|
||||
if (machineIDToMachineType(sender) == MachineType:L1Cache) {
|
||||
//
|
||||
// NOTE direct local hits should not call this
|
||||
//
|
||||
return GenericMachineType:L1Cache_wCC;
|
||||
} else {
|
||||
return ConvertMachToGenericMach(machineIDToMachineType(sender));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NETWORK PORTS
|
||||
|
||||
out_port(requestNetwork_out, RequestMsg, requestFromCache);
|
||||
out_port(responseNetwork_out, ResponseMsg, responseFromCache);
|
||||
|
||||
in_port(forwardRequestNetwork_in, RequestMsg, forwardToCache) {
|
||||
if (forwardRequestNetwork_in.isReady()) {
|
||||
peek(forwardRequestNetwork_in, RequestMsg, block_on="Address") {
|
||||
|
||||
Entry cache_entry := getCacheEntry(in_msg.Address);
|
||||
TBE tbe := TBEs[in_msg.Address];
|
||||
|
||||
if (in_msg.Type == CoherenceRequestType:GETX) {
|
||||
trigger(Event:Fwd_GETX, in_msg.Address, cache_entry, tbe);
|
||||
}
|
||||
else if (in_msg.Type == CoherenceRequestType:WB_ACK) {
|
||||
trigger(Event:Writeback_Ack, in_msg.Address, cache_entry, tbe);
|
||||
}
|
||||
else if (in_msg.Type == CoherenceRequestType:WB_NACK) {
|
||||
trigger(Event:Writeback_Nack, in_msg.Address, cache_entry, tbe);
|
||||
}
|
||||
else if (in_msg.Type == CoherenceRequestType:INV) {
|
||||
trigger(Event:Inv, in_msg.Address, cache_entry, tbe);
|
||||
}
|
||||
else {
|
||||
error("Unexpected message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(responseNetwork_in, ResponseMsg, responseToCache) {
|
||||
if (responseNetwork_in.isReady()) {
|
||||
peek(responseNetwork_in, ResponseMsg, block_on="Address") {
|
||||
|
||||
Entry cache_entry := getCacheEntry(in_msg.Address);
|
||||
TBE tbe := TBEs[in_msg.Address];
|
||||
|
||||
if (in_msg.Type == CoherenceResponseType:DATA) {
|
||||
trigger(Event:Data, in_msg.Address, cache_entry, tbe);
|
||||
}
|
||||
else {
|
||||
error("Unexpected message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mandatory Queue
|
||||
in_port(mandatoryQueue_in, RubyRequest, mandatoryQueue, desc="...") {
|
||||
if (mandatoryQueue_in.isReady()) {
|
||||
peek(mandatoryQueue_in, RubyRequest, block_on="LineAddress") {
|
||||
|
||||
Entry cache_entry := getCacheEntry(in_msg.LineAddress);
|
||||
if (is_invalid(cache_entry) &&
|
||||
cacheMemory.cacheAvail(in_msg.LineAddress) == false ) {
|
||||
// make room for the block
|
||||
trigger(Event:Replacement, cacheMemory.cacheProbe(in_msg.LineAddress),
|
||||
getCacheEntry(cacheMemory.cacheProbe(in_msg.LineAddress)),
|
||||
TBEs[cacheMemory.cacheProbe(in_msg.LineAddress)]);
|
||||
}
|
||||
else {
|
||||
trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.LineAddress,
|
||||
cache_entry, TBEs[in_msg.LineAddress]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ACTIONS
|
||||
|
||||
action(a_issueRequest, "a", desc="Issue a request") {
|
||||
enqueue(requestNetwork_out, RequestMsg, latency=issue_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:GETX;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.MessageSize := MessageSizeType:Control;
|
||||
}
|
||||
}
|
||||
|
||||
action(b_issuePUT, "b", desc="Issue a PUT request") {
|
||||
enqueue(requestNetwork_out, RequestMsg, latency=issue_latency) {
|
||||
assert(is_valid(cache_entry));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:PUTX;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.DataBlk := cache_entry.DataBlk;
|
||||
out_msg.MessageSize := MessageSizeType:Data;
|
||||
}
|
||||
}
|
||||
|
||||
action(e_sendData, "e", desc="Send data from cache to requestor") {
|
||||
peek(forwardRequestNetwork_in, RequestMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency=cache_response_latency) {
|
||||
assert(is_valid(cache_entry));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.DataBlk := cache_entry.DataBlk;
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(ee_sendDataFromTBE, "\e", desc="Send data from TBE to requestor") {
|
||||
peek(forwardRequestNetwork_in, RequestMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency=cache_response_latency) {
|
||||
assert(is_valid(tbe));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.DataBlk := tbe.DataBlk;
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(i_allocateL1CacheBlock, "i", desc="Allocate a cache block") {
|
||||
if (is_valid(cache_entry)) {
|
||||
} else {
|
||||
set_cache_entry(cacheMemory.allocate(address, new Entry));
|
||||
}
|
||||
}
|
||||
|
||||
action(h_deallocateL1CacheBlock, "h", desc="deallocate a cache block") {
|
||||
if (is_valid(cache_entry)) {
|
||||
cacheMemory.deallocate(address);
|
||||
unset_cache_entry();
|
||||
}
|
||||
}
|
||||
|
||||
action(m_popMandatoryQueue, "m", desc="Pop the mandatory request queue") {
|
||||
mandatoryQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(n_popResponseQueue, "n", desc="Pop the response queue") {
|
||||
profileMsgDelay(1, responseNetwork_in.dequeue_getDelayCycles());
|
||||
}
|
||||
|
||||
action(o_popForwardedRequestQueue, "o", desc="Pop the forwarded request queue") {
|
||||
profileMsgDelay(2, forwardRequestNetwork_in.dequeue_getDelayCycles());
|
||||
}
|
||||
|
||||
action(p_profileMiss, "p", desc="Profile cache miss") {
|
||||
peek(mandatoryQueue_in, RubyRequest) {
|
||||
cacheMemory.profileMiss(in_msg);
|
||||
}
|
||||
}
|
||||
|
||||
action(r_load_hit, "r", desc="Notify sequencer the load completed.") {
|
||||
assert(is_valid(cache_entry));
|
||||
DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
|
||||
sequencer.readCallback(address,
|
||||
GenericMachineType:L1Cache,
|
||||
cache_entry.DataBlk);
|
||||
}
|
||||
|
||||
action(rx_load_hit, "rx", desc="External load completed.") {
|
||||
peek(responseNetwork_in, ResponseMsg) {
|
||||
assert(is_valid(cache_entry));
|
||||
DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
|
||||
sequencer.readCallback(address,
|
||||
getNondirectHitMachType(in_msg.Sender),
|
||||
cache_entry.DataBlk);
|
||||
}
|
||||
}
|
||||
|
||||
action(s_store_hit, "s", desc="Notify sequencer that store completed.") {
|
||||
assert(is_valid(cache_entry));
|
||||
DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
|
||||
sequencer.writeCallback(address,
|
||||
GenericMachineType:L1Cache,
|
||||
cache_entry.DataBlk);
|
||||
}
|
||||
|
||||
action(sx_store_hit, "sx", desc="External store completed.") {
|
||||
peek(responseNetwork_in, ResponseMsg) {
|
||||
assert(is_valid(cache_entry));
|
||||
DPRINTF(RubySlicc,"%s\n", cache_entry.DataBlk);
|
||||
sequencer.writeCallback(address,
|
||||
getNondirectHitMachType(in_msg.Sender),
|
||||
cache_entry.DataBlk);
|
||||
}
|
||||
}
|
||||
|
||||
action(u_writeDataToCache, "u", desc="Write data to the cache") {
|
||||
peek(responseNetwork_in, ResponseMsg) {
|
||||
assert(is_valid(cache_entry));
|
||||
cache_entry.DataBlk := in_msg.DataBlk;
|
||||
}
|
||||
}
|
||||
|
||||
action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") {
|
||||
if (send_evictions) {
|
||||
DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address);
|
||||
sequencer.evictionCallback(address);
|
||||
}
|
||||
}
|
||||
|
||||
action(v_allocateTBE, "v", desc="Allocate TBE") {
|
||||
TBEs.allocate(address);
|
||||
set_tbe(TBEs[address]);
|
||||
}
|
||||
|
||||
action(w_deallocateTBE, "w", desc="Deallocate TBE") {
|
||||
TBEs.deallocate(address);
|
||||
unset_tbe();
|
||||
}
|
||||
|
||||
action(x_copyDataFromCacheToTBE, "x", desc="Copy data from cache to TBE") {
|
||||
assert(is_valid(cache_entry));
|
||||
assert(is_valid(tbe));
|
||||
tbe.DataBlk := cache_entry.DataBlk;
|
||||
}
|
||||
|
||||
action(z_stall, "z", desc="stall") {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// TRANSITIONS
|
||||
|
||||
transition({IS, IM, MI, II}, {Load, Ifetch, Store, Replacement}) {
|
||||
z_stall;
|
||||
}
|
||||
|
||||
transition({IS, IM}, {Fwd_GETX, Inv}) {
|
||||
z_stall;
|
||||
}
|
||||
|
||||
transition(MI, Inv) {
|
||||
o_popForwardedRequestQueue;
|
||||
}
|
||||
|
||||
transition(M, Store) {
|
||||
s_store_hit;
|
||||
m_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(M, {Load, Ifetch}) {
|
||||
r_load_hit;
|
||||
m_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(I, Inv) {
|
||||
o_popForwardedRequestQueue;
|
||||
}
|
||||
|
||||
transition(I, Store, IM) {
|
||||
v_allocateTBE;
|
||||
i_allocateL1CacheBlock;
|
||||
a_issueRequest;
|
||||
p_profileMiss;
|
||||
m_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(I, {Load, Ifetch}, IS) {
|
||||
v_allocateTBE;
|
||||
i_allocateL1CacheBlock;
|
||||
a_issueRequest;
|
||||
p_profileMiss;
|
||||
m_popMandatoryQueue;
|
||||
}
|
||||
|
||||
transition(IS, Data, M) {
|
||||
u_writeDataToCache;
|
||||
rx_load_hit;
|
||||
w_deallocateTBE;
|
||||
n_popResponseQueue;
|
||||
}
|
||||
|
||||
transition(IM, Data, M) {
|
||||
u_writeDataToCache;
|
||||
sx_store_hit;
|
||||
w_deallocateTBE;
|
||||
n_popResponseQueue;
|
||||
}
|
||||
|
||||
transition(M, Fwd_GETX, I) {
|
||||
e_sendData;
|
||||
forward_eviction_to_cpu;
|
||||
o_popForwardedRequestQueue;
|
||||
}
|
||||
|
||||
transition(I, Replacement) {
|
||||
h_deallocateL1CacheBlock;
|
||||
}
|
||||
|
||||
transition(M, {Replacement,Inv}, MI) {
|
||||
v_allocateTBE;
|
||||
b_issuePUT;
|
||||
x_copyDataFromCacheToTBE;
|
||||
forward_eviction_to_cpu;
|
||||
h_deallocateL1CacheBlock;
|
||||
}
|
||||
|
||||
transition(MI, Writeback_Ack, I) {
|
||||
w_deallocateTBE;
|
||||
o_popForwardedRequestQueue;
|
||||
}
|
||||
|
||||
transition(MI, Fwd_GETX, II) {
|
||||
ee_sendDataFromTBE;
|
||||
o_popForwardedRequestQueue;
|
||||
}
|
||||
|
||||
transition(MI, Writeback_Nack, MII) {
|
||||
o_popForwardedRequestQueue;
|
||||
}
|
||||
|
||||
transition(MII, Fwd_GETX, I) {
|
||||
ee_sendDataFromTBE;
|
||||
w_deallocateTBE;
|
||||
o_popForwardedRequestQueue;
|
||||
}
|
||||
|
||||
transition(II, Writeback_Nack, I) {
|
||||
w_deallocateTBE;
|
||||
o_popForwardedRequestQueue;
|
||||
}
|
||||
}
|
||||
632
simulators/gem5/src/mem/protocol/MI_example-dir.sm
Normal file
632
simulators/gem5/src/mem/protocol/MI_example-dir.sm
Normal file
@ -0,0 +1,632 @@
|
||||
|
||||
machine(Directory, "Directory protocol")
|
||||
: DirectoryMemory * directory,
|
||||
MemoryControl * memBuffer,
|
||||
int directory_latency = 12
|
||||
{
|
||||
|
||||
MessageBuffer forwardFromDir, network="To", virtual_network="3", ordered="false", vnet_type="forward";
|
||||
MessageBuffer responseFromDir, network="To", virtual_network="4", ordered="false", vnet_type="response";
|
||||
MessageBuffer dmaResponseFromDir, network="To", virtual_network="1", ordered="true", vnet_type="response";
|
||||
|
||||
MessageBuffer requestToDir, network="From", virtual_network="2", ordered="true", vnet_type="request";
|
||||
MessageBuffer dmaRequestToDir, network="From", virtual_network="0", ordered="true", vnet_type="request";
|
||||
|
||||
// STATES
|
||||
state_declaration(State, desc="Directory states", default="Directory_State_I") {
|
||||
// Base states
|
||||
I, AccessPermission:Read_Write, desc="Invalid";
|
||||
M, AccessPermission:Invalid, desc="Modified";
|
||||
|
||||
M_DRD, AccessPermission:Busy, desc="Blocked on an invalidation for a DMA read";
|
||||
M_DWR, AccessPermission:Busy, desc="Blocked on an invalidation for a DMA write";
|
||||
|
||||
M_DWRI, AccessPermission:Busy, desc="Intermediate state M_DWR-->I";
|
||||
M_DRDI, AccessPermission:Busy, desc="Intermediate state M_DRD-->I";
|
||||
|
||||
IM, AccessPermission:Busy, desc="Intermediate state I-->M";
|
||||
MI, AccessPermission:Busy, desc="Intermediate state M-->I";
|
||||
ID, AccessPermission:Busy, desc="Intermediate state for DMA_READ when in I";
|
||||
ID_W, AccessPermission:Busy, desc="Intermediate state for DMA_WRITE when in I";
|
||||
}
|
||||
|
||||
// Events
|
||||
enumeration(Event, desc="Directory events") {
|
||||
// processor requests
|
||||
GETX, desc="A GETX arrives";
|
||||
GETS, desc="A GETS arrives";
|
||||
PUTX, desc="A PUTX arrives";
|
||||
PUTX_NotOwner, desc="A PUTX arrives";
|
||||
|
||||
// DMA requests
|
||||
DMA_READ, desc="A DMA Read memory request";
|
||||
DMA_WRITE, desc="A DMA Write memory request";
|
||||
|
||||
// Memory Controller
|
||||
Memory_Data, desc="Fetched data from memory arrives";
|
||||
Memory_Ack, desc="Writeback Ack from memory arrives";
|
||||
}
|
||||
|
||||
// TYPES
|
||||
|
||||
// DirectoryEntry
|
||||
structure(Entry, desc="...", interface="AbstractEntry") {
|
||||
State DirectoryState, desc="Directory state";
|
||||
DataBlock DataBlk, desc="data for the block";
|
||||
NetDest Sharers, desc="Sharers for this block";
|
||||
NetDest Owner, desc="Owner of this block";
|
||||
}
|
||||
|
||||
// TBE entries for DMA requests
|
||||
structure(TBE, desc="TBE entries for outstanding DMA requests") {
|
||||
Address PhysicalAddress, desc="physical address";
|
||||
State TBEState, desc="Transient State";
|
||||
DataBlock DataBlk, desc="Data to be written (DMA write only)";
|
||||
int Len, desc="...";
|
||||
MachineID DmaRequestor, desc="DMA requestor";
|
||||
}
|
||||
|
||||
structure(TBETable, external="yes") {
|
||||
TBE lookup(Address);
|
||||
void allocate(Address);
|
||||
void deallocate(Address);
|
||||
bool isPresent(Address);
|
||||
}
|
||||
|
||||
// ** OBJECTS **
|
||||
TBETable TBEs, template_hack="<Directory_TBE>";
|
||||
|
||||
void set_tbe(TBE b);
|
||||
void unset_tbe();
|
||||
|
||||
Entry getDirectoryEntry(Address addr), return_by_pointer="yes" {
|
||||
Entry dir_entry := static_cast(Entry, "pointer", directory[addr]);
|
||||
|
||||
if (is_valid(dir_entry)) {
|
||||
return dir_entry;
|
||||
}
|
||||
|
||||
dir_entry := static_cast(Entry, "pointer",
|
||||
directory.allocate(addr, new Entry));
|
||||
return dir_entry;
|
||||
}
|
||||
|
||||
State getState(TBE tbe, Address addr) {
|
||||
if (is_valid(tbe)) {
|
||||
return tbe.TBEState;
|
||||
} else if (directory.isPresent(addr)) {
|
||||
return getDirectoryEntry(addr).DirectoryState;
|
||||
} else {
|
||||
return State:I;
|
||||
}
|
||||
}
|
||||
|
||||
void setState(TBE tbe, Address addr, State state) {
|
||||
|
||||
if (is_valid(tbe)) {
|
||||
tbe.TBEState := state;
|
||||
}
|
||||
|
||||
if (directory.isPresent(addr)) {
|
||||
|
||||
if (state == State:M) {
|
||||
assert(getDirectoryEntry(addr).Owner.count() == 1);
|
||||
assert(getDirectoryEntry(addr).Sharers.count() == 0);
|
||||
}
|
||||
|
||||
getDirectoryEntry(addr).DirectoryState := state;
|
||||
|
||||
if (state == State:I) {
|
||||
assert(getDirectoryEntry(addr).Owner.count() == 0);
|
||||
assert(getDirectoryEntry(addr).Sharers.count() == 0);
|
||||
directory.invalidateBlock(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AccessPermission getAccessPermission(Address addr) {
|
||||
TBE tbe := TBEs[addr];
|
||||
if(is_valid(tbe)) {
|
||||
return Directory_State_to_permission(tbe.TBEState);
|
||||
}
|
||||
|
||||
if(directory.isPresent(addr)) {
|
||||
return Directory_State_to_permission(getDirectoryEntry(addr).DirectoryState);
|
||||
}
|
||||
|
||||
return AccessPermission:NotPresent;
|
||||
}
|
||||
|
||||
void setAccessPermission(Address addr, State state) {
|
||||
if (directory.isPresent(addr)) {
|
||||
getDirectoryEntry(addr).changePermission(Directory_State_to_permission(state));
|
||||
}
|
||||
}
|
||||
|
||||
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
|
||||
return getDirectoryEntry(addr).DataBlk;
|
||||
}
|
||||
|
||||
// ** OUT_PORTS **
|
||||
out_port(forwardNetwork_out, RequestMsg, forwardFromDir);
|
||||
out_port(responseNetwork_out, ResponseMsg, responseFromDir);
|
||||
out_port(requestQueue_out, ResponseMsg, requestToDir); // For recycling requests
|
||||
out_port(dmaResponseNetwork_out, DMAResponseMsg, dmaResponseFromDir);
|
||||
|
||||
//added by SS
|
||||
out_port(memQueue_out, MemoryMsg, memBuffer);
|
||||
// ** IN_PORTS **
|
||||
|
||||
in_port(dmaRequestQueue_in, DMARequestMsg, dmaRequestToDir) {
|
||||
if (dmaRequestQueue_in.isReady()) {
|
||||
peek(dmaRequestQueue_in, DMARequestMsg) {
|
||||
TBE tbe := TBEs[in_msg.LineAddress];
|
||||
if (in_msg.Type == DMARequestType:READ) {
|
||||
trigger(Event:DMA_READ, in_msg.LineAddress, tbe);
|
||||
} else if (in_msg.Type == DMARequestType:WRITE) {
|
||||
trigger(Event:DMA_WRITE, in_msg.LineAddress, tbe);
|
||||
} else {
|
||||
error("Invalid message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(requestQueue_in, RequestMsg, requestToDir) {
|
||||
if (requestQueue_in.isReady()) {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
TBE tbe := TBEs[in_msg.Address];
|
||||
if (in_msg.Type == CoherenceRequestType:GETS) {
|
||||
trigger(Event:GETS, in_msg.Address, tbe);
|
||||
} else if (in_msg.Type == CoherenceRequestType:GETX) {
|
||||
trigger(Event:GETX, in_msg.Address, tbe);
|
||||
} else if (in_msg.Type == CoherenceRequestType:PUTX) {
|
||||
if (getDirectoryEntry(in_msg.Address).Owner.isElement(in_msg.Requestor)) {
|
||||
trigger(Event:PUTX, in_msg.Address, tbe);
|
||||
} else {
|
||||
trigger(Event:PUTX_NotOwner, in_msg.Address, tbe);
|
||||
}
|
||||
} else {
|
||||
error("Invalid message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//added by SS
|
||||
// off-chip memory request/response is done
|
||||
in_port(memQueue_in, MemoryMsg, memBuffer) {
|
||||
if (memQueue_in.isReady()) {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
TBE tbe := TBEs[in_msg.Address];
|
||||
if (in_msg.Type == MemoryRequestType:MEMORY_READ) {
|
||||
trigger(Event:Memory_Data, in_msg.Address, tbe);
|
||||
} else if (in_msg.Type == MemoryRequestType:MEMORY_WB) {
|
||||
trigger(Event:Memory_Ack, in_msg.Address, tbe);
|
||||
} else {
|
||||
DPRINTF(RubySlicc,"%s\n", in_msg.Type);
|
||||
error("Invalid message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
|
||||
action(a_sendWriteBackAck, "a", desc="Send writeback ack to requestor") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:WB_ACK;
|
||||
out_msg.Requestor := in_msg.Requestor;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(l_sendWriteBackAck, "la", desc="Send writeback ack to requestor") {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:WB_ACK;
|
||||
out_msg.Requestor := in_msg.OriginalRequestorMachId;
|
||||
out_msg.Destination.add(in_msg.OriginalRequestorMachId);
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(b_sendWriteBackNack, "b", desc="Send writeback nack to requestor") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:WB_NACK;
|
||||
out_msg.Requestor := in_msg.Requestor;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(c_clearOwner, "c", desc="Clear the owner field") {
|
||||
getDirectoryEntry(address).Owner.clear();
|
||||
}
|
||||
|
||||
action(d_sendData, "d", desc="Send data to requestor") {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.Destination.add(in_msg.OriginalRequestorMachId);
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(dr_sendDMAData, "dr", desc="Send Data to DMA controller from directory") {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
enqueue(dmaResponseNetwork_out, DMAResponseMsg, latency="1") {
|
||||
assert(is_valid(tbe));
|
||||
out_msg.PhysicalAddress := address;
|
||||
out_msg.LineAddress := address;
|
||||
out_msg.Type := DMAResponseType:DATA;
|
||||
out_msg.DataBlk := in_msg.DataBlk; // we send the entire data block and rely on the dma controller to split it up if need be
|
||||
out_msg.Destination.add(tbe.DmaRequestor);
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
action(drp_sendDMAData, "drp", desc="Send Data to DMA controller from incoming PUTX") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(dmaResponseNetwork_out, DMAResponseMsg, latency="1") {
|
||||
assert(is_valid(tbe));
|
||||
out_msg.PhysicalAddress := address;
|
||||
out_msg.LineAddress := address;
|
||||
out_msg.Type := DMAResponseType:DATA;
|
||||
out_msg.DataBlk := in_msg.DataBlk; // we send the entire data block and rely on the dma controller to split it up if need be
|
||||
out_msg.Destination.add(tbe.DmaRequestor);
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(da_sendDMAAck, "da", desc="Send Ack to DMA controller") {
|
||||
enqueue(dmaResponseNetwork_out, DMAResponseMsg, latency="1") {
|
||||
assert(is_valid(tbe));
|
||||
out_msg.PhysicalAddress := address;
|
||||
out_msg.LineAddress := address;
|
||||
out_msg.Type := DMAResponseType:ACK;
|
||||
out_msg.Destination.add(tbe.DmaRequestor);
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
|
||||
action(e_ownerIsRequestor, "e", desc="The owner is now the requestor") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
getDirectoryEntry(address).Owner.clear();
|
||||
getDirectoryEntry(address).Owner.add(in_msg.Requestor);
|
||||
}
|
||||
}
|
||||
|
||||
action(f_forwardRequest, "f", desc="Forward request to owner") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
APPEND_TRANSITION_COMMENT("Own: ");
|
||||
APPEND_TRANSITION_COMMENT(getDirectoryEntry(in_msg.Address).Owner);
|
||||
APPEND_TRANSITION_COMMENT("Req: ");
|
||||
APPEND_TRANSITION_COMMENT(in_msg.Requestor);
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := in_msg.Type;
|
||||
out_msg.Requestor := in_msg.Requestor;
|
||||
out_msg.Destination := getDirectoryEntry(in_msg.Address).Owner;
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(inv_sendCacheInvalidate, "inv", desc="Invalidate a cache block") {
|
||||
peek(dmaRequestQueue_in, DMARequestMsg) {
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:INV;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.Destination := getDirectoryEntry(in_msg.PhysicalAddress).Owner;
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(i_popIncomingRequestQueue, "i", desc="Pop incoming request queue") {
|
||||
requestQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(p_popIncomingDMARequestQueue, "p", desc="Pop incoming DMA queue") {
|
||||
dmaRequestQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(l_writeDataToMemory, "pl", desc="Write PUTX data to memory") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
// assert(in_msg.Dirty);
|
||||
// assert(in_msg.MessageSize == MessageSizeType:Writeback_Data);
|
||||
getDirectoryEntry(in_msg.Address).DataBlk := in_msg.DataBlk;
|
||||
//getDirectoryEntry(in_msg.Address).DataBlk.copyPartial(in_msg.DataBlk, addressOffset(in_msg.Address), in_msg.Len);
|
||||
}
|
||||
}
|
||||
|
||||
action(dwt_writeDMADataFromTBE, "dwt", desc="DMA Write data to memory from TBE") {
|
||||
assert(is_valid(tbe));
|
||||
getDirectoryEntry(address).DataBlk.copyPartial(tbe.DataBlk, addressOffset(tbe.PhysicalAddress), tbe.Len);
|
||||
}
|
||||
|
||||
action(v_allocateTBE, "v", desc="Allocate TBE") {
|
||||
peek(dmaRequestQueue_in, DMARequestMsg) {
|
||||
TBEs.allocate(address);
|
||||
set_tbe(TBEs[address]);
|
||||
tbe.DataBlk := in_msg.DataBlk;
|
||||
tbe.PhysicalAddress := in_msg.PhysicalAddress;
|
||||
tbe.Len := in_msg.Len;
|
||||
tbe.DmaRequestor := in_msg.Requestor;
|
||||
}
|
||||
}
|
||||
|
||||
action(r_allocateTbeForDmaRead, "\r", desc="Allocate TBE for DMA Read") {
|
||||
peek(dmaRequestQueue_in, DMARequestMsg) {
|
||||
TBEs.allocate(address);
|
||||
set_tbe(TBEs[address]);
|
||||
tbe.DmaRequestor := in_msg.Requestor;
|
||||
}
|
||||
}
|
||||
|
||||
action(v_allocateTBEFromRequestNet, "\v", desc="Allocate TBE") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
TBEs.allocate(address);
|
||||
set_tbe(TBEs[address]);
|
||||
tbe.DataBlk := in_msg.DataBlk;
|
||||
}
|
||||
}
|
||||
|
||||
action(w_deallocateTBE, "w", desc="Deallocate TBE") {
|
||||
TBEs.deallocate(address);
|
||||
unset_tbe();
|
||||
}
|
||||
|
||||
action(z_recycleRequestQueue, "z", desc="recycle request queue") {
|
||||
requestQueue_in.recycle();
|
||||
}
|
||||
|
||||
action(y_recycleDMARequestQueue, "y", desc="recycle dma request queue") {
|
||||
dmaRequestQueue_in.recycle();
|
||||
}
|
||||
|
||||
|
||||
action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_READ;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.OriginalRequestorMachId := in_msg.Requestor;
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
out_msg.DataBlk := getDirectoryEntry(in_msg.Address).DataBlk;
|
||||
DPRINTF(RubySlicc,"%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(qf_queueMemoryFetchRequestDMA, "qfd", desc="Queue off-chip fetch request") {
|
||||
peek(dmaRequestQueue_in, DMARequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_READ;
|
||||
out_msg.Sender := machineID;
|
||||
//out_msg.OriginalRequestorMachId := machineID;
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
out_msg.DataBlk := getDirectoryEntry(address).DataBlk;
|
||||
DPRINTF(RubySlicc,"%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(qw_queueMemoryWBRequest_partial, "qwp", desc="Queue off-chip writeback request") {
|
||||
peek(dmaRequestQueue_in, DMARequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_WB;
|
||||
//out_msg.OriginalRequestorMachId := machineID;
|
||||
//out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.DataBlk.copyPartial(in_msg.DataBlk, addressOffset(in_msg.PhysicalAddress), in_msg.Len);
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
//out_msg.Prefetch := in_msg.Prefetch;
|
||||
|
||||
DPRINTF(RubySlicc,"%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(qw_queueMemoryWBRequest_partialTBE, "qwt", desc="Queue off-chip writeback request") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency="1") {
|
||||
assert(is_valid(tbe));
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_WB;
|
||||
out_msg.OriginalRequestorMachId := in_msg.Requestor;
|
||||
// get incoming data
|
||||
// out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.DataBlk.copyPartial(tbe.DataBlk, addressOffset(tbe.PhysicalAddress), tbe.Len);
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
//out_msg.Prefetch := in_msg.Prefetch;
|
||||
|
||||
DPRINTF(RubySlicc,"%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
action(l_queueMemoryWBRequest, "lq", desc="Write PUTX data to memory") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_WB;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.OriginalRequestorMachId := in_msg.Requestor;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
//out_msg.Prefetch := in_msg.Prefetch;
|
||||
|
||||
DPRINTF(RubySlicc,"%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(l_popMemQueue, "q", desc="Pop off-chip request queue") {
|
||||
memQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(w_writeDataToMemoryFromTBE, "\w", desc="Write date to directory memory from TBE") {
|
||||
//getDirectoryEntry(address).DataBlk := TBEs[address].DataBlk;
|
||||
assert(is_valid(tbe));
|
||||
getDirectoryEntry(address).DataBlk.copyPartial(tbe.DataBlk,
|
||||
addressOffset(tbe.PhysicalAddress),
|
||||
tbe.Len);
|
||||
|
||||
}
|
||||
|
||||
// TRANSITIONS
|
||||
|
||||
transition({M_DRD, M_DWR, M_DWRI, M_DRDI}, GETX) {
|
||||
z_recycleRequestQueue;
|
||||
}
|
||||
|
||||
transition({IM, MI, ID, ID_W}, {GETX, GETS, PUTX, PUTX_NotOwner} ) {
|
||||
z_recycleRequestQueue;
|
||||
}
|
||||
|
||||
transition({IM, MI, ID, ID_W}, {DMA_READ, DMA_WRITE} ) {
|
||||
y_recycleDMARequestQueue;
|
||||
}
|
||||
|
||||
|
||||
transition(I, GETX, IM) {
|
||||
//d_sendData;
|
||||
qf_queueMemoryFetchRequest;
|
||||
e_ownerIsRequestor;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(IM, Memory_Data, M) {
|
||||
d_sendData;
|
||||
//e_ownerIsRequestor;
|
||||
l_popMemQueue;
|
||||
}
|
||||
|
||||
|
||||
transition(I, DMA_READ, ID) {
|
||||
//dr_sendDMAData;
|
||||
r_allocateTbeForDmaRead;
|
||||
qf_queueMemoryFetchRequestDMA;
|
||||
p_popIncomingDMARequestQueue;
|
||||
}
|
||||
|
||||
transition(ID, Memory_Data, I) {
|
||||
dr_sendDMAData;
|
||||
//p_popIncomingDMARequestQueue;
|
||||
w_deallocateTBE;
|
||||
l_popMemQueue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
transition(I, DMA_WRITE, ID_W) {
|
||||
v_allocateTBE;
|
||||
qw_queueMemoryWBRequest_partial;
|
||||
p_popIncomingDMARequestQueue;
|
||||
}
|
||||
|
||||
transition(ID_W, Memory_Ack, I) {
|
||||
dwt_writeDMADataFromTBE;
|
||||
da_sendDMAAck;
|
||||
w_deallocateTBE;
|
||||
l_popMemQueue;
|
||||
}
|
||||
|
||||
transition(M, DMA_READ, M_DRD) {
|
||||
v_allocateTBE;
|
||||
inv_sendCacheInvalidate;
|
||||
p_popIncomingDMARequestQueue;
|
||||
}
|
||||
|
||||
transition(M_DRD, PUTX, M_DRDI) {
|
||||
l_writeDataToMemory;
|
||||
drp_sendDMAData;
|
||||
c_clearOwner;
|
||||
l_queueMemoryWBRequest;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(M_DRDI, Memory_Ack, I) {
|
||||
l_sendWriteBackAck;
|
||||
w_deallocateTBE;
|
||||
l_popMemQueue;
|
||||
}
|
||||
|
||||
|
||||
transition(M, DMA_WRITE, M_DWR) {
|
||||
v_allocateTBE;
|
||||
inv_sendCacheInvalidate;
|
||||
p_popIncomingDMARequestQueue;
|
||||
}
|
||||
|
||||
transition(M_DWR, PUTX, M_DWRI) {
|
||||
l_writeDataToMemory;
|
||||
qw_queueMemoryWBRequest_partialTBE;
|
||||
c_clearOwner;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(M_DWRI, Memory_Ack, I) {
|
||||
w_writeDataToMemoryFromTBE;
|
||||
l_sendWriteBackAck;
|
||||
da_sendDMAAck;
|
||||
w_deallocateTBE;
|
||||
l_popMemQueue;
|
||||
}
|
||||
|
||||
transition(M, GETX, M) {
|
||||
f_forwardRequest;
|
||||
e_ownerIsRequestor;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(M, PUTX, MI) {
|
||||
l_writeDataToMemory;
|
||||
c_clearOwner;
|
||||
v_allocateTBEFromRequestNet;
|
||||
l_queueMemoryWBRequest;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(MI, Memory_Ack, I) {
|
||||
w_writeDataToMemoryFromTBE;
|
||||
l_sendWriteBackAck;
|
||||
w_deallocateTBE;
|
||||
l_popMemQueue;
|
||||
}
|
||||
|
||||
transition(M, PUTX_NotOwner, M) {
|
||||
b_sendWriteBackNack;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(I, PUTX_NotOwner, I) {
|
||||
b_sendWriteBackNack;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
}
|
||||
143
simulators/gem5/src/mem/protocol/MI_example-dma.sm
Normal file
143
simulators/gem5/src/mem/protocol/MI_example-dma.sm
Normal file
@ -0,0 +1,143 @@
|
||||
|
||||
machine(DMA, "DMA Controller")
|
||||
: DMASequencer * dma_sequencer,
|
||||
int request_latency = 6
|
||||
{
|
||||
|
||||
MessageBuffer responseFromDir, network="From", virtual_network="1", ordered="true", vnet_type="response", no_vector="true";
|
||||
MessageBuffer reqToDirectory, network="To", virtual_network="0", ordered="false", vnet_type="request", no_vector="true";
|
||||
|
||||
state_declaration(State, desc="DMA states", default="DMA_State_READY") {
|
||||
READY, AccessPermission:Invalid, desc="Ready to accept a new request";
|
||||
BUSY_RD, AccessPermission:Busy, desc="Busy: currently processing a request";
|
||||
BUSY_WR, AccessPermission:Busy, desc="Busy: currently processing a request";
|
||||
}
|
||||
|
||||
enumeration(Event, desc="DMA events") {
|
||||
ReadRequest, desc="A new read request";
|
||||
WriteRequest, desc="A new write request";
|
||||
Data, desc="Data from a DMA memory read";
|
||||
Ack, desc="DMA write to memory completed";
|
||||
}
|
||||
|
||||
MessageBuffer mandatoryQueue, ordered="false", no_vector="true";
|
||||
State cur_state, no_vector="true";
|
||||
|
||||
State getState(Address addr) {
|
||||
return cur_state;
|
||||
}
|
||||
void setState(Address addr, State state) {
|
||||
cur_state := state;
|
||||
}
|
||||
|
||||
AccessPermission getAccessPermission(Address addr) {
|
||||
return AccessPermission:NotPresent;
|
||||
}
|
||||
|
||||
void setAccessPermission(Address addr, State state) {
|
||||
}
|
||||
|
||||
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
|
||||
error("DMA Controller does not support getDataBlock function.\n");
|
||||
}
|
||||
|
||||
out_port(reqToDirectory_out, DMARequestMsg, reqToDirectory, desc="...");
|
||||
|
||||
in_port(dmaRequestQueue_in, SequencerMsg, mandatoryQueue, desc="...") {
|
||||
if (dmaRequestQueue_in.isReady()) {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
if (in_msg.Type == SequencerRequestType:LD ) {
|
||||
trigger(Event:ReadRequest, in_msg.LineAddress);
|
||||
} else if (in_msg.Type == SequencerRequestType:ST) {
|
||||
trigger(Event:WriteRequest, in_msg.LineAddress);
|
||||
} else {
|
||||
error("Invalid request type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(dmaResponseQueue_in, DMAResponseMsg, responseFromDir, desc="...") {
|
||||
if (dmaResponseQueue_in.isReady()) {
|
||||
peek( dmaResponseQueue_in, DMAResponseMsg) {
|
||||
if (in_msg.Type == DMAResponseType:ACK) {
|
||||
trigger(Event:Ack, in_msg.LineAddress);
|
||||
} else if (in_msg.Type == DMAResponseType:DATA) {
|
||||
trigger(Event:Data, in_msg.LineAddress);
|
||||
} else {
|
||||
error("Invalid response type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(s_sendReadRequest, "s", desc="Send a DMA read request to memory") {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
enqueue(reqToDirectory_out, DMARequestMsg, latency=request_latency) {
|
||||
out_msg.PhysicalAddress := in_msg.PhysicalAddress;
|
||||
out_msg.LineAddress := in_msg.LineAddress;
|
||||
out_msg.Type := DMARequestType:READ;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Len := in_msg.Len;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(s_sendWriteRequest, "\s", desc="Send a DMA write request to memory") {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
enqueue(reqToDirectory_out, DMARequestMsg, latency=request_latency) {
|
||||
out_msg.PhysicalAddress := in_msg.PhysicalAddress;
|
||||
out_msg.LineAddress := in_msg.LineAddress;
|
||||
out_msg.Type := DMARequestType:WRITE;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Len := in_msg.Len;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(a_ackCallback, "a", desc="Notify dma controller that write request completed") {
|
||||
peek (dmaResponseQueue_in, DMAResponseMsg) {
|
||||
dma_sequencer.ackCallback();
|
||||
}
|
||||
}
|
||||
|
||||
action(d_dataCallback, "d", desc="Write data to dma sequencer") {
|
||||
peek (dmaResponseQueue_in, DMAResponseMsg) {
|
||||
dma_sequencer.dataCallback(in_msg.DataBlk);
|
||||
}
|
||||
}
|
||||
|
||||
action(p_popRequestQueue, "p", desc="Pop request queue") {
|
||||
dmaRequestQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(p_popResponseQueue, "\p", desc="Pop request queue") {
|
||||
dmaResponseQueue_in.dequeue();
|
||||
}
|
||||
|
||||
transition(READY, ReadRequest, BUSY_RD) {
|
||||
s_sendReadRequest;
|
||||
p_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(READY, WriteRequest, BUSY_WR) {
|
||||
s_sendWriteRequest;
|
||||
p_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_RD, Data, READY) {
|
||||
d_dataCallback;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_WR, Ack, READY) {
|
||||
a_ackCallback;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
}
|
||||
122
simulators/gem5/src/mem/protocol/MI_example-msg.sm
Normal file
122
simulators/gem5/src/mem/protocol/MI_example-msg.sm
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999-2005 Mark D. Hill and David A. Wood
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id: MOESI_SMP_directory-msg.sm 1.8 05/01/19 15:48:36-06:00 mikem@royal16.cs.wisc.edu $
|
||||
*
|
||||
*/
|
||||
|
||||
// CoherenceRequestType
|
||||
enumeration(CoherenceRequestType, desc="...") {
|
||||
GETX, desc="Get eXclusive";
|
||||
GETS, desc="Get Shared";
|
||||
PUTX, desc="Put eXclusive";
|
||||
PUTO, desc="Put Owned";
|
||||
WB_ACK, desc="Writeback ack";
|
||||
WB_NACK, desc="Writeback neg. ack";
|
||||
INV, desc="Invalidation";
|
||||
FWD, desc="Generic FWD";
|
||||
}
|
||||
|
||||
// CoherenceResponseType
|
||||
enumeration(CoherenceResponseType, desc="...") {
|
||||
ACK, desc="ACKnowledgment, responder doesn't have a copy";
|
||||
DATA, desc="Data";
|
||||
DATA_EXCLUSIVE_CLEAN, desc="Data, no other processor has a copy, data is clean";
|
||||
DATA_EXCLUSIVE_DIRTY, desc="Data, no other processor has a copy, data is dirty";
|
||||
UNBLOCK, desc="Unblock";
|
||||
UNBLOCK_EXCLUSIVE, desc="Unblock, we're in E/M";
|
||||
WRITEBACK_CLEAN, desc="Clean writeback (no data)";
|
||||
WRITEBACK_DIRTY, desc="Dirty writeback (contains data)";
|
||||
WRITEBACK, desc="Generic writeback (contains data)";
|
||||
}
|
||||
|
||||
// TriggerType
|
||||
enumeration(TriggerType, desc="...") {
|
||||
ALL_ACKS, desc="See corresponding event";
|
||||
}
|
||||
|
||||
// TriggerMsg
|
||||
structure(TriggerMsg, desc="...", interface="Message") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
TriggerType Type, desc="Type of trigger";
|
||||
}
|
||||
|
||||
// RequestMsg (and also forwarded requests)
|
||||
structure(RequestMsg, desc="...", interface="NetworkMessage") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)";
|
||||
MachineID Requestor, desc="Node who initiated the request";
|
||||
NetDest Destination, desc="Multicast destination mask";
|
||||
DataBlock DataBlk, desc="data for the cache line";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
}
|
||||
|
||||
// ResponseMsg (and also unblock requests)
|
||||
structure(ResponseMsg, desc="...", interface="NetworkMessage") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)";
|
||||
MachineID Sender, desc="Node who sent the data";
|
||||
NetDest Destination, desc="Node to whom the data is sent";
|
||||
DataBlock DataBlk, desc="data for the cache line";
|
||||
bool Dirty, desc="Is the data dirty (different than memory)?";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
}
|
||||
|
||||
enumeration(DMARequestType, desc="...", default="DMARequestType_NULL") {
|
||||
READ, desc="Memory Read";
|
||||
WRITE, desc="Memory Write";
|
||||
NULL, desc="Invalid";
|
||||
}
|
||||
|
||||
enumeration(DMAResponseType, desc="...", default="DMAResponseType_NULL") {
|
||||
DATA, desc="DATA read";
|
||||
ACK, desc="ACK write";
|
||||
NULL, desc="Invalid";
|
||||
}
|
||||
|
||||
structure(DMARequestMsg, desc="...", interface="NetworkMessage") {
|
||||
DMARequestType Type, desc="Request type (read/write)";
|
||||
Address PhysicalAddress, desc="Physical address for this request";
|
||||
Address LineAddress, desc="Line address for this request";
|
||||
MachineID Requestor, desc="Node who initiated the request";
|
||||
NetDest Destination, desc="Destination";
|
||||
DataBlock DataBlk, desc="DataBlk attached to this request";
|
||||
int Len, desc="The length of the request";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
}
|
||||
|
||||
structure(DMAResponseMsg, desc="...", interface="NetworkMessage") {
|
||||
DMAResponseType Type, desc="Response type (DATA/ACK)";
|
||||
Address PhysicalAddress, desc="Physical address for this request";
|
||||
Address LineAddress, desc="Line address for this request";
|
||||
NetDest Destination, desc="Destination";
|
||||
DataBlock DataBlk, desc="DataBlk attached to this request";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
}
|
||||
6
simulators/gem5/src/mem/protocol/MI_example.slicc
Normal file
6
simulators/gem5/src/mem/protocol/MI_example.slicc
Normal file
@ -0,0 +1,6 @@
|
||||
protocol "MI_example";
|
||||
include "RubySlicc_interfaces.slicc";
|
||||
include "MI_example-msg.sm";
|
||||
include "MI_example-cache.sm";
|
||||
include "MI_example-dir.sm";
|
||||
include "MI_example-dma.sm";
|
||||
1276
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-L1cache.sm
Normal file
1276
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-L1cache.sm
Normal file
File diff suppressed because it is too large
Load Diff
2768
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-L2cache.sm
Normal file
2768
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-L2cache.sm
Normal file
File diff suppressed because it is too large
Load Diff
923
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-dir.sm
Normal file
923
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-dir.sm
Normal file
@ -0,0 +1,923 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999-2005 Mark D. Hill and David A. Wood
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
machine(Directory, "Directory protocol")
|
||||
: DirectoryMemory * directory,
|
||||
MemoryControl * memBuffer,
|
||||
int directory_latency = 6
|
||||
{
|
||||
|
||||
// ** IN QUEUES **
|
||||
MessageBuffer foo1, network="From", virtual_network="0", ordered="false", vnet_type="foo"; // a mod-L2 bank -> this Dir
|
||||
MessageBuffer requestToDir, network="From", virtual_network="1", ordered="false", vnet_type="request"; // a mod-L2 bank -> this Dir
|
||||
MessageBuffer responseToDir, network="From", virtual_network="2", ordered="false", vnet_type="response"; // a mod-L2 bank -> this Dir
|
||||
|
||||
MessageBuffer goo1, network="To", virtual_network="0", ordered="false", vnet_type="goo";
|
||||
MessageBuffer forwardFromDir, network="To", virtual_network="1", ordered="false", vnet_type="forward";
|
||||
MessageBuffer responseFromDir, network="To", virtual_network="2", ordered="false", vnet_type="response"; // Dir -> mod-L2 bank
|
||||
|
||||
|
||||
// STATES
|
||||
state_declaration(State, desc="Directory states", default="Directory_State_I") {
|
||||
// Base states
|
||||
I, AccessPermission:Read_Write, desc="Invalid";
|
||||
S, AccessPermission:Read_Only, desc="Shared";
|
||||
O, AccessPermission:Maybe_Stale, desc="Owner";
|
||||
M, AccessPermission:Maybe_Stale, desc="Modified";
|
||||
|
||||
IS, AccessPermission:Busy, desc="Blocked, was in idle";
|
||||
SS, AccessPermission:Read_Only, desc="Blocked, was in shared";
|
||||
OO, AccessPermission:Busy, desc="Blocked, was in owned";
|
||||
MO, AccessPermission:Busy, desc="Blocked, going to owner or maybe modified";
|
||||
MM, AccessPermission:Busy, desc="Blocked, going to modified";
|
||||
MM_DMA, AccessPermission:Busy, desc="Blocked, going to I";
|
||||
|
||||
MI, AccessPermission:Busy, desc="Blocked on a writeback";
|
||||
MIS, AccessPermission:Busy, desc="Blocked on a writeback, but don't remove from sharers when received";
|
||||
OS, AccessPermission:Busy, desc="Blocked on a writeback";
|
||||
OSS, AccessPermission:Busy, desc="Blocked on a writeback, but don't remove from sharers when received";
|
||||
|
||||
XI_M, AccessPermission:Busy, desc="In a stable state, going to I, waiting for the memory controller";
|
||||
XI_U, AccessPermission:Busy, desc="In a stable state, going to I, waiting for an unblock";
|
||||
OI_D, AccessPermission:Busy, desc="In O, going to I, waiting for data";
|
||||
|
||||
OD, AccessPermission:Busy, desc="In O, waiting for dma ack from L2";
|
||||
MD, AccessPermission:Busy, desc="In M, waiting for dma ack from L2";
|
||||
}
|
||||
|
||||
// Events
|
||||
enumeration(Event, desc="Directory events") {
|
||||
GETX, desc="A GETX arrives";
|
||||
GETS, desc="A GETS arrives";
|
||||
PUTX, desc="A PUTX arrives";
|
||||
PUTO, desc="A PUTO arrives";
|
||||
PUTO_SHARERS, desc="A PUTO arrives, but don't remove from sharers list";
|
||||
Unblock, desc="An unblock message arrives";
|
||||
Last_Unblock, desc="An unblock message arrives, we're not waiting for any additional unblocks";
|
||||
Exclusive_Unblock, desc="The processor become the exclusive owner (E or M) of the line";
|
||||
Clean_Writeback, desc="The final message as part of a PutX/PutS, no data";
|
||||
Dirty_Writeback, desc="The final message as part of a PutX/PutS, contains data";
|
||||
Memory_Data, desc="Fetched data from memory arrives";
|
||||
Memory_Ack, desc="Writeback Ack from memory arrives";
|
||||
DMA_READ, desc="DMA Read";
|
||||
DMA_WRITE, desc="DMA Write";
|
||||
DMA_ACK, desc="DMA Ack";
|
||||
Data, desc="Data to directory";
|
||||
}
|
||||
|
||||
// TYPES
|
||||
|
||||
// DirectoryEntry
|
||||
structure(Entry, desc="...", interface='AbstractEntry') {
|
||||
State DirectoryState, desc="Directory state";
|
||||
DataBlock DataBlk, desc="data for the block";
|
||||
NetDest Sharers, desc="Sharers for this block";
|
||||
NetDest Owner, desc="Owner of this block";
|
||||
int WaitingUnblocks, desc="Number of acks we're waiting for";
|
||||
}
|
||||
|
||||
structure(TBE, desc="...") {
|
||||
Address PhysicalAddress, desc="Physical address for this entry";
|
||||
int Len, desc="Length of request";
|
||||
DataBlock DataBlk, desc="DataBlk";
|
||||
MachineID Requestor, desc="original requestor";
|
||||
}
|
||||
|
||||
structure(TBETable, external = "yes") {
|
||||
TBE lookup(Address);
|
||||
void allocate(Address);
|
||||
void deallocate(Address);
|
||||
bool isPresent(Address);
|
||||
}
|
||||
|
||||
// ** OBJECTS **
|
||||
TBETable TBEs, template_hack="<Directory_TBE>";
|
||||
|
||||
void set_tbe(TBE b);
|
||||
void unset_tbe();
|
||||
|
||||
Entry getDirectoryEntry(Address addr), return_by_pointer="yes" {
|
||||
Entry dir_entry := static_cast(Entry, "pointer", directory[addr]);
|
||||
|
||||
if (is_valid(dir_entry)) {
|
||||
return dir_entry;
|
||||
}
|
||||
|
||||
dir_entry := static_cast(Entry, "pointer",
|
||||
directory.allocate(addr, new Entry));
|
||||
return dir_entry;
|
||||
}
|
||||
|
||||
State getState(TBE tbe, Address addr) {
|
||||
return getDirectoryEntry(addr).DirectoryState;
|
||||
}
|
||||
|
||||
void setState(TBE tbe, Address addr, State state) {
|
||||
if (directory.isPresent(addr)) {
|
||||
|
||||
if (state == State:I) {
|
||||
assert(getDirectoryEntry(addr).Owner.count() == 0);
|
||||
assert(getDirectoryEntry(addr).Sharers.count() == 0);
|
||||
}
|
||||
|
||||
if (state == State:S) {
|
||||
assert(getDirectoryEntry(addr).Owner.count() == 0);
|
||||
}
|
||||
|
||||
if (state == State:O) {
|
||||
assert(getDirectoryEntry(addr).Owner.count() == 1);
|
||||
assert(getDirectoryEntry(addr).Sharers.isSuperset(getDirectoryEntry(addr).Owner) == false);
|
||||
}
|
||||
|
||||
if (state == State:M) {
|
||||
assert(getDirectoryEntry(addr).Owner.count() == 1);
|
||||
assert(getDirectoryEntry(addr).Sharers.count() == 0);
|
||||
}
|
||||
|
||||
if ((state != State:SS) && (state != State:OO)) {
|
||||
assert(getDirectoryEntry(addr).WaitingUnblocks == 0);
|
||||
}
|
||||
|
||||
if ( (getDirectoryEntry(addr).DirectoryState != State:I) && (state == State:I) ) {
|
||||
getDirectoryEntry(addr).DirectoryState := state;
|
||||
// disable coherence checker
|
||||
// sequencer.checkCoherence(addr);
|
||||
}
|
||||
else {
|
||||
getDirectoryEntry(addr).DirectoryState := state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AccessPermission getAccessPermission(Address addr) {
|
||||
if (directory.isPresent(addr)) {
|
||||
DPRINTF(RubySlicc, "%s\n", Directory_State_to_permission(getDirectoryEntry(addr).DirectoryState));
|
||||
return Directory_State_to_permission(getDirectoryEntry(addr).DirectoryState);
|
||||
}
|
||||
|
||||
DPRINTF(RubySlicc, "AccessPermission_NotPresent\n");
|
||||
return AccessPermission:NotPresent;
|
||||
}
|
||||
|
||||
void setAccessPermission(Address addr, State state) {
|
||||
if (directory.isPresent(addr)) {
|
||||
getDirectoryEntry(addr).changePermission(Directory_State_to_permission(state));
|
||||
}
|
||||
}
|
||||
|
||||
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
|
||||
return getDirectoryEntry(addr).DataBlk;
|
||||
}
|
||||
|
||||
// if no sharers, then directory can be considered both a sharer and exclusive w.r.t. coherence checking
|
||||
bool isBlockShared(Address addr) {
|
||||
if (directory.isPresent(addr)) {
|
||||
if (getDirectoryEntry(addr).DirectoryState == State:I) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isBlockExclusive(Address addr) {
|
||||
if (directory.isPresent(addr)) {
|
||||
if (getDirectoryEntry(addr).DirectoryState == State:I) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// ** OUT_PORTS **
|
||||
out_port(forwardNetwork_out, RequestMsg, forwardFromDir);
|
||||
out_port(responseNetwork_out, ResponseMsg, responseFromDir);
|
||||
// out_port(requestQueue_out, ResponseMsg, requestFromDir); // For recycling requests
|
||||
out_port(goo1_out, ResponseMsg, goo1);
|
||||
out_port(memQueue_out, MemoryMsg, memBuffer);
|
||||
|
||||
// ** IN_PORTS **
|
||||
|
||||
in_port(foo1_in, ResponseMsg, foo1) {
|
||||
|
||||
}
|
||||
|
||||
// in_port(unblockNetwork_in, ResponseMsg, unblockToDir) {
|
||||
// if (unblockNetwork_in.isReady()) {
|
||||
in_port(unblockNetwork_in, ResponseMsg, responseToDir) {
|
||||
if (unblockNetwork_in.isReady()) {
|
||||
peek(unblockNetwork_in, ResponseMsg) {
|
||||
if (in_msg.Type == CoherenceResponseType:UNBLOCK) {
|
||||
if (getDirectoryEntry(in_msg.Address).WaitingUnblocks == 1) {
|
||||
trigger(Event:Last_Unblock, in_msg.Address,
|
||||
TBEs[in_msg.Address]);
|
||||
} else {
|
||||
trigger(Event:Unblock, in_msg.Address,
|
||||
TBEs[in_msg.Address]);
|
||||
}
|
||||
} else if (in_msg.Type == CoherenceResponseType:UNBLOCK_EXCLUSIVE) {
|
||||
trigger(Event:Exclusive_Unblock, in_msg.Address,
|
||||
TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceResponseType:WRITEBACK_DIRTY_DATA) {
|
||||
trigger(Event:Dirty_Writeback, in_msg.Address,
|
||||
TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceResponseType:WRITEBACK_CLEAN_ACK) {
|
||||
trigger(Event:Clean_Writeback, in_msg.Address,
|
||||
TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE) {
|
||||
trigger(Event:Data, in_msg.Address,
|
||||
TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceResponseType:DMA_ACK) {
|
||||
trigger(Event:DMA_ACK, in_msg.Address,
|
||||
TBEs[in_msg.Address]);
|
||||
} else {
|
||||
error("Invalid message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(requestQueue_in, RequestMsg, requestToDir) {
|
||||
if (requestQueue_in.isReady()) {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
if (in_msg.Type == CoherenceRequestType:GETS) {
|
||||
trigger(Event:GETS, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceRequestType:GETX) {
|
||||
trigger(Event:GETX, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceRequestType:PUTX) {
|
||||
trigger(Event:PUTX, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceRequestType:PUTO) {
|
||||
trigger(Event:PUTO, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceRequestType:PUTO_SHARERS) {
|
||||
trigger(Event:PUTO_SHARERS, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == CoherenceRequestType:DMA_READ) {
|
||||
trigger(Event:DMA_READ, makeLineAddress(in_msg.Address),
|
||||
TBEs[makeLineAddress(in_msg.Address)]);
|
||||
} else if (in_msg.Type == CoherenceRequestType:DMA_WRITE) {
|
||||
trigger(Event:DMA_WRITE, makeLineAddress(in_msg.Address),
|
||||
TBEs[makeLineAddress(in_msg.Address)]);
|
||||
} else {
|
||||
error("Invalid message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// off-chip memory request/response is done
|
||||
in_port(memQueue_in, MemoryMsg, memBuffer) {
|
||||
if (memQueue_in.isReady()) {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
if (in_msg.Type == MemoryRequestType:MEMORY_READ) {
|
||||
trigger(Event:Memory_Data, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else if (in_msg.Type == MemoryRequestType:MEMORY_WB) {
|
||||
trigger(Event:Memory_Ack, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else {
|
||||
DPRINTF(RubySlicc, "%s\n", in_msg.Type);
|
||||
error("Invalid message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
|
||||
action(a_sendWriteBackAck, "a", desc="Send writeback ack to requestor") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:WB_ACK;
|
||||
out_msg.Requestor := in_msg.Requestor;
|
||||
out_msg.RequestorMachine := MachineType:Directory;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(b_sendWriteBackNack, "b", desc="Send writeback nack to requestor") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:WB_NACK;
|
||||
out_msg.Requestor := in_msg.Requestor;
|
||||
out_msg.RequestorMachine := MachineType:Directory;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(c_clearOwner, "c", desc="Clear the owner field") {
|
||||
getDirectoryEntry(address).Owner.clear();
|
||||
}
|
||||
|
||||
action(c_moveOwnerToSharer, "cc", desc="Move owner to sharers") {
|
||||
getDirectoryEntry(address).Sharers.addNetDest(getDirectoryEntry(address).Owner);
|
||||
getDirectoryEntry(address).Owner.clear();
|
||||
}
|
||||
|
||||
action(cc_clearSharers, "\c", desc="Clear the sharers field") {
|
||||
getDirectoryEntry(address).Sharers.clear();
|
||||
}
|
||||
|
||||
action(d_sendDataMsg, "d", desc="Send data to requestor") {
|
||||
peek(memQueue_in, MemoryMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.SenderMachine := MachineType:Directory;
|
||||
out_msg.Destination.add(in_msg.OriginalRequestorMachId);
|
||||
//out_msg.DataBlk := getDirectoryEntry(in_msg.Address).DataBlk;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Dirty := false; // By definition, the block is now clean
|
||||
out_msg.Acks := in_msg.Acks;
|
||||
if (in_msg.ReadX) {
|
||||
out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE;
|
||||
} else {
|
||||
out_msg.Type := CoherenceResponseType:DATA;
|
||||
}
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(p_fwdDataToDMA, "\d", desc="Send data to requestor") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.SenderMachine := MachineType:Directory;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.DataBlk := getDirectoryEntry(in_msg.Address).DataBlk;
|
||||
out_msg.Dirty := false; // By definition, the block is now clean
|
||||
out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE;
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
action(e_ownerIsUnblocker, "e", desc="The owner is now the unblocker") {
|
||||
peek(unblockNetwork_in, ResponseMsg) {
|
||||
getDirectoryEntry(address).Owner.clear();
|
||||
getDirectoryEntry(address).Owner.add(in_msg.Sender);
|
||||
}
|
||||
}
|
||||
|
||||
action(f_forwardRequest, "f", desc="Forward request to owner") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := in_msg.Type;
|
||||
out_msg.Requestor := in_msg.Requestor;
|
||||
out_msg.RequestorMachine := machineIDToMachineType(in_msg.Requestor);
|
||||
out_msg.Destination.addNetDest(getDirectoryEntry(in_msg.Address).Owner);
|
||||
out_msg.Acks := getDirectoryEntry(address).Sharers.count();
|
||||
if (getDirectoryEntry(address).Sharers.isElement(in_msg.Requestor)) {
|
||||
out_msg.Acks := out_msg.Acks - 1;
|
||||
}
|
||||
out_msg.MessageSize := MessageSizeType:Forwarded_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(f_forwardRequestDirIsRequestor, "\f", desc="Forward request to owner") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := in_msg.Type;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.RequestorMachine := machineIDToMachineType(in_msg.Requestor);
|
||||
out_msg.Destination.addNetDest(getDirectoryEntry(in_msg.Address).Owner);
|
||||
out_msg.Acks := getDirectoryEntry(address).Sharers.count();
|
||||
if (getDirectoryEntry(address).Sharers.isElement(in_msg.Requestor)) {
|
||||
out_msg.Acks := out_msg.Acks - 1;
|
||||
}
|
||||
out_msg.MessageSize := MessageSizeType:Forwarded_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(g_sendInvalidations, "g", desc="Send invalidations to sharers, not including the requester") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
if ((getDirectoryEntry(in_msg.Address).Sharers.count() > 1) ||
|
||||
((getDirectoryEntry(in_msg.Address).Sharers.count() > 0) && (getDirectoryEntry(in_msg.Address).Sharers.isElement(in_msg.Requestor) == false))) {
|
||||
enqueue(forwardNetwork_out, RequestMsg, latency=directory_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceRequestType:INV;
|
||||
out_msg.Requestor := in_msg.Requestor;
|
||||
out_msg.RequestorMachine := machineIDToMachineType(in_msg.Requestor);
|
||||
// out_msg.Destination := getDirectoryEntry(in_msg.Address).Sharers;
|
||||
out_msg.Destination.addNetDest(getDirectoryEntry(in_msg.Address).Sharers);
|
||||
out_msg.Destination.remove(in_msg.Requestor);
|
||||
out_msg.MessageSize := MessageSizeType:Invalidate_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(i_popIncomingRequestQueue, "i", desc="Pop incoming request queue") {
|
||||
requestQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(j_popIncomingUnblockQueue, "j", desc="Pop incoming unblock queue") {
|
||||
unblockNetwork_in.dequeue();
|
||||
}
|
||||
|
||||
action(l_writeDataToMemory, "l", desc="Write PUTX/PUTO data to memory") {
|
||||
peek(unblockNetwork_in, ResponseMsg) {
|
||||
assert(in_msg.Dirty);
|
||||
assert(in_msg.MessageSize == MessageSizeType:Writeback_Data);
|
||||
getDirectoryEntry(in_msg.Address).DataBlk := in_msg.DataBlk;
|
||||
DPRINTF(RubySlicc, "Address: %s, Data Block: %s\n",
|
||||
in_msg.Address, in_msg.DataBlk);
|
||||
}
|
||||
}
|
||||
|
||||
action(p_writeFwdDataToMemory, "p", desc="Write Response data to memory") {
|
||||
peek(unblockNetwork_in, ResponseMsg) {
|
||||
getDirectoryEntry(in_msg.Address).DataBlk := in_msg.DataBlk;
|
||||
DPRINTF(RubySlicc, "Address: %s, Data Block: %s\n",
|
||||
in_msg.Address, in_msg.DataBlk);
|
||||
}
|
||||
}
|
||||
|
||||
action(ll_checkDataInMemory, "\ld", desc="Check PUTX/PUTO data is same as in the memory") {
|
||||
peek(unblockNetwork_in, ResponseMsg) {
|
||||
assert(in_msg.Dirty == false);
|
||||
assert(in_msg.MessageSize == MessageSizeType:Writeback_Control);
|
||||
|
||||
// NOTE: The following check would not be valid in a real
|
||||
// implementation. We include the data in the "dataless"
|
||||
// message so we can assert the clean data matches the datablock
|
||||
// in memory
|
||||
assert(getDirectoryEntry(in_msg.Address).DataBlk == in_msg.DataBlk);
|
||||
}
|
||||
}
|
||||
|
||||
action(m_addUnlockerToSharers, "m", desc="Add the unlocker to the sharer list") {
|
||||
peek(unblockNetwork_in, ResponseMsg) {
|
||||
getDirectoryEntry(address).Sharers.add(in_msg.Sender);
|
||||
}
|
||||
}
|
||||
|
||||
action(n_incrementOutstanding, "n", desc="Increment outstanding requests") {
|
||||
getDirectoryEntry(address).WaitingUnblocks := getDirectoryEntry(address).WaitingUnblocks + 1;
|
||||
}
|
||||
|
||||
action(o_decrementOutstanding, "o", desc="Decrement outstanding requests") {
|
||||
getDirectoryEntry(address).WaitingUnblocks := getDirectoryEntry(address).WaitingUnblocks - 1;
|
||||
assert(getDirectoryEntry(address).WaitingUnblocks >= 0);
|
||||
}
|
||||
|
||||
action(q_popMemQueue, "q", desc="Pop off-chip request queue") {
|
||||
memQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_READ;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.OriginalRequestorMachId := in_msg.Requestor;
|
||||
out_msg.DataBlk := getDirectoryEntry(in_msg.Address).DataBlk;
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
//out_msg.Prefetch := false;
|
||||
// These are not used by memory but are passed back here with the read data:
|
||||
out_msg.ReadX := (in_msg.Type == CoherenceRequestType:GETS && getDirectoryEntry(address).Sharers.count() == 0);
|
||||
out_msg.Acks := getDirectoryEntry(address).Sharers.count();
|
||||
if (getDirectoryEntry(address).Sharers.isElement(in_msg.Requestor)) {
|
||||
out_msg.Acks := out_msg.Acks - 1;
|
||||
}
|
||||
DPRINTF(RubySlicc, "%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(qw_queueMemoryWBRequest, "qw", desc="Queue off-chip writeback request") {
|
||||
peek(unblockNetwork_in, ResponseMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_WB;
|
||||
out_msg.Sender := machineID;
|
||||
if (is_valid(tbe)) {
|
||||
out_msg.OriginalRequestorMachId := tbe.Requestor;
|
||||
}
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
//out_msg.Prefetch := false;
|
||||
// Not used:
|
||||
out_msg.ReadX := false;
|
||||
out_msg.Acks := getDirectoryEntry(address).Sharers.count(); // for dma requests
|
||||
DPRINTF(RubySlicc, "%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(qw_queueMemoryWBRequest2, "/qw", desc="Queue off-chip writeback request") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(memQueue_out, MemoryMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := MemoryRequestType:MEMORY_WB;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.OriginalRequestorMachId := in_msg.Requestor;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.MessageSize := in_msg.MessageSize;
|
||||
//out_msg.Prefetch := false;
|
||||
// Not used:
|
||||
out_msg.ReadX := false;
|
||||
out_msg.Acks := getDirectoryEntry(address).Sharers.count(); // for dma requests
|
||||
DPRINTF(RubySlicc, "%s\n", out_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// action(z_stall, "z", desc="Cannot be handled right now.") {
|
||||
// Special name recognized as do nothing case
|
||||
// }
|
||||
|
||||
action(zz_recycleRequest, "\z", desc="Recycle the request queue") {
|
||||
requestQueue_in.recycle();
|
||||
}
|
||||
|
||||
action(a_sendDMAAck, "\a", desc="Send DMA Ack that write completed, along with Inv Ack count") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.SenderMachine := MachineType:Directory;
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Acks := getDirectoryEntry(address).Sharers.count(); // for dma requests
|
||||
out_msg.Type := CoherenceResponseType:DMA_ACK;
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(a_sendDMAAck2, "\aa", desc="Send DMA Ack that write completed, along with Inv Ack count") {
|
||||
peek(unblockNetwork_in, ResponseMsg) {
|
||||
enqueue(responseNetwork_out, ResponseMsg, latency="1") {
|
||||
out_msg.Address := address;
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.SenderMachine := MachineType:Directory;
|
||||
if (is_valid(tbe)) {
|
||||
out_msg.Destination.add(tbe.Requestor);
|
||||
}
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Acks := getDirectoryEntry(address).Sharers.count(); // for dma requests
|
||||
out_msg.Type := CoherenceResponseType:DMA_ACK;
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(l_writeDMADataToMemory, "\l", desc="Write data from a DMA_WRITE to memory") {
|
||||
peek(requestQueue_in, RequestMsg) {
|
||||
getDirectoryEntry(address).DataBlk.copyPartial(in_msg.DataBlk, addressOffset(in_msg.Address), in_msg.Len);
|
||||
}
|
||||
}
|
||||
|
||||
action(l_writeDMADataToMemoryFromTBE, "\ll", desc="Write data from a DMA_WRITE to memory") {
|
||||
assert(is_valid(tbe));
|
||||
getDirectoryEntry(address).DataBlk.copyPartial(tbe.DataBlk,
|
||||
addressOffset(tbe.PhysicalAddress), tbe.Len);
|
||||
}
|
||||
|
||||
action(v_allocateTBE, "v", desc="Allocate TBE entry") {
|
||||
peek (requestQueue_in, RequestMsg) {
|
||||
TBEs.allocate(address);
|
||||
set_tbe(TBEs[address]);
|
||||
tbe.PhysicalAddress := in_msg.Address;
|
||||
tbe.Len := in_msg.Len;
|
||||
tbe.DataBlk := in_msg.DataBlk;
|
||||
tbe.Requestor := in_msg.Requestor;
|
||||
}
|
||||
}
|
||||
|
||||
action(w_deallocateTBE, "w", desc="Deallocate TBE entry") {
|
||||
TBEs.deallocate(address);
|
||||
unset_tbe();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TRANSITIONS
|
||||
|
||||
transition(I, GETX, MM) {
|
||||
qf_queueMemoryFetchRequest;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(I, DMA_READ, XI_M) {
|
||||
qf_queueMemoryFetchRequest;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(I, DMA_WRITE, XI_U) {
|
||||
qw_queueMemoryWBRequest2;
|
||||
a_sendDMAAck; // ack count may be zero
|
||||
l_writeDMADataToMemory;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(XI_M, Memory_Data, I) {
|
||||
d_sendDataMsg; // ack count may be zero
|
||||
q_popMemQueue;
|
||||
}
|
||||
|
||||
transition(XI_U, Exclusive_Unblock, I) {
|
||||
cc_clearSharers;
|
||||
c_clearOwner;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(S, GETX, MM) {
|
||||
qf_queueMemoryFetchRequest;
|
||||
g_sendInvalidations;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(S, DMA_READ) {
|
||||
//qf_queueMemoryFetchRequest;
|
||||
p_fwdDataToDMA;
|
||||
//g_sendInvalidations; // the DMA will collect the invalidations then send an Unblock Exclusive
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(S, DMA_WRITE, XI_U) {
|
||||
qw_queueMemoryWBRequest2;
|
||||
a_sendDMAAck; // ack count may be zero
|
||||
l_writeDMADataToMemory;
|
||||
g_sendInvalidations; // the DMA will collect invalidations
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(I, GETS, IS) {
|
||||
qf_queueMemoryFetchRequest;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition({S, SS}, GETS, SS) {
|
||||
qf_queueMemoryFetchRequest;
|
||||
n_incrementOutstanding;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition({I, S}, PUTO) {
|
||||
b_sendWriteBackNack;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition({I, S, O}, PUTX) {
|
||||
b_sendWriteBackNack;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(O, GETX, MM) {
|
||||
f_forwardRequest;
|
||||
g_sendInvalidations;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(O, DMA_READ, OD) {
|
||||
f_forwardRequest; // this will cause the data to go to DMA directly
|
||||
//g_sendInvalidations; // this will cause acks to be sent to the DMA
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(OD, DMA_ACK, O) {
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition({O,M}, DMA_WRITE, OI_D) {
|
||||
f_forwardRequestDirIsRequestor; // need the modified data before we can proceed
|
||||
g_sendInvalidations; // these go to the DMA Controller
|
||||
v_allocateTBE;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(OI_D, Data, XI_U) {
|
||||
qw_queueMemoryWBRequest;
|
||||
a_sendDMAAck2; // ack count may be zero
|
||||
p_writeFwdDataToMemory;
|
||||
l_writeDMADataToMemoryFromTBE;
|
||||
w_deallocateTBE;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition({O, OO}, GETS, OO) {
|
||||
f_forwardRequest;
|
||||
n_incrementOutstanding;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(M, GETX, MM) {
|
||||
f_forwardRequest;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
// no exclusive unblock will show up to the directory
|
||||
transition(M, DMA_READ, MD) {
|
||||
f_forwardRequest; // this will cause the data to go to DMA directly
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(MD, DMA_ACK, M) {
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(M, GETS, MO) {
|
||||
f_forwardRequest;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(M, PUTX, MI) {
|
||||
a_sendWriteBackAck;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
// happens if M->O transition happens on-chip
|
||||
transition(M, PUTO, MI) {
|
||||
a_sendWriteBackAck;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(M, PUTO_SHARERS, MIS) {
|
||||
a_sendWriteBackAck;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(O, PUTO, OS) {
|
||||
a_sendWriteBackAck;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
transition(O, PUTO_SHARERS, OSS) {
|
||||
a_sendWriteBackAck;
|
||||
i_popIncomingRequestQueue;
|
||||
}
|
||||
|
||||
|
||||
transition({MM, MO, MI, MIS, OS, OSS, XI_M, XI_U, OI_D, OD, MD}, {GETS, GETX, PUTO, PUTO_SHARERS, PUTX, DMA_READ, DMA_WRITE}) {
|
||||
zz_recycleRequest;
|
||||
}
|
||||
|
||||
transition({MM, MO}, Exclusive_Unblock, M) {
|
||||
cc_clearSharers;
|
||||
e_ownerIsUnblocker;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(MO, Unblock, O) {
|
||||
m_addUnlockerToSharers;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition({IS, SS, OO}, {GETX, PUTO, PUTO_SHARERS, PUTX, DMA_READ, DMA_WRITE}) {
|
||||
zz_recycleRequest;
|
||||
}
|
||||
|
||||
transition(IS, GETS) {
|
||||
zz_recycleRequest;
|
||||
}
|
||||
|
||||
transition(IS, Unblock, S) {
|
||||
m_addUnlockerToSharers;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(IS, Exclusive_Unblock, M) {
|
||||
cc_clearSharers;
|
||||
e_ownerIsUnblocker;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(SS, Unblock) {
|
||||
m_addUnlockerToSharers;
|
||||
o_decrementOutstanding;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(SS, Last_Unblock, S) {
|
||||
m_addUnlockerToSharers;
|
||||
o_decrementOutstanding;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(OO, Unblock) {
|
||||
m_addUnlockerToSharers;
|
||||
o_decrementOutstanding;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(OO, Last_Unblock, O) {
|
||||
m_addUnlockerToSharers;
|
||||
o_decrementOutstanding;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(MI, Dirty_Writeback, I) {
|
||||
c_clearOwner;
|
||||
cc_clearSharers;
|
||||
l_writeDataToMemory;
|
||||
qw_queueMemoryWBRequest;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(MIS, Dirty_Writeback, S) {
|
||||
c_moveOwnerToSharer;
|
||||
l_writeDataToMemory;
|
||||
qw_queueMemoryWBRequest;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(MIS, Clean_Writeback, S) {
|
||||
c_moveOwnerToSharer;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(OS, Dirty_Writeback, S) {
|
||||
c_clearOwner;
|
||||
l_writeDataToMemory;
|
||||
qw_queueMemoryWBRequest;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(OSS, Dirty_Writeback, S) {
|
||||
c_moveOwnerToSharer;
|
||||
l_writeDataToMemory;
|
||||
qw_queueMemoryWBRequest;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(OSS, Clean_Writeback, S) {
|
||||
c_moveOwnerToSharer;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(MI, Clean_Writeback, I) {
|
||||
c_clearOwner;
|
||||
cc_clearSharers;
|
||||
ll_checkDataInMemory;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition(OS, Clean_Writeback, S) {
|
||||
c_clearOwner;
|
||||
ll_checkDataInMemory;
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition({MI, MIS}, Unblock, M) {
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition({OS, OSS}, Unblock, O) {
|
||||
j_popIncomingUnblockQueue;
|
||||
}
|
||||
|
||||
transition({I, S, O, M, IS, SS, OO, MO, MM, MI, MIS, OS, OSS}, Memory_Data) {
|
||||
d_sendDataMsg;
|
||||
q_popMemQueue;
|
||||
}
|
||||
|
||||
transition({I, S, O, M, IS, SS, OO, MO, MM, MI, MIS, OS, OSS, XI_U, XI_M}, Memory_Ack) {
|
||||
//a_sendAck;
|
||||
q_popMemQueue;
|
||||
}
|
||||
|
||||
}
|
||||
299
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-dma.sm
Normal file
299
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-dma.sm
Normal file
@ -0,0 +1,299 @@
|
||||
|
||||
machine(DMA, "DMA Controller")
|
||||
: DMASequencer * dma_sequencer,
|
||||
int request_latency = 14,
|
||||
int response_latency = 14
|
||||
{
|
||||
|
||||
MessageBuffer goo1, network="From", virtual_network="0", ordered="false", vnet_type="goo";
|
||||
MessageBuffer goo2, network="From", virtual_network="1", ordered="false", vnet_type="goo";
|
||||
MessageBuffer responseFromDir, network="From", virtual_network="2", ordered="false", vnet_type="response";
|
||||
|
||||
MessageBuffer foo1, network="To", virtual_network="0", ordered="false", vnet_type="foo";
|
||||
MessageBuffer reqToDir, network="To", virtual_network="1", ordered="false", vnet_type="request";
|
||||
MessageBuffer respToDir, network="To", virtual_network="2", ordered="false", vnet_type="dmaresponse";
|
||||
|
||||
state_declaration(State, desc="DMA states", default="DMA_State_READY") {
|
||||
READY, AccessPermission:Invalid, desc="Ready to accept a new request";
|
||||
BUSY_RD, AccessPermission:Busy, desc="Busy: currently processing a request";
|
||||
BUSY_WR, AccessPermission:Busy, desc="Busy: currently processing a request";
|
||||
}
|
||||
|
||||
enumeration(Event, desc="DMA events") {
|
||||
ReadRequest, desc="A new read request";
|
||||
WriteRequest, desc="A new write request";
|
||||
Data, desc="Data from a DMA memory read";
|
||||
DMA_Ack, desc="DMA write to memory completed";
|
||||
Inv_Ack, desc="Invalidation Ack from a sharer";
|
||||
All_Acks, desc="All acks received";
|
||||
}
|
||||
|
||||
structure(TBE, desc="...") {
|
||||
Address address, desc="Physical address";
|
||||
int NumAcks, default="0", desc="Number of Acks pending";
|
||||
DataBlock DataBlk, desc="Data";
|
||||
}
|
||||
|
||||
structure(DMASequencer, external = "yes") {
|
||||
void ackCallback();
|
||||
void dataCallback(DataBlock);
|
||||
}
|
||||
|
||||
structure(TBETable, external = "yes") {
|
||||
TBE lookup(Address);
|
||||
void allocate(Address);
|
||||
void deallocate(Address);
|
||||
bool isPresent(Address);
|
||||
}
|
||||
|
||||
MessageBuffer mandatoryQueue, ordered="false";
|
||||
MessageBuffer triggerQueue, ordered="true";
|
||||
TBETable TBEs, template_hack="<DMA_TBE>";
|
||||
State cur_state;
|
||||
|
||||
void set_tbe(TBE b);
|
||||
void unset_tbe();
|
||||
|
||||
State getState(TBE tbe, Address addr) {
|
||||
return cur_state;
|
||||
}
|
||||
void setState(TBE tbe, Address addr, State state) {
|
||||
cur_state := state;
|
||||
}
|
||||
|
||||
AccessPermission getAccessPermission(Address addr) {
|
||||
return AccessPermission:NotPresent;
|
||||
}
|
||||
|
||||
void setAccessPermission(Address addr, State state) {
|
||||
}
|
||||
|
||||
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
|
||||
error("DMA Controller does not support getDataBlock().\n");
|
||||
}
|
||||
|
||||
out_port(reqToDirectory_out, RequestMsg, reqToDir, desc="...");
|
||||
out_port(respToDirectory_out, ResponseMsg, respToDir, desc="...");
|
||||
out_port(foo1_out, ResponseMsg, foo1, desc="...");
|
||||
out_port(triggerQueue_out, TriggerMsg, triggerQueue, desc="...");
|
||||
|
||||
in_port(goo1_in, RequestMsg, goo1) {
|
||||
if (goo1_in.isReady()) {
|
||||
peek(goo1_in, RequestMsg) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(goo2_in, RequestMsg, goo2) {
|
||||
if (goo2_in.isReady()) {
|
||||
peek(goo2_in, RequestMsg) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(dmaRequestQueue_in, SequencerMsg, mandatoryQueue, desc="...") {
|
||||
if (dmaRequestQueue_in.isReady()) {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
if (in_msg.Type == SequencerRequestType:LD ) {
|
||||
trigger(Event:ReadRequest, in_msg.LineAddress,
|
||||
TBEs[in_msg.LineAddress]);
|
||||
} else if (in_msg.Type == SequencerRequestType:ST) {
|
||||
trigger(Event:WriteRequest, in_msg.LineAddress,
|
||||
TBEs[in_msg.LineAddress]);
|
||||
} else {
|
||||
error("Invalid request type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(dmaResponseQueue_in, ResponseMsg, responseFromDir, desc="...") {
|
||||
if (dmaResponseQueue_in.isReady()) {
|
||||
peek( dmaResponseQueue_in, ResponseMsg) {
|
||||
if (in_msg.Type == CoherenceResponseType:DMA_ACK) {
|
||||
trigger(Event:DMA_Ack, makeLineAddress(in_msg.Address),
|
||||
TBEs[makeLineAddress(in_msg.Address)]);
|
||||
} else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE ||
|
||||
in_msg.Type == CoherenceResponseType:DATA) {
|
||||
trigger(Event:Data, makeLineAddress(in_msg.Address),
|
||||
TBEs[makeLineAddress(in_msg.Address)]);
|
||||
} else if (in_msg.Type == CoherenceResponseType:ACK) {
|
||||
trigger(Event:Inv_Ack, makeLineAddress(in_msg.Address),
|
||||
TBEs[makeLineAddress(in_msg.Address)]);
|
||||
} else {
|
||||
error("Invalid response type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger Queue
|
||||
in_port(triggerQueue_in, TriggerMsg, triggerQueue) {
|
||||
if (triggerQueue_in.isReady()) {
|
||||
peek(triggerQueue_in, TriggerMsg) {
|
||||
if (in_msg.Type == TriggerType:ALL_ACKS) {
|
||||
trigger(Event:All_Acks, in_msg.Address, TBEs[in_msg.Address]);
|
||||
} else {
|
||||
error("Unexpected message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(s_sendReadRequest, "s", desc="Send a DMA read request to memory") {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
enqueue(reqToDirectory_out, RequestMsg, latency=request_latency) {
|
||||
out_msg.Address := in_msg.PhysicalAddress;
|
||||
out_msg.Type := CoherenceRequestType:DMA_READ;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Len := in_msg.Len;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.RequestorMachine := MachineType:DMA;
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(s_sendWriteRequest, "\s", desc="Send a DMA write request to memory") {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
enqueue(reqToDirectory_out, RequestMsg, latency=request_latency) {
|
||||
out_msg.Address := in_msg.PhysicalAddress;
|
||||
out_msg.Type := CoherenceRequestType:DMA_WRITE;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Len := in_msg.Len;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.RequestorMachine := MachineType:DMA;
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(a_ackCallback, "a", desc="Notify dma controller that write request completed") {
|
||||
dma_sequencer.ackCallback();
|
||||
}
|
||||
|
||||
action(o_checkForCompletion, "o", desc="Check if we have received all the messages required for completion") {
|
||||
assert(is_valid(tbe));
|
||||
if (tbe.NumAcks == 0) {
|
||||
enqueue(triggerQueue_out, TriggerMsg) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := TriggerType:ALL_ACKS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(u_updateAckCount, "u", desc="Update ack count") {
|
||||
peek(dmaResponseQueue_in, ResponseMsg) {
|
||||
assert(is_valid(tbe));
|
||||
tbe.NumAcks := tbe.NumAcks - in_msg.Acks;
|
||||
}
|
||||
}
|
||||
|
||||
action( u_sendExclusiveUnblockToDir, "\u", desc="send exclusive unblock to directory") {
|
||||
enqueue(respToDirectory_out, ResponseMsg, latency=response_latency) {
|
||||
out_msg.Address := address;
|
||||
out_msg.Type := CoherenceResponseType:UNBLOCK_EXCLUSIVE;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.SenderMachine := MachineType:DMA;
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
|
||||
action(p_popRequestQueue, "p", desc="Pop request queue") {
|
||||
dmaRequestQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(p_popResponseQueue, "\p", desc="Pop request queue") {
|
||||
dmaResponseQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(p_popTriggerQueue, "pp", desc="Pop trigger queue") {
|
||||
triggerQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(t_updateTBEData, "t", desc="Update TBE Data") {
|
||||
peek(dmaResponseQueue_in, ResponseMsg) {
|
||||
assert(is_valid(tbe));
|
||||
tbe.DataBlk := in_msg.DataBlk;
|
||||
}
|
||||
}
|
||||
|
||||
action(d_dataCallbackFromTBE, "/d", desc="data callback with data from TBE") {
|
||||
assert(is_valid(tbe));
|
||||
dma_sequencer.dataCallback(tbe.DataBlk);
|
||||
}
|
||||
|
||||
action(v_allocateTBE, "v", desc="Allocate TBE entry") {
|
||||
TBEs.allocate(address);
|
||||
set_tbe(TBEs[address]);
|
||||
}
|
||||
|
||||
action(w_deallocateTBE, "w", desc="Deallocate TBE entry") {
|
||||
TBEs.deallocate(address);
|
||||
unset_tbe();
|
||||
}
|
||||
|
||||
action(z_stall, "z", desc="dma is busy..stall") {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
||||
|
||||
transition(READY, ReadRequest, BUSY_RD) {
|
||||
s_sendReadRequest;
|
||||
v_allocateTBE;
|
||||
p_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_RD, Inv_Ack) {
|
||||
u_updateAckCount;
|
||||
o_checkForCompletion;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_RD, Data, READY) {
|
||||
t_updateTBEData;
|
||||
d_dataCallbackFromTBE;
|
||||
w_deallocateTBE;
|
||||
//u_updateAckCount;
|
||||
//o_checkForCompletion;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_RD, All_Acks, READY) {
|
||||
d_dataCallbackFromTBE;
|
||||
//u_sendExclusiveUnblockToDir;
|
||||
w_deallocateTBE;
|
||||
p_popTriggerQueue;
|
||||
}
|
||||
|
||||
transition(READY, WriteRequest, BUSY_WR) {
|
||||
s_sendWriteRequest;
|
||||
v_allocateTBE;
|
||||
p_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_WR, Inv_Ack) {
|
||||
u_updateAckCount;
|
||||
o_checkForCompletion;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_WR, DMA_Ack) {
|
||||
u_updateAckCount; // actually increases
|
||||
o_checkForCompletion;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_WR, All_Acks, READY) {
|
||||
a_ackCallback;
|
||||
u_sendExclusiveUnblockToDir;
|
||||
w_deallocateTBE;
|
||||
p_popTriggerQueue;
|
||||
}
|
||||
}
|
||||
104
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-msg.sm
Normal file
104
simulators/gem5/src/mem/protocol/MOESI_CMP_directory-msg.sm
Normal file
@ -0,0 +1,104 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999-2005 Mark D. Hill and David A. Wood
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
// CoherenceRequestType
|
||||
enumeration(CoherenceRequestType, desc="...") {
|
||||
GETX, desc="Get eXclusive";
|
||||
GETS, desc="Get Shared";
|
||||
PUTX, desc="Put eXclusive";
|
||||
PUTO, desc="Put Owned";
|
||||
PUTO_SHARERS, desc="Put Owned, but sharers exist so don't remove from sharers list";
|
||||
PUTS, desc="Put Shared";
|
||||
WB_ACK, desc="Writeback ack";
|
||||
WB_ACK_DATA, desc="Writeback ack";
|
||||
WB_NACK, desc="Writeback neg. ack";
|
||||
INV, desc="Invalidation";
|
||||
|
||||
DMA_READ, desc="DMA Read";
|
||||
DMA_WRITE, desc="DMA Write";
|
||||
}
|
||||
|
||||
// CoherenceResponseType
|
||||
enumeration(CoherenceResponseType, desc="...") {
|
||||
ACK, desc="ACKnowledgment, responder doesn't have a copy";
|
||||
DATA, desc="Data";
|
||||
DATA_EXCLUSIVE, desc="Data, no processor has a copy";
|
||||
UNBLOCK, desc="Unblock";
|
||||
UNBLOCK_EXCLUSIVE, desc="Unblock, we're in E/M";
|
||||
WRITEBACK_CLEAN_DATA, desc="Clean writeback (contains data)";
|
||||
WRITEBACK_CLEAN_ACK, desc="Clean writeback (contains no data)";
|
||||
WRITEBACK_DIRTY_DATA, desc="Dirty writeback (contains data)";
|
||||
DMA_ACK, desc="Ack that a DMA write completed";
|
||||
}
|
||||
|
||||
// TriggerType
|
||||
enumeration(TriggerType, desc="...") {
|
||||
ALL_ACKS, desc="See corresponding event";
|
||||
}
|
||||
|
||||
// TriggerMsg
|
||||
structure(TriggerMsg, desc="...", interface="Message") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
TriggerType Type, desc="Type of trigger";
|
||||
}
|
||||
|
||||
// RequestMsg (and also forwarded requests)
|
||||
structure(RequestMsg, desc="...", interface="NetworkMessage") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
int Len, desc="Length of Request";
|
||||
CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)";
|
||||
MachineID Requestor, desc="Node who initiated the request";
|
||||
MachineType RequestorMachine, desc="type of component";
|
||||
NetDest Destination, desc="Multicast destination mask";
|
||||
DataBlock DataBlk, desc="data for the cache line (DMA WRITE request)";
|
||||
int Acks, desc="How many acks to expect";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
RubyAccessMode AccessMode, desc="user/supervisor access type";
|
||||
PrefetchBit Prefetch, desc="Is this a prefetch request";
|
||||
}
|
||||
|
||||
// ResponseMsg (and also unblock requests)
|
||||
structure(ResponseMsg, desc="...", interface="NetworkMessage") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)";
|
||||
MachineID Sender, desc="Node who sent the data";
|
||||
MachineType SenderMachine, desc="type of component sending msg";
|
||||
NetDest Destination, desc="Node to whom the data is sent";
|
||||
DataBlock DataBlk, desc="data for the cache line";
|
||||
bool Dirty, desc="Is the data dirty (different than memory)?";
|
||||
int Acks, desc="How many acks to expect";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
protocol "MOESI_CMP_directory";
|
||||
include "RubySlicc_interfaces.slicc";
|
||||
include "MOESI_CMP_directory-msg.sm";
|
||||
include "MOESI_CMP_directory-L2cache.sm";
|
||||
include "MOESI_CMP_directory-L1cache.sm";
|
||||
include "MOESI_CMP_directory-dma.sm";
|
||||
include "MOESI_CMP_directory-dir.sm";
|
||||
2386
simulators/gem5/src/mem/protocol/MOESI_CMP_token-L1cache.sm
Normal file
2386
simulators/gem5/src/mem/protocol/MOESI_CMP_token-L1cache.sm
Normal file
File diff suppressed because it is too large
Load Diff
1496
simulators/gem5/src/mem/protocol/MOESI_CMP_token-L2cache.sm
Normal file
1496
simulators/gem5/src/mem/protocol/MOESI_CMP_token-L2cache.sm
Normal file
File diff suppressed because it is too large
Load Diff
1316
simulators/gem5/src/mem/protocol/MOESI_CMP_token-dir.sm
Normal file
1316
simulators/gem5/src/mem/protocol/MOESI_CMP_token-dir.sm
Normal file
File diff suppressed because it is too large
Load Diff
176
simulators/gem5/src/mem/protocol/MOESI_CMP_token-dma.sm
Normal file
176
simulators/gem5/src/mem/protocol/MOESI_CMP_token-dma.sm
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
machine(DMA, "DMA Controller")
|
||||
: DMASequencer * dma_sequencer,
|
||||
int request_latency = 6
|
||||
{
|
||||
|
||||
MessageBuffer responseFromDir, network="From", virtual_network="5", ordered="true", vnet_type="response", no_vector="true";
|
||||
MessageBuffer reqToDirectory, network="To", virtual_network="0", ordered="false", vnet_type="request", no_vector="true";
|
||||
|
||||
state_declaration(State, desc="DMA states", default="DMA_State_READY") {
|
||||
READY, AccessPermission:Invalid, desc="Ready to accept a new request";
|
||||
BUSY_RD, AccessPermission:Busy, desc="Busy: currently processing a request";
|
||||
BUSY_WR, AccessPermission:Busy, desc="Busy: currently processing a request";
|
||||
}
|
||||
|
||||
enumeration(Event, desc="DMA events") {
|
||||
ReadRequest, desc="A new read request";
|
||||
WriteRequest, desc="A new write request";
|
||||
Data, desc="Data from a DMA memory read";
|
||||
Ack, desc="DMA write to memory completed";
|
||||
}
|
||||
|
||||
structure(DMASequencer, external="yes") {
|
||||
void ackCallback();
|
||||
void dataCallback(DataBlock);
|
||||
}
|
||||
|
||||
MessageBuffer mandatoryQueue, ordered="false", no_vector="true";
|
||||
State cur_state, no_vector="true";
|
||||
|
||||
State getState(Address addr) {
|
||||
return cur_state;
|
||||
}
|
||||
void setState(Address addr, State state) {
|
||||
cur_state := state;
|
||||
}
|
||||
|
||||
AccessPermission getAccessPermission(Address addr) {
|
||||
return AccessPermission:NotPresent;
|
||||
}
|
||||
|
||||
void setAccessPermission(Address addr, State state) {
|
||||
}
|
||||
|
||||
DataBlock getDataBlock(Address addr), return_by_ref="yes" {
|
||||
error("DMA Controller does not support getDataBlock function.\n");
|
||||
}
|
||||
|
||||
out_port(reqToDirectory_out, DMARequestMsg, reqToDirectory, desc="...");
|
||||
|
||||
in_port(dmaRequestQueue_in, SequencerMsg, mandatoryQueue, desc="...") {
|
||||
if (dmaRequestQueue_in.isReady()) {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
if (in_msg.Type == SequencerRequestType:LD ) {
|
||||
trigger(Event:ReadRequest, in_msg.LineAddress);
|
||||
} else if (in_msg.Type == SequencerRequestType:ST) {
|
||||
trigger(Event:WriteRequest, in_msg.LineAddress);
|
||||
} else {
|
||||
error("Invalid request type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in_port(dmaResponseQueue_in, DMAResponseMsg, responseFromDir, desc="...") {
|
||||
if (dmaResponseQueue_in.isReady()) {
|
||||
peek( dmaResponseQueue_in, DMAResponseMsg) {
|
||||
if (in_msg.Type == DMAResponseType:ACK) {
|
||||
trigger(Event:Ack, in_msg.LineAddress);
|
||||
} else if (in_msg.Type == DMAResponseType:DATA) {
|
||||
trigger(Event:Data, in_msg.LineAddress);
|
||||
} else {
|
||||
error("Invalid response type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(s_sendReadRequest, "s", desc="Send a DMA read request to memory") {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
enqueue(reqToDirectory_out, DMARequestMsg, latency=request_latency) {
|
||||
out_msg.PhysicalAddress := in_msg.PhysicalAddress;
|
||||
out_msg.LineAddress := in_msg.LineAddress;
|
||||
out_msg.Type := DMARequestType:READ;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Len := in_msg.Len;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(s_sendWriteRequest, "\s", desc="Send a DMA write request to memory") {
|
||||
peek(dmaRequestQueue_in, SequencerMsg) {
|
||||
enqueue(reqToDirectory_out, DMARequestMsg, latency=request_latency) {
|
||||
out_msg.PhysicalAddress := in_msg.PhysicalAddress;
|
||||
out_msg.LineAddress := in_msg.LineAddress;
|
||||
out_msg.Type := DMARequestType:WRITE;
|
||||
out_msg.Requestor := machineID;
|
||||
out_msg.DataBlk := in_msg.DataBlk;
|
||||
out_msg.Len := in_msg.Len;
|
||||
out_msg.Destination.add(map_Address_to_Directory(address));
|
||||
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action(a_ackCallback, "a", desc="Notify dma controller that write request completed") {
|
||||
peek (dmaResponseQueue_in, DMAResponseMsg) {
|
||||
dma_sequencer.ackCallback();
|
||||
}
|
||||
}
|
||||
|
||||
action(d_dataCallback, "d", desc="Write data to dma sequencer") {
|
||||
peek (dmaResponseQueue_in, DMAResponseMsg) {
|
||||
dma_sequencer.dataCallback(in_msg.DataBlk);
|
||||
}
|
||||
}
|
||||
|
||||
action(p_popRequestQueue, "p", desc="Pop request queue") {
|
||||
dmaRequestQueue_in.dequeue();
|
||||
}
|
||||
|
||||
action(p_popResponseQueue, "\p", desc="Pop request queue") {
|
||||
dmaResponseQueue_in.dequeue();
|
||||
}
|
||||
|
||||
transition(READY, ReadRequest, BUSY_RD) {
|
||||
s_sendReadRequest;
|
||||
p_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(READY, WriteRequest, BUSY_WR) {
|
||||
s_sendWriteRequest;
|
||||
p_popRequestQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_RD, Data, READY) {
|
||||
d_dataCallback;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
|
||||
transition(BUSY_WR, Ack, READY) {
|
||||
a_ackCallback;
|
||||
p_popResponseQueue;
|
||||
}
|
||||
}
|
||||
140
simulators/gem5/src/mem/protocol/MOESI_CMP_token-msg.sm
Normal file
140
simulators/gem5/src/mem/protocol/MOESI_CMP_token-msg.sm
Normal file
@ -0,0 +1,140 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999-2005 Mark D. Hill and David A. Wood
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
// CoherenceRequestType
|
||||
enumeration(CoherenceRequestType, desc="...") {
|
||||
GETX, desc="Get eXclusive";
|
||||
GETS, desc="Get Shared";
|
||||
}
|
||||
|
||||
// PersistentType
|
||||
enumeration(PersistentRequestType, desc="...") {
|
||||
GETX_PERSISTENT, desc="...";
|
||||
GETS_PERSISTENT, desc="...";
|
||||
DEACTIVATE_PERSISTENT,desc="...";
|
||||
}
|
||||
|
||||
// CoherenceResponseType
|
||||
enumeration(CoherenceResponseType, desc="...") {
|
||||
DATA_OWNER, desc="Data";
|
||||
ACK_OWNER, desc="data-less owner token";
|
||||
DATA_SHARED, desc="Data";
|
||||
ACK, desc="ACKnowledgment";
|
||||
WB_TOKENS, desc="L1 to L2 writeback";
|
||||
WB_SHARED_DATA, desc="L1 to L2 writeback with data";
|
||||
WB_OWNED, desc="L1 to L2 writeback with data";
|
||||
INV, desc="L1 informing L2 of loss of all tokens";
|
||||
}
|
||||
|
||||
// TriggerType
|
||||
enumeration(TriggerType, desc="...") {
|
||||
REQUEST_TIMEOUT, desc="See corresponding event";
|
||||
USE_TIMEOUT, desc="See corresponding event";
|
||||
DATA, desc="data for dma read response";
|
||||
DATA_ALL_TOKENS, desc="data and all tokens for dma write response";
|
||||
}
|
||||
|
||||
// TriggerMsg
|
||||
structure(TriggerMsg, desc="...", interface="Message") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
TriggerType Type, desc="Type of trigger";
|
||||
}
|
||||
|
||||
// PersistentMsg
|
||||
structure(PersistentMsg, desc="...", interface="NetworkMessage") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
PersistentRequestType Type, desc="Type of starvation request";
|
||||
MachineID Requestor, desc="Node who initiated the request";
|
||||
NetDest Destination, desc="Destination set";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
RubyAccessMode AccessMode, desc="user/supervisor access type";
|
||||
PrefetchBit Prefetch, desc="Is this a prefetch request";
|
||||
}
|
||||
|
||||
// RequestMsg
|
||||
structure(RequestMsg, desc="...", interface="NetworkMessage") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)";
|
||||
MachineID Requestor, desc="Node who initiated the request";
|
||||
NetDest Destination, desc="Multicast destination mask";
|
||||
bool isLocal, desc="Is this request from a local L1";
|
||||
int RetryNum, desc="retry sequence number";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
RubyAccessMode AccessMode, desc="user/supervisor access type";
|
||||
PrefetchBit Prefetch, desc="Is this a prefetch request";
|
||||
}
|
||||
|
||||
// ResponseMsg
|
||||
structure(ResponseMsg, desc="...", interface="NetworkMessage") {
|
||||
Address Address, desc="Physical address for this request";
|
||||
CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)";
|
||||
MachineID Sender, desc="Node who sent the data";
|
||||
NetDest Destination, desc="Node to whom the data is sent";
|
||||
int Tokens, desc="Number of tokens being transfered for this line";
|
||||
DataBlock DataBlk, desc="data for the cache line";
|
||||
bool Dirty, desc="Is the data dirty (different than memory)?";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
}
|
||||
|
||||
enumeration(DMARequestType, desc="...", default="DMARequestType_NULL") {
|
||||
READ, desc="Memory Read";
|
||||
WRITE, desc="Memory Write";
|
||||
NULL, desc="Invalid";
|
||||
}
|
||||
|
||||
enumeration(DMAResponseType, desc="...", default="DMAResponseType_NULL") {
|
||||
DATA, desc="DATA read";
|
||||
ACK, desc="ACK write";
|
||||
NULL, desc="Invalid";
|
||||
}
|
||||
|
||||
structure(DMARequestMsg, desc="...", interface="NetworkMessage") {
|
||||
DMARequestType Type, desc="Request type (read/write)";
|
||||
Address PhysicalAddress, desc="Physical address for this request";
|
||||
Address LineAddress, desc="Line address for this request";
|
||||
MachineID Requestor, desc="Node who initiated the request";
|
||||
NetDest Destination, desc="Destination";
|
||||
DataBlock DataBlk, desc="DataBlk attached to this request";
|
||||
int Len, desc="The length of the request";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
}
|
||||
|
||||
structure(DMAResponseMsg, desc="...", interface="NetworkMessage") {
|
||||
DMAResponseType Type, desc="Response type (DATA/ACK)";
|
||||
Address PhysicalAddress, desc="Physical address for this request";
|
||||
Address LineAddress, desc="Line address for this request";
|
||||
NetDest Destination, desc="Destination";
|
||||
DataBlock DataBlk, desc="DataBlk attached to this request";
|
||||
MessageSizeType MessageSize, desc="size category of the message";
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user