diff --git a/ecp5/arch.cc b/ecp5/arch.cc index d887aa69..371dbb12 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -329,6 +329,44 @@ std::string Arch::getBelPackagePin(BelId bel) const return ""; } +int Arch::getPioBelBank(BelId bel) const +{ + for (int i = 0; i < chip_info->num_pios; i++) { + if (chip_info->pio_info[i].abs_loc == bel.location && chip_info->pio_info[i].bel_index == bel.index) { + return chip_info->pio_info[i].bank; + } + } + NPNR_ASSERT_FALSE("failed to find PIO"); +} + +std::string Arch::getPioFunctionName(BelId bel) const +{ + for (int i = 0; i < chip_info->num_pios; i++) { + if (chip_info->pio_info[i].abs_loc == bel.location && chip_info->pio_info[i].bel_index == bel.index) { + const char *func = chip_info->pio_info[i].function_name.get(); + if (func == nullptr) + return ""; + else + return func; + } + } + NPNR_ASSERT_FALSE("failed to find PIO"); +} + +BelId Arch::getPioByFunctionName(const std::string &name) const +{ + for (int i = 0; i < chip_info->num_pios; i++) { + const char *func = chip_info->pio_info[i].function_name.get(); + if (func != nullptr && func == name) { + BelId bel; + bel.location = chip_info->pio_info[i].abs_loc; + bel.index = chip_info->pio_info[i].bel_index; + return bel; + } + } + return BelId(); +} + std::vector Arch::getBelPins(BelId bel) const { diff --git a/ecp5/arch.h b/ecp5/arch.h index 38362d6b..c2efb2bd 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -733,6 +733,10 @@ struct Arch : BaseCtx BelId getPackagePinBel(const std::string &pin) const; std::string getBelPackagePin(BelId bel) const; + int getPioBelBank(BelId bel) const; + // For getting GCLK, PLL, Vref, etc, pins + std::string getPioFunctionName(BelId bel) const; + BelId getPioByFunctionName(const std::string &name) const; PortType getBelPinType(BelId bel, PortPin pin) const; diff --git a/ecp5/io.cc b/ecp5/io.cc new file mode 100644 index 00000000..e86c84f6 --- /dev/null +++ b/ecp5/io.cc @@ -0,0 +1,217 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * 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 "io.h" + +NEXTPNR_NAMESPACE_BEGIN + +std::string iovoltage_to_str(IOVoltage v) +{ + switch (v) { + case IOVoltage::VCC_3V3: + return "3V3"; + case IOVoltage::VCC_2V5: + return "2V5"; + case IOVoltage::VCC_1V8: + return "1V8"; + case IOVoltage::VCC_1V5: + return "1V5"; + case IOVoltage::VCC_1V35: + return "1V35"; + case IOVoltage::VCC_1V2: + return "1V2"; + } + NPNR_ASSERT_FALSE("unknown IO voltage"); +} + +IOVoltage iovoltage_from_str(const std::string &name) +{ + if (name == "3V3") + return IOVoltage::VCC_3V3; + if (name == "2V5") + return IOVoltage::VCC_2V5; + if (name == "1V8") + return IOVoltage::VCC_1V8; + if (name == "1V5") + return IOVoltage::VCC_1V5; + if (name == "1V35") + return IOVoltage::VCC_1V35; + if (name == "1V2") + return IOVoltage::VCC_1V2; + NPNR_ASSERT_FALSE("unknown IO voltage"); +} + +std::string iotype_to_str(IOType type) +{ + if (type == IOType::TYPE_NONE) + return "NONE"; +#define X(t) \ + if (type == IOType::t) \ + return #t; +#include "iotypes.inc" +#undef X + if (type == IOType::TYPE_UNKNOWN) + return ""; + NPNR_ASSERT_FALSE("unknown IO type"); +} + +IOType ioType_from_str(const std::string &name) +{ + if (name == "NONE") + return IOType::TYPE_NONE; +#define X(t) \ + if (name == #t) \ + return IOType::t; +#include "iotypes.inc" + return IOType::TYPE_UNKNOWN; +} + +IOVoltage get_vccio(IOType type) +{ + switch (type) { + case IOType::LVTTL33: + case IOType::LVCMOS33: + case IOType::LVCMOS33D: + case IOType::LVPECL33: + case IOType::LVPECL33E: + return IOVoltage::VCC_3V3; + case IOType::LVCMOS25: + case IOType::LVCMOS25D: + case IOType::LVDS: + case IOType::SLVS: + case IOType::SUBLVDS: + case IOType::LVDS25E: + case IOType::MLVDS25: + case IOType::MLVDS25E: + case IOType::BLVDS25: + return IOVoltage::VCC_2V5; + case IOType::LVCMOS18: + case IOType::LVCMOS18D: + case IOType::SSTL18_I: + case IOType::SSTL18_II: + case IOType::SSTL18D_I: + case IOType::SSTL18D_II: + return IOVoltage::VCC_1V8; + case IOType::LVCMOS15: + case IOType::SSTL15_I: + case IOType::SSTL15_II: + case IOType::SSTL15D_I: + case IOType::SSTL15D_II: + return IOVoltage::VCC_1V5; + case IOType::SSTL135_I: + case IOType::SSTL135_II: + case IOType::SSTL135D_I: + case IOType::SSTL135D_II: + return IOVoltage::VCC_1V35; + case IOType::LVCMOS12: + case IOType::HSUL12: + case IOType::HSUL12D: + return IOVoltage::VCC_1V2; + default: + NPNR_ASSERT_FALSE("unknown IO type, unable to determine VccIO"); + } +} + +bool is_strong_vccio_constraint(IOType type, PortType dir, IOSide side) +{ + if (dir == PORT_OUT || dir == PORT_INOUT) + return true; + switch (type) { + case IOType::TYPE_NONE: + case IOType::LVCMOS33D: + case IOType::LVPECL33: + case IOType::LVDS: + case IOType::MLVDS25: + case IOType::BLVDS25: + case IOType::SLVS: + case IOType::SUBLVDS: + case IOType::LVCMOS12: + case IOType::HSUL12: + case IOType::HSUL12D: + return false; + case IOType::LVCMOS33: + case IOType::LVTTL33: + case IOType::LVCMOS25: + return (side == IOSide::LEFT || side == IOSide::RIGHT); + default: + return true; + } +} + +bool is_differential(IOType type) +{ + switch (type) { + case IOType::LVCMOS33D: + case IOType::LVCMOS25D: + case IOType::LVPECL33: + case IOType::LVDS: + case IOType::MLVDS25: + case IOType::BLVDS25: + case IOType::SLVS: + case IOType::SUBLVDS: + case IOType::LVCMOS18D: + case IOType::SSTL18D_I: + case IOType::SSTL18D_II: + case IOType::SSTL15D_I: + case IOType::SSTL15D_II: + case IOType::SSTL135D_I: + case IOType::SSTL135D_II: + case IOType::HSUL12D: + return true; + default: + return false; + } +} + +bool is_referenced(IOType type) +{ + switch (type) { + case IOType::SSTL18_I: + case IOType::SSTL18_II: + case IOType::SSTL18D_I: + case IOType::SSTL18D_II: + case IOType::SSTL15_I: + case IOType::SSTL15_II: + case IOType::SSTL15D_I: + case IOType::SSTL15D_II: + case IOType::SSTL135_I: + case IOType::SSTL135_II: + case IOType::SSTL135D_I: + case IOType::SSTL135D_II: + case IOType::HSUL12: + case IOType::HSUL12D: + return true; + default: + return false; + } +} + +bool valid_loc_for_io(IOType type, PortType dir, IOSide side, int z) +{ + bool is_lr = side == IOSide::LEFT || side == IOSide::RIGHT; + if (is_referenced(type) && !is_lr) + return false; + if (is_differential(type) && (!is_lr || ((z % 2) == 1))) + return false; + if ((type == IOType::LVCMOS18D || type == IOType::LVDS) && (dir == PORT_OUT || PORT_INOUT) && z != 0) + return false; + return true; +} + +NEXTPNR_NAMESPACE_END diff --git a/ecp5/io.h b/ecp5/io.h new file mode 100644 index 00000000..f0d941d9 --- /dev/null +++ b/ecp5/io.h @@ -0,0 +1,70 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * 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. + * + */ + +#ifndef IO_H +#define IO_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +enum class IOVoltage +{ + VCC_3V3, + VCC_2V5, + VCC_1V8, + VCC_1V5, + VCC_1V35, + VCC_1V2 +}; + +std::string iovoltage_to_str(IOVoltage v); +IOVoltage iovoltage_from_str(const std::string &name); + +enum class IOType +{ + TYPE_NONE, +#define X(t) t, +#include "iotypes.inc" +#undef X + TYPE_UNKNOWN, +}; + +enum class IOSide +{ + LEFT, + RIGHT, + TOP, + BOTTOM, +}; + +std::string iotype_to_str(IOType type); +IOType ioType_from_str(const std::string &name); + +// IO related functions +IOVoltage get_vccio(IOType type); +bool is_strong_vccio_constraint(IOType type, PortType dir, IOSide side); +bool is_differential(IOType type); +bool is_referenced(IOType type); + +bool valid_loc_for_io(IOType type, PortType dir, IOSide side, int z); + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/ecp5/iotypes.inc b/ecp5/iotypes.inc new file mode 100644 index 00000000..5984e79e --- /dev/null +++ b/ecp5/iotypes.inc @@ -0,0 +1,37 @@ +X(LVTTL33) +X(LVCMOS33) +X(LVCMOS25) +X(LVCMOS18) +X(LVCMOS15) +X(LVCMOS12) + +X(SSTL18_I) +X(SSTL18_II) +X(SSTL15_I) +X(SSTL15_II) +X(SSTL135_I) +X(SSTL135_II) +X(HSUL12) + +X(SSTL18D_I) +X(SSTL18D_II) +X(SSTL135D_I) +X(SSTL135D_II) +X(SSTL15D_I) +X(SSTL15D_II) +X(HSUL12D) +X(LVCMOS33D) +X(LVCMOS25D) + +X(LVDS) +X(BLVDS25) +X(MLVDS25) +X(LVPECL33) +X(SLVS) +X(SUBLVDS) +X(LVCMOS18D) + +X(LVDS25E) +X(BLVDS25E) +X(MLVDS25E) +X(LVPECL33E) diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 91ec5163..e63ee937 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -229,6 +229,7 @@ void DesignWidget::addToHistory(QTreeWidgetItem *item) void DesignWidget::newContext(Context *ctx) { + highlightSelected.clear(); treeWidget->clear(); // reset pointers since they are not valid after clear nets_root = nullptr; @@ -337,48 +338,78 @@ void DesignWidget::newContext(Context *ctx) for (auto pip : nameToItem[2].toStdMap()) { pip_root->addChild(pip.second); } + + nets_root = new QTreeWidgetItem(treeWidget); + nets_root->setText(0, "Nets"); + treeWidget->insertTopLevelItem(0, nets_root); + + cells_root = new QTreeWidgetItem(treeWidget); + cells_root->setText(0, "Cells"); + treeWidget->insertTopLevelItem(0, cells_root); + updateTree(); } void DesignWidget::updateTree() { - clearProperties(); - delete nets_root; - delete cells_root; - nameToItem[3].clear(); - nameToItem[4].clear(); + if (!ctx) + return; + clearProperties(); + + // treeWidget->setSortingEnabled(false); + + // Remove nets not existing any more + QMap::iterator i = nameToItem[3].begin(); + while (i != nameToItem[3].end()) { + QMap::iterator prev = i; + ++i; + if (ctx->nets.find(ctx->id(prev.key().toStdString())) == ctx->nets.end()) { + if (treeWidget->currentItem() == prev.value()) + treeWidget->setCurrentItem(nets_root); + if (highlightSelected.contains(prev.value())) + highlightSelected.remove(prev.value()); + delete prev.value(); + nameToItem[3].erase(prev); + } + } // Add nets to tree - nets_root = new QTreeWidgetItem(treeWidget); - nets_root->setText(0, "Nets"); - treeWidget->insertTopLevelItem(0, nets_root); - if (ctx) { - for (auto &item : ctx->nets) { - auto id = item.first; - QString name = QString(id.c_str(ctx)); + for (auto &item : ctx->nets) { + auto id = item.first; + QString name = QString(id.c_str(ctx)); + if (!nameToItem[3].contains(name)) { IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::NET, name, nullptr); + nets_root->addChild(newItem); nameToItem[3].insert(name, newItem); } } - for (auto item : nameToItem[3].toStdMap()) { - nets_root->addChild(item.second); - } + // Remove cells not existing any more + i = nameToItem[4].begin(); + while (i != nameToItem[4].end()) { + QMap::iterator prev = i; + ++i; + if (ctx->cells.find(ctx->id(prev.key().toStdString())) == ctx->cells.end()) { + if (treeWidget->currentItem() == prev.value()) + treeWidget->setCurrentItem(cells_root); + if (highlightSelected.contains(prev.value())) + highlightSelected.remove(prev.value()); + delete prev.value(); + nameToItem[4].erase(prev); + } + } // Add cells to tree - cells_root = new QTreeWidgetItem(treeWidget); - cells_root->setText(0, "Cells"); - treeWidget->insertTopLevelItem(0, cells_root); - if (ctx) { - for (auto &item : ctx->cells) { - auto id = item.first; - QString name = QString(id.c_str(ctx)); + for (auto &item : ctx->cells) { + auto id = item.first; + QString name = QString(id.c_str(ctx)); + if (!nameToItem[4].contains(name)) { IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::CELL, name, nullptr); + cells_root->addChild(newItem); nameToItem[4].insert(name, newItem); } } - for (auto item : nameToItem[4].toStdMap()) { - cells_root->addChild(item.second); - } + // treeWidget->sortByColumn(0, Qt::AscendingOrder); + // treeWidget->setSortingEnabled(true); } QtProperty *DesignWidget::addTopLevelProperty(const QString &id) { diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 9343419b..e21af678 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -289,6 +289,9 @@ FPGAViewWidget::~FPGAViewWidget() {} void FPGAViewWidget::newContext(Context *ctx) { ctx_ = ctx; + onSelectedArchItem(std::vector()); + for (int i = 0; i < 8; i++) + onHighlightGroupChanged(std::vector(), i); pokeRenderer(); } diff --git a/gui/ice40/worker.cc b/gui/ice40/worker.cc index 09093ec8..fd46ecad 100644 --- a/gui/ice40/worker.cc +++ b/gui/ice40/worker.cc @@ -109,7 +109,6 @@ void Worker::budget(double freq) Q_EMIT taskStarted(); try { ctx->target_freq = freq; - assign_budget(ctx); Q_EMIT budget_finish(true); } catch (WorkerInterruptionRequested) { Q_EMIT taskCanceled(); @@ -121,6 +120,8 @@ void Worker::place(bool timing_driven) Q_EMIT taskStarted(); try { ctx->timing_driven = timing_driven; + log_info("Assigned budget %0.2f MHz", ctx->target_freq / 1e6); + assign_budget(ctx); Q_EMIT place_finished(ctx->place()); } catch (WorkerInterruptionRequested) { Q_EMIT taskCanceled();