This patch add a new Ruby cache coherence protocol based on Arm' AMBA5
CHI specification. The CHI protocol defines and implements two state
machine types:
- Cache_Controller: generic cache controller that can be configured as:
- Top-level L1 I/D cache
- A intermediate level (L2, L3, ...) private or shared cache
- A CHI home node (i.e. the point of coherence of the system and
has the global directory)
- A DMA requester
- Memory_Controller: implements a CHI slave node and interfaces with
gem5 memory controller. This controller has the functionality of a
Directory_Controller on the other Ruby protocols, except it doesn't
have a directory.
The Cache_Controller has multiple cache allocation/deallocation
parameters to control the clusivity with respect to upstream caches.
Allocation can be completely disabled to use Cache_Controller as a
DMA requester or as a home node without a shared LLC.
The standard configuration file configs/ruby/CHI.py provides a
'create_system' compatible with configs/example/fs.py and
configs/example/se.py and creates a system with private L1/L2 caches
per core and a shared LLC at the home nodes. Different cache topologies
can be defined by modifying 'create_system' or by creating custom
scripts using the structures defined in configs/ruby/CHI.py.
This patch also includes the 'CustomMesh' topology script to be used
with CHI. CustomMesh generates a 2D mesh topology with the placement
of components manually defined in a separate configuration file using
the --noc-config parameter.
The example in configs/example/noc_config/2x4.yaml creates a simple 2x4
mesh. For example, to run a SE mode simulation, with 4 cores,
4 mem ctnrls, and 4 home nodes (L3 caches):
build/ARM/gem5.opt configs/example/se.py \
--cmd 'tests/test-progs/hello/bin/arm/linux/hello' \
--ruby --num-cpus=4 --num-dirs=4 --num-l3caches=4 \
--topology=CustomMesh --noc-config=configs/example/noc_config/2x4.yaml
If one doesn't care about the component placement on the interconnect,
the 'Crossbar' and 'Pt2Pt' may be used and they do not require the
--noc-config option.
Additional authors:
Joshua Randall <joshua.randall@arm.com>
Pedro Benedicte <pedro.benedicteillescas@arm.com>
Tuan Ta <tuan.ta2@arm.com>
JIRA: https://gem5.atlassian.net/browse/GEM5-908
Change-Id: I856524b0afd30842194190f5bd69e7e6ded906b0
Signed-off-by: Tiago Mück <tiago.muck@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/42563
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Maintainer: Jason Lowe-Power <power.jg@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
233 lines
6.9 KiB
C++
233 lines
6.9 KiB
C++
/*
|
|
* Copyright (c) 2021 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.
|
|
*/
|
|
|
|
#ifndef __MEM_RUBY_COMMON_EXPECTEDMAP_HH__
|
|
#define __MEM_RUBY_COMMON_EXPECTEDMAP_HH__
|
|
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <unordered_map>
|
|
|
|
// ExpectedMap helper class is used to facilitate tracking of pending
|
|
// response and data messages in the CHI protocol. It offers additional
|
|
// functionality when compared to plain counters:
|
|
// - tracks the expected type for received messages
|
|
// - tracks segmented data messages (i.e. when a line transfer is split in
|
|
// multiple messages)
|
|
|
|
template<typename RespType, typename DataType>
|
|
class ExpectedMap
|
|
{
|
|
private:
|
|
|
|
template<typename Type>
|
|
struct ExpectedState
|
|
{
|
|
struct EnumClassHash
|
|
{
|
|
std::size_t operator()(Type t) const
|
|
{
|
|
return static_cast<std::size_t>(t);
|
|
}
|
|
};
|
|
|
|
private:
|
|
// chunks is the number segmented messages we expect to receive
|
|
// before incrementing numReceived. This is tipically always 1 for all
|
|
// non-data messages
|
|
int chunks;
|
|
int currChunk;
|
|
int numReceived;
|
|
std::unordered_map<Type, bool, EnumClassHash> expectedTypes;
|
|
|
|
public:
|
|
ExpectedState()
|
|
:chunks(1), currChunk(0), numReceived(0)
|
|
{}
|
|
|
|
void
|
|
clear(int msg_chunks)
|
|
{
|
|
chunks = msg_chunks;
|
|
currChunk = 0;
|
|
numReceived = 0;
|
|
expectedTypes.clear();
|
|
}
|
|
|
|
void
|
|
addExpectedType(const Type &val)
|
|
{
|
|
expectedTypes[val] = false;
|
|
}
|
|
|
|
int received() const { return numReceived; }
|
|
|
|
bool
|
|
increaseReceived(const Type &val)
|
|
{
|
|
if (expectedTypes.find(val) == expectedTypes.end())
|
|
return false;
|
|
|
|
expectedTypes[val] = true;
|
|
++currChunk;
|
|
if (currChunk == chunks) {
|
|
++numReceived;
|
|
currChunk = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
receivedType(const Type &val) const
|
|
{
|
|
auto i = expectedTypes.find(val);
|
|
if (i != expectedTypes.end())
|
|
return i->second;
|
|
else
|
|
return false;
|
|
}
|
|
};
|
|
|
|
ExpectedState<DataType> expectedData;
|
|
ExpectedState<RespType> expectedResp;
|
|
int totalExpected;
|
|
|
|
public:
|
|
ExpectedMap()
|
|
:expectedData(), expectedResp(), totalExpected(0)
|
|
{}
|
|
|
|
// Clear the tracking state and specified the number of chunks are required
|
|
// to receive a complete data message
|
|
void
|
|
clear(int dataChunks)
|
|
{
|
|
expectedData.clear(dataChunks);
|
|
expectedResp.clear(1);
|
|
totalExpected = 0;
|
|
}
|
|
|
|
// Register an expected response message type
|
|
void
|
|
addExpectedRespType(const RespType &val)
|
|
{
|
|
expectedResp.addExpectedType(val);
|
|
}
|
|
|
|
// Register an expected data message type
|
|
void
|
|
addExpectedDataType(const DataType &val)
|
|
{
|
|
expectedData.addExpectedType(val);
|
|
}
|
|
|
|
// Set the number of expected messages
|
|
void setExpectedCount(int val) { totalExpected = val; }
|
|
|
|
void addExpectedCount(int val) { totalExpected += val; }
|
|
|
|
// Returns the number of messages received.
|
|
// Notice that a data message counts as received only after all of
|
|
// its chunks are received.
|
|
int
|
|
received() const
|
|
{
|
|
return expectedData.received() + expectedResp.received();
|
|
}
|
|
|
|
// Returns the remaining number of expected messages
|
|
int expected() const { return totalExpected - received(); }
|
|
|
|
// Has any expected message ?
|
|
bool hasExpected() const { return expected() != 0; }
|
|
|
|
// Has received any data ?
|
|
bool hasReceivedData() const { return expectedData.received() != 0; }
|
|
|
|
// Has received any response ?
|
|
bool hasReceivedResp() const { return expectedResp.received() != 0; }
|
|
|
|
|
|
// Notifies that a response message was received
|
|
bool
|
|
receiveResp(const RespType &val)
|
|
{
|
|
assert(received() < totalExpected);
|
|
return expectedResp.increaseReceived(val);
|
|
}
|
|
|
|
// Notifies that a data message chunk was received
|
|
bool
|
|
receiveData(const DataType &val)
|
|
{
|
|
assert(received() <= totalExpected);
|
|
return expectedData.increaseReceived(val);
|
|
}
|
|
|
|
// Has received any data of the given type ?
|
|
bool
|
|
receivedDataType(const DataType &val) const
|
|
{
|
|
return expectedData.receivedType(val);
|
|
}
|
|
|
|
// Has received any response of the given type ?
|
|
bool
|
|
receivedRespType(const RespType &val) const
|
|
{
|
|
return expectedResp.receivedType(val);
|
|
}
|
|
|
|
void
|
|
print(std::ostream& out) const
|
|
{
|
|
out << expected();
|
|
}
|
|
};
|
|
|
|
template<typename RespType, typename DataType>
|
|
inline std::ostream&
|
|
operator<<(std::ostream& out, const ExpectedMap<RespType,DataType>& obj)
|
|
{
|
|
obj.print(out);
|
|
return out;
|
|
}
|
|
|
|
|
|
#endif // __MEM_RUBY_COMMON_EXPECTEDMAP_HH__
|