diff --git a/nexus/constids.inc b/nexus/constids.inc index 98817899..5407cb23 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -514,3 +514,17 @@ X(SCLKIN) X(SCLKOUT) X(ECLK) X(CEIN) + +X(IDDRX1) +X(ODDRX1) +X(TXDATA0) +X(TXDATA1) +X(TSDATA0) +X(RXDATA0) +X(RXDATA1) +X(INDD) +X(DOUT) +X(TOUT) +X(Q0) +X(Q1) +X(SCLK) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 6715af47..aea57d6a 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -257,11 +257,11 @@ struct NexusFasmWriter if (pin_style & PINOPT_INV) { if (pin_mux == PINMUX_INV || pin_mux == PINMUX_0) write_bit(stringf("%sMUX.INV", ctx->nameOf(port.first))); - else if (pin_mux == PINMUX_SIG) + else if (pin_mux == PINMUX_SIG && !(pin_style & PINBIT_GATED)) write_bit(stringf("%sMUX.%s", ctx->nameOf(port.first), ctx->nameOf(port.first))); } // Pins that must be explictly enabled - if ((pin_style & PINBIT_GATED) && (pin_mux == PINMUX_SIG)) + if ((pin_style & PINBIT_GATED) && (pin_mux == PINMUX_SIG) && (port.second.net != nullptr)) write_bit(stringf("%sMUX.%s", ctx->nameOf(port.first), ctx->nameOf(port.first))); // Pins that must be explictly set to 1 rather than just left floating if ((pin_style & PINBIT_1) && (pin_mux == PINMUX_1)) @@ -560,6 +560,18 @@ struct NexusFasmWriter return (key.size() >= 3 && (key.compare(key.size() - 3, 3, "MUX") == 0)); } + // Write config for some kind of IOLOGIC cell + void write_iol(const CellInfo *cell) + { + BelId bel = cell->bel; + push_bel(bel); + write_enum(cell, "MODE"); + write_enum(cell, "IDDRX1_ODDRX1.OUTPUT"); + write_enum(cell, "GSR", "DISABLED"); + write_cell_muxes(cell); + pop(); + } + // Write config for some kind of DSP cell void write_dsp(const CellInfo *cell) { @@ -872,6 +884,8 @@ struct NexusFasmWriter write_lram(ci); else if (ci->type == id_DPHY_CORE) write_dphy(ci); + else if (ci->type == id_IOLOGIC || ci->type == id_SIOLOGIC) + write_iol(ci); blank(); } // Handle DCC route-throughs diff --git a/nexus/pack.cc b/nexus/pack.cc index 6b310d64..6385121b 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1147,6 +1147,92 @@ struct NexusPacker } } + void transform_iologic() + { + dict iol_rules; + iol_rules[id_IDDRX1].new_type = id_IOLOGIC; + iol_rules[id_IDDRX1].set_params.emplace_back(id_MODE, std::string("IDDRX1_ODDRX1")); + iol_rules[id_IDDRX1].port_xform[id_SCLK] = id_SCLKIN; + iol_rules[id_IDDRX1].port_xform[id_RST] = id_LSRIN; + iol_rules[id_IDDRX1].port_xform[id_D] = id_DI; + iol_rules[id_IDDRX1].port_xform[id_Q0] = id_RXDATA0; + iol_rules[id_IDDRX1].port_xform[id_Q1] = id_RXDATA1; + + iol_rules[id_ODDRX1].new_type = id_IOLOGIC; + iol_rules[id_ODDRX1].set_params.emplace_back(id_MODE, std::string("IDDRX1_ODDRX1")); + iol_rules[id_ODDRX1].set_params.emplace_back(ctx->id("IDDRX1_ODDRX1.OUTPUT"), std::string("ENABLED")); + iol_rules[id_ODDRX1].port_xform[id_SCLK] = id_SCLKOUT; + iol_rules[id_ODDRX1].port_xform[id_RST] = id_LSROUT; + iol_rules[id_ODDRX1].port_xform[id_Q] = id_DOUT; + iol_rules[id_ODDRX1].port_xform[id_D0] = id_TXDATA0; + iol_rules[id_ODDRX1].port_xform[id_D1] = id_TXDATA1; + + generic_xform(iol_rules, true); + } + + void merge_iol_cell(CellInfo *base, CellInfo *mergee) + { + for (auto ¶m : mergee->params) { + if (param.first == id_MODE && base->params.count(id_MODE) && param.second.as_string() == "IREG_OREG") + continue; // mixed tristate register and I/ODDR + base->params[param.first] = param.second; + } + for (auto &port : mergee->ports) { + replace_port(mergee, port.first, base, port.first); + } + ctx->cells.erase(mergee->name); + } + + void constrain_merge_iol() + { + dict> io_to_iol; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_IOLOGIC) + continue; + CellInfo *iob = nullptr; + NetInfo *di = get_net_or_empty(ci, id_DI); + if (di != nullptr && di->driver.cell != nullptr) + iob = di->driver.cell; + NetInfo *dout = get_net_or_empty(ci, id_DOUT); + if (dout != nullptr && dout->users.size() == 1) + iob = dout->users.at(0).cell; + NetInfo *tout = get_net_or_empty(ci, id_DOUT); + if (tout != nullptr && tout->users.size() == 1) + iob = tout->users.at(0).cell; + if (iob->type != id_SEIO18_CORE && iob->type != id_SEIO33_CORE && iob->type != id_DIFFIO18_CORE) + log_error("Failed to find associated IOB for IOLOGIC %s\n", ctx->nameOf(ci)); + io_to_iol[iob->name].push_back(ci); + } + for (auto &io_iol : io_to_iol) { + // Merge all IOLOGIC on an IO into a base IOLOGIC + CellInfo *iol = io_iol.second.at(0); + for (size_t i = 1; i < io_iol.second.size(); i++) + merge_iol_cell(iol, io_iol.second.at(i)); + // Constrain, and update type if appropriate + CellInfo *iob = ctx->cells.at(io_iol.first).get(); + if (iob->type == id_SEIO33_CORE) + iol->type = id_SIOLOGIC; + Loc iol_loc = ctx->getBelLocation(get_bel_attr(iob)); + if (iob->type == id_DIFFIO18_CORE) + iol_loc.z = 3; + else + iol_loc.z += 3; + BelId iol_bel = ctx->getBelByLocation(iol_loc); + NPNR_ASSERT(iol_bel != BelId()); + NPNR_ASSERT(ctx->getBelType(iol_bel) == iol->type); + log_info("Constraining IOLOGIC %s to bel %s\n", ctx->nameOf(iol), ctx->nameOfBel(iol_bel)); + iol->attrs[id_BEL] = ctx->getBelName(iol_bel).str(ctx); + } + } + + void pack_iologic() + { + log_info("Packing IOLOGIC...\n"); + transform_iologic(); + constrain_merge_iol(); + } + void pack_widefn() { std::vector widefns; @@ -1994,6 +2080,7 @@ struct NexusPacker void operator()() { pack_io(); + pack_iologic(); pack_dsps(); convert_prims(); pack_bram(); diff --git a/nexus/pins.cc b/nexus/pins.cc index e1012755..03bddd72 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -224,6 +224,10 @@ static const dict base_cell_pin_data = { {id_LSROUT, PINSTYLE_IOL_CELSR}, {id_CEIN, PINSTYLE_IOL_CELSR}, {id_CEOUT, PINSTYLE_IOL_CELSR}, + {id_TXDATA0, PINSTYLE_CIB}, + {id_TXDATA1, PINSTYLE_CIB}, + {id_TSDATA0, PINSTYLE_CIB}, + }}, {id_IOLOGIC, { @@ -233,6 +237,9 @@ static const dict base_cell_pin_data = { {id_LSROUT, PINSTYLE_IOL_CELSR}, {id_CEIN, PINSTYLE_IOL_CELSR}, {id_CEOUT, PINSTYLE_IOL_CELSR}, + {id_TXDATA0, PINSTYLE_CIB}, + {id_TXDATA1, PINSTYLE_CIB}, + {id_TSDATA0, PINSTYLE_CIB}, }}}; } // namespace