blob: 1e34593b154e55edf396051bcdfa977c0b9516e0 [file] [log] [blame]
#!/usr/bin/env python3
from lib.rr_graph import graph2
from lib.rr_graph import tracks
from lib.rr_graph import points
import lib.rr_graph_xml.graph2 as xml_graph2
# bi-directional channels
def create_tracks(graph, grid_width, grid_height, rcw, verbose=False):
print("Creating tracks, channel width: %d" % rcw)
# Not strictly required, but VPR wants this I think?
assert rcw % 2 == 0
def alt_pos(begin, end, swap):
if swap:
return end, begin, graph2.NodeDirection.DEC_DIR
else:
return begin, end, graph2.NodeDirection.INC_DIR
# chanx going entire width
for y in range(0, grid_height - 1):
for tracki in range(rcw):
begin, end, direction = alt_pos(
(1, y), (grid_width - 2, y), tracki % 2 == 1
)
graph.add_track(
track=tracks.Track(
direction='X',
x_low=begin[0],
x_high=end[0],
y_low=begin[1],
y_high=end[1],
),
segment_id=graph.segments[0].id,
capacity=1,
direction=direction,
name="CHANX{:04d}@{:04d}".format(y, tracki),
)
# chany going entire height
for x in range(0, grid_width - 1):
for tracki in range(rcw):
begin, end, direction = alt_pos(
(x, 1), (x, grid_height - 2), tracki % 2 == 1
)
graph.add_track(
track=tracks.Track(
direction='Y',
x_low=begin[0],
x_high=end[0],
y_low=begin[1],
y_high=end[1],
),
segment_id=graph.segments[0].id,
capacity=1,
direction=direction,
name="CHANY{:04d}@{:04d}".format(x, tracki),
)
def create_tracks_from_points(
name, graph, unique_pos, short, grid_width, grid_height
):
xs, ys = points.decompose_points_into_tracks(
unique_pos,
grid_width,
grid_height,
right_only=True,
)
tracks_list, track_connections = tracks.make_tracks(
xs, ys, unique_pos, grid_width, grid_height
)
tracks_model = tracks.Tracks(tracks_list, track_connections)
nodes = []
for idx, track in enumerate(tracks_list):
nodes.append(
graph.add_track(
track=track,
segment_id=graph.segments[0].id,
capacity=1,
name="{}{}".format(name, idx),
)
)
for aidx, bidx in track_connections:
graph.add_edge(
src_node=nodes[aidx],
sink_node=nodes[bidx],
switch_id=short,
)
graph.add_edge(
src_node=nodes[bidx],
sink_node=nodes[aidx],
switch_id=short,
)
return nodes, tracks_model
def create_global_constant_tracks(graph, mux, short, grid_width, grid_height):
""" Create channels for global fanout """
unique_pos = set()
for x in range(grid_width):
for y in range(grid_height):
if x == 0:
continue
if y == 0:
continue
if x == grid_width - 1:
continue
if y == grid_height - 1:
continue
if x == grid_width - 2 and y == grid_height - 2:
continue
unique_pos.add((x, y))
vcc_nodes, vcc_tracks = create_tracks_from_points(
"VCC", graph, unique_pos, short, grid_width, grid_height
)
gnd_nodes, gnd_tracks = create_tracks_from_points(
"GND", graph, unique_pos, short, grid_width, grid_height
)
vcc_pin = graph.create_pin_name_from_tile_type_and_pin('VCC', 'VCC')
gnd_pin = graph.create_pin_name_from_tile_type_and_pin('GND', 'GND')
found_vcc = False
found_gnd = False
for loc in graph.grid:
block_type = graph.block_types[loc.block_type_id]
for pin_class in block_type.pin_class:
for pin in pin_class.pin:
if pin.name == vcc_pin:
nodes = vcc_nodes
tracks = vcc_tracks
found_vcc = True
elif pin.name == gnd_pin:
nodes = gnd_nodes
tracks = gnd_tracks
found_gnd = True
else:
continue
pin_map = {}
for pin_node, pin_side in graph.loc_pin_map[(loc.x, loc.y,
pin.ptc)]:
pin_map[pin_side] = pin_node
made_connection = False
for pin_dir, idx in tracks.get_tracks_for_wire_at_coord(
(loc.x, loc.y)).items():
if pin_dir in pin_map:
made_connection = True
graph.add_edge(
src_node=pin_map[pin_side],
sink_node=nodes[idx],
switch_id=mux,
)
break
assert made_connection, (pin, pin_map)
assert found_vcc
assert found_gnd
def channel_common(node):
""" Return the value of the channel that is common.
Args:
node (graph2.Node): Node to get common dimension.
"""
if node.type == graph2.NodeType.CHANX:
assert node.loc.y_low == node.loc.y_high
return node.loc.y_low
elif node.type == graph2.NodeType.CHANY:
assert node.loc.x_low == node.loc.x_high
return node.loc.x_low
else:
assert False, node.type
def channel_start(node):
""" Return the start value of the channel
Args:
node (graph2.Node): Node to get start dimension.
"""
if node.type == graph2.NodeType.CHANX:
return node.loc.x_low
elif node.type == graph2.NodeType.CHANY:
return node.loc.y_low
else:
assert False, node.type
def walk_pins(graph):
""" Yields all pins from grid.
Yield is tuple (graph2.GridLoc, graph2.PinClass, graph2.Pin, pin node idx, tracks.Direction).
"""
for loc in graph.grid:
block_type = graph.block_types[loc.block_type_id]
for pin_class_idx, pin_class in enumerate(block_type.pin_class):
for pin in pin_class.pin:
for pin_node, pin_side in graph.loc_pin_map[(loc.x, loc.y,
pin.ptc)]:
yield loc, pin_class, pin, pin_node, pin_side
def connect_blocks_to_tracks(
graph, grid_width, grid_height, rcw, switch, verbose=False
):
ytracks = {}
for inode in graph.tracks:
if graph.nodes[inode].type == graph2.NodeType.CHANX:
continue
elif graph.nodes[inode].type == graph2.NodeType.CHANY:
x = channel_common(graph.nodes[inode])
if x not in ytracks:
ytracks[x] = []
ytracks[x].append(inode)
else:
assert False, graph.nodes[inode]
special_pins = set(
(
graph.create_pin_name_from_tile_type_and_pin('TILE', 'COUT'),
graph.create_pin_name_from_tile_type_and_pin('TILE', 'CIN'),
graph.create_pin_name_from_tile_type_and_pin('VCC', 'VCC'),
graph.create_pin_name_from_tile_type_and_pin('GND', 'GND'),
)
)
print("Indexing nodes")
print("Skipping connecting block pins to CHANX")
print("Connecting left-right block pins to CHANY")
# Walk every pin and connect them to every track on the left or right,
# depending on pin direction
#
# Pins in the TOP/BOTTOM direction are asserted to be the carry pins.
for loc, pin_class, pin, pin_node, pin_side in walk_pins(graph):
if pin.name in special_pins:
continue
if pin_side == tracks.Direction.LEFT:
if loc.x == 0:
continue
tracks_for_pin = ytracks[loc.x - 1]
elif pin_side == tracks.Direction.RIGHT:
if loc.x == grid_width - 1:
continue
tracks_for_pin = ytracks[loc.x]
else:
assert False, pin_side
if pin_class.type == graph2.PinType.OUTPUT:
for track_inode in tracks_for_pin:
graph.add_edge(
src_node=pin_node,
sink_node=track_inode,
switch_id=switch,
)
elif pin_class.type == graph2.PinType.INPUT:
for track_inode in tracks_for_pin:
graph.add_edge(
src_node=track_inode,
sink_node=pin_node,
switch_id=switch,
)
def connect_tracks_to_tracks(graph, switch, verbose=False):
print("Connecting tracks to tracks")
def try_connect(ainode, binode):
""" Connect channel at ainode and binode if possible.
Args:
ainode (int): Node index of destination channel
binode (int): Node index of source channel.
"""
atrack = graph.nodes[ainode]
btrack = graph.nodes[binode]
# Check if source channel can connect to destinatio channel.
# Note this code assumes unidirectional channels are in use.
if (btrack.direction == graph2.NodeDirection.INC_DIR
and channel_start(btrack) <= channel_common(atrack)) \
or (btrack.direction == graph2.NodeDirection.DEC_DIR
and channel_start(btrack) >= channel_common(atrack)):
graph.add_edge(binode, ainode, switch)
xtracks = []
ytracks = []
for inode in graph.tracks:
if graph.nodes[inode].type == graph2.NodeType.CHANX:
xtracks.append(inode)
elif graph.nodes[inode].type == graph2.NodeType.CHANY:
ytracks.append(inode)
else:
assert False, graph.nodes[inode]
for xinode in xtracks:
for yinode in ytracks:
try_connect(xinode, yinode)
try_connect(yinode, xinode)
def rebuild_graph(fn, fn_out, rcw=6, verbose=False):
"""
Add rcw tracks spanning full channel to both X and Y channels
Connect all of those to all the adjacent pins
Fully connect tracks at intersections
For intersections this means we actually have two edges per intersection
since source and sink must be specified
"""
print('Importing input g')
xml_graph = xml_graph2.Graph(
fn,
output_file_name=fn_out,
)
graph = xml_graph.graph
grid_width = max(p.x for p in graph.grid) + 1
grid_height = max(p.y for p in graph.grid) + 1
mux = graph.get_switch_id('mux')
try:
short = graph.get_switch_id('short')
except KeyError:
short = xml_graph.add_switch(
graph2.Switch(
id=None,
name='short',
type=graph2.SwitchType.SHORT,
timing=None,
sizing=graph2.SwitchSizing(
mux_trans_size=0,
buf_size=0,
),
)
)
create_tracks(graph, grid_width, grid_height, rcw, verbose=verbose)
create_global_constant_tracks(graph, mux, short, grid_width, grid_height)
connect_blocks_to_tracks(graph, grid_width, grid_height, rcw, switch=mux)
connect_tracks_to_tracks(graph, switch=mux, verbose=verbose)
print("Completed rebuild")
xml_graph.root_attrib["tool_version"] = "dev"
xml_graph.root_attrib["tool_comment"] = "Generated from black magic"
channels_obj = graph.create_channels(pad_segment=graph.segments[0].id)
xml_graph.serialize_to_xml(
channels_obj=channels_obj,
nodes_obj=graph.nodes,
edges_obj=graph.edges
)
def main():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", action='store_true')
parser.add_argument("--route_chan_width", type=int, default=20)
parser.add_argument("--read_rr_graph")
parser.add_argument("--write_rr_graph")
args = parser.parse_args()
rebuild_graph(
args.read_rr_graph,
args.write_rr_graph,
rcw=args.route_chan_width,
verbose=args.verbose
)
if __name__ == "__main__":
main()