diff --git a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp index a92ab537..673252d2 100644 --- a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp +++ b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.cpp @@ -74,6 +74,7 @@ public: QtProperty *indexToProperty(const QModelIndex &index) const; QTreeWidgetItem *indexToItem(const QModelIndex &index) const; QtBrowserItem *indexToBrowserItem(const QModelIndex &index) const; + QtBrowserItem *itemToBrowserItem(QTreeWidgetItem *item) const { return m_itemToIndex.value(item); }; bool lastColumn(int column) const; void disableItem(QTreeWidgetItem *item) const; void enableItem(QTreeWidgetItem *item) const; @@ -1068,6 +1069,16 @@ void QtTreePropertyBrowser::editItem(QtBrowserItem *item) d_ptr->editItem(item); } +QTreeWidget *QtTreePropertyBrowser::treeWidget() const +{ + return d_ptr->treeWidget(); +} + +QtBrowserItem *QtTreePropertyBrowser::itemToBrowserItem(QTreeWidgetItem *item) +{ + return d_ptr->itemToBrowserItem(item); +} + #if QT_VERSION >= 0x040400 QT_END_NAMESPACE #endif diff --git a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.h b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.h index c5f7fa88..7bc96b69 100644 --- a/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.h +++ b/3rdparty/QtPropertyBrowser/src/qttreepropertybrowser.h @@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE #endif +class QTreeWidget; class QTreeWidgetItem; class QtTreePropertyBrowserPrivate; @@ -107,6 +108,9 @@ public: void editItem(QtBrowserItem *item); + //ADDED:miodrag + QTreeWidget *treeWidget() const; + QtBrowserItem *itemToBrowserItem(QTreeWidgetItem *item); Q_SIGNALS: void collapsed(QtBrowserItem *item); diff --git a/gui/base.qrc b/gui/base.qrc index 8f07aabe..bf21986b 100644 --- a/gui/base.qrc +++ b/gui/base.qrc @@ -9,5 +9,6 @@ resources/resultset_previous.png resources/resultset_next.png resources/resultset_last.png + resources/splash.png diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 0c7632ee..07b71105 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -61,15 +62,10 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr context, QWidget *parent setCentralWidget(centralWidget); - DesignWidget *designview = new DesignWidget(); + designview = new DesignWidget(); designview->setMinimumWidth(300); splitter_h->addWidget(designview); - connect(this, SIGNAL(contextChanged(Context *)), designview, SLOT(newContext(Context *))); - connect(this, SIGNAL(updateTreeView()), designview, SLOT(updateTree())); - - connect(designview, SIGNAL(info(std::string)), this, SLOT(writeInfo(std::string))); - tabWidget = new QTabWidget(); console = new PythonTab(); @@ -81,38 +77,64 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr context, QWidget *parent centralTabWidget->addTab(fpgaView, "Graphics"); connect(this, SIGNAL(contextChanged(Context *)), fpgaView, SLOT(newContext(Context *))); - connect(designview, SIGNAL(selected(std::vector)), fpgaView, SLOT(onSelectedArchItem(std::vector))); + connect(designview, SIGNAL(selected(std::vector)), fpgaView, + SLOT(onSelectedArchItem(std::vector))); + + connect(designview, SIGNAL(highlight(std::vector, int)), fpgaView, + SLOT(onHighlightGroupChanged(std::vector, int))); + + connect(this, SIGNAL(contextChanged(Context *)), designview, SLOT(newContext(Context *))); + connect(this, SIGNAL(updateTreeView()), designview, SLOT(updateTree())); + + connect(designview, SIGNAL(info(std::string)), this, SLOT(writeInfo(std::string))); splitter_v->addWidget(centralTabWidget); splitter_v->addWidget(tabWidget); + displaySplash(); } BaseMainWindow::~BaseMainWindow() {} +void BaseMainWindow::displaySplash() +{ + splash = new QSplashScreen(); + splash->setPixmap(QPixmap(":/icons/resources/splash.png")); + splash->show(); + connect(designview, SIGNAL(finishContextLoad()), splash, SLOT(close())); + connect(designview, SIGNAL(contextLoadStatus(std::string)), this, SLOT(displaySplashMessage(std::string))); + QCoreApplication::instance()->processEvents(); +} + +void BaseMainWindow::displaySplashMessage(std::string msg) +{ + splash->showMessage(msg.c_str(), Qt::AlignCenter | Qt::AlignBottom, Qt::white); + QCoreApplication::instance()->processEvents(); +} + void BaseMainWindow::writeInfo(std::string text) { console->info(text); } void BaseMainWindow::createMenusAndBars() { - actionNew = new QAction("New", this); + actionNew = new QAction("New", this); actionNew->setIcon(QIcon(":/icons/resources/new.png")); actionNew->setShortcuts(QKeySequence::New); actionNew->setStatusTip("New project file"); connect(actionNew, SIGNAL(triggered()), this, SLOT(new_proj())); - actionOpen = new QAction("Open", this); + actionOpen = new QAction("Open", this); actionOpen->setIcon(QIcon(":/icons/resources/open.png")); actionOpen->setShortcuts(QKeySequence::Open); actionOpen->setStatusTip("Open an existing project file"); connect(actionOpen, SIGNAL(triggered()), this, SLOT(open_proj())); - QAction *actionSave = new QAction("Save", this); + QAction *actionSave = new QAction("Save", this); actionSave->setIcon(QIcon(":/icons/resources/save.png")); actionSave->setShortcuts(QKeySequence::Save); actionSave->setStatusTip("Save existing project to disk"); actionSave->setEnabled(false); connect(actionSave, SIGNAL(triggered()), this, SLOT(save_proj())); - QAction *actionExit = new QAction("Exit", this); + QAction *actionExit = new QAction("Exit", this); actionExit->setIcon(QIcon(":/icons/resources/exit.png")); actionExit->setShortcuts(QKeySequence::Quit); actionExit->setStatusTip("Exit the application"); diff --git a/gui/basewindow.h b/gui/basewindow.h index 4d3d80a1..18b5339e 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ Q_DECLARE_METATYPE(NEXTPNR_NAMESPACE_PREFIX DecalXY) NEXTPNR_NAMESPACE_BEGIN class PythonTab; +class DesignWidget; class BaseMainWindow : public QMainWindow { @@ -48,9 +50,11 @@ class BaseMainWindow : public QMainWindow protected: void createMenusAndBars(); + void displaySplash(); protected Q_SLOTS: void writeInfo(std::string text); + void displaySplashMessage(std::string msg); virtual void new_proj() = 0; virtual void open_proj() = 0; @@ -72,6 +76,8 @@ class BaseMainWindow : public QMainWindow QAction *actionNew; QAction *actionOpen; QProgressBar *progressBar; + QSplashScreen *splash; + DesignWidget *designview; }; NEXTPNR_NAMESPACE_END diff --git a/gui/designwidget.cc b/gui/designwidget.cc index edf9f1ec..335ed929 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -29,16 +29,6 @@ NEXTPNR_NAMESPACE_BEGIN -enum class ElementType -{ - NONE, - BEL, - WIRE, - PIP, - NET, - CELL -}; - class ElementTreeItem : public QTreeWidgetItem { public: @@ -87,29 +77,53 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net propertyEditor = new QtTreePropertyBrowser(this); propertyEditor->setFactoryForManager(variantManager, variantFactory); propertyEditor->setPropertiesWithoutValueMarked(true); - propertyEditor->show(); - + propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu); + QLineEdit *lineEdit = new QLineEdit(); lineEdit->setClearButtonEnabled(true); lineEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition); lineEdit->setPlaceholderText("Search..."); - actionFirst = new QAction("", this); + actionFirst = new QAction("", this); actionFirst->setIcon(QIcon(":/icons/resources/resultset_first.png")); actionFirst->setEnabled(false); + connect(actionFirst, &QAction::triggered, this, [this] { + history_ignore = true; + history_index = 0; + treeWidget->setCurrentItem(history.at(history_index)); + updateButtons(); + }); - actionPrev = new QAction("", this); + actionPrev = new QAction("", this); actionPrev->setIcon(QIcon(":/icons/resources/resultset_previous.png")); actionPrev->setEnabled(false); + connect(actionPrev, &QAction::triggered, this, [this] { + history_ignore = true; + history_index--; + treeWidget->setCurrentItem(history.at(history_index)); + updateButtons(); + }); - actionNext = new QAction("", this); + actionNext = new QAction("", this); actionNext->setIcon(QIcon(":/icons/resources/resultset_next.png")); actionNext->setEnabled(false); + connect(actionNext, &QAction::triggered, this, [this] { + history_ignore = true; + history_index++; + treeWidget->setCurrentItem(history.at(history_index)); + updateButtons(); + }); - actionLast = new QAction("", this); + actionLast = new QAction("", this); actionLast->setIcon(QIcon(":/icons/resources/resultset_last.png")); actionLast->setEnabled(false); + connect(actionLast, &QAction::triggered, this, [this] { + history_ignore = true; + history_index = int(history.size() - 1); + treeWidget->setCurrentItem(history.at(history_index)); + updateButtons(); + }); QToolBar *toolbar = new QToolBar(); toolbar->addAction(actionFirst); @@ -153,16 +167,61 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net setLayout(mainLayout); // Connection - connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, &DesignWidget::prepareMenu); + connect(propertyEditor->treeWidget(), &QTreeWidget::customContextMenuRequested, this, + &DesignWidget::prepareMenuProperty); + connect(propertyEditor->treeWidget(), &QTreeWidget::itemDoubleClicked, this, &DesignWidget::onItemDoubleClicked); connect(treeWidget, SIGNAL(itemSelectionChanged()), SLOT(onItemSelectionChanged())); + connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, &DesignWidget::prepareMenuTree); + + history_index = -1; + history_ignore = false; + + highlightColors[0] = QColor("#6495ed"); + highlightColors[1] = QColor("#7fffd4"); + highlightColors[2] = QColor("#98fb98"); + highlightColors[3] = QColor("#ffd700"); + highlightColors[4] = QColor("#cd5c5c"); + highlightColors[5] = QColor("#fa8072"); + highlightColors[6] = QColor("#ff69b4"); + highlightColors[7] = QColor("#da70d6"); } DesignWidget::~DesignWidget() {} +void DesignWidget::updateButtons() +{ + int count = int(history.size()); + actionFirst->setEnabled(history_index > 0); + actionPrev->setEnabled(history_index > 0); + actionNext->setEnabled(history_index < (count - 1)); + actionLast->setEnabled(history_index < (count - 1)); +} + +void DesignWidget::addToHistory(QTreeWidgetItem *item) +{ + if (!history_ignore) { + int count = int(history.size()); + for (int i = count - 1; i > history_index; i--) + history.pop_back(); + history.push_back(item); + history_index++; + } + history_ignore = false; + updateButtons(); +} + void DesignWidget::newContext(Context *ctx) { treeWidget->clear(); + history_ignore = false; + history_index = -1; + history.clear(); + updateButtons(); + + for (int i = 0; i < 6; i++) + nameToItem[i].clear(); + this->ctx = ctx; // Add bels to tree @@ -171,6 +230,7 @@ void DesignWidget::newContext(Context *ctx) bel_root->setText(0, "Bels"); treeWidget->insertTopLevelItem(0, bel_root); if (ctx) { + Q_EMIT contextLoadStatus("Configuring bels..."); for (auto bel : ctx->getBels()) { auto id = ctx->getBelName(bel); QStringList items = QString(id.c_str(ctx)).split("/"); @@ -182,7 +242,7 @@ void DesignWidget::newContext(Context *ctx) name += items.at(i); if (!bel_items.contains(name)) { if (i == items.size() - 1) - bel_items.insert(name, new IdStringTreeItem(id, ElementType::BEL, items.at(i), parent)); + nameToItem[0].insert(name, new IdStringTreeItem(id, ElementType::BEL, items.at(i), parent)); else bel_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent)); } @@ -193,6 +253,9 @@ void DesignWidget::newContext(Context *ctx) for (auto bel : bel_items.toStdMap()) { bel_root->addChild(bel.second); } + for (auto bel : nameToItem[0].toStdMap()) { + bel_root->addChild(bel.second); + } // Add wires to tree QTreeWidgetItem *wire_root = new QTreeWidgetItem(treeWidget); @@ -200,6 +263,7 @@ void DesignWidget::newContext(Context *ctx) wire_root->setText(0, "Wires"); treeWidget->insertTopLevelItem(0, wire_root); if (ctx) { + Q_EMIT contextLoadStatus("Configuring wires..."); for (auto wire : ctx->getWires()) { auto id = ctx->getWireName(wire); QStringList items = QString(id.c_str(ctx)).split("/"); @@ -211,7 +275,7 @@ void DesignWidget::newContext(Context *ctx) name += items.at(i); if (!wire_items.contains(name)) { if (i == items.size() - 1) - wire_items.insert(name, new IdStringTreeItem(id, ElementType::WIRE, items.at(i), parent)); + nameToItem[1].insert(name, new IdStringTreeItem(id, ElementType::WIRE, items.at(i), parent)); else wire_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent)); } @@ -222,13 +286,16 @@ void DesignWidget::newContext(Context *ctx) for (auto wire : wire_items.toStdMap()) { wire_root->addChild(wire.second); } - + for (auto wire : nameToItem[1].toStdMap()) { + wire_root->addChild(wire.second); + } // Add pips to tree QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget); QMap pip_items; pip_root->setText(0, "Pips"); treeWidget->insertTopLevelItem(0, pip_root); if (ctx) { + Q_EMIT contextLoadStatus("Configuring pips..."); for (auto pip : ctx->getPips()) { auto id = ctx->getPipName(pip); QStringList items = QString(id.c_str(ctx)).split("/"); @@ -240,7 +307,7 @@ void DesignWidget::newContext(Context *ctx) name += items.at(i); if (!pip_items.contains(name)) { if (i == items.size() - 1) - pip_items.insert(name, new IdStringTreeItem(id, ElementType::PIP, items.at(i), parent)); + nameToItem[2].insert(name, new IdStringTreeItem(id, ElementType::PIP, items.at(i), parent)); else pip_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent)); } @@ -251,6 +318,9 @@ void DesignWidget::newContext(Context *ctx) for (auto pip : pip_items.toStdMap()) { pip_root->addChild(pip.second); } + for (auto pip : nameToItem[2].toStdMap()) { + pip_root->addChild(pip.second); + } // Add nets to tree nets_root = new QTreeWidgetItem(treeWidget); @@ -261,6 +331,8 @@ void DesignWidget::newContext(Context *ctx) cells_root = new QTreeWidgetItem(treeWidget); cells_root->setText(0, "Cells"); treeWidget->insertTopLevelItem(0, cells_root); + + Q_EMIT finishContextLoad(); } void DesignWidget::updateTree() @@ -268,45 +340,48 @@ void DesignWidget::updateTree() clearProperties(); delete nets_root; delete cells_root; + nameToItem[3].clear(); + nameToItem[4].clear(); // Add nets to tree nets_root = new QTreeWidgetItem(treeWidget); - QMap nets_items; 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)); - nets_items.insert(name, new IdStringTreeItem(id, ElementType::NET, name, nullptr)); + IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::NET, name, nullptr); + nameToItem[3].insert(name, newItem); } } - for (auto item : nets_items.toStdMap()) { + for (auto item : nameToItem[3].toStdMap()) { nets_root->addChild(item.second); } // Add cells to tree cells_root = new QTreeWidgetItem(treeWidget); - QMap cells_items; 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)); - cells_items.insert(name, new IdStringTreeItem(id, ElementType::CELL, name, nullptr)); + IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::CELL, name, nullptr); + nameToItem[4].insert(name, newItem); } } - for (auto item : cells_items.toStdMap()) { + for (auto item : nameToItem[4].toStdMap()) { cells_root->addChild(item.second); } } - -void DesignWidget::addProperty(QtProperty *property, const QString &id) +QtProperty *DesignWidget::addTopLevelProperty(const QString &id) { - propertyToId[property] = id; - idToProperty[id] = property; - propertyEditor->addProperty(property); + QtProperty *topItem = groupManager->addProperty(id); + propertyToId[topItem] = id; + idToProperty[id] = topItem; + propertyEditor->addProperty(topItem); + return topItem; } void DesignWidget::clearProperties() @@ -320,10 +395,73 @@ void DesignWidget::clearProperties() idToProperty.clear(); } +QString DesignWidget::getElementTypeName(ElementType type) +{ + if (type == ElementType::NONE) + return ""; + if (type == ElementType::BEL) + return "BEL"; + if (type == ElementType::WIRE) + return "WIRE"; + if (type == ElementType::PIP) + return "PIP"; + if (type == ElementType::NET) + return "NET"; + if (type == ElementType::CELL) + return "CELL"; + return ""; +} +int DesignWidget::getElementIndex(ElementType type) +{ + if (type == ElementType::BEL) + return 0; + if (type == ElementType::WIRE) + return 1; + if (type == ElementType::PIP) + return 2; + if (type == ElementType::NET) + return 3; + if (type == ElementType::CELL) + return 4; + return -1; +} + +ElementType DesignWidget::getElementTypeByName(QString type) +{ + if (type == "BEL") + return ElementType::BEL; + if (type == "WIRE") + return ElementType::WIRE; + if (type == "PIP") + return ElementType::PIP; + if (type == "NET") + return ElementType::NET; + if (type == "CELL") + return ElementType::CELL; + return ElementType::NONE; +} + +void DesignWidget::addProperty(QtProperty *topItem, int propertyType, const QString &name, QVariant value, + const ElementType &type) +{ + QtVariantProperty *item = readOnlyManager->addProperty(propertyType, name); + item->setValue(value); + item->setPropertyId(getElementTypeName(type)); + topItem->addSubProperty(item); +} + +QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name) +{ + QtProperty *item = groupManager->addProperty(name); + topItem->addSubProperty(item); + return item; +} + void DesignWidget::onItemSelectionChanged() { - if (treeWidget->selectedItems().size()== 0) return; - + if (treeWidget->selectedItems().size() == 0) + return; + QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0); if (!clickItem->parent()) @@ -336,36 +474,24 @@ void DesignWidget::onItemSelectionChanged() std::vector decals; + addToHistory(clickItem); + clearProperties(); if (type == ElementType::BEL) { IdString c = static_cast(clickItem)->getData(); BelId bel = ctx->getBelByName(c); - - decals.push_back(ctx->getBelDecal(bel)); + + decals.push_back(ctx->getBelDecal(bel)); Q_EMIT selected(decals); - QtProperty *topItem = groupManager->addProperty("Bel"); - addProperty(topItem, "Bel"); + QtProperty *topItem = addTopLevelProperty("Bel"); - QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name"); - nameItem->setValue(c.c_str(ctx)); - topItem->addSubProperty(nameItem); - - QtVariantProperty *typeItem = readOnlyManager->addProperty(QVariant::String, "Type"); - typeItem->setValue(ctx->belTypeToId(ctx->getBelType(bel)).c_str(ctx)); - topItem->addSubProperty(typeItem); - - QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available"); - availItem->setValue(ctx->checkBelAvail(bel)); - topItem->addSubProperty(availItem); - - QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Cell"); - cellItem->setValue(ctx->getBoundBelCell(bel).c_str(ctx)); - topItem->addSubProperty(cellItem); - - QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Cell"); - conflictItem->setValue(ctx->getConflictingBelCell(bel).c_str(ctx)); - topItem->addSubProperty(conflictItem); + addProperty(topItem, QVariant::String, "Name", c.c_str(ctx)); + addProperty(topItem, QVariant::String, "Type", ctx->belTypeToId(ctx->getBelType(bel)).c_str(ctx)); + addProperty(topItem, QVariant::Bool, "Available", ctx->checkBelAvail(bel)); + addProperty(topItem, QVariant::String, "Bound Cell", ctx->getBoundBelCell(bel).c_str(ctx), ElementType::CELL); + addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->getConflictingBelCell(bel).c_str(ctx), + ElementType::CELL); } else if (type == ElementType::WIRE) { IdString c = static_cast(clickItem)->getData(); @@ -374,76 +500,56 @@ void DesignWidget::onItemSelectionChanged() decals.push_back(ctx->getWireDecal(wire)); Q_EMIT selected(decals); - QtProperty *topItem = groupManager->addProperty("Wire"); - addProperty(topItem, "Wire"); + QtProperty *topItem = addTopLevelProperty("Wire"); - QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name"); - nameItem->setValue(c.c_str(ctx)); - topItem->addSubProperty(nameItem); - - QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available"); - availItem->setValue(ctx->checkWireAvail(wire)); - topItem->addSubProperty(availItem); - - QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Net"); - cellItem->setValue(ctx->getBoundWireNet(wire).c_str(ctx)); - topItem->addSubProperty(cellItem); - - QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Net"); - conflictItem->setValue(ctx->getConflictingWireNet(wire).c_str(ctx)); - topItem->addSubProperty(conflictItem); + addProperty(topItem, QVariant::String, "Name", c.c_str(ctx)); + addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire)); + addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundWireNet(wire).c_str(ctx), ElementType::NET); + addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx), + ElementType::NET); + QtProperty *belpinItem = addSubGroup(topItem, "BelPin Uphill"); BelPin uphill = ctx->getBelPinUphill(wire); - QtProperty *belpinItem = groupManager->addProperty("BelPin Uphill"); - topItem->addSubProperty(belpinItem); - - QtVariantProperty *belUphillItem = readOnlyManager->addProperty(QVariant::String, "Bel"); if (uphill.bel != BelId()) - belUphillItem->setValue(ctx->getBelName(uphill.bel).c_str(ctx)); + addProperty(belpinItem, QVariant::String, "Bel", ctx->getBelName(uphill.bel).c_str(ctx), ElementType::BEL); else - belUphillItem->setValue(""); - belpinItem->addSubProperty(belUphillItem); + addProperty(belpinItem, QVariant::String, "Bel", "", ElementType::BEL); - QtVariantProperty *portUphillItem = readOnlyManager->addProperty(QVariant::String, "PortPin"); - portUphillItem->setValue(ctx->portPinToId(uphill.pin).c_str(ctx)); - belpinItem->addSubProperty(portUphillItem); + addProperty(belpinItem, QVariant::String, "PortPin", ctx->portPinToId(uphill.pin).c_str(ctx), ElementType::BEL); - QtProperty *downhillItem = groupManager->addProperty("BelPins Downhill"); - topItem->addSubProperty(downhillItem); + QtProperty *downhillItem = addSubGroup(topItem, "BelPin Downhill"); for (const auto &item : ctx->getBelPinsDownhill(wire)) { QString belname = ""; if (item.bel != BelId()) belname = ctx->getBelName(item.bel).c_str(ctx); QString pinname = ctx->portPinToId(item.pin).c_str(ctx); - QtProperty *dhItem = groupManager->addProperty(belname + "-" + pinname); - downhillItem->addSubProperty(dhItem); - - QtVariantProperty *belItem = readOnlyManager->addProperty(QVariant::String, "Bel"); - belItem->setValue(belname); - dhItem->addSubProperty(belItem); - - QtVariantProperty *portItem = readOnlyManager->addProperty(QVariant::String, "PortPin"); - portItem->setValue(pinname); - dhItem->addSubProperty(portItem); + QtProperty *dhItem = addSubGroup(downhillItem, belname + "-" + pinname); + addProperty(dhItem, QVariant::String, "Bel", belname, ElementType::BEL); + addProperty(dhItem, QVariant::String, "PortPin", pinname); } -/* - QtProperty *pipsDownItem = groupManager->addProperty("Pips Downhill"); - topItem->addSubProperty(pipsDownItem); + + int counter = 0; + QtProperty *pipsDownItem = addSubGroup(downhillItem, "Pips Downhill"); for (const auto &item : ctx->getPipsDownhill(wire)) { - QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, ""); - pipItem->setValue(ctx->getPipName(item).c_str(ctx)); - pipsDownItem->addSubProperty(pipItem); + addProperty(pipsDownItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP); + counter++; + if (counter == 50) { + addProperty(pipsDownItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE); + break; + } } - QtProperty *pipsUpItem = groupManager->addProperty("Pips Uphill"); - topItem->addSubProperty(pipsUpItem); + counter = 0; + QtProperty *pipsUpItem = addSubGroup(downhillItem, "Pips Uphill"); for (const auto &item : ctx->getPipsUphill(wire)) { - QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, ""); - pipItem->setValue(ctx->getPipName(item).c_str(ctx)); - pipsUpItem->addSubProperty(pipItem); + addProperty(pipsUpItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP); + counter++; + if (counter == 50) { + addProperty(pipsUpItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE); + break; + } } -*/ } else if (type == ElementType::PIP) { IdString c = static_cast(clickItem)->getData(); PipId pip = ctx->getPipByName(c); @@ -451,199 +557,108 @@ void DesignWidget::onItemSelectionChanged() decals.push_back(ctx->getPipDecal(pip)); Q_EMIT selected(decals); - QtProperty *topItem = groupManager->addProperty("Pip"); - addProperty(topItem, "Pip"); + QtProperty *topItem = addTopLevelProperty("Pip"); - QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name"); - nameItem->setValue(c.c_str(ctx)); - topItem->addSubProperty(nameItem); - - QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available"); - availItem->setValue(ctx->checkPipAvail(pip)); - topItem->addSubProperty(availItem); - - QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Net"); - cellItem->setValue(ctx->getBoundPipNet(pip).c_str(ctx)); - topItem->addSubProperty(cellItem); - - QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Net"); - conflictItem->setValue(ctx->getConflictingPipNet(pip).c_str(ctx)); - topItem->addSubProperty(conflictItem); - - QtVariantProperty *srcWireItem = readOnlyManager->addProperty(QVariant::String, "Src Wire"); - srcWireItem->setValue(ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx)); - topItem->addSubProperty(srcWireItem); - - QtVariantProperty *destWireItem = readOnlyManager->addProperty(QVariant::String, "Dest Wire"); - destWireItem->setValue(ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx)); - topItem->addSubProperty(destWireItem); + addProperty(topItem, QVariant::String, "Name", c.c_str(ctx)); + addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip)); + addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundPipNet(pip).c_str(ctx), ElementType::NET); + addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingPipNet(pip).c_str(ctx), + ElementType::NET); + addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx), + ElementType::WIRE); + addProperty(topItem, QVariant::String, "Dest Wire", ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx), + ElementType::WIRE); DelayInfo delay = ctx->getPipDelay(pip); - QtProperty *delayItem = groupManager->addProperty("Delay"); - topItem->addSubProperty(delayItem); - - QtVariantProperty *raiseDelayItem = readOnlyManager->addProperty(QVariant::Double, "Raise"); - raiseDelayItem->setValue(delay.raiseDelay()); - delayItem->addSubProperty(raiseDelayItem); - - QtVariantProperty *fallDelayItem = readOnlyManager->addProperty(QVariant::Double, "Fall"); - fallDelayItem->setValue(delay.fallDelay()); - delayItem->addSubProperty(fallDelayItem); - - QtVariantProperty *avgDelayItem = readOnlyManager->addProperty(QVariant::Double, "Average"); - avgDelayItem->setValue(delay.avgDelay()); - delayItem->addSubProperty(avgDelayItem); + QtProperty *delayItem = addSubGroup(topItem, "Delay"); + addProperty(delayItem, QVariant::Double, "Raise", delay.raiseDelay()); + addProperty(delayItem, QVariant::Double, "Fall", delay.fallDelay()); + addProperty(delayItem, QVariant::Double, "Average", delay.avgDelay()); } else if (type == ElementType::NET) { IdString c = static_cast(clickItem)->getData(); NetInfo *net = ctx->nets.at(c).get(); - QtProperty *topItem = groupManager->addProperty("Net"); - addProperty(topItem, "Net"); + QtProperty *topItem = addTopLevelProperty("Net"); - QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name"); - nameItem->setValue(net->name.c_str(ctx)); - topItem->addSubProperty(nameItem); + addProperty(topItem, QVariant::String, "Name", net->name.c_str(ctx)); - QtProperty *driverItem = groupManager->addProperty("Driver"); - topItem->addSubProperty(driverItem); - - QtVariantProperty *portItem = readOnlyManager->addProperty(QVariant::String, "Port"); - portItem->setValue(net->driver.port.c_str(ctx)); - driverItem->addSubProperty(portItem); - - QtVariantProperty *budgetItem = readOnlyManager->addProperty(QVariant::Double, "Budget"); - budgetItem->setValue(net->driver.budget); - driverItem->addSubProperty(budgetItem); - - QtVariantProperty *cellNameItem = readOnlyManager->addProperty(QVariant::String, "Cell"); + QtProperty *driverItem = addSubGroup(topItem, "Driver"); + addProperty(driverItem, QVariant::String, "Port", net->driver.port.c_str(ctx)); + addProperty(driverItem, QVariant::Double, "Budget", net->driver.budget); if (net->driver.cell) - cellNameItem->setValue(net->driver.cell->name.c_str(ctx)); + addProperty(driverItem, QVariant::String, "Cell", net->driver.cell->name.c_str(ctx), ElementType::CELL); else - cellNameItem->setValue(""); - driverItem->addSubProperty(cellNameItem); + addProperty(driverItem, QVariant::String, "Cell", "", ElementType::CELL); - QtProperty *usersItem = groupManager->addProperty("Users"); - topItem->addSubProperty(usersItem); + QtProperty *usersItem = addSubGroup(topItem, "Users"); for (auto &item : net->users) { - QtProperty *portItem = groupManager->addProperty(item.port.c_str(ctx)); - usersItem->addSubProperty(portItem); + QtProperty *portItem = addSubGroup(usersItem, item.port.c_str(ctx)); - QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Port"); - nameItem->setValue(item.port.c_str(ctx)); - portItem->addSubProperty(nameItem); - - QtVariantProperty *budgetItem = readOnlyManager->addProperty(QVariant::Double, "Budget"); - budgetItem->setValue(item.budget); - portItem->addSubProperty(budgetItem); - - QtVariantProperty *userItem = readOnlyManager->addProperty(QVariant::String, "Cell"); + addProperty(portItem, QVariant::String, "Port", item.port.c_str(ctx)); + addProperty(portItem, QVariant::Double, "Budget", item.budget); if (item.cell) - userItem->setValue(item.cell->name.c_str(ctx)); + addProperty(portItem, QVariant::String, "Cell", item.cell->name.c_str(ctx), ElementType::CELL); else - userItem->setValue(""); - portItem->addSubProperty(userItem); + addProperty(portItem, QVariant::String, "Cell", "", ElementType::CELL); } - QtProperty *attrsItem = groupManager->addProperty("Attributes"); - topItem->addSubProperty(attrsItem); + QtProperty *attrsItem = addSubGroup(topItem, "Attributes"); for (auto &item : net->attrs) { - QtVariantProperty *attrItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx)); - attrItem->setValue(item.second.c_str()); - attrsItem->addSubProperty(attrItem); + addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str()); } - QtProperty *wiresItem = groupManager->addProperty("Wires"); - topItem->addSubProperty(wiresItem); + QtProperty *wiresItem = addSubGroup(topItem, "Wires"); for (auto &item : net->wires) { auto name = ctx->getWireName(item.first).c_str(ctx); - QtProperty *wireItem = groupManager->addProperty(name); - - QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name"); - nameItem->setValue(name); - wireItem->addSubProperty(nameItem); - - QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, "Pip"); + QtProperty *wireItem = addSubGroup(wiresItem, name); + addProperty(wireItem, QVariant::String, "Name", name); if (item.second.pip != PipId()) - pipItem->setValue(ctx->getPipName(item.second.pip).c_str(ctx)); + addProperty(wireItem, QVariant::String, "Pip", ctx->getPipName(item.second.pip).c_str(ctx), + ElementType::PIP); else - pipItem->setValue(""); - wireItem->addSubProperty(pipItem); + addProperty(wireItem, QVariant::String, "Pip", "", ElementType::PIP); - QtVariantProperty *strengthItem = readOnlyManager->addProperty(QVariant::Int, "Strength"); - strengthItem->setValue((int)item.second.strength); - wireItem->addSubProperty(strengthItem); - - wiresItem->addSubProperty(wireItem); + addProperty(wireItem, QVariant::Int, "Strength", (int)item.second.strength); } } else if (type == ElementType::CELL) { IdString c = static_cast(clickItem)->getData(); CellInfo *cell = ctx->cells.at(c).get(); - QtProperty *topItem = groupManager->addProperty("Cell"); - addProperty(topItem, "Cell"); + QtProperty *topItem = addTopLevelProperty("Cell"); - QtVariantProperty *cellNameItem = readOnlyManager->addProperty(QVariant::String, "Name"); - cellNameItem->setValue(cell->name.c_str(ctx)); - topItem->addSubProperty(cellNameItem); - - QtVariantProperty *cellTypeItem = readOnlyManager->addProperty(QVariant::String, "Type"); - cellTypeItem->setValue(cell->type.c_str(ctx)); - topItem->addSubProperty(cellTypeItem); - - QtVariantProperty *cellBelItem = readOnlyManager->addProperty(QVariant::String, "Bel"); + addProperty(topItem, QVariant::String, "Name", cell->name.c_str(ctx)); + addProperty(topItem, QVariant::String, "Type", cell->type.c_str(ctx)); if (cell->bel != BelId()) - cellBelItem->setValue(ctx->getBelName(cell->bel).c_str(ctx)); + addProperty(topItem, QVariant::String, "Bel", ctx->getBelName(cell->bel).c_str(ctx), ElementType::BEL); else - cellBelItem->setValue(""); - topItem->addSubProperty(cellBelItem); + addProperty(topItem, QVariant::String, "Bel", "", ElementType::BEL); + addProperty(topItem, QVariant::Int, "Bel strength", int(cell->belStrength)); - QtVariantProperty *cellBelStrItem = readOnlyManager->addProperty(QVariant::Int, "Bel strength"); - cellBelStrItem->setValue(int(cell->belStrength)); - topItem->addSubProperty(cellBelStrItem); - - QtProperty *cellPortsItem = groupManager->addProperty("Ports"); - topItem->addSubProperty(cellPortsItem); + QtProperty *cellPortsItem = addSubGroup(topItem, "Ports"); for (auto &item : cell->ports) { PortInfo p = item.second; - QtProperty *portInfoItem = groupManager->addProperty(p.name.c_str(ctx)); - - QtVariantProperty *portInfoNameItem = readOnlyManager->addProperty(QVariant::String, "Name"); - portInfoNameItem->setValue(p.name.c_str(ctx)); - portInfoItem->addSubProperty(portInfoNameItem); - - QtVariantProperty *portInfoTypeItem = readOnlyManager->addProperty(QVariant::Int, "Type"); - portInfoTypeItem->setValue(int(p.type)); - portInfoItem->addSubProperty(portInfoTypeItem); - - QtVariantProperty *portInfoNetItem = readOnlyManager->addProperty(QVariant::String, "Net"); + QtProperty *portInfoItem = addSubGroup(cellPortsItem, p.name.c_str(ctx)); + addProperty(portInfoItem, QVariant::String, "Name", p.name.c_str(ctx)); + addProperty(portInfoItem, QVariant::Int, "Type", int(p.type)); if (p.net) - portInfoNetItem->setValue(p.net->name.c_str(ctx)); + addProperty(portInfoItem, QVariant::String, "Net", p.net->name.c_str(ctx), ElementType::NET); else - portInfoNetItem->setValue(""); - portInfoItem->addSubProperty(portInfoNetItem); - - cellPortsItem->addSubProperty(portInfoItem); + addProperty(portInfoItem, QVariant::String, "Net", "", ElementType::NET); } - QtProperty *cellAttrItem = groupManager->addProperty("Attributes"); - topItem->addSubProperty(cellAttrItem); + QtProperty *cellAttrItem = addSubGroup(topItem, "Attributes"); for (auto &item : cell->attrs) { - QtVariantProperty *attrItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx)); - attrItem->setValue(item.second.c_str()); - cellAttrItem->addSubProperty(attrItem); + addProperty(cellAttrItem, QVariant::String, item.first.c_str(ctx), item.second.c_str()); } - QtProperty *cellParamsItem = groupManager->addProperty("Parameters"); - topItem->addSubProperty(cellParamsItem); + QtProperty *cellParamsItem = addSubGroup(topItem, "Parameters"); for (auto &item : cell->params) { - QtVariantProperty *paramItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx)); - paramItem->setValue(item.second.c_str()); - cellParamsItem->addSubProperty(paramItem); + addProperty(cellParamsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str()); } QtProperty *cellPinsItem = groupManager->addProperty("Pins"); @@ -652,38 +667,159 @@ void DesignWidget::onItemSelectionChanged() std::string cell_port = item.first.c_str(ctx); std::string bel_pin = item.second.c_str(ctx); - QtProperty *pinGroupItem = groupManager->addProperty((cell_port + " -> " + bel_pin).c_str()); + QtProperty *pinGroupItem = addSubGroup(cellPortsItem, (cell_port + " -> " + bel_pin).c_str()); - QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Cell"); - cellItem->setValue(cell_port.c_str()); - pinGroupItem->addSubProperty(cellItem); - - QtVariantProperty *belItem = readOnlyManager->addProperty(QVariant::String, "Bel"); - belItem->setValue(bel_pin.c_str()); - pinGroupItem->addSubProperty(belItem); - - cellPinsItem->addSubProperty(pinGroupItem); + addProperty(pinGroupItem, QVariant::String, "Cell", cell_port.c_str(), ElementType::CELL); + addProperty(pinGroupItem, QVariant::String, "Bel", bel_pin.c_str(), ElementType::BEL); } } } -void DesignWidget::prepareMenu(const QPoint &pos) +std::vector DesignWidget::getDecals(ElementType type, IdString value) +{ + std::vector decals; + switch (type) { + case ElementType::BEL: { + BelId bel = ctx->getBelByName(value); + if (bel != BelId()) { + decals.push_back(ctx->getBelDecal(bel)); + } + } break; + case ElementType::WIRE: { + WireId wire = ctx->getWireByName(value); + if (wire != WireId()) { + decals.push_back(ctx->getWireDecal(wire)); + Q_EMIT selected(decals); + } + } break; + case ElementType::PIP: { + PipId pip = ctx->getPipByName(value); + if (pip != PipId()) { + decals.push_back(ctx->getPipDecal(pip)); + Q_EMIT selected(decals); + } + } break; + case ElementType::NET: { + } break; + case ElementType::CELL: { + } break; + default: + break; + } + return decals; +} + +void DesignWidget::updateHighlightGroup(QTreeWidgetItem *item, int group) +{ + if (highlightSelected.contains(item)) { + if (highlightSelected[item] == group) { + highlightSelected.remove(item); + } else + highlightSelected[item] = group; + } else + highlightSelected.insert(item, group); + + std::vector decals; + + for (auto it : highlightSelected.toStdMap()) { + if (it.second == group) { + ElementType type = static_cast(it.first)->getType(); + IdString value = static_cast(it.first)->getData(); + std::vector d = getDecals(type, value); + std::move(d.begin(), d.end(), std::back_inserter(decals)); + } + } + + Q_EMIT highlight(decals, group); +} + +void DesignWidget::prepareMenuProperty(const QPoint &pos) +{ + QTreeWidget *tree = propertyEditor->treeWidget(); + + itemContextMenu = tree->itemAt(pos); + if (itemContextMenu->parent() == nullptr) + return; + + QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu); + if (!browserItem) + return; + QtProperty *selectedProperty = browserItem->property(); + ElementType type = getElementTypeByName(selectedProperty->propertyId()); + if (type == ElementType::NONE) + return; + IdString value = ctx->id(selectedProperty->valueText().toStdString()); + + QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx)); + + QMenu menu(this); + QAction *selectAction = new QAction("&Select", this); + connect(selectAction, &QAction::triggered, this, [this, type, value] { Q_EMIT selected(getDecals(type, value)); }); + menu.addAction(selectAction); + + QMenu *subMenu = menu.addMenu("Highlight"); + QActionGroup *group = new QActionGroup(this); + group->setExclusive(true); + for (int i = 0; i < 8; i++) { + QPixmap pixmap(32, 32); + pixmap.fill(QColor(highlightColors[i])); + QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this); + action->setCheckable(true); + subMenu->addAction(action); + group->addAction(action); + if (highlightSelected.contains(item) && highlightSelected[item] == i) + action->setChecked(true); + connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); }); + } + menu.exec(tree->mapToGlobal(pos)); +} + +void DesignWidget::prepareMenuTree(const QPoint &pos) { QTreeWidget *tree = treeWidget; itemContextMenu = tree->itemAt(pos); - QAction *selectAction = new QAction("&Select", this); - selectAction->setStatusTip("Select item on view"); + ElementType type = static_cast(itemContextMenu)->getType(); + IdString value = static_cast(itemContextMenu)->getData(); - connect(selectAction, SIGNAL(triggered()), this, SLOT(selectObject())); + if (type == ElementType::NONE) + return; + + QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx)); QMenu menu(this); - menu.addAction(selectAction); - + QMenu *subMenu = menu.addMenu("Highlight"); + QActionGroup *group = new QActionGroup(this); + group->setExclusive(true); + for (int i = 0; i < 8; i++) { + QPixmap pixmap(32, 32); + pixmap.fill(QColor(highlightColors[i])); + QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this); + action->setCheckable(true); + subMenu->addAction(action); + group->addAction(action); + if (highlightSelected.contains(item) && highlightSelected[item] == i) + action->setChecked(true); + connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); }); + } menu.exec(tree->mapToGlobal(pos)); } -void DesignWidget::selectObject() { Q_EMIT info("selected " + itemContextMenu->text(0).toStdString() + "\n"); } +void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column) +{ + QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property(); + ElementType type = getElementTypeByName(selectedProperty->propertyId()); + QString value = selectedProperty->valueText(); + int index = getElementIndex(type); + switch (type) { + case ElementType::NONE: + return; + default: { + if (nameToItem[index].contains(value)) + treeWidget->setCurrentItem(nameToItem[index].value(value)); + } break; + } +} NEXTPNR_NAMESPACE_END diff --git a/gui/designwidget.h b/gui/designwidget.h index 618c7bbf..1afe817d 100644 --- a/gui/designwidget.h +++ b/gui/designwidget.h @@ -21,6 +21,7 @@ #define DESIGNWIDGET_H #include +#include #include "nextpnr.h" #include "qtgroupboxpropertybrowser.h" #include "qtpropertymanager.h" @@ -29,6 +30,16 @@ NEXTPNR_NAMESPACE_BEGIN +enum class ElementType +{ + NONE, + BEL, + WIRE, + PIP, + NET, + CELL +}; + class DesignWidget : public QWidget { Q_OBJECT @@ -38,17 +49,30 @@ class DesignWidget : public QWidget ~DesignWidget(); private: - void addProperty(QtProperty *property, const QString &id); void clearProperties(); - + QtProperty *addTopLevelProperty(const QString &id); + QtProperty *addSubGroup(QtProperty *topItem, const QString &name); + void addProperty(QtProperty *topItem, int propertyType, const QString &name, QVariant value, + const ElementType &type = ElementType::NONE); + QString getElementTypeName(ElementType type); + ElementType getElementTypeByName(QString type); + int getElementIndex(ElementType type); + void updateButtons(); + void addToHistory(QTreeWidgetItem *item); + std::vector getDecals(ElementType type, IdString value); + void updateHighlightGroup(QTreeWidgetItem *item, int group); Q_SIGNALS: void info(std::string text); void selected(std::vector decal); + void highlight(std::vector decal, int group); + void finishContextLoad(); + void contextLoadStatus(std::string text); private Q_SLOTS: - void prepareMenu(const QPoint &pos); + void prepareMenuProperty(const QPoint &pos); + void prepareMenuTree(const QPoint &pos); void onItemSelectionChanged(); - void selectObject(); + void onItemDoubleClicked(QTreeWidgetItem *item, int column); public Q_SLOTS: void newContext(Context *ctx); void updateTree(); @@ -67,6 +91,12 @@ class DesignWidget : public QWidget QMap propertyToId; QMap idToProperty; + + QMap nameToItem[6]; + std::vector history; + int history_index; + bool history_ignore; + QTreeWidgetItem *nets_root; QTreeWidgetItem *cells_root; @@ -74,6 +104,9 @@ class DesignWidget : public QWidget QAction *actionPrev; QAction *actionNext; QAction *actionLast; + + QColor highlightColors[8]; + QMap highlightSelected; }; NEXTPNR_NAMESPACE_END diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 1d0e8b57..2d8d4cef 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -240,7 +240,8 @@ void LineShader::draw(const LineShaderData &line, const QColor &color, float thi vao_.release(); } -FPGAViewWidget::FPGAViewWidget(QWidget *parent) : QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr), selectedItemsChanged(false) +FPGAViewWidget::FPGAViewWidget(QWidget *parent) + : QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr), selectedItemsChanged_(false) { backgroundColor_ = QColor("#000000"); gridColor_ = QColor("#333"); @@ -250,6 +251,16 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent) : QOpenGLWidget(parent), lineSha gActiveColor_ = QColor("#f0f0f0"); gSelectedColor_ = QColor("#ff6600"); frameColor_ = QColor("#0066ba"); + highlightColors[0] = QColor("#6495ed"); + highlightColors[1] = QColor("#7fffd4"); + highlightColors[2] = QColor("#98fb98"); + highlightColors[3] = QColor("#ffd700"); + highlightColors[4] = QColor("#cd5c5c"); + highlightColors[5] = QColor("#fa8072"); + highlightColors[6] = QColor("#ff69b4"); + highlightColors[7] = QColor("#da70d6"); + for (int i = 0; i < 8; i++) + highlightItemsChanged_[i] = false; auto fmt = format(); fmt.setMajorVersion(3); @@ -272,6 +283,7 @@ FPGAViewWidget::~FPGAViewWidget() {} void FPGAViewWidget::newContext(Context *ctx) { ctx_ = ctx; + selectedItems_.clear(); update(); } @@ -411,27 +423,44 @@ void FPGAViewWidget::paintGL() drawDecal(shaders, ctx_->getGroupDecal(group)); } - if (selectedItemsChanged) - { - selectedItemsChanged = false; + if (selectedItemsChanged_) { + selectedItemsChanged_ = false; selectedShader_.clear(); for (auto decal : selectedItems_) { drawDecal(selectedShader_, decal); } } + for (int i = 0; i < 8; i++) { + if (highlightItemsChanged_[i]) { + highlightItemsChanged_[i] = false; + highlightShader_[i].clear(); + for (auto decal : highlightItems_[i]) { + drawDecal(highlightShader_[i], decal); + } + } + } } lineShader_.draw(shaders[0], gFrameColor_, thick11Px, matrix); lineShader_.draw(shaders[1], gHiddenColor_, thick11Px, matrix); lineShader_.draw(shaders[2], gInactiveColor_, thick11Px, matrix); lineShader_.draw(shaders[3], gActiveColor_, thick11Px, matrix); + for (int i = 0; i < 8; i++) + lineShader_.draw(highlightShader_[i], highlightColors[i], thick11Px, matrix); lineShader_.draw(selectedShader_, gSelectedColor_, thick11Px, matrix); } void FPGAViewWidget::onSelectedArchItem(std::vector decals) { selectedItems_ = decals; - selectedItemsChanged = true; + selectedItemsChanged_ = true; + update(); +} + +void FPGAViewWidget::onHighlightGroupChanged(std::vector decals, int group) +{ + highlightItems_[group] = decals; + highlightItemsChanged_[group] = true; update(); } diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index dd86277e..33eb2800 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -245,6 +245,8 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions public Q_SLOTS: void newContext(Context *ctx); void onSelectedArchItem(std::vector decals); + void onHighlightGroupChanged(std::vector decals, int group); + private: QPoint lastPos_; LineShader lineShader_; @@ -272,7 +274,12 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions LineShaderData selectedShader_; std::vector selectedItems_; - bool selectedItemsChanged; + bool selectedItemsChanged_; + + LineShaderData highlightShader_[8]; + std::vector highlightItems_[8]; + bool highlightItemsChanged_[8]; + QColor highlightColors[8]; }; NEXTPNR_NAMESPACE_END diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc index 772ca6ac..4b1f2c57 100644 --- a/gui/ice40/mainwindow.cc +++ b/gui/ice40/mainwindow.cc @@ -71,43 +71,43 @@ void MainWindow::createMenu() QMenu *menu_Design = new QMenu("&Design", menuBar); menuBar->addAction(menu_Design->menuAction()); - actionLoadJSON = new QAction("Open JSON", this); + actionLoadJSON = new QAction("Open JSON", this); actionLoadJSON->setIcon(QIcon(":/icons/resources/open_json.png")); actionLoadJSON->setStatusTip("Open an existing JSON file"); actionLoadJSON->setEnabled(true); connect(actionLoadJSON, SIGNAL(triggered()), this, SLOT(open_json())); - actionLoadPCF = new QAction("Open PCF", this); + actionLoadPCF = new QAction("Open PCF", this); actionLoadPCF->setIcon(QIcon(":/icons/resources/open_pcf.png")); actionLoadPCF->setStatusTip("Open PCF file"); actionLoadPCF->setEnabled(false); connect(actionLoadPCF, SIGNAL(triggered()), this, SLOT(open_pcf())); - actionPack = new QAction("Pack", this); + actionPack = new QAction("Pack", this); actionPack->setIcon(QIcon(":/icons/resources/pack.png")); actionPack->setStatusTip("Pack current design"); actionPack->setEnabled(false); connect(actionPack, SIGNAL(triggered()), task, SIGNAL(pack())); - actionAssignBudget = new QAction("Assign Budget", this); + actionAssignBudget = new QAction("Assign Budget", this); actionAssignBudget->setIcon(QIcon(":/icons/resources/time_add.png")); actionAssignBudget->setStatusTip("Assign time budget for current design"); actionAssignBudget->setEnabled(false); connect(actionAssignBudget, SIGNAL(triggered()), this, SLOT(budget())); - actionPlace = new QAction("Place", this); + actionPlace = new QAction("Place", this); actionPlace->setIcon(QIcon(":/icons/resources/place.png")); actionPlace->setStatusTip("Place current design"); actionPlace->setEnabled(false); connect(actionPlace, SIGNAL(triggered()), this, SLOT(place())); - actionRoute = new QAction("Route", this); + actionRoute = new QAction("Route", this); actionRoute->setIcon(QIcon(":/icons/resources/route.png")); actionRoute->setStatusTip("Route current design"); actionRoute->setEnabled(false); connect(actionRoute, SIGNAL(triggered()), task, SIGNAL(route())); - actionSaveAsc = new QAction("Save ASC", this); + actionSaveAsc = new QAction("Save ASC", this); actionSaveAsc->setIcon(QIcon(":/icons/resources/save_asc.png")); actionSaveAsc->setStatusTip("Save ASC file"); actionSaveAsc->setEnabled(false); @@ -132,19 +132,19 @@ void MainWindow::createMenu() menu_Design->addAction(actionRoute); menu_Design->addAction(actionSaveAsc); - actionPlay = new QAction("Play", this); + actionPlay = new QAction("Play", this); actionPlay->setIcon(QIcon(":/icons/resources/control_play.png")); actionPlay->setStatusTip("Continue running task"); actionPlay->setEnabled(false); connect(actionPlay, SIGNAL(triggered()), task, SLOT(continue_thread())); - actionPause = new QAction("Pause", this); + actionPause = new QAction("Pause", this); actionPause->setIcon(QIcon(":/icons/resources/control_pause.png")); actionPause->setStatusTip("Pause running task"); actionPause->setEnabled(false); connect(actionPause, SIGNAL(triggered()), task, SLOT(pause_thread())); - actionStop = new QAction("Stop", this); + actionStop = new QAction("Stop", this); actionStop->setIcon(QIcon(":/icons/resources/control_stop.png")); actionStop->setStatusTip("Stop running task"); actionStop->setEnabled(false); @@ -226,6 +226,7 @@ void MainWindow::new_proj() ctx = std::unique_ptr(new Context(chipArgs)); actionLoadJSON->setEnabled(true); + Q_EMIT displaySplash(); Q_EMIT contextChanged(ctx.get()); } } diff --git a/gui/resources/splash.png b/gui/resources/splash.png new file mode 100644 index 00000000..14d2842b Binary files /dev/null and b/gui/resources/splash.png differ diff --git a/ice40/arch.cc b/ice40/arch.cc index c9cda40d..08e32ae6 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -334,6 +334,7 @@ IdString Arch::getPipName(PipId pip) const { NPNR_ASSERT(pip != PipId()); +#if 1 int x = chip_info->pip_data[pip.index].x; int y = chip_info->pip_data[pip.index].y; @@ -344,6 +345,9 @@ IdString Arch::getPipName(PipId pip) const std::replace(dst_name.begin(), dst_name.end(), '/', '.'); return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name); +#else + return id(chip_info->pip_data[pip.index].name.get()); +#endif } // ----------------------------------------------------------------------- @@ -480,9 +484,9 @@ DecalXY Arch::getWireDecal(WireId wire) const DecalXY Arch::getPipDecal(PipId pip) const { DecalXY decalxy; - decalxy.decal.type = DecalId::TYPE_PIP; - decalxy.decal.index = pip.index; - decalxy.decal.active = pip_to_net.at(pip.index) != IdString(); + // decalxy.decal.type = DecalId::TYPE_PIP; + // decalxy.decal.index = pip.index; + // decalxy.decal.active = pip_to_net.at(pip.index) != IdString(); return decalxy; }; @@ -512,11 +516,8 @@ std::vector Arch::getDecalGraphics(DecalId decal) const } if (decal.type == DecalId::TYPE_WIRE) { - WireId wire; - wire.index = decal.index; - - int n = chip_info->wire_data[wire.index].num_segments; - const WireSegmentPOD *p = chip_info->wire_data[wire.index].segments.get(); + int n = chip_info->wire_data[decal.index].num_segments; + const WireSegmentPOD *p = chip_info->wire_data[decal.index].segments.get(); GraphicElement::style_t style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE; @@ -524,6 +525,12 @@ std::vector Arch::getDecalGraphics(DecalId decal) const gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style); } + if (decal.type == DecalId::TYPE_PIP) { + const PipInfoPOD &p = chip_info->pip_data[decal.index]; + GraphicElement::style_t style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_HIDDEN; + gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style); + } + if (decal.type == DecalId::TYPE_BEL) { BelId bel; bel.index = decal.index; diff --git a/ice40/arch.h b/ice40/arch.h index a02e0ced..5dab414b 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -63,9 +63,11 @@ NPNR_PACKED_STRUCT(struct BelPortPOD { }); NPNR_PACKED_STRUCT(struct PipInfoPOD { + // RelPtr name; int32_t src, dst; int32_t delay; int8_t x, y; + int16_t src_seg, dst_seg; int16_t switch_mask; int32_t switch_index; }); diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 51fe169c..698cd173 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -390,9 +390,9 @@ with open(args.filename, "r") as f: wire_xy[mode[1]] = list() wire_xy[mode[1]].append((int(line[0]), int(line[1]))) if mode[1] not in wire_segments: - wire_segments[mode[1]] = set() + wire_segments[mode[1]] = dict() if ("TILE_WIRE_" + wname[2].upper().replace("/", "_")) in gfx_wire_ids: - wire_segments[mode[1]].add((wname[0], wname[1], gfx_wire_ids["TILE_WIRE_" + wname[2].upper().replace("/", "_")])) + wire_segments[mode[1]][(wname[0], wname[1])] = wname[2] continue if mode[0] in ("buffer", "routing"): @@ -1077,6 +1077,7 @@ for wire, info in enumerate(wireinfo): bba.r("wire_segments_%d" % wire, "segments") else: bba.u32(0, "segments") + bba.u8(info["x"], "x") bba.u8(info["y"], "y") bba.u8(wiretypes[wire_type(info["name"])], "type") @@ -1085,18 +1086,35 @@ for wire, info in enumerate(wireinfo): for wire in range(num_wires): if len(wire_segments[wire]): bba.l("wire_segments_%d" % wire, "WireSegmentPOD") - for seg in sorted(wire_segments[wire]): - bba.u8(seg[0], "x") - bba.u8(seg[1], "y") - bba.u16(seg[2], "index") + for xy, seg in sorted(wire_segments[wire].items()): + bba.u8(xy[0], "x") + bba.u8(xy[1], "y") + bba.u16(gfx_wire_ids["TILE_WIRE_" + seg.upper().replace("/", "_")], "index") bba.l("pip_data_%s" % dev_name, "PipInfoPOD") for info in pipinfo: + src_seg = -1 + src_segname = wire_names_r[info["src"]] + if (info["x"], info["y"]) in wire_segments[info["src"]]: + src_segname = wire_segments[info["src"]][(info["x"], info["y"])] + src_seg = gfx_wire_ids["TILE_WIRE_" + src_segname.upper().replace("/", "_")] + src_segname = src_segname.replace("/", ".") + + dst_seg = -1 + dst_segname = wire_names_r[info["dst"]] + if (info["x"], info["y"]) in wire_segments[info["dst"]]: + dst_segname = wire_segments[info["dst"]][(info["x"], info["y"])] + dst_seg = gfx_wire_ids["TILE_WIRE_" + dst_segname.upper().replace("/", "_")] + dst_segname = dst_segname.replace("/", ".") + + # bba.s("X%d/Y%d/%s->%s" % (info["x"], info["y"], src_segname, dst_segname), "name") bba.u32(info["src"], "src") bba.u32(info["dst"], "dst") bba.u32(info["delay"], "delay") bba.u8(info["x"], "x") bba.u8(info["y"], "y") + bba.u16(src_seg, "src_seg") + bba.u16(dst_seg, "dst_seg") bba.u16(info["switch_mask"], "switch_mask") bba.u32(info["switch_index"], "switch_index") diff --git a/ice40/gfx.cc b/ice40/gfx.cc index 19aaed13..aa2fc9ce 100644 --- a/ice40/gfx.cc +++ b/ice40/gfx.cc @@ -485,4 +485,223 @@ void gfxTileWire(std::vector &g, int x, int y, GfxTileWireId id, } } +static bool getWireXY_main(GfxTileWireId id, float &x, float &y) +{ + // Horizontal Span-4 Wires + + if (id >= TILE_WIRE_SP4_H_L_36 && id <= TILE_WIRE_SP4_H_L_47) { + int idx = (id - TILE_WIRE_SP4_H_L_36) + 48; + x = main_swbox_x1 + 0.0025 * (idx + 35); + y = main_swbox_y2; + return true; + } + + if (id >= TILE_WIRE_SP4_H_R_0 && id <= TILE_WIRE_SP4_H_R_47) { + int idx = id - TILE_WIRE_SP4_H_R_0; + x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35); + y = main_swbox_y2; + return true; + } + + // Vertical Span-4 Wires + + if (id >= TILE_WIRE_SP4_V_T_36 && id <= TILE_WIRE_SP4_V_T_47) { + int idx = (id - TILE_WIRE_SP4_V_T_36) + 48; + y = 1.0 - (0.03 + 0.0025 * (270 - idx)); + x = main_swbox_x1; + return true; + } + + if (id >= TILE_WIRE_SP4_V_B_0 && id <= TILE_WIRE_SP4_V_B_47) { + int idx = id - TILE_WIRE_SP4_V_B_0; + y = 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1))); + x = main_swbox_x1; + return true; + } + + // Horizontal Span-12 Wires + + if (id >= TILE_WIRE_SP12_H_L_22 && id <= TILE_WIRE_SP12_H_L_23) { + int idx = (id - TILE_WIRE_SP12_H_L_22) + 24; + x = main_swbox_x1 + 0.0025 * (idx + 5); + y = main_swbox_y2; + return true; + } + + if (id >= TILE_WIRE_SP12_H_R_0 && id <= TILE_WIRE_SP12_H_R_23) { + int idx = id - TILE_WIRE_SP12_H_R_0; + x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5); + y = main_swbox_y2; + return true; + } + + // Vertical Right Span-4 + + if (id >= TILE_WIRE_SP4_R_V_B_0 && id <= TILE_WIRE_SP4_R_V_B_47) { + int idx = id - TILE_WIRE_SP4_R_V_B_0; + y = 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1))); + x = main_swbox_x2; + return true; + } + + // Vertical Span-12 Wires + + if (id >= TILE_WIRE_SP12_V_T_22 && id <= TILE_WIRE_SP12_V_T_23) { + int idx = (id - TILE_WIRE_SP12_V_T_22) + 24; + y = 1.0 - (0.03 + 0.0025 * (300 - idx)); + x = main_swbox_x1; + return true; + } + + if (id >= TILE_WIRE_SP12_V_B_0 && id <= TILE_WIRE_SP12_V_B_23) { + int idx = id - TILE_WIRE_SP12_V_B_0; + y = 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1))); + x = main_swbox_x1; + return true; + } + + // Global2Local + + if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) { + int idx = id - TILE_WIRE_GLB2LOCAL_0; + x = main_swbox_x1 + 0.005 * (idx + 5); + y = main_swbox_y1; + return true; + } + + // GlobalNets + + if (id >= TILE_WIRE_GLB_NETWK_0 && id <= TILE_WIRE_GLB_NETWK_7) { + int idx = id - TILE_WIRE_GLB_NETWK_0; + x = main_swbox_x1; + y = main_swbox_y1 + 0.005 * (13 - idx); + return true; + } + + // Neighbours + + if (id >= TILE_WIRE_NEIGH_OP_BNL_0 && id <= TILE_WIRE_NEIGH_OP_TOP_7) { + int idx = id - TILE_WIRE_NEIGH_OP_BNL_0; + y = main_swbox_y2 - (0.0025 * (idx + 10) + 0.01 * (idx / 8)); + x = main_swbox_x1; + return true; + } + + // Local Tracks + + if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) { + int idx = id - TILE_WIRE_LOCAL_G0_0; + float yoff = (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075; + x = main_swbox_x2; + y = yoff + 0.005 * idx + 0.05 * (idx / 8); + return true; + } + + // LC Outputs + + if (id >= TILE_WIRE_LUTFF_0_OUT && id <= TILE_WIRE_LUTFF_7_OUT) { + int idx = id - TILE_WIRE_LUTFF_0_OUT; + y = 1.0 - (0.03 + 0.0025 * (152 + idx)); + x = main_swbox_x2; + return true; + } + + // LC Control + + if (id >= TILE_WIRE_LUTFF_GLOBAL_CEN && id <= TILE_WIRE_LUTFF_GLOBAL_S_R) { + int idx = id - TILE_WIRE_LUTFF_GLOBAL_CEN; + x = main_swbox_x2 - 0.005 * (idx + 5); + y = main_swbox_y1; + return true; + } + + return false; +} + +static bool getWireXY_local(GfxTileWireId id, float &x, float &y) +{ + if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) { + int idx = id - TILE_WIRE_LOCAL_G0_0; + float yoff = (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075; + x = local_swbox_x1; + y = yoff + 0.005 * idx + 0.05 * (idx / 8); + return true; + } + + if (id >= TILE_WIRE_LUTFF_0_IN_0 && id <= TILE_WIRE_LUTFF_7_IN_3) { + int idx = id - TILE_WIRE_LUTFF_0_IN_0; + int z = idx / 4; + int input = idx % 4; + x = local_swbox_x2; + y = (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch; + return true; + } + + return false; +} + +void pipGfx(std::vector &g, int x, int y, + float x1, float y1, float x2, float y2, + float swx1, float swy1, float swx2, float swy2, + GraphicElement::style_t style) +{ + float tx = 0.5 * (x1 + x2); + float ty = 0.5 * (y1 + y2); + + GraphicElement el; + el.type = GraphicElement::G_LINE; + el.style = style; + + if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) { + tx = x1 + 0.25 * fabsf(y1 - y2); + goto edge_pip; + } + + if (fabsf(x1 - swx2) < 0.001 && fabsf(x2 - swx2) < 0.001) { + tx = x1 - 0.25 * fabsf(y1 - y2); + goto edge_pip; + } + + if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) { + ty = y1 + 0.25 * fabsf(x1 - x2); + goto edge_pip; + } + + if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) { + ty = y1 + 0.25 * fabsf(x1 - x2); + goto edge_pip; + } + + el.x1 = x + x1; + el.y1 = y + y1; + el.x2 = x + x2; + el.y2 = y + y2; + g.push_back(el); + return; + +edge_pip: + el.x1 = x + x1; + el.y1 = y + y1; + el.x2 = x + tx; + el.y2 = y + ty; + g.push_back(el); + + el.x1 = x + tx; + el.y1 = y + ty; + el.x2 = x + x2; + el.y2 = y + y2; + g.push_back(el); +} + +void gfxTilePip(std::vector &g, int x, int y, GfxTileWireId src, GfxTileWireId dst, GraphicElement::style_t style) +{ + float x1, y1, x2, y2; + + if (getWireXY_main(src, x1, y1) && getWireXY_main(dst, x2, y2)) + pipGfx(g, x, y, x1, y1, x2, y2, main_swbox_x1, main_swbox_y1, main_swbox_x2, main_swbox_y2, style); + + if (getWireXY_local(src, x1, y1) && getWireXY_local(dst, x2, y2)) + pipGfx(g, x, y, x1, y1, x2, y2, local_swbox_x1, local_swbox_y1, local_swbox_x2, local_swbox_y2, style); +} + NEXTPNR_NAMESPACE_END diff --git a/ice40/gfx.h b/ice40/gfx.h index a65f7683..a1cbd65b 100644 --- a/ice40/gfx.h +++ b/ice40/gfx.h @@ -468,6 +468,7 @@ enum GfxTileWireId }; void gfxTileWire(std::vector &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style); +void gfxTilePip(std::vector &g, int x, int y, GfxTileWireId src, GfxTileWireId dst, GraphicElement::style_t style); NEXTPNR_NAMESPACE_END