Daecheol You has uploaded this change for review. ( https://gem5-review.googlesource.com/c/public/gem5/+/40279 )

Change subject: python: Mesh topology with user-defined layout
......................................................................

python: Mesh topology with user-defined layout

There is a limitation on mesh configuration
with Mesh_XY since it requires an evenly divisible number of cache
and directory controllers. Mesh_layout enables to configure
mesh router nodes with the user-defined layout file.
A user can describe an attached controller list for each router node
manually

Change-Id: Iea99a01d6f100c36d4a08a8677cb64912d6117d0
---
M configs/network/Network.py
A configs/topologies/Mesh_layout.py
A configs/topologies/layout_example.txt
3 files changed, 345 insertions(+), 0 deletions(-)



diff --git a/configs/network/Network.py b/configs/network/Network.py
index 8690912..f63e27d 100644
--- a/configs/network/Network.py
+++ b/configs/network/Network.py
@@ -38,6 +38,8 @@
                       help="check configs/topologies for complete set")
     parser.add_option("--mesh-rows", type="int", default=0,
                       help="the number of rows in the mesh topology")
+    parser.add_option("--layout-path", type="string",
+                      help="Mesh layout file path")
     parser.add_option("--network", type="choice", default="simple",
                       choices=['simple', 'garnet'],
                       help="""'simple'|'garnet' (garnet2.0 will be
diff --git a/configs/topologies/Mesh_layout.py b/configs/topologies/Mesh_layout.py
new file mode 100644
index 0000000..773543d
--- /dev/null
+++ b/configs/topologies/Mesh_layout.py
@@ -0,0 +1,303 @@
+# Copyright (c) 2010 Advanced Micro Devices, Inc.
+#               2016 Georgia Institute of Technology
+#               2021 Samsung Electronics Co., Ltd.
+# 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 __future__ import print_function
+from __future__ import absolute_import
+
+from collections import defaultdict
+from collections import deque
+
+import re
+
+from m5.params import *
+from m5.objects import *
+
+from common import FileSystemConfig
+
+from topologies.BaseTopology import SimpleTopology
+
+# Create a mesh as descirbed in a layout file.
+# The layout file descirbes the number of rows and columns,
+# and external controllers (caches and directoryies) attached to each node.
+# Please, see configs/topologies/layout_example.txt for further explanation
+
+# Same as Mesh_XY, XY routing is enforced (using link weights)
+# to guarantee deadlock freedom.
+
+class Mesh_layout(SimpleTopology):
+    description='Mesh_layout'
+
+    def __init__(self, controllers):
+        self.nodes = controllers
+        self.sep = ':'
+
+    # Parse each token in a layout file
+
+    def parse_layout(self, path):
+        with open(path, 'r') as f:
+            while True:
+                stream = f.readline()
+                if not stream:
+                    break
+                elif stream[0] == '#':
+                    continue
+
+                pos = 0
+                while True:
+                    i = stream.find(self.sep, pos)
+                    # no token in the stream
+                    if i < 0:
+                        break
+                    token = stream[pos:i].strip()
+                    yield token
+                    pos = i + 1
+
+    # Make a mesh as described in a layout file
+
+    def makeTopology(self, options, network, IntLink, ExtLink, Router):
+        nodes = self.nodes
+        controllers = defaultdict(deque)
+        ctrl_type_list = set()
+        for ctrl in nodes:
+            controllers[ctrl.type].append(ctrl)
+            ctrl_type_list.add(ctrl.type)
+
+        # Default values for link latency and router latency.
+        # Can be over-ridden on a per link/router basis
+        link_latency = options.link_latency # used by simple and garnet
+        router_latency = options.router_latency # only used by garnet
+
+        # Get the number of rows and columns
+        # Assume the layout file descirbes 'rows' first, and then 'cols'
+        tokens = self.parse_layout(options.layout_path)
+        for token in tokens:
+            # First two attributes in layout file
+            # should be the number of rows and columns
+            assert(token == 'rows' or token == 'cols')
+
+            self.sep = ","
+            attribute = next(tokens)
+            self.sep = ":"
+            if token == 'rows':
+                num_rows = int(attribute)
+            elif token == 'cols':
+                num_columns = int(attribute)
+                break
+
+        # Create the routers in the mesh
+        routers = [Router(router_id=i, latency = router_latency) \
+            for i in range(num_rows*num_columns)]
+        network.routers = routers
+
+        # 'nodes' should be described in the layout file
+        # after 'rows' and 'cols'
+        token = next(tokens)
+        assert(token == 'nodes')
+
+        # Parse attatched controllers of each router node
+        # and make external links for each router
+        # Previously parsed controllers is applied to the nodes
+        # as long as the node's column index is smaller than or
+        # equal to the 'last_valid_index'
+        node_pattern = re.compile('\d+/\d+')
+        cache_pattern = re.compile('L\d+')
+        link_count = 0
+        ext_links = []
+        for row in range(num_rows):
+            last_valid_index = -1
+            for col in range(num_columns):
+                # Need to parse a new controller list
+                # for the current node
+                if col > last_valid_index:
+                    token = next(tokens)
+
+                    # Format of node name should be row/col
+                    matched = node_pattern.search(token)
+                    assert(matched)
+
+                    row_col = token.split('/')
+                    row_num = int(row_col[0])
+                    col_num = int(row_col[1])
+
+ # row and column number of the node should be incremental
+                    assert(row_num == row)
+                    assert(col_num > last_valid_index)
+
+                    # Get the controllers attached to the node
+                    self.sep = ","
+                    attribute = next(tokens)
+                    attached_ctrls = attribute.split()
+
+                    # Apply this controller configuration
+                    # until the column index of last_valid_index
+                    last_valid_index = col_num
+                    self.sep = ":"
+
+                for ctrl in attached_ctrls:
+                    # Controller ID can be described
+                    ctrl = ctrl.split('-')
+                    ctrl_type = ctrl[0]
+                    ctrl_id = -1
+
+                    # Get a controller ID if it is described
+                    if len(ctrl) == 2:
+                        ctrl_id = int(ctrl[1])
+
+                    # Controller should be cache (like L1, L2, etc)
+ # or directory (Dir) or none (no controllers are attached)
+                    cache_matched = cache_pattern.match(ctrl_type)
+                    assert(matched or ctrl_type == 'Dir' or
+                           ctrl_type == 'None')
+                    if cache_matched:
+                        ctrl_type += 'Cache_Controller'
+                    elif ctrl_type == 'Dir':
+                        ctrl_type = 'Directory_Controller'
+                    # No controllers are attached
+                    else:
+                        break;
+
+                    # If controller ID is not specified,
+                    # use the controller in the order maded by the protocol
+                    if ctrl_id == -1:
+                        ctrl_obj = controllers[ctrl_type].popleft()
+                    # If controller ID is specified in the layout file,
+                    # pop the controller of specified ID
+                    else:
+                        for ctrl_obj in controllers[ctrl_type]:
+                            if ctrl_obj.version.getValue() == ctrl_id:
+                                controllers[ctrl_type].remove(ctrl_obj)
+                                break
+                        # The controller must be in the queue
+                        assert(ctrl_obj.version.getValue() == ctrl_id)
+
+                    router_id = col + (row * num_columns)
+                    ext_links.append(ExtLink(link_id=link_count,
+                                             ext_node= ctrl_obj,
+                                             int_node=routers[router_id],
+                                             latency = link_latency))
+                    link_count += 1
+
+        # If we have a remaining directory controller,
+        # it is a boot ROM directory and connected to router 0
+        ctrl_type = 'Directory_Controller'
+        if controllers[ctrl_type]:
+            ctrl_obj = controllers[ctrl_type].popleft()
+            ext_links.append(ExtLink(link_id=link_count,
+                                     ext_node= ctrl_obj,
+                                     int_node=routers[0],
+                                    latency = link_latency))
+            link_count += 1
+
+        # DMA nodes are connected to router 0
+        ctrl_type = 'DMA_Controller'
+        while controllers[ctrl_type]:
+            ctrl_obj = controllers[ctrl_type].popleft()
+            ext_links.append(ExtLink(link_id=link_count,
+                                     ext_node= ctrl_obj,
+                                     int_node=routers[0],
+                                    latency = link_latency))
+            link_count += 1
+
+        # All controllers generated by the protocol
+        # should be matched with the layout file
+        for ctrl_type in ctrl_type_list:
+            assert(not controllers[ctrl_type])
+
+        network.ext_links = ext_links
+
+        # Create the mesh links.
+        int_links = []
+
+        # East output to West input links (weight = 1)
+        for row in range(num_rows):
+            for col in range(num_columns):
+                if (col + 1 < num_columns):
+                    east_out = col + (row * num_columns)
+                    west_in = (col + 1) + (row * num_columns)
+                    int_links.append(IntLink(link_id=link_count,
+                                             src_node=routers[east_out],
+                                             dst_node=routers[west_in],
+                                             src_outport="East",
+                                             dst_inport="West",
+                                             latency = link_latency,
+                                             weight=1))
+                    link_count += 1
+
+        # West output to East input links (weight = 1)
+        for row in range(num_rows):
+            for col in range(num_columns):
+                if (col + 1 < num_columns):
+                    east_in = col + (row * num_columns)
+                    west_out = (col + 1) + (row * num_columns)
+                    int_links.append(IntLink(link_id=link_count,
+                                             src_node=routers[west_out],
+                                             dst_node=routers[east_in],
+                                             src_outport="West",
+                                             dst_inport="East",
+                                             latency = link_latency,
+                                             weight=1))
+                    link_count += 1
+
+        # North output to South input links (weight = 2)
+        for col in range(num_columns):
+            for row in range(num_rows):
+                if (row + 1 < num_rows):
+                    north_out = col + (row * num_columns)
+                    south_in = col + ((row + 1) * num_columns)
+                    int_links.append(IntLink(link_id=link_count,
+                                             src_node=routers[north_out],
+                                             dst_node=routers[south_in],
+                                             src_outport="North",
+                                             dst_inport="South",
+                                             latency = link_latency,
+                                             weight=2))
+                    link_count += 1
+
+        # South output to North input links (weight = 2)
+        for col in range(num_columns):
+            for row in range(num_rows):
+                if (row + 1 < num_rows):
+                    north_in = col + (row * num_columns)
+                    south_out = col + ((row + 1) * num_columns)
+                    int_links.append(IntLink(link_id=link_count,
+                                             src_node=routers[south_out],
+                                             dst_node=routers[north_in],
+                                             src_outport="South",
+                                             dst_inport="North",
+                                             latency = link_latency,
+                                             weight=2))
+                    link_count += 1
+
+
+        network.int_links = int_links
+
+    # Register nodes with filesystem
+    def registerTopology(self, options):
+        for i in range(options.num_cpus):
+            FileSystemConfig.register_node([i],
+                    MemorySize(options.mem_size) // options.num_cpus, i)
diff --git a/configs/topologies/layout_example.txt b/configs/topologies/layout_example.txt
new file mode 100644
index 0000000..98b5eae
--- /dev/null
+++ b/configs/topologies/layout_example.txt
@@ -0,0 +1,40 @@
+# This is an example of 6x8 mesh layout.
+# The example assumes that MESI_Three_Level protocol is used.
+# 64 CPUs (L0 and L1) shared caches (L2) are connected in 32 router nodes
+# and 8 directories are distributed on each right and left side of the mesh.
+# 8 router nodes at four corners have no controllers attached.
+#
+# First two attribute should be number of rows and columns like below.
+#   row: {number of rows}, cols: {number of columns}
+#
+# Then, external controllers attached to each router node are described.
+# The format is like below.
+#   nodes: {row number}/{column number}: {attached controller list},
+# Each controller is separated with space.
+# A cache is described as 'L{cache level}'
+# and a directory is described as 'Dir'.
+# If no controllers are attatched, the controller list is described as 'None'.
+#
+# Note that the same controller configuration is applied
+# until the router node of specified column nubmer.
+# For example, the routers in the first row of the example layout
+# have the controllers like below:
+#   0/0, 0/1: No controllers are attched
+#   0/2, 0/3, 0/4, 0/5: L0 L1 L2 L0 L1 L2
+#   0/6, 0/7: No controllers are attched
+#
+# When describing controller list for each router,
+# controller ID can be specified additionally.
+# The format is {controller type}-{controller ID}.
+# For instance, if you want to specify L0 and L1 cache with ID of 1,
+# and a directory with ID of 7, describtion is like below.
+#   0/0: L0-1 L1-1 Dir-7,
+
+rows: 6,
+cols: 8,
+nodes: 0/1: None, 0/5: L0 L1 L2 L0 L1 L2, 0/7: None,
+       1/0: Dir,  1/6: L0 L1 L2 L0 L1 L2, 1/7: Dir,
+       2/0: Dir,  2/6: L0 L1 L2 L0 L1 L2, 2/7: Dir,
+       3/0: Dir,  3/6: L0 L1 L2 L0 L1 L2, 3/7: Dir,
+       4/0: Dir,  4/6: L0 L1 L2 L0 L1 L2, 4/7: Dir,
+       5/1: None, 5/5: L0 L1 L2 L0 L1 L2, 5/7: None,

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/40279
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: Iea99a01d6f100c36d4a08a8677cb64912d6117d0
Gerrit-Change-Number: 40279
Gerrit-PatchSet: 1
Gerrit-Owner: Daecheol You <daecheol....@samsung.com>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list -- gem5-dev@gem5.org
To unsubscribe send an email to gem5-dev-le...@gem5.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to