| from data_structs import Loc | |
| from lib.rr_graph import tracks | |
| import lib.rr_graph.graph2 as rr | |
| # ============================================================================= | |
| def add_track(graph, track, segment_id, node_timing=None): | |
| """ | |
| Adds a track to the graph. Returns the node object representing the track | |
| node. | |
| """ | |
| if node_timing is None: | |
| node_timing = rr.NodeTiming(r=0.0, c=0.0) | |
| node_id = graph.add_track(track, segment_id, timing=node_timing) | |
| node = graph.nodes[-1] | |
| assert node.id == node_id | |
| return node | |
| def add_node(graph, loc, direction, segment_id): | |
| """ | |
| Adds a track of length 1 to the graph. Returns the node object | |
| """ | |
| return add_track( | |
| graph, | |
| tracks.Track( | |
| direction=direction, | |
| x_low=loc.x, | |
| x_high=loc.x, | |
| y_low=loc.y, | |
| y_high=loc.y, | |
| ), segment_id | |
| ) | |
| def add_edge( | |
| graph, src_node_id, dst_node_id, switch_id, meta_name=None, | |
| meta_value="" | |
| ): | |
| """ | |
| Adds an edge to the routing graph. If the given switch corresponds to a | |
| "pass" type switch then adds two edges going both ways. | |
| """ | |
| # Sanity check | |
| assert src_node_id != dst_node_id, \ | |
| (src_node_id, dst_node_id, switch_id, meta_name, meta_value) | |
| # Connect src to dst | |
| graph.add_edge(src_node_id, dst_node_id, switch_id, meta_name, meta_value) | |
| # Check if the switch is of the "pass" type. If so then add an edge going | |
| # in the opposite way. | |
| switch = graph.switch_map[switch_id] | |
| if switch.type in [rr.SwitchType.SHORT, rr.SwitchType.PASS_GATE]: | |
| graph.add_edge( | |
| dst_node_id, src_node_id, switch_id, meta_name, meta_value | |
| ) | |
| # ============================================================================= | |
| def node_joint_location(node_a, node_b): | |
| """ | |
| Given two VPR nodes returns a location of the point where they touch each | |
| other. | |
| """ | |
| loc_a1 = Loc(node_a.loc.x_low, node_a.loc.y_low, 0) | |
| loc_a2 = Loc(node_a.loc.x_high, node_a.loc.y_high, 0) | |
| loc_b1 = Loc(node_b.loc.x_low, node_b.loc.y_low, 0) | |
| loc_b2 = Loc(node_b.loc.x_high, node_b.loc.y_high, 0) | |
| if loc_a1 == loc_b1: | |
| return loc_a1 | |
| if loc_a1 == loc_b2: | |
| return loc_a1 | |
| if loc_a2 == loc_b1: | |
| return loc_a2 | |
| if loc_a2 == loc_b2: | |
| return loc_a2 | |
| assert False, (node_a, node_b) | |
| def connect( | |
| graph, | |
| src_node, | |
| dst_node, | |
| switch_id=None, | |
| segment_id=None, | |
| meta_name=None, | |
| meta_value="" | |
| ): | |
| """ | |
| Connect two VPR nodes in a way that certain rules are obeyed. | |
| The rules are: | |
| - a CHANX cannot connect directly to a CHANY and vice versa, | |
| - a CHANX cannot connect to an IPIN facing left or right, | |
| - a CHANY cannot connect to an IPIN facting top or bottom, | |
| - an OPIN facing left or right cannot connect to a CHANX, | |
| - an OPIN facing top or bottom cannot connect to a CHANY | |
| Whenever a rule is not met then the connection is made through a padding | |
| node: | |
| src -> [delayless] -> pad -> [desired switch] -> dst | |
| Otherwise the connection is made directly | |
| src -> [desired switch] -> dst | |
| The influence of whether the rules are obeyed or not on the actual VPR | |
| behavior is unclear. | |
| """ | |
| # Use the default delayless switch if none is given | |
| if switch_id is None: | |
| switch_id = graph.get_delayless_switch_id() | |
| # Determine which segment to use if none given | |
| if segment_id is None: | |
| # If the source is IPIN/OPIN then use the same segment as used by | |
| # the destination. | |
| # If the destination is IPIN/OPIN then do the opposite. | |
| # Finally if both are CHANX/CHANY then use the source's segment. | |
| if src_node.type in [rr.NodeType.IPIN, rr.NodeType.OPIN]: | |
| segment_id = dst_node.segment.segment_id | |
| elif dst_node.type in [rr.NodeType.IPIN, rr.NodeType.OPIN]: | |
| segment_id = src_node.segment.segment_id | |
| else: | |
| segment_id = src_node.segment.segment_id | |
| # CHANX to CHANY or vice-versa | |
| chanx_to_chany = src_node.type == rr.NodeType.CHANX and dst_node.type == rr.NodeType.CHANY | |
| chany_to_chanx = src_node.type == rr.NodeType.CHANY and dst_node.type == rr.NodeType.CHANX | |
| chany_to_chany = src_node.type == rr.NodeType.CHANY and dst_node.type == rr.NodeType.CHANY | |
| chanx_to_chanx = src_node.type == rr.NodeType.CHANX and dst_node.type == rr.NodeType.CHANX | |
| if chany_to_chanx or chanx_to_chany: | |
| # Check loc | |
| node_joint_location(src_node, dst_node) | |
| # Connect directly | |
| add_edge( | |
| graph, src_node.id, dst_node.id, switch_id, meta_name, meta_value | |
| ) | |
| # CHANX to CHANX or CHANY to CHANY | |
| elif chany_to_chany or chanx_to_chanx: | |
| loc = node_joint_location(src_node, dst_node) | |
| direction = "X" if src_node.type == rr.NodeType.CHANY else "Y" | |
| # Padding node | |
| pad_node = add_node(graph, loc, direction, segment_id) | |
| # Connect through the padding node | |
| add_edge( | |
| graph, src_node.id, pad_node.id, graph.get_delayless_switch_id() | |
| ) | |
| add_edge( | |
| graph, pad_node.id, dst_node.id, switch_id, meta_name, meta_value | |
| ) | |
| # OPIN to CHANX/CHANY | |
| elif src_node.type == rr.NodeType.OPIN and dst_node.type in \ | |
| [rr.NodeType.CHANX, rr.NodeType.CHANY]: | |
| # All OPINs go right (towards +X) | |
| assert src_node.loc.side == tracks.Direction.RIGHT, src_node | |
| # Connected to CHANX | |
| if dst_node.type == rr.NodeType.CHANX: | |
| loc = node_joint_location(src_node, dst_node) | |
| # Padding node | |
| pad_node = add_node(graph, loc, "Y", segment_id) | |
| # Connect through the padding node | |
| add_edge( | |
| graph, src_node.id, pad_node.id, | |
| graph.get_delayless_switch_id() | |
| ) | |
| add_edge( | |
| graph, pad_node.id, dst_node.id, switch_id, meta_name, | |
| meta_value | |
| ) | |
| # Connected to CHANY | |
| elif dst_node.type == rr.NodeType.CHANY: | |
| # Directly | |
| add_edge( | |
| graph, src_node.id, dst_node.id, switch_id, meta_name, | |
| meta_value | |
| ) | |
| # Should not happen | |
| else: | |
| assert False, dst_node | |
| # CHANX/CHANY to IPIN | |
| elif dst_node.type == rr.NodeType.IPIN and src_node.type in \ | |
| [rr.NodeType.CHANX, rr.NodeType.CHANY]: | |
| # All IPINs go top (toward +Y) | |
| assert dst_node.loc.side == tracks.Direction.TOP, dst_node | |
| # Connected to CHANY | |
| if src_node.type == rr.NodeType.CHANY: | |
| loc = node_joint_location(src_node, dst_node) | |
| # Padding node | |
| pad_node = add_node(graph, loc, "X", segment_id) | |
| # Connect through the padding node | |
| add_edge( | |
| graph, src_node.id, pad_node.id, | |
| graph.get_delayless_switch_id() | |
| ) | |
| add_edge( | |
| graph, pad_node.id, dst_node.id, switch_id, meta_name, | |
| meta_value | |
| ) | |
| # Connected to CHANX | |
| elif src_node.type == rr.NodeType.CHANX: | |
| # Directly | |
| add_edge( | |
| graph, src_node.id, dst_node.id, switch_id, meta_name, | |
| meta_value | |
| ) | |
| # Should not happen | |
| else: | |
| assert False, dst_node | |
| # An unhandled case | |
| else: | |
| assert False, (src_node, dst_node) |