aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYRabbit <rabbit@yrabbit.cyou>2023-04-12 13:42:16 +1000
committermyrtle <gatecat@ds0.me>2023-04-12 14:35:17 +0200
commitfddacb3dc1dd4c977d48bd91fc73b8177e44a632 (patch)
tree2ab54633423e38a6f1651ed59997cfd079d15a68
parent7557d33dc670f0863c5a085e1d755ff693e05fbf (diff)
downloadnextpnr-fddacb3dc1dd4c977d48bd91fc73b8177e44a632.tar.gz
nextpnr-fddacb3dc1dd4c977d48bd91fc73b8177e44a632.tar.bz2
nextpnr-fddacb3dc1dd4c977d48bd91fc73b8177e44a632.zip
gowin: implement IDES16 and OSER16 primitives
These are very cumbersome primitives that take up two cells and consequently 4 IOLOGIC bels. The primitives are implemented for the chips that contain them and are supported by apicula GW1NSR-4C, GW1NR-9 and GW1NR-9C. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
-rw-r--r--gowin/arch.cc162
-rw-r--r--gowin/arch.h6
-rw-r--r--gowin/cells.cc1
-rw-r--r--gowin/constids.inc17
-rw-r--r--gowin/pack.cc287
5 files changed, 454 insertions, 19 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc
index ce893f1b..8033048f 100644
--- a/gowin/arch.cc
+++ b/gowin/arch.cc
@@ -1194,6 +1194,70 @@ void Arch::add_rpll_ports(DatabasePOD const *db, BelsPOD const *bel, IdString be
}
}
+static bool skip_aux_oser16(std::string device, int row, int col)
+{
+ if (device == "GW1NSR-4C") {
+ switch (col) {
+ case 2: /* fall-through*/
+ case 4: /* fall-through*/
+ case 6: /* fall-through*/
+ case 8: /* fall-through*/
+ case 9: /* fall-through*/
+ case 11: /* fall-through*/
+ case 13: /* fall-through*/
+ case 15: /* fall-through*/
+ case 17: /* fall-through*/
+ case 18: /* fall-through*/
+ case 20: /* fall-through*/
+ case 22: /* fall-through*/
+ case 24: /* fall-through*/
+ case 26: /* fall-through*/
+ case 27: /* fall-through*/
+ case 29: /* fall-through*/
+ case 31: /* fall-through*/
+ case 33: /* fall-through*/
+ case 35:
+ return true;
+ default:
+ break;
+ }
+ }
+ if (device == "GW1NR-9" || device == "GW1NR-9C") {
+ switch (col) {
+ case 2: /* fall-through*/
+ case 4: /* fall-through*/
+ case 6: /* fall-through*/
+ case 8: /* fall-through*/
+ case 9: /* fall-through*/
+ case 11: /* fall-through*/
+ case 13: /* fall-through*/
+ case 15: /* fall-through*/
+ case 17: /* fall-through*/
+ case 18: /* fall-through*/
+ case 19: /* fall-through*/
+ case 21: /* fall-through*/
+ case 23: /* fall-through*/
+ case 25: /* fall-through*/
+ case 27: /* fall-through*/
+ case 28: /* fall-through*/
+ case 29: /* fall-through*/
+ case 31: /* fall-through*/
+ case 33: /* fall-through*/
+ case 35: /* fall-through*/
+ case 36: /* fall-through*/
+ case 37: /* fall-through*/
+ case 39: /* fall-through*/
+ case 41: /* fall-through*/
+ case 43: /* fall-through*/
+ case 45:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
Arch::Arch(ArchArgs args) : args(args)
{
family = args.family;
@@ -1745,6 +1809,92 @@ Arch::Arch(ArchArgs args) : args(args)
}
}
} break;
+ case ID_OSER16: {
+ if (skip_aux_oser16(device, row, col)) {
+ break;
+ }
+ belname = idf("R%dC%d_OSER16", row + 1, col + 1);
+ addBel(belname, id_OSER16, Loc(col, row, BelZ::oser16_z), false);
+
+ IdString const oser16_in_ports[] = {id_RESET, id_PCLK, id_D0, id_D1, id_D2, id_D3,
+ id_D4, id_D5, id_D6, id_D7, id_D8, id_D9,
+ id_D10, id_D11, id_D12, id_D13, id_D14, id_D15};
+ for (IdString port : oser16_in_ports) {
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, port.hash())->src_id);
+ addBelInput(belname, port, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
+ }
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, id_Q0.hash())->src_id);
+ addBelOutput(belname, id_Q, 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);
+ }
+ } else {
+ addBelInput(belname, id_FCLK, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
+ }
+ }
+ } break;
+ case ID_IDES16: {
+ if (skip_aux_oser16(device, row, col)) {
+ break;
+ }
+ belname = idf("R%dC%d_IDES16", row + 1, col + 1);
+ addBel(belname, id_IDES16, Loc(col, row, BelZ::ides16_z), false);
+
+ IdString const ides16_in_ports[] = {id_RESET, id_PCLK, id_CALIB, id_D};
+ for (IdString port : ides16_in_ports) {
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, port.hash())->src_id);
+ addBelInput(belname, port, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
+ }
+ IdString const ides16_out_ports[] = {id_Q0, id_Q1, id_Q2, id_Q3, id_Q4, id_Q5, id_Q6, id_Q7,
+ id_Q8, id_Q9, id_Q10, id_Q11, id_Q12, id_Q13, id_Q14, id_Q15};
+ for (IdString port : ides16_out_ports) {
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, port.hash())->src_id);
+ addBelOutput(belname, port, 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);
+ }
+ } else {
+ addBelInput(belname, id_FCLK, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
+ }
+ }
+ } break;
default:
break;
}
@@ -2312,6 +2462,7 @@ void Arch::fix_pll_nets(Context *ctx)
// mark with hclk is used
void Arch::mark_used_hclk(Context *ctx)
{
+ pool<IdString> aux_cells;
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (ci->type != id_IOLOGIC) {
@@ -2322,6 +2473,7 @@ void Arch::mark_used_hclk(Context *ctx)
}
// if it's an aux cell
if (ci->attrs.count(id_IOLOGIC_MASTER_CELL)) {
+ aux_cells.insert(ci->name);
continue;
}
ci->setAttr(id_IOLOGIC_FCLK, Property("UNKNOWN"));
@@ -2345,16 +2497,16 @@ void Arch::mark_used_hclk(Context *ctx)
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)));
- }
}
}
}
}
}
+ for (auto acell : aux_cells) {
+ IdString main_cell = ctx->id(ctx->cells.at(acell)->attrs.at(id_IOLOGIC_MASTER_CELL).as_string());
+ Property &fclk = ctx->cells.at(main_cell)->attrs.at(id_IOLOGIC_FCLK);
+ ctx->cells.at(acell)->setAttr(id_IOLOGIC_FCLK, fclk);
+ }
}
void Arch::pre_route(Context *ctx)
diff --git a/gowin/arch.h b/gowin/arch.h
index 595c82a0..23da3661 100644
--- a/gowin/arch.h
+++ b/gowin/arch.h
@@ -532,6 +532,8 @@ struct Arch : BaseArch<ArchRanges>
namespace BelZ {
enum
{
+ ioba_z = 0, // IOBA
+ iobb_z = 1, // IOBB
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
@@ -542,7 +544,9 @@ enum
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.
+ oser16_z = 293, // OSER16
+ ides16_z = 294, // IDES16
+ free_z = 295 // 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 436b4766..c2d77e22 100644
--- a/gowin/cells.cc
+++ b/gowin/cells.cc
@@ -106,6 +106,7 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
new_cell->addOutput(id_CLKOUTD3);
new_cell->addOutput(id_LOCK);
} else if (type == id_IOLOGIC) {
+ new_cell->addInput(id_FCLK);
new_cell->addInput(id_PCLK);
new_cell->addInput(id_RESET);
} else if (type == id_DUMMY_CELL) {
diff --git a/gowin/constids.inc b/gowin/constids.inc
index 5bd2aa46..daedb8a6 100644
--- a/gowin/constids.inc
+++ b/gowin/constids.inc
@@ -767,8 +767,6 @@ X(TX3)
X(FCLK)
X(PCLK)
X(CALIB)
-X(Q8)
-X(Q9)
X(ODDR_ALWAYS_LOW)
X(ODDR_ALWAYS_HIGH)
X(GW9_ALWAYS_LOW0)
@@ -776,6 +774,7 @@ X(GW9_ALWAYS_LOW1)
X(GW9C_ALWAYS_LOW0)
X(GW9C_ALWAYS_LOW1)
X(OBUF_TYPE)
+X(IBUF_TYPE)
X(SBUF)
X(DBUF)
X(ODDR)
@@ -805,6 +804,20 @@ X(IOLOGIC_MASTER_CELL)
X(IOLOGIC_AUX_CELL)
X(D8)
X(D9)
+X(D10)
+X(D11)
+X(D12)
+X(D13)
+X(D14)
+X(D15)
+X(Q8)
+X(Q9)
+X(Q10)
+X(Q11)
+X(Q12)
+X(Q13)
+X(Q14)
+X(Q15)
// Wide LUTs
X(MUX2_LUT5)
diff --git a/gowin/pack.cc b/gowin/pack.cc
index d40913b6..2ff42e55 100644
--- a/gowin/pack.cc
+++ b/gowin/pack.cc
@@ -817,12 +817,14 @@ static bool is_gowin_iologic(const Context *ctx, const CellInfo *cell)
case ID_OSER4: /* fall-through*/
case ID_OSER8: /* fall-through*/
case ID_OSER10: /* fall-through*/
+ case ID_OSER16: /* fall-through*/
case ID_OVIDEO: /* fall-through*/
case ID_IDDR: /* fall-through*/
case ID_IDDRC: /* fall-through*/
case ID_IDES4: /* fall-through*/
case ID_IDES8: /* fall-through*/
case ID_IDES10: /* fall-through*/
+ case ID_IDES16: /* fall-through*/
case ID_IVIDEO:
return true;
default:
@@ -869,6 +871,21 @@ static void reconnect_ides_outs(CellInfo *ci)
}
}
+static void get_next_oser16_loc(std::string device, Loc &loc)
+{
+ if (device == "GW1NSR-4C") {
+ if (loc.y == 0) {
+ ++loc.x;
+ } else {
+ ++loc.y;
+ }
+ } else {
+ if (device == "GW1NR-9" || device == "GW1NR-9C") {
+ ++loc.x;
+ }
+ }
+}
+
// Pack IO logic
static void pack_iologic(Context *ctx)
{
@@ -1054,7 +1071,7 @@ static void pack_iologic(Context *ctx)
}
} else {
std::unique_ptr<CellInfo> dummy =
- create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_DUMMY_IOLOGIC_IO");
+ create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_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));
@@ -1064,7 +1081,6 @@ static void pack_iologic(Context *ctx)
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->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
aux_cell->setParam(ctx->id("OUTMODE"), std::string("DDRENABLE"));
aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), ci->name.str(ctx));
@@ -1099,8 +1115,8 @@ static void pack_iologic(Context *ctx)
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. Can't place IDES/OSER here\n", ctx->nameOf(ci),
- iob_bel->second.as_string().c_str());
+ log_error("No bel for %s at %s. Can't place IDES/OSER here\n", ctx->nameOf(ci),
+ iob_bel->second.as_string().c_str());
}
std::string in_mode;
switch (ci->type.hash()) {
@@ -1124,19 +1140,16 @@ static void pack_iologic(Context *ctx)
ci->setParam(ctx->id("INMODE"), in_mode);
bool use_diff_io = false;
if (d_src->attrs.count(id_DIFF_TYPE)) {
- ci->setAttr(id_OBUF_TYPE, std::string("DBUF"));
+ ci->setAttr(id_IBUF_TYPE, std::string("DBUF"));
use_diff_io = true;
} else {
- ci->setAttr(id_OBUF_TYPE, std::string("SBUF"));
+ ci->setAttr(id_IBUF_TYPE, std::string("SBUF"));
}
// disconnect D input: it is wired internally
delete_nets.insert(ci->getPort(id_D)->name);
d_src->disconnectPort(id_O);
ci->disconnectPort(id_D);
-
- // XXX place for -9 and -9C oddity
-
ci->setAttr(id_IOLOGIC_TYPE, ci->type.str(ctx));
reconnect_ides_outs(ci);
@@ -1158,7 +1171,7 @@ static void pack_iologic(Context *ctx)
}
} else {
std::unique_ptr<CellInfo> dummy =
- create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_DUMMY_IOLOGIC_IO");
+ create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_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));
@@ -1168,7 +1181,6 @@ static void pack_iologic(Context *ctx)
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->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
aux_cell->setParam(ctx->id("INMODE"), std::string("DDRENABLE"));
aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), ci->name.str(ctx));
@@ -1183,6 +1195,259 @@ static void pack_iologic(Context *ctx)
}
ci->type = id_IOLOGIC;
} break;
+ case ID_OSER16: {
+ IdString output = id_Q;
+ 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_error("No constraints for %s. The pins for IDES/OSER must be specified explicitly.\n",
+ ctx->nameOf(q0_dst));
+ }
+
+ Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(iob_bel->second.as_string()));
+ if (loc.z != BelZ::ioba_z) {
+ log_error("IDES16/OSER16 %s must be an A pin.\n", ctx->nameOf(ci));
+ }
+
+ loc.z = BelZ::oser16_z;
+ ci->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ BelId bel = ctx->getBelByLocation(loc);
+ if (bel == BelId()) {
+ log_error("No bel for %s at %s. Can't place IDES/OSER here\n", ctx->nameOf(ci),
+ iob_bel->second.as_string().c_str());
+ }
+
+ 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);
+ loc.z = BelZ::ioba_z;
+ if (ctx->bels.at(ctx->getBelByLocation(loc)).pins.count(id_GW9C_ALWAYS_LOW1)) {
+ q0_dst->disconnectPort(id_GW9C_ALWAYS_LOW1);
+ q0_dst->connectPort(id_GW9C_ALWAYS_LOW1, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
+ }
+ if (ctx->bels.at(ctx->getBelByLocation(loc)).pins.count(id_GW9_ALWAYS_LOW0)) {
+ q0_dst->disconnectPort(id_GW9_ALWAYS_LOW0);
+ q0_dst->connectPort(id_GW9_ALWAYS_LOW0, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
+ }
+
+ // make aux cells
+ std::unique_ptr<CellInfo> dummy =
+ create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO");
+ loc.z = BelZ::iobb_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;
+
+ // main iologic cell
+ std::string master_name = ci->name.str(ctx) + "_MAIN";
+
+ // aux cells
+ std::unique_ptr<CellInfo> aux_cell = create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX0");
+ aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("OSER16"));
+ aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), master_name);
+ aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ aux_cell->setParam(ctx->id("OUTMODE"), std::string("ODDRX8"));
+ aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
+ 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));
+
+ // aux iologic cells
+ loc.z = BelZ::iologic_z + 1;
+ aux_cell = create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX1");
+ aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
+ aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), master_name);
+ aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ aux_cell->setParam(ctx->id("OUTMODE"), std::string("DDRENABLE16"));
+ aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
+ 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));
+
+ // master
+ get_next_oser16_loc(ctx->device, loc);
+ loc.z = BelZ::iologic_z;
+ aux_cell = create_generic_cell(ctx, id_IOLOGIC, master_name);
+ aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
+ aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ aux_cell->setParam(ctx->id("OUTMODE"), std::string("DDRENABLE16"));
+ aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
+ 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);
+ }
+ ci->movePortTo(id_FCLK, aux_cell.get(), id_FCLK);
+ ci->movePortTo(id_D12, aux_cell.get(), id_D0);
+ ci->movePortTo(id_D13, aux_cell.get(), id_D1);
+ ci->movePortTo(id_D14, aux_cell.get(), id_D2);
+ ci->movePortTo(id_D15, aux_cell.get(), id_D3);
+ new_cells.push_back(std::move(aux_cell));
+
+ // bottom row is special and may need two additional ports
+ loc.z = BelZ::ioba_z;
+ if (ctx->getBelByLocation(loc) != BelId()) {
+ dummy = create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO0");
+ dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ new_cells.push_back(std::move(dummy));
+ }
+
+ // XXX Prohibit the use of 4th IO and IOLOGIC
+ loc.z = BelZ::iobb_z;
+ if (ctx->getBelByLocation(loc) != BelId()) {
+ dummy = create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO1");
+ dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ new_cells.push_back(std::move(dummy));
+ }
+ master_name = ci->name.str(ctx) + "_AUX2";
+ loc.z = BelZ::iologic_z + 1;
+ dummy = create_generic_cell(ctx, id_DUMMY_CELL, master_name);
+ dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ new_cells.push_back(std::move(dummy));
+ } break;
+ case ID_IDES16: {
+ CellInfo *d_src = net_driven_by(ctx, ci->getPort(id_D), is_iob, id_O);
+ NPNR_ASSERT(d_src != nullptr);
+
+ auto iob_bel = d_src->attrs.find(id_BEL);
+ if (iob_bel == d_src->attrs.end()) {
+ log_error("No constraints for %s. The pins for IDES/OSER must be specified explicitly.\n",
+ ctx->nameOf(d_src));
+ }
+ Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(iob_bel->second.as_string()));
+ if (loc.z != BelZ::ioba_z) {
+ log_error("IDES16/OSER16 %s must be an A pin.\n", ctx->nameOf(ci));
+ }
+
+ loc.z += BelZ::ides16_z;
+ ci->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ BelId bel = ctx->getBelByLocation(loc);
+ if (bel == BelId()) {
+ log_error("No bel for %s at %s. Can't place IDES/OSER here\n", ctx->nameOf(ci),
+ iob_bel->second.as_string().c_str());
+ }
+
+ bool use_diff_io = false;
+ if (d_src->attrs.count(id_DIFF_TYPE)) {
+ ci->setAttr(id_IBUF_TYPE, std::string("DBUF"));
+ use_diff_io = true;
+ } else {
+ ci->setAttr(id_IBUF_TYPE, std::string("SBUF"));
+ }
+ // disconnect D input: it is wired internally
+ delete_nets.insert(ci->getPort(id_D)->name);
+ d_src->disconnectPort(id_O);
+ ci->disconnectPort(id_D);
+ ci->setAttr(id_IOLOGIC_TYPE, ci->type.str(ctx));
+
+ // make aux cells
+ std::unique_ptr<CellInfo> dummy =
+ create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO");
+ loc.z = BelZ::iobb_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;
+
+ // main iologic cell
+ std::string master_name = ci->name.str(ctx) + "_MAIN";
+
+ // aux cells
+ std::unique_ptr<CellInfo> aux_cell = create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX0");
+ aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("IDES16"));
+ aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), master_name);
+ aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ aux_cell->setParam(ctx->id("INMODE"), std::string("IDDRX8"));
+ aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
+ 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));
+
+ // aux iologic cells
+ loc.z = BelZ::iologic_z + 1;
+ aux_cell = create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX1");
+ aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
+ aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), master_name);
+ aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ aux_cell->setParam(ctx->id("INMODE"), std::string("DDRENABLE16"));
+ aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
+ 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));
+
+ // master
+ get_next_oser16_loc(ctx->device, loc);
+ loc.z = BelZ::iologic_z;
+ aux_cell = create_generic_cell(ctx, id_IOLOGIC, master_name);
+ aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
+ aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ aux_cell->setParam(ctx->id("INMODE"), std::string("DDRENABLE16"));
+ aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
+ 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);
+ }
+ ci->movePortTo(id_FCLK, aux_cell.get(), id_FCLK);
+ ci->movePortTo(id_Q0, aux_cell.get(), id_Q6);
+ ci->movePortTo(id_Q1, aux_cell.get(), id_Q7);
+ ci->movePortTo(id_Q2, aux_cell.get(), id_Q8);
+ ci->movePortTo(id_Q3, aux_cell.get(), id_Q9);
+ new_cells.push_back(std::move(aux_cell));
+
+ // bottom row is special and may need two additional ports
+ loc.z = BelZ::ioba_z;
+ if (ctx->getBelByLocation(loc) != BelId()) {
+ dummy = create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO0");
+ dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ new_cells.push_back(std::move(dummy));
+ }
+
+ // XXX Prohibit the use of 4th IO and IOLOGIC
+ loc.z = BelZ::iobb_z;
+ if (ctx->getBelByLocation(loc) != BelId()) {
+ dummy = create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO1");
+ dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ new_cells.push_back(std::move(dummy));
+ }
+ master_name = ci->name.str(ctx) + "_AUX2";
+ loc.z = BelZ::iologic_z + 1;
+ dummy = create_generic_cell(ctx, id_DUMMY_CELL, master_name);
+ dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
+ new_cells.push_back(std::move(dummy));
+ } break;
default:
break;
}