This fixes the routers being set as the links' children. This is necessary here as we only assign the routers to the network once all routers and links are created. Change-Id: I2ac90b575bad593ddbb8ab716872a30a5c4c6979 Signed-off-by: Tiago Mück <tiago.muck@arm.com> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/63677 Maintainer: Bobby Bruce <bbruce@ucdavis.edu> Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com>
524 lines
20 KiB
Python
524 lines
20 KiB
Python
# Copyright (c) 2021,2022 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.
|
|
#
|
|
|
|
import math
|
|
|
|
from m5.util import fatal
|
|
from m5.params import *
|
|
from m5.objects import *
|
|
|
|
from m5.defines import buildEnv
|
|
|
|
if buildEnv["PROTOCOL"] == "CHI":
|
|
import ruby.CHI_config as CHI
|
|
|
|
from topologies.BaseTopology import SimpleTopology
|
|
|
|
|
|
class CustomMesh(SimpleTopology):
|
|
description = "CustomMesh"
|
|
|
|
def __init__(self, controllers):
|
|
self.nodes = controllers
|
|
|
|
# --------------------------------------------------------------------------
|
|
# _makeMesh
|
|
# --------------------------------------------------------------------------
|
|
|
|
def _makeMesh(
|
|
self,
|
|
IntLink,
|
|
link_latency,
|
|
num_rows,
|
|
num_columns,
|
|
cross_links,
|
|
cross_link_latency,
|
|
):
|
|
|
|
# East->West, West->East, North->South, South->North
|
|
# XY routing weights
|
|
link_weights = [1, 1, 2, 2]
|
|
|
|
# East output to West input links
|
|
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)
|
|
llat = (
|
|
cross_link_latency
|
|
if (east_out, west_in) in cross_links
|
|
else link_latency
|
|
)
|
|
self._int_links.append(
|
|
IntLink(
|
|
link_id=self._link_count,
|
|
src_node=self._routers[east_out],
|
|
dst_node=self._routers[west_in],
|
|
dst_inport="West",
|
|
latency=llat,
|
|
weight=link_weights[0],
|
|
)
|
|
)
|
|
self._link_count += 1
|
|
|
|
# West output to East input links
|
|
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)
|
|
llat = (
|
|
cross_link_latency
|
|
if (west_out, east_in) in cross_links
|
|
else link_latency
|
|
)
|
|
self._int_links.append(
|
|
IntLink(
|
|
link_id=self._link_count,
|
|
src_node=self._routers[west_out],
|
|
dst_node=self._routers[east_in],
|
|
dst_inport="East",
|
|
latency=llat,
|
|
weight=link_weights[1],
|
|
)
|
|
)
|
|
self._link_count += 1
|
|
|
|
# North output to South input links
|
|
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)
|
|
llat = (
|
|
cross_link_latency
|
|
if (north_out, south_in) in cross_links
|
|
else link_latency
|
|
)
|
|
self._int_links.append(
|
|
IntLink(
|
|
link_id=self._link_count,
|
|
src_node=self._routers[north_out],
|
|
dst_node=self._routers[south_in],
|
|
dst_inport="South",
|
|
latency=llat,
|
|
weight=link_weights[2],
|
|
)
|
|
)
|
|
self._link_count += 1
|
|
|
|
# South output to North input links
|
|
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)
|
|
llat = (
|
|
cross_link_latency
|
|
if (south_out, north_in) in cross_links
|
|
else link_latency
|
|
)
|
|
self._int_links.append(
|
|
IntLink(
|
|
link_id=self._link_count,
|
|
src_node=self._routers[south_out],
|
|
dst_node=self._routers[north_in],
|
|
dst_inport="North",
|
|
latency=llat,
|
|
weight=link_weights[3],
|
|
)
|
|
)
|
|
self._link_count += 1
|
|
|
|
# --------------------------------------------------------------------------
|
|
# distributeNodes
|
|
# --------------------------------------------------------------------------
|
|
|
|
def _createRNFRouter(self, mesh_router):
|
|
# Create a zero-latency router bridging node controllers
|
|
# and the mesh router
|
|
node_router = self._Router(router_id=len(self._routers), latency=0)
|
|
self._routers.append(node_router)
|
|
|
|
# connect node_router <-> mesh router
|
|
self._int_links.append(
|
|
self._IntLink(
|
|
link_id=self._link_count,
|
|
src_node=node_router,
|
|
dst_node=mesh_router,
|
|
latency=self._router_link_latency,
|
|
)
|
|
)
|
|
self._link_count += 1
|
|
|
|
self._int_links.append(
|
|
self._IntLink(
|
|
link_id=self._link_count,
|
|
src_node=mesh_router,
|
|
dst_node=node_router,
|
|
latency=self._router_link_latency,
|
|
)
|
|
)
|
|
self._link_count += 1
|
|
|
|
return node_router
|
|
|
|
def distributeNodes(self, node_placement_config, node_list):
|
|
if len(node_list) == 0:
|
|
return
|
|
|
|
num_nodes_per_router = node_placement_config.num_nodes_per_router
|
|
router_idx_list = node_placement_config.router_list
|
|
|
|
if num_nodes_per_router:
|
|
# evenly distribute nodes to all listed routers
|
|
assert len(router_idx_list) * num_nodes_per_router == len(
|
|
node_list
|
|
)
|
|
|
|
for idx, node in enumerate(node_list):
|
|
mesh_router_idx = router_idx_list[idx // num_nodes_per_router]
|
|
router = self._routers[mesh_router_idx]
|
|
|
|
# Create another router bridging RNF node controllers
|
|
# and the mesh router
|
|
# for non-RNF nodes, node router is mesh router
|
|
if isinstance(node, CHI.CHI_RNF):
|
|
router = self._createRNFRouter(router)
|
|
|
|
# connect all ctrls in the node to node_router
|
|
ctrls = node.getNetworkSideControllers()
|
|
for c in ctrls:
|
|
self._ext_links.append(
|
|
self._ExtLink(
|
|
link_id=self._link_count,
|
|
ext_node=c,
|
|
int_node=router,
|
|
latency=self._node_link_latency,
|
|
)
|
|
)
|
|
self._link_count += 1
|
|
else:
|
|
# try to circulate all nodes to all routers, some routers may be
|
|
# connected to zero or more than one node.
|
|
idx = 0
|
|
for node in node_list:
|
|
ridx = router_idx_list[idx]
|
|
router = self._routers[ridx]
|
|
|
|
if isinstance(node, CHI.CHI_RNF):
|
|
router = self._createRNFRouter(router)
|
|
ctrls = node.getNetworkSideControllers()
|
|
for c in ctrls:
|
|
self._ext_links.append(
|
|
self._ExtLink(
|
|
link_id=self._link_count,
|
|
ext_node=c,
|
|
int_node=router,
|
|
latency=self._node_link_latency,
|
|
)
|
|
)
|
|
self._link_count += 1
|
|
idx = (idx + 1) % len(router_idx_list)
|
|
|
|
# --------------------------------------------------------------------------
|
|
# makeTopology
|
|
# --------------------------------------------------------------------------
|
|
|
|
def makeTopology(self, options, network, IntLink, ExtLink, Router):
|
|
assert buildEnv["PROTOCOL"] == "CHI"
|
|
|
|
num_rows = options.num_rows
|
|
num_cols = options.num_cols
|
|
num_mesh_routers = num_rows * num_cols
|
|
|
|
self._IntLink = IntLink
|
|
self._ExtLink = ExtLink
|
|
self._Router = Router
|
|
|
|
if hasattr(options, "router_link_latency"):
|
|
self._router_link_latency = options.router_link_latency
|
|
self._node_link_latency = options.node_link_latency
|
|
else:
|
|
print("WARNING: router/node link latencies not provided")
|
|
self._router_link_latency = options.link_latency
|
|
self._node_link_latency = options.link_latency
|
|
|
|
# classify nodes into different types
|
|
rnf_nodes = []
|
|
hnf_nodes = []
|
|
mn_nodes = []
|
|
mem_nodes = []
|
|
io_mem_nodes = []
|
|
rni_dma_nodes = []
|
|
rni_io_nodes = []
|
|
|
|
# Notice below that all the type must be the same for all nodes with
|
|
# the same base type.
|
|
rnf_params = None
|
|
hnf_params = None
|
|
mn_params = None
|
|
mem_params = None
|
|
io_mem_params = None
|
|
rni_dma_params = None
|
|
rni_io_params = None
|
|
|
|
def check_same(val, curr):
|
|
assert curr == None or curr == val
|
|
return val
|
|
|
|
for n in self.nodes:
|
|
if isinstance(n, CHI.CHI_RNF):
|
|
rnf_nodes.append(n)
|
|
rnf_params = check_same(type(n).NoC_Params, rnf_params)
|
|
elif isinstance(n, CHI.CHI_HNF):
|
|
hnf_nodes.append(n)
|
|
hnf_params = check_same(type(n).NoC_Params, hnf_params)
|
|
elif isinstance(n, CHI.CHI_MN):
|
|
mn_nodes.append(n)
|
|
mn_params = check_same(type(n).NoC_Params, mn_params)
|
|
elif isinstance(n, CHI.CHI_SNF_MainMem):
|
|
mem_nodes.append(n)
|
|
mem_params = check_same(type(n).NoC_Params, mem_params)
|
|
elif isinstance(n, CHI.CHI_SNF_BootMem):
|
|
io_mem_nodes.append(n)
|
|
io_mem_params = check_same(type(n).NoC_Params, io_mem_params)
|
|
elif isinstance(n, CHI.CHI_RNI_DMA):
|
|
rni_dma_nodes.append(n)
|
|
rni_dma_params = check_same(type(n).NoC_Params, rni_dma_params)
|
|
elif isinstance(n, CHI.CHI_RNI_IO):
|
|
rni_io_nodes.append(n)
|
|
rni_io_params = check_same(type(n).NoC_Params, rni_io_params)
|
|
else:
|
|
fatal(
|
|
"topologies.CustomMesh: {} not supported".format(
|
|
n.__class__.__name__
|
|
)
|
|
)
|
|
|
|
# Create all mesh routers
|
|
self._routers = [
|
|
Router(router_id=i, latency=options.router_latency)
|
|
for i in range(num_mesh_routers)
|
|
]
|
|
|
|
self._link_count = 0
|
|
self._int_links = []
|
|
self._ext_links = []
|
|
|
|
# Create all the mesh internal links.
|
|
self._makeMesh(
|
|
IntLink,
|
|
self._router_link_latency,
|
|
num_rows,
|
|
num_cols,
|
|
options.cross_links,
|
|
options.cross_link_latency,
|
|
)
|
|
|
|
# Place CHI_RNF on the mesh
|
|
self.distributeNodes(rnf_params, rnf_nodes)
|
|
|
|
# Place CHI_HNF on the mesh
|
|
self.distributeNodes(hnf_params, hnf_nodes)
|
|
|
|
# Place CHI_MN on the mesh
|
|
self.distributeNodes(mn_params, mn_nodes)
|
|
|
|
# Place CHI_SNF_MainMem on the mesh
|
|
self.distributeNodes(mem_params, mem_nodes)
|
|
|
|
# Place all IO mem nodes on the mesh
|
|
self.distributeNodes(io_mem_params, io_mem_nodes)
|
|
|
|
# Place all IO request nodes on the mesh
|
|
self.distributeNodes(rni_dma_params, rni_dma_nodes)
|
|
self.distributeNodes(rni_io_params, rni_io_nodes)
|
|
|
|
# Set up
|
|
network.int_links = self._int_links
|
|
network.ext_links = self._ext_links
|
|
# fix Routers being set as link child
|
|
for r in self._routers:
|
|
if r.has_parent():
|
|
r.get_parent().clear_child(r.get_name())
|
|
network.routers = self._routers
|
|
|
|
pairing = getattr(options, "pairing", None)
|
|
if pairing != None:
|
|
self._autoPairHNFandSNF(hnf_list, mem_ctrls, pairing)
|
|
|
|
# --------------------------------------------------------------------------
|
|
# _autoPair
|
|
# --------------------------------------------------------------------------
|
|
def _autoPairHNFandSNF(self, cache_ctrls, mem_ctrls, pairing):
|
|
# Use the pairing defined by the configuration to reassign the
|
|
# memory ranges
|
|
pair_debug = False
|
|
|
|
print("Pairing HNFs to SNFs")
|
|
print(pairing)
|
|
|
|
all_cache = []
|
|
for c in cache_ctrls:
|
|
all_cache.extend(c.getNetworkSideControllers())
|
|
all_mem = []
|
|
for c in mem_ctrls:
|
|
all_mem.extend(c.getNetworkSideControllers())
|
|
|
|
# checks and maps index from pairing map to component
|
|
assert len(pairing) == len(all_cache)
|
|
|
|
def _tolist(val):
|
|
return val if isinstance(val, list) else [val]
|
|
|
|
for m in all_mem:
|
|
m._pairing = []
|
|
|
|
pairing_check = max(1, len(all_mem) / len(all_cache))
|
|
for cidx, c in enumerate(all_cache):
|
|
c._pairing = []
|
|
for midx in _tolist(pairing[cidx]):
|
|
c._pairing.append(all_mem[midx])
|
|
if c not in all_mem[midx]._pairing:
|
|
all_mem[midx]._pairing.append(c)
|
|
assert len(c._pairing) == pairing_check
|
|
if pair_debug:
|
|
print(c.path())
|
|
for r in c.addr_ranges:
|
|
print("%s" % r)
|
|
for p in c._pairing:
|
|
print("\t" + p.path())
|
|
for r in p.addr_ranges:
|
|
print("\t%s" % r)
|
|
|
|
# all must be paired
|
|
for c in all_cache:
|
|
assert len(c._pairing) > 0
|
|
for m in all_mem:
|
|
assert len(m._pairing) > 0
|
|
|
|
# only support a single range for the main memory controllers
|
|
tgt_range_start = all_mem[0].addr_ranges[0].start.value
|
|
for mem in all_mem:
|
|
for r in mem.addr_ranges:
|
|
if r.start.value != tgt_range_start:
|
|
fatal(
|
|
"topologies.CustomMesh: not supporting pairing of "
|
|
"main memory with multiple ranges"
|
|
)
|
|
|
|
# reassign ranges for a 1 -> N paring
|
|
def _rerange(src_cntrls, tgt_cntrls, fix_tgt_peer):
|
|
assert len(tgt_cntrls) >= len(src_cntrls)
|
|
|
|
def _rangeToBit(addr_ranges):
|
|
bit = None
|
|
for r in addr_ranges:
|
|
if bit == None:
|
|
bit = r.intlvMatch
|
|
else:
|
|
assert bit == r.intlvMatch
|
|
return bit
|
|
|
|
def _getPeer(cntrl):
|
|
return cntrl.memory_out_port.peer.simobj
|
|
|
|
sorted_src = list(src_cntrls)
|
|
sorted_src.sort(key=lambda x: _rangeToBit(x.addr_ranges))
|
|
|
|
# paired controllers need to have seq. interleaving match values
|
|
intlvMatch = 0
|
|
for src in sorted_src:
|
|
for tgt in src._pairing:
|
|
for r in tgt.addr_ranges:
|
|
r.intlvMatch = intlvMatch
|
|
if fix_tgt_peer:
|
|
_getPeer(tgt).range.intlvMatch = intlvMatch
|
|
intlvMatch = intlvMatch + 1
|
|
|
|
# recreate masks
|
|
for src in sorted_src:
|
|
for src_range in src.addr_ranges:
|
|
if src_range.start.value != tgt_range_start:
|
|
continue
|
|
new_src_mask = []
|
|
for m in src_range.masks:
|
|
# TODO should mask all the way to the max range size
|
|
new_src_mask.append(
|
|
m | (m * 2) | (m * 4) | (m * 8) | (m * 16)
|
|
)
|
|
for tgt in src._pairing:
|
|
paired = False
|
|
for tgt_range in tgt.addr_ranges:
|
|
if tgt_range.start.value == src_range.start.value:
|
|
src_range.masks = new_src_mask
|
|
new_tgt_mask = []
|
|
lsbs = len(tgt_range.masks) - len(new_src_mask)
|
|
for i in range(lsbs):
|
|
new_tgt_mask.append(tgt_range.masks[i])
|
|
for m in new_src_mask:
|
|
new_tgt_mask.append(m)
|
|
tgt_range.masks = new_tgt_mask
|
|
if fix_tgt_peer:
|
|
_getPeer(tgt).range.masks = new_tgt_mask
|
|
paired = True
|
|
if not paired:
|
|
fatal(
|
|
"topologies.CustomMesh: could not "
|
|
"reassign ranges {} {}".format(
|
|
src.path(), tgt.path()
|
|
)
|
|
)
|
|
|
|
if len(all_mem) >= len(all_cache):
|
|
_rerange(all_cache, all_mem, True)
|
|
else:
|
|
_rerange(all_mem, all_cache, False)
|
|
|
|
if pair_debug:
|
|
print("")
|
|
for cidx, c in enumerate(all_cache):
|
|
assert len(c._pairing) == pairing_check
|
|
print(c.path())
|
|
for r in c.addr_ranges:
|
|
print("%s" % r)
|
|
for p in c._pairing:
|
|
print("\t" + p.path())
|
|
for r in p.addr_ranges:
|
|
print("\t%s" % r)
|