blob: 09a32acb1fbe5a017d1a3d8aa86d9270382391ff [file] [log] [blame]
# 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