| # Copyright lowRISC contributors. |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import logging as log |
| |
| from .item import Edge, NodeType |
| |
| |
| class Xbar: |
| """Xbar contains configurations to generate TL-UL crossbar. |
| """ |
| def __init__(self): |
| self.clock = "" # str # primary clock of xbar |
| self.reset = "" # str # primary reset of xbar |
| self.name = "" # str # e.g. "main" --> main_xbar |
| self.ip_path = "" # additional path to generated rtl/dv folders: outdir/ip_path/rtl |
| |
| self.blocks = [] |
| self.nodes = [] |
| self.edges = [] |
| self.clocks = [] |
| self.resets = [] |
| |
| def __repr__(self): |
| out = "<Xbar(%s) #nodes:%d clock:%s" % (self.name, len( |
| self.nodes), self.clock) |
| out += " #edges:%d>\n" % (len(self.edges)) |
| |
| # print nodes |
| out += " Nodes:\n" |
| for node in self.nodes: |
| out += " - " + node.name + "\n" |
| |
| out += " Edges:\n" |
| for edge in self.edges: |
| out += " - " + edge.us.name + " => " + edge.ds.name + "\n" |
| # print edges |
| return out |
| |
| def get_edges_from_node(self, node): # Node -> Edges |
| return [ |
| edge for edge in self.edges |
| if node.name in (edge.us.name, edge.ns.name) |
| ] |
| |
| def get_node(self, node): # str -> Node |
| result = [x for x in self.nodes if x.name == node] |
| if len(result) != 1: |
| raise # Exception |
| |
| return result[0] |
| |
| @property |
| def hosts(self): |
| return [x for x in self.nodes if x.node_type == NodeType.HOST] |
| |
| @property |
| def devices(self): |
| return [x for x in self.nodes if x.node_type == NodeType.DEVICE] |
| |
| @property |
| def socket_1ns(self): |
| return [x for x in self.nodes if x.node_type == NodeType.SOCKET_1N] |
| |
| def get_downstream_device(self, node): # Node -> Node |
| if (node.node_type == NodeType.DEVICE): |
| return node |
| |
| if len(node.ds) == 0: |
| log.error( |
| "Node (%s) doesn't have downstream Node: US(%s), DS(%s)" % |
| (node.name, ' '.join(map(repr, node.us)), ' '.join( |
| map(repr, node.ds)))) |
| return self.get_downstream_device(node.ds[0].ds) |
| |
| def get_downstream_device_from_edge(self, edge): # Edge -> Node |
| return self.get_downstream_device(edge.ds) |
| |
| def get_leaf_from_s1n(self, node, idx): # Node -> int -> Node |
| """ get end-device node from Socket_1n's Downstream port |
| |
| Current implementation can't have multiple devices under the tree of |
| one downstream port in Socket_1N |
| """ |
| return self.get_downstream_device(node.ds[idx].ds) |
| |
| def get_s1n_if_exist(self, node): # Node -> Node |
| """ return SOCKET_1N if exists down from the node, if not return itself |
| """ |
| if node.node_type == NodeType.DEVICE: |
| log.error("get_s1n_if_exist hits DEVICE type (unexpected)") |
| return node |
| if node.node_type == NodeType.SOCKET_1N: |
| return node |
| return self.get_s1n_if_exist(node.ds[0].ds) |
| |
| def get_leaf_from_node(self, node, idx): # Node -> int -> Node |
| """ get end device node from any node, idx is given to look down. |
| """ |
| num_dev = len(self.get_s1n_if_exist(node).ds) |
| if idx >= num_dev: |
| log.error( |
| "given index is greater than number of devices under the node") |
| |
| return self.get_leaf_from_s1n(self.get_s1n_if_exist(node), idx) |
| |
| def get_devices_from_host(self, host): # Node -> Nodes |
| devices = list( |
| map(self.get_downstream_device_from_edge, |
| self.get_s1n_if_exist(host).ds)) |
| |
| return devices |
| |
| def get_addr(self, device): # Node -> Tuple[int,int] |
| if device.node_type != NodeType.DEVICE: |
| log.error("get_addr receives non DEVICE type node") |
| |
| return (device.address_from, device.address_to) |
| |
| def connect_nodes(self, u_node, d_node): # str -> str -> bool |
| # Create edges between Nodes |
| # Return false if Nodes aren't exist or same connection exists |
| upNode = self.get_node(u_node) |
| dnNode = self.get_node(d_node) |
| |
| edge = Edge(upNode, dnNode) |
| |
| if any([ |
| e.us.name == edge.us.name and e.ds.name == edge.ds.name |
| for e in self.edges |
| ]): |
| return False |
| |
| self.edges.append(edge) |
| |
| upNode.ds.append(edge) |
| dnNode.us.append(edge) |
| |
| return True |
| |
| def insert_node(self, new_node, node): |
| if new_node.node_type == NodeType.ASYNC_FIFO: |
| if node.node_type == NodeType.HOST: |
| # Insert node to downstream |
| edge = Edge(node, new_node) |
| new_node.ds = node.ds |
| node.ds = [edge] |
| new_node.us = [edge] |
| self.nodes.append(new_node) |
| self.edges.append(edge) |
| for e in new_node.ds: |
| # replace us to new_node |
| e.us = new_node |
| elif node.node_type == NodeType.DEVICE: |
| # insert node to upstream |
| edge = Edge(new_node, node) |
| new_node.us = node.us |
| new_node.ds = node |
| node.us = [edge] |
| new_node.ds = [edge] |
| self.nodes.append(new_node) |
| self.edges.append(edge) |
| for e in new_node.us: |
| # replace us to new_node |
| e.ds = new_node |
| else: |
| raise |
| elif new_node.node_type == NodeType.SOCKET_M1: |
| # Revise every upstream |
| edge = Edge(new_node, node) |
| new_node.us = node.us |
| node.us = [edge] |
| new_node.ds = [edge] |
| self.nodes.append(new_node) |
| self.edges.append(edge) |
| for e in new_node.us: |
| e.ds = new_node |
| |
| elif new_node.node_type == NodeType.SOCKET_1N: |
| # Revise every downstream |
| edge = Edge(node, new_node) |
| new_node.ds = node.ds |
| node.ds = [edge] |
| new_node.us = [edge] |
| # TODO: add new_node.us logic |
| self.nodes.append(new_node) |
| self.edges.append(edge) |
| for e in new_node.ds: |
| e.us = new_node |
| else: |
| # Caller passes HOST or DEVICE as a new node. Error! |
| log.error( |
| "Xbar.insert_node is called with HOST or DEVICE: %s. Ignored" % |
| (new_node.name)) |
| |
| return self |
| |
| def repr_tree(self, node, indent): |
| """string format of tree connection from node to devices |
| |
| Desired output: |
| host_a |
| -> asf_nn |
| -> s1n_nn |
| -> sm1_mm |
| -> device_c |
| -> sm1_nn |
| -> device_b |
| |
| """ |
| out = "// " |
| if indent != 0: |
| # not First |
| out += ' ' * indent + '-> ' |
| |
| out += node.name |
| |
| if node.node_type != NodeType.DEVICE: |
| # still more nodes exist under this node |
| for ds in node.ds: |
| out += '\n' |
| out += self.repr_tree(ds.ds, indent + 2) |
| |
| return out |