Пример #1
0
 def get_fanin(net):
     drivers = []
     npos = tiles.pos_from_name(net, chip_size, bias)
     for tile in c.get_all_tiles():
         tinf = tile.info
         tname = tinf.name
         pos = tiles.pos_from_name(tname, chip_size, bias)
         if abs(pos[0] - npos[0]) >= 10 or abs(pos[1] - npos[1]) >= 10:
             continue
         if net.startswith("G_"):
             tnet = net
         else:
             tnet = nets.normalise_name(chip_size, tname, net, bias)
         tdb = pytrellis.get_tile_bitdata(
             pytrellis.TileLocator(c.info.family, c.info.name, tinf.type))
         try:
             mux = tdb.get_mux_data_for_sink(tnet)
             for src in mux.get_sources():
                 drivers.append(
                     (nets.canonicalise_name(chip_size, tname, src,
                                             bias), True, tname))
         except IndexError:
             pass
         for fc in tdb.get_fixed_conns():
             if fc.sink == tnet:
                 drivers.append(
                     (nets.canonicalise_name(chip_size, tname, fc.source,
                                             bias), False, tname))
     return drivers
Пример #2
0
 def get_fanout(net):
     drivers = []
     npos = tiles.pos_from_name(net)
     for tile in c.get_all_tiles():
         tinf = tile.info
         tname = tinf.name
         pos = tiles.pos_from_name(tname)
         if abs(pos[0] - npos[0]) >= 12 or abs(pos[1] - npos[1]) >= 12:
             continue
         if net.startswith("G_"):
             tnet = net
         else:
             tnet = nets.normalise_name(chip_size, tname, net)
         tdb = pytrellis.get_tile_bitdata(
             pytrellis.TileLocator(c.info.family, c.info.name, tinf.type))
         for sink in tdb.get_sinks():
             mux = tdb.get_mux_data_for_sink(sink)
             if tnet in mux.arcs:
                 drivers.append(
                     (nets.canonicalise_name(chip_size, tname,
                                             sink), True, tname))
         for fc in tdb.get_fixed_conns():
             if fc.source == tnet:
                 drivers.append(
                     (nets.canonicalise_name(chip_size, tname,
                                             fc.sink), False, tname))
     return drivers
Пример #3
0
    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, self.bias)
            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, self.bias)

                    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, self.bias)

                    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, self.bias)
                        if nn is not None:
                            drivers.append((nn, sink.second, tname))
            self.dh_arc_cache[wire] = drivers
            return drivers
Пример #4
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
Пример #5
0
def canonicalise_name(chip_size, tile, wire, bias):
    """
    Convert a normalised name in a given tile back to a canonical global name
    :param chip_size: chip size as tuple (max_row, max_col)
    :param tile: tilename
    :param wire: normalised netname
    :return: global canonical netname
    """
    if wire.startswith("G_"):
        return wire
    m = rel_netname_re.match(wire)
    tile_pos = tiles.pos_from_name(tile, chip_size, bias)
    wire_pos = tile_pos
    if m:
        assert len(m.groups()) >= 1
        for g in m.groups():
            if g is not None:
                delta = int(g[1:])
                if g[0] == "N":
                    wire_pos = (wire_pos[0] - delta, wire_pos[1])
                elif g[0] == "S":
                    wire_pos = (wire_pos[0] + delta, wire_pos[1])
                elif g[0] == "W":
                    wire_pos = (wire_pos[0], wire_pos[1] - delta)
                elif g[0] == "E":
                    wire_pos = (wire_pos[0], wire_pos[1] + delta)
        wire = wire.split("_", 1)[1]
    if wire_pos[0] < 0 or wire_pos[0] > chip_size[0] or wire_pos[
            1] < 0 or wire_pos[1] > chip_size[1]:
        return None  #TODO: edge normalisation
    return "R{}C{}_{}".format(wire_pos[0], wire_pos[1], wire)
Пример #6
0
def format_rel(a, b):
    ra, ca = tiles.pos_from_name(a, (126, 95), 0)
    rb, cb = tiles.pos_from_name(b, (126, 95), 0)
    rel = ""
    if rb < ra:
        rel += "n{}".format(ra - rb)
    elif rb > ra:
        rel += "s{}".format(rb - ra)

    if cb < ca:
        rel += "w{}".format(ca - cb)
    elif cb > ca:
        rel += "e{}".format(cb - ca)

    if rel != "":
        rel = "_" + rel
    return rel
Пример #7
0
 def completer(str, idx):
     if not tile_net_re.match(str):
         return None
     loc = tiles.pos_from_name(str)
     nets = get_nets_at(loc)
     for n in nets:
         if n.startswith(str):
             if idx > 0:
                 idx -= 1
             else:
                 return n
     return None
Пример #8
0
 def init_bels(self):
     self.bels_by_type = {"SLICE": []}
     all_tiles = self.chip.get_all_tiles()
     for tile in all_tiles:
         tinf = tile.info
         tname = tinf.name
         pos = tiles.pos_from_name(tname)
         if tinf.type == "PLC2":
             for loc in ("A", "B", "C", "D"):
                 bel = "R{}C{}{}".format(pos[0], pos[1], loc)
                 self.bels[bel] = ("SLICE", (tname, loc))
                 self.bels_by_type["SLICE"].append(bel)
Пример #9
0
def main(argv):
    args = parser.parse_args(argv[1:])
    tilegrid = database.get_tilegrid(args.family, args.device)
    device_info = database.get_devices()["families"][args.family]["devices"][
        args.device]

    max_row = device_info["max_row"]
    max_col = device_info["max_col"]
    bias = device_info["col_bias"]

    tiles = []
    for i in range(max_row + 1):
        row = []
        for j in range(max_col + 1):
            row.append([])
        tiles.append(row)

    for identifier, data in sorted(tilegrid.items()):
        name = identifier.split(":")[0]
        row, col = tilelib.pos_from_name(name, (max_row, max_col), bias)
        colour = get_colour(data["type"])
        tiles[row][col].append((name, data["type"], colour))

    f = args.outfile
    print("""<html>
            <head><title>{} Tiles</title></head>
            <body>
            <h1>{} Tilegrid</h1>
            <table style='font-size: 8pt; border: 2px solid black; text-align: center'>
        """.format(args.device, args.device),
          file=f)
    for trow in tiles:
        print("<tr>", file=f)
        row_max_height = 0
        for tloc in trow:
            row_max_height = max(row_max_height, len(tloc))
        row_height = max(75, 30 * row_max_height)
        for tloc in trow:
            print("<td style='border: 2px solid black; height: {}px'>".format(
                row_height),
                  file=f)
            for tile in tloc:
                print(
                    "<div style='height: {}%; background-color: {}'><em>{}</em><br/><strong><a href='../tilehtml/{}.html' style='color: black'>{}</a></strong></div>"
                    .format(100 / len(tloc), tile[2], tile[0], tile[1],
                            tile[1]),
                    file=f)
            print("</td>", file=f)
        print("</tr>", file=f)
    print("</table></body></html>", file=f)
Пример #10
0
    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, self.bias)

        def get_score(x_wire):
            pos = tiles.pos_from_name(x_wire, chip_size, self.bias)
            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)
Пример #11
0
    def inst_slice(self, name, a0=None, a1=None, b0=None, b1=None, c0=None, c1=None, d0=None, d1=None, m0=None, m1=None,
                   clk=None, ce=None, lsr=None, f0=None, f1=None, q0=None, q1=None, params=dict()):
        print("Instantiating slice {}".format(name))
        bel = self.bel_for_cell(name, "SLICE")
        beltype, belloc = self.bels[bel]
        tile, loc = belloc
        chip_size = (self.chip.get_max_row(), self.chip.get_max_col())
        pos = tiles.pos_from_name(tile, chip_size, self.bias)
        net_prefix = "R{}C{}".format(pos[0], pos[1])
        slice_index = "ABCD".index(loc)
        lc0 = 2 * slice_index
        lc1 = 2 * slice_index + 1

        self.connect_output("{}_F{}".format(net_prefix, lc0), f0)
        self.connect_output("{}_F{}".format(net_prefix, lc1), f1)
        self.connect_output("{}_Q{}".format(net_prefix, lc0), q0)
        self.connect_output("{}_Q{}".format(net_prefix, lc1), q1)

        self.connect_input("{}_A{}".format(net_prefix, lc0), a0)
        self.connect_input("{}_A{}".format(net_prefix, lc1), a1)
        self.connect_input("{}_B{}".format(net_prefix, lc0), b0)
        self.connect_input("{}_B{}".format(net_prefix, lc1), b1)
        self.connect_input("{}_C{}".format(net_prefix, lc0), c0)
        self.connect_input("{}_C{}".format(net_prefix, lc1), c1)
        self.connect_input("{}_D{}".format(net_prefix, lc0), d0)
        self.connect_input("{}_D{}".format(net_prefix, lc1), d1)

        self.connect_input("{}_M{}".format(net_prefix, lc0), m0)
        self.connect_input("{}_M{}".format(net_prefix, lc1), m1)

        self.connect_input("{}_MUXCLK{}".format(net_prefix, slice_index), clk)
        self.connect_input("{}_CE{}".format(net_prefix, slice_index), ce)
        self.connect_input("{}_MUXLSR{}".format(net_prefix, slice_index), lsr)

        for key, value in sorted(params.items()):
            if key.endswith(".INIT"):
                bv = pytrellis.BoolVector()
                for x in value:
                    bv.append(x)
                self.config[tile].add_word("SLICE{}.{}".format(loc, key), bv)
            else:
                self.config[tile].add_enum("SLICE{}.{}".format(loc, key), value)
        print()
Пример #12
0
def normalise_name(chip_size, tile, wire, bias):
    """
    Wire name normalisation for tile wires and fuzzing
    All net names that we have access too are canonical, global names
    These are thus no good for building up a database that is the same for all tiles
    of a given type, as the names will be different in each location.

    Lattice names are of the form R{r}C{c}_{NETNAME}

    Hence, we normalise names in the following way:
     - Global wires have the prefix "G_" added
     - Wires where (r, c) correspond to the current tile have their prefix removed
     - Wires to the left (in TAP_DRIVEs) are given the prefix L, and wires to the right
       are given the prefix R
     - Wires within a DQS group are given the prefix DQSG_
     - Wires within a bank are given the prefix BNK_
     - Other wires are given a relative position prefix using the syntax
       ([NS]\d+)?([EW]\d+)?_
       so a wire whose nominal location is 6 tiles up would be given a prefix N6_
       a wire whose nominal location is 2 tiles down and 1 tile right would be given a prefix
       S2E1_

    TODO: this is more complicated at the edges of the device, where irregular names are used to keep the row and column
    of the nominal position in bounds. Extra logic will be needed to catch and regularise these cases.

    chip_size: chip size as tuple (max_row, max_col)
    tile: name of the relevant tile
    wire: full Lattice name of the wire
    bias: Use 1-based column indexing

    Returns the normalised netname
    """
    upos = wire.index("_")
    prefix = wire[:upos]
    prefix_pos = tiles.pos_from_name(prefix, chip_size, bias)
    tile_pos = tiles.pos_from_name(tile, chip_size, bias)
    netname = wire[upos + 1:]
    if tile.startswith("TAP") and netname.startswith("H"):
        if prefix_pos[1] < tile_pos[1]:
            return "L_" + netname
        elif prefix_pos[1] > tile_pos[1]:
            return "R_" + netname
        else:
            assert False, "bad TAP_DRIVE netname"
    elif is_global(wire):
        return "G_" + netname
    elif dqs_ssig_re.match(wire):
        return "DQSG_" + netname
    elif bnk_eclk_re.match(wire):
        if "ECLK" in tile:
            return "G_" + netname
        else:
            return "BNK_" + bnk_eclk_re.match(wire).group(1)
    elif netname in ("INRD", "LVDS"):
        return "BNK_" + netname
    netname, prefix_pos = handle_edge_name(chip_size, tile_pos, prefix_pos,
                                           netname)
    if tile_pos == prefix_pos:
        return netname
    else:
        prefix = ""
        if prefix_pos[0] < tile_pos[0]:
            prefix += "N{}".format(tile_pos[0] - prefix_pos[0])
        elif prefix_pos[0] > tile_pos[0]:
            prefix += "S{}".format(prefix_pos[0] - tile_pos[0])
        if prefix_pos[1] > tile_pos[1]:
            prefix += "E{}".format(prefix_pos[1] - tile_pos[1])
        elif prefix_pos[1] < tile_pos[1]:
            prefix += "W{}".format(tile_pos[1] - prefix_pos[1])
        return prefix + "_" + netname
Пример #13
0
locations have slightly different routing - swapped output tristate and data

This script fixes this by patching tile names
"""

for f, d in [("LIFCL", "LIFCL-40"), ("LIFCL", "LFD2NX-40"),
             ("LFCPNX", "LFCPNX-100")]:
    tgp = path.join(database.get_db_root(), f, d, "tilegrid.json")
    with open(tgp, "r") as infile:
        tg = json.load(infile)["tiles"]

    tiles_by_xy = [[]]
    max_row = 0
    max_col = 0
    for tile in sorted(tg.keys()):
        r, c = tiles.pos_from_name(tile)
        max_row = max(r, max_row)
        max_col = max(c, max_col)
        while r >= len(tiles_by_xy):
            tiles_by_xy.append([])
        while c >= len(tiles_by_xy[r]):
            tiles_by_xy[r].append([])
        tiles_by_xy[r][c].append(tile)

    # Top tiles
    is_odd = False
    for col in tiles_by_xy[0]:
        for tile in col:
            tt = tiles.type_from_fullname(tile)
            if not tt.startswith("SYSIO"):
                continue
Пример #14
0
def normalise_name(chip_size, tile, wire, family):
    """
    Wire name normalisation for tile wires and fuzzing
    All net names that we have access too are canonical, global names
    These are thus no good for building up a database that is the same for all tiles
    of a given type, as the names will be different in each location.

    Lattice names are of the form R{r}C{c}_{NETNAME}

    Hence, we normalise names in the following way:
     - Global wires have the prefix "G_" added
     - Wires where (r, c) correspond to the current tile have their prefix removed
     - Wires to the left (in TAP_DRIVEs) are given the prefix L, and wires to the right
       are given the prefix R
     - Wires within a DQS group are given the prefix DQSG_
     - Wires within a bank are given the prefix BNK_
     - Other wires are given a relative position prefix using the syntax
       ([NS]\d+)?([EW]\d+)?_
       so a wire whose nominal location is 6 tiles up would be given a prefix N6_
       a wire whose nominal location is 2 tiles down and 1 tile right would be given a prefix
       S2E1_

    TODO: this is more complicated at the edges of the device, where irregular names are used to keep the row and column
    of the nominal position in bounds. Extra logic will be needed to catch and regularise these cases.

    chip_size: chip size as tuple (max_row, max_col)
    tile: name of the relevant tile
    wire: full Lattice name of the wire
    family: Device family to normalise. Affects column indexing (e.g. MachXO2 uses 1-based
          column indexing) and naming of global wires, TAP_DRIVEs, DQS, bank wires,
          etc.

    Returns the normalised netname
    """

    if family == "ECP5":
        def handle_family_net(tile, wire, prefix_pos, tile_pos, netname):
            return ecp5.handle_family_net(tile, wire, prefix_pos, tile_pos, netname)
        bias = 0
    elif family == "MachXO2":
        def handle_family_net(tile, wire, prefix_pos, tile_pos, netname):
            return machxo2.handle_family_net(tile, wire, prefix_pos, tile_pos, netname)
        bias = 1
    else:
        raise ValueError("Unknown device family.")

    upos = wire.index("_")
    prefix = wire[:upos]
    prefix_pos = tiles.pos_from_name(prefix, chip_size, bias)
    tile_pos = tiles.pos_from_name(tile, chip_size, bias)
    netname = wire[upos+1:]

    family_net = handle_family_net(tile, wire, prefix_pos, tile_pos, netname)
    if family_net:
        return family_net

    netname, prefix_pos = handle_edge_name(chip_size, tile_pos, prefix_pos, netname)
    if tile_pos == prefix_pos:
        return netname
    else:
        prefix = ""
        if prefix_pos[0] < tile_pos[0]:
            prefix += "N{}".format(tile_pos[0] - prefix_pos[0])
        elif prefix_pos[0] > tile_pos[0]:
            prefix += "S{}".format(prefix_pos[0] - tile_pos[0])
        if prefix_pos[1] > tile_pos[1]:
            prefix += "E{}".format(prefix_pos[1] - tile_pos[1])
        elif prefix_pos[1] < tile_pos[1]:
            prefix += "W{}".format(tile_pos[1] - prefix_pos[1])
        return prefix + "_" + netname
Пример #15
0
def main():
    for name, max_row, max_col in configs:
        cfg = FuzzConfig(job="GLOBAL_{}".format(name), device=name, sv="../shared/empty_40.v", tiles=[])
        cfg.setup()
        db_path = path.join(database.get_db_root(), "LIFCL", name, "globals.json")
        def load_db():
            if path.exists(db_path):
                with open(db_path, "r") as dbf:
                   return json.load(dbf)
            else:
                return {"branches": []}
        def save_db():
            with open(db_path, "w") as dbf:
                print(json.dumps(gdb, sort_keys=True, indent=4), file=dbf)
        gdb = load_db()
        # Determine branch driver locations
        test_row = 4
        clock_wires = ["R{}C{}_JCLK0".format(test_row, c) for c in range(1, max_col)]
        clock_info = lapie.get_node_data(cfg.udb, clock_wires)
        branch_to_col = {}
        for n in clock_info:
            r, c = pos_from_name(n.name)
            hpbx_c = None
            for uh in n.uphill_pips:
                if "_HPBX0" in uh.from_wire:
                    hpbx_r, hpbx_c = pos_from_name(uh.from_wire)
                    assert hpbx_r == r
                    break
            assert hpbx_c is not None
            if hpbx_c not in branch_to_col:
                branch_to_col[hpbx_c] = []
            branch_to_col[hpbx_c].append(c)
        branches = []

        branch_wires = ["R{}C{}_HPBX0000".format(test_row, bc) for bc in sorted(branch_to_col.keys())]
        branch_wire_info = lapie.get_node_data(cfg.udb, branch_wires)
        branch_driver_col = {}
        # Branches directly driven by a VPSX
        # Also, get a test column for the spine exploration later
        sp_test_col = None
        for bw in branch_wire_info:
            r, c = pos_from_name(bw.name)
            for uh in bw.uphill_pips:
                if "VPSX" in uh.from_wire:
                    vpsx_r, vpsx_c = pos_from_name(uh.from_wire)
                    branch_driver_col[c] = vpsx_c
                    if sp_test_col is None:
                        sp_test_col = c
        # Branches driven by another branch
        for bw in branch_wire_info:
            r, c = pos_from_name(bw.name)
            if c in branch_driver_col:
                continue
            for uh in bw.uphill_pips:
                if "HPBX0" in uh.from_wire:
                    hpbx_r, hpbx_c = pos_from_name(uh.from_wire)
                    branch_driver_col[c] = branch_driver_col[hpbx_c]            
        for bc, scs in sorted(branch_to_col.items()):
            tap_drv_col = branch_driver_col[bc] + 1
            side = "R" if tap_drv_col < bc else "L" 
            branches.append(dict(branch_col=bc, tap_driver_col=tap_drv_col, tap_side=side, from_col=min(scs), to_col=max(scs)))
        gdb["branches"] = branches
        save_db()
        # Spines
        sp_branch_wires = ["R{}C{}_HPBX0000".format(r, sp_test_col) for r in range(1, max_row)]
        spine_to_branch_row = {}
        sp_info = lapie.get_node_data(cfg.udb, sp_branch_wires)
        for n in sp_info:
            r, c = pos_from_name(n.name)
            vpsx_r = None
            for uh in n.uphill_pips:
                if "VPSX" in uh.from_wire:
                    vpsx_r, vpsx_c = pos_from_name(uh.from_wire)
                    break
            assert vpsx_r is not None
            if vpsx_r not in spine_to_branch_row:
                spine_to_branch_row[vpsx_r] = []
            spine_to_branch_row[vpsx_r].append(r)
        spines = []
        sp_test_row = None
        for sr, brs in sorted(spine_to_branch_row.items()):
            if sp_test_row is None:
                sp_test_row = sr
            spines.append(dict(spine_row=sr, from_row=min(brs), to_row=max(brs)))
        gdb["spines"] = spines
        save_db()
        # HROWs
        hrow_to_spine_col = {}
        spine_wires = ["R{}C{}_VPSX0000".format(sp_test_row, c) for c in sorted(set(branch_driver_col.values()))]
        hr_info = lapie.get_node_data(cfg.udb, spine_wires)
        for n in hr_info:
            r, c = pos_from_name(n.name)
            hrow_c = None
            for uh in n.uphill_pips:
                if "HPRX0" in uh.from_wire:
                    hrow_r, hrow_c = pos_from_name(uh.from_wire)
                    break
            assert hrow_c is not None
            if hrow_c not in hrow_to_spine_col:
                hrow_to_spine_col[hrow_c] = []
            hrow_to_spine_col[hrow_c].append(c)
        hrows = []
        sp_test_row = None
        for hrc, scs in sorted(hrow_to_spine_col.items()):
            hrows.append(dict(hrow_col=hrc, spine_cols=scs))
        gdb["hrows"] = hrows
        save_db()
Пример #16
0
def get_distance(a, b):
    ra, ca = tiles.pos_from_name(a, (126, 95), 0)
    rb, cb = tiles.pos_from_name(b, (126, 95), 0)
    return abs(ra - rb) + abs(ca - cb)