| # 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 = 1 |
| elif fifo_passthru: |
| dnode.hreq_pass = req_pass |
| dnode.hrsp_pass = rsp_pass |
| dnode.hdepth = 1 |
| 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 | (1 << 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 | (1 << 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 | (1 << 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 | (1 << 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 = 1 |
| elif fifo_passthru: |
| log.info("Fifo present with passthrough") |
| unode.dreq_pass = req_pass |
| unode.drsp_pass = rsp_pass |
| unode.ddepth = 1 |
| 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 |