From 95ace0fade44633db7ea6d3d65702dc8a2d1d488 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Wed, 22 Mar 2023 17:15:17 +1000 Subject: 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 --- gowin/arch.cc | 159 ++++++++++++++++++++++++++++++++++++++++++++++------- gowin/arch.h | 29 ++++++---- gowin/cells.cc | 4 ++ gowin/cells.h | 2 +- gowin/constids.inc | 25 +++++++++ gowin/pack.cc | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 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 PortType getBelPinType(BelId bel, IdString pin) const override; std::vector getBelPins(BelId bel) const override; std::array 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 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 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 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 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 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) { -- cgit v1.2.3