| #!/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() |