gowin: Add PLL support for the GW1NR-9C chip

This chip is used in the Tangnano9k board.

  * all parameters of the rPLL primitive are supported;
  * all PLL outputs are treated as clock sources and optimized routing
    is applied to them;
  * primitive rPLL on different chips has a completely different
    structure: for example in GW1N-1 it takes two cells, and in GW1NR-9C
    as many as four, despite this unification was carried out and
    different chips are processed by the same functions, but this led to
    the fact that you can not use the PLL chip GW1N-1 with the old
    apicula bases - will issue a warning and refuse to encode primitive.
    In other cases compatibility is supported.
  * Cosmetic change: the usage report shows the rPLL names without any
    service bels.
  * I use ctx->idf() on occasion, it's not a total redesign.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2023-01-26 20:26:05 +10:00
parent 9b5e5f124c
commit 2d45d57b32
7 changed files with 134 additions and 113 deletions

View File

@ -1098,23 +1098,6 @@ void Arch::addMuxBels(const DatabasePOD *db, int row, int col)
} }
} }
void Arch::add_plla_ports(BelsPOD const *bel, IdString belname, int row, int col)
{
IdString portname;
for (int pid : {ID_CLKIN, ID_CLKFB, ID_FBDSEL0, ID_FBDSEL1, ID_FBDSEL2, ID_FBDSEL3, ID_FBDSEL4, ID_FBDSEL5,
ID_IDSEL0, ID_IDSEL1, ID_IDSEL2, ID_IDSEL3, ID_IDSEL4, ID_IDSEL5, ID_ODSEL0, ID_ODSEL1,
ID_ODSEL2, ID_ODSEL3, ID_ODSEL4, ID_PSDA0, ID_PSDA1, ID_PSDA2, ID_PSDA3, ID_DUTYDA0,
ID_DUTYDA1, ID_DUTYDA2, ID_DUTYDA3, ID_FDLY0, ID_FDLY1, ID_FDLY2, ID_FDLY3}) {
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, pid)->src_id);
addBelInput(belname, IdString(pid), idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
}
for (int pid : {ID_LOCK, ID_CLKOUT, ID_CLKOUTP, ID_CLKOUTD, ID_CLKOUTD3}) {
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, pid)->src_id);
addBelOutput(belname, IdString(pid), idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
}
}
void Arch::add_pllvr_ports(DatabasePOD const *db, BelsPOD const *bel, IdString belname, int row, int col) void Arch::add_pllvr_ports(DatabasePOD const *db, BelsPOD const *bel, IdString belname, int row, int col)
{ {
IdString portname; IdString portname;
@ -1137,7 +1120,9 @@ void Arch::add_pllvr_ports(DatabasePOD const *db, BelsPOD const *bel, IdString b
int srccol = alias_src->src_col; int srccol = alias_src->src_col;
IdString srcid = IdString(alias_src->src_id); IdString srcid = IdString(alias_src->src_id);
wire = wireToGlobal(srcrow, srccol, db, srcid); wire = wireToGlobal(srcrow, srccol, db, srcid);
// addWire(wire, portname, srccol, srcrow); if (wires.count(wire) == 0) {
addWire(wire, srcid, srccol, srcrow);
}
} }
addBelInput(belname, IdString(pid), wire); addBelInput(belname, IdString(pid), wire);
} }
@ -1146,11 +1131,69 @@ void Arch::add_pllvr_ports(DatabasePOD const *db, BelsPOD const *bel, IdString b
addBelOutput(belname, IdString(pid), idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this))); addBelOutput(belname, IdString(pid), idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
} }
} }
void Arch::add_rpll_ports(DatabasePOD const *db, BelsPOD const *bel, IdString belname, int row, int col)
{
IdString portname;
for (int pid :
{ID_CLKIN, ID_CLKFB, ID_FBDSEL0, ID_FBDSEL1, ID_FBDSEL2, ID_FBDSEL3, ID_FBDSEL4, ID_FBDSEL5, ID_IDSEL0,
ID_IDSEL1, ID_IDSEL2, ID_IDSEL3, ID_IDSEL4, ID_IDSEL5, ID_ODSEL0, ID_ODSEL1, ID_ODSEL2, ID_ODSEL3,
ID_ODSEL4, ID_ODSEL5, ID_PSDA0, ID_PSDA1, ID_PSDA2, ID_PSDA3, ID_DUTYDA0, ID_DUTYDA1, ID_DUTYDA2,
ID_DUTYDA3, ID_FDLY0, ID_FDLY1, ID_FDLY2, ID_FDLY3, ID_RESET, ID_RESET_P}) {
const PairPOD *port = pairLookup(bel->ports.get(), bel->num_ports, pid);
// old base
if (port == nullptr) {
log_warning("When building nextpnr, obsolete old apicula bases were used. Probably not working properly "
"with PLL.\n");
return;
}
portname = IdString(port->src_id);
IdString wire = idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
if (wires.count(wire) == 0) {
GlobalAliasPOD alias;
alias.dest_col = col;
alias.dest_row = row;
alias.dest_id = portname.hash();
auto alias_src = genericLookup(db->aliases.get(), db->num_aliases, alias, aliasCompare);
NPNR_ASSERT(alias_src != nullptr);
int srcrow = alias_src->src_row;
int srccol = alias_src->src_col;
IdString srcid = IdString(alias_src->src_id);
wire = wireToGlobal(srcrow, srccol, db, srcid);
if (wires.count(wire) == 0) {
addWire(wire, srcid, srccol, srcrow);
}
}
addBelInput(belname, IdString(pid), wire);
}
for (int pid : {ID_LOCK, ID_CLKOUT, ID_CLKOUTP, ID_CLKOUTD, ID_CLKOUTD3}) {
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, pid)->src_id);
IdString wire = idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
if (wires.count(wire) == 0) {
GlobalAliasPOD alias;
alias.dest_col = col;
alias.dest_row = row;
alias.dest_id = portname.hash();
auto alias_src = genericLookup(db->aliases.get(), db->num_aliases, alias, aliasCompare);
NPNR_ASSERT(alias_src != nullptr);
int srcrow = alias_src->src_row;
int srccol = alias_src->src_col;
IdString srcid = IdString(alias_src->src_id);
wire = wireToGlobal(srcrow, srccol, db, srcid);
if (wires.count(wire) == 0) {
addWire(wire, srcid, srccol, srcrow);
}
}
addBelOutput(belname, IdString(pid), wire);
}
}
Arch::Arch(ArchArgs args) : args(args) Arch::Arch(ArchArgs args) : args(args)
{ {
family = args.family; family = args.family;
max_clock = 5; max_clock = 6;
if (family == "GW1NZ-1") { if (family == "GW1NZ-1") {
max_clock = 3; max_clock = 3;
} }
@ -1312,24 +1355,9 @@ Arch::Arch(ArchArgs args) : args(args)
add_pllvr_ports(db, bel, belname, row, col); add_pllvr_ports(db, bel, belname, row, col);
break; break;
case ID_RPLLA: case ID_RPLLA:
snprintf(buf, 32, "R%dC%d_RPLLA", row + 1, col + 1); belname = idf("R%dC%d_rPLL", row + 1, col + 1);
belname = id(buf); addBel(belname, id_rPLL, Loc(col, row, BelZ::pll_z), false);
addBel(belname, id_RPLLA, Loc(col, row, BelZ::pll_z), false); add_rpll_ports(db, bel, belname, row, col);
add_plla_ports(bel, belname, row, col);
break;
case ID_RPLLB:
snprintf(buf, 32, "R%dC%d_RPLLB", row + 1, col + 1);
belname = id(buf);
addBel(belname, id_RPLLB, Loc(col, row, BelZ::pll_z), false);
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_RESET)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_RESET, id(buf));
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_RESET_P)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_RESET_P, id(buf));
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_ODSEL5)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_ODSEL5, id(buf));
break; break;
case ID_BUFS7: case ID_BUFS7:
z++; /* fall-through*/ z++; /* fall-through*/
@ -2086,7 +2114,7 @@ void Arch::fix_pll_nets(Context *ctx)
{ {
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get(); CellInfo *ci = cell.second.get();
if (ci->type != id_RPLLA && ci->type != id_PLLVR) { if (ci->type != id_rPLL && ci->type != id_PLLVR) {
continue; continue;
} }
// *** CLKIN // *** CLKIN
@ -2101,7 +2129,7 @@ void Arch::fix_pll_nets(Context *ctx)
break; break;
} }
if (net_driven_by(ctx, net, is_RPLL_T_IN_iob, id_O) != nullptr) { if (net_driven_by(ctx, net, is_RPLL_T_IN_iob, id_O) != nullptr) {
if (ci->type == id_RPLLA) { if (ci->type == id_rPLL) {
ci->disconnectPort(id_CLKIN); ci->disconnectPort(id_CLKIN);
ci->setParam(id_INSEL, Property("CLKIN0")); ci->setParam(id_INSEL, Property("CLKIN0"));
break; break;

View File

@ -478,8 +478,8 @@ struct Arch : BaseArch<ArchRanges>
void pre_route(Context *ctx); void pre_route(Context *ctx);
void post_route(Context *ctx); void post_route(Context *ctx);
void auto_longwires(); void auto_longwires();
void add_plla_ports(BelsPOD const *bel, IdString belname, int row, int col);
void add_pllvr_ports(DatabasePOD const *db, BelsPOD const *bel, IdString belname, int row, int col); void add_pllvr_ports(DatabasePOD const *db, BelsPOD const *bel, IdString belname, int row, int col);
void add_rpll_ports(DatabasePOD const *db, BelsPOD const *bel, IdString belname, int row, int col);
void fix_pll_nets(Context *ctx); void fix_pll_nets(Context *ctx);
bool is_GCLKT_iob(const CellInfo *cell); bool is_GCLKT_iob(const CellInfo *cell);

View File

@ -79,16 +79,12 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
} else if (type == id_BUFS) { } else if (type == id_BUFS) {
new_cell->addInput(id_I); new_cell->addInput(id_I);
new_cell->addOutput(id_O); new_cell->addOutput(id_O);
} else if (type == id_RPLLB) { } else if (type == id_rPLL) {
new_cell->addInput(id_RESET);
new_cell->addInput(id_RESET_P);
new_cell->addInput(id_ODSEL5);
} else if (type == id_RPLLA) {
for (IdString iid : for (IdString iid :
{id_CLKIN, id_CLKFB, id_FBDSEL0, id_FBDSEL1, id_FBDSEL2, id_FBDSEL3, id_FBDSEL4, id_FBDSEL5, {id_CLKIN, id_CLKFB, id_FBDSEL0, id_FBDSEL1, id_FBDSEL2, id_FBDSEL3, id_FBDSEL4, id_FBDSEL5, id_IDSEL0,
id_IDSEL0, id_IDSEL1, id_IDSEL2, id_IDSEL3, id_IDSEL4, id_IDSEL5, id_ODSEL0, id_ODSEL1, id_IDSEL1, id_IDSEL2, id_IDSEL3, id_IDSEL4, id_IDSEL5, id_ODSEL0, id_ODSEL1, id_ODSEL2, id_ODSEL3,
id_ODSEL2, id_ODSEL3, id_ODSEL4, id_PSDA0, id_PSDA1, id_PSDA2, id_PSDA3, id_DUTYDA0, id_ODSEL4, id_ODSEL5, id_PSDA0, id_PSDA1, id_PSDA2, id_PSDA3, id_DUTYDA0, id_DUTYDA1, id_DUTYDA2,
id_DUTYDA1, id_DUTYDA2, id_DUTYDA3, id_FDLY0, id_FDLY1, id_FDLY2, id_FDLY3}) { id_DUTYDA3, id_FDLY0, id_FDLY1, id_FDLY2, id_FDLY3, id_RESET, id_RESET_P}) {
new_cell->addInput(iid); new_cell->addInput(iid);
} }
new_cell->addOutput(id_CLKOUT); new_cell->addOutput(id_CLKOUT);
@ -206,36 +202,6 @@ void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool<IdString> &to
} }
} }
void reconnect_rplla(Context *ctx, CellInfo *pll, CellInfo *plla)
{
pll->movePortTo(id_CLKIN, plla, id_CLKIN);
pll->movePortTo(id_CLKFB, plla, id_CLKFB);
for (int i = 0; i < 6; ++i) {
pll->movePortTo(ctx->idf("FBDSEL[%d]", i), plla, ctx->idf("FBDSEL%d", i));
pll->movePortTo(ctx->idf("IDSEL[%d]", i), plla, ctx->idf("IDSEL%d", i));
if (i < 5) {
pll->movePortTo(ctx->idf("ODSEL[%d]", i), plla, ctx->idf("ODSEL%d", i));
}
if (i < 4) {
pll->movePortTo(ctx->idf("PSDA[%d]", i), plla, ctx->idf("PSDA%d", i));
pll->movePortTo(ctx->idf("DUTYDA[%d]", i), plla, ctx->idf("DUTYDA%d", i));
pll->movePortTo(ctx->idf("FDLY[%d]", i), plla, ctx->idf("FDLY%d", i));
}
}
pll->movePortTo(id_CLKOUT, plla, id_CLKOUT);
pll->movePortTo(id_CLKOUTP, plla, id_CLKOUTP);
pll->movePortTo(id_CLKOUTD, plla, id_CLKOUTD);
pll->movePortTo(id_CLKOUTD3, plla, id_CLKOUTD3);
pll->movePortTo(id_LOCK, plla, id_LOCK);
}
void reconnect_rpllb(Context *ctx, CellInfo *pll, CellInfo *pllb)
{
pll->movePortTo(id_RESET, pllb, id_RESET);
pll->movePortTo(id_RESET_P, pllb, id_RESET_P);
pll->movePortTo(ctx->id("ODSEL[5]"), pllb, id_ODSEL5);
}
void reconnect_pllvr(Context *ctx, CellInfo *pll, CellInfo *new_pll) void reconnect_pllvr(Context *ctx, CellInfo *pll, CellInfo *new_pll)
{ {
pll->movePortTo(id_CLKIN, new_pll, id_CLKIN); pll->movePortTo(id_CLKIN, new_pll, id_CLKIN);
@ -260,6 +226,29 @@ void reconnect_pllvr(Context *ctx, CellInfo *pll, CellInfo *new_pll)
pll->movePortTo(id_LOCK, new_pll, id_LOCK); pll->movePortTo(id_LOCK, new_pll, id_LOCK);
} }
void reconnect_rpll(Context *ctx, CellInfo *pll, CellInfo *new_pll)
{
pll->movePortTo(id_CLKIN, new_pll, id_CLKIN);
pll->movePortTo(id_CLKFB, new_pll, id_CLKFB);
pll->movePortTo(id_RESET, new_pll, id_RESET);
pll->movePortTo(id_RESET_P, new_pll, id_RESET_P);
for (int i = 0; i < 6; ++i) {
pll->movePortTo(ctx->idf("FBDSEL[%d]", i), new_pll, ctx->idf("FBDSEL%d", i));
pll->movePortTo(ctx->idf("IDSEL[%d]", i), new_pll, ctx->idf("IDSEL%d", i));
pll->movePortTo(ctx->idf("ODSEL[%d]", i), new_pll, ctx->idf("ODSEL%d", i));
if (i < 4) {
pll->movePortTo(ctx->idf("PSDA[%d]", i), new_pll, ctx->idf("PSDA%d", i));
pll->movePortTo(ctx->idf("DUTYDA[%d]", i), new_pll, ctx->idf("DUTYDA%d", i));
pll->movePortTo(ctx->idf("FDLY[%d]", i), new_pll, ctx->idf("FDLY%d", i));
}
}
pll->movePortTo(id_CLKOUT, new_pll, id_CLKOUT);
pll->movePortTo(id_CLKOUTP, new_pll, id_CLKOUTP);
pll->movePortTo(id_CLKOUTD, new_pll, id_CLKOUTD);
pll->movePortTo(id_CLKOUTD3, new_pll, id_CLKOUTD3);
pll->movePortTo(id_LOCK, new_pll, id_LOCK);
}
void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw) void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw)
{ {
if (ramw->hierpath == IdString()) if (ramw->hierpath == IdString())

View File

@ -122,9 +122,8 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l
void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool<IdString> &todelete_cells); void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool<IdString> &todelete_cells);
// Reconnect PLL signals (B) // Reconnect PLL signals (B)
void reconnect_pllvr(Context *ctx, CellInfo *pll, CellInfo *pllb); void reconnect_pllvr(Context *ctx, CellInfo *pll, CellInfo *new_pll);
void reconnect_rplla(Context *ctx, CellInfo *pll, CellInfo *pllb); void reconnect_rpll(Context *ctx, CellInfo *pll, CellInfo *new_pll);
void reconnect_rpllb(Context *ctx, CellInfo *pll, CellInfo *pllb);
// Convert RAM16 to write port // Convert RAM16 to write port
void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw); void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw);

View File

@ -855,7 +855,6 @@ X(OSCF)
// PLLs // PLLs
X(rPLL) X(rPLL)
X(RPLLA) X(RPLLA)
X(RPLLB)
X(PLLVR) X(PLLVR)
// primitive attributes // primitive attributes

View File

@ -53,7 +53,7 @@ std::pair<WireId, BelId> GowinGlobalRouter::clock_src(Context *ctx, PortRef cons
} }
return std::make_pair(WireId(), BelId()); return std::make_pair(WireId(), BelId());
} }
if (driver.cell->type == id_RPLLA || driver.cell->type == id_PLLVR) { if (driver.cell->type == id_rPLL || driver.cell->type == id_PLLVR) {
if (driver.port == id_CLKOUT || driver.port == id_CLKOUTP || driver.port == id_CLKOUTD || if (driver.port == id_CLKOUT || driver.port == id_CLKOUTP || driver.port == id_CLKOUTD ||
driver.port == id_CLKOUTD3) { driver.port == id_CLKOUTD3) {
wire = bel.pins[driver.port].wire; wire = bel.pins[driver.port].wire;
@ -103,12 +103,20 @@ void GowinGlobalRouter::gather_clock_nets(Context *ctx, std::vector<globalnet_t>
IdString GowinGlobalRouter::route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, IdString GowinGlobalRouter::route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock,
pool<IdString> &used_pips, pool<IdString> &undo_wires) pool<IdString> &used_pips, pool<IdString> &undo_wires)
{ {
static std::vector<IdString> one_hop = {id_S111, id_S121, id_N111, id_N121, id_W111, id_W121, id_E111, id_E121}; static std::vector<IdString> one_hop_0 = {id_W111, id_W121, id_E111, id_E121};
char buf[40]; static std::vector<IdString> one_hop_4 = {id_S111, id_S121, id_N111, id_N121};
// uphill pips // uphill pips
for (auto const uphill : ctx->getPipsUphill(dstWire)) { for (auto const uphill : ctx->getPipsUphill(dstWire)) {
WireId srcWire = ctx->getPipSrcWire(uphill); WireId srcWire = ctx->getPipSrcWire(uphill);
if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) != one_hop.end()) { bool found;
if (clock < 4) {
found = find(one_hop_0.begin(), one_hop_0.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) !=
one_hop_0.end();
} else {
found = find(one_hop_4.begin(), one_hop_4.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) !=
one_hop_4.end();
}
if (found) {
// found one hop pip // found one hop pip
if (used_wires.count(srcWire)) { if (used_wires.count(srcWire)) {
if (used_wires[srcWire] != clock) { if (used_wires[srcWire] != clock) {
@ -117,8 +125,13 @@ IdString GowinGlobalRouter::route_to_non_clock_port(Context *ctx, WireId const d
} }
WireInfo wi = ctx->wire_info(srcWire); WireInfo wi = ctx->wire_info(srcWire);
std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1); std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1);
snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str()); IdString gb = ctx->idf("R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str());
IdString gb = ctx->id(buf); if (ctx->verbose) {
log_info(" 1-hop gb:%s\n", gb.c_str(ctx));
}
// sanity
NPNR_ASSERT(find(ctx->getPipsUphill(srcWire).begin(), ctx->getPipsUphill(srcWire).end(), gb) !=
ctx->getPipsUphill(srcWire).end());
auto up_pips = ctx->getPipsUphill(srcWire); auto up_pips = ctx->getPipsUphill(srcWire);
if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) { if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) {
if (!used_wires.count(srcWire)) { if (!used_wires.count(srcWire)) {
@ -154,11 +167,10 @@ void GowinGlobalRouter::route_net(Context *ctx, globalnet_t const &net)
char buf[30]; char buf[30];
PipId gb_pip_id; PipId gb_pip_id;
if (user.port == id_CLK) { if (user.port == id_CLK || user.port == id_CLKIN) {
WireInfo const wi = ctx->wire_info(dstWire); WireInfo const wi = ctx->wire_info(dstWire);
snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, net.clock, gb_pip_id =
ctx->wire_info(dstWire).type.c_str(ctx)); ctx->idf("R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, net.clock, ctx->wire_info(dstWire).type.c_str(ctx));
gb_pip_id = ctx->id(buf);
// sanity // sanity
NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) != NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) !=
ctx->getPipsUphill(dstWire).end()); ctx->getPipsUphill(dstWire).end());
@ -192,11 +204,10 @@ void GowinGlobalRouter::route_net(Context *ctx, globalnet_t const &net)
dstWire = ctx->getPipSrcWire(gb_pip_id); dstWire = ctx->getPipSrcWire(gb_pip_id);
WireInfo dstWireInfo = ctx->wire_info(dstWire); WireInfo dstWireInfo = ctx->wire_info(dstWire);
int branch_tap_idx = net.clock > 3 ? 1 : 0; int branch_tap_idx = net.clock > 3 ? 1 : 0;
snprintf(buf, sizeof(buf), "R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx, PipId gt_pip_id =
branch_tap_idx); ctx->idf("R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx, branch_tap_idx);
PipId gt_pip_id = ctx->id(buf);
if (ctx->verbose) { if (ctx->verbose) {
log_info(" GT Pip:%s\n", buf); log_info(" GT Pip:%s\n", gt_pip_id.c_str(ctx));
} }
// sanity // sanity
NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) != NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) !=
@ -251,12 +262,13 @@ void GowinGlobalRouter::route_net(Context *ctx, globalnet_t const &net)
for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) {
if (ctx->getPipSrcWire(uphill_pip) == net.clock_wire) { if (ctx->getPipSrcWire(uphill_pip) == net.clock_wire) {
src_pip_id = uphill_pip; src_pip_id = uphill_pip;
break;
} }
} }
NPNR_ASSERT(src_pip_id != PipId());
if (ctx->verbose) { if (ctx->verbose) {
log_info(" Src Pip:%s\n", src_pip_id.c_str(ctx)); log_info(" Src Pip:%s\n", src_pip_id.c_str(ctx));
} }
NPNR_ASSERT(src_pip_id != PipId());
// if already routed // if already routed
if (used_pips.count(src_pip_id)) { if (used_pips.count(src_pip_id)) {
if (ctx->verbose) { if (ctx->verbose) {

View File

@ -1034,7 +1034,7 @@ static void pack_plls(Context *ctx)
if (ctx->verbose) if (ctx->verbose)
log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx)); log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx));
if (is_pll(ctx, ci)) { if (is_pll(ctx, ci)) {
std::string parm_device = str_or_default(ci->params, id_DEVICE, "GW1N-1"); std::string parm_device = str_or_default(ci->params, id_DEVICE, ctx->device.c_str());
if (parm_device != ctx->device) { if (parm_device != ctx->device) {
log_error("Cell '%s': wrong PLL device:%s instead of %s\n", ctx->nameOf(ci), parm_device.c_str(), log_error("Cell '%s': wrong PLL device:%s instead of %s\n", ctx->nameOf(ci), parm_device.c_str(),
ctx->device.c_str()); ctx->device.c_str());
@ -1043,27 +1043,21 @@ static void pack_plls(Context *ctx)
switch (ci->type.hash()) { switch (ci->type.hash()) {
case ID_rPLL: { case ID_rPLL: {
if (parm_device == "GW1N-1" || parm_device == "GW1NZ-1") { if (parm_device == "GW1N-1" || parm_device == "GW1NZ-1" || parm_device == "GW1NR-9C") {
pll_disable_unused_ports(ctx, ci); pll_disable_unused_ports(ctx, ci);
// B half // A cell
std::unique_ptr<CellInfo> cell = create_generic_cell(ctx, id_RPLLB, ci->name.str(ctx) + "$rpllb"); std::unique_ptr<CellInfo> cell = create_generic_cell(ctx, id_rPLL, ci->name.str(ctx) + "$rpll");
reconnect_rpllb(ctx, ci, cell.get()); reconnect_rpll(ctx, ci, cell.get());
new_cells.push_back(std::move(cell)); new_cells.push_back(std::move(cell));
auto pllb_cell = new_cells.back().get(); auto pll_cell = new_cells.back().get();
// A half
cell = create_generic_cell(ctx, id_RPLLA, ci->name.str(ctx) + "$rplla");
reconnect_rplla(ctx, ci, cell.get());
new_cells.push_back(std::move(cell));
auto plla_cell = new_cells.back().get();
// need params for gowin_pack // need params for gowin_pack
for (auto &parm : ci->params) { for (auto &parm : ci->params) {
plla_cell->setParam(parm.first, parm.second); pll_cell->setParam(parm.first, parm.second);
pllb_cell->setParam(parm.first, parm.second);
} }
packed_cells.insert(ci->name); packed_cells.insert(ci->name);
} else { } else {
log_error("PLL isn't supported for %s\n", ctx->device.c_str()); log_error("rPLL isn't supported for %s\n", ctx->device.c_str());
} }
} break; } break;
case ID_PLLVR: { case ID_PLLVR: {
@ -1080,7 +1074,7 @@ static void pack_plls(Context *ctx)
} }
packed_cells.insert(ci->name); packed_cells.insert(ci->name);
} else { } else {
log_error("PLL isn't supported for %s\n", ctx->device.c_str()); log_error("PLLVR isn't supported for %s\n", ctx->device.c_str());
} }
} break; } break;
default: default: