Merge pull request #1077 from yrabbit/gw1nsr-4c_0
gowin: add a PLL primitive for the GW1NS-4 series
This commit is contained in:
commit
06eaffc57c
124
gowin/arch.cc
124
gowin/arch.cc
@ -384,6 +384,9 @@ void Arch::addPip(IdString name, IdString type, IdString srcWire, IdString dstWi
|
|||||||
pi.delay = delay;
|
pi.delay = delay;
|
||||||
pi.loc = loc;
|
pi.loc = loc;
|
||||||
|
|
||||||
|
// log_info("addpip %s->%s %.6f | %s name:%s\n" , srcWire.c_str(this), dstWire.c_str(this),
|
||||||
|
// getDelayNS(delay.maxDelay()), srcWire.c_str(this), name.c_str(this));
|
||||||
|
|
||||||
wire_info(srcWire).downhill.push_back(name);
|
wire_info(srcWire).downhill.push_back(name);
|
||||||
wire_info(dstWire).uphill.push_back(name);
|
wire_info(dstWire).uphill.push_back(name);
|
||||||
pip_ids.push_back(name);
|
pip_ids.push_back(name);
|
||||||
@ -1112,10 +1115,46 @@ void Arch::add_plla_ports(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;
|
||||||
|
|
||||||
|
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_VREN, 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}) {
|
||||||
|
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);
|
||||||
|
// addWire(wire, portname, 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);
|
||||||
|
addBelOutput(belname, IdString(pid), idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
Arch::Arch(ArchArgs args) : args(args)
|
Arch::Arch(ArchArgs args) : args(args)
|
||||||
{
|
{
|
||||||
family = args.family;
|
family = args.family;
|
||||||
|
|
||||||
|
max_clock = 5;
|
||||||
|
if (family == "GW1NZ-1") {
|
||||||
|
max_clock = 3;
|
||||||
|
}
|
||||||
|
|
||||||
// Load database
|
// Load database
|
||||||
std::string chipdb = stringf("gowin/chipdb-%s.bin", family.c_str());
|
std::string chipdb = stringf("gowin/chipdb-%s.bin", family.c_str());
|
||||||
auto db = reinterpret_cast<const DatabasePOD *>(get_chipdb(chipdb));
|
auto db = reinterpret_cast<const DatabasePOD *>(get_chipdb(chipdb));
|
||||||
@ -1267,12 +1306,17 @@ 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: {
|
case ID_PLLVR:
|
||||||
|
belname = idf("R%dC%d_PLLVR", row + 1, col + 1);
|
||||||
|
addBel(belname, id_PLLVR, Loc(col, row, BelZ::pllvr_z), false);
|
||||||
|
add_pllvr_ports(db, bel, belname, row, col);
|
||||||
|
break;
|
||||||
|
case ID_RPLLA:
|
||||||
snprintf(buf, 32, "R%dC%d_RPLLA", row + 1, col + 1);
|
snprintf(buf, 32, "R%dC%d_RPLLA", row + 1, col + 1);
|
||||||
belname = id(buf);
|
belname = id(buf);
|
||||||
addBel(belname, id_RPLLA, Loc(col, row, BelZ::pll_z), false);
|
addBel(belname, id_RPLLA, Loc(col, row, BelZ::pll_z), false);
|
||||||
add_plla_ports(bel, belname, row, col);
|
add_plla_ports(bel, belname, row, col);
|
||||||
} break;
|
break;
|
||||||
case ID_RPLLB:
|
case ID_RPLLB:
|
||||||
snprintf(buf, 32, "R%dC%d_RPLLB", row + 1, col + 1);
|
snprintf(buf, 32, "R%dC%d_RPLLB", row + 1, col + 1);
|
||||||
belname = id(buf);
|
belname = id(buf);
|
||||||
@ -2006,16 +2050,26 @@ static bool is_spec_iob(const Context *ctx, const CellInfo *cell, IdString pin_n
|
|||||||
return have_pin;
|
return have_pin;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_PLL_T_IN_iob(const Context *ctx, const CellInfo *cell)
|
static bool is_RPLL_T_IN_iob(const Context *ctx, const CellInfo *cell)
|
||||||
{
|
{
|
||||||
return is_spec_iob(ctx, cell, ctx->id("RPLL_T_IN"));
|
return is_spec_iob(ctx, cell, ctx->id("RPLL_T_IN"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_PLL_T_FB_iob(const Context *ctx, const CellInfo *cell)
|
static bool is_LPLL_T_IN_iob(const Context *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
return is_spec_iob(ctx, cell, ctx->id("LPLL_T_IN"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_RPLL_T_FB_iob(const Context *ctx, const CellInfo *cell)
|
||||||
{
|
{
|
||||||
return is_spec_iob(ctx, cell, ctx->id("RPLL_T_FB"));
|
return is_spec_iob(ctx, cell, ctx->id("RPLL_T_FB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_LPLL_T_FB_iob(const Context *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
return is_spec_iob(ctx, cell, ctx->id("LPLL_T_FB"));
|
||||||
|
}
|
||||||
|
|
||||||
bool Arch::is_GCLKT_iob(const CellInfo *cell)
|
bool Arch::is_GCLKT_iob(const CellInfo *cell)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 6; ++i) {
|
for (int i = 0; i < 6; ++i) {
|
||||||
@ -2032,7 +2086,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) {
|
if (ci->type != id_RPLLA && ci->type != id_PLLVR) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// *** CLKIN
|
// *** CLKIN
|
||||||
@ -2046,10 +2100,54 @@ void Arch::fix_pll_nets(Context *ctx)
|
|||||||
ci->setParam(id_INSEL, Property("UNKNOWN"));
|
ci->setParam(id_INSEL, Property("UNKNOWN"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (net_driven_by(ctx, net, is_PLL_T_IN_iob, id_O) != nullptr) {
|
if (net_driven_by(ctx, net, is_RPLL_T_IN_iob, id_O) != nullptr) {
|
||||||
ci->disconnectPort(id_CLKIN);
|
if (ci->type == id_RPLLA) {
|
||||||
ci->setParam(id_INSEL, Property("CLKIN0"));
|
ci->disconnectPort(id_CLKIN);
|
||||||
break;
|
ci->setParam(id_INSEL, Property("CLKIN0"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
BelId bel = id("R1C37_PLLVR");
|
||||||
|
if (ci->type == id_PLLVR) {
|
||||||
|
if (checkBelAvail(bel) || ci->belStrength != STRENGTH_LOCKED) {
|
||||||
|
if (ci->bel == bel) {
|
||||||
|
unbindBel(bel);
|
||||||
|
} else {
|
||||||
|
if (!checkBelAvail(bel) && ci->belStrength != STRENGTH_LOCKED) {
|
||||||
|
CellInfo *other_ci = getBoundBelCell(bel);
|
||||||
|
unbindBel(bel);
|
||||||
|
BelId our_bel = ci->bel;
|
||||||
|
unbindBel(our_bel);
|
||||||
|
bindBel(our_bel, other_ci, STRENGTH_LOCKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ci->disconnectPort(id_CLKIN);
|
||||||
|
ci->setParam(id_INSEL, Property("CLKIN0"));
|
||||||
|
bindBel(bel, ci, STRENGTH_LOCKED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (net_driven_by(ctx, net, is_LPLL_T_IN_iob, id_O) != nullptr) {
|
||||||
|
BelId bel = id("R1C28_PLLVR");
|
||||||
|
if (ci->type == id_PLLVR) {
|
||||||
|
if (checkBelAvail(bel) || ci->belStrength != STRENGTH_LOCKED) {
|
||||||
|
if (ci->bel == bel) {
|
||||||
|
unbindBel(bel);
|
||||||
|
} else {
|
||||||
|
if (!checkBelAvail(bel) && ci->belStrength != STRENGTH_LOCKED) {
|
||||||
|
CellInfo *other_ci = getBoundBelCell(bel);
|
||||||
|
unbindBel(bel);
|
||||||
|
BelId our_bel = ci->bel;
|
||||||
|
unbindBel(our_bel);
|
||||||
|
bindBel(our_bel, other_ci, STRENGTH_LOCKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ci->disconnectPort(id_CLKIN);
|
||||||
|
ci->setParam(id_INSEL, Property("CLKIN0"));
|
||||||
|
bindBel(bel, ci, STRENGTH_LOCKED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// XXX do special bels (HCLK etc)
|
// XXX do special bels (HCLK etc)
|
||||||
// This is general routing through CLK0 pip
|
// This is general routing through CLK0 pip
|
||||||
@ -2071,7 +2169,13 @@ void Arch::fix_pll_nets(Context *ctx)
|
|||||||
ci->setParam(id_FBSEL, Property("UNKNOWN"));
|
ci->setParam(id_FBSEL, Property("UNKNOWN"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (net_driven_by(ctx, net, is_PLL_T_FB_iob, id_O) != nullptr) {
|
// XXX Redesign for chips other than N-1 and NS-4
|
||||||
|
if (net_driven_by(ctx, net, is_RPLL_T_FB_iob, id_O) != nullptr) {
|
||||||
|
ci->disconnectPort(id_CLKFB);
|
||||||
|
ci->setParam(id_FBSEL, Property("CLKFB2"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (net_driven_by(ctx, net, is_LPLL_T_FB_iob, id_O) != nullptr) {
|
||||||
ci->disconnectPort(id_CLKFB);
|
ci->disconnectPort(id_CLKFB);
|
||||||
ci->setParam(id_FBSEL, Property("CLKFB2"));
|
ci->setParam(id_FBSEL, Property("CLKFB2"));
|
||||||
break;
|
break;
|
||||||
|
@ -479,6 +479,7 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
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_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 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);
|
||||||
|
|
||||||
@ -507,6 +508,9 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
|
|
||||||
// Permissible combinations of modes in a single slice
|
// Permissible combinations of modes in a single slice
|
||||||
std::map<const IdString, IdString> dff_comp_mode;
|
std::map<const IdString, IdString> dff_comp_mode;
|
||||||
|
|
||||||
|
// max global clock wires
|
||||||
|
int max_clock;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bels Z range
|
// Bels Z range
|
||||||
@ -521,7 +525,8 @@ enum
|
|||||||
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
|
||||||
pll_z = 289, // PLL
|
pll_z = 289, // PLL
|
||||||
free_z = 290 // Must be the last, one can use z starting from this value, adjust accordingly.
|
pllvr_z = 290, // PLLVR
|
||||||
|
free_z = 291 // Must be the last, one can use z starting from this value, adjust accordingly.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +96,19 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
|
|||||||
new_cell->addOutput(id_CLKOUTD);
|
new_cell->addOutput(id_CLKOUTD);
|
||||||
new_cell->addOutput(id_CLKOUTD3);
|
new_cell->addOutput(id_CLKOUTD3);
|
||||||
new_cell->addOutput(id_LOCK);
|
new_cell->addOutput(id_LOCK);
|
||||||
|
} else if (type == id_PLLVR) {
|
||||||
|
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_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, id_VREN}) {
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
@ -223,6 +236,30 @@ void reconnect_rpllb(Context *ctx, CellInfo *pll, CellInfo *pllb)
|
|||||||
pll->movePortTo(ctx->id("ODSEL[5]"), pllb, id_ODSEL5);
|
pll->movePortTo(ctx->id("ODSEL[5]"), pllb, id_ODSEL5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reconnect_pllvr(Context *ctx, CellInfo *pll, CellInfo *new_pll)
|
||||||
|
{
|
||||||
|
pll->movePortTo(id_CLKIN, new_pll, id_CLKIN);
|
||||||
|
pll->movePortTo(id_VREN, new_pll, id_VREN);
|
||||||
|
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())
|
||||||
|
@ -121,7 +121,8 @@ 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)
|
// Reconnect PLL signals (B)
|
||||||
|
void reconnect_pllvr(Context *ctx, CellInfo *pll, CellInfo *pllb);
|
||||||
void reconnect_rplla(Context *ctx, CellInfo *pll, CellInfo *pllb);
|
void reconnect_rplla(Context *ctx, CellInfo *pll, CellInfo *pllb);
|
||||||
void reconnect_rpllb(Context *ctx, CellInfo *pll, CellInfo *pllb);
|
void reconnect_rpllb(Context *ctx, CellInfo *pll, CellInfo *pllb);
|
||||||
|
|
||||||
|
@ -313,6 +313,7 @@ X(COUT2)
|
|||||||
X(COUT3)
|
X(COUT3)
|
||||||
X(COUT4)
|
X(COUT4)
|
||||||
X(COUT5)
|
X(COUT5)
|
||||||
|
X(VREN)
|
||||||
|
|
||||||
// wires
|
// wires
|
||||||
// SN
|
// SN
|
||||||
@ -855,6 +856,7 @@ X(OSCF)
|
|||||||
X(rPLL)
|
X(rPLL)
|
||||||
X(RPLLA)
|
X(RPLLA)
|
||||||
X(RPLLB)
|
X(RPLLB)
|
||||||
|
X(PLLVR)
|
||||||
|
|
||||||
// primitive attributes
|
// primitive attributes
|
||||||
X(INIT)
|
X(INIT)
|
||||||
|
@ -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) {
|
if (driver.cell->type == id_RPLLA || 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;
|
||||||
@ -294,7 +294,7 @@ void GowinGlobalRouter::mark_globals(Context *ctx)
|
|||||||
gather_clock_nets(ctx, clock_nets);
|
gather_clock_nets(ctx, clock_nets);
|
||||||
// XXX we need to use the list of indexes of clocks from the database
|
// XXX we need to use the list of indexes of clocks from the database
|
||||||
// use 6 clocks (XXX 3 for GW1NZ-1)
|
// use 6 clocks (XXX 3 for GW1NZ-1)
|
||||||
int max_clock = 3, cur_clock = -1;
|
int max_clock = ctx->max_clock, cur_clock = -1;
|
||||||
for (auto &net : clock_nets) {
|
for (auto &net : clock_nets) {
|
||||||
// XXX only IO clock for now
|
// XXX only IO clock for now
|
||||||
if (net.clock_wire == WireId()) {
|
if (net.clock_wire == WireId()) {
|
||||||
|
@ -989,11 +989,37 @@ static bool is_pll(const Context *ctx, const CellInfo *cell)
|
|||||||
switch (cell->type.hash()) {
|
switch (cell->type.hash()) {
|
||||||
case ID_rPLL:
|
case ID_rPLL:
|
||||||
return true;
|
return true;
|
||||||
|
case ID_PLLVR:
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pll_disable_unused_ports(Context *ctx, CellInfo *ci)
|
||||||
|
{
|
||||||
|
// Unused ports will be disabled during image generation. Here we add flags for such ports.
|
||||||
|
Property pr_enable("ENABLE"), pr_disable("DISABLE");
|
||||||
|
IdString ports[][2] = {
|
||||||
|
{id_CLKOUTP, id_CLKOUTPS}, {id_CLKOUTD, id_CLKOUTDIV}, {id_CLKOUTD3, id_CLKOUTDIV3}, {id_LOCK, id_FLOCK}};
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
ci->setParam(ports[i][1], port_used(ci, ports[i][0]) ? pr_enable : pr_disable);
|
||||||
|
}
|
||||||
|
// resets
|
||||||
|
NetInfo *net = ci->getPort(id_RESET);
|
||||||
|
ci->setParam(id_RSTEN, pr_enable);
|
||||||
|
if (!port_used(ci, id_RESET) || net->name == ctx->id("$PACKER_VCC_NET") ||
|
||||||
|
net->name == ctx->id("$PACKER_GND_NET")) {
|
||||||
|
ci->setParam(id_RSTEN, pr_disable);
|
||||||
|
}
|
||||||
|
ci->setParam(id_PWDEN, pr_enable);
|
||||||
|
net = ci->getPort(id_RESET_P);
|
||||||
|
if (!port_used(ci, id_RESET_P) || net->name == ctx->id("$PACKER_VCC_NET") ||
|
||||||
|
net->name == ctx->id("$PACKER_GND_NET")) {
|
||||||
|
ci->setParam(id_PWDEN, pr_disable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Pack PLLs
|
// Pack PLLs
|
||||||
static void pack_plls(Context *ctx)
|
static void pack_plls(Context *ctx)
|
||||||
{
|
{
|
||||||
@ -1010,36 +1036,15 @@ static void pack_plls(Context *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, "GW1N-1");
|
||||||
if (parm_device != ctx->device) {
|
if (parm_device != ctx->device) {
|
||||||
log_error("Wrong PLL device:%s vs %s\n", parm_device.c_str(), ctx->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());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
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") {
|
||||||
// Unused ports will be disabled during image generation. Here we add flags for such ports.
|
pll_disable_unused_ports(ctx, ci);
|
||||||
Property pr_enable("ENABLE"), pr_disable("DISABLE");
|
|
||||||
IdString ports[][2] = {{id_CLKOUTP, id_CLKOUTPS},
|
|
||||||
{id_CLKOUTD, id_CLKOUTDIV},
|
|
||||||
{id_CLKOUTD3, id_CLKOUTDIV3},
|
|
||||||
{id_LOCK, id_FLOCK}};
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
ci->setParam(ports[i][1], port_used(ci, ports[i][0]) ? pr_enable : pr_disable);
|
|
||||||
}
|
|
||||||
// resets
|
|
||||||
NetInfo *net = ci->getPort(id_RESET);
|
|
||||||
ci->setParam(id_RSTEN, pr_enable);
|
|
||||||
if (!port_used(ci, id_RESET) || net->name == ctx->id("$PACKER_VCC_NET") ||
|
|
||||||
net->name == ctx->id("$PACKER_GND_NET")) {
|
|
||||||
ci->setParam(id_RSTEN, pr_disable);
|
|
||||||
}
|
|
||||||
ci->setParam(id_PWDEN, pr_enable);
|
|
||||||
net = ci->getPort(id_RESET_P);
|
|
||||||
if (!port_used(ci, id_RESET_P) || net->name == ctx->id("$PACKER_VCC_NET") ||
|
|
||||||
net->name == ctx->id("$PACKER_GND_NET")) {
|
|
||||||
ci->setParam(id_PWDEN, pr_disable);
|
|
||||||
}
|
|
||||||
|
|
||||||
// B half
|
// B half
|
||||||
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_RPLLB, ci->name.str(ctx) + "$rpllb");
|
||||||
reconnect_rpllb(ctx, ci, cell.get());
|
reconnect_rpllb(ctx, ci, cell.get());
|
||||||
@ -1061,6 +1066,23 @@ static void pack_plls(Context *ctx)
|
|||||||
log_error("PLL isn't supported for %s\n", ctx->device.c_str());
|
log_error("PLL isn't supported for %s\n", ctx->device.c_str());
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
case ID_PLLVR: {
|
||||||
|
if (parm_device == "GW1NSR-4C") {
|
||||||
|
pll_disable_unused_ports(ctx, ci);
|
||||||
|
std::unique_ptr<CellInfo> cell = create_generic_cell(ctx, id_PLLVR, ci->name.str(ctx) + "$pllvr");
|
||||||
|
reconnect_pllvr(ctx, ci, cell.get());
|
||||||
|
new_cells.push_back(std::move(cell));
|
||||||
|
auto pll_cell = new_cells.back().get();
|
||||||
|
|
||||||
|
// need params for gowin_pack
|
||||||
|
for (auto &parm : ci->params) {
|
||||||
|
pll_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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user