| # 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 .abstract_ruby_cache_hierarhcy import AbstractRubyCacheHierarchy |
| from ..abstract_two_level_cache_hierarchy import AbstractTwoLevelCacheHierarchy |
| from ...coherence_protocol import CoherenceProtocol |
| from ...isas import ISA |
| from ...boards.abstract_board import AbstractBoard |
| from ...runtime import get_runtime_coherence_protocol, get_runtime_isa |
| |
| from .topologies.simple_pt2pt import SimplePt2Pt |
| from .caches.mesi_two_level.l1_cache import L1Cache |
| from .caches.mesi_two_level.l2_cache import L2Cache |
| from .caches.mesi_two_level.directory import Directory |
| from .caches.mesi_two_level.dma_controller import DMAController |
| |
| from m5.objects import ( |
| RubySystem, |
| RubySequencer, |
| DMASequencer, |
| RubyPortProxy, |
| ) |
| |
| |
| class MESITwoLevelCacheHierarchy( |
| AbstractRubyCacheHierarchy, AbstractTwoLevelCacheHierarchy |
| ): |
| """A two level private L1 shared L2 MESI hierarchy. |
| |
| In addition to the normal two level parameters, you can also change the |
| number of L2 banks in this protocol. |
| |
| 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, |
| num_l2_banks: int, |
| ): |
| AbstractRubyCacheHierarchy.__init__(self=self) |
| AbstractTwoLevelCacheHierarchy.__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, |
| ) |
| |
| self._num_l2_banks = num_l2_banks |
| |
| def incorporate_cache(self, board: AbstractBoard) -> None: |
| if ( |
| get_runtime_coherence_protocol() |
| != CoherenceProtocol.MESI_TWO_LEVEL |
| ): |
| raise EnvironmentError( |
| "The MESITwoLevelCacheHierarchy must be used with with the " |
| "MESI_Two_Level coherence protocol." |
| ) |
| |
| cache_line_size = board.get_cache_line_size() |
| |
| self.ruby_system = RubySystem() |
| |
| # MESI_Two_Level needs 5 virtual networks |
| self.ruby_system.number_of_virtual_networks = 5 |
| |
| self.ruby_system.network = SimplePt2Pt(self.ruby_system) |
| self.ruby_system.network.number_of_virtual_networks = 5 |
| |
| self._l1_controllers = [] |
| for i, core in enumerate(board.get_processor().get_cores()): |
| cache = L1Cache( |
| self._l1i_size, |
| self._l1i_assoc, |
| self._l1d_size, |
| self._l1d_assoc, |
| self.ruby_system.network, |
| core, |
| self._num_l2_banks, |
| cache_line_size, |
| get_runtime_isa(), |
| board.get_clock_domain(), |
| ) |
| |
| if board.has_io_bus(): |
| cache.sequencer = RubySequencer( |
| version=i, |
| dcache=cache.L1Dcache, |
| clk_domain=cache.clk_domain, |
| pio_request_port=board.get_io_bus().cpu_side_ports, |
| mem_request_port=board.get_io_bus().cpu_side_ports, |
| pio_response_port=board.get_io_bus().mem_side_ports, |
| ) |
| else: |
| cache.sequencer = RubySequencer( |
| version=i, |
| dcache=cache.L1Dcache, |
| clk_domain=cache.clk_domain, |
| ) |
| |
| cache.ruby_system = self.ruby_system |
| |
| core.connect_icache(cache.sequencer.in_ports) |
| core.connect_dcache(cache.sequencer.in_ports) |
| |
| core.connect_walker_ports( |
| cache.sequencer.in_ports, cache.sequencer.in_ports |
| ) |
| |
| # Connect the interrupt ports |
| if get_runtime_isa() == ISA.X86: |
| int_req_port = cache.sequencer.interrupt_out_port |
| int_resp_port = cache.sequencer.in_ports |
| core.connect_interrupt(int_req_port, int_resp_port) |
| |
| self._l1_controllers.append(cache) |
| |
| self._l2_controllers = [ |
| L2Cache( |
| self._l2_size, |
| self._l2_assoc, |
| self.ruby_system.network, |
| self._num_l2_banks, |
| cache_line_size, |
| ) |
| for _ in range(self._num_l2_banks) |
| ] |
| # TODO: Make this prettier: The problem is not being able to proxy |
| # the ruby system correctly |
| for cache in self._l2_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_memory().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 |
| |
| dma_ports = board.get_dma_ports() |
| self._dma_controllers = [] |
| 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.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._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) |