aboutsummaryrefslogtreecommitdiffstats
path: root/gowin
diff options
context:
space:
mode:
authorYRabbit <rabbit@yrabbit.cyou>2022-11-10 19:14:41 +1000
committerYRabbit <rabbit@yrabbit.cyou>2022-11-10 19:14:41 +1000
commita84ded4793ce66b0f4854349c929afae334d1e56 (patch)
tree53451f52123c43d3c6cf0f49c73f46627263b21e /gowin
parentac17c36bec5b0ae8d57b66f825acb6f21f2ca323 (diff)
downloadnextpnr-a84ded4793ce66b0f4854349c929afae334d1e56.tar.gz
nextpnr-a84ded4793ce66b0f4854349c929afae334d1e56.tar.bz2
nextpnr-a84ded4793ce66b0f4854349c929afae334d1e56.zip
gowin: add initial PLL support
The rPLL primitive for the simplest chip (GW1N-1) in the family is processed. All parameters of the primitive are passed on to gowin_pack, and general-purpose wires are used for routing outputs of the primitive. Compatible with older versions of apicula, but in this case will refuse to place the new primitive. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
Diffstat (limited to 'gowin')
-rw-r--r--gowin/arch.cc40
-rw-r--r--gowin/arch.h4
-rw-r--r--gowin/cells.cc64
-rw-r--r--gowin/cells.h4
-rw-r--r--gowin/constids.inc44
-rw-r--r--gowin/pack.cc72
6 files changed, 227 insertions, 1 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc
index f00dfb0a..54e38c0f 100644
--- a/gowin/arch.cc
+++ b/gowin/arch.cc
@@ -1071,6 +1071,26 @@ 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;
+ char buf[40];
+
+ 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);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelInput(belname, IdString(pid), id(buf));
+ }
+ 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);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelOutput(belname, IdString(pid), id(buf));
+ }
+}
+
Arch::Arch(ArchArgs args) : args(args)
{
family = args.family;
@@ -1222,6 +1242,26 @@ Arch::Arch(ArchArgs args) : args(args)
bool dff = true;
bool oddrc = false;
switch (static_cast<ConstIds>(bel->type_id)) {
+ case ID_RPLLA: {
+ snprintf(buf, 32, "R%dC%d_RPLLA", row + 1, col + 1);
+ belname = id(buf);
+ addBel(belname, id_RPLLA, Loc(col, row, BelZ::pll_z), false);
+ 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;
case ID_BUFS7:
z++; /* fall-through*/
case ID_BUFS6:
diff --git a/gowin/arch.h b/gowin/arch.h
index a1fc25ae..994b6d98 100644
--- a/gowin/arch.h
+++ b/gowin/arch.h
@@ -468,6 +468,7 @@ struct Arch : BaseArch<ArchRanges>
void pre_pack(Context *ctx);
void post_pack(Context *ctx);
void auto_longwires();
+ void add_plla_ports(BelsPOD const *bel, IdString belname, int row, int col);
GowinGlobalRouter globals_router;
void mark_gowin_globals(Context *ctx);
@@ -507,7 +508,8 @@ enum
gnd_0_z = 278, // virtual VSS bel Z
osc_z = 280, // Z for the oscillator bels
bufs_0_z = 281, // Z for long wire buffer bel
- free_z = 289 // Must be the last, one can use z starting from this value, adjust accordingly.
+ pll_z = 289, // PLL
+ free_z = 290 // Must be the last, one can use z starting from this value, adjust accordingly.
};
}
diff --git a/gowin/cells.cc b/gowin/cells.cc
index fc5e7388..c1026bb5 100644
--- a/gowin/cells.cc
+++ b/gowin/cells.cc
@@ -79,6 +79,23 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
} else if (type == id_BUFS) {
new_cell->addInput(id_I);
new_cell->addOutput(id_O);
+ } else if (type == id_RPLLB) {
+ new_cell->addInput(id_RESET);
+ new_cell->addInput(id_RESET_P);
+ new_cell->addInput(id_ODSEL5);
+ } else if (type == id_RPLLA) {
+ for (IdString iid :
+ {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}) {
+ new_cell->addInput(iid);
+ }
+ new_cell->addOutput(id_CLKOUT);
+ new_cell->addOutput(id_CLKOUTP);
+ new_cell->addOutput(id_CLKOUTD);
+ new_cell->addOutput(id_CLKOUTD3);
+ new_cell->addOutput(id_LOCK);
} else {
log_error("unable to create generic cell of type %s\n", type.c_str(ctx));
}
@@ -176,6 +193,53 @@ 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);
+ pll->movePortTo(ctx->id("FBDSEL[0]"), plla, id_FBDSEL0);
+ pll->movePortTo(ctx->id("FBDSEL[1]"), plla, id_FBDSEL1);
+ pll->movePortTo(ctx->id("FBDSEL[2]"), plla, id_FBDSEL2);
+ pll->movePortTo(ctx->id("FBDSEL[3]"), plla, id_FBDSEL3);
+ pll->movePortTo(ctx->id("FBDSEL[4]"), plla, id_FBDSEL4);
+ pll->movePortTo(ctx->id("FBDSEL[5]"), plla, id_FBDSEL5);
+ pll->movePortTo(ctx->id("IDSEL[0]"), plla, id_IDSEL0);
+ pll->movePortTo(ctx->id("IDSEL[1]"), plla, id_IDSEL1);
+ pll->movePortTo(ctx->id("IDSEL[2]"), plla, id_IDSEL2);
+ pll->movePortTo(ctx->id("IDSEL[3]"), plla, id_IDSEL3);
+ pll->movePortTo(ctx->id("IDSEL[4]"), plla, id_IDSEL4);
+ pll->movePortTo(ctx->id("IDSEL[5]"), plla, id_IDSEL5);
+ pll->movePortTo(ctx->id("ODSEL[0]"), plla, id_ODSEL0);
+ pll->movePortTo(ctx->id("ODSEL[1]"), plla, id_ODSEL1);
+ pll->movePortTo(ctx->id("ODSEL[2]"), plla, id_ODSEL2);
+ pll->movePortTo(ctx->id("ODSEL[3]"), plla, id_ODSEL3);
+ pll->movePortTo(ctx->id("ODSEL[4]"), plla, id_ODSEL4);
+ pll->movePortTo(ctx->id("PSDA[0]"), plla, id_PSDA0);
+ pll->movePortTo(ctx->id("PSDA[1]"), plla, id_PSDA1);
+ pll->movePortTo(ctx->id("PSDA[2]"), plla, id_PSDA2);
+ pll->movePortTo(ctx->id("PSDA[3]"), plla, id_PSDA3);
+ pll->movePortTo(ctx->id("DUTYDA[0]"), plla, id_DUTYDA0);
+ pll->movePortTo(ctx->id("DUTYDA[1]"), plla, id_DUTYDA1);
+ pll->movePortTo(ctx->id("DUTYDA[2]"), plla, id_DUTYDA2);
+ pll->movePortTo(ctx->id("DUTYDA[3]"), plla, id_DUTYDA3);
+ pll->movePortTo(ctx->id("FDLY[0]"), plla, id_FDLY0);
+ pll->movePortTo(ctx->id("FDLY[1]"), plla, id_FDLY1);
+ pll->movePortTo(ctx->id("FDLY[2]"), plla, id_FDLY2);
+ pll->movePortTo(ctx->id("FDLY[3]"), plla, id_FDLY3);
+ 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 sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw)
{
if (ramw->hierpath == IdString())
diff --git a/gowin/cells.h b/gowin/cells.h
index 3a570f97..227206c8 100644
--- a/gowin/cells.h
+++ b/gowin/cells.h
@@ -119,6 +119,10 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l
// Convert a Gowin IO buffer to a IOB bel
void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool<IdString> &todelete_cells);
+// Reconnect rPLL signals (B)
+void reconnect_rplla(Context *ctx, CellInfo *pll, CellInfo *pllb);
+void reconnect_rpllb(Context *ctx, CellInfo *pll, CellInfo *pllb);
+
// Convert RAM16 to write port
void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw);
diff --git a/gowin/constids.inc b/gowin/constids.inc
index ec1137fc..59f364e7 100644
--- a/gowin/constids.inc
+++ b/gowin/constids.inc
@@ -851,6 +851,11 @@ X(OSCZ)
X(OSCH)
X(OSCF)
+// PLLs
+X(rPLL)
+X(RPLLA)
+X(RPLLB)
+
// primitive attributes
X(INIT)
X(FF_USED)
@@ -861,6 +866,7 @@ X(ENABLE_USED)
X(BEL)
X(DIFF)
X(DIFF_TYPE)
+X(DEVICE)
// ports
X(EN)
@@ -886,6 +892,44 @@ X(V)
X(G)
X(OSCOUT)
X(OSCEN)
+X(RESET_P)
+X(CLKFB)
+X(FBDSEL0)
+X(FBDSEL1)
+X(FBDSEL2)
+X(FBDSEL3)
+X(FBDSEL4)
+X(FBDSEL5)
+X(IDSEL0)
+X(IDSEL1)
+X(IDSEL2)
+X(IDSEL3)
+X(IDSEL4)
+X(IDSEL5)
+X(ODSEL0)
+X(ODSEL1)
+X(ODSEL2)
+X(ODSEL3)
+X(ODSEL4)
+X(ODSEL5)
+X(PSDA0)
+X(PSDA1)
+X(PSDA2)
+X(PSDA3)
+X(DUTYDA0)
+X(DUTYDA1)
+X(DUTYDA2)
+X(DUTYDA3)
+X(FDLY0)
+X(FDLY1)
+X(FDLY2)
+X(FDLY3)
+X(CLKIN)
+X(CLKOUT)
+X(CLKOUTP)
+X(CLKOUTD)
+X(CLKOUTD3)
+X(LOCK)
// timing
X(X0)
diff --git a/gowin/pack.cc b/gowin/pack.cc
index d978ac40..5260dda5 100644
--- a/gowin/pack.cc
+++ b/gowin/pack.cc
@@ -986,6 +986,77 @@ static void pack_diff_io(Context *ctx)
}
}
+static bool is_pll(const Context *ctx, const CellInfo *cell)
+{
+ switch (cell->type.hash()) {
+ case ID_rPLL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Pack PLLs
+static void pack_plls(Context *ctx)
+{
+ pool<IdString> packed_cells;
+ pool<IdString> delete_nets;
+
+ std::vector<std::unique_ptr<CellInfo>> new_cells;
+ log_info("Packing PLLs..\n");
+
+ for (auto &cell : ctx->cells) {
+ CellInfo *ci = cell.second.get();
+ if (ctx->verbose)
+ log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx));
+ if (is_pll(ctx, ci)) {
+ std::string parm_device = str_or_default(ci->params, id_DEVICE, "GW1N-1");
+ if (parm_device != ctx->device) {
+ log_error("Wrong PLL device:%s vs %s\n", parm_device.c_str(), ctx->device.c_str());
+ continue;
+ }
+
+ switch (ci->type.hash()) {
+ case ID_rPLL: {
+ if (parm_device == "GW1N-1") {
+ // B half
+ std::unique_ptr<CellInfo> cell = create_generic_cell(ctx, id_RPLLB, ci->name.str(ctx) + "$rpllb");
+ reconnect_rpllb(ctx, ci, cell.get());
+ new_cells.push_back(std::move(cell));
+ auto pllb_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
+ for (auto &parm : ci->params) {
+ plla_cell->setParam(parm.first, parm.second);
+ pllb_cell->setParam(parm.first, parm.second);
+ }
+ packed_cells.insert(ci->name);
+ } else {
+ log_error("PLL isn't supported for %s\n", ctx->device.c_str());
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ }
+
+ for (auto pcell : packed_cells) {
+ ctx->cells.erase(pcell);
+ }
+ for (auto dnet : delete_nets) {
+ ctx->nets.erase(dnet);
+ }
+ for (auto &ncell : new_cells) {
+ ctx->cells[ncell->name] = std::move(ncell);
+ }
+}
+
// Pack IO buffers
static void pack_io(Context *ctx)
{
@@ -1108,6 +1179,7 @@ bool Arch::pack()
pack_alus(ctx);
pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx);
+ pack_plls(ctx);
post_pack(ctx);
ctx->settings[id_pack] = 1;
ctx->assignArchInfo();