mirror of https://github.com/YosysHQ/nextpnr.git
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:
parent
7d2caf6939
commit
a18bd2e055
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue