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:
parent
ac17c36bec
commit
a84ded4793
@ -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:
|
||||||
|
@ -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.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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())
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user