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>
This commit is contained in:
YRabbit 2023-04-12 13:42:16 +10:00 committed by myrtle
parent 7557d33dc6
commit fddacb3dc1
5 changed files with 454 additions and 19 deletions

View File

@ -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)

View File

@ -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.
};
}

View File

@ -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) {

View File

@ -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)

View File

@ -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;
}