blob: 788ad17b38bd14b112fc01a3cfa0bb9bdb2756dc [file] [log] [blame]
#!/usr/bin/env python3
import pytrellis
import tiles
import nets
import database
import heapq
# Simple automatic router
class Autorouter:
def __init__(self, chip):
self.chip = chip
self.chip_size = (self.chip.get_max_row(), self.chip.get_max_col())
self.dh_arc_cache = {}
self.net_to_wire = {}
self.wire_to_net = {}
# Get arcs downhill of a wire
# Returns list [(dest, configurable, loc), ...]
def get_arcs_downhill(self, wire):
if wire in self.dh_arc_cache:
return self.dh_arc_cache[wire]
else:
drivers = []
chip_size = (self.chip.get_max_row(), self.chip.get_max_col())
try:
npos = tiles.pos_from_name(wire, chip_size, 0)
except AssertionError:
return []
wname = wire.split("_", 1)[1]
hspan = 0
vspan = 0
if wname.startswith("H") and wname[1:3].isdigit():
hspan = int(wname[1:3])
if wname.startswith("V") and wname[1:3].isdigit():
vspan = int(wname[1:3])
positions = {(npos[0], npos[1]), (npos[0] + vspan, npos[1]), (npos[0] - vspan, npos[1]),
(npos[0], npos[1] + hspan), (npos[0], npos[1] - hspan)}
for pos in positions:
for tile in self.chip.get_tiles_by_position(pos[0], pos[1]):
tinf = tile.info
tname = tinf.name
if tname.startswith("TAP"):
continue
pos = tiles.pos_from_name(tname, chip_size, 0)
if abs(pos[0] - npos[0]) not in (vspan, 0) or abs(pos[1] - npos[1]) not in (hspan, 0):
continue
if wire.startswith("G_"):
twire = wire
else:
twire = nets.normalise_name(self.chip_size, tname, wire, 0)
tdb = pytrellis.get_tile_bitdata(
pytrellis.TileLocator(self.chip.info.family, self.chip.info.name, tinf.type))
downhill = tdb.get_downhill_wires(twire)
for sink in downhill:
nn = nets.canonicalise_name(self.chip_size, tname, sink.first, 0)
if nn is not None:
drivers.append((nn, sink.second, tname))
self.dh_arc_cache[wire] = drivers
return drivers
# Enable an Arc
def bind_arc(self, net, uphill_wire, arc, config):
dest_wire, configurable, tile = arc
assert (dest_wire not in self.wire_to_net) or (self.wire_to_net[dest_wire] == net)
self.wire_to_net[dest_wire] = net
exists = False
if net in self.net_to_wire:
exists = dest_wire in self.net_to_wire[net]
self.net_to_wire[net].add(dest_wire)
else:
self.net_to_wire[net] = {dest_wire}
if configurable and not exists:
src_wirename = nets.normalise_name(self.chip_size, tile, uphill_wire, 0)
sink_wirename = nets.normalise_name(self.chip_size, tile, dest_wire, 0)
config[tile].add_arc(sink_wirename, src_wirename)
# Bind a net to a wire (used for port connections)
def bind_net_to_port(self, net, port_wire):
assert (port_wire not in self.wire_to_net) or (self.wire_to_net[port_wire] == net)
self.wire_to_net[port_wire] = net
if net in self.net_to_wire:
self.net_to_wire[net].add(port_wire)
else:
self.net_to_wire[net] = {port_wire}
# Route a net to a wire
def route_net_to_wire(self, net, wire, config):
print(" Routing net '{}' to wire/pin '{}'...".format(net, wire))
chip_size = (self.chip.get_max_row(), self.chip.get_max_col())
dest_pos = tiles.pos_from_name(wire, chip_size, 0)
def get_score(x_wire):
pos = tiles.pos_from_name(x_wire, chip_size, 0)
score = abs(pos[0] - dest_pos[0]) + abs(pos[1] - dest_pos[1])
x_wname = x_wire.split("_", 1)[1]
if x_wname[1:3].isdigit() and score > 3:
score -= int(x_wname[1:3])
return score
assert net in self.net_to_wire
bfs_queue = [(get_score(x), x) for x in sorted(self.net_to_wire[net])]
heapq.heapify(bfs_queue)
seen_wires = set()
routed = False
backtrace = {} # map wire -> (source, arc)
while not routed and len(bfs_queue) > 0:
score, curr_wire = heapq.heappop(bfs_queue)
#print(curr_wire)
arcs = self.get_arcs_downhill(curr_wire)
for arc in arcs:
dest, cfg, loc = arc
if dest == wire:
backtrace[dest] = (curr_wire, arc)
routed = True
break
elif dest not in seen_wires:
if dest in self.wire_to_net and self.wire_to_net[dest] != net:
continue
backtrace[dest] = (curr_wire, arc)
heapq.heappush(bfs_queue, (get_score(dest), dest))
seen_wires.add(dest)
if routed:
cursor = wire
while cursor in backtrace:
cursor, arc = backtrace[cursor]
self.bind_arc(net, cursor, arc, config)
else:
assert False, "failed to route net {} to wire {}".format(net, wire)
def main():
pytrellis.load_database(database.get_db_root())
chip = pytrellis.Chip("LFE5U-45F")
rt = Autorouter(chip)
config = {_.info.name: pytrellis.TileConfig() for _ in chip.get_all_tiles()}
rt.bind_net_to_port("x", "R15C10_Q0")
rt.route_net_to_wire("x", "R15C50_A0", config)
for tile, tcfg in sorted(config.items()):
cfgtext = tcfg.to_string()
if len(cfgtext.strip()) > 0:
print(".tile {}".format(tile))
print(cfgtext)
if __name__ == "__main__":
main()