diff --git a/common/design_utils.cc b/common/design_utils.cc index 10212a03..dbdde245 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -111,6 +111,7 @@ void disconnect_port(const Context *ctx, CellInfo *cell, IdString port_name) port.net->users.end()); if (port.net->driver.cell == cell && port.net->driver.port == port_name) port.net->driver.cell = nullptr; + port.net = nullptr; } } diff --git a/common/router1.cc b/common/router1.cc index 02c817c7..d89f3ef2 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -497,7 +497,8 @@ struct Router1 } qw.randtag = ctx->rng(); - queue.push(qw); + if (src_wire != dst_wire) + queue.push(qw); visited[qw.wire] = qw; } diff --git a/common/timing.cc b/common/timing.cc index 4e84fffe..4a2122a5 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -175,12 +175,23 @@ struct Timing // Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and // the current output port, increment fanin counter + int found = 0; for (auto i : input_ports) { DelayInfo comb_delay; bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); - if (is_path) + if (is_path) { port_fanin[o]++; + found++; + } } + if (!found && portClass == TMG_COMB_OUTPUT) { + // log_warning("fake root %s\n", o->net->name.c_str(ctx)); + topographical_order.emplace_back(o->net); + TimingData td; + td.false_startpoint = true; + td.max_arrival = 0; + net_data[o->net][ClockEvent{async_clock, RISING_EDGE}] = td; + } } } } diff --git a/gui/leuctra/mainwindow.cc b/gui/leuctra/mainwindow.cc index fdf009a8..08ea3ac1 100644 --- a/gui/leuctra/mainwindow.cc +++ b/gui/leuctra/mainwindow.cc @@ -22,13 +22,14 @@ #include "log.h" #include +#include static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN -MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) - : BaseMainWindow(std::move(context), args, parent) +MainWindow::MainWindow(std::unique_ptr context, CommandHandler *handler, QWidget *parent) + : BaseMainWindow(std::move(context), handler, parent) { initMainResource(); @@ -53,7 +54,7 @@ void MainWindow::createMenu() { actionLoadUCF = new QAction("Open UCF", this); actionLoadUCF->setIcon(QIcon(":/icons/resources/open_ucf.png")); actionLoadUCF->setStatusTip("Open UCF file"); - actionLoadUCF->setEnabled(false); + actionLoadUCF->setEnabled(true); connect(actionLoadUCF, &QAction::triggered, this, &MainWindow::open_ucf); // Add actions in menus @@ -64,7 +65,14 @@ void MainWindow::createMenu() { menuDesign->addAction(actionLoadUCF); } -void MainWindow::new_proj() {} +void MainWindow::new_proj() { + QMap family; + family.insert("Spartan 6", Arch::FAMILY_SPARTAN6); + bool ok; + QString item = QInputDialog::getItem(this, "Select new context", "Family:", family.keys(), 0, false, &ok); + if (ok && !item.isEmpty()) { + } +} void MainWindow::open_ucf() { diff --git a/gui/leuctra/mainwindow.h b/gui/leuctra/mainwindow.h index b0616b9c..d60c2af0 100644 --- a/gui/leuctra/mainwindow.h +++ b/gui/leuctra/mainwindow.h @@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow Q_OBJECT public: - explicit MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent = 0); + explicit MainWindow(std::unique_ptr context, CommandHandler *handler, QWidget *parent = 0); virtual ~MainWindow(); public: diff --git a/leuctra/arch.cc b/leuctra/arch.cc index f6beb3fa..4e24ea76 100644 --- a/leuctra/arch.cc +++ b/leuctra/arch.cc @@ -490,14 +490,20 @@ std::vector Arch::getBelPins(BelId bel) const delay_t Arch::estimateDelay(WireId src, WireId dst) const { - // TODO - return 13; + // XXX + int dx = std::abs(src.location.x - dst.location.x); + int dy = std::abs(src.location.y - dst.location.y); + return (dx + dy + 10) * 300; } delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const { - // TODO - return 13; + // XXX + auto &src_loc = net_info->driver.cell->bel.location; + auto &dst_loc = sink.cell->bel.location; + int dx = std::abs(src_loc.x - dst_loc.x); + int dy = std::abs(src_loc.y - dst_loc.y); + return (dx + dy + 10) * 300; } bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } @@ -515,16 +521,23 @@ bool Arch::place() cfg.ioBufTypes.insert(id("IOB")); if (!placer_heap(getCtx(), cfg)) return false; + getCtx()->settings[getCtx()->id("place")] = 1; } else if (placer == "sa") { if (!placer1(getCtx(), Placer1Cfg(getCtx()))) return false; + getCtx()->settings[getCtx()->id("place")] = 1; } else { log_error("Leuctra architecture does not support placer '%s'\n", placer.c_str()); } return true; } -bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } +bool Arch::route() { + bool retVal = router1(getCtx(), Router1Cfg(getCtx())); + if (retVal) + getCtx()->settings[getCtx()->id("route")] = 1; + return retVal; +} // ----------------------------------------------------------------------- @@ -574,14 +587,182 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { // XXX - delay.min_delay = 11; - delay.max_delay = 13; - return true; + delay.min_delay = 150; + delay.max_delay = 150; + if (cell->type == id("LEUCTRA_LC")) { + if (toPort == id("O6") || toPort == id("O5") || toPort == id("CO") || toPort == id("DCO") || toPort == id("XO")) { + if (fromPort == id("I1") || fromPort == id("RA1")) + return true; + if (fromPort == id("I2") || fromPort == id("RA2")) + return true; + if (fromPort == id("I3") || fromPort == id("RA3")) + return true; + if (fromPort == id("I4") || fromPort == id("RA4")) + return true; + if (fromPort == id("I5") || fromPort == id("RA5")) + return true; + if (toPort != id("O5") && (fromPort == id("I6") || fromPort == id("RA6"))) + return true; + } + if (toPort == id("CO") || toPort == id("DCO") || toPort == id("XO")) { + if (fromPort == id("XI")) { + if (cell->params.count(id("CYINIT")) && cell->params.at(id("CYINIT")) == Property("XI")) + return true; + if (cell->params.count(id("CYMUX")) && cell->params.at(id("CYMUX")) == Property("XI")) + return true; + } + if (fromPort == id("DCI")) + return true; + } + if ((toPort == id("MO") || toPort == id("DMO")) && (fromPort == id("DMI0") || fromPort == id("DMI1") || fromPort == id("XI"))) + return true; + return false; + } else if (cell->type == id("LEUCTRA_FF")) { + if (cell->params.at(id("MODE")).as_string() == "COMB") { + return true; + } + return false; + } else if (cell->type == id("BUFGMUX")) { + if (toPort == id("O")) { + if (fromPort == id("I0")) + return true; + if (fromPort == id("I1")) + return true; + } + return false; + } else if (cell->type == id("OLOGIC2")) { + if (toPort == id("OQ") && fromPort == id("D1")) + return true; + return false; + } else if (cell->type == id("ILOGIC2")) { + if (toPort == id("FABRICOUT") && fromPort == id("D")) + return true; + return false; + } else if (cell->type == id("RAMB16BWER")) { + return false; + } else if (cell->type == id("RAMB8BWER")) { + return false; + } + log_warning("cell type '%s' arc '%s' '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this), fromPort.c_str(this), toPort.c_str(this), cell->name.c_str(this)); + return false; } TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const { + if (cell->type == id("LEUCTRA_LC")) { + if (cell->attrs.count(id("CONST"))) + return TMG_IGNORE; + if (port == id("O6") || port == id("O5") || port == id("MO") || port == id("DMO") || port == id("DCO") || port == id("CO") || port == id("XO")) + return TMG_COMB_OUTPUT; + if (port == id("I1") || port == id("RA1")) + return TMG_COMB_INPUT; + if (port == id("I2") || port == id("RA2")) + return TMG_COMB_INPUT; + if (port == id("I3") || port == id("RA3")) + return TMG_COMB_INPUT; + if (port == id("I4") || port == id("RA4")) + return TMG_COMB_INPUT; + if (port == id("I5") || port == id("RA5")) + return TMG_COMB_INPUT; + if (port == id("I6") || port == id("RA6")) + return TMG_COMB_INPUT; + if (port == id("WA1") || port == id("WA2") || port == id("WA3") || port == id("WA4") || port == id("WA5") || port == id("WA6") || port == id("WA7") || port == id("WA8") || port == id("WE") || port == id("DDI5") || port == id("DDI7") || port == id("DDI8")) { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } + if (port == id("DMI0") || port == id("DMI1") || port == id("DCI")) + return TMG_COMB_INPUT; + if (port == id("XI")) { + if (cell->params.count(id("DIMUX")) && cell->params.at(id("DIMUX")).as_string() == "XI") { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } else { + return TMG_COMB_INPUT; + } + } + if (port == id("CLK")) + return TMG_CLOCK_INPUT; + } + if (cell->type == id("LEUCTRA_FF")) { + if (cell->params.at(id("MODE")).as_string() == "COMB") { + if (port == id("D") || + port == id("SR") || + port == id("CLK") || + port == id("CE")) { + return TMG_COMB_INPUT; + } + if (port == id("Q")) { + return TMG_COMB_OUTPUT; + } + } else { + if (port == id("D") || + port == id("SR") || + port == id("CE")) { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } + if (port == id("CLK")) + return TMG_CLOCK_INPUT; + if (port == id("Q")) { + clockInfoCount = 1; + return TMG_REGISTER_OUTPUT; + } + } + } + if (cell->type == id("BUFGMUX")) { + if (port == id("O")) + return TMG_COMB_OUTPUT; + if (port == id("I0")) + return TMG_COMB_INPUT; + if (port == id("I1")) + return TMG_COMB_INPUT; + if (port == id("S")) + return TMG_IGNORE; + } + if (cell->type == id("IOB")) { + if (port == id("I")) + return TMG_STARTPOINT; + if (port == id("O")) + return TMG_ENDPOINT; + if (port == id("T")) + return TMG_ENDPOINT; + } + if (cell->type == id("ILOGIC2")) { + if (port == id("D")) + return TMG_COMB_INPUT; + if (port == id("FABRICOUT")) + return TMG_COMB_OUTPUT; + } + if (cell->type == id("OLOGIC2")) { + if (port == id("D1")) + return TMG_COMB_INPUT; + if (port == id("OQ")) + return TMG_COMB_OUTPUT; + } + if (cell->type == id("RAMB8BWER")) { + if (port == id("CLKAWRCLK")) + return TMG_CLOCK_INPUT; + if (port == id("CLKBRDCLK")) + return TMG_CLOCK_INPUT; + clockInfoCount = 1; + if (cell->ports.at(port).type == PORT_IN) + return TMG_REGISTER_INPUT; + else + return TMG_REGISTER_OUTPUT; + } + if (cell->type == id("RAMB16BWER")) { + if (port == id("CLKA")) + return TMG_CLOCK_INPUT; + if (port == id("CLKB")) + return TMG_CLOCK_INPUT; + clockInfoCount = 1; + if (cell->ports.at(port).type == PORT_IN) + return TMG_REGISTER_INPUT; + else + return TMG_REGISTER_OUTPUT; + } // XXX + log_warning("cell type '%s' port '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this), port.c_str(this), cell->name.c_str(this)); return TMG_IGNORE; } @@ -591,10 +772,56 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.setup = getDelayFromNS(0); info.hold = getDelayFromNS(0); info.clockToQ = getDelayFromNS(0); - // XXX + if (cell->type == id("LEUCTRA_LC") || cell->type == id("LEUCTRA_FF")) { + info.clock_port = id("CLK"); + if (cell->params.count(id("CLKINV")) && cell->params.at(id("CLKINV")) == Property("CLK_B")) + info.edge = FALLING_EDGE; + else + info.edge = RISING_EDGE; + } + if (cell->type == id("RAMB8BWER")) { + // XXX wrong wrong wrong + info.clock_port = id("CLKAWRCLK"); + if (cell->params.count(id("CLKAWRCLKINV")) && cell->params.at(id("CLKAWRCLKINV")) == Property("CLKAWRCLK_B")) + info.edge = FALLING_EDGE; + else + info.edge = RISING_EDGE; + } + if (cell->type == id("RAMB16BWER")) { + // XXX wrong wrong wrong + info.clock_port = id("CLKA"); + if (cell->params.count(id("CLKAINV")) && cell->params.at(id("CLKAINV")) == Property("CLKA_B")) + info.edge = FALLING_EDGE; + else + info.edge = RISING_EDGE; + } return info; } +// Assign arch arg info +void Arch::assignArchInfo() +{ +#if 0 + for (auto &net : getCtx()->nets) { + NetInfo *ni = net.second.get(); + if (isGlobalNet(ni)) + ni->is_global = true; + ni->is_enable = false; + ni->is_reset = false; + for (auto usr : ni->users) { + if (is_enable_port(this, usr)) + ni->is_enable = true; + if (is_reset_port(this, usr)) + ni->is_reset = true; + } + } + for (auto &cell : getCtx()->cells) { + CellInfo *ci = cell.second.get(); + assignCellInfo(ci); + } +#endif +} + #ifdef WITH_HEAP const std::string Arch::defaultPlacer = "heap"; #else diff --git a/leuctra/arch.h b/leuctra/arch.h index 3a2a150f..ee77c41c 100644 --- a/leuctra/arch.h +++ b/leuctra/arch.h @@ -685,6 +685,10 @@ struct Arch : BaseCtx BelId getBelByLocation(Loc loc) const { BelId res; + if (loc.x >= device_info->width || loc.y >= device_info->height) + return BelId(); + if (loc.z >= getTileType(loc.x, loc.y).num_bels) + return BelId(); res.location.x = loc.x; res.location.y = loc.y; res.index = loc.z; @@ -789,6 +793,11 @@ struct Arch : BaseCtx std::vector getBelPins(BelId bel) const; + BelPOD::BelFlags getBelFlags(BelId bel) const { + auto &tile = getTile(bel.location); + return tile.bels[bel.index].flags; + } + BelId getRelatedBel(BelId bel, int relation) const { auto &tile = getTile(bel.location); auto &related = tile.bels[bel.index].related[relation]; @@ -1050,8 +1059,8 @@ struct Arch : BaseCtx { // TODO: delays. DelayInfo del; - del.min_delay = 11; - del.max_delay = 13; + del.min_delay = 150; + del.max_delay = 150; return del; } @@ -1135,7 +1144,7 @@ struct Arch : BaseCtx delay_t estimateDelay(WireId src, WireId dst) const; delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const; delay_t getDelayEpsilon() const { return 20; } - delay_t getRipupDelayPenalty() const { return 200; } + delay_t getRipupDelayPenalty() const { return 80; } float getDelayNS(delay_t v) const { return v * 0.001; } DelayInfo getDelayFromNS(float ns) const { @@ -1176,22 +1185,43 @@ struct Arch : BaseCtx // Placement validity checks // TODO: validate bel subtype (SLICEM vs SLICEL, IOBM vs IOBS, ...). bool isValidBelForCell(CellInfo *cell, BelId bel) const { - if (cell->type == id("LEUCTRA_FF") && (0x924924ull & 1ull << bel.index)) - return false; + if (cell->type == id("LEUCTRA_FF")) { + if (!cell->constr_parent) { + if (0x924924ull & 1ull << bel.index) + return false; + // XXX FIX FIX FIX + if (0xffcfffull & 1ull << bel.index) + return false; + } + } + if (cell->type == id("LEUCTRA_LC")) { + int mask = cell->attrs[id("LOCMASK")].as_int64(); + int lci = bel.index / 3 % 4; + if (!(mask & 1 << lci)) + return false; + if (cell->attrs[id("NEEDS_L")].as_bool()) { + if (!(getBelFlags(bel) & (BelPOD::FLAG_SLICEL | BelPOD::FLAG_SLICEM))) + return false; + } + if (cell->attrs[id("NEEDS_M")].as_bool()) { + if (!(getBelFlags(bel) & BelPOD::FLAG_SLICEM)) + return false; + } + // XXX more? + } return true; } bool isBelLocationValid(BelId bel) const { CellInfo *cell = getBoundBelCell(bel); - if (cell == nullptr) - return true; - else - return isValidBelForCell(cell, bel); + if (cell && !isValidBelForCell(cell, bel)) + return false; + return true; } // Apply UCF constraints to the context bool applyUCF(std::string filename, std::istream &in); - //void assignArchInfo(); + void assignArchInfo(); static const std::string defaultPlacer; static const std::vector availablePlacers; }; diff --git a/leuctra/archdefs.h b/leuctra/archdefs.h index 39145492..d4a47532 100644 --- a/leuctra/archdefs.h +++ b/leuctra/archdefs.h @@ -205,7 +205,7 @@ template <> struct hash std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept { std::size_t seed = std::hash()(pip.location); - seed ^= std::hash()(pip.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= std::hash()(pip.index ^ pip.subindex) + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed; } }; diff --git a/leuctra/cells.cc b/leuctra/cells.cc index d7d5243d..4fe734da 100644 --- a/leuctra/cells.cc +++ b/leuctra/cells.cc @@ -19,12 +19,20 @@ #include "cells.h" #include +#include #include "design_utils.h" #include "log.h" #include "util.h" NEXTPNR_NAMESPACE_BEGIN +struct LutData { + int nbits; + NetInfo *nets[6]; + Property init; + CellInfo *cell; +}; + void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir) { IdString id = ctx->id(name); @@ -58,29 +66,62 @@ std::unique_ptr create_leuctra_cell(Context *ctx, IdString type, std:: }; if (type == ctx->id("LEUCTRA_FF")) { - new_cell->params[ctx->id("MODE")] = "FFSYNC"; - new_cell->params[ctx->id("INIT")] = "0"; + new_cell->params[ctx->id("MODE")] = Property("FF_SYNC"); add_port(ctx, new_cell.get(), "D", PORT_IN); add_port(ctx, new_cell.get(), "CLK", PORT_IN); add_port(ctx, new_cell.get(), "CE", PORT_IN); add_port(ctx, new_cell.get(), "SR", PORT_IN); add_port(ctx, new_cell.get(), "Q", PORT_OUT); } else if (type == ctx->id("LEUCTRA_LC")) { - new_cell->params[ctx->id("INIT")] = "0000000000000000000000000000000000000000000000000000000000000000"; + new_cell->params[ctx->id("MODE")] = Property("ROM"); + new_cell->params[ctx->id("INIT")] = Property(0, 64); + new_cell->attrs[ctx->id("NEEDS_L")] = Property(false); + new_cell->attrs[ctx->id("NEEDS_M")] = Property(false); + new_cell->attrs[ctx->id("LOCMASK")] = Property(0xf, 4); add_port(ctx, new_cell.get(), "I1", PORT_IN); add_port(ctx, new_cell.get(), "I2", PORT_IN); add_port(ctx, new_cell.get(), "I3", PORT_IN); add_port(ctx, new_cell.get(), "I4", PORT_IN); add_port(ctx, new_cell.get(), "I5", PORT_IN); add_port(ctx, new_cell.get(), "I6", PORT_IN); + add_port(ctx, new_cell.get(), "RA1", PORT_IN); + add_port(ctx, new_cell.get(), "RA2", PORT_IN); + add_port(ctx, new_cell.get(), "RA3", PORT_IN); + add_port(ctx, new_cell.get(), "RA4", PORT_IN); + add_port(ctx, new_cell.get(), "RA5", PORT_IN); + add_port(ctx, new_cell.get(), "RA6", PORT_IN); + add_port(ctx, new_cell.get(), "WA1", PORT_IN); + add_port(ctx, new_cell.get(), "WA2", PORT_IN); + add_port(ctx, new_cell.get(), "WA3", PORT_IN); + add_port(ctx, new_cell.get(), "WA4", PORT_IN); + add_port(ctx, new_cell.get(), "WA5", PORT_IN); + add_port(ctx, new_cell.get(), "WA6", PORT_IN); + add_port(ctx, new_cell.get(), "WA7", PORT_IN); + add_port(ctx, new_cell.get(), "WA8", PORT_IN); + add_port(ctx, new_cell.get(), "WE", PORT_IN); + add_port(ctx, new_cell.get(), "CLK", PORT_IN); add_port(ctx, new_cell.get(), "O6", PORT_OUT); + add_port(ctx, new_cell.get(), "O5", PORT_OUT); + add_port(ctx, new_cell.get(), "DMI0", PORT_IN); + add_port(ctx, new_cell.get(), "DMI1", PORT_IN); + add_port(ctx, new_cell.get(), "XI", PORT_IN); + add_port(ctx, new_cell.get(), "DCI", PORT_IN); + add_port(ctx, new_cell.get(), "MO", PORT_OUT); + add_port(ctx, new_cell.get(), "XO", PORT_OUT); + add_port(ctx, new_cell.get(), "CO", PORT_OUT); + add_port(ctx, new_cell.get(), "DCO", PORT_OUT); + add_port(ctx, new_cell.get(), "DMO", PORT_OUT); + add_port(ctx, new_cell.get(), "DDI5", PORT_IN); + add_port(ctx, new_cell.get(), "DDI7", PORT_IN); + add_port(ctx, new_cell.get(), "DDI8", PORT_IN); } else if (type == ctx->id("IOB")) { - new_cell->params[ctx->id("DIR")] = "INPUT"; - new_cell->attrs[ctx->id("IOSTANDARD")] = "LVCMOS33"; - add_port(ctx, new_cell.get(), "O", PORT_IN); add_port(ctx, new_cell.get(), "T", PORT_IN); add_port(ctx, new_cell.get(), "I", PORT_OUT); + add_port(ctx, new_cell.get(), "PADOUT", PORT_OUT); + add_port(ctx, new_cell.get(), "DIFFO_OUT", PORT_OUT); + add_port(ctx, new_cell.get(), "DIFFO_IN", PORT_IN); + add_port(ctx, new_cell.get(), "DIFFI_IN", PORT_IN); } else if (type == ctx->id("ILOGIC2")) { add_port(ctx, new_cell.get(), "D", PORT_IN); add_port(ctx, new_cell.get(), "FABRICOUT", PORT_OUT); @@ -89,11 +130,6 @@ std::unique_ptr create_leuctra_cell(Context *ctx, IdString type, std:: add_port(ctx, new_cell.get(), "OQ", PORT_OUT); add_port(ctx, new_cell.get(), "T1", PORT_IN); add_port(ctx, new_cell.get(), "TQ", PORT_OUT); - } else if (type == ctx->id("LUT1")) { - new_cell->params[ctx->id("INIT")] = "0"; - - add_port(ctx, new_cell.get(), "I0", PORT_IN); - add_port(ctx, new_cell.get(), "O", PORT_OUT); } else { log_error("unable to create Leuctra cell of type %s", type.c_str(ctx)); } @@ -104,14 +140,14 @@ void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector &todelete_cells) { if (nxio->type == ctx->id("$nextpnr_ibuf")) { - iob->params[ctx->id("DIR")] = "INPUT"; + iob->params[ctx->id("DIR")] = Property("INPUT"); replace_port(nxio, ctx->id("O"), iob, ctx->id("I")); } else if (nxio->type == ctx->id("$nextpnr_obuf")) { - iob->params[ctx->id("DIR")] = "OUTPUT"; + iob->params[ctx->id("DIR")] = Property("OUTPUT"); replace_port(nxio, ctx->id("I"), iob, ctx->id("O")); } else if (nxio->type == ctx->id("$nextpnr_iobuf")) { // N.B. tristate will be dealt with below - iob->params[ctx->id("DIR")] = "BIDIR"; + iob->params[ctx->id("DIR")] = Property("BIDIR"); replace_port(nxio, ctx->id("I"), iob, ctx->id("O")); replace_port(nxio, ctx->id("O"), iob, ctx->id("I")); } else { @@ -126,7 +162,7 @@ void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector inv_lut = create_leuctra_cell(ctx, ctx->id("LUT1"), iob->name.str(ctx) + "$invert_T"); replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("I0")); - inv_lut->params[ctx->id("INIT")] = "1"; + inv_lut->params[ctx->id("INIT")] = Property(1, 2); connect_ports(ctx, inv_lut.get(), ctx->id("O"), iob, ctx->id("T")); created_cells.push_back(std::move(inv_lut)); @@ -142,84 +178,451 @@ void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector> &created_cells, +void convert_ff(Context *ctx, CellInfo *orig, CellInfo *ff, std::vector> &new_cells, std::unordered_set &todelete_cells) { + bool is_latch; + IdString sr_pin; + IdString ce_pin; + IdString clk_pin; + bool clk_inv; + Property mode; + bool srval; if (orig->type == ctx->id("FDRE")) { - ff->params[ctx->id("MODE")] = "FF_SYNC"; - ff->params[ctx->id("INIT")] = "0"; - replace_port(orig, ctx->id("D"), ff, ctx->id("D")); - replace_port(orig, ctx->id("C"), ff, ctx->id("CLK")); - replace_port(orig, ctx->id("CE"), ff, ctx->id("CE")); - replace_port(orig, ctx->id("R"), ff, ctx->id("SR")); - replace_port(orig, ctx->id("Q"), ff, ctx->id("Q")); + mode = Property("FF_SYNC"); + srval = false; + sr_pin = ctx->id("R"); + is_latch = false; + clk_inv = false; + } else if (orig->type == ctx->id("FDSE")) { + mode = Property("FF_SYNC"); + srval = true; + sr_pin = ctx->id("S"); + is_latch = false; + clk_inv = false; + } else if (orig->type == ctx->id("FDCE")) { + mode = Property("FF_ASYNC"); + srval = false; + sr_pin = ctx->id("CLR"); + is_latch = false; + clk_inv = false; + } else if (orig->type == ctx->id("FDPE")) { + mode = Property("FF_ASYNC"); + srval = true; + sr_pin = ctx->id("PRE"); + is_latch = false; + clk_inv = false; + } else if (orig->type == ctx->id("LDCE")) { + mode = Property("LATCH"); + srval = false; + sr_pin = ctx->id("CLR"); + is_latch = true; + clk_inv = true; + } else if (orig->type == ctx->id("LDPE")) { + mode = Property("LATCH"); + srval = true; + sr_pin = ctx->id("PRE"); + is_latch = true; + clk_inv = true; + } else if (orig->type == ctx->id("FDRE_1")) { + mode = Property("FF_SYNC"); + srval = false; + sr_pin = ctx->id("R"); + is_latch = false; + clk_inv = true; + } else if (orig->type == ctx->id("FDSE_1")) { + mode = Property("FF_SYNC"); + srval = true; + sr_pin = ctx->id("S"); + is_latch = false; + clk_inv = true; + } else if (orig->type == ctx->id("FDCE_1")) { + mode = Property("FF_ASYNC"); + srval = false; + sr_pin = ctx->id("CLR"); + is_latch = false; + clk_inv = true; + } else if (orig->type == ctx->id("FDPE_1")) { + mode = Property("FF_ASYNC"); + srval = true; + sr_pin = ctx->id("PRE"); + is_latch = false; + clk_inv = true; + } else if (orig->type == ctx->id("LDCE_1")) { + mode = Property("LATCH"); + srval = false; + sr_pin = ctx->id("CLR"); + is_latch = true; + clk_inv = false; + } else if (orig->type == ctx->id("LDPE_1")) { + mode = Property("LATCH"); + srval = true; + sr_pin = ctx->id("PRE"); + is_latch = true; + clk_inv = false; } else { - NPNR_ASSERT(false); + NPNR_ASSERT_FALSE("WEIRD FF TYPE"); + } + if (is_latch) { + clk_pin = ctx->id("G"); + ce_pin = ctx->id("GE"); + } else { + clk_pin = ctx->id("C"); + ce_pin = ctx->id("CE"); + } + + ff->params[ctx->id("MODE")] = mode; + ff->params[ctx->id("SRVAL")] = Property(srval, 1); + if (orig->params.count(ctx->id("INIT"))) { + if (orig->params[ctx->id("INIT")].str[0] != 'x') + ff->params[ctx->id("INIT")] = orig->params[ctx->id("INIT")]; + orig->params.erase(ctx->id("INIT")); + } + + NetInfo *net; + bool net_inv = false, cval = false; + + if (get_invertible_port(ctx, orig, ctx->id("D"), false, false, net, net_inv)) + set_invertible_port(ctx, ff, ctx->id("D"), net, net_inv, false, new_cells); + + if (get_invertible_port(ctx, orig, sr_pin, false, false, net, net_inv)) { + if (get_const_val(ctx, net, cval) && (cval ^ net_inv) == false) { + // SR tied to 0 — remove it. + } else { + set_invertible_port(ctx, ff, ctx->id("SR"), net, net_inv, false, new_cells); + ff->params[ctx->id("SRUSED")] = Property(true); + } + } + + if (get_invertible_port(ctx, orig, ce_pin, false, false, net, net_inv)) { + if (get_const_val(ctx, net, cval) && (cval ^ net_inv) == true) { + // CE tied to 1 — remove it. + } else { + set_invertible_port(ctx, ff, ctx->id("CE"), net, net_inv, false, new_cells); + ff->params[ctx->id("CEUSED")] = Property(true); + } + } + + if (get_invertible_port(ctx, orig, clk_pin, clk_inv, true, net, net_inv)) { + set_invertible_port(ctx, ff, ctx->id("CLK"), net, net_inv, true, new_cells); + } + + replace_port(orig, ctx->id("Q"), ff, ctx->id("Q")); + + for (auto ¶m : orig->params) { + log_error("FF %s has leftover param %s = %s\n", orig->name.c_str(ctx), param.first.c_str(ctx), param.second.str.c_str()); } } -void convert_lut(Context *ctx, CellInfo *orig, CellInfo *lut, std::vector> &created_cells, +LutData get_lut(Context *ctx, NetInfo *net) { + LutData res; + res.cell = net->driver.cell; + if (!res.cell || res.cell->type == ctx->id("GND")) { + res.nbits = 0; + res.init = Property(0, 1); + } else if (res.cell->type == ctx->id("VCC")) { + res.nbits = 0; + res.init = Property(1, 1); + } else if (res.cell->type == ctx->id("INV")) { + res.nbits = 1; + res.nets[0] = res.cell->ports[ctx->id("I")].net; + res.init = Property(1, 2); + } else if (res.cell->type == ctx->id("LUT1")) { + res.nbits = 1; + res.nets[0] = res.cell->ports[ctx->id("I0")].net; + res.init = res.cell->params[ctx->id("INIT")]; + } else if (res.cell->type == ctx->id("LUT2")) { + res.nbits = 2; + res.nets[0] = res.cell->ports[ctx->id("I0")].net; + res.nets[1] = res.cell->ports[ctx->id("I1")].net; + res.init = res.cell->params[ctx->id("INIT")]; + } else if (res.cell->type == ctx->id("LUT3")) { + res.nbits = 3; + res.nets[0] = res.cell->ports[ctx->id("I0")].net; + res.nets[1] = res.cell->ports[ctx->id("I1")].net; + res.nets[2] = res.cell->ports[ctx->id("I2")].net; + res.init = res.cell->params[ctx->id("INIT")]; + } else if (res.cell->type == ctx->id("LUT4")) { + res.nbits = 4; + res.nets[0] = res.cell->ports[ctx->id("I0")].net; + res.nets[1] = res.cell->ports[ctx->id("I1")].net; + res.nets[2] = res.cell->ports[ctx->id("I2")].net; + res.nets[3] = res.cell->ports[ctx->id("I3")].net; + res.init = res.cell->params[ctx->id("INIT")]; + } else if (res.cell->type == ctx->id("LUT5")) { + res.nbits = 5; + res.nets[0] = res.cell->ports[ctx->id("I0")].net; + res.nets[1] = res.cell->ports[ctx->id("I1")].net; + res.nets[2] = res.cell->ports[ctx->id("I2")].net; + res.nets[3] = res.cell->ports[ctx->id("I3")].net; + res.nets[4] = res.cell->ports[ctx->id("I4")].net; + res.init = res.cell->params[ctx->id("INIT")]; + } else if (res.cell->type == ctx->id("LUT6")) { + res.nbits = 6; + res.nets[0] = res.cell->ports[ctx->id("I0")].net; + res.nets[1] = res.cell->ports[ctx->id("I1")].net; + res.nets[2] = res.cell->ports[ctx->id("I2")].net; + res.nets[3] = res.cell->ports[ctx->id("I3")].net; + res.nets[4] = res.cell->ports[ctx->id("I4")].net; + res.nets[5] = res.cell->ports[ctx->id("I5")].net; + res.init = res.cell->params[ctx->id("INIT")]; + } else { + res.cell = nullptr; + res.nbits = 1; + res.nets[0] = net; + res.init = Property(2, 2); + } + return res; +} + +void kill_lut(Context *ctx, const LutData &ld, std::unordered_set &todelete_cells) { + if (!ld.cell) + return; + todelete_cells.insert(ld.cell->name); + for (auto &port : ld.cell->ports) + if (port.second.net) + disconnect_port(ctx, ld.cell, port.first); +} + +CellInfo *convert_lut(Context *ctx, NetInfo *net, std::string name, std::vector> &created_cells, std::unordered_set &todelete_cells) { - auto &orig_init = orig->params[ctx->id("INIT")]; - std::string new_init = "0000000000000000000000000000000000000000000000000000000000000000"; - int nbits = 0; - if (orig->type == ctx->id("LUT1")) { - nbits = 2; - replace_port(orig, ctx->id("I0"), lut, ctx->id("I1")); - replace_port(orig, ctx->id("O"), lut, ctx->id("O6")); - } else if (orig->type == ctx->id("LUT2")) { - nbits = 4; - replace_port(orig, ctx->id("I0"), lut, ctx->id("I1")); - replace_port(orig, ctx->id("I1"), lut, ctx->id("I2")); - replace_port(orig, ctx->id("O"), lut, ctx->id("O6")); - } else if (orig->type == ctx->id("LUT3")) { - nbits = 8; - replace_port(orig, ctx->id("I0"), lut, ctx->id("I1")); - replace_port(orig, ctx->id("I1"), lut, ctx->id("I2")); - replace_port(orig, ctx->id("I2"), lut, ctx->id("I3")); - replace_port(orig, ctx->id("O"), lut, ctx->id("O6")); - } else if (orig->type == ctx->id("LUT4")) { - nbits = 16; - replace_port(orig, ctx->id("I0"), lut, ctx->id("I1")); - replace_port(orig, ctx->id("I1"), lut, ctx->id("I2")); - replace_port(orig, ctx->id("I2"), lut, ctx->id("I3")); - replace_port(orig, ctx->id("I3"), lut, ctx->id("I4")); - replace_port(orig, ctx->id("O"), lut, ctx->id("O6")); - } else if (orig->type == ctx->id("LUT5")) { - nbits = 32; - replace_port(orig, ctx->id("I0"), lut, ctx->id("I1")); - replace_port(orig, ctx->id("I1"), lut, ctx->id("I2")); - replace_port(orig, ctx->id("I2"), lut, ctx->id("I3")); - replace_port(orig, ctx->id("I3"), lut, ctx->id("I4")); - replace_port(orig, ctx->id("I4"), lut, ctx->id("I5")); - replace_port(orig, ctx->id("O"), lut, ctx->id("O6")); - } else if (orig->type == ctx->id("LUT6")) { - new_init = orig_init; - replace_port(orig, ctx->id("I0"), lut, ctx->id("I1")); - replace_port(orig, ctx->id("I1"), lut, ctx->id("I2")); - replace_port(orig, ctx->id("I2"), lut, ctx->id("I3")); - replace_port(orig, ctx->id("I3"), lut, ctx->id("I4")); - replace_port(orig, ctx->id("I4"), lut, ctx->id("I5")); - replace_port(orig, ctx->id("I5"), lut, ctx->id("I6")); - replace_port(orig, ctx->id("O"), lut, ctx->id("O6")); + LutData ld = get_lut(ctx, net); + Property new_init(0, 64); + for (int i = 0; i < 64; i++) { + new_init.str[i] = ld.init.str[i % (1 << ld.nbits)]; + } + new_init.update_intval(); + kill_lut(ctx, ld, todelete_cells); + + std::unique_ptr lut_cell = + create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), name); + created_cells.push_back(std::move(lut_cell)); + CellInfo *lut = created_cells.back().get(); + lut->params[ctx->id("INIT")] = std::move(new_init); + + if (ld.nbits >= 1) + connect_port(ctx, ld.nets[0], lut, ctx->id("I1")); + if (ld.nbits >= 2) + connect_port(ctx, ld.nets[1], lut, ctx->id("I2")); + if (ld.nbits >= 3) + connect_port(ctx, ld.nets[2], lut, ctx->id("I3")); + if (ld.nbits >= 4) + connect_port(ctx, ld.nets[3], lut, ctx->id("I4")); + if (ld.nbits >= 5) + connect_port(ctx, ld.nets[4], lut, ctx->id("I5")); + if (ld.nbits >= 6) + connect_port(ctx, ld.nets[5], lut, ctx->id("I6")); + + if (ld.cell) + connect_port(ctx, net, lut, ctx->id("O6")); + + return lut; +} + +std::pair convert_muxf7(Context *ctx, NetInfo *net, std::string name, std::vector> &created_cells, + std::unordered_set &todelete_cells) +{ + CellInfo *drv = net->driver.cell; + if (drv && drv->type == ctx->id("MUXF7")) { + // Good. + NetInfo *net0 = drv->ports.at(ctx->id("I0")).net; + NetInfo *net1 = drv->ports.at(ctx->id("I1")).net; + NetInfo *netsel = drv->ports.at(ctx->id("S")).net; + CellInfo *lc0 = convert_lut(ctx, net0, name + "$i0", created_cells, todelete_cells); + CellInfo *lc1 = convert_lut(ctx, net1, name + "$i1", created_cells, todelete_cells); + connect_ports(ctx, lc0, ctx->id("O6"), lc1, ctx->id("DMI0")); + connect_ports(ctx, lc1, ctx->id("O6"), lc1, ctx->id("DMI1")); + disconnect_port(ctx, drv, ctx->id("I0")); + disconnect_port(ctx, drv, ctx->id("I1")); + disconnect_port(ctx, drv, ctx->id("S")); + disconnect_port(ctx, drv, ctx->id("O")); + todelete_cells.insert(drv->name); + connect_port(ctx, netsel, lc1, ctx->id("XI")); + connect_port(ctx, net, lc1, ctx->id("MO")); + lc1->attrs[ctx->id("LOCMASK")] = Property(0x5, 4); + lc1->attrs[ctx->id("NEEDS_L")] = Property(true); + lc0->constr_parent = lc1; + lc0->constr_z = 3; + lc1->constr_children.push_back(lc0); + return std::make_pair(lc0, lc1); } else { - NPNR_ASSERT(false); + // Not so good. + CellInfo *lc1 = convert_lut(ctx, net, name, created_cells, todelete_cells); + connect_ports(ctx, lc1, ctx->id("O6"), lc1, ctx->id("DMI1")); + set_const_port(ctx, lc1, ctx->id("XI"), true, created_cells); + lc1->attrs[ctx->id("LOCMASK")] = Property(0x5, 4); + lc1->attrs[ctx->id("NEEDS_L")] = Property(true); + return std::make_pair(nullptr, lc1); } - if (nbits) { - unsigned long init = std::stoul(orig_init); - for (int i = 0; i < 64; i++) { - int obit = i % nbits; - new_init[63-i] = '0' + (init >> obit & 1); +} + +void convert_muxf8(Context *ctx, NetInfo *net, std::string name, std::vector> &created_cells, + std::unordered_set &todelete_cells) +{ + CellInfo *drv = net->driver.cell; + CellInfo *lc00, *lc01, *lc10, *lc11; + if (drv && drv->type == ctx->id("MUXF8")) { + // Good. + NetInfo *net0 = drv->ports.at(ctx->id("I0")).net; + NetInfo *net1 = drv->ports.at(ctx->id("I1")).net; + NetInfo *netsel = drv->ports.at(ctx->id("S")).net; + std::tie(lc00, lc01) = convert_muxf7(ctx, net0, name + "$i0", created_cells, todelete_cells); + std::tie(lc10, lc11) = convert_muxf7(ctx, net1, name + "$i1", created_cells, todelete_cells); + if (!lc10) { + lc10 = convert_lut(ctx, nullptr, name + "$f8", created_cells, todelete_cells); + lc10->constr_parent = lc11; + lc10->constr_z = 3; + lc11->constr_children.push_back(lc10); } + connect_ports(ctx, lc01, ctx->id("DMO"), lc10, ctx->id("DMI0")); + connect_ports(ctx, lc11, ctx->id("DMO"), lc10, ctx->id("DMI1")); + disconnect_port(ctx, drv, ctx->id("I0")); + disconnect_port(ctx, drv, ctx->id("I1")); + disconnect_port(ctx, drv, ctx->id("S")); + disconnect_port(ctx, drv, ctx->id("O")); + todelete_cells.insert(drv->name); + connect_port(ctx, netsel, lc10, ctx->id("XI")); + connect_port(ctx, net, lc10, ctx->id("MO")); + lc11->attrs[ctx->id("LOCMASK")] = Property(0x1, 4); + lc01->constr_parent = lc11; + lc01->constr_z = 6; + lc11->constr_children.push_back(lc01); + } else { + NPNR_ASSERT_FALSE("WEIRD MUXF8"); } - lut->params[ctx->id("INIT")] = new_init; +} + +CellInfo *convert_carry4(Context *ctx, CellInfo *c4, CellInfo *link, std::vector> &created_cells, + std::unordered_set &todelete_cells) +{ + NetInfo *co[4]; + NetInfo *xo[4]; + NetInfo *di[4]; + NetInfo *s[4]; + NetInfo *cyinit; + cyinit = c4->ports[ctx->id("CYINIT")].net; + s[0] = c4->ports[ctx->id("S[0]")].net; + s[1] = c4->ports[ctx->id("S[1]")].net; + s[2] = c4->ports[ctx->id("S[2]")].net; + s[3] = c4->ports[ctx->id("S[3]")].net; + di[0] = c4->ports[ctx->id("DI[0]")].net; + di[1] = c4->ports[ctx->id("DI[1]")].net; + di[2] = c4->ports[ctx->id("DI[2]")].net; + di[3] = c4->ports[ctx->id("DI[3]")].net; + co[0] = c4->ports[ctx->id("CO[0]")].net; + co[1] = c4->ports[ctx->id("CO[1]")].net; + co[2] = c4->ports[ctx->id("CO[2]")].net; + co[3] = c4->ports[ctx->id("CO[3]")].net; + xo[0] = c4->ports[ctx->id("O[0]")].net; + xo[1] = c4->ports[ctx->id("O[1]")].net; + xo[2] = c4->ports[ctx->id("O[2]")].net; + xo[3] = c4->ports[ctx->id("O[3]")].net; + disconnect_port(ctx, c4, ctx->id("CO[0]")); + disconnect_port(ctx, c4, ctx->id("CO[1]")); + disconnect_port(ctx, c4, ctx->id("CO[2]")); + disconnect_port(ctx, c4, ctx->id("CO[3]")); + disconnect_port(ctx, c4, ctx->id("O[0]")); + disconnect_port(ctx, c4, ctx->id("O[1]")); + disconnect_port(ctx, c4, ctx->id("O[2]")); + disconnect_port(ctx, c4, ctx->id("O[3]")); + disconnect_port(ctx, c4, ctx->id("S[0]")); + disconnect_port(ctx, c4, ctx->id("S[1]")); + disconnect_port(ctx, c4, ctx->id("S[2]")); + disconnect_port(ctx, c4, ctx->id("S[3]")); + disconnect_port(ctx, c4, ctx->id("DI[0]")); + disconnect_port(ctx, c4, ctx->id("DI[1]")); + disconnect_port(ctx, c4, ctx->id("DI[2]")); + disconnect_port(ctx, c4, ctx->id("DI[3]")); + disconnect_port(ctx, c4, ctx->id("CI")); + disconnect_port(ctx, c4, ctx->id("CYINIT")); + todelete_cells.insert(c4->name); + CellInfo *lcs[4]; + int num = 0; + for (int i = 0; i < 4; i++) { + if (!co[i]->users.size()) + co[i] = nullptr; + if (!xo[i]->users.size()) + xo[i] = nullptr; + if (co[i] || xo[i]) + num = i + 1; + } + for (int i = 0; i < num; i++) { + // XXX more cases + if (i == 0 && !link) { + bool cval, is_const; + is_const = get_const_val(ctx, cyinit, cval); + if (is_const) { + lcs[i] = convert_lut(ctx, s[i], c4->name.str(ctx) + "$lc" + std::to_string(i), created_cells, todelete_cells); + connect_port(ctx, di[i], lcs[i], ctx->id("XI")); + lcs[i]->params[ctx->id("CYMUX")] = Property("XI"); + if (cval) + lcs[i]->params[ctx->id("CYINIT")] = Property("1"); + else + lcs[i]->params[ctx->id("CYINIT")] = Property("0"); + } else { + std::unique_ptr lut_cell = + create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), c4->name.str(ctx) + "$lc" + std::to_string(i)); + created_cells.push_back(std::move(lut_cell)); + lcs[i] = created_cells.back().get(); + lcs[i]->params[ctx->id("INIT")] = Property::from_string("1010101010101010101010101010101011001100110011001100110011001100"); + connect_port(ctx, s[i], lcs[i], ctx->id("I1")); + connect_port(ctx, di[i], lcs[i], ctx->id("I2")); + set_const_port(ctx, lcs[i], ctx->id("I6"), true, created_cells); + connect_port(ctx, cyinit, lcs[i], ctx->id("XI")); + lcs[i]->params[ctx->id("CYINIT")] = Property("XI"); + lcs[i]->params[ctx->id("CYMUX")] = Property("O5"); + } + lcs[0]->attrs[ctx->id("LOCMASK")] = Property(1, 4); + lcs[0]->attrs[ctx->id("NEEDS_L")] = Property(true); + } else { + lcs[i] = convert_lut(ctx, s[i], c4->name.str(ctx) + "$lc" + std::to_string(i), created_cells, todelete_cells); + connect_port(ctx, di[i], lcs[i], ctx->id("XI")); + lcs[i]->params[ctx->id("CYMUX")] = Property("XI"); + + connect_ports(ctx, link, ctx->id("DCO"), lcs[i], ctx->id("DCI")); + lcs[i]->constr_parent = link; + if (i == 0) { + // XXX does this work lol + //lcs[i]->constr_spec = 0; + lcs[i]->constr_z = -9; + lcs[i]->constr_y = 1; + } else { + lcs[i]->constr_z = 3; + } + link->constr_children.push_back(lcs[i]); + } + if (xo[i]) + connect_port(ctx, xo[i], lcs[i], ctx->id("XO")); + if (co[i]) { + if (!xo[i]) { + connect_port(ctx, co[i], lcs[i], ctx->id("CO")); + } else { + std::unique_ptr ff_cell = + create_leuctra_cell(ctx, ctx->id("LEUCTRA_FF"), c4->name.str(ctx) + "$lc" + std::to_string(i) + "$ff"); + created_cells.push_back(std::move(ff_cell)); + CellInfo *ff = created_cells.back().get(); + ff->constr_parent = lcs[i]; + ff->constr_z = 1; + lcs[i]->constr_children.push_back(ff); + ff->params[ctx->id("MODE")] = Property("COMB"); + ff->params[ctx->id("CLKINV")] = Property("CLK_B"); + set_const_port(ctx, ff, ctx->id("CLK"), true, created_cells); + connect_ports(ctx, lcs[i], ctx->id("CO"), ff, ctx->id("D")); + connect_port(ctx, co[i], ff, ctx->id("Q")); + } + } + link = lcs[i]; + } + return link; } void insert_ilogic_pass(Context *ctx, CellInfo *iob, CellInfo *ilogic) { replace_port(iob, ctx->id("I"), ilogic, ctx->id("FABRICOUT")); connect_ports(ctx, iob, ctx->id("I"), ilogic, ctx->id("D")); + ilogic->params[ctx->id("IMUX")] = Property("1"); + ilogic->params[ctx->id("FABRICOUTUSED")] = Property("0"); ilogic->constr_parent = iob; iob->constr_children.push_back(ilogic); // XXX enum @@ -231,9 +634,14 @@ void insert_ologic_pass(Context *ctx, CellInfo *iob, CellInfo *ologic) replace_port(iob, ctx->id("O"), ologic, ctx->id("D1")); connect_ports(ctx, ologic, ctx->id("OQ"), iob, ctx->id("O")); NetInfo *net_t = iob->ports.at(ctx->id("T")).net; + ologic->params[ctx->id("OMUX")] = Property("D1"); + ologic->params[ctx->id("D1USED")] = Property("0"); + ologic->params[ctx->id("O1USED")] = Property("0"); if (net_t != nullptr) { replace_port(iob, ctx->id("T"), ologic, ctx->id("T1")); connect_ports(ctx, ologic, ctx->id("TQ"), iob, ctx->id("T")); + ologic->params[ctx->id("TMUX")] = Property("T1"); + ologic->params[ctx->id("T1USED")] = Property("0"); } ologic->constr_parent = iob; iob->constr_children.push_back(ologic); @@ -241,4 +649,228 @@ void insert_ologic_pass(Context *ctx, CellInfo *iob, CellInfo *ologic) ologic->constr_spec = 2; } +bool get_const_val(Context *ctx, NetInfo *net, bool &out) { + if (!net->driver.cell) + return false; + if (net->driver.cell->type == ctx->id("GND")) { + out = false; + return true; + } + if (net->driver.cell->type == ctx->id("VCC")) { + out = true; + return true; + } + return false; +} + +void set_const_port(Context *ctx, CellInfo *cell, IdString port, bool val, std::vector> &new_cells) { + if (!cell->ports.count(port)) { + cell->ports[port].name = port; + cell->ports[port].type = PORT_IN; + } + + std::unique_ptr const_cell{new CellInfo}; + std::unique_ptr const_net{new NetInfo}; + IdString name = ctx->id(cell->name.str(ctx) + "$const$" + port.str(ctx)); + IdString const_port; + if (val) { + const_cell->type = ctx->id("VCC"); + const_port = ctx->id("P"); + } else { + const_cell->type = ctx->id("GND"); + const_port = ctx->id("G"); + } + const_cell->name = name; + const_net->name = name; + const_cell->ports[const_port].type = PORT_OUT; + connect_port(ctx, const_net.get(), const_cell.get(), const_port); + connect_port(ctx, const_net.get(), cell, port); + ctx->nets[name] = std::move(const_net); + new_cells.push_back(std::move(const_cell)); +} + +bool get_invertible_port(Context *ctx, CellInfo *cell, IdString port, bool invert, bool invertible, NetInfo *&net, bool &invert_out) { + invert_out = invert; + if (!cell->ports.count(port)) + return false; + net = cell->ports.at(port).net; + if (!net) + return false; + disconnect_port(ctx, cell, port); + // XXX support buses + IdString param_name = ctx->id("IS_" + port.str(ctx) + "_INVERTED"); + if (cell->params.count(param_name)) { + Property val = cell->params[param_name]; + invert_out ^= val.as_bool(); + cell->params.erase(param_name); + } + + if (invertible) { + while (net->driver.cell && net->driver.cell->type == ctx->id("INV")) { + CellInfo *icell = net->driver.cell; + if (!icell->ports.count(ctx->id("I"))) + return false; + net = icell->ports.at(ctx->id("I")).net; + if (!net) + return false; + invert_out ^= true; + } + } + return true; +} + +void set_invertible_port(Context *ctx, CellInfo *cell, IdString port, NetInfo *net, bool invert, bool invertible, std::vector> &new_cells) { + if (!net) + return; + cell->ports[port].name = port; + cell->ports[port].type = PORT_IN; + + if (invert && !invertible) { + std::unique_ptr inv_cell{new CellInfo}; + std::unique_ptr inv_net{new NetInfo}; + IdString name = ctx->id(cell->name.str(ctx) + "$inv$" + port.str(ctx)); + inv_cell->type = ctx->id("INV"); + inv_cell->name = name; + inv_net->name = name; + add_port(ctx, inv_cell.get(), "O", PORT_OUT); + add_port(ctx, inv_cell.get(), "I", PORT_IN); + connect_port(ctx, inv_net.get(), inv_cell.get(), ctx->id("O")); + connect_port(ctx, inv_net.get(), cell, port); + connect_port(ctx, net, inv_cell.get(), ctx->id("I")); + ctx->nets[name] = std::move(inv_net); + new_cells.push_back(std::move(inv_cell)); + } else { + connect_port(ctx, net, cell, port); + if (invertible) { + std::string val; + if (invert) + val = port.str(ctx) + "_B"; + else + val = port.str(ctx); + IdString param = ctx->id(port.str(ctx) + "INV"); + cell->params[param] = Property(val); + } + } +} + +bool handle_invertible_port(Context *ctx, CellInfo *cell, IdString port, bool invert, bool invertible, std::vector> &new_cells) { + NetInfo *net; + bool net_inv; + if (get_invertible_port(ctx, cell, port, invert, invertible, net, net_inv)) { + set_invertible_port(ctx, cell, port, net, net_inv, invertible, new_cells); + return true; + } else { + return false; + } +} + +void fixup_ramb16(Context *ctx, CellInfo *cell, std::vector> &new_cells, + std::unordered_set &todelete_cells) { + bool swizzle = false; + if (cell->params.count(ctx->id("DATA_WIDTH_A")) && cell->params.at(ctx->id("DATA_WIDTH_A")).as_int64() == 36) + swizzle = true; + if (cell->params.count(ctx->id("DATA_WIDTH_B")) && cell->params.at(ctx->id("DATA_WIDTH_B")).as_int64() == 36) + swizzle = true; + if (!cell->params.count(ctx->id("RAM_MODE"))) + cell->params[ctx->id("RAM_MODE")] = Property("TDP"); + if (!cell->params.count(ctx->id("EN_RSTRAM_A"))) + cell->params[ctx->id("EN_RSTRAM_A")] = Property("TRUE"); + if (!cell->params.count(ctx->id("EN_RSTRAM_B"))) + cell->params[ctx->id("EN_RSTRAM_B")] = Property("TRUE"); + if (!cell->params.count(ctx->id("RST_PRIORITY_A"))) + cell->params[ctx->id("RST_PRIORITY_A")] = Property("CE"); + if (!cell->params.count(ctx->id("RST_PRIORITY_B"))) + cell->params[ctx->id("RST_PRIORITY_B")] = Property("CE"); + handle_invertible_port(ctx, cell, ctx->id("CLKA"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("CLKB"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("ENA"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("ENB"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("REGCEA"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("REGCEB"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("RSTA"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("RSTB"), false, true, new_cells); + for (int i = 0; i < 32; i++) { + rename_port(ctx, cell, ctx->id("DOA[" + std::to_string(i) + "]"), ctx->id("DOA" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DOB[" + std::to_string(i) + "]"), ctx->id("DOB" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DIA[" + std::to_string(i) + "]"), ctx->id("DIA" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DIB[" + std::to_string(i) + "]"), ctx->id("DIB" + std::to_string(i))); + } + for (int i = 0; i < 4; i++) { + rename_port(ctx, cell, ctx->id("DOPA[" + std::to_string(i) + "]"), ctx->id("DOPA" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DOPB[" + std::to_string(i) + "]"), ctx->id("DOPB" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DIPA[" + std::to_string(i) + "]"), ctx->id("DIPA" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DIPB[" + std::to_string(i) + "]"), ctx->id("DIPB" + std::to_string(i))); + } + for (int i = 0; i < 14; i++) { + int si = i; + if (swizzle) { + if (i == 4) + si = 13; + else if (i == 13) + si = 4; + } + rename_port(ctx, cell, ctx->id("ADDRA[" + std::to_string(i) + "]"), ctx->id("ADDRA" + std::to_string(si))); + rename_port(ctx, cell, ctx->id("ADDRB[" + std::to_string(i) + "]"), ctx->id("ADDRB" + std::to_string(si))); + } + for (int i = 0; i < 4; i++) { + NetInfo *net; + bool net_inv; + if (get_invertible_port(ctx, cell, ctx->id("WEA[" + std::to_string(i) + "]"), false, true, net, net_inv)) { + set_invertible_port(ctx, cell, ctx->id("WEA" + std::to_string(i)), net, net_inv, true, new_cells); + } + if (get_invertible_port(ctx, cell, ctx->id("WEB[" + std::to_string(i) + "]"), false, true, net, net_inv)) { + set_invertible_port(ctx, cell, ctx->id("WEB" + std::to_string(i)), net, net_inv, true, new_cells); + } + } +} + +void fixup_ramb8(Context *ctx, CellInfo *cell, std::vector> &new_cells, + std::unordered_set &todelete_cells) { + if (!cell->params.count(ctx->id("RAM_MODE"))) + cell->params[ctx->id("RAM_MODE")] = Property("TDP"); + if (!cell->params.count(ctx->id("EN_RSTRAM_A"))) + cell->params[ctx->id("EN_RSTRAM_A")] = Property("TRUE"); + if (!cell->params.count(ctx->id("EN_RSTRAM_B"))) + cell->params[ctx->id("EN_RSTRAM_B")] = Property("TRUE"); + if (!cell->params.count(ctx->id("RST_PRIORITY_A"))) + cell->params[ctx->id("RST_PRIORITY_A")] = Property("CE"); + if (!cell->params.count(ctx->id("RST_PRIORITY_B"))) + cell->params[ctx->id("RST_PRIORITY_B")] = Property("CE"); + handle_invertible_port(ctx, cell, ctx->id("CLKAWRCLK"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("CLKBRDCLK"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("ENAWREN"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("ENBRDEN"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("REGCEA"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("REGCEBREGCE"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("RSTA"), false, true, new_cells); + handle_invertible_port(ctx, cell, ctx->id("RSTBRST"), false, true, new_cells); + for (int i = 0; i < 16; i++) { + rename_port(ctx, cell, ctx->id("DOADO[" + std::to_string(i) + "]"), ctx->id("DOADO" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DOBDO[" + std::to_string(i) + "]"), ctx->id("DOBDO" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DIADI[" + std::to_string(i) + "]"), ctx->id("DIADI" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DIBDI[" + std::to_string(i) + "]"), ctx->id("DIBDI" + std::to_string(i))); + } + for (int i = 0; i < 2; i++) { + rename_port(ctx, cell, ctx->id("DOPADOP[" + std::to_string(i) + "]"), ctx->id("DOPADOP" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DOPBDOP[" + std::to_string(i) + "]"), ctx->id("DOPBDOP" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DIPADIP[" + std::to_string(i) + "]"), ctx->id("DIPADIP" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("DIPBDIP[" + std::to_string(i) + "]"), ctx->id("DIPBDIP" + std::to_string(i))); + } + for (int i = 0; i < 13; i++) { + rename_port(ctx, cell, ctx->id("ADDRAWRADDR[" + std::to_string(i) + "]"), ctx->id("ADDRAWRADDR" + std::to_string(i))); + rename_port(ctx, cell, ctx->id("ADDRBRDADDR[" + std::to_string(i) + "]"), ctx->id("ADDRBRDADDR" + std::to_string(i))); + } + for (int i = 0; i < 2; i++) { + NetInfo *net; + bool net_inv; + if (get_invertible_port(ctx, cell, ctx->id("WEAWEL[" + std::to_string(i) + "]"), false, true, net, net_inv)) { + set_invertible_port(ctx, cell, ctx->id("WEAWEL" + std::to_string(i)), net, net_inv, true, new_cells); + } + if (get_invertible_port(ctx, cell, ctx->id("WEBWEU[" + std::to_string(i) + "]"), false, true, net, net_inv)) { + set_invertible_port(ctx, cell, ctx->id("WEBWEU" + std::to_string(i)), net, net_inv, true, new_cells); + } + } +} + + NEXTPNR_NAMESPACE_END diff --git a/leuctra/cells.h b/leuctra/cells.h index b22ee7fc..03d9c165 100644 --- a/leuctra/cells.h +++ b/leuctra/cells.h @@ -41,11 +41,17 @@ inline bool is_xilinx_iobuf(const BaseCtx *ctx, const CellInfo *cell) { } inline bool is_xilinx_ff(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == ctx->id("FDRE"); + return cell->type == ctx->id("FDRE") + || cell->type == ctx->id("FDSE") + || cell->type == ctx->id("FDCE") + || cell->type == ctx->id("FDPE") + || cell->type == ctx->id("LDCE") + || cell->type == ctx->id("LDPE"); } inline bool is_xilinx_lut(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == ctx->id("LUT1") + return cell->type == ctx->id("INV") + || cell->type == ctx->id("LUT1") || cell->type == ctx->id("LUT2") || cell->type == ctx->id("LUT3") || cell->type == ctx->id("LUT4") @@ -56,14 +62,37 @@ inline bool is_xilinx_lut(const BaseCtx *ctx, const CellInfo *cell) { // Convert a nextpnr IO buffer to an IOB void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector> &created_cells, std::unordered_set &todelete_cells); -void convert_ff(Context *ctx, CellInfo *orig, CellInfo *ff, std::vector> &created_cells, +void convert_ff(Context *ctx, CellInfo *orig, CellInfo *ff, std::vector> &new_cells, std::unordered_set &todelete_cells); -void convert_lut(Context *ctx, CellInfo *orig, CellInfo *lc, std::vector> &created_cells, +CellInfo *convert_lut(Context *ctx, NetInfo *net, std::string name, std::vector> &created_cells, + std::unordered_set &todelete_cells); +std::pair convert_muxf7(Context *ctx, NetInfo *net, std::string name, std::vector> &created_cells, + std::unordered_set &todelete_cells); +void convert_muxf8(Context *ctx, NetInfo *net, std::string name, std::vector> &created_cells, + std::unordered_set &todelete_cells); +CellInfo *convert_carry4(Context *ctx, CellInfo *c4, CellInfo *link, std::vector> &created_cells, + std::unordered_set &todelete_cells); +void fixup_ramb16(Context *ctx, CellInfo *cell, std::vector> &new_cells, + std::unordered_set &todelete_cells); +void fixup_ramb8(Context *ctx, CellInfo *cell, std::vector> &new_cells, std::unordered_set &todelete_cells); void insert_ilogic_pass(Context *ctx, CellInfo *iob, CellInfo *ilogic); void insert_ologic_pass(Context *ctx, CellInfo *iob, CellInfo *ologic); +// Gets the constant value driving the given net, returns truee iff it really was a constant. +bool get_const_val(Context *ctx, NetInfo *net, bool &out); +// Connects a given port to a constant driver. +void set_const_port(Context *ctx, CellInfo *cell, IdString port, bool val, std::vector> &new_cells); +// Takes a port, finds the net driving it, passes through INV cells, collects IS__INVERTED, collects the final driver and inversion value, returns true iff net connected. +bool get_invertible_port(Context *ctx, CellInfo *cell, IdString port, bool invert, bool invertible, NetInfo *&net, bool &invert_out); +// Takes a port and connects it to the given net, possibly with an inversion. If invertible is false, emulate it by inserting an INV cell if necessary. +void set_invertible_port(Context *ctx, CellInfo *cell, IdString port, NetInfo *net, bool invert, bool invertible, std::vector> &new_cells); +// Runs get_inversion, then set_inversion. +bool handle_invertible_port(Context *ctx, CellInfo *cell, IdString port, bool invert, bool invertible, std::vector> &new_cells); +void handle_invertible_bus(Context *ctx, CellInfo *cell, IdString port, int len, std::vector> &new_cells); +void handle_bus(Context *ctx, CellInfo *cell, IdString port, int len); + NEXTPNR_NAMESPACE_END #endif diff --git a/leuctra/main.cc b/leuctra/main.cc index 0036d8c1..57585e55 100644 --- a/leuctra/main.cc +++ b/leuctra/main.cc @@ -34,7 +34,7 @@ class LeuctraCommandHandler : public CommandHandler public: LeuctraCommandHandler(int argc, char **argv); virtual ~LeuctraCommandHandler(){}; - std::unique_ptr createContext() override; + std::unique_ptr createContext(std::unordered_map &values) override; void setupArchContext(Context *ctx) override{}; void customAfterLoad(Context *ctx) override; void customBitstream(Context *ctx) override; @@ -65,8 +65,9 @@ void LeuctraCommandHandler::customBitstream(Context *ctx) { } } -std::unique_ptr LeuctraCommandHandler::createContext() +std::unique_ptr LeuctraCommandHandler::createContext(std::unordered_map &values) { + ArchArgs chipArgs; if (vm.count("device")) chipArgs.device = vm["device"].as(); else @@ -75,7 +76,13 @@ std::unique_ptr LeuctraCommandHandler::createContext() chipArgs.package = vm["package"].as(); if (vm.count("speed")) chipArgs.speed = vm["speed"].as(); - return std::unique_ptr(new Context(chipArgs)); + // XXX parse values + + auto ctx = std::unique_ptr(new Context(chipArgs)); + for (auto &val : values) + ctx->settings[ctx->id(val.first)] = val.second; + + return ctx; } void LeuctraCommandHandler::customAfterLoad(Context *ctx) diff --git a/leuctra/pack.cc b/leuctra/pack.cc index f6080248..417b3c38 100644 --- a/leuctra/pack.cc +++ b/leuctra/pack.cc @@ -18,6 +18,7 @@ */ #include "nextpnr.h" +#include "design_utils.h" #include "cells.h" #include "log.h" #include "util.h" @@ -54,14 +55,52 @@ class LeuctraPacker new_cells.clear(); } - // Remove nextpnr iob cells, insert Xilinx primitives instead. - void pack_iob() - { - log_info("Packing IOBs...\n"); + CellInfo *fetch_nxio(CellInfo *cell, IdString port) { + PortInfo pi = cell->ports.at(port); + if (pi.net == nullptr) + return nullptr; + CellInfo *res = nullptr; + IdString res_port; + if (is_nextpnr_iob(ctx, pi.net->driver.cell)) { + res = pi.net->driver.cell; + res_port = pi.net->driver.port; + } else if (pi.net->driver.cell) { + if (pi.net->driver.cell != cell || pi.net->driver.port != port) { + log_error("Stray driver on net %s: %s %s\n", pi.net->name.c_str(ctx), pi.net->driver.cell->name.c_str(ctx), pi.net->driver.port.c_str(ctx)); + } + } + for (auto &usr : pi.net->users) + if (usr.cell == cell && usr.port == port) { + continue; + } else if (is_nextpnr_iob(ctx, usr.cell)) { + if (res) { + log_error("Two nextpnr bufs on net %s: %s %s\n", pi.net->name.c_str(ctx), usr.cell->name.c_str(ctx), res->name.c_str(ctx)); + } + res = usr.cell; + res_port = usr.port; + } else { + log_error("Stray load on net %s: %s %s\n", pi.net->name.c_str(ctx), usr.cell->name.c_str(ctx), usr.port.c_str(ctx)); + } + if (!res) + return nullptr; + // Kill the connection. + disconnect_port(ctx, res, res_port); + disconnect_port(ctx, cell, port); + return res; + } + // Insert IOB cells for ports that don't have one yet. + void insert_iob() + { + log_info("Inserting IOBs...\n"); + + /* TODO: get this working maybe? */ for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_nextpnr_iob(ctx, ci)) { + abort(); + NPNR_ASSERT_FALSE("SURVIVED"); + CellInfo *iob = nullptr; std::unique_ptr io_cell = create_leuctra_cell(ctx, ctx->id("IOB"), ci->name.str(ctx) + "$iob"); @@ -73,10 +112,12 @@ class LeuctraPacker if (iob != nullptr) { for (const auto &attr : ci->attrs) iob->attrs[attr.first] = attr.second; + for (const auto ¶m : ci->params) + iob->params[param.first] = param.second; auto loc_attr = iob->attrs.find(ctx->id("LOC")); if (loc_attr != iob->attrs.end()) { - std::string pin = loc_attr->second; + std::string pin = loc_attr->second.as_string(); BelId pinBel = ctx->getPackagePinBel(pin); if (pinBel == BelId()) { log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n", @@ -85,7 +126,7 @@ class LeuctraPacker log_info("pin '%s' constrained to Bel '%s'.\n", iob->name.c_str(ctx), ctx->getBelName(pinBel).c_str(ctx)); } - iob->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx); + iob->attrs[ctx->id("BEL")] = Property(ctx->getBelName(pinBel).str(ctx)); } } } @@ -94,6 +135,311 @@ class LeuctraPacker flush_cells(); } + // Convert Xilinx IO buffer primitives into IOB cells. + void convert_iob() + { + enum IoStdKind { + // Single ended, settable drive. + IOSTD_SINGLE_DRIVE, + // Single ended. + IOSTD_SINGLE, + // Pseudo-differential. + IOSTD_PSEUDO_DIFF, + // True differential. + IOSTD_DIFF, + }; + std::map iostds; + iostds["LVTTL"] = IOSTD_SINGLE_DRIVE; + iostds["LVCMOS33"] = IOSTD_SINGLE_DRIVE; + iostds["LVCMOS25"] = IOSTD_SINGLE_DRIVE; + iostds["LVCMOS18"] = IOSTD_SINGLE_DRIVE; + iostds["LVCMOS15"] = IOSTD_SINGLE_DRIVE; + iostds["LVCMOS12"] = IOSTD_SINGLE_DRIVE; + iostds["LVCMOS18_JEDEC"] = IOSTD_SINGLE_DRIVE; + iostds["LVCMOS15_JEDEC"] = IOSTD_SINGLE_DRIVE; + iostds["LVCMOS12_JEDEC"] = IOSTD_SINGLE_DRIVE; + iostds["PCI33_3"] = IOSTD_SINGLE; + iostds["PCI66_3"] = IOSTD_SINGLE; + iostds["SDIO"] = IOSTD_SINGLE; + iostds["MOBILE_DDR"] = IOSTD_SINGLE; + iostds["DIFF_MOBILE_DDR"] = IOSTD_PSEUDO_DIFF; + iostds["I2C"] = IOSTD_SINGLE; + iostds["SMBUS"] = IOSTD_SINGLE; + iostds["HSTL_I"] = IOSTD_SINGLE; + iostds["DIFF_HSTL_I"] = IOSTD_PSEUDO_DIFF; + iostds["HSTL_I_18"] = IOSTD_SINGLE; + iostds["DIFF_HSTL_I_18"] = IOSTD_PSEUDO_DIFF; + iostds["HSTL_II"] = IOSTD_SINGLE; + iostds["DIFF_HSTL_II"] = IOSTD_PSEUDO_DIFF; + iostds["HSTL_II_18"] = IOSTD_SINGLE; + iostds["DIFF_HSTL_II_18"] = IOSTD_PSEUDO_DIFF; + iostds["HSTL_III"] = IOSTD_SINGLE; + iostds["DIFF_HSTL_III"] = IOSTD_PSEUDO_DIFF; + iostds["HSTL_III_18"] = IOSTD_SINGLE; + iostds["DIFF_HSTL_III_18"] = IOSTD_PSEUDO_DIFF; + iostds["SSTL3_I"] = IOSTD_SINGLE; + iostds["DIFF_SSTL3_I"] = IOSTD_PSEUDO_DIFF; + iostds["SSTL2_I"] = IOSTD_SINGLE; + iostds["DIFF_SSTL2_I"] = IOSTD_PSEUDO_DIFF; + iostds["SSTL18_I"] = IOSTD_SINGLE; + iostds["DIFF_SSTL18_I"] = IOSTD_PSEUDO_DIFF; + iostds["SSTL3_II"] = IOSTD_SINGLE; + iostds["DIFF_SSTL3_II"] = IOSTD_PSEUDO_DIFF; + iostds["SSTL2_II"] = IOSTD_SINGLE; + iostds["DIFF_SSTL2_II"] = IOSTD_PSEUDO_DIFF; + iostds["SSTL18_II"] = IOSTD_SINGLE; + iostds["DIFF_SSTL18_II"] = IOSTD_PSEUDO_DIFF; + iostds["SSTL15_II"] = IOSTD_SINGLE; + iostds["DIFF_SSTL15_II"] = IOSTD_PSEUDO_DIFF; + iostds["BLVDS_25"] = IOSTD_PSEUDO_DIFF; + iostds["LVPECL_25"] = IOSTD_PSEUDO_DIFF; + iostds["LVPECL_33"] = IOSTD_PSEUDO_DIFF; + iostds["DISPLAY_PORT"] = IOSTD_PSEUDO_DIFF; + iostds["LVDS_33"] = IOSTD_DIFF; + iostds["LVDS_25"] = IOSTD_DIFF; + iostds["MINI_LVDS_33"] = IOSTD_DIFF; + iostds["MINI_LVDS_25"] = IOSTD_DIFF; + iostds["RSDS_33"] = IOSTD_DIFF; + iostds["RSDS_25"] = IOSTD_DIFF; + iostds["PPDS_33"] = IOSTD_DIFF; + iostds["PPDS_25"] = IOSTD_DIFF; + iostds["TMDS_33"] = IOSTD_DIFF; + iostds["TML_33"] = IOSTD_DIFF; + + log_info("Converting IOBs...\n"); + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + + IdString port_i, port_o, port_t, port_ib; + IdString port_pad_m, port_pad_s; + bool diff = false; + + if (ci->type == ctx->id("IBUF") || ci->type == ctx->id("IBUFG")) { + port_i = ctx->id("O"); + port_pad_m = ctx->id("I"); + } else if (ci->type == ctx->id("IBUFDS") || ci->type == ctx->id("IBUFGDS")) { + port_i = ctx->id("O"); + port_pad_m = ctx->id("I"); + port_pad_s = ctx->id("IB"); + diff = true; + } else if (ci->type == ctx->id("IBUFDS_DIFF_OUT") || ci->type == ctx->id("IBUFGDS_DIFF_OUT")) { + port_i = ctx->id("O"); + port_ib = ctx->id("OB"); + port_pad_m = ctx->id("I"); + port_pad_s = ctx->id("IB"); + diff = true; + } else if (ci->type == ctx->id("IOBUF")) { + port_i = ctx->id("O"); + port_o = ctx->id("I"); + port_t = ctx->id("T"); + port_pad_m = ctx->id("IO"); + } else if (ci->type == ctx->id("IOBUFDS")) { + port_i = ctx->id("O"); + port_o = ctx->id("I"); + port_t = ctx->id("T"); + port_pad_m = ctx->id("IO"); + port_pad_s = ctx->id("IOB"); + diff = true; + } else if (ci->type == ctx->id("OBUF")) { + port_o = ctx->id("I"); + port_pad_m = ctx->id("O"); + } else if (ci->type == ctx->id("OBUFDS")) { + port_o = ctx->id("I"); + port_pad_m = ctx->id("O"); + port_pad_s = ctx->id("OB"); + diff = true; + } else if (ci->type == ctx->id("OBUFT")) { + port_o = ctx->id("I"); + port_t = ctx->id("T"); + port_pad_m = ctx->id("O"); + } else if (ci->type == ctx->id("OBUFTDS")) { + port_o = ctx->id("I"); + port_t = ctx->id("T"); + port_pad_m = ctx->id("O"); + port_pad_s = ctx->id("OB"); + diff = true; + } else { + // Not an IO buffer. + continue; + } + + // Get the nextpnr-inserted buffers. + CellInfo *nxb_m = fetch_nxio(ci, port_pad_m); + CellInfo *nxb_s = nullptr; + if (port_pad_s != IdString()) + nxb_s = fetch_nxio(ci, port_pad_s); + + if (!nxb_m) + log_error("Buffer %s not connected to port.\n", ci->name.c_str(ctx)); + packed_cells.insert(nxb_m->name); + if (nxb_s) + packed_cells.insert(nxb_s->name); + + // Merge UCF constraints into the buffer. + for (const auto ¶m : nxb_m->params) + ci->params[param.first] = param.second; + for (const auto &attr : nxb_m->attrs) + ci->attrs[attr.first] = attr.second; + + std::string iostd; + auto iostd_attr = ci->params.find(ctx->id("IOSTANDARD")); + if (iostd_attr != ci->params.end()) { + iostd = iostd_attr->second.as_string(); + } else { + // Hm. + if (diff) + iostd = "LVDS_33"; + else + iostd = "LVCMOS33"; + } + if (iostds.find(iostd) == iostds.end()) + log_error("Unknown IO standard %s for buffer %s", iostd.c_str(), ci->name.c_str(ctx)); + enum IoStdKind kind = iostds[iostd]; + if (kind == IOSTD_PSEUDO_DIFF || kind == IOSTD_DIFF) { + diff = true; + } else { + if (diff) { + log_error("Single-ended IO standard %s for differential buffer %s", iostd.c_str(), ci->name.c_str(ctx)); + } + } + + // Create the buffer cells. + std::unique_ptr iobm_cell = + create_leuctra_cell(ctx, ctx->id("IOB"), ci->name.str(ctx) + "$iob"); + new_cells.push_back(std::move(iobm_cell)); + CellInfo *iobm = new_cells.back().get(); + CellInfo *iobs = nullptr; + if (diff) { + std::unique_ptr iobs_cell = + create_leuctra_cell(ctx, ctx->id("IOB"), ci->name.str(ctx) + "$iobs"); + new_cells.push_back(std::move(iobm_cell)); + iobs = new_cells.back().get(); + if (kind == IOSTD_DIFF) { + iobm->params[ctx->id("__MODE__")] = Property("IOBM"); + iobs->params[ctx->id("__MODE__")] = Property("IOBS"); + } else { + iobm->params[ctx->id("__MODE__")] = Property("IOB"); + iobs->params[ctx->id("__MODE__")] = Property("IOB"); + } + } else { + iobm->params[ctx->id("__MODE__")] = Property("IOB"); + } + + // Deal with input path. + if (port_i != IdString()) { + replace_port(ci, port_i, iobm, ctx->id("I")); + if (diff) { + iobs->params[ctx->id("PADOUTUSED")] = Property("0"); + connect_ports(ctx, iobs, ctx->id("PADOUT"), iobm, ctx->id("DIFFI_IN")); + } + iobm->params[ctx->id("IMUX")] = Property("I"); + iobm->params[ctx->id("BYPASS_MUX")] = Property("I"); + iobm->params[ctx->id("ISTANDARD")] = Property(iostd); + if (iobs) + iobm->params[ctx->id("ISTANDARD")] = Property(iostd); + } + if (port_ib != IdString()) { + replace_port(ci, port_ib, iobs, ctx->id("I")); + if (diff) { + iobm->params[ctx->id("PADOUTUSED")] = Property("0"); + connect_ports(ctx, iobm, ctx->id("PADOUT"), iobs, ctx->id("DIFFI_IN")); + } + iobs->params[ctx->id("IMUX")] = Property("I"); + iobs->params[ctx->id("BYPASS_MUX")] = Property("I"); + } + + // Deal with output path. + if (port_o != IdString()) { + NetInfo *net_o = ci->ports.at(port_o).net; + NetInfo *net_t = nullptr; + if (port_t != IdString()) + net_t = ci->ports.at(port_t).net; + connect_port(ctx, net_o, iobm, ctx->id("O")); + if (kind == IOSTD_PSEUDO_DIFF) { + // XXX + NPNR_ASSERT_FALSE("PSEUDO DIFF OUTPUT"); + connect_port(ctx, net_o, iobs, ctx->id("O")); + } + disconnect_port(ctx, ci, port_o); + if (net_t) { + connect_port(ctx, net_t, iobm, ctx->id("T")); + if (kind == IOSTD_PSEUDO_DIFF) + connect_port(ctx, net_t, iobs, ctx->id("T")); + disconnect_port(ctx, ci, port_t); + } + iobm->params[ctx->id("OSTANDARD")] = Property(iostd); + if (iobs) + iobs->params[ctx->id("OSTANDARD")] = Property(iostd); + + iobm->params[ctx->id("OUSED")] = Property("0"); + if (port_t != IdString()) + iobm->params[ctx->id("TUSED")] = Property("0"); + + if (kind == IOSTD_PSEUDO_DIFF) { + iobs->params[ctx->id("OUSED")] = Property("0"); + if (port_t != IdString()) + iobs->params[ctx->id("TUSED")] = Property("0"); + } + + if (kind == IOSTD_SINGLE_DRIVE) { + if (ci->params.count(ctx->id("DRIVE"))) + iobm->params[ctx->id("DRIVEATTRBOX")] = ci->params[ctx->id("DRIVE")]; + else + iobm->params[ctx->id("DRIVEATTRBOX")] = Property("12"); + if (ci->params.count(ctx->id("SLEW"))) + iobm->params[ctx->id("SLEW")] = ci->params[ctx->id("SLEW")]; + else + iobm->params[ctx->id("SLEW")] = Property("SLOW"); + } + + if (kind == IOSTD_DIFF) { + connect_ports(ctx, iobm, ctx->id("DIFFO_OUT"), iobs, ctx->id("DIFFO_IN")); + iobs->params[ctx->id("OUTMUX")] = Property("0"); + } + } + + if (ci->params.count(ctx->id("PULLTYPE"))) + iobm->params[ctx->id("PULLTYPE")] = ci->params[ctx->id("PULLTYPE")]; + if (ci->params.count(ctx->id("SUSPEND"))) + iobm->params[ctx->id("SUSPEND")] = ci->params[ctx->id("SUSPEND")]; + if (ci->params.count(ctx->id("PRE_EMPHASIS"))) + iobm->params[ctx->id("PRE_EMPHASIS")] = ci->params[ctx->id("PRE_EMPHASIS")]; + + auto loc_attr = ci->attrs.find(ctx->id("LOC")); + if (loc_attr != ci->attrs.end()) { + std::string pin = loc_attr->second.as_string(); + BelId pinBel = ctx->getPackagePinBel(pin); + if (pinBel == BelId()) { + log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n", + ci->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str()); + } else { + log_info("pin '%s' constrained to Bel '%s'.\n", ci->name.c_str(ctx), + ctx->getBelName(pinBel).c_str(ctx)); + } + iobm->attrs[ctx->id("BEL")] = Property(ctx->getBelName(pinBel).str(ctx)); + if (iobs) { + BelId opinBel = pinBel; + if ((pinBel.index & 1) && kind == IOSTD_DIFF) { + log_error("True differential IO pin '%s' constrained to pin '%s', which is not a master pin.\n", + ci->name.c_str(ctx), pin.c_str()); + } + opinBel.index ^= 1; + iobs->attrs[ctx->id("BEL")] = Property(ctx->getBelName(opinBel).str(ctx)); + } + } else { + if (iobs) { + // XXX + NPNR_ASSERT_FALSE("UNCONSTRAINED DIFF PAIR"); + } + } + + packed_cells.insert(ci->name); + } + + flush_cells(); + } + // Ensure ilogic/ologic cell for every IOB that needs one. void pack_iologic() { @@ -122,10 +468,10 @@ class LeuctraPacker } auto bel_attr = ci->attrs.find(ctx->id("BEL")); if (bel_attr != ci->attrs.end()) { - BelId bel = ctx->getBelByName(ctx->id(ci->attrs[ctx->id("BEL")])); + BelId bel = ctx->getBelByName(ctx->id(ci->attrs[ctx->id("BEL")].as_string())); for (auto &child : ci->constr_children) { BelId child_bel = ctx->getRelatedBel(bel, child->constr_spec); - child->attrs[ctx->id("BEL")] = ctx->getBelName(child_bel).str(ctx); + child->attrs[ctx->id("BEL")] = Property(ctx->getBelName(child_bel).str(ctx)); child->constr_parent = nullptr; child->constr_spec = -1; @@ -138,6 +484,23 @@ class LeuctraPacker flush_cells(); } + // Convert FFs/latches to LEUCTRA_FFs. + void pack_bram() + { + log_info("Packing Block RAMs...\n"); + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == ctx->id("RAMB16BWER")) { + fixup_ramb16(ctx, ci, new_cells, packed_cells); + } else if (ci->type == ctx->id("RAMB8BWER")) { + fixup_ramb8(ctx, ci, new_cells, packed_cells); + } + } + + flush_cells(); + } + // Convert FFs/latches to LEUCTRA_FFs. void pack_ff() { @@ -145,7 +508,18 @@ class LeuctraPacker for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; - if (is_xilinx_ff(ctx, ci)) { + if (ci->type == ctx->id("FDRE") || + ci->type == ctx->id("FDSE") || + ci->type == ctx->id("FDCE") || + ci->type == ctx->id("FDPE") || + ci->type == ctx->id("FDRE_1") || + ci->type == ctx->id("FDSE_1") || + ci->type == ctx->id("FDCE_1") || + ci->type == ctx->id("FDPE_1") || + ci->type == ctx->id("LDCE") || + ci->type == ctx->id("LDPE") || + ci->type == ctx->id("LDCE_1") || + ci->type == ctx->id("LDPE_1")) { std::unique_ptr ff_cell = create_leuctra_cell(ctx, ctx->id("LEUCTRA_FF"), ci->name.str(ctx) + "$ff"); convert_ff(ctx, ci, ff_cell.get(), new_cells, packed_cells); @@ -158,7 +532,289 @@ class LeuctraPacker flush_cells(); } - // Convert FFs/latches to LEUCTRA_FFs. + // Convert RAMs to LEUCTRA_LCs. + void pack_ram() + { + log_info("Packing distributed RAM...\n"); + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == ctx->id("RAM32X1D") || ci->type == ctx->id("RAM64X1D")) { + int sz = ci->type == ctx->id("RAM32X1D") ? 5 : 6; + CellInfo *lcs[2]; + for (int i = 0; i < 2; i++) { + std::unique_ptr lut_cell = + create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), ci->name.str(ctx) + "$lc" + std::to_string(i)); + new_cells.push_back(std::move(lut_cell)); + lcs[i] = new_cells.back().get(); + lcs[i]->params[ctx->id("MODE")] = Property("RAM" + std::to_string(sz)); + if (sz == 6) + lcs[i]->params[ctx->id("DIMUX")] = Property("XI"); + lcs[i]->attrs[ctx->id("NEEDS_M")] = Property(true); + } + NetInfo *net; + bool net_inv; + if (get_invertible_port(ctx, ci, ctx->id("WCLK"), false, true, net, net_inv)) { + set_invertible_port(ctx, lcs[0], ctx->id("CLK"), net, net_inv, true, new_cells); + set_invertible_port(ctx, lcs[1], ctx->id("CLK"), net, net_inv, true, new_cells); + } + net = ci->ports[ctx->id("WE")].net; + disconnect_port(ctx, ci, ctx->id("WE")); + connect_port(ctx,net, lcs[0], ctx->id("WE")); + connect_port(ctx,net, lcs[1], ctx->id("WE")); + for (int i = 0; i < sz; i++) { + IdString pname = ctx->id("A" + std::to_string(i)); + net = ci->ports[pname].net; + disconnect_port(ctx, ci, pname); + connect_port(ctx,net, lcs[0], ctx->id("WA" + std::to_string(i+1))); + connect_port(ctx,net, lcs[1], ctx->id("WA" + std::to_string(i+1))); + connect_port(ctx,net, lcs[1], ctx->id("RA" + std::to_string(i+1))); + pname = ctx->id("DPRA" + std::to_string(i)); + net = ci->ports[pname].net; + disconnect_port(ctx, ci, pname); + connect_port(ctx,net, lcs[0], ctx->id("RA" + std::to_string(i+1))); + } + net = ci->ports[ctx->id("D")].net; + disconnect_port(ctx, ci, ctx->id("D")); + if (sz == 5) { + connect_port(ctx,net, lcs[0], ctx->id("DDI5")); + connect_port(ctx,net, lcs[1], ctx->id("DDI5")); + set_const_port(ctx, lcs[0], ctx->id("RA6"), true, new_cells); + set_const_port(ctx, lcs[1], ctx->id("RA6"), true, new_cells); + } else { + connect_port(ctx,net, lcs[0], ctx->id("XI")); + connect_port(ctx,net, lcs[1], ctx->id("XI")); + } + replace_port(ci, ctx->id("SPO"), lcs[1], ctx->id("O6")); + replace_port(ci, ctx->id("DPO"), lcs[0], ctx->id("O6")); + Property v = ci->params[ctx->id("INIT")]; + Property nv(0, 64); + if (sz == 5) { + for (int i = 0; i < 64; i++) + nv.str[i] = v.str[i%32]; + nv.update_intval(); + } else { + nv = v; + } + lcs[0]->params[ctx->id("INIT")] = nv; + lcs[1]->params[ctx->id("INIT")] = nv; + lcs[1]->attrs[ctx->id("LOCMASK")] = Property(0x8, 4); + lcs[0]->constr_parent = lcs[1]; + lcs[0]->constr_z = -3; + lcs[1]->constr_children.push_back(lcs[0]); + packed_cells.insert(ci->name); + } else if (ci->type == ctx->id("RAM128X1D")) { + CellInfo *lcs[4]; + for (int i = 0; i < 4; i++) { + std::unique_ptr lut_cell = + create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), ci->name.str(ctx) + "$lc" + std::to_string(i)); + new_cells.push_back(std::move(lut_cell)); + lcs[i] = new_cells.back().get(); + lcs[i]->params[ctx->id("MODE")] = Property("RAM7"); + lcs[i]->params[ctx->id("DIMUX")] = Property("DDI7"); + lcs[i]->attrs[ctx->id("NEEDS_M")] = Property(true); + } + NetInfo *net; + bool net_inv; + if (get_invertible_port(ctx, ci, ctx->id("WCLK"), false, true, net, net_inv)) { + for (int i = 0; i < 4; i++) + set_invertible_port(ctx, lcs[i], ctx->id("CLK"), net, net_inv, true, new_cells); + } + net = ci->ports[ctx->id("WE")].net; + disconnect_port(ctx, ci, ctx->id("WE")); + for (int i = 0; i < 4; i++) + connect_port(ctx,net, lcs[i], ctx->id("WE")); + for (int i = 0; i < 7; i++) { + IdString pname = ctx->id("A[" + std::to_string(i) + "]"); + net = ci->ports[pname].net; + disconnect_port(ctx, ci, pname); + for (int j = 0; j < 4; j++) + connect_port(ctx,net, lcs[j], ctx->id("WA" + std::to_string(i+1))); + if (i < 6) { + for (int j = 2; j < 4; j++) + connect_port(ctx,net, lcs[j], ctx->id("RA" + std::to_string(i+1))); + } else { + connect_port(ctx,net, lcs[2], ctx->id("XI")); + } + + pname = ctx->id("DPRA[" + std::to_string(i) + "]"); + net = ci->ports[pname].net; + disconnect_port(ctx, ci, pname); + if (i < 6) { + for (int j = 0; j < 2; j++) + connect_port(ctx,net, lcs[j], ctx->id("RA" + std::to_string(i+1))); + } else { + connect_port(ctx,net, lcs[0], ctx->id("XI")); + } + } + net = ci->ports[ctx->id("D")].net; + disconnect_port(ctx, ci, ctx->id("D")); + for (int i = 0; i < 4; i++) + connect_port(ctx,net, lcs[i], ctx->id("DDI7")); + replace_port(ci, ctx->id("SPO"), lcs[2], ctx->id("MO")); + replace_port(ci, ctx->id("DPO"), lcs[0], ctx->id("MO")); + + connect_ports(ctx, lcs[3], ctx->id("O6"), lcs[2], ctx->id("DMI0")); + connect_ports(ctx, lcs[2], ctx->id("O6"), lcs[2], ctx->id("DMI1")); + connect_ports(ctx, lcs[1], ctx->id("O6"), lcs[0], ctx->id("DMI0")); + connect_ports(ctx, lcs[0], ctx->id("O6"), lcs[0], ctx->id("DMI1")); + + Property v = ci->params[ctx->id("INIT")]; + Property nv(0, 64); + for (int i = 0; i < 64; i++) + nv.str[i] = v.str[i]; + nv.update_intval(); + lcs[3]->params[ctx->id("INIT")] = nv; + lcs[1]->params[ctx->id("INIT")] = nv; + for (int i = 0; i < 64; i++) + nv.str[i] = v.str[i + 64]; + nv.update_intval(); + lcs[2]->params[ctx->id("INIT")] = nv; + lcs[0]->params[ctx->id("INIT")] = nv; + + lcs[3]->attrs[ctx->id("LOCMASK")] = Property(0x8, 4); + for (int i = 0; i < 3; i++) { + lcs[i]->constr_parent = lcs[3]; + lcs[i]->constr_z = (i - 3) * 3; + lcs[3]->constr_children.push_back(lcs[i]); + } + packed_cells.insert(ci->name); + } else if (ci->type == ctx->id("RAM32M")) { + CellInfo *lcs[4]; + for (int i = 0; i < 4; i++) { + std::unique_ptr lut_cell = + create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), ci->name.str(ctx) + "$lc" + std::to_string(i)); + new_cells.push_back(std::move(lut_cell)); + lcs[i] = new_cells.back().get(); + lcs[i]->params[ctx->id("MODE")] = Property("RAM5"); + lcs[i]->params[ctx->id("DIMUX")] = Property("XI"); + lcs[i]->attrs[ctx->id("NEEDS_M")] = Property(true); + } + NetInfo *net; + bool net_inv; + if (get_invertible_port(ctx, ci, ctx->id("WCLK"), false, true, net, net_inv)) { + for (int i = 0; i < 4; i++) + set_invertible_port(ctx, lcs[i], ctx->id("CLK"), net, net_inv, true, new_cells); + } + net = ci->ports[ctx->id("WE")].net; + disconnect_port(ctx, ci, ctx->id("WE")); + for (int i = 0; i < 4; i++) + connect_port(ctx,net, lcs[i], ctx->id("WE")); + for (int i = 0; i < 4; i++) { + std::string l{"ABCD"[i]}; + for (int j = 0; j < 5; j++) { + IdString pname = ctx->id("ADDR" + l + "[" + std::to_string(j) + "]"); + net = ci->ports[pname].net; + disconnect_port(ctx, ci, pname); + if (i == 3) { + for (int k = 0; k < 4; j++) + connect_port(ctx,net, lcs[k], ctx->id("WA" + std::to_string(j+1))); + } + connect_port(ctx,net, lcs[i], ctx->id("RA" + std::to_string(j+1))); + } + set_const_port(ctx, lcs[i], ctx->id("RA6"), true, new_cells); + replace_port(ci, ctx->id("DI" + l + "[0]"), lcs[i], ctx->id("XI")); + replace_port(ci, ctx->id("DI" + l + "[1]"), lcs[i], ctx->id("DDI5")); + replace_port(ci, ctx->id("DO" + l + "[0]"), lcs[i], ctx->id("O5")); + replace_port(ci, ctx->id("DO" + l + "[1]"), lcs[i], ctx->id("O6")); + + Property v = ci->params[ctx->id("INIT_" + l)]; + Property nv(0, 64); + for (int j = 0; j < 32; j++) + for (int k = 0; k < 2; k++) + nv.str[j | k << 5] = v.str[j << 1 | k]; + nv.update_intval(); + lcs[i]->params[ctx->id("INIT")] = nv; + } + + lcs[3]->attrs[ctx->id("LOCMASK")] = Property(0x8, 4); + for (int i = 0; i < 3; i++) { + lcs[i]->constr_parent = lcs[3]; + lcs[i]->constr_z = (i - 3) * 3; + lcs[3]->constr_children.push_back(lcs[i]); + } + packed_cells.insert(ci->name); + } + } + + flush_cells(); + } + + // Convert CARRY4s to LEUCTRA_LCs. + void pack_carry() + { + log_info("Packing CARRY4s...\n"); + + // CARRY4 -> next CARRY4 in the chain + std::unordered_map chain; + // CARRY4s that are chain starts. + std::vector init; + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == ctx->id("CARRY4")) { + NetInfo *net = ci->ports.at(ctx->id("CI")).net; + CellInfo *prev = nullptr; + if (net) + prev = net->driver.cell; + bool cval; + if (!prev || get_const_val(ctx, net, cval)) { + init.push_back(ci); + } else { + if (prev->type != ctx->id("CARRY4") || net->driver.port != ctx->id("CO[3]")) + log_error("CARRY4 %s has weird CI: %s (%s) %s", ci->name.c_str(ctx), prev->name.c_str(ctx), prev->type.c_str(ctx), net->driver.port.c_str(ctx)); + if (chain.count(prev)) + log_error("Split carry chain: %s %s %s", prev->name.c_str(ctx), ci->name.c_str(ctx), chain[prev]->name.c_str(ctx)); + chain[prev] = ci; + } + } + } + + for (auto cell : init) { + CellInfo *cur = cell; + CellInfo *link = nullptr; + while (cur) { + link = convert_carry4(ctx, cur, link, new_cells, packed_cells); + cur = chain[cur]; + } + } + + flush_cells(); + } + + // Convert MUXF8s to LEUCTRA_LCs. + void pack_muxf8() + { + log_info("Packing MUXF8s...\n"); + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == ctx->id("MUXF8")) { + NetInfo *net = ci->ports.at(ctx->id("O")).net; + convert_muxf8(ctx, net, ci->name.str(ctx) + "$lc", new_cells, packed_cells); + } + } + + flush_cells(); + } + + // Convert MUXF7s to LEUCTRA_LCs. + void pack_muxf7() + { + log_info("Packing MUXF7s...\n"); + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == ctx->id("MUXF7")) { + NetInfo *net = ci->ports.at(ctx->id("O")).net; + convert_muxf7(ctx, net, ci->name.str(ctx) + "$lc", new_cells, packed_cells); + } + } + + flush_cells(); + } + + // Convert LUTs to LEUCTRA_LCs. void pack_lut() { log_info("Packing LUTs...\n"); @@ -166,12 +822,26 @@ class LeuctraPacker for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_xilinx_lut(ctx, ci)) { - std::unique_ptr lut_cell = - create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), ci->name.str(ctx) + "$lc"); - convert_lut(ctx, ci, lut_cell.get(), new_cells, packed_cells); - new_cells.push_back(std::move(lut_cell)); + NetInfo *net = ci->ports.at(ctx->id("O")).net; + convert_lut(ctx, net, ci->name.str(ctx) + "$lc", new_cells, packed_cells); + } + } - packed_cells.insert(ci->name); + flush_cells(); + } + + // Convert misc cell types. + void pack_misc() + { + log_info("Converting misc cell types...\n"); + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == ctx->id("BUFG")) { + ci->type = ctx->id("BUFGMUX"); + rename_port(ctx, ci, ctx->id("I"), ctx->id("I0")); + set_const_port(ctx, ci, ctx->id("S"), true, new_cells); + ci->params[ctx->id("SINV")] = Property("S_B"); } } @@ -200,20 +870,22 @@ class LeuctraPacker log_info("Packing constants..\n"); std::unique_ptr gnd_cell = create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), "$PACKER_GND"); - gnd_cell->params[ctx->id("INIT")] = "0000000000000000000000000000000000000000000000000000000000000000"; + gnd_cell->params[ctx->id("INIT")] = Property(0, 64); std::unique_ptr gnd_net = std::unique_ptr(new NetInfo); gnd_net->name = ctx->id("$PACKER_GND_NET"); gnd_net->driver.cell = gnd_cell.get(); gnd_net->driver.port = ctx->id("O6"); gnd_cell->ports.at(ctx->id("O6")).net = gnd_net.get(); + gnd_cell->attrs[ctx->id("CONST")] = Property(false); std::unique_ptr vcc_cell = create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), "$PACKER_VCC"); - vcc_cell->params[ctx->id("INIT")] = "1111111111111111111111111111111111111111111111111111111111111111"; + vcc_cell->params[ctx->id("INIT")] = Property(-1, 64); std::unique_ptr vcc_net = std::unique_ptr(new NetInfo); vcc_net->name = ctx->id("$PACKER_VCC_NET"); vcc_net->driver.cell = vcc_cell.get(); vcc_net->driver.port = ctx->id("O6"); vcc_cell->ports.at(ctx->id("O6")).net = vcc_net.get(); + vcc_cell->attrs[ctx->id("CONST")] = Property(true); std::vector dead_nets; @@ -253,10 +925,20 @@ class LeuctraPacker public: void pack() { - pack_iob(); + //insert_iob(); + convert_iob(); pack_iologic(); + pack_bram(); pack_ff(); + pack_ram(); + // pack_srl(); + pack_carry(); + pack_muxf8(); + pack_muxf7(); + // clean_inv(); pack_lut(); + // pack_lc_ff(); + pack_misc(); pack_constants(); } @@ -274,6 +956,7 @@ bool Arch::pack() try { log_break(); LeuctraPacker(ctx).pack(); + ctx->settings[ctx->id("pack")] = 1; log_info("Checksum: 0x%08x\n", ctx->checksum()); // XXX //assignArchInfo(); diff --git a/leuctra/project.cc b/leuctra/project.cc deleted file mode 100644 index d58abc7a..00000000 --- a/leuctra/project.cc +++ /dev/null @@ -1,47 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Miodrag Milanovic - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "project.h" -#include -#include -#include -#include "log.h" - -NEXTPNR_NAMESPACE_BEGIN - -void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path) -{ - root.put("project.arch.package", ctx->archArgs().package); - root.put("project.arch.speed", ctx->archArgs().speed); -} - -std::unique_ptr ProjectHandler::createContext(pt::ptree &root) -{ - ArchArgs chipArgs; - chipArgs.device = root.get("project.arch.type"); - chipArgs.package = root.get("project.arch.package"); - chipArgs.speed = root.get("project.arch.speed"); - - return std::unique_ptr(new Context(chipArgs)); -} - -void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {} - -NEXTPNR_NAMESPACE_END - diff --git a/leuctra/textcfg.cc b/leuctra/textcfg.cc index 3503513d..6dc91afa 100644 --- a/leuctra/textcfg.cc +++ b/leuctra/textcfg.cc @@ -32,16 +32,21 @@ void write_textcfg(const Context *ctx, std::ostream &out) auto &bel = ctx->getTileTypeBel(belid); auto name = IdString(bel.name_id); out << "PRIM " << belid.location.x << " " << belid.location.y << " " << name.str(ctx) << " " << cell.second->name.str(ctx) << std::endl; - for (auto &attr : cell.second->attrs) { - out << "OPT " << attr.first.str(ctx) << " " << attr.second << std::endl; - } - for (auto ¶m : cell.second->params) { - out << "OPT " << param.first.str(ctx) << " " << param.second << std::endl; + for (auto &attr : cell.second->params) { + if (attr.second.is_string) + out << "PARAMSTR " << attr.first.str(ctx) << " " << attr.second.as_string() << std::endl; + else { + std::string sv; + for (auto bit: attr.second.as_bits()) + sv.push_back(bit ? '1' : '0'); + out << "PARAM " << attr.first.str(ctx) << " " << sv << std::endl; + } } } for (auto &net : ctx->nets) { out << "NET " << net.second->name.str(ctx) << std::endl; - out << "FROM " << net.second->driver.cell->name.str(ctx) << " " << net.second->driver.port.str(ctx) << std::endl; + if (net.second->driver.cell) + out << "FROM " << net.second->driver.cell->name.str(ctx) << " " << net.second->driver.port.str(ctx) << std::endl; for (auto &user : net.second->users) { out << "TO " << user.cell->name.str(ctx) << " " << user.port.str(ctx) << std::endl; } diff --git a/leuctra/ucf.cc b/leuctra/ucf.cc index 735b4a0c..22538971 100644 --- a/leuctra/ucf.cc +++ b/leuctra/ucf.cc @@ -65,7 +65,7 @@ bool Arch::applyUCF(std::string filename, std::istream &in) if (words.size() >= 0) { std::string verb = words.at(0); if (verb == "CONFIG") { - log_warning(" ignoring unsupported LPF command '%s' (on line %d)\n", command.c_str(), + log_warning(" ignoring unsupported UCF command '%s' (on line %d)\n", command.c_str(), lineno); } else if (verb == "NET") { if (words.size() < 2) @@ -82,12 +82,15 @@ bool Arch::applyUCF(std::string filename, std::istream &in) pos += 2; auto fnd_cell = cells.find(id(target)); if (fnd_cell != cells.end()) { - fnd_cell->second->attrs[id(attr)] = value; + if (attr == "LOC") + fnd_cell->second->attrs[id(attr)] = value; + else + fnd_cell->second->params[id(attr)] = value; } } else if (attr == "PULLUP" || attr == "PULLDOWN" || attr == "KEEPER") { auto fnd_cell = cells.find(id(target)); if (fnd_cell != cells.end()) { - fnd_cell->second->attrs[id("PULLTYPE")] = attr; + fnd_cell->second->params[id("PULLTYPE")] = attr; } } else if (attr == "PERIOD") { if (pos + 2 > words.size() || words.at(pos) != "=")