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>
This commit is contained in:
YRabbit 2022-11-10 19:14:41 +10:00
parent ac17c36bec
commit a84ded4793
6 changed files with 227 additions and 1 deletions

View File

@ -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) Arch::Arch(ArchArgs args) : args(args)
{ {
family = args.family; family = args.family;
@ -1222,6 +1242,26 @@ Arch::Arch(ArchArgs args) : args(args)
bool dff = true; bool dff = true;
bool oddrc = false; bool oddrc = false;
switch (static_cast<ConstIds>(bel->type_id)) { 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: case ID_BUFS7:
z++; /* fall-through*/ z++; /* fall-through*/
case ID_BUFS6: case ID_BUFS6:

View File

@ -468,6 +468,7 @@ struct Arch : BaseArch<ArchRanges>
void pre_pack(Context *ctx); void pre_pack(Context *ctx);
void post_pack(Context *ctx); void post_pack(Context *ctx);
void auto_longwires(); void auto_longwires();
void add_plla_ports(BelsPOD const *bel, IdString belname, int row, int col);
GowinGlobalRouter globals_router; GowinGlobalRouter globals_router;
void mark_gowin_globals(Context *ctx); void mark_gowin_globals(Context *ctx);
@ -507,7 +508,8 @@ enum
gnd_0_z = 278, // virtual VSS bel Z gnd_0_z = 278, // virtual VSS bel Z
osc_z = 280, // Z for the oscillator bels osc_z = 280, // Z for the oscillator bels
bufs_0_z = 281, // Z for long wire buffer bel 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.
}; };
} }

View File

@ -79,6 +79,23 @@ 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) {
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 { } else {
log_error("unable to create generic cell of type %s\n", type.c_str(ctx)); 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) void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw)
{ {
if (ramw->hierpath == IdString()) if (ramw->hierpath == IdString())

View File

@ -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 // Convert a Gowin IO buffer to a IOB bel
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 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 // 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

@ -851,6 +851,11 @@ X(OSCZ)
X(OSCH) X(OSCH)
X(OSCF) X(OSCF)
// PLLs
X(rPLL)
X(RPLLA)
X(RPLLB)
// primitive attributes // primitive attributes
X(INIT) X(INIT)
X(FF_USED) X(FF_USED)
@ -861,6 +866,7 @@ X(ENABLE_USED)
X(BEL) X(BEL)
X(DIFF) X(DIFF)
X(DIFF_TYPE) X(DIFF_TYPE)
X(DEVICE)
// ports // ports
X(EN) X(EN)
@ -886,6 +892,44 @@ X(V)
X(G) X(G)
X(OSCOUT) X(OSCOUT)
X(OSCEN) 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 // timing
X(X0) X(X0)

View File

@ -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 // Pack IO buffers
static void pack_io(Context *ctx) static void pack_io(Context *ctx)
{ {
@ -1108,6 +1179,7 @@ bool Arch::pack()
pack_alus(ctx); pack_alus(ctx);
pack_lut_lutffs(ctx); pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx); pack_nonlut_ffs(ctx);
pack_plls(ctx);
post_pack(ctx); post_pack(ctx);
ctx->settings[id_pack] = 1; ctx->settings[id_pack] = 1;
ctx->assignArchInfo(); ctx->assignArchInfo();