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 <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2025-09-02 15:51:08 +10:00 committed by GitHub
parent 7d2caf6939
commit a18bd2e055
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 37 additions and 4 deletions

View File

@ -44,6 +44,16 @@ struct GowinGlobalRouter
bool segment_wire_filter(PipId pip) const { return !gwu.is_segment_pip(pip); } 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 bool dcs_input_filter(PipId pip) const
{ {
return !ctx->getWireName(ctx->getPipSrcWire(pip))[1].in( return !ctx->getWireName(ctx->getPipSrcWire(pip))[1].in(
@ -79,8 +89,8 @@ struct GowinGlobalRouter
if (ctx->debug && false /*&& res*/) { if (ctx->debug && false /*&& res*/) {
log_info("%s <- %s [%s <- %s]\n", ctx->nameOfWire(ctx->getPipDstWire(pip)), 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)); 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, log_info(" res:%d, src_valid:%d, dst_valid:%d, src local:%d, dst local:%d, dst gate:%d\n", res, src_valid,
is_local(src_type), is_local(dst_type)); dst_valid, is_local(src_type), is_local(dst_type), gwu.wire_is_clock_gate(dst));
} }
return res; return res;
} }
@ -623,7 +633,7 @@ struct GowinGlobalRouter
src = ctx->getBelPinWire(true_src_ci->bel, net_before_buf->driver.port); src = ctx->getBelPinWire(true_src_ci->bel, net_before_buf->driver.port);
ctx->bindWire(src, net, STRENGTH_LOCKED); ctx->bindWire(src, net, STRENGTH_LOCKED);
backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip, WireId src_wire) { 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 // remove net
buf_ci->movePortTo(id_O, true_src_ci, net_before_buf->driver.port); 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_clk_net(NetInfo *net)
{ {
RouteResult route_result = route_direct_net(net, [&](PipId pip, WireId src_wire) { 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) { if (route_result != NOT_ROUTED) {
log_info(" '%s' net was routed using global resources %s.\n", ctx->nameOf(net), log_info(" '%s' net was routed using global resources %s.\n", ctx->nameOf(net),

View File

@ -14,6 +14,9 @@ from apycula import chipdb
# Bel flags # Bel flags
BEL_FLAG_SIMPLE_IO = 0x100 BEL_FLAG_SIMPLE_IO = 0x100
# Wire flags
WIRE_FLAG_CLOCK_GATE = 0x1
# Chip flags # Chip flags
CHIP_HAS_SP32 = 0x1 CHIP_HAS_SP32 = 0x1
CHIP_NEED_SP_FIX = 0x2 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.create_wire(wire, "PINCFG_IN")
tt.add_bel_pin(bel, port, wire, PinType.INPUT) 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): def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
has_extra_func = (y, x) in db.extra_func 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_extra_funcs(tt, db, x, y)
create_hclk_switch_matrix(tt, db, x, y) create_hclk_switch_matrix(tt, db, x, y)
create_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) chip.set_tile_type(x, y, tdesc.tiletype)
def add_port_wire(tt, bel, portmap, name, wire_type, port_type, pin_name = None): def add_port_wire(tt, bel, portmap, name, wire_type, port_type, pin_name = None):

View File

@ -12,6 +12,10 @@ namespace BelFlags {
static constexpr uint32_t FLAG_SIMPLE_IO = 0x100; static constexpr uint32_t FLAG_SIMPLE_IO = 0x100;
} }
namespace WireFlags {
static constexpr uint32_t FLAG_CLOCK_GATE = 0x1;
}
struct GowinUtils struct GowinUtils
{ {
Context *ctx; Context *ctx;
@ -85,6 +89,12 @@ struct GowinUtils
} }
bool driver_is_clksrc(const PortRef &driver); 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 // BSRAM
bool has_SP32(void); bool has_SP32(void);
bool need_SP_fix(void); bool need_SP_fix(void);