From 49f8620ac99a3af95eacc4ad8f1f11c6b36dcd25 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sat, 22 Jul 2023 10:01:35 +1000 Subject: [PATCH] gowin: Himbaechel. Implement PLLs - The global router is modified to work out the routing of PLL outputs and inputs; - Added API function to change wire type after its creation - there was a need to unify all wires included in the node at the stage of node creation, when all wires have already been created. Signed-off-by: YRabbit --- himbaechel/himbaechel_dbgen/chip.py | 3 ++ himbaechel/uarch/gowin/constids.inc | 3 ++ himbaechel/uarch/gowin/globals.cc | 50 +++++++++++++++---- himbaechel/uarch/gowin/gowin.h | 2 + himbaechel/uarch/gowin/gowin_arch_gen.py | 61 ++++++++++++++++++++---- himbaechel/uarch/gowin/pack.cc | 38 +++++++++++++-- 6 files changed, 134 insertions(+), 23 deletions(-) diff --git a/himbaechel/himbaechel_dbgen/chip.py b/himbaechel/himbaechel_dbgen/chip.py index 857935df..f392cb73 100644 --- a/himbaechel/himbaechel_dbgen/chip.py +++ b/himbaechel/himbaechel_dbgen/chip.py @@ -235,6 +235,9 @@ class TileType(BBAStruct): def has_wire(self, wire: str): # Check if a wire has already been created return self.strs.id(wire) in self._wire2idx + def set_wire_type(self, wire: str, type: str): + # wire type change + self.wires[self._wire2idx[self.strs.id(wire)]].wire_type = self.strs.id(type) def serialise_lists(self, context: str, bba: BBAWriter): # list children of members for i, bel in enumerate(self.bels): diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index e646f91b..6cec43f4 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1059,6 +1059,7 @@ X(router) // misc X(GOWIN_GND) X(GOWIN_VCC) +X(PLL) // wire types X(GLOBAL_CLK) @@ -1075,6 +1076,8 @@ X(MUX_OUT) X(MUX_SEL) X(ALU_CIN) X(ALU_COUT) +X(PLL_O) +X(PLL_I) // fake dff inputs X(XD0) diff --git a/himbaechel/uarch/gowin/globals.cc b/himbaechel/uarch/gowin/globals.cc index bb2e2b08..01ebaf8c 100644 --- a/himbaechel/uarch/gowin/globals.cc +++ b/himbaechel/uarch/gowin/globals.cc @@ -43,14 +43,21 @@ struct GowinGlobalRouter // allow io->global, global->global and global->tile clock bool global_pip_filter(PipId pip) const { - /* + auto is_local = [&](IdString wire_type) { + return !wire_type.in(id_GLOBAL_CLK, id_IO_O, id_IO_I, id_PLL_O, id_PLL_I, id_TILE_CLK); + }; + IdString src_type = ctx->getWireType(ctx->getPipSrcWire(pip)); IdString dst_type = ctx->getWireType(ctx->getPipDstWire(pip)); - bool src_valid = src_type.in(id_GLOBAL_CLK, id_IO_O); - bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK); - return src_valid && dst_valid; - */ - return true; + bool src_valid = src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O); + bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I); + + if (ctx->debug) { + log_info("%s <- %s [%s <- %s]\n", ctx->getWireName(ctx->getPipDstWire(pip)).str(ctx).c_str(), + ctx->getWireName(ctx->getPipSrcWire(pip)).str(ctx).c_str(), dst_type.c_str(ctx), + src_type.c_str(ctx)); + } + return (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid); } bool is_relaxed_sink(const PortRef &sink) const { return false; } @@ -151,10 +158,19 @@ struct GowinGlobalRouter void route_clk_net(NetInfo *net) { - for (auto usr : net->users.enumerate()) - backwards_bfs_route(net, usr.index, 1000000, true, - [&](PipId pip) { return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); }); - log_info(" routed net '%s' using global resources\n", ctx->nameOf(net)); + bool routed = false; + for (auto usr : net->users.enumerate()) { + routed = backwards_bfs_route(net, usr.index, 1000000, true, [&](PipId pip) { + return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); + }); + if (!routed) { + break; + } + } + + if (routed) { + log_info(" routed net '%s' using global resources\n", ctx->nameOf(net)); + } } bool driver_is_clksrc(const PortRef &driver) @@ -166,10 +182,24 @@ struct GowinGlobalRouter IdStringList pin_func = gwu.get_pin_funcs(driver.cell->bel); for (size_t i = 0; i < pin_func.size(); ++i) { if (pin_func[i].str(ctx).rfind("GCLKT", 0) == 0) { + if (ctx->debug) { + log_info("Clock pin:%s:%s\n", ctx->getBelName(driver.cell->bel).str(ctx).c_str(), + pin_func[i].c_str(ctx)); + } return true; } } } + // PLL outputs + if (driver.cell->type.in(id_rPLL, id_PLLVR)) { + if (driver.port.in(id_CLKOUT, id_CLKOUTD, id_CLKOUTD3, id_CLKOUTP)) { + if (ctx->debug) { + log_info("PLL out:%s:%s\n", ctx->getBelName(driver.cell->bel).str(ctx).c_str(), + driver.port.c_str(ctx)); + } + return true; + } + } return false; } diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index e8313038..fd3c6dd9 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -39,6 +39,8 @@ enum ALU0_Z = 30, // :35, 6 ALU RAMW_Z = 36, // RAM16SDP4 + PLL_Z = 275, + GSR_Z = 276, VCC_Z = 277, VSS_Z = 278 }; diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 824e5977..38e81275 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -22,6 +22,7 @@ MUX27_Z = 29 ALU0_Z = 30 # : 35, 6 ALUs RAMW_Z = 36 # RAM16SDP4 +PLL_Z = 275 GSR_Z = 276 VCC_Z = 277 GND_Z = 278 @@ -121,8 +122,11 @@ def create_nodes(chip: Chip, db: chipdb): global_nodes.setdefault('VCC', []).append(NodeWire(x, y, 'VCC')) # add nodes from the apicula db - for node_name, node in db.nodes.items(): + for node_name, node_hdr in db.nodes.items(): + wire_type, node = node_hdr for y, x, wire in node: + if wire_type: + chip.tile_type_at(x, y).set_wire_type(wire, wire_type) new_node = NodeWire(x, y, wire) gl_nodes = global_nodes.setdefault(node_name, []) if new_node not in gl_nodes: @@ -139,9 +143,7 @@ def create_nodes(chip: Chip, db: chipdb): # coordinates. def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int): def get_wire_type(name): - if name.startswith('GB') or name.startswith('GT'): - return "GLOBAL_CLK" - elif name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}: + if name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}: return "X0" return "" @@ -150,7 +152,7 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int): tt.create_wire(dst, get_wire_type(dst)) for src in srcs.keys(): if not tt.has_wire(src): - tt.create_wire(src, get_wire_type(dst)) + tt.create_wire(src, get_wire_type(src)) tt.create_pip(src, dst) # clock wires for dst, srcs in db.grid[y][x].clock_pips.items(): @@ -336,18 +338,56 @@ def create_ssram_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int): for i in range(4): tt.add_bel_pin(ff, f"DI[{i}]", f"{lut_inputs[i]}5", PinType.INPUT) tt.add_bel_pin(ff, f"WAD[{i}]", f"{lut_inputs[i]}4", PinType.INPUT) - # RAD[0] is RAD[0] is assumed to be connected to A3, A2, A1 and A0. But + # RAD[0] is assumed to be connected to A3, A2, A1 and A0. But # for now we connect it only to A0, the others will be connected # directly during packing. RAD[1...3] - similarly. tt.add_bel_pin(ff, f"RAD[{i}]", f"{lut_inputs[i]}0", PinType.INPUT) tt.add_bel_pin(ff, f"DO[{i}]", f"F{i}", PinType.OUTPUT) - tt.add_bel_pin(ff, f"CLK", "CLK2", PinType.INPUT) tt.add_bel_pin(ff, f"CE", "CE2", PinType.INPUT) tt.add_bel_pin(ff, f"WRE", "LSR2", PinType.INPUT) return (ttyp, tt) +# PLL main tile +_pll_inputs = {'CLKFB', 'FBDSEL0', 'FBDSEL1', 'FBDSEL2', 'FBDSEL3', + 'FBDSEL4', 'FBDSEL5', 'IDSEL0', 'IDSEL1', 'IDSEL2', 'IDSEL3', + 'IDSEL4', 'IDSEL5', 'ODSEL0', 'ODSEL1', 'ODSEL2', 'ODSEL3', + 'ODSEL4', 'ODSEL5', 'RESET', 'RESET_P', 'PSDA0', 'PSDA1', + 'PSDA2', 'PSDA3', 'DUTYDA0', 'DUTYDA1', 'DUTYDA2', 'DUTYDA3', + 'FDLY0', 'FDLY1', 'FDLY2', 'FDLY3', 'CLKIN'} +_pll_outputs = {'CLKOUT', 'LOCK', 'CLKOUTP', 'CLKOUTD', 'CLKOUTD3'} +def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int): + if ttyp in created_tiletypes: + return ttyp, None + typename = "PLL" + tt = chip.create_tile_type(f"{typename}_{ttyp}") + tt.extra_data = TileExtraData(chip.strs.id(typename)) + + # wires + if chip.name == 'GW1NS-4': + pll_name = 'PLLVR' + bel_type = 'PLLVR' + else: + pll_name = 'RPLLA' + bel_type = 'rPLL' + portmap = db.grid[y][x].bels[pll_name].portmap + pll = tt.create_bel("PLL", bel_type, z = PLL_Z) + # Not sure how this will affect routing - PLLs are fixed and their outputs + # will be handled by a dedicated router + #pll.flags = BEL_FLAG_GLOBAL + for pin, wire in portmap.items(): + if pin in _pll_inputs: + tt.create_wire(wire, "PLL_I") + tt.add_bel_pin(pll, pin, wire, PinType.INPUT) + else: + assert pin in _pll_outputs, f"Unknown PLL pin{pin}" + tt.create_wire(wire, "PLL_O") + tt.add_bel_pin(pll, pin, wire, PinType.OUTPUT) + + create_switch_matrix(tt, db, x, y) + return (ttyp, tt) + # pinouts, packages... _tbrlre = re.compile(r"IO([TBRL])(\d+)(\w)") def create_packages(chip: Chip, db: chipdb): @@ -411,9 +451,10 @@ def main(): # these differences (in case it turns out later that there is a slightly # different routing or something like that). logic_tiletypes = {12, 13, 14, 15, 16} - io_tiletypes = {52, 53, 55, 58, 59, 64, 65, 66} + io_tiletypes = {52, 53, 54, 55, 58, 59, 64, 65, 66} ssram_tiletypes = {17, 18, 19} gsr_tiletypes = {1} + pll_tiletypes = {86, 87, 42, 45} # Setup tile grid for x in range(X): for y in range(Y): @@ -440,6 +481,10 @@ def main(): ttyp, _ = create_io_tiletype(ch, db, x, y, ttyp) created_tiletypes.add(ttyp) ch.set_tile_type(x, y, f"IO_{ttyp}") + elif ttyp in pll_tiletypes: + ttyp, _ = create_pll_tiletype(ch, db, x, y, ttyp) + created_tiletypes.add(ttyp) + ch.set_tile_type(x, y, f"PLL_{ttyp}") else: ttyp, _ = create_null_tiletype(ch, db, x, y, ttyp) created_tiletypes.add(ttyp) diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index 79406698..ff9da757 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -505,17 +505,18 @@ struct GowinPacker } } - // Pack global set-reset + // =================================== + // Global set/reset + // =================================== void pack_gsr(void) { log_info("Packing GSR..\n"); bool user_gsr = false; for (auto &cell : ctx->cells) { - CellInfo *ci = cell.second.get(); - if (ctx->verbose) - log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx)); - if (ci->type == id_GSR) { + auto &ci = *cell.second; + + if (ci.type == id_GSR) { user_gsr = true; break; } @@ -541,6 +542,32 @@ struct GowinPacker } } + // =================================== + // PLL + // =================================== + void pack_pll(void) + { + log_info("Packing PLL..\n"); + + for (auto &cell : ctx->cells) { + auto &ci = *cell.second; + + if (ci.type == id_rPLL) { + // pin renaming for compatibility + for (int i = 0; i < 6; ++i) { + ci.renamePort(ctx->idf("FBDSEL[%d]", i), ctx->idf("FBDSEL%d", i)); + ci.renamePort(ctx->idf("IDSEL[%d]", i), ctx->idf("IDSEL%d", i)); + ci.renamePort(ctx->idf("ODSEL[%d]", i), ctx->idf("ODSEL%d", i)); + if (i < 4) { + ci.renamePort(ctx->idf("PSDA[%d]", i), ctx->idf("PSDA%d", i)); + ci.renamePort(ctx->idf("DUTYDA[%d]", i), ctx->idf("DUTYDA%d", i)); + ci.renamePort(ctx->idf("FDLY[%d]", i), ctx->idf("FDLY%d", i)); + } + } + } + } + } + void run(void) { pack_iobs(); @@ -549,6 +576,7 @@ struct GowinPacker pack_wideluts(); pack_alus(); constrain_lutffs(); + pack_pll(); pack_ram16sdp4(); } };