From a18bd2e055cdd723f322c55d1b454bba9deb0015 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 2 Sep 2025 15:51:08 +1000 Subject: [PATCH] Gowin. BUGFIX. Add data about gate wires. (#1547) Very rarely (about once a year), the dedicated clock router would malfunction, issuing an incorrect route. The reason turned out to be the so-called gate wires to the global clock wire system from the logic. Among the PIPs for which these wires are sinks, there are PIPs where the sources are also clock wires. This leads to the possibility of feeding the clock signal back into the gate and again into the global clock MUX. If handled carelessly, this can lead to a complete loop. But the loop option itself is particularly useful in the case of DCS (dynamic clock selection) - the fact is that because these primitives have four clock inputs and each of them could theoretically address all 56 clock sources, but in practice there are not enough wires and the DCS inputs cannot serve as sinks for all clock sources. The simplest solution (and the one that currently works) is to use the gate to re-enter the clock system, but this time changing the clock source. This commit explicitly marks wires as gates and removes the possibility of looping (however unlikely it may be) where a loop is not needed. Signed-off-by: YRabbit --- himbaechel/uarch/gowin/globals.cc | 19 +++++++++++++++---- himbaechel/uarch/gowin/gowin_arch_gen.py | 12 ++++++++++++ himbaechel/uarch/gowin/gowin_utils.h | 10 ++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/himbaechel/uarch/gowin/globals.cc b/himbaechel/uarch/gowin/globals.cc index 1011bc41..e5b89e92 100644 --- a/himbaechel/uarch/gowin/globals.cc +++ b/himbaechel/uarch/gowin/globals.cc @@ -44,6 +44,16 @@ struct GowinGlobalRouter bool segment_wire_filter(PipId pip) const { return !gwu.is_segment_pip(pip); } + // To avoid a cycle where we connect the clock wire to the gate in + // the global clock system and it ends up in the global clock MUX + // again, we only allow connections from general-purpose wires. + bool clock_gate_wire_filter(PipId pip) const + { + WireId dst = ctx->getPipDstWire(pip); + IdString src_type = ctx->getWireType(ctx->getPipSrcWire(pip)); + return !(gwu.wire_is_clock_gate(dst) && src_type.in(id_GLOBAL_CLK, id_TILE_CLK)); + } + bool dcs_input_filter(PipId pip) const { return !ctx->getWireName(ctx->getPipSrcWire(pip))[1].in( @@ -79,8 +89,8 @@ struct GowinGlobalRouter if (ctx->debug && false /*&& res*/) { log_info("%s <- %s [%s <- %s]\n", ctx->nameOfWire(ctx->getPipDstWire(pip)), ctx->nameOfWire(ctx->getPipSrcWire(pip)), dst_type.c_str(ctx), src_type.c_str(ctx)); - log_info(" res:%d, src_valid:%d, dst_valid:%d, src local:%d, dst local:%d\n", res, src_valid, dst_valid, - is_local(src_type), is_local(dst_type)); + log_info(" res:%d, src_valid:%d, dst_valid:%d, src local:%d, dst local:%d, dst gate:%d\n", res, src_valid, + dst_valid, is_local(src_type), is_local(dst_type), gwu.wire_is_clock_gate(dst)); } return res; } @@ -623,7 +633,7 @@ struct GowinGlobalRouter src = ctx->getBelPinWire(true_src_ci->bel, net_before_buf->driver.port); ctx->bindWire(src, net, STRENGTH_LOCKED); backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip, WireId src_wire) { - return segment_wire_filter(pip) && dcs_input_filter(pip); + return clock_gate_wire_filter(pip) && segment_wire_filter(pip) && dcs_input_filter(pip); }); // remove net buf_ci->movePortTo(id_O, true_src_ci, net_before_buf->driver.port); @@ -635,7 +645,8 @@ struct GowinGlobalRouter RouteResult route_clk_net(NetInfo *net) { RouteResult route_result = route_direct_net(net, [&](PipId pip, WireId src_wire) { - return global_pip_filter(pip, src_wire) && segment_wire_filter(pip) && dcs_input_filter(pip); + return clock_gate_wire_filter(pip) && global_pip_filter(pip, src_wire) && segment_wire_filter(pip) && + dcs_input_filter(pip); }); if (route_result != NOT_ROUTED) { log_info(" '%s' net was routed using global resources %s.\n", ctx->nameOf(net), diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 5a82ccb8..c6172355 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -14,6 +14,9 @@ from apycula import chipdb # Bel flags BEL_FLAG_SIMPLE_IO = 0x100 +# Wire flags +WIRE_FLAG_CLOCK_GATE = 0x1 + # Chip flags CHIP_HAS_SP32 = 0x1 CHIP_NEED_SP_FIX = 0x2 @@ -768,6 +771,14 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int): tt.create_wire(wire, "PINCFG_IN") tt.add_bel_pin(bel, port, wire, PinType.INPUT) +def set_wire_flags(tt: TileType, tdesc: TypeDesc): + if tdesc.extra_func and 'clock_gates' in tdesc.extra_func: + for wire_name in tdesc.extra_func['clock_gates']: + wname_id = tt.strs.id(wire_name) + for wire_data in tt.wires: + if wire_data.name == wname_id: + wire_data.flags |= WIRE_FLAG_CLOCK_GATE + def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int): has_extra_func = (y, x) in db.extra_func @@ -817,6 +828,7 @@ def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: i create_extra_funcs(tt, db, x, y) create_hclk_switch_matrix(tt, db, x, y) create_switch_matrix(tt, db, x, y) + set_wire_flags(tt, tdesc) chip.set_tile_type(x, y, tdesc.tiletype) def add_port_wire(tt, bel, portmap, name, wire_type, port_type, pin_name = None): diff --git a/himbaechel/uarch/gowin/gowin_utils.h b/himbaechel/uarch/gowin/gowin_utils.h index 3c8fd453..c3a598ea 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -12,6 +12,10 @@ namespace BelFlags { static constexpr uint32_t FLAG_SIMPLE_IO = 0x100; } +namespace WireFlags { +static constexpr uint32_t FLAG_CLOCK_GATE = 0x1; +} + struct GowinUtils { Context *ctx; @@ -85,6 +89,12 @@ struct GowinUtils } bool driver_is_clksrc(const PortRef &driver); + // clock nets + bool wire_is_clock_gate(WireId wire) const + { + return chip_wire_info(ctx->chip_info, wire).flags & WireFlags::FLAG_CLOCK_GATE; + } + // BSRAM bool has_SP32(void); bool need_SP_fix(void);