From fddacb3dc1dd4c977d48bd91fc73b8177e44a632 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Wed, 12 Apr 2023 13:42:16 +1000 Subject: 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 --- gowin/arch.cc | 162 +++++++++++++++++++++++++++++- gowin/arch.h | 6 +- gowin/cells.cc | 1 + gowin/constids.inc | 17 +++- gowin/pack.cc | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 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 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 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 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 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 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 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 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 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 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 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 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; } -- cgit v1.2.3