lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 1 | # Copyright lowRISC contributors. |
| 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | |
| 5 | import logging as log |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 6 | |
Eunchan Kim | 837c796 | 2020-04-30 12:15:49 -0700 | [diff] [blame] | 7 | from .item import Edge, NodeType |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 8 | |
| 9 | |
| 10 | class Xbar: |
| 11 | """Xbar contains configurations to generate TL-UL crossbar. |
| 12 | """ |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 13 | def __init__(self): |
Weicai Yang | 88ced02 | 2020-11-30 15:34:56 -0800 | [diff] [blame] | 14 | self.clock = "" # str # primary clock of xbar |
| 15 | self.reset = "" # str # primary reset of xbar |
| 16 | self.name = "" # str # e.g. "main" --> main_xbar |
| 17 | self.ip_path = "" # additional path to generated rtl/dv folders: outdir/ip_path/rtl |
| 18 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 19 | self.blocks = [] |
| 20 | self.nodes = [] |
| 21 | self.edges = [] |
| 22 | self.clocks = [] |
Timothy Chen | 09d859b | 2019-11-08 14:01:12 -0800 | [diff] [blame] | 23 | self.resets = [] |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 24 | |
| 25 | def __repr__(self): |
Weicai Yang | a60ae7d | 2020-02-21 14:32:50 -0800 | [diff] [blame] | 26 | out = "<Xbar(%s) #nodes:%d clock:%s" % (self.name, len( |
| 27 | self.nodes), self.clock) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 28 | out += " #edges:%d>\n" % (len(self.edges)) |
| 29 | |
| 30 | # print nodes |
| 31 | out += " Nodes:\n" |
| 32 | for node in self.nodes: |
| 33 | out += " - " + node.name + "\n" |
| 34 | |
| 35 | out += " Edges:\n" |
| 36 | for edge in self.edges: |
| 37 | out += " - " + edge.us.name + " => " + edge.ds.name + "\n" |
| 38 | # print edges |
| 39 | return out |
| 40 | |
| 41 | def get_edges_from_node(self, node): # Node -> Edges |
| 42 | return [ |
| 43 | edge for edge in self.edges |
| 44 | if node.name in (edge.us.name, edge.ns.name) |
| 45 | ] |
| 46 | |
| 47 | def get_node(self, node): # str -> Node |
| 48 | result = [x for x in self.nodes if x.name == node] |
| 49 | if len(result) != 1: |
| 50 | raise # Exception |
| 51 | |
| 52 | return result[0] |
| 53 | |
| 54 | @property |
| 55 | def hosts(self): |
| 56 | return [x for x in self.nodes if x.node_type == NodeType.HOST] |
| 57 | |
| 58 | @property |
| 59 | def devices(self): |
| 60 | return [x for x in self.nodes if x.node_type == NodeType.DEVICE] |
| 61 | |
| 62 | @property |
| 63 | def socket_1ns(self): |
| 64 | return [x for x in self.nodes if x.node_type == NodeType.SOCKET_1N] |
| 65 | |
| 66 | def get_downstream_device(self, node): # Node -> Node |
| 67 | if (node.node_type == NodeType.DEVICE): |
| 68 | return node |
| 69 | |
| 70 | if len(node.ds) == 0: |
Weicai Yang | a60ae7d | 2020-02-21 14:32:50 -0800 | [diff] [blame] | 71 | log.error( |
| 72 | "Node (%s) doesn't have downstream Node: US(%s), DS(%s)" % |
| 73 | (node.name, ' '.join(map(repr, node.us)), ' '.join( |
| 74 | map(repr, node.ds)))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 75 | return self.get_downstream_device(node.ds[0].ds) |
| 76 | |
| 77 | def get_downstream_device_from_edge(self, edge): # Edge -> Node |
| 78 | return self.get_downstream_device(edge.ds) |
| 79 | |
| 80 | def get_leaf_from_s1n(self, node, idx): # Node -> int -> Node |
| 81 | """ get end-device node from Socket_1n's Downstream port |
| 82 | |
| 83 | Current implementation can't have multiple devices under the tree of |
| 84 | one downstream port in Socket_1N |
| 85 | """ |
| 86 | return self.get_downstream_device(node.ds[idx].ds) |
| 87 | |
| 88 | def get_s1n_if_exist(self, node): # Node -> Node |
| 89 | """ return SOCKET_1N if exists down from the node, if not return itself |
| 90 | """ |
| 91 | if node.node_type == NodeType.DEVICE: |
| 92 | log.error("get_s1n_if_exist hits DEVICE type (unexpected)") |
| 93 | return node |
| 94 | if node.node_type == NodeType.SOCKET_1N: |
| 95 | return node |
| 96 | return self.get_s1n_if_exist(node.ds[0].ds) |
| 97 | |
| 98 | def get_leaf_from_node(self, node, idx): # Node -> int -> Node |
| 99 | """ get end device node from any node, idx is given to look down. |
| 100 | """ |
| 101 | num_dev = len(self.get_s1n_if_exist(node).ds) |
| 102 | if idx >= num_dev: |
| 103 | log.error( |
| 104 | "given index is greater than number of devices under the node") |
| 105 | |
| 106 | return self.get_leaf_from_s1n(self.get_s1n_if_exist(node), idx) |
| 107 | |
| 108 | def get_devices_from_host(self, host): # Node -> Nodes |
| 109 | devices = list( |
| 110 | map(self.get_downstream_device_from_edge, |
| 111 | self.get_s1n_if_exist(host).ds)) |
| 112 | |
| 113 | return devices |
| 114 | |
| 115 | def get_addr(self, device): # Node -> Tuple[int,int] |
| 116 | if device.node_type != NodeType.DEVICE: |
| 117 | log.error("get_addr receives non DEVICE type node") |
| 118 | |
| 119 | return (device.address_from, device.address_to) |
| 120 | |
| 121 | def connect_nodes(self, u_node, d_node): # str -> str -> bool |
| 122 | # Create edges between Nodes |
| 123 | # Return false if Nodes aren't exist or same connection exists |
| 124 | upNode = self.get_node(u_node) |
| 125 | dnNode = self.get_node(d_node) |
| 126 | |
| 127 | edge = Edge(upNode, dnNode) |
| 128 | |
| 129 | if any([ |
| 130 | e.us.name == edge.us.name and e.ds.name == edge.ds.name |
| 131 | for e in self.edges |
| 132 | ]): |
| 133 | return False |
| 134 | |
| 135 | self.edges.append(edge) |
| 136 | |
| 137 | upNode.ds.append(edge) |
| 138 | dnNode.us.append(edge) |
| 139 | |
| 140 | return True |
| 141 | |
| 142 | def insert_node(self, new_node, node): |
| 143 | if new_node.node_type == NodeType.ASYNC_FIFO: |
| 144 | if node.node_type == NodeType.HOST: |
| 145 | # Insert node to downstream |
| 146 | edge = Edge(node, new_node) |
| 147 | new_node.ds = node.ds |
| 148 | node.ds = [edge] |
| 149 | new_node.us = [edge] |
| 150 | self.nodes.append(new_node) |
| 151 | self.edges.append(edge) |
| 152 | for e in new_node.ds: |
| 153 | # replace us to new_node |
| 154 | e.us = new_node |
| 155 | elif node.node_type == NodeType.DEVICE: |
| 156 | # insert node to upstream |
| 157 | edge = Edge(new_node, node) |
| 158 | new_node.us = node.us |
| 159 | new_node.ds = node |
| 160 | node.us = [edge] |
| 161 | new_node.ds = [edge] |
| 162 | self.nodes.append(new_node) |
| 163 | self.edges.append(edge) |
| 164 | for e in new_node.us: |
| 165 | # replace us to new_node |
| 166 | e.ds = new_node |
| 167 | else: |
| 168 | raise |
| 169 | elif new_node.node_type == NodeType.SOCKET_M1: |
| 170 | # Revise every upstream |
| 171 | edge = Edge(new_node, node) |
| 172 | new_node.us = node.us |
| 173 | node.us = [edge] |
| 174 | new_node.ds = [edge] |
| 175 | self.nodes.append(new_node) |
| 176 | self.edges.append(edge) |
| 177 | for e in new_node.us: |
| 178 | e.ds = new_node |
| 179 | |
| 180 | elif new_node.node_type == NodeType.SOCKET_1N: |
| 181 | # Revise every downstream |
| 182 | edge = Edge(node, new_node) |
| 183 | new_node.ds = node.ds |
| 184 | node.ds = [edge] |
| 185 | new_node.us = [edge] |
| 186 | # TODO: add new_node.us logic |
| 187 | self.nodes.append(new_node) |
| 188 | self.edges.append(edge) |
| 189 | for e in new_node.ds: |
| 190 | e.us = new_node |
| 191 | else: |
| 192 | # Caller passes HOST or DEVICE as a new node. Error! |
| 193 | log.error( |
| 194 | "Xbar.insert_node is called with HOST or DEVICE: %s. Ignored" % |
| 195 | (new_node.name)) |
| 196 | |
| 197 | return self |
| 198 | |
| 199 | def repr_tree(self, node, indent): |
| 200 | """string format of tree connection from node to devices |
| 201 | |
| 202 | Desired output: |
| 203 | host_a |
| 204 | -> asf_nn |
| 205 | -> s1n_nn |
| 206 | -> sm1_mm |
| 207 | -> device_c |
| 208 | -> sm1_nn |
| 209 | -> device_b |
| 210 | |
| 211 | """ |
| 212 | out = "// " |
| 213 | if indent != 0: |
| 214 | # not First |
| 215 | out += ' ' * indent + '-> ' |
| 216 | |
| 217 | out += node.name |
| 218 | |
| 219 | if node.node_type != NodeType.DEVICE: |
| 220 | # still more nodes exist under this node |
| 221 | for ds in node.ds: |
| 222 | out += '\n' |
| 223 | out += self.repr_tree(ds.ds, indent + 2) |
| 224 | |
| 225 | return out |