|  | # 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) | 
|  |  | 
|  | # By default, assume connecting to SOCKET_1N upstream and bypass all FIFOs | 
|  | # If upstream requires pipelining, it will be added through process pipeline | 
|  | new_node.hdepth = 0 | 
|  | new_node.hreq_pass = 2**len(node.us) - 1 | 
|  | new_node.hrsp_pass = 2**len(node.us) - 1 | 
|  | new_node.ddepth = 0 | 
|  | new_node.dreq_pass = 1 | 
|  | new_node.drsp_pass = 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) | 
|  |  | 
|  | # By default, assume connecting to SOCKET_M1 downstream and bypass all FIFOs | 
|  | # If upstream requires pipelining, it will be added through process pipeline | 
|  | new_node.hdepth = 0 | 
|  | new_node.hreq_pass = 1 | 
|  | new_node.hrsp_pass = 1 | 
|  | new_node.ddepth = 0 | 
|  | new_node.dreq_pass = 2**len(node.ds) - 1 | 
|  | new_node.drsp_pass = 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. | 
|  |  | 
|  | log.info("Processing pipeline for host {}".format(host.name)) | 
|  |  | 
|  | fifo_pass = host.req_fifo_pass or host.rsp_fifo_pass | 
|  |  | 
|  | # FIFO present with no passthrough option | 
|  | # FIFO present with passthrough option | 
|  | # FIFO not present and full passthrough | 
|  | full_fifo = False | 
|  | fifo_passthru = False | 
|  | full_passthru = True | 
|  | if host.pipeline is True and fifo_pass is False: | 
|  | full_fifo = True | 
|  |  | 
|  | elif host.pipeline is True and fifo_pass is True: | 
|  | fifo_passthru = True | 
|  |  | 
|  | elif host.pipeline is False: | 
|  | full_passthru = True | 
|  |  | 
|  | dnode = host.ds[0].ds | 
|  |  | 
|  | if dnode.node_type == NodeType.ASYNC_FIFO: | 
|  | continue | 
|  |  | 
|  | req_pass = 1 if host.req_fifo_pass else 0 | 
|  | rsp_pass = 1 if host.rsp_fifo_pass else 0 | 
|  | if dnode.node_type == NodeType.SOCKET_1N: | 
|  | if full_fifo: | 
|  | dnode.hreq_pass = 0 | 
|  | dnode.hrsp_pass = 0 | 
|  | dnode.hdepth = 2 | 
|  | elif fifo_passthru: | 
|  | dnode.hreq_pass = req_pass | 
|  | dnode.hrsp_pass = rsp_pass | 
|  | dnode.hdepth = 2 | 
|  | elif full_passthru: | 
|  | dnode.hreq_pass = 1 | 
|  | dnode.hrsp_pass = 1 | 
|  | dnode.hdepth = 0 | 
|  |  | 
|  | log.info( | 
|  | "Finished processing socket1n {}, req pass={}, rsp pass={}, depth={}".format( | 
|  | dnode.name, dnode.hreq_pass, dnode.hrsp_pass, dnode.hdepth)) | 
|  |  | 
|  | elif dnode.node_type == NodeType.SOCKET_M1: | 
|  | idx = dnode.us.index(host.ds[0]) | 
|  |  | 
|  | # first clear out entry | 
|  | dnode.hreq_pass = dnode.hreq_pass & ~(1 << idx) | 
|  | dnode.hreq_pass = dnode.hreq_pass & ~(1 << idx) | 
|  | if full_fifo: | 
|  | log.info("fifo present no bypass") | 
|  | dnode.hdepth = dnode.hdepth | (2 << idx * 4) | 
|  | elif fifo_passthru: | 
|  | log.info("fifo present with bypass") | 
|  | dnode.hreq_pass = dnode.hreq_pass | (req_pass << idx) | 
|  | dnode.hreq_pass = dnode.hrsp_pass | (rsp_pass << idx) | 
|  | dnode.hdepth = dnode.hdepth | (2 << idx * 4) | 
|  | elif full_passthru: | 
|  | log.info("fifo not present") | 
|  | dnode.hreq_pass = dnode.hreq_pass | (1 << idx) | 
|  | dnode.hreq_pass = dnode.hrsp_pass | (1 << idx) | 
|  | dnode.hdepth = dnode.hdepth & ~(0xF << idx * 4) | 
|  |  | 
|  | log.info( | 
|  | "Finished processing socketm1 {}, req pass={}, rsp pass={}, depth={}".format( | 
|  | dnode.name, dnode.hreq_pass, dnode.hrsp_pass, 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 | 
|  |  | 
|  | log.info("Processing pipeline for device {}".format(device.name)) | 
|  |  | 
|  | # FIFO present with no passthrough option | 
|  | # FIFO present with passthrough option | 
|  | # FIFO not present and full passthrough | 
|  | fifo_pass = device.req_fifo_pass or device.rsp_fifo_pass | 
|  | full_fifo = False | 
|  | fifo_passthru = False | 
|  | full_passthru = True | 
|  | if device.pipeline is True and fifo_pass is False: | 
|  | full_fifo = True | 
|  |  | 
|  | elif device.pipeline is True and fifo_pass is True: | 
|  | fifo_passthru = True | 
|  |  | 
|  | elif device.pipeline is False: | 
|  | full_passthru = True | 
|  |  | 
|  | unode = device.us[0].us | 
|  |  | 
|  | if unode.node_type == NodeType.ASYNC_FIFO: | 
|  | continue | 
|  |  | 
|  | req_pass = 1 if device.req_fifo_pass else 0 | 
|  | rsp_pass = 1 if device.rsp_fifo_pass else 0 | 
|  | if unode.node_type == NodeType.SOCKET_1N: | 
|  | idx = unode.ds.index(device.us[0]) | 
|  |  | 
|  | # first clear out entry | 
|  | unode.dreq_pass = unode.dreq_pass & ~(1 << idx) | 
|  | unode.drsp_pass = unode.drsp_pass & ~(1 << idx) | 
|  | if full_fifo: | 
|  | unode.ddepth = unode.ddepth | (2 << idx * 4) | 
|  | elif fifo_passthru: | 
|  | unode.dreq_pass = unode.dreq_pass | (req_pass << idx) | 
|  | unode.drsp_pass = unode.drsp_pass | (rsp_pass << idx) | 
|  | unode.ddepth = unode.ddepth | (2 << idx * 4) | 
|  | elif full_passthru: | 
|  | unode.dreq_pass = unode.dreq_pass | (1 << idx) | 
|  | unode.drsp_pass = unode.drsp_pass | (1 << idx) | 
|  | unode.ddepth = unode.ddepth & ~(0xF << idx * 4) | 
|  |  | 
|  | log.info("Finished processing socket1n {}, req pass={:x}, req pass={:x} depth={:x}". | 
|  | format(unode.name, unode.dreq_pass, unode.drsp_pass, unode.ddepth)) | 
|  |  | 
|  | elif unode.node_type == NodeType.SOCKET_M1: | 
|  | if full_fifo: | 
|  | log.info("Fifo present with no passthrough") | 
|  | unode.dreq_pass = 0 | 
|  | unode.drsp_pass = 0 | 
|  | unode.ddepth = 2 | 
|  | elif fifo_passthru: | 
|  | log.info("Fifo present with passthrough") | 
|  | unode.dreq_pass = req_pass | 
|  | unode.drsp_pass = rsp_pass | 
|  | unode.ddepth = 2 | 
|  | elif full_passthru: | 
|  | log.info("No Fifo") | 
|  | unode.dreq_pass = 1 | 
|  | unode.drsp_pass = 1 | 
|  | unode.ddepth = 0 | 
|  |  | 
|  | log.info("Finished processing socketm1 {}, req pass={:x}, rsp pass={:x}, depth={:x}". | 
|  | format(unode.name, unode.dreq_pass, unode.drsp_pass, unode.ddepth)) | 
|  |  | 
|  | return xbar |