mirror of https://github.com/YosysHQ/nextpnr.git
Gowin. GW5A series PLLs. (#1557)
PLLA-type PLLs are implemented, which are used in GW5A-25A chips. These are six powerful PLLs, each of which can generate seven independent frequencies. Since these devices have an unusual configuration—their fuse bits are located outside the main grid and therefore their Bels do not have specific “correct” coordinates—the extra bel functions mechanism is used to describe them. But all the complexity falls on the apicula part. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
4ab735c690
commit
1742d09edb
|
|
@ -1033,6 +1033,7 @@ X(OSCO)
|
||||||
X(rPLL)
|
X(rPLL)
|
||||||
X(RPLLA)
|
X(RPLLA)
|
||||||
X(PLLVR)
|
X(PLLVR)
|
||||||
|
X(PLLA)
|
||||||
|
|
||||||
// primitive attributes
|
// primitive attributes
|
||||||
X(INIT)
|
X(INIT)
|
||||||
|
|
@ -1145,7 +1146,15 @@ X(P47B)
|
||||||
X(P47C)
|
X(P47C)
|
||||||
X(P47D)
|
X(P47D)
|
||||||
|
|
||||||
// PLL parameters
|
// PLL
|
||||||
|
X(CLKOUT0)
|
||||||
|
X(CLKOUT1)
|
||||||
|
X(CLKOUT2)
|
||||||
|
X(CLKOUT3)
|
||||||
|
X(CLKOUT4)
|
||||||
|
X(CLKOUT5)
|
||||||
|
X(CLKOUT6)
|
||||||
|
X(CLKOUT7)
|
||||||
X(CLKOUTPS)
|
X(CLKOUTPS)
|
||||||
X(CLKOUTDIV)
|
X(CLKOUTDIV)
|
||||||
X(CLKOUTDIV3)
|
X(CLKOUTDIV3)
|
||||||
|
|
|
||||||
|
|
@ -77,12 +77,12 @@ struct GowinGlobalRouter
|
||||||
IdString dst_type = ctx->getWireType(dst);
|
IdString dst_type = ctx->getWireType(dst);
|
||||||
bool src_is_outpin = src_type.in(id_IO_O, id_PLL_O, id_HCLK, id_DLLDLY, id_OSCOUT);
|
bool src_is_outpin = src_type.in(id_IO_O, id_PLL_O, id_HCLK, id_DLLDLY, id_OSCOUT);
|
||||||
bool src_valid = not_dcs_pip && (src_type == id_GLOBAL_CLK || src_is_outpin);
|
bool src_valid = not_dcs_pip && (src_type == id_GLOBAL_CLK || src_is_outpin);
|
||||||
bool dst_valid = not_dcs_pip && dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I, id_HCLK);
|
bool dst_valid = not_dcs_pip && dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_PLL_O, id_IO_I, id_HCLK);
|
||||||
|
|
||||||
bool res;
|
bool res;
|
||||||
if (src == src_wire && (src_type == id_PLL_O || (!src_is_outpin))) {
|
if (src == src_wire && (src_type == id_PLL_O || (!src_is_outpin))) {
|
||||||
bool dst_is_spine = dst_name.str(ctx).rfind("SPINE", 0) == 0;
|
bool dst_is_spine = dst_name.str(ctx).rfind("SPINE", 0) == 0;
|
||||||
res = src_valid && dst_is_spine;
|
res = src_valid && (dst_is_spine || dst_type == id_PLL_O);
|
||||||
} else {
|
} else {
|
||||||
res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -510,8 +510,7 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
||||||
if not tt.has_wire(dst):
|
if not tt.has_wire(dst):
|
||||||
tt.create_wire(dst, get_wire_type(dst))
|
tt.create_wire(dst, get_wire_type(dst))
|
||||||
for src in srcs.keys():
|
for src in srcs.keys():
|
||||||
if src not in db.wire_delay:
|
assert src in db.wire_delay, f"No timing info for {src} wire"
|
||||||
continue
|
|
||||||
if not tt.has_wire(src):
|
if not tt.has_wire(src):
|
||||||
if src in {"VSS", "VCC"}:
|
if src in {"VSS", "VCC"}:
|
||||||
tt.create_wire(src, get_wire_type(src), const_value = src)
|
tt.create_wire(src, get_wire_type(src), const_value = src)
|
||||||
|
|
@ -770,6 +769,15 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
||||||
if not tt.has_wire(wire):
|
if not tt.has_wire(wire):
|
||||||
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)
|
||||||
|
elif func == 'pll':
|
||||||
|
pll = tt.create_bel("PLL", "PLLA", z = PLL_Z)
|
||||||
|
pll.flags = BEL_FLAG_GLOBAL
|
||||||
|
for pin, wire in desc['outputs'].items():
|
||||||
|
tt.create_wire(wire, "PLL_O")
|
||||||
|
tt.add_bel_pin(pll, pin, wire, PinType.OUTPUT)
|
||||||
|
for pin, wire in desc['inputs'].items():
|
||||||
|
tt.create_wire(wire, "PLL_I")
|
||||||
|
tt.add_bel_pin(pll, pin, wire, PinType.INPUT)
|
||||||
|
|
||||||
def set_wire_flags(tt: TileType, tdesc: TypeDesc):
|
def set_wire_flags(tt: TileType, tdesc: TypeDesc):
|
||||||
if tdesc.extra_func and 'clock_gates' in tdesc.extra_func:
|
if tdesc.extra_func and 'clock_gates' in tdesc.extra_func:
|
||||||
|
|
@ -1314,13 +1322,6 @@ def create_dsp_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc
|
||||||
return tt
|
return tt
|
||||||
|
|
||||||
# PLL main tile
|
# 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', 'VREN'}
|
|
||||||
_pll_outputs = {'CLKOUT', 'LOCK', 'CLKOUTP', 'CLKOUTD', 'CLKOUTD3'}
|
|
||||||
def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
||||||
typename = "PLL"
|
typename = "PLL"
|
||||||
tiletype = f"{typename}_{ttyp}"
|
tiletype = f"{typename}_{ttyp}"
|
||||||
|
|
@ -1337,8 +1338,8 @@ def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc
|
||||||
tt = chip.create_tile_type(tiletype)
|
tt = chip.create_tile_type(tiletype)
|
||||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||||
|
|
||||||
|
|
||||||
# wires
|
# wires
|
||||||
|
pll_outputs = {'CLKOUT', 'LOCK', 'CLKOUTP', 'CLKOUTD', 'CLKOUTD3'}
|
||||||
if chip.name == 'GW1NS-4':
|
if chip.name == 'GW1NS-4':
|
||||||
pll_name = 'PLLVR'
|
pll_name = 'PLLVR'
|
||||||
bel_type = 'PLLVR'
|
bel_type = 'PLLVR'
|
||||||
|
|
@ -1349,13 +1350,12 @@ def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc
|
||||||
pll = tt.create_bel("PLL", bel_type, z = PLL_Z)
|
pll = tt.create_bel("PLL", bel_type, z = PLL_Z)
|
||||||
pll.flags = BEL_FLAG_GLOBAL
|
pll.flags = BEL_FLAG_GLOBAL
|
||||||
for pin, wire in portmap.items():
|
for pin, wire in portmap.items():
|
||||||
if pin in _pll_inputs:
|
if pin in pll_outputs:
|
||||||
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.create_wire(wire, "PLL_O")
|
||||||
tt.add_bel_pin(pll, pin, wire, PinType.OUTPUT)
|
tt.add_bel_pin(pll, pin, wire, PinType.OUTPUT)
|
||||||
|
else:
|
||||||
|
tt.create_wire(wire, "PLL_I")
|
||||||
|
tt.add_bel_pin(pll, pin, wire, PinType.INPUT)
|
||||||
tdesc.tiletype = tiletype
|
tdesc.tiletype = tiletype
|
||||||
return tt
|
return tt
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,9 @@ bool GowinUtils::driver_is_clksrc(const PortRef &driver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// PLL outputs
|
// PLL outputs
|
||||||
if (driver.cell->type.in(id_rPLL, id_PLLVR)) {
|
if (driver.cell->type.in(id_rPLL, id_PLLVR, id_PLLA)) {
|
||||||
if (driver.port.in(id_CLKOUT, id_CLKOUTD, id_CLKOUTD3, id_CLKOUTP)) {
|
if (driver.port.in(id_CLKOUT, id_CLKOUTD, id_CLKOUTD3, id_CLKOUTP, id_CLKOUT0, id_CLKOUT1, id_CLKOUT2,
|
||||||
|
id_CLKOUT3, id_CLKOUT4, id_CLKOUT5, id_CLKOUT6, id_CLKOUT7)) {
|
||||||
if (ctx->debug) {
|
if (ctx->debug) {
|
||||||
if (driver.cell->bel != BelId()) {
|
if (driver.cell->bel != BelId()) {
|
||||||
log_info("PLL out bel:%s:%s\n", ctx->nameOfBel(driver.cell->bel), driver.port.c_str(ctx));
|
log_info("PLL out bel:%s:%s\n", ctx->nameOfBel(driver.cell->bel), driver.port.c_str(ctx));
|
||||||
|
|
|
||||||
|
|
@ -3905,8 +3905,24 @@ struct GowinPacker
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
auto &ci = *cell.second;
|
auto &ci = *cell.second;
|
||||||
|
|
||||||
if (ci.type.in(id_rPLL, id_PLLVR)) {
|
if (ci.type.in(id_rPLL, id_PLLVR, id_PLLA)) {
|
||||||
// pin renaming for compatibility
|
// pin renaming for compatibility
|
||||||
|
if (ci.type == id_PLLA) {
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
ci.renamePort(ctx->idf("MDWDI[%d]", i), ctx->idf("MDWDI%d", i));
|
||||||
|
ci.renamePort(ctx->idf("MDRDO[%d]", i), ctx->idf("MDRDO%d", i));
|
||||||
|
if (i < 7) {
|
||||||
|
ci.renamePort(ctx->idf("SSCMDSEL[%d]", i), ctx->idf("SSCMDSEL%d", i));
|
||||||
|
if (i < 3) {
|
||||||
|
ci.renamePort(ctx->idf("SSCMDSEL_FRAC[%d]", i), ctx->idf("SSCMDSEL_FRAC%d", i));
|
||||||
|
ci.renamePort(ctx->idf("PSSEL[%d]", i), ctx->idf("PSSEL%d", i));
|
||||||
|
}
|
||||||
|
if (i < 2) {
|
||||||
|
ci.renamePort(ctx->idf("MDOPC[%d]", i), ctx->idf("MDOPC%d", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for (int i = 0; i < 6; ++i) {
|
for (int i = 0; i < 6; ++i) {
|
||||||
ci.renamePort(ctx->idf("FBDSEL[%d]", i), ctx->idf("FBDSEL%d", 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("IDSEL[%d]", i), ctx->idf("IDSEL%d", i));
|
||||||
|
|
@ -3917,6 +3933,7 @@ struct GowinPacker
|
||||||
ci.renamePort(ctx->idf("FDLY[%d]", i), ctx->idf("FDLY%d", i));
|
ci.renamePort(ctx->idf("FDLY[%d]", i), ctx->idf("FDLY%d", i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// If CLKIN is connected to a special pin, then it makes sense
|
// If CLKIN is connected to a special pin, then it makes sense
|
||||||
// to try to place the PLL so that it uses a direct connection
|
// to try to place the PLL so that it uses a direct connection
|
||||||
// to this pin.
|
// to this pin.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue