diff --git a/src/python/SConscript b/src/python/SConscript index e7e464e2df..aeeb8925a3 100644 --- a/src/python/SConscript +++ b/src/python/SConscript @@ -61,6 +61,8 @@ PySource('gem5.components.cachehierarchies', 'gem5/components/cachehierarchies/abstract_cache_hierarchy.py') PySource('gem5.components.cachehierarchies', 'gem5/components/cachehierarchies/abstract_two_level_cache_hierarchy.py') +PySource('gem5.components.cachehierarchies', + 'gem5/components/cachehierarchies/abstract_three_level_cache_hierarchy.py') PySource('gem5.components.cachehierarchies.chi', 'gem5/components/cachehierarchies/chi/__init__.py') PySource('gem5.components.cachehierarchies.chi', @@ -108,6 +110,9 @@ PySource('gem5.components.cachehierarchies.ruby', 'gem5/components/cachehierarchies/ruby/abstract_ruby_cache_hierarchy.py') PySource('gem5.components.cachehierarchies.ruby', 'gem5/components/cachehierarchies/ruby/mesi_two_level_cache_hierarchy.py') +PySource('gem5.components.cachehierarchies.ruby', + 'gem5/components/cachehierarchies/ruby/' + 'mesi_three_level_cache_hierarchy.py') PySource('gem5.components.cachehierarchies.ruby', 'gem5/components/cachehierarchies/ruby/mi_example_cache_hierarchy.py') PySource('gem5.components.cachehierarchies.ruby.caches', @@ -131,6 +136,24 @@ PySource('gem5.components.cachehierarchies.ruby.caches.mesi_two_level', 'gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l1_cache.py') PySource('gem5.components.cachehierarchies.ruby.caches.mesi_two_level', 'gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l2_cache.py') +PySource('gem5.components.cachehierarchies.ruby.caches.mesi_three_level', + 'gem5/components/cachehierarchies/ruby/caches/mesi_three_level/' + '__init__.py') +PySource('gem5.components.cachehierarchies.ruby.caches.mesi_three_level', + 'gem5/components/cachehierarchies/ruby/caches/mesi_three_level/' + 'directory.py') +PySource('gem5.components.cachehierarchies.ruby.caches.mesi_three_level', + 'gem5/components/cachehierarchies/ruby/caches/mesi_three_level/' + 'dma_controller.py') +PySource('gem5.components.cachehierarchies.ruby.caches.mesi_three_level', + 'gem5/components/cachehierarchies/ruby/caches/mesi_three_level/' + 'l1_cache.py') +PySource('gem5.components.cachehierarchies.ruby.caches.mesi_three_level', + 'gem5/components/cachehierarchies/ruby/caches/mesi_three_level/' + 'l2_cache.py') +PySource('gem5.components.cachehierarchies.ruby.caches.mesi_three_level', + 'gem5/components/cachehierarchies/ruby/caches/mesi_three_level/' + 'l3_cache.py') PySource('gem5.components.cachehierarchies.ruby.caches.mi_example', 'gem5/components/cachehierarchies/ruby/caches/mi_example/__init__.py') PySource('gem5.components.cachehierarchies.ruby.caches.mi_example', diff --git a/src/python/gem5/components/cachehierarchies/abstract_three_level_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/abstract_three_level_cache_hierarchy.py new file mode 100644 index 0000000000..4d2f21abdc --- /dev/null +++ b/src/python/gem5/components/cachehierarchies/abstract_three_level_cache_hierarchy.py @@ -0,0 +1,52 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +class AbstractThreeLevelCacheHierarchy: + """ + An abstract three-level hierarchy with configurable size and associativity + for each of L1, L2, and L3 caches. + """ + + def __init__( + self, + l1i_size: str, + l1i_assoc: int, + l1d_size: str, + l1d_assoc: int, + l2_size: str, + l2_assoc: int, + l3_size: str, + l3_assoc: int, + ): + self._l1i_size = l1i_size + self._l1i_assoc = l1i_assoc + self._l1d_size = l1d_size + self._l1d_assoc = l1d_assoc + self._l2_size = l2_size + self._l2_assoc = l2_assoc + self._l3_size = l3_size + self._l3_assoc = l3_assoc diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/__init__.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/directory.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/directory.py new file mode 100644 index 0000000000..cd4f166fed --- /dev/null +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/directory.py @@ -0,0 +1,51 @@ +# Copyright (c) 2021 The Regents of the University of California +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from ......utils.override import overrides +from ..abstract_directory import AbstractDirectory + +from m5.objects import MessageBuffer, RubyDirectoryMemory + + +class Directory(AbstractDirectory): + def __init__(self, network, cache_line_size, mem_range, port): + + super().__init__(network, cache_line_size) + self.addr_ranges = [mem_range] + self.directory = RubyDirectoryMemory() + # Connect this directory to the memory side. + self.memory_out_port = port + + @overrides(AbstractDirectory) + def connectQueues(self, network): + self.requestToDir = MessageBuffer() + self.requestToDir.in_port = network.out_port + self.responseToDir = MessageBuffer() + self.responseToDir.in_port = network.out_port + self.responseFromDir = MessageBuffer() + self.responseFromDir.out_port = network.in_port + self.requestToMemory = MessageBuffer() + self.responseFromMemory = MessageBuffer() diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/dma_controller.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/dma_controller.py new file mode 100644 index 0000000000..ab76d4cb5e --- /dev/null +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/dma_controller.py @@ -0,0 +1,43 @@ +# Copyright (c) 2021 The Regents of the University of California +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from ......utils.override import overrides +from ..abstract_dma_controller import AbstractDMAController + +from m5.objects import MessageBuffer + + +class DMAController(AbstractDMAController): + def __init__(self, network, cache_line_size): + super().__init__(network, cache_line_size) + + @overrides(AbstractDMAController) + def connectQueues(self, network): + self.mandatoryQueue = MessageBuffer() + self.responseFromDir = MessageBuffer(ordered=True) + self.responseFromDir.in_port = network.out_port + self.requestToDir = MessageBuffer() + self.requestToDir.out_port = network.in_port diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l1_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l1_cache.py new file mode 100644 index 0000000000..2ce13d3b08 --- /dev/null +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l1_cache.py @@ -0,0 +1,110 @@ +# Copyright (c) 2022 The Regents of the University of California +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from .....processors.abstract_core import AbstractCore +from ......isas import ISA +from ......utils.override import * + +from m5.objects import ( + MessageBuffer, + RubyPrefetcher, + RubyCache, + ClockDomain, + LRURP, + L0Cache_Controller, +) + +import math + +# L0Cache_Controller is the ruby backend's terminology corresponding to +# L1 cache in stdlib terms. +class L1Cache(L0Cache_Controller): + + _version = 0 + + @classmethod + def versionCount(cls): + cls._version += 1 + return cls._version - 1 + + def __init__( + self, + l1i_size, + l1i_assoc, + l1d_size, + l1d_assoc, + network, + core: AbstractCore, + cache_line_size, + target_isa: ISA, + clk_domain: ClockDomain, + ): + super().__init__() + + # This is the cache memory object that stores the cache data and tags + self.Icache = RubyCache( + size=l1i_size, + assoc=l1i_assoc, + start_index_bit=self.getBlockSizeBits(), + is_icache=True, + replacement_policy=LRURP(), + ) + self.Dcache = RubyCache( + size=l1d_size, + assoc=l1d_assoc, + start_index_bit=self.getBlockSizeBits(), + is_icache=False, + replacement_policy=LRURP(), + ) + self.clk_domain = clk_domain + self.prefetcher = RubyPrefetcher() + self.send_evictions = core.requires_send_evicts() + self.transitions_per_cycle = 32 + self.enable_prefetch = False + self.request_latency = 2 + self.response_latency = 2 + + self.version = self.versionCount() + self._cache_line_size = cache_line_size + self.connectQueues(network) + + def getBlockSizeBits(self): + bits = int(math.log(self._cache_line_size, 2)) + if 2**bits != self._cache_line_size.value: + raise Exception("Cache line size is not a power of 2!") + return bits + + def connectQueues(self, network): + self.prefetchQueue = MessageBuffer() + self.mandatoryQueue = MessageBuffer() + self.optionalQueue = MessageBuffer() + + # bufferToL1 and bufferFromL1 are ruby backend terminology. + # In stdlib terms, they are bufferToL2 and bufferFromL2 respectively. + # These buffers are connections between L1 cache and L2 cache. + # Later on, we'll need to connect those buffers to L2. + self.bufferToL1 = MessageBuffer(ordered=True) + self.bufferFromL1 = MessageBuffer(ordered=True) diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l2_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l2_cache.py new file mode 100644 index 0000000000..e29f566191 --- /dev/null +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l2_cache.py @@ -0,0 +1,113 @@ +# Copyright (c) 2022 The Regents of the University of California +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from .....processors.abstract_core import AbstractCore +from ......isas import ISA +from ......utils.override import * + +from m5.objects import ( + MessageBuffer, + RubyPrefetcher, + RubyCache, + ClockDomain, + L1Cache_Controller, +) + +import math + +# L1Cache_Controller is ruby backend's terminology corresponding to +# L2Cache in stdlib's terms +class L2Cache(L1Cache_Controller): + + _version = 0 + + @classmethod + def versionCount(cls): + cls._version += 1 + return cls._version - 1 + + def __init__( + self, + l2_size, + l2_assoc, + network, + core: AbstractCore, + num_l3Caches, + cache_line_size, + cluster_id, + target_isa: ISA, + clk_domain: ClockDomain, + ): + super().__init__() + + # This is the cache memory object that stores the cache data and tags + self.cache = RubyCache( + size=l2_size, + assoc=l2_assoc, + start_index_bit=self.getBlockSizeBits(), + is_icache=False, + ) + # l2_select_num_bits is ruby backend terminology. + # In stdlib terms, it is number of bits for selecting L3 cache. + self.l2_select_num_bits = int(math.log(num_l3Caches, 2)) + self.cluster_id = cluster_id + self.clk_domain = clk_domain + self.prefetcher = RubyPrefetcher() + self.transitions_per_cycle = 32 + # l1_request_latency, l1_response_latency, to_l2_latency are + # ruby backend terminology. + # In stdlib terms, they are L2 cache request latency, L2 response + # latency, and to L3 cache latency respectively. + self.l1_request_latency = 2 + self.l1_response_latency = 2 + self.to_l2_latency = 1 + + self.version = self.versionCount() + self._cache_line_size = cache_line_size + self.connectQueues(network) + + def connectQueues(self, network): + self.mandatoryQueue = MessageBuffer() + self.optionalQueue = MessageBuffer() + + # In the below terms, L2 are ruby backend terminology. + # They are L3 in stdlib. + + # Request from/to L2 buffers + self.requestFromL2 = MessageBuffer() + self.requestFromL2.in_port = network.out_port + self.requestToL2 = MessageBuffer() + self.requestToL2.out_port = network.in_port + + # Response from/to L2 buffers + self.responseFromL2 = MessageBuffer() + self.responseFromL2.in_port = network.out_port + self.responseToL2 = MessageBuffer() + self.responseToL2.out_port = network.in_port + + # Unblock to L2 buffer + self.unblockToL2 = MessageBuffer() + self.unblockToL2.out_port = network.in_port diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l3_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l3_cache.py new file mode 100644 index 0000000000..6d46d1fdf0 --- /dev/null +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l3_cache.py @@ -0,0 +1,89 @@ +# Copyright (c) 2022 The Regents of the University of California +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from m5.objects import MessageBuffer, RubyCache, L2Cache_Controller + +import math + +# L2Cache_Controller is ruby backend's terminology corresponding to +# L3 cache in stdlib. +class L3Cache(L2Cache_Controller): + + _version = 0 + + @classmethod + def versionCount(cls): + cls._version += 1 + return cls._version - 1 + + def __init__( + self, + l3_size, + l3_assoc, + network, + num_l3Caches, + cache_line_size, + cluster_id, + ): + super().__init__() + + # This is the cache memory object that stores the cache data and tags + self.L2cache = RubyCache( + size=l3_size, + assoc=l3_assoc, + start_index_bit=self.getIndexBit(num_l3Caches), + ) + + self.transitions_per_cycle = 4 + self.cluster_id = cluster_id + self.l2_request_latency = 2 + self.l2_response_latency = 2 + self.to_l1_latency = 1 + + self.version = self.versionCount() + self._cache_line_size = cache_line_size + self.connectQueues(network) + + def getIndexBit(self, num_l3caches): + l3_bits = int(math.log(num_l3caches, 2)) + bits = int(math.log(self._cache_line_size, 2)) + l3_bits + return bits + + def connectQueues(self, network): + # In the below terms, L1 and L2 are ruby backend terminology. + # In stdlib, they are L2 and L3 caches respectively. + self.DirRequestFromL2Cache = MessageBuffer() + self.DirRequestFromL2Cache.out_port = network.in_port + self.L1RequestFromL2Cache = MessageBuffer() + self.L1RequestFromL2Cache.out_port = network.in_port + self.responseFromL2Cache = MessageBuffer() + self.responseFromL2Cache.out_port = network.in_port + self.unblockToL2Cache = MessageBuffer() + self.unblockToL2Cache.in_port = network.out_port + self.L1RequestToL2Cache = MessageBuffer() + self.L1RequestToL2Cache.in_port = network.out_port + self.responseToL2Cache = MessageBuffer() + self.responseToL2Cache.in_port = network.out_port diff --git a/src/python/gem5/components/cachehierarchies/ruby/mesi_three_level_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/ruby/mesi_three_level_cache_hierarchy.py new file mode 100644 index 0000000000..89b6b21177 --- /dev/null +++ b/src/python/gem5/components/cachehierarchies/ruby/mesi_three_level_cache_hierarchy.py @@ -0,0 +1,225 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +from .abstract_ruby_cache_hierarchy import AbstractRubyCacheHierarchy +from ..abstract_three_level_cache_hierarchy import ( + AbstractThreeLevelCacheHierarchy, +) +from ....coherence_protocol import CoherenceProtocol +from ....isas import ISA +from ...boards.abstract_board import AbstractBoard +from ....utils.requires import requires + +from .topologies.simple_pt2pt import SimplePt2Pt +from .caches.mesi_three_level.l1_cache import L1Cache +from .caches.mesi_three_level.l2_cache import L2Cache +from .caches.mesi_three_level.l3_cache import L3Cache +from .caches.mesi_three_level.directory import Directory +from .caches.mesi_three_level.dma_controller import DMAController + +from m5.objects import RubySystem, RubySequencer, DMASequencer, RubyPortProxy + + +class MESIThreeLevelCacheHierarchy( + AbstractRubyCacheHierarchy, AbstractThreeLevelCacheHierarchy +): + """A three-level private-L1-private-L2-shared-L3 MESI hierarchy. + + The on-chip network is a point-to-point all-to-all simple network. + """ + + def __init__( + self, + l1i_size: str, + l1i_assoc: str, + l1d_size: str, + l1d_assoc: str, + l2_size: str, + l2_assoc: str, + l3_size: str, + l3_assoc: str, + num_l3_banks: int, + ): + AbstractRubyCacheHierarchy.__init__(self=self) + AbstractThreeLevelCacheHierarchy.__init__( + self, + l1i_size=l1i_size, + l1i_assoc=l1i_assoc, + l1d_size=l1d_size, + l1d_assoc=l1d_assoc, + l2_size=l2_size, + l2_assoc=l2_assoc, + l3_size=l3_size, + l3_assoc=l3_assoc, + ) + + self._num_l3_banks = num_l3_banks + + def incorporate_cache(self, board: AbstractBoard) -> None: + + requires( + coherence_protocol_required=CoherenceProtocol.MESI_THREE_LEVEL + ) + + cache_line_size = board.get_cache_line_size() + + self.ruby_system = RubySystem() + + # MESI_Three_Level needs 3 virtual networks + self.ruby_system.number_of_virtual_networks = 3 + + self.ruby_system.network = SimplePt2Pt(self.ruby_system) + self.ruby_system.network.number_of_virtual_networks = 3 + + self._l1_controllers = [] + self._l2_controllers = [] + self._l3_controllers = [] + cores = board.get_processor().get_cores() + for core_idx, core in enumerate(cores): + l1_cache = L1Cache( + l1i_size=self._l1i_size, + l1i_assoc=self._l1i_assoc, + l1d_size=self._l1d_size, + l1d_assoc=self._l1d_assoc, + network=self.ruby_system.network, + core=core, + cache_line_size=cache_line_size, + target_isa=board.processor.get_isa(), + clk_domain=board.get_clock_domain(), + ) + + l1_cache.sequencer = RubySequencer( + version=core_idx, + dcache=l1_cache.Dcache, + clk_domain=l1_cache.clk_domain, + ) + + if board.has_io_bus(): + l1_cache.sequencer.connectIOPorts(board.get_io_bus()) + + l1_cache.ruby_system = self.ruby_system + + core.connect_icache(l1_cache.sequencer.in_ports) + core.connect_dcache(l1_cache.sequencer.in_ports) + + core.connect_walker_ports( + l1_cache.sequencer.in_ports, l1_cache.sequencer.in_ports + ) + + # Connect the interrupt ports + if board.get_processor().get_isa() == ISA.X86: + int_req_port = l1_cache.sequencer.interrupt_out_port + int_resp_port = l1_cache.sequencer.in_ports + core.connect_interrupt(int_req_port, int_resp_port) + else: + core.connect_interrupt() + + self._l1_controllers.append(l1_cache) + + # For testing purpose, we use point-to-point topology. So, the + # assigned cluster ID is ignored by ruby. + # Thus, we set cluster_id to 0. + l2_cache = L2Cache( + l2_size=self._l2_size, + l2_assoc=self._l2_assoc, + network=self.ruby_system.network, + core=core, + num_l3Caches=self._num_l3_banks, + cache_line_size=cache_line_size, + cluster_id=0, + target_isa=board.processor.get_isa(), + clk_domain=board.get_clock_domain(), + ) + + l2_cache.ruby_system = self.ruby_system + # L0Cache in the ruby backend is l1 cache in stdlib + # L1Cache in the ruby backend is l2 cache in stdlib + l2_cache.bufferFromL0 = l1_cache.bufferToL1 + l2_cache.bufferToL0 = l1_cache.bufferFromL1 + + self._l2_controllers.append(l2_cache) + + for _ in range(self._num_l3_banks): + l3_cache = L3Cache( + l3_size=self._l3_size, + l3_assoc=self._l3_assoc, + network=self.ruby_system.network, + num_l3Caches=self._num_l3_banks, + cache_line_size=cache_line_size, + cluster_id=0, # cluster_id is ignored in point-to-point topology + ) + l3_cache.ruby_system = self.ruby_system + self._l3_controllers.append(l3_cache) + + # TODO: Make this prettier: The problem is not being able to proxy + # the ruby system correctly + for cache in self._l3_controllers: + cache.ruby_system = self.ruby_system + + self._directory_controllers = [ + Directory(self.ruby_system.network, cache_line_size, range, port) + for range, port in board.get_mem_ports() + ] + # TODO: Make this prettier: The problem is not being able to proxy + # the ruby system correctly + for dir in self._directory_controllers: + dir.ruby_system = self.ruby_system + + self._dma_controllers = [] + if board.has_dma_ports(): + dma_ports = board.get_dma_ports() + for i, port in enumerate(dma_ports): + ctrl = DMAController(self.ruby_system.network, cache_line_size) + ctrl.dma_sequencer = DMASequencer(version=i, in_ports=port) + self._dma_controllers.append(ctrl) + ctrl.ruby_system = self.ruby_system + + self.ruby_system.num_of_sequencers = len(self._l1_controllers) + len( + self._dma_controllers + ) + self.ruby_system.l1_controllers = self._l1_controllers + self.ruby_system.l2_controllers = self._l2_controllers + self.ruby_system.l3_controllers = self._l3_controllers + self.ruby_system.directory_controllers = self._directory_controllers + + if len(self._dma_controllers) != 0: + self.ruby_system.dma_controllers = self._dma_controllers + + # Create the network and connect the controllers. + self.ruby_system.network.connectControllers( + self._l1_controllers + + self._l2_controllers + + self._l3_controllers + + self._directory_controllers + + self._dma_controllers + ) + self.ruby_system.network.setup_buffers() + + # Set up a proxy port for the system_port. Used for load binaries and + # other functional-only things. + self.ruby_system.sys_port_proxy = RubyPortProxy() + board.connect_system_port(self.ruby_system.sys_port_proxy.in_ports)