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:
friemel
2012-10-24 19:18:57 +00:00
parent f7ff71bd46
commit b41eec3f65
3222 changed files with 658579 additions and 1 deletions

View 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")

View 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")

View 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'

View 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")

View 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

View 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'])

View 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")

View 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 &section)
{
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]));
}

View 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 &section);
};
#endif //__ABSTRACT_MEMORY_HH__

View 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);
}

View 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__

View 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;
}

View 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__

View 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")

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &section);
};
#endif // __CACHE_HH__

File diff suppressed because it is too large Load Diff

463
simulators/gem5/src/mem/cache/mshr.cc vendored Normal file
View 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
View 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__

View 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(&registers[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;
}
}
}

View 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__

View 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'

View 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')

View 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);
}

View 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__

View 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);
}

View 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__

View 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);
}

View 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__

View 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);
}

View 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__

View 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')

View 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));
}

View 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__

View 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);
}

View 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

View 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();
}
}

View 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__

View 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 &params) :
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;
}
}
}

View 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 &params);
/**
* 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__

View 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")

View 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);
}

View 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__ */

View 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 */

View 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;
}
}
}

View 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__

View 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);
}

View 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__

View 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);
}

View 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__

View 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

View 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();
}
}

View 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__

View 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);
}

View 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__

View 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());
}
}

View 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

View 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);
}

View 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__

View 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());
}

View 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

View 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__

View 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);
}

View 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__

View 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 &section)
{
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;
}
}

View 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 &section);
};
#endif // __MEM_PAGE_TABLE_HH__

View 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);
}

View 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__

View 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);
}

View 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__

View 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;
}

View 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__

View 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;
}
}

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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;
}
}

View 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";
}

View File

@ -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";

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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";
}

View 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";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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;
}
}

View 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";
}

View File

@ -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";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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