From a84ded4793ce66b0f4854349c929afae334d1e56 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 10 Nov 2022 19:14:41 +1000 Subject: [PATCH 1/2] 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 --- gowin/arch.cc | 40 ++++++++++++++++++++++++++ gowin/arch.h | 4 ++- gowin/cells.cc | 64 +++++++++++++++++++++++++++++++++++++++++ gowin/cells.h | 4 +++ gowin/constids.inc | 44 ++++++++++++++++++++++++++++ gowin/pack.cc | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 227 insertions(+), 1 deletion(-) 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(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 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 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 &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 &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 packed_cells; + pool delete_nets; + + std::vector> 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 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(); From 9013b2de5020ea94fd34b04cbb255b4bad8cbfab Mon Sep 17 00:00:00 2001 From: YRabbit Date: Fri, 11 Nov 2022 09:19:16 +1000 Subject: [PATCH 2/2] gowin: use ctx->idf() a bit Replacing snprintf() with ctx->idf() in PLL commit, but not yet a complete overhaul. Signed-off-by: YRabbit --- gowin/arch.cc | 10 +++------- gowin/cells.cc | 48 ++++++++++++++---------------------------------- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 54e38c0f..e6eeced3 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -236,8 +236,7 @@ bool Arch::allocate_longwire(NetInfo *ni, int lw_idx) } // old driver -> bufs LW input net - snprintf(buf, sizeof(buf), "$PACKER_BUFS_%c", longwire + 'A'); - auto net = std::make_unique(id(buf)); + auto net = std::make_unique(idf("$PACKER_BUFS_%c", longwire + 'A')); NetInfo *bufs_net = net.get(); nets[net->name] = std::move(net); @@ -1074,20 +1073,17 @@ 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)); + 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); - snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); - addBelOutput(belname, IdString(pid), id(buf)); + addBelOutput(belname, IdString(pid), idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this))); } } diff --git a/gowin/cells.cc b/gowin/cells.cc index c1026bb5..a76ea1d8 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -197,35 +197,18 @@ 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); + 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); @@ -260,15 +243,12 @@ void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw) void sram_to_slice(Context *ctx, CellInfo *ram, CellInfo *slice, int index) { - char buf1[32]; if (slice->hierpath == IdString()) slice->hierpath = slice->hierpath; - snprintf(buf1, 32, "INIT_%d", index); - slice->params[id_INIT] = ram->params[ctx->id(buf1)]; + slice->params[id_INIT] = ram->params[ctx->idf("INIT_%d", index)]; - snprintf(buf1, 32, "DO[%d]", index); - ram->movePortTo(ctx->id(buf1), slice, id_F); + ram->movePortTo(ctx->idf("DO[%d]", index), slice, id_F); ram->copyPortTo(ctx->id("RAD[0]"), slice, id_A); ram->copyPortTo(ctx->id("RAD[1]"), slice, id_B);