aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYRabbit <rabbit@yrabbit.cyou>2023-03-22 17:15:17 +1000
committermyrtle <gatecat@ds0.me>2023-03-23 12:37:53 +0100
commit95ace0fade44633db7ea6d3d65702dc8a2d1d488 (patch)
treeb7afc4268d449e62ef5edb2fb7181654ed3b303c
parentb3c33bd0ab3cd123aa73e9cb46dea41baadf6dd5 (diff)
downloadnextpnr-95ace0fade44633db7ea6d3d65702dc8a2d1d488.tar.gz
nextpnr-95ace0fade44633db7ea6d3d65702dc8a2d1d488.tar.bz2
nextpnr-95ace0fade44633db7ea6d3d65702dc8a2d1d488.zip
gowin: Add support for OSER primitives
* placement of OSER4, OVIDEO, OSER8 and SER10 primitives is supported; * primitives are implemented for the GW1N-1, GW1NZ-1, GW1NSR-4C, GW1NR-9, GW1NR-9C chips; * the initial support for special HCLK clock wires is implemented to the extent necessary for OSER primitives to function; * output to both regular IO and TLVDS_OBUF is supported; * tricks required for IOLOGIC to work on one side of the -9 and -9C chips are taken into account; * various edits, such as using idf() instead of the local buffer. Compatible with old apicula bases. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
-rw-r--r--gowin/arch.cc159
-rw-r--r--gowin/arch.h29
-rw-r--r--gowin/cells.cc4
-rw-r--r--gowin/cells.h2
-rw-r--r--gowin/constids.inc25
-rw-r--r--gowin/pack.cc159
6 files changed, 338 insertions, 40 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc
index 042e7a62..53bf890f 100644
--- a/gowin/arch.cc
+++ b/gowin/arch.cc
@@ -384,9 +384,6 @@ void Arch::addPip(IdString name, IdString type, IdString srcWire, IdString dstWi
pi.delay = delay;
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(dstWire).uphill.push_back(name);
pip_ids.push_back(name);
@@ -597,19 +594,33 @@ void Arch::addCellTimingClockToOut(IdString cell, IdString port, IdString clock,
// ---------------------------------------------------------------
+IdString Arch::apply_local_aliases(int row, int col, const DatabasePOD *db, IdString &wire)
+{
+ const TilePOD *tile = db->grid[row * db->cols + col].get();
+ auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, wire.index);
+ IdString res_wire = IdString();
+ if (local_alias != nullptr) {
+ wire = IdString(local_alias->src_id);
+ res_wire = idf("R%dC%d_%s", row + 1, col + 1, wire.c_str(this));
+ }
+ return res_wire;
+}
+
// TODO represent wires more intelligently.
IdString Arch::wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString &wire)
{
const std::string &wirename = wire.str(this);
- char buf[32];
if (wirename == "VCC" || wirename == "VSS") {
row = 0;
col = 0;
return wire;
}
if (!isdigit(wirename[1]) || !isdigit(wirename[2]) || !isdigit(wirename[3])) {
- snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, wirename.c_str());
- return id(buf);
+ IdString res_wire = apply_local_aliases(row, col, db, wire);
+ if (res_wire == IdString()) {
+ return idf("R%dC%d_%s", row + 1, col + 1, wirename.c_str());
+ }
+ return res_wire;
}
char direction = wirename[0];
int num = std::stoi(wirename.substr(1, 2));
@@ -628,8 +639,7 @@ IdString Arch::wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString
col += segment;
break;
default:
- snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, wirename.c_str());
- return id(buf);
+ return idf("R%dC%d_%s", row + 1, col + 1, wirename.c_str());
break;
}
// wires wrap around the edges
@@ -647,18 +657,13 @@ IdString Arch::wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString
col = 2 * db->cols - 1 - col;
direction = 'E';
}
- snprintf(buf, 32, "%c%d0", direction, num);
- wire = id(buf);
+ wire = idf("%c%d0", direction, num);
// local aliases
- const TilePOD *tile = db->grid[row * db->cols + col].get();
- auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, wire.index);
- if (local_alias != nullptr) {
- wire = IdString(local_alias->src_id);
- snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, wire.c_str(this));
- } else {
- snprintf(buf, 32, "R%dC%d_%c%d", row + 1, col + 1, direction, num);
+ IdString res_wire = apply_local_aliases(row, col, db, wire);
+ if (res_wire == IdString()) {
+ res_wire = idf("R%dC%d_%c%d", row + 1, col + 1, direction, num);
}
- return id(buf);
+ return res_wire;
}
const PairPOD *pairLookup(const PairPOD *list, const size_t len, const int dest)
@@ -1581,6 +1586,10 @@ Arch::Arch(ArchArgs args) : args(args)
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_XXX_VSS1, id(buf));
}
+ if (!z && device_id == id("GW1NR-9C")) {
+ addBelInput(belname, id_XXX_0, idf("R%dC%d_C6", row + 1, col + 1));
+ addBelInput(belname, id_XXX_1, idf("R%dC%d_D6", row + 1, col + 1));
+ }
} break;
// Simplified IO
case ID_IOBJS:
@@ -1627,7 +1636,7 @@ Arch::Arch(ArchArgs args) : args(args)
case ID_ODDRA: {
snprintf(buf, 32, "R%dC%d_ODDR%s%c", row + 1, col + 1, oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0));
belname = id(buf);
- addBel(belname, id_ODDR, Loc(col, row, BelZ::iologic_0_z + z), false);
+ addBel(belname, id_ODDR, Loc(col, row, BelZ::oddr_0_z + z), false);
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_D0)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
@@ -1681,6 +1690,64 @@ Arch::Arch(ArchArgs args) : args(args)
addWire(q1_name, id_q1, row, col);
addBelOutput(belname, id_Q1, q1_name);
} break;
+ case ID_IOLOGICB:
+ z++; /* fall-through*/
+ case ID_IOLOGICA: {
+ belname = idf("R%dC%d_IOLOGIC%c", row + 1, col + 1, 'A' + z);
+ addBel(belname, id_IOLOGIC, Loc(col, row, BelZ::iologic_z + z), false);
+
+ for (int i = 0; i < 10; ++i) {
+ if (i < 4) {
+ // TX
+ IdString const tx[] = {id_TX0, id_TX1, id_TX2, id_TX3};
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, tx[i].hash())->src_id);
+ addBelInput(belname, tx[i], idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
+ }
+ // D
+ IdString const d[] = {id_D0, id_D1, id_D2, id_D3, id_D4, id_D5, id_D6, id_D7, id_D8, id_D9};
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, d[i].hash())->src_id);
+ addBelInput(belname, d[i], idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
+ }
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_PCLK)->src_id);
+ addBelInput(belname, id_PCLK, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
+ auto fclk = pairLookup(bel->ports.get(), bel->num_ports, ID_FCLK);
+ // XXX as long as there is no special processing of the pins
+ if (fclk != nullptr) {
+ portname = IdString(fclk->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);
+ if (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);
+ if (wires.count(wire) == 0) {
+ addWire(wire, srcid, srccol, srcrow);
+ }
+ addBelInput(belname, id_FCLK, wire);
+ }
+ // XXX here we are creating an
+ // IOLOGIC with a missing FCLK input. This is so
+ // because bels with the same type can be placed in
+ // on the chip where there is no pin, so no
+ // IOLOGIC makes sense. But since each type is
+ // described only once in the database we can't really
+ // mark these special bel somehow.
+ // By creating an IOLOGIC without an FCLK input we
+ // create a routing error later, so that "bad"
+ // locations are handled.
+ } else {
+ addBelInput(belname, id_FCLK, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
+ }
+ }
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_RESET)->src_id);
+ addBelInput(belname, id_RESET, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
+ } break;
default:
break;
}
@@ -2245,6 +2312,54 @@ void Arch::fix_pll_nets(Context *ctx)
}
}
+// mark with hclk is used
+void Arch::mark_used_hclk(Context *ctx)
+{
+ for (auto &cell : ctx->cells) {
+ CellInfo *ci = cell.second.get();
+ if (ci->type != id_IOLOGIC) {
+ continue;
+ }
+ if (ci->attrs.count(id_IOLOGIC_FCLK)) {
+ continue;
+ }
+ // if it's an aux cell
+ if (ci->attrs.count(id_IOLOGIC_MASTER_CELL)) {
+ continue;
+ }
+ ci->setAttr(id_IOLOGIC_FCLK, Property("UNKNOWN"));
+
+ // *** FCLK
+ if (port_used(ci, id_FCLK)) {
+ NetInfo const *net = ci->getPort(id_FCLK);
+ for (auto const &user : net->users) {
+ if (user.cell != ci) {
+ continue;
+ }
+ if (user.port != id_FCLK) {
+ continue;
+ }
+ WireId dstWire = ctx->getNetinfoSinkWire(net, user, 0);
+ if (ctx->verbose) {
+ log_info(" Cell:%s, port:%s, wire:%s\n", user.cell->name.c_str(this), user.port.c_str(this),
+ dstWire.c_str(this));
+ }
+ for (PipId const &pip : getPipsUphill(dstWire)) {
+ if (!checkPipAvail(pip)) {
+ WireId src_wire = getPipSrcWire(pip);
+ ci->setAttr(id_IOLOGIC_FCLK, Property(wire_info(src_wire).type.str(this)));
+ if (ci->attrs.count(id_IOLOGIC_AUX_CELL)) {
+ IdString aux_cell_name = ctx->id(ci->attrs[id_IOLOGIC_AUX_CELL].as_string());
+ ctx->cells[aux_cell_name]->setAttr(id_IOLOGIC_FCLK,
+ Property(wire_info(src_wire).type.str(this)));
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
void Arch::pre_route(Context *ctx)
{
fix_pll_nets(ctx);
@@ -2253,7 +2368,11 @@ void Arch::pre_route(Context *ctx)
}
}
-void Arch::post_route(Context *ctx) { fix_longwire_bels(); }
+void Arch::post_route(Context *ctx)
+{
+ fix_longwire_bels();
+ mark_used_hclk(ctx);
+}
bool Arch::route()
{
diff --git a/gowin/arch.h b/gowin/arch.h
index 822cdfc1..595c82a0 100644
--- a/gowin/arch.h
+++ b/gowin/arch.h
@@ -401,6 +401,11 @@ struct Arch : BaseArch<ArchRanges>
PortType getBelPinType(BelId bel, IdString pin) const override;
std::vector<IdString> getBelPins(BelId bel) const override;
std::array<IdString, 1> getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override;
+ // Placement validity checks
+ virtual bool isValidBelForCellType(IdString cell_type, BelId bel) const override
+ {
+ return cell_type == id_DUMMY_CELL || cell_type == this->getBelType(bel);
+ }
WireId getWireByName(IdStringList name) const override;
IdStringList getWireName(WireId wire) const override;
@@ -490,6 +495,9 @@ struct Arch : BaseArch<ArchRanges>
bool is_GCLKT_iob(const CellInfo *cell);
void bind_pll_to_bel(CellInfo *ci, PLL loc);
+ void mark_used_hclk(Context *ctx);
+ IdString apply_local_aliases(int row, int col, const DatabasePOD *db, IdString &wire);
+
GowinGlobalRouter globals_router;
void mark_gowin_globals(Context *ctx);
void route_gowin_globals(Context *ctx);
@@ -524,16 +532,17 @@ struct Arch : BaseArch<ArchRanges>
namespace BelZ {
enum
{
- mux_0_z = 10, // start Z for the MUX2LUT5 bels
- iologic_0_z = 20, // start Z for the IOLOGIC bels
- lutram_0_z = 30, // start Z for the IOLOGIC bels
- vcc_0_z = 277, // virtual VCC bel Z
- 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
- pll_z = 289, // PLL
- pllvr_z = 290, // PLLVR
- free_z = 291 // Must be the last, one can use z starting from this value, adjust accordingly.
+ mux_0_z = 10, // start Z for the MUX2LUT5 bels
+ oddr_0_z = 20, // XXX start Z for the ODDR bels
+ lutram_0_z = 30, // start Z for the LUTRAM bels
+ vcc_0_z = 277, // virtual VCC bel Z
+ 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
+ pll_z = 289, // PLL
+ pllvr_z = 290, // PLLVR
+ iologic_z = 291, // IOLOGIC
+ free_z = 293 // 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 c30a4706..436b4766 100644
--- a/gowin/cells.cc
+++ b/gowin/cells.cc
@@ -105,6 +105,10 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
new_cell->addOutput(id_CLKOUTD);
new_cell->addOutput(id_CLKOUTD3);
new_cell->addOutput(id_LOCK);
+ } else if (type == id_IOLOGIC) {
+ new_cell->addInput(id_PCLK);
+ new_cell->addInput(id_RESET);
+ } else if (type == id_DUMMY_CELL) {
} else {
log_error("unable to create generic cell of type %s\n", type.c_str(ctx));
}
diff --git a/gowin/cells.h b/gowin/cells.h
index 7aba4805..4cabe086 100644
--- a/gowin/cells.h
+++ b/gowin/cells.h
@@ -105,7 +105,7 @@ inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type
inline bool is_sram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_RAM16SDP4; }
-inline bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type.index == ID_IOB); }
+inline bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type == id_IOB || cell->type == id_IOBS); }
// Convert a LUT primitive to (part of) an GENERIC_SLICE, swapping ports
// as needed. Set no_dff if a DFF is not being used, so that the output
diff --git a/gowin/constids.inc b/gowin/constids.inc
index b678cc77..afe3bc15 100644
--- a/gowin/constids.inc
+++ b/gowin/constids.inc
@@ -667,6 +667,9 @@ X(IOBH)
X(IOBI)
X(IOBJ)
+// misc
+X(DUMMY_CELL)
+
// simplified iobs
X(IOBS)
X(IOBAS)
@@ -757,10 +760,18 @@ X(LWO7)
// IOLOGIC
X(TX)
+X(TX0)
+X(TX1)
+X(TX2)
+X(TX3)
+X(FCLK)
+X(PCLK)
X(XXX_VSS)
X(XXX_VCC)
X(XXX_VSS0)
X(XXX_VSS1)
+X(XXX_0)
+X(XXX_1)
X(OBUF_TYPE)
X(SBUF)
X(DBUF)
@@ -770,6 +781,20 @@ X(ODDRA)
X(ODDRB)
X(ODDRCA)
X(ODDRCB)
+X(OSER4)
+X(OSER8)
+X(OSER10)
+X(OVIDEO)
+X(OSER16)
+X(IOLOGIC)
+X(IOLOGICA)
+X(IOLOGICB)
+X(IOLOGIC_TYPE)
+X(IOLOGIC_FCLK)
+X(IOLOGIC_MASTER_CELL)
+X(IOLOGIC_AUX_CELL)
+X(D8)
+X(D9)
// Wide LUTs
X(MUX2_LUT5)
diff --git a/gowin/pack.cc b/gowin/pack.cc
index fc870890..6e875a8c 100644
--- a/gowin/pack.cc
+++ b/gowin/pack.cc
@@ -812,8 +812,12 @@ static bool is_gowin_diff_iob(const Context *ctx, const CellInfo *cell)
static bool is_gowin_iologic(const Context *ctx, const CellInfo *cell)
{
switch (cell->type.index) {
- case ID_ODDR: /* fall-through*/
- case ID_ODDRC:
+ case ID_ODDR: /* fall-through*/
+ case ID_ODDRC: /* fall-through*/
+ case ID_OSER4: /* fall-through*/
+ case ID_OSER8: /* fall-through*/
+ case ID_OSER10: /* fall-through*/
+ case ID_OVIDEO:
return true;
default:
return false;
@@ -836,7 +840,7 @@ static void pack_iologic(Context *ctx)
if (is_gowin_iologic(ctx, ci)) {
CellInfo *q0_dst = nullptr;
CellInfo *q1_dst = nullptr;
- switch (ci->type.index) {
+ switch (ci->type.hash()) {
case ID_ODDRC: /* fall-through*/
case ID_ODDR: {
q0_dst = net_only_drives(ctx, ci->ports.at(id_Q0).net, is_iob, id_I);
@@ -851,7 +855,7 @@ static void pack_iologic(Context *ctx)
if (iob_bel != q0_dst->attrs.end()) {
// already know there to place, no need of any cluster stuff
Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(iob_bel->second.as_string()));
- loc.z += BelZ::iologic_0_z;
+ loc.z += BelZ::oddr_0_z;
ci->attrs[id_BEL] = ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx);
} else {
// make cluster from ODDR and OBUF
@@ -864,7 +868,7 @@ static void pack_iologic(Context *ctx)
q0_dst->cluster = ci->name;
q0_dst->constr_x = 0;
q0_dst->constr_y = 0;
- q0_dst->constr_z = -BelZ::iologic_0_z;
+ q0_dst->constr_z = -BelZ::oddr_0_z;
q0_dst->constr_abs_z = false;
}
@@ -891,13 +895,142 @@ static void pack_iologic(Context *ctx)
ci->addInput(id_XXX_VCC);
ci->connectPort(id_XXX_VCC, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
}
- if (ctx->gw1n9_quirk && iob_bel != q0_dst->attrs.end()) {
- bool have_XXX_VSS0 =
- ctx->bels[ctx->getBelByNameStr(iob_bel->second.as_string())].pins.count(id_XXX_VSS0);
- if (have_XXX_VSS0) {
+ if (iob_bel != q0_dst->attrs.end()) {
+ IdString io_bel_name = ctx->getBelByNameStr(iob_bel->second.as_string());
+ if (ctx->gw1n9_quirk && ctx->bels[io_bel_name].pins.count(id_XXX_VSS0)) {
q0_dst->disconnectPort(id_XXX_VSS0);
q0_dst->connectPort(id_XXX_VSS0, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
}
+ if (ctx->bels[io_bel_name].pins.count(id_XXX_1)) {
+ q0_dst->disconnectPort(id_XXX_1);
+ q0_dst->connectPort(id_XXX_1, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
+ }
+ }
+ } break;
+ case ID_OSER4: /* fall-through */
+ case ID_OSER8: /* fall-through */
+ case ID_OSER10: /* fall-through */
+ case ID_OVIDEO: {
+ IdString output = id_Q;
+ IdString output_1 = IdString();
+ if (ci->type == id_OSER4 || ci->type == id_OSER8) {
+ output = id_Q0;
+ output_1 = id_Q1;
+ }
+ q0_dst = net_only_drives(ctx, ci->ports.at(output).net, is_iob, id_I);
+ NPNR_ASSERT(q0_dst != nullptr);
+
+ auto iob_bel = q0_dst->attrs.find(id_BEL);
+ if (iob_bel == q0_dst->attrs.end()) {
+ log_info("No constraints for %s\n", ctx->nameOf(q0_dst));
+ NPNR_ASSERT_FALSE("The pins for IDES/OSER must be specified explicitly.");
+ }
+
+ Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(iob_bel->second.as_string()));
+ loc.z += BelZ::iologic_z;
+ ci->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ BelId bel = ctx->getBelByLocation(loc);
+ if (bel == BelId()) {
+ log_info("No bel for %s at %s\n", ctx->nameOf(ci), iob_bel->second.as_string().c_str());
+ NPNR_ASSERT_FALSE("Can't place IDES/OSER here");
+ }
+
+ std::string out_mode;
+ switch (ci->type.hash()) {
+ case ID_OSER4:
+ out_mode = "ODDRX2";
+ break;
+ case ID_OSER8:
+ out_mode = "ODDRX4";
+ break;
+ case ID_OSER10:
+ out_mode = "ODDRX5";
+ break;
+ case ID_OVIDEO:
+ out_mode = "VIDEORX";
+ break;
+ }
+ ci->setParam(ctx->id("OUTMODE"), out_mode);
+ bool use_diff_io = false;
+ if (q0_dst->attrs.count(id_DIFF_TYPE)) {
+ ci->setAttr(id_OBUF_TYPE, std::string("DBUF"));
+ use_diff_io = true;
+ } else {
+ ci->setAttr(id_OBUF_TYPE, std::string("SBUF"));
+ }
+
+ // disconnect Q output: it is wired internally
+ delete_nets.insert(ci->ports.at(output).net->name);
+ q0_dst->disconnectPort(id_I);
+ ci->disconnectPort(output);
+ bool have_XXX = ctx->bels[ctx->getBelByNameStr(iob_bel->second.as_string())].pins.count(id_XXX_1);
+ if (have_XXX) {
+ q0_dst->disconnectPort(id_XXX_1);
+ q0_dst->connectPort(id_XXX_1, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
+ }
+
+ // if Q1 is connected then disconnet it too
+ if (output_1 != IdString() && port_used(ci, output_1)) {
+ q1_dst = net_only_drives(ctx, ci->ports.at(output_1).net, is_iob, id_OEN);
+ if (q1_dst != nullptr) {
+ delete_nets.insert(ci->ports.at(output_1).net->name);
+ q0_dst->disconnectPort(id_OEN);
+ ci->disconnectPort(output_1);
+ ci->setAttr(id_IOBUF, 1);
+ }
+ } else {
+ // force OEN = 0 in order to enable output
+ // XXX check for IOBUF and TBUF
+ if (ci->type == id_OSER4 || ci->type == id_OSER8) {
+ int port_num = ci->type == id_OSER4 ? 2 : 4;
+ for (int i = 0; i < port_num; ++i) {
+ IdString port = ctx->idf("TX%d", i);
+ ci->disconnectPort(port);
+ ci->connectPort(port, ctx->nets[ctx->id("$PACKER_GND_NET")].get());
+ }
+ }
+ }
+
+ ci->setAttr(id_IOBUF, 0);
+ ci->setAttr(id_IOLOGIC_TYPE, ci->type.str(ctx));
+
+ if (ci->type == id_OSER4) {
+ ci->type = id_IOLOGIC;
+ // two OSER4 share FCLK, check it
+ Loc other_loc = loc;
+ other_loc.z = 1 - loc.z + 2 * BelZ::iologic_z;
+ BelId other_bel = ctx->getBelByLocation(other_loc);
+ CellInfo *other_cell = ctx->getBoundBelCell(other_bel);
+ if (other_cell != nullptr) {
+ NPNR_ASSERT(other_cell->type == id_OSER4);
+ if (ci->ports.at(id_FCLK).net != other_cell->ports.at(id_FCLK).net) {
+ log_error("%s and %s have differnet FCLK nets\n", ctx->nameOf(ci), ctx->nameOf(other_cell));
+ }
+ }
+ } else {
+ std::unique_ptr<CellInfo> dummy =
+ create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_DUMMY_IOLOGIC_IO");
+ loc.z = 1 - loc.z + BelZ::iologic_z;
+ if (!use_diff_io) {
+ dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ new_cells.push_back(std::move(dummy));
+ }
+ loc.z += BelZ::iologic_z;
+
+ std::unique_ptr<CellInfo> aux_cell =
+ create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX");
+ ci->setAttr(ctx->id("IOLOGIC_AUX_CELL"), ci->name.str(ctx) + "_AUX");
+ aux_cell->setParam(ctx->id("OUTMODE"), std::string("DDRENABLE"));
+ aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), ci->name.str(ctx));
+ aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ if (port_used(ci, id_RESET)) {
+ aux_cell->connectPort(id_RESET, ci->ports.at(id_RESET).net);
+ }
+ if (port_used(ci, id_PCLK)) {
+ aux_cell->connectPort(id_PCLK, ci->ports.at(id_PCLK).net);
+ }
+ new_cells.push_back(std::move(aux_cell));
+ ci->type = id_IOLOGIC;
}
} break;
default:
@@ -1139,6 +1272,7 @@ static void pack_io(Context *ctx)
IdString new_cell_type = id_IOB;
std::string constr_bel_name = std::string("");
bool have_xxx_port = false;
+ bool have_xxx0_port = false;
// check whether the given IO is limited to simplified IO cells
auto constr_bel = ci->attrs.find(id_BEL);
if (constr_bel != ci->attrs.end()) {
@@ -1157,6 +1291,7 @@ static void pack_io(Context *ctx)
if (ctx->gw1n9_quirk) {
have_xxx_port = ctx->bels[constr_bel].pins.count(id_XXX_VSS0) != 0;
}
+ have_xxx0_port = ctx->bels[constr_bel].pins.count(id_XXX_0) != 0;
}
}
@@ -1172,6 +1307,12 @@ static void pack_io(Context *ctx)
gwiob->addInput(id_XXX_VSS1);
gwiob->connectPort(id_XXX_VSS1, ctx->nets[ctx->id("$PACKER_GND_NET")].get());
}
+ if (have_xxx0_port && ci->type != id_IBUF) {
+ gwiob->addInput(id_XXX_0);
+ gwiob->connectPort(id_XXX_0, ctx->nets[ctx->id("$PACKER_GND_NET")].get());
+ gwiob->addInput(id_XXX_1);
+ gwiob->connectPort(id_XXX_1, ctx->nets[ctx->id("$PACKER_GND_NET")].get());
+ }
packed_cells.insert(ci->name);
if (iob != nullptr) {