| # 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 Node, NodeType |
| from .xbar import Xbar |
| |
| |
| def elaborate(xbar: Xbar) -> bool: |
| """elaborate reads all nodes and edges then |
| construct internal FIFOs, Sockets. |
| """ |
| # Condition check |
| if len(xbar.nodes) <= 1 or len(xbar.edges) == 0: |
| log.error( |
| "# of Nodes is less than 2 or no Edge exists. Cannot proceed.") |
| return False |
| |
| for host in xbar.hosts: |
| process_node(host, xbar) |
| log.info("Node Processed: " + repr(xbar)) |
| |
| # Pipeline |
| process_pipeline(xbar) |
| |
| # Build address map |
| # Each socket_1n should have address map |
| |
| return True |
| |
| |
| def process_node(node, xbar): # node: Node -> xbar: Xbar -> Xbar |
| """process each node based on algorithm |
| |
| 1. If a node has different clock from main clock and not ASYNC_FIFO: |
| a. (New Node) Create ASYNC_FIFO node. |
| b. Revise every edges from the node to have start node as ASYNC_FIFO |
| node. (New Edge) create a edge from the node to ASYNC_FIFO node. |
| - Repeat the algorithm with ASYNC_FIFO node. |
| c. Revise every edges to the node to have end node as ASYNC_FIFO |
| node. (New Edge) create a edge from ASYNC_FIFO node to the node. |
| d. If it is not DEVICE, HOST node, raise Error. If it is DEVICE, end |
| (next item). |
| 2. If a node has multiple edges having it as a end node and not SOCKET_M1: |
| a. (New node) Create SOCKET_M1 node. |
| b. Revise every edges to the node to have SOCKET_M1 node as end node. |
| c. (New Edge) create a edge from SOCKET_M1 to the node. |
| d. Repeat the algorithm with the node. |
| 3. If a node has multiple edges having it as a start node and not SOCKET_1N: |
| a. (New node) Create SOCKET_1N node. |
| b. Revise every edges from the node to have SOCKET_1N node as start node. |
| c. (New Edge) Create a edge from the node to SOCKET_1N node. |
| d. (for loop) Repeat the algorithm with SOCKET_1N's other side node. |
| """ |
| |
| # If a node has different clock from main clock and not ASYNC_FIFO: |
| if node.node_type != NodeType.ASYNC_FIFO and node.clocks[0] != xbar.clock: |
| # (New Node) Create ASYNC_FIFO node |
| new_node = Node(name="asf_" + str(len(xbar.nodes)), |
| node_type=NodeType.ASYNC_FIFO, |
| clock=xbar.clock, |
| reset=xbar.reset) |
| |
| # if node is HOST, host clock synchronizes into xbar domain |
| # if node is DEVICE, xbar synchronizes into device clock domain |
| if node.node_type == NodeType.HOST: |
| new_node.clocks.insert(0, node.clocks[0]) |
| new_node.resets.insert(0, node.resets[0]) |
| else: |
| new_node.clocks.append(node.clocks[0]) |
| new_node.resets.append(node.resets[0]) |
| |
| xbar.insert_node(new_node, node) |
| |
| process_node(new_node, xbar) |
| |
| # If a node has multiple edges having it as a end node and not SOCKET_M1: |
| elif node.node_type != NodeType.SOCKET_M1 and len(node.us) > 1: |
| # (New node) Create SOCKET_M1 node |
| new_node = Node(name="sm1_" + str(len(xbar.nodes)), |
| node_type=NodeType.SOCKET_M1, |
| clock=xbar.clock, |
| reset=xbar.reset) |
| new_node.hdepth = 2 |
| new_node.hpass = 2**len(node.us) - 1 |
| new_node.ddepth = 2 |
| new_node.dpass = 1 |
| xbar.insert_node(new_node, node) |
| process_node(new_node, xbar) |
| |
| # If a node has multiple edges having it as a start node and not SOCKET_1N: |
| elif node.node_type != NodeType.SOCKET_1N and len(node.ds) > 1: |
| # (New node) Create SOCKET_1N node |
| new_node = Node(name="s1n_" + str(len(xbar.nodes)), |
| node_type=NodeType.SOCKET_1N, |
| clock=xbar.clock, |
| reset=xbar.reset) |
| new_node.hdepth = 2 |
| new_node.hpass = 1 |
| new_node.ddepth = 2 |
| new_node.dpass = 2**len(node.ds) - 1 |
| xbar.insert_node(new_node, node) |
| |
| # (for loop) Repeat the algorithm with SOCKET_1N's other side node |
| for edge in new_node.ds: |
| process_node(edge.ds, xbar) |
| |
| return xbar |
| |
| |
| def process_pipeline(xbar): |
| """Check if HOST, DEVICE has settings different from default, then propagate it to end |
| """ |
| for host in xbar.hosts: |
| # go downstream and change the HReqPass/Depth at the first instance. |
| # If it is async, skip. |
| # If Socket 1N, |
| # if pipeline True and bypass false, set hpass to 0 |
| # if pipeline is False, set depth to 0 |
| # If Socket M1, find position of the host and follow procedure above |
| # If it is device, it means host and device are directly connected. Ignore now. |
| |
| # After process node is done, always only one downstream exists in any host node |
| if host.pipeline is True and host.pipeline_byp is True: |
| # No need to process, same as default |
| continue |
| |
| no_bypass = (host.pipeline is True and host.pipeline_byp is False) |
| dnode = host.ds[0].ds |
| |
| if dnode.node_type == NodeType.ASYNC_FIFO: |
| continue |
| |
| if dnode.node_type == NodeType.SOCKET_1N: |
| dnode.hpass = 0 if no_bypass else dnode.hpass |
| |
| elif dnode.node_type == NodeType.SOCKET_M1: |
| idx = dnode.us.index(host.ds) |
| dnode.hpass = dnode.hpass ^ ( |
| 1 << idx) if no_bypass else dnode.hpass |
| |
| # keep variables separate in case we ever need to differentiate |
| dnode.dpass = 0 if no_bypass else dnode.dpass |
| dnode.hdepth = 0 if host.pipeline is False else dnode.hdepth |
| dnode.ddepth = dnode.hdepth |
| |
| for device in xbar.devices: |
| # go upstream and set DReq/RspPass at the first instance. |
| # If it is async, skip |
| # If Socket M1 |
| # If pipeline True and bypass False, set dpass to 0 |
| # If pipeline False, set depth to 0 |
| # If Socket 1N, find position of the device and follow procedure above |
| # If it is host, ignore |
| |
| if device.pipeline is True and device.pipeline_byp is True: |
| continue |
| |
| no_bypass = (device.pipeline is True and device.pipeline_byp is False) |
| unode = device.us[0].us |
| |
| if unode.node_type == NodeType.ASYNC_FIFO: |
| continue |
| |
| if unode.node_type == NodeType.SOCKET_1N: |
| idx = unode.ds.index(device.us[0]) |
| unode.dpass = unode.dpass ^ ( |
| 1 << idx) if no_bypass else unode.dpass |
| |
| elif unode.node_type == NodeType.SOCKET_M1: |
| unode.dpass = 0 if no_bypass else unode.dpass |
| |
| # keep variables separate in case we ever need to differentiate |
| unode.hpass = 0 if no_bypass else unode.hpass |
| unode.ddepth = 0 if device.pipeline is False else unode.ddepth |
| unode.hdepth = unode.ddepth |
| |
| return xbar |