def quadrants(): mod = codegen.Module() cst = codegen.Constraints() ibuf(mod, cst, true_pins[2], clk="myclk") pnr = tiled_fuzzer.run_pnr(mod, cst, {}) modules = [] constrs = [] idxes = [] for i in range(2, db.cols): for j in [2, db.rows - 3]: # avoid bram if "DFF0" not in db.grid[j - 1][i - 1].bels: print(i, j) continue mod = codegen.Module() cst = codegen.Constraints() ibuf(mod, cst, true_pins[0], clk="myclk") dff(mod, cst, j, i, clk="myclk") modules.append(mod) constrs.append(cst) idxes.append((j, i)) for i in [2, db.cols - 2]: for j in range(2, db.rows): if "DFF0" not in db.grid[j - 1][i - 1].bels: print(i, j) continue mod = codegen.Module() cst = codegen.Constraints() ibuf(mod, cst, true_pins[0], clk="myclk") dff(mod, cst, j, i, clk="myclk") modules.append(mod) constrs.append(cst) idxes.append((j, i)) pnr_res = pool.map(lambda param: tiled_fuzzer.run_pnr(*param, {}), zip(modules, constrs)) res = {} for (row, col), (mybs, *_) in zip(idxes, pnr_res): sweep_tiles = fuse_h4x.tile_bitmap(fse, mybs ^ pnr.bitmap) # find which tap was used taps = [ r for (r, c, typ), t in sweep_tiles.items() if typ in {13, 14, 15, 16, 18, 19} ] # find which center tile was used t8x = [(r, c) for (r, c, typ), t in sweep_tiles.items() if typ >= 80 and typ < 90] rows, cols, _ = res.setdefault(t8x[0], (set(), set(), taps[0])) rows.add(row - 1) cols.add(col - 1) return res
def center_muxes(ct, rows, cols): "Find which mux drives which spine, and maps their inputs to clock pins" fr = min(rows) dff_locs = [(fr + 1, c + 1) for c in cols][:len(true_pins)] mod = codegen.Module() cst = codegen.Constraints() ibufs = [ibuf(mod, cst, p) for p in true_pins] dffs = [dff(mod, cst, row, col) for row, col in dff_locs] pnr = tiled_fuzzer.run_pnr(mod, cst, {}) modules = [] constrs = [] for i, pin in enumerate(true_pins): mod = codegen.Module() cst = codegen.Constraints() ibufs = [ibuf(mod, cst, p) for p in true_pins] dffs = [dff(mod, cst, row, col) for row, col in dff_locs] mod.assigns = list(zip(dffs, ibufs))[:i + 1] modules.append(mod) constrs.append(cst) pnr_res = pool.map(lambda param: tiled_fuzzer.run_pnr(*param, {}), zip(modules, constrs)) gb_sources = {} gb_destinations = {} src_seen = set() dst_seen = set() base = pnr.bitmap for i, (bs_sweep, *_) in enumerate(pnr_res): pin = true_pins[i] new = base ^ bs_sweep tiles = chipdb.tile_bitmap(db, new) try: _, _, clk_pips = gowin_unpack.parse_tile_(db, ct[0], ct[1], tiles[ct], noalias=True) dest, = set(clk_pips.keys()) - dst_seen dst_seen.add(dest) src, = set(clk_pips.values()) - src_seen src_seen.add(src) except ValueError: # it seems this uses a dynamically configurable mux routed to VCC/VSS continue print(i, pin, src, dest) gb_destinations[(ct[1], i)] = dest gb_sources[src] = pin return gb_sources, gb_destinations
def iob(locations): for iostd in iostandards: for ttyp, tiles in locations.items(): # for each tile of this type locs = tiles.copy() mod = codegen.Module() cst = codegen.Constraints() # get bels in this ttyp bels = {name[-1] for loc in tiles.values() for name in loc} for pin in bels: # [A, B, C, D, ...] for attr, attr_values in iobattrs.items( ): # each IOB attribute # XXX remove if iostd == "PCI33" and attr == "SINGLE_RESISTOR": continue attr_vals = attr_values.values if attr_vals == None: attr_vals = attr_values.table[iostd] for attr_val in attr_vals: # each value of the attribute for typ, conn in iobmap.items(): # skip illegal atributesa for mode if typ not in attr_values.allowed_modes: continue # find the next location that has pin # or make a new module loc = find_next_loc(pin, locs) if (loc == None): yield Fuzzer(ttyp, mod, cst, {}, iostd) locs = tiles.copy() mod = codegen.Module() cst = codegen.Constraints() loc = find_next_loc(pin, locs) # special pins if is_illegal(iostd, loc, attr): continue name = make_name("IOB", typ) iob = codegen.Primitive(typ, name) for port in chain.from_iterable(conn.values()): iob.portmap[port] = name + "_" + port for direction, wires in conn.items(): wnames = [name + "_" + w for w in wires] getattr(mod, direction).update(wnames) mod.primitives[name] = iob cst.ports[name] = loc # complex iob. connect OEN and O if typ == "IOBUF": iob.portmap["OEN"] = name + "_O" if attr_val: # port attribute value cst.attrs[name] = {attr: attr_val} if iostd: cst.attrs.setdefault(name, {}).update( {"IO_TYPE": iostd}) yield Fuzzer(ttyp, mod, cst, {}, iostd)
def iob(locations, corners): cnt = Counter() # keep track of how many runs are needed for ttyp, tiles in locations.items(): # for each tile of this type mod = codegen.Module() cst = codegen.Constraints() # get bels in this ttyp bels = {name[-1] for loc in tiles.values() for name in loc} locs = tiles.copy() for pin in bels: # [A, B, C, D, ...] for typ, conn in iobmap.items(): # find the next location that has pin # or make a new module for tile, names in locs.items(): name = tile + pin if name in names: del locs[tile] loc = name break else: # no usable tiles yield ttyp, mod, cst, {} cnt[ttyp] += 1 locs = tiles.copy() mod = codegen.Module() cst = codegen.Constraints() for tile, names in locs.items(): name = tile + pin if name in names: del locs[tile] loc = name break name = make_name("IOB", typ) iob = codegen.Primitive(typ, name) for port in chain.from_iterable(conn.values()): iob.portmap[port] = name + "_" + port for direction, wires in conn.items(): wnames = [name + "_" + w for w in wires] getattr(mod, direction).update(wnames) mod.primitives[name] = iob cst.ports[name] = loc yield ttyp, mod, cst, {} cnt[ttyp] += 1 # insert dummie in the corners to detect the bank enable bits runs = cnt.most_common(1)[0][1] for _ in range(runs): for ttyp in corners: mod = codegen.Module() cst = codegen.Constraints() cfg = {} yield ttyp, mod, cst, cfg
def dff(locations): for ttyp in range(12, 18): # for each tile type mod = codegen.Module() cst = codegen.Constraints() try: # get all tiles of this type # iter causes the loop to not repeat the same locs per cls locs = iter(locations[ttyp]) except KeyError: continue for cls in range(3): # for each cls for side in ["A", "B"]: for typ, port in dffmap.items(): # for each bel type try: loc = next(locs) # get the next unused tile except StopIteration: yield ttyp, mod, cst, {} locs = iter(locations[ttyp]) loc = next(locs) mod = codegen.Module() cst = codegen.Constraints() lutname = make_name("DUMMY", "LUT4") lut = codegen.Primitive("LUT4", lutname) lut.params["INIT"] = "16'hffff" lut.portmap['F'] = lutname + "_F" lut.portmap['I0'] = lutname + "_I0" lut.portmap['I1'] = lutname + "_I1" lut.portmap['I2'] = lutname + "_I2" lut.portmap['I3'] = lutname + "_I3" mod.wires.update(lut.portmap.values()) mod.primitives[lutname] = lut name = make_name("DFF", typ) dff = codegen.Primitive(typ, name) dff.portmap['CLK'] = name + "_CLK" dff.portmap['D'] = lutname + "_F" dff.portmap['Q'] = name + "_Q" if port: dff.portmap[port] = name + "_" + port mod.wires.update(dff.portmap.values()) mod.primitives[name] = dff row = loc[0] + 1 col = loc[1] + 1 cst.cells[lutname] = f"R{row}C{col}[{cls}][{side}]" cst.cells[name] = f"R{row}C{col}[{cls}][{side}]" yield ttyp, mod, cst, {}
def main(): parser = argparse.ArgumentParser(description='Unpack Gowin bitstream') parser.add_argument('bitstream') parser.add_argument('-d', '--device', required=True) parser.add_argument('-o', '--output', default='unpack.v') args = parser.parse_args() with importlib.resources.open_binary("apycula", f"{args.device}.pickle") as f: db = pickle.load(f) bitmap = read_bitstream(args.bitstream)[0] bm = chipdb.tile_bitmap(db, bitmap) mod = codegen.Module() for dest, src in db.aliases.items(): mod.wires.update({src, dest}) mod.assigns.append((dest, src)) for idx, t in bm.items(): row, col = idx print(idx) #for bitrow in t: # print(*bitrow, sep='') #if idx == (5, 0): # from fuse_h4x import * # fse = readFse(open("/home/pepijn/bin/gowin/IDE/share/device/GW1N-1/GW1N-1.fse", 'rb')) # breakpoint() bels, pips, clock_pips = parse_tile_(db, row, col, t) print(bels) #print(pips) print(clock_pips) tile2verilog(row, col, bels, pips, clock_pips, mod, db) with open(args.output, 'w') as f: mod.write(f)
def dualmode(ttyp): for pin in dualmode_pins: mod = codegen.Module() cst = codegen.Constraints() cfg = {pin: "0"} # modules with different ttyp can be combined, so in theory it could happen # that there is an IOB in the module, which claims the dual-purpose pin. # P&R will not be able to place it and the fuzzling result will be misleading. # Non-optimal: prohibit combining with anything. yield Fuzzer(ttyp, mod, cst, cfg, 'dual_mode_fuzzing')
def dualmode(ttyp): for pin in dualmode_pins: mod = codegen.Module() cst = codegen.Constraints() cfg = {pin: 'false'} yield ttyp, mod, cst, cfg
iob(pin_locations, [ fse['header']['grid'][61][0][0], fse['header']['grid'][61][-1][0], fse['header']['grid'][61][0][-1], fse['header']['grid'][61][-1][-1], ]), dff(locations), dualmode(fse['header']['grid'][61][0][0]), ) for ttyp, mod, cst, cfg in fuzzers: modmap.setdefault(ttyp, []).append(mod) cstmap.setdefault(ttyp, []).append(cst) cfgmap.setdefault(ttyp, []).append(cfg) modules = [ reduce(lambda a, b: a + b, m, codegen.Module()) for m in zip_longest(*modmap.values(), fillvalue=codegen.Module()) ] constrs = [ reduce(lambda a, b: a + b, c, codegen.Constraints()) for c in zip_longest(*cstmap.values(), fillvalue=codegen.Constraints()) ] configs = [ reduce(lambda a, b: { **a, **b }, c, {}) for c in zip_longest(*cfgmap.values(), fillvalue={}) ] type_re = re.compile(r"inst\d+_([A-Z]+)_([A-Z]+)")
num = row elif col == 47: #TODO parameterize edge = 'R' num = row else: edge = 'B' num = col f.write(f"{name} PLACE_IO{edge}{num}[{idx}]\n") iob = codegen.Primitive("IOBUF", name) iob.portmap['I'] = f"R{row}C{col}_I{idx}" iob.portmap['O'] = f"R{row}C{col}_O{idx}" iob.portmap['IO'] = f"R{row}C{col}_IO{idx}" iob.portmap['OEN'] = f"R{row}C{col}_OEN{idx}" mod.wires.update(iob.portmap.values()) mod.inouts.add(f"R{row}C{col}_IO{idx}") mod.primitives[name] = iob with open("blinky.fasm", "w") as f: write_fasm(ctx, param_map, f) mod = codegen.Module() with open("blinky.posp", "w") as f: write_posp(f) with open("blinky.vm", "w") as f: mod.write(f) #code.interact(local=locals())
def main(): parser = argparse.ArgumentParser(description='Unpack Gowin bitstream') parser.add_argument('bitstream') parser.add_argument('-d', '--device', required=True) parser.add_argument('-o', '--output', default='unpack.v') parser.add_argument('-c', '--config', default=None) parser.add_argument('-s', '--cst', default=None) parser.add_argument('--noalu', metavar='N', type = int, default = 0, help = '0 - detect the ALU') args = parser.parse_args() device = args.device # For tool integration it is allowed to pass a full part number m = re.match("GW1N([A-Z]*)-(LV|UV|UX)([0-9])C?([A-Z]{2}[0-9]+)(C[0-9]/I[0-9])", device) m = re.match("GW1N(S?)[A-Z]*-(LV|UV|UX)([0-9])C?([A-Z]{2}[0-9]+P?)(C[0-9]/I[0-9])", device) if m: mods = m.group(1) luts = m.group(3) device = f"GW1N{mods}-{luts}" with importlib.resources.open_binary("apycula", f"{device}.pickle") as f: db = pickle.load(f) bitmap = read_bitstream(args.bitstream)[0] bm = chipdb.tile_bitmap(db, bitmap) mod = codegen.Module() cfg = codegen.DeviceConfig(default_device_config()) cst = codegen.Constraints() for (drow, dcol, dname), (srow, scol, sname) in db.aliases.items(): src = f"R{srow+1}C{scol+1}_{sname}" dest = f"R{drow+1}C{dcol+1}_{dname}" mod.wires.update({src, dest}) mod.assigns.append((dest, src)) # banks first: need to know iostandards for pos in db.corners.keys(): row, col = pos try: t = bm[(row, col)] except KeyError: continue bels, pips, clock_pips = parse_tile_(db, row, col, t) tile2verilog(row, col, bels, pips, clock_pips, mod, cfg, cst, db) for idx, t in bm.items(): row, col = idx # skip banks & dual pisn if (row, col) in db.corners: continue #for bitrow in t: # print(*bitrow, sep='') #if idx == (5, 0): # from fuse_h4x import * # fse = readFse(open("/home/pepijn/bin/gowin/IDE/share/device/GW1N-1/GW1N-1.fse", 'rb')) # breakpoint() bels, pips, clock_pips = parse_tile_(db, row, col, t, noiostd = False) #print(bels) #print(pips) #print(clock_pips) if args.noalu: removeALUs(bels) else: removeLUTs(bels) ram16_remove_bels(bels) tile2verilog(row, col, bels, pips, clock_pips, mod, cfg, cst, db) with open(args.output, 'w') as f: mod.write(f) if args.config: with open(args.config, 'w') as f: cfg.write(f) if args.cst: with open(args.cst, 'w') as f: cst.write(f)
def taps(rows, cols): "Find which colunm is driven by which tap" # conver to sorted list of 1-indexed vendor constraints rows = [row + 1 for row in sorted(rows)] cols = [col + 1 for col in sorted(cols)] modules = [] constrs = [] locs = [] # use a different row for each clock # row by row, column by column, hook up the clock to the dff # in the old IDE row 1 always used clock 1 and so forth for col in cols: for gclk, row in enumerate(rows[:len(true_pins)]): mod = codegen.Module() cst = codegen.Constraints() clks = [ibuf(mod, cst, p) for p in true_pins] for i, clk in zip(rows, clks): flop = dff(mod, cst, i, col) if i <= row: mod.assigns.append((flop, clk)) modules.append(mod) constrs.append(cst) locs.append((gclk, col - 1)) pnr_res = pool.map(lambda param: tiled_fuzzer.run_pnr(*param, {}), zip(modules, constrs)) last_dffcol = None seen_primary_taps = set() seen_secondary_taps = set() seen_spines = set() clks = {} for (gclk, dff_col), (sweep_bs, *_) in zip(locs, pnr_res): sweep_tiles = chipdb.tile_bitmap(db, sweep_bs) if dff_col != last_dffcol: seen_primary_taps = set() seen_secondary_taps = set() seen_spines = set() last_dffcol = dff_col tap = None print("#" * 80) print("gclk", gclk, "dff_col", dff_col) for loc, tile in sweep_tiles.items(): row, col = loc _, _, clk_pips = gowin_unpack.parse_tile_(db, row, col, tile, noalias=True) spines = set(s for s in clk_pips.keys() if s.startswith("SPINE")) new_spines = spines - seen_spines seen_spines.update(spines) print(clk_pips.keys()) if "GT00" in clk_pips and col not in seen_primary_taps: tap = col seen_primary_taps.add(col) if "GT10" in clk_pips and col not in seen_secondary_taps: tap = col seen_secondary_taps.add(col) print("loc", row, col, "tap", tap, "new spines", new_spines) # if tap == None: breakpoint() clks.setdefault(gclk, {}).setdefault(tap, set()).add(dff_col) print(clks) return clks
pnr_data = {} for fuzzer in fuzzers: pnr_data.setdefault(fuzzer.iostd, DataForPnr({}, {}, {})) pnr_data[fuzzer.iostd].modmap.setdefault(fuzzer.ttyp, []).append(fuzzer.mod) pnr_data[fuzzer.iostd].cstmap.setdefault(fuzzer.ttyp, []).append(fuzzer.cst) pnr_data[fuzzer.iostd].cfgmap.setdefault(fuzzer.ttyp, []).append(fuzzer.cfg) modules = [] constrs = [] configs = [] for data in pnr_data.values(): modules += [ reduce(lambda a, b: a + b, m, codegen.Module()) for m in zip_longest(*data.modmap.values(), fillvalue=codegen.Module()) ] constrs += [ reduce(lambda a, b: a + b, c, codegen.Constraints()) for c in zip_longest(*data.cstmap.values(), fillvalue=codegen.Constraints()) ] configs += [ reduce(lambda a, b: { **a, **b }, c, {}) for c in zip_longest(*data.cfgmap.values(), fillvalue={}) ]
def taps(rows, cols): "Find which colunm is driven by which tap" mod = codegen.Module() cst = codegen.Constraints() clks = [ibuf(mod, cst, p) for p in true_pins] # conver to sorted list of 1-indexed vendor constraints rows = [row + 1 for row in sorted(rows)] cols = [col + 1 for col in sorted(cols)] for row in rows[:len(true_pins)]: for col in cols: flop = dff(mod, cst, row, col) bs_base, _, _, _, _ = tiled_fuzzer.run_pnr(mod, cst, {}) modules = [] constrs = [] locs = [] for gclk, row in enumerate(rows[:len(true_pins)]): for col in cols: mod = codegen.Module() cst = codegen.Constraints() clks = [ibuf(mod, cst, p) for p in true_pins] for i, clk in zip(rows, clks): for j in cols: flop = dff(mod, cst, i, j) if i < row: mod.assigns.append((flop, clk)) elif i == row and j == col: mod.assigns.append((flop, clk)) modules.append(mod) constrs.append(cst) locs.append((gclk, col - 1)) pnr_res = pool.map(lambda param: tiled_fuzzer.run_pnr(*param, {}), zip(modules, constrs)) clks = {} complete_taps = set() last_gclk = None for (gclk, dff_col), (sweep_bs, *_) in zip(locs, pnr_res): sweep_tiles = chipdb.tile_bitmap(db, sweep_bs ^ bs_base) tap = None if last_gclk != None and gclk != last_gclk: complete_taps.update(clks[last_gclk].keys()) last_gclk = gclk if gclk == 4: # secondary taps complete_taps = set() # print("#"*80) # print("gclk", gclk, "dff_col", dff_col) for loc, tile in sweep_tiles.items(): row, col = loc _, _, clk_pips = gowin_unpack.parse_tile_(db, row, col, tile, noalias=True) # print("loc", row, col, "pips", clk_pips) if ("GT00" in clk_pips and gclk < 4) or \ ("GT10" in clk_pips and gclk >= 4): # print("tap", col) if col not in complete_taps: tap = col clks.setdefault(gclk, {}).setdefault(tap, set()).add(dff_col) # print(complete_taps, clks) #if not tap: break return clks