diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 4a0b31b5..91331f7e 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -456,7 +456,7 @@ std::vector Arch::getDecalGraphics(DecalId decal) const el.x1 = bel.location.x + logic_cell_x1; el.x2 = bel.location.x + logic_cell_x2; el.y1 = bel.location.y + logic_cell_y1 + (2 * z) * logic_cell_pitch; - el.y2 = bel.location.y + logic_cell_y2 + (2 * z + 1) * logic_cell_pitch; + el.y2 = bel.location.y + logic_cell_y2 + (2 * z + 0.5f) * logic_cell_pitch; ret.push_back(el); } } diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 95256732..6d43b369 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -385,6 +385,7 @@ std::vector get_pll_tiles(Context *ctx, BelId bel) std::string name = ctx->locInfo(bel)->bel_data[bel.index].name.get(); std::vector tiles; Loc loc = ctx->getBelLocation(bel); + static const std::set pll1_lr = {"PLL1_LR", "BANKREF4"}; if (name == "EHXPLL_UL") { tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x - 1, "PLL0_UL")); @@ -394,7 +395,7 @@ std::vector get_pll_tiles(Context *ctx, BelId bel) tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x + 1, "BANKREF8")); } else if (name == "EHXPLL_LR") { tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x, "PLL0_LR")); - tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x - 1, "PLL1_LR")); + tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x - 1, pll1_lr)); } else if (name == "EHXPLL_UR") { tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x - 1, "PLL0_UR")); tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x - 1, "PLL1_UR")); diff --git a/gui/designwidget.cc b/gui/designwidget.cc index a45752fc..20a81a7d 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -20,6 +20,7 @@ #include "designwidget.h" #include +#include #include #include #include @@ -46,15 +47,28 @@ void TreeView::mouseMoveEvent(QMouseEvent *event) void TreeView::leaveEvent(QEvent *event) { Q_EMIT hoverIndexChanged(QModelIndex()); } -DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), selectionModel(nullptr) +DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr) { + tabWidget = new QTabWidget(); + // Add tree view - treeView = new TreeView(); - treeModel = new TreeModel::Model(); - treeView->setModel(treeModel); - treeView->setContextMenuPolicy(Qt::CustomContextMenu); - treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); - treeView->viewport()->setMouseTracking(true); + for (int i = 0; i < 6; i++) { + treeView[i] = new TreeView(); + treeModel[i] = new TreeModel::Model(); + treeView[i]->setModel(treeModel[i]); + treeView[i]->setContextMenuPolicy(Qt::CustomContextMenu); + treeView[i]->setSelectionMode(QAbstractItemView::ExtendedSelection); + treeView[i]->viewport()->setMouseTracking(true); + selectionModel[i] = nullptr; + } + + tabWidget->addTab(treeView[0], "Bels"); + tabWidget->addTab(treeView[1], "Wires"); + tabWidget->addTab(treeView[2], "Pips"); + tabWidget->addTab(treeView[3], "Cells"); + tabWidget->addTab(treeView[4], "Nets"); + tabWidget->addTab(treeView[5], "Groups"); + // Add property view variantManager = new QtVariantPropertyManager(this); readOnlyManager = new QtVariantPropertyManager(this); @@ -80,7 +94,14 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel connect(actionFirst, &QAction::triggered, this, [this] { history_ignore = true; history_index = 0; - selectionModel->setCurrentIndex(history.at(history_index), QItemSelectionModel::ClearAndSelect); + auto h = history.at(history_index); + if (tabWidget->currentIndex() != h.first) { + selectionModel[tabWidget->currentIndex()]->clearSelection(); + tabWidget->setCurrentIndex(h.first); + selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::Select); + } else { + selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::ClearAndSelect); + } updateButtons(); }); @@ -90,7 +111,14 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel connect(actionPrev, &QAction::triggered, this, [this] { history_ignore = true; history_index--; - selectionModel->setCurrentIndex(history.at(history_index), QItemSelectionModel::ClearAndSelect); + auto h = history.at(history_index); + if (tabWidget->currentIndex() != h.first) { + selectionModel[tabWidget->currentIndex()]->clearSelection(); + tabWidget->setCurrentIndex(h.first); + selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::Select); + } else { + selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::ClearAndSelect); + } updateButtons(); }); @@ -100,7 +128,14 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel connect(actionNext, &QAction::triggered, this, [this] { history_ignore = true; history_index++; - selectionModel->setCurrentIndex(history.at(history_index), QItemSelectionModel::ClearAndSelect); + auto h = history.at(history_index); + if (tabWidget->currentIndex() != h.first) { + selectionModel[tabWidget->currentIndex()]->clearSelection(); + tabWidget->setCurrentIndex(h.first); + selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::Select); + } else { + selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::ClearAndSelect); + } updateButtons(); }); @@ -110,7 +145,14 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel connect(actionLast, &QAction::triggered, this, [this] { history_ignore = true; history_index = int(history.size() - 1); - selectionModel->setCurrentIndex(history.at(history_index), QItemSelectionModel::ClearAndSelect); + auto h = history.at(history_index); + if (tabWidget->currentIndex() != h.first) { + selectionModel[tabWidget->currentIndex()]->clearSelection(); + tabWidget->setCurrentIndex(h.first); + selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::Select); + } else { + selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::ClearAndSelect); + } updateButtons(); }); @@ -120,11 +162,14 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel connect(actionClear, &QAction::triggered, this, [this] { history_index = -1; history.clear(); - QModelIndex index = selectionModel->selectedIndexes().at(0); - if (index.isValid()) { - ElementType type = treeModel->nodeFromIndex(index)->type(); - if (type != ElementType::NONE) - addToHistory(index); + int num = tabWidget->currentIndex(); + if (selectionModel[num]->selectedIndexes().size() > 0) { + QModelIndex index = selectionModel[num]->selectedIndexes().at(0); + if (index.isValid()) { + ElementType type = treeModel[num]->nodeFromIndex(index)->type(); + if (type != ElementType::NONE) + addToHistory(num, index); + } } updateButtons(); }); @@ -142,7 +187,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel vbox1->setSpacing(5); vbox1->setContentsMargins(0, 0, 0, 0); vbox1->addWidget(searchEdit); - vbox1->addWidget(treeView); + vbox1->addWidget(tabWidget); QWidget *toolbarWidget = new QWidget(); QHBoxLayout *hbox = new QHBoxLayout; @@ -177,11 +222,18 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel connect(propertyEditor->treeWidget(), &QTreeWidget::itemDoubleClicked, this, &DesignWidget::onItemDoubleClicked); connect(propertyEditor, &QtTreePropertyBrowser::hoverPropertyChanged, this, &DesignWidget::onHoverPropertyChanged); - connect(treeView, &TreeView::customContextMenuRequested, this, &DesignWidget::prepareMenuTree); - connect(treeView, &TreeView::doubleClicked, this, &DesignWidget::onDoubleClicked); - connect(treeView, &TreeView::hoverIndexChanged, this, &DesignWidget::onHoverIndexChanged); - selectionModel = treeView->selectionModel(); - connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &DesignWidget::onSelectionChanged); + for (int num = 0; num < 6; num++) { + connect(treeView[num], &TreeView::customContextMenuRequested, + [this, num](const QPoint &pos) { prepareMenuTree(num, pos); }); + connect(treeView[num], &TreeView::doubleClicked, [this](const QModelIndex &index) { onDoubleClicked(index); }); + connect(treeView[num], &TreeView::hoverIndexChanged, + [this, num](QModelIndex index) { onHoverIndexChanged(num, index); }); + selectionModel[num] = treeView[num]->selectionModel(); + connect(selectionModel[num], &QItemSelectionModel::selectionChanged, + [this, num](const QItemSelection &selected, const QItemSelection &deselected) { + onSelectionChanged(num, selected, deselected); + }); + } history_index = -1; history_ignore = false; @@ -207,13 +259,13 @@ void DesignWidget::updateButtons() actionLast->setEnabled(history_index < (count - 1)); } -void DesignWidget::addToHistory(QModelIndex item) +void DesignWidget::addToHistory(int tab, QModelIndex 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.push_back(std::make_pair(tab, item)); history_index++; } history_ignore = false; @@ -236,7 +288,58 @@ void DesignWidget::newContext(Context *ctx) { std::lock_guard lock_ui(ctx->ui_mutex); std::lock_guard lock(ctx->mutex); - treeModel->loadContext(ctx); + + { + TreeModel::ElementXYRoot::ElementMap belMap; + for (const auto& bel : ctx->getBels()) { + auto loc = ctx->getBelLocation(bel); + belMap[std::pair(loc.x, loc.y)].push_back(bel); + } + auto belGetter = [](Context *ctx, BelId id) { return ctx->getBelName(id); }; + + getTreeByElementType(ElementType::BEL) + ->loadData(ctx, std::unique_ptr>( + new TreeModel::ElementXYRoot(ctx, belMap, belGetter, ElementType::BEL))); + } + + { + TreeModel::ElementXYRoot::ElementMap wireMap; +#ifdef ARCH_ICE40 + for (int i = 0; i < ctx->chip_info->num_wires; i++) { + const auto wire = &ctx->chip_info->wire_data[i]; + WireId wireid; + wireid.index = i; + wireMap[std::pair(wire->x, wire->y)].push_back(wireid); + } +#endif +#ifdef ARCH_ECP5 + for (const auto& wire : ctx->getWires()) { + wireMap[std::pair(wire.location.x, wire.location.y)].push_back(wire); + } +#endif + auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); }; + getTreeByElementType(ElementType::WIRE) + ->loadData(ctx, std::unique_ptr>( + new TreeModel::ElementXYRoot(ctx, wireMap, wireGetter, ElementType::WIRE))); + } + + { + TreeModel::ElementXYRoot::ElementMap pipMap; + for (const auto& pip : ctx->getPips()) { + auto loc = ctx->getPipLocation(pip); + pipMap[std::pair(loc.x, loc.y)].push_back(pip); + } + auto pipGetter = [](Context *ctx, PipId id) { return ctx->getPipName(id); }; + + getTreeByElementType(ElementType::PIP) + ->loadData(ctx, std::unique_ptr>( + new TreeModel::ElementXYRoot(ctx, pipMap, pipGetter, ElementType::PIP))); + } + + getTreeByElementType(ElementType::CELL) + ->loadData(ctx, std::unique_ptr(new TreeModel::IdStringList(ElementType::CELL))); + getTreeByElementType(ElementType::NET) + ->loadData(ctx, std::unique_ptr(new TreeModel::IdStringList(ElementType::NET))); } updateTree(); } @@ -260,7 +363,18 @@ void DesignWidget::updateTree() { std::lock_guard lock_ui(ctx->ui_mutex); std::lock_guard lock(ctx->mutex); - treeModel->updateCellsNets(ctx); + + std::vector cells; + for (auto &pair : ctx->cells) { + cells.push_back(pair.first); + } + std::vector nets; + for (auto &pair : ctx->nets) { + nets.push_back(pair.first); + } + + getTreeByElementType(ElementType::CELL)->updateElements(cells); + getTreeByElementType(ElementType::NET)->updateElements(nets); } } QtProperty *DesignWidget::addTopLevelProperty(const QString &id) @@ -315,6 +429,40 @@ ElementType DesignWidget::getElementTypeByName(QString type) return ElementType::NONE; } +TreeModel::Model *DesignWidget::getTreeByElementType(ElementType type) +{ + if (type == ElementType::NONE) + return nullptr; + if (type == ElementType::BEL) + return treeModel[0]; + if (type == ElementType::WIRE) + return treeModel[1]; + if (type == ElementType::PIP) + return treeModel[2]; + if (type == ElementType::NET) + return treeModel[3]; + if (type == ElementType::CELL) + return treeModel[4]; + return nullptr; +} +int DesignWidget::getIndexByElementType(ElementType type) +{ + if (type == ElementType::NONE) + return -1; + 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; + if (type == ElementType::GROUP) + return 5; + return -1; +} void DesignWidget::addProperty(QtProperty *topItem, int propertyType, const QString &name, QVariant value, const ElementType &type) { @@ -333,6 +481,12 @@ QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name) return item; } +void DesignWidget::clearAllSelectionModels() +{ + for (int i = 0; i <= getIndexByElementType(ElementType::GROUP); i++) + selectionModel[i]->clearSelection(); +} + void DesignWidget::onClickedBel(BelId bel, bool keep) { boost::optional item; @@ -340,14 +494,20 @@ void DesignWidget::onClickedBel(BelId bel, bool keep) std::lock_guard lock_ui(ctx->ui_mutex); std::lock_guard lock(ctx->mutex); - item = treeModel->nodeForIdType(ElementType::BEL, ctx->getBelName(bel)); + item = getTreeByElementType(ElementType::BEL)->nodeForId(ctx->getBelName(bel)); if (!item) return; Q_EMIT selected(getDecals(ElementType::BEL, ctx->getBelName(bel)), keep); } - selectionModel->setCurrentIndex(treeModel->indexFromNode(*item), - keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); + int index = getIndexByElementType(ElementType::BEL); + if (!keep) + clearAllSelectionModels(); + if (tabWidget->currentIndex() != index) { + tabWidget->setCurrentIndex(index); + } + selectionModel[index]->setCurrentIndex(getTreeByElementType(ElementType::BEL)->indexFromNode(*item), + keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); } void DesignWidget::onClickedWire(WireId wire, bool keep) @@ -357,14 +517,19 @@ void DesignWidget::onClickedWire(WireId wire, bool keep) std::lock_guard lock_ui(ctx->ui_mutex); std::lock_guard lock(ctx->mutex); - item = treeModel->nodeForIdType(ElementType::WIRE, ctx->getWireName(wire)); + item = getTreeByElementType(ElementType::WIRE)->nodeForId(ctx->getWireName(wire)); if (!item) return; Q_EMIT selected(getDecals(ElementType::WIRE, ctx->getWireName(wire)), keep); } - selectionModel->setCurrentIndex(treeModel->indexFromNode(*item), - keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); + int index = getIndexByElementType(ElementType::WIRE); + if (!keep) + clearAllSelectionModels(); + if (tabWidget->currentIndex() != index) + tabWidget->setCurrentIndex(index); + selectionModel[index]->setCurrentIndex(getTreeByElementType(ElementType::WIRE)->indexFromNode(*item), + keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); } void DesignWidget::onClickedPip(PipId pip, bool keep) @@ -374,41 +539,61 @@ void DesignWidget::onClickedPip(PipId pip, bool keep) std::lock_guard lock_ui(ctx->ui_mutex); std::lock_guard lock(ctx->mutex); - item = treeModel->nodeForIdType(ElementType::PIP, ctx->getPipName(pip)); + item = getTreeByElementType(ElementType::PIP)->nodeForId(ctx->getPipName(pip)); if (!item) return; Q_EMIT selected(getDecals(ElementType::PIP, ctx->getPipName(pip)), keep); } - selectionModel->setCurrentIndex(treeModel->indexFromNode(*item), - keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); + + int index = getIndexByElementType(ElementType::PIP); + if (!keep) + clearAllSelectionModels(); + if (tabWidget->currentIndex() != index) + tabWidget->setCurrentIndex(index); + selectionModel[index]->setCurrentIndex(getTreeByElementType(ElementType::PIP)->indexFromNode(*item), + keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); } -void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelection &) +void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QItemSelection &) { - if (selectionModel->selectedIndexes().size() == 0) - return; - - if (selectionModel->selectedIndexes().size() > 1) { - std::vector decals; - for (auto index : selectionModel->selectedIndexes()) { - TreeModel::Item *item = treeModel->nodeFromIndex(index); + int num_selected = 0; + std::vector decals; + for (int i = 0; i <= getIndexByElementType(ElementType::GROUP); i++) { + num_selected += selectionModel[i]->selectedIndexes().size(); + for (auto index : selectionModel[i]->selectedIndexes()) { + TreeModel::Item *item = treeModel[i]->nodeFromIndex(index); std::vector d = getDecals(item->type(), item->id()); std::move(d.begin(), d.end(), std::back_inserter(decals)); } + } + + // Keep other tree seleciton only if Control is pressed + if (num_selected > 1 && QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) == true) { Q_EMIT selected(decals, false); return; } - QModelIndex index = selectionModel->selectedIndexes().at(0); + + // For deselect and multiple select just send all + if (selectionModel[num]->selectedIndexes().size() != 1) { + Q_EMIT selected(decals, false); + return; + } + + QModelIndex index = selectionModel[num]->selectedIndexes().at(0); if (!index.isValid()) return; - TreeModel::Item *clickItem = treeModel->nodeFromIndex(index); + TreeModel::Item *clickItem = treeModel[num]->nodeFromIndex(index); ElementType type = clickItem->type(); if (type == ElementType::NONE) return; - addToHistory(index); + // Clear other tab selections + for (int i = 0; i <= getIndexByElementType(ElementType::GROUP); i++) + if (i!=num) selectionModel[i]->clearSelection(); + + addToHistory(num, index); clearProperties(); @@ -714,7 +899,7 @@ void DesignWidget::prepareMenuProperty(const QPoint &pos) if (type == ElementType::NONE) continue; IdString value = ctx->id(selectedProperty->valueText().toStdString()); - auto node = treeModel->nodeForIdType(type, value); + auto node = getTreeByElementType(type)->nodeForId(value); if (!node) continue; items.append(*node); @@ -755,17 +940,19 @@ void DesignWidget::prepareMenuProperty(const QPoint &pos) menu.exec(tree->mapToGlobal(pos)); } -void DesignWidget::prepareMenuTree(const QPoint &pos) +void DesignWidget::prepareMenuTree(int num, const QPoint &pos) { int selectedIndex = -1; - if (selectionModel->selectedIndexes().size() == 0) + if (selectionModel[num]->selectedIndexes().size() == 0) return; QList items; - for (auto index : selectionModel->selectedIndexes()) { - TreeModel::Item *item = treeModel->nodeFromIndex(index); - items.append(item); + for (int i = 0; i <= getIndexByElementType(ElementType::GROUP); i++) { + for (auto index : selectionModel[i]->selectedIndexes()) { + TreeModel::Item *item = treeModel[i]->nodeFromIndex(index); + items.append(item); + } } if (items.size() == 1) { TreeModel::Item *item = items.at(0); @@ -787,23 +974,31 @@ void DesignWidget::prepareMenuTree(const QPoint &pos) action->setChecked(true); connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); }); } - menu.exec(treeView->mapToGlobal(pos)); + menu.exec(treeView[num]->mapToGlobal(pos)); } void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column) { QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property(); ElementType type = getElementTypeByName(selectedProperty->propertyId()); - auto it = treeModel->nodeForIdType(type, ctx->id(selectedProperty->valueText().toStdString())); - if (it) - selectionModel->setCurrentIndex(treeModel->indexFromNode(*it), QItemSelectionModel::ClearAndSelect); + if (type == ElementType::NONE) + return; + auto it = getTreeByElementType(type)->nodeForId(ctx->id(selectedProperty->valueText().toStdString())); + if (it) { + int num = getIndexByElementType(type); + clearAllSelectionModels(); + if (tabWidget->currentIndex() != num) + tabWidget->setCurrentIndex(num); + selectionModel[num]->setCurrentIndex(getTreeByElementType(type)->indexFromNode(*it), + QItemSelectionModel::ClearAndSelect); + } } void DesignWidget::onDoubleClicked(const QModelIndex &index) { Q_EMIT zoomSelected(); } void DesignWidget::onSearchInserted() { - if (currentSearch == searchEdit->text()) { + if (currentSearch == searchEdit->text() && currentIndexTab == tabWidget->currentIndex()) { currentIndex++; if (currentIndex >= currentSearchIndexes.size()) currentIndex = 0; @@ -812,17 +1007,19 @@ void DesignWidget::onSearchInserted() std::lock_guard lock(ctx->mutex); currentSearch = searchEdit->text(); - currentSearchIndexes = treeModel->search(searchEdit->text()); + currentSearchIndexes = treeModel[tabWidget->currentIndex()]->search(searchEdit->text()); currentIndex = 0; + currentIndexTab = tabWidget->currentIndex(); } if (currentSearchIndexes.size() > 0 && currentIndex < currentSearchIndexes.size()) - selectionModel->setCurrentIndex(currentSearchIndexes.at(currentIndex), QItemSelectionModel::ClearAndSelect); + selectionModel[tabWidget->currentIndex()]->setCurrentIndex(currentSearchIndexes.at(currentIndex), + QItemSelectionModel::ClearAndSelect); } -void DesignWidget::onHoverIndexChanged(QModelIndex index) +void DesignWidget::onHoverIndexChanged(int num, QModelIndex index) { if (index.isValid()) { - TreeModel::Item *item = treeModel->nodeFromIndex(index); + TreeModel::Item *item = treeModel[num]->nodeFromIndex(index); if (item->type() != ElementType::NONE) { std::vector decals = getDecals(item->type(), item->id()); if (decals.size() > 0) @@ -841,7 +1038,7 @@ void DesignWidget::onHoverPropertyChanged(QtBrowserItem *item) if (type != ElementType::NONE) { IdString value = ctx->id(selectedProperty->valueText().toStdString()); if (value != IdString()) { - auto node = treeModel->nodeForIdType(type, value); + auto node = getTreeByElementType(type)->nodeForId(value); if (node) { std::vector decals = getDecals((*node)->type(), (*node)->id()); if (decals.size() > 0) diff --git a/gui/designwidget.h b/gui/designwidget.h index 0248d2c7..89c6e702 100644 --- a/gui/designwidget.h +++ b/gui/designwidget.h @@ -21,6 +21,7 @@ #define DESIGNWIDGET_H #include +#include #include #include #include "nextpnr.h" @@ -65,11 +66,14 @@ class DesignWidget : public QWidget const ElementType &type = ElementType::NONE); QString getElementTypeName(ElementType type); ElementType getElementTypeByName(QString type); + TreeModel::Model *getTreeByElementType(ElementType type); + int getIndexByElementType(ElementType type); int getElementIndex(ElementType type); void updateButtons(); - void addToHistory(QModelIndex item); + void addToHistory(int tab, QModelIndex item); std::vector getDecals(ElementType type, IdString value); void updateHighlightGroup(QList item, int group); + void clearAllSelectionModels(); Q_SIGNALS: void selected(std::vector decal, bool keep); void highlight(std::vector decal, int group); @@ -78,12 +82,12 @@ class DesignWidget : public QWidget private Q_SLOTS: void prepareMenuProperty(const QPoint &pos); - void prepareMenuTree(const QPoint &pos); - void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void prepareMenuTree(int num, const QPoint &pos); + void onSelectionChanged(int num, const QItemSelection &selected, const QItemSelection &deselected); void onItemDoubleClicked(QTreeWidgetItem *item, int column); void onDoubleClicked(const QModelIndex &index); void onSearchInserted(); - void onHoverIndexChanged(QModelIndex index); + void onHoverIndexChanged(int num, QModelIndex index); void onHoverPropertyChanged(QtBrowserItem *item); public Q_SLOTS: void newContext(Context *ctx); @@ -95,9 +99,11 @@ class DesignWidget : public QWidget private: Context *ctx; - TreeView *treeView; - QItemSelectionModel *selectionModel; - TreeModel::Model *treeModel; + QTabWidget *tabWidget; + + TreeView *treeView[6]; + QItemSelectionModel *selectionModel[6]; + TreeModel::Model *treeModel[6]; QLineEdit *searchEdit; QtVariantPropertyManager *variantManager; QtVariantPropertyManager *readOnlyManager; @@ -108,7 +114,7 @@ class DesignWidget : public QWidget QMap propertyToId; QMap idToProperty; - std::vector history; + std::vector> history; int history_index; bool history_ignore; @@ -124,6 +130,7 @@ class DesignWidget : public QWidget QString currentSearch; QList currentSearchIndexes; int currentIndex; + int currentIndexTab; }; NEXTPNR_NAMESPACE_END diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 24fbc35d..45b35e6b 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -56,7 +56,8 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent) colors_.highlight[7] = QColor("#da70d6"); rendererArgs_->changed = false; - rendererArgs_->flags.zoomOutbound = true; + rendererArgs_->gridChanged = false; + rendererArgs_->zoomOutbound = true; auto fmt = format(); fmt.setMajorVersion(3); @@ -86,12 +87,17 @@ FPGAViewWidget::~FPGAViewWidget() {} void FPGAViewWidget::newContext(Context *ctx) { ctx_ = ctx; + { + QMutexLocker lock(&rendererArgsLock_); + + rendererArgs_->gridChanged = true; + } onSelectedArchItem(std::vector(), false); for (int i = 0; i < 8; i++) onHighlightGroupChanged(std::vector(), i); { QMutexLocker lock(&rendererArgsLock_); - rendererArgs_->flags.zoomOutbound = true; + rendererArgs_->zoomOutbound = true; } pokeRenderer(); } @@ -109,19 +115,6 @@ void FPGAViewWidget::initializeGL() QtImGui::initialize(this); glClearColor(colors_.background.red() / 255, colors_.background.green() / 255, colors_.background.blue() / 255, 0.0); - - - { - QMutexLocker locker(&rendererDataLock_); - // Render grid. - auto grid = LineShaderData(); - for (float i = -100.0f; i < 100.0f; i += 1.0f) { - PolyLine(-100.0f, i, 100.0f, i).build(grid); - PolyLine(i, -100.0f, i, 100.0f).build(grid); - } - grid.last_render = 1; - lineShader_.update_vbos(GraphicElement::STYLE_GRID, grid); - } } float FPGAViewWidget::PickedElement::distance(Context *ctx, float wx, float wy) const @@ -346,22 +339,7 @@ void FPGAViewWidget::paintGL() lineShader_.draw(GraphicElement::STYLE_HOVER, colors_.hovered, thick2Px, matrix); - // Flags from pipeline. - PassthroughFlags flags = rendererData_->flags; - - // Check flags passed through pipeline. - if (flags.zoomOutbound) { - // If we're doing init zoomOutbound, make sure we're actually drawing - // something already. - if (rendererData_->gfxByStyle[GraphicElement::STYLE_FRAME].vertices.size() != 0) { - zoomOutbound(); - flags.zoomOutbound = false; - { - QMutexLocker lock(&rendererArgsLock_); - rendererArgs_->flags.zoomOutbound = false; - } - } - } + // Render ImGui QtImGui::newFrame(); QMutexLocker lock(&rendererArgsLock_); if (!(rendererArgs_->hoveredDecal == DecalXY()) && rendererArgs_->hintText.size() > 0) @@ -444,7 +422,7 @@ void FPGAViewWidget::renderLines(void) DecalXY hoveredDecal; std::vector highlightedDecals[8]; bool highlightedOrSelectedChanged; - PassthroughFlags flags; + bool gridChanged; { // Take the renderer arguments lock, copy over all we need. QMutexLocker lock(&rendererArgsLock_); @@ -456,8 +434,9 @@ void FPGAViewWidget::renderLines(void) highlightedDecals[i] = rendererArgs_->highlightedDecals[i]; highlightedOrSelectedChanged = rendererArgs_->changed; + gridChanged = rendererArgs_->gridChanged; rendererArgs_->changed = false; - flags = rendererArgs_->flags; + rendererArgs_->gridChanged = false; } // Render decals if necessary. @@ -528,6 +507,7 @@ void FPGAViewWidget::renderLines(void) // If we're not re-rendering any highlights/selections, let's // copy them over from teh current object. + data->gfxGrid = rendererData_->gfxGrid; if (!highlightedOrSelectedChanged) { data->gfxSelected = rendererData_->gfxSelected; data->gfxHovered = rendererData_->gfxHovered; @@ -539,7 +519,19 @@ void FPGAViewWidget::renderLines(void) rendererData_ = std::move(data); } } - + if (gridChanged) + { + QMutexLocker locker(&rendererDataLock_); + rendererData_->gfxGrid.clear(); + // Render grid. + for (float i = 0.0f; i < 1.0f * ctx_->getGridDimX() + 1; i += 1.0f) { + PolyLine(i, 0.0f, i, 1.0f * ctx_->getGridDimY()).build(rendererData_->gfxGrid); + } + for (float i = 0.0f; i < 1.0f * ctx_->getGridDimY() + 1; i += 1.0f) { + PolyLine(0.0f, i, 1.0f * ctx_->getGridDimX(), i).build(rendererData_->gfxGrid); + } + rendererData_->gfxGrid.last_render++; + } if (highlightedOrSelectedChanged) { QMutexLocker locker(&rendererDataLock_); @@ -573,8 +565,12 @@ void FPGAViewWidget::renderLines(void) } { - QMutexLocker locker(&rendererDataLock_); - rendererData_->flags = flags; + QMutexLocker lock(&rendererArgsLock_); + + if (rendererArgs_->zoomOutbound) { + zoomOutbound(); + rendererArgs_->zoomOutbound = false; + } } } @@ -851,7 +847,8 @@ void FPGAViewWidget::zoomSelected() { { QMutexLocker lock(&rendererDataLock_); - zoomToBB(rendererData_->bbSelected, 0.5f, true); + if (rendererData_->bbSelected.x0() != std::numeric_limits::infinity()) + zoomToBB(rendererData_->bbSelected, 0.5f, true); } update(); } @@ -876,6 +873,8 @@ void FPGAViewWidget::leaveEvent(QEvent *event) void FPGAViewWidget::update_vbos() { + lineShader_.update_vbos(GraphicElement::STYLE_GRID, rendererData_->gfxGrid); + for (int style = GraphicElement::STYLE_FRAME; style < GraphicElement::STYLE_HIGHLIGHTED0; style++) { diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index e76e6f32..3c0cfbbd 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -127,7 +127,7 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions private: const float zoomNear_ = 0.1f; // do not zoom closer than this - float zoomFar_ = 10.0f; // do not zoom further than this + float zoomFar_ = 10.0f; // do not zoom further than this const float zoomLvl1_ = 1.0f; const float zoomLvl2_ = 5.0f; @@ -240,21 +240,6 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions QColor highlight[8]; } colors_; - // Flags that are passed through from renderer arguments to renderer data. - // These are used by the UI code to signal events that will only fire when - // the next frame gets rendered. - struct PassthroughFlags - { - bool zoomOutbound; - - PassthroughFlags() : zoomOutbound(false) {} - PassthroughFlags &operator=(const PassthroughFlags &other) noexcept - { - zoomOutbound = other.zoomOutbound; - return *this; - } - }; - struct RendererArgs { // Decals that he user selected. @@ -265,19 +250,22 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions DecalXY hoveredDecal; // Whether to render the above three or skip it. bool changed; + // Whether to render grid or skip it. + bool gridChanged; - // Flags to pass back into the RendererData. - PassthroughFlags flags; + // Flags for rendering. + bool zoomOutbound; // Hint text std::string hintText; // cursor pos - int x,y; + int x, y; }; std::unique_ptr rendererArgs_; QMutex rendererArgsLock_; struct RendererData { + LineShaderData gfxGrid; LineShaderData gfxByStyle[GraphicElement::STYLE_MAX]; LineShaderData gfxSelected; LineShaderData gfxHovered; @@ -288,8 +276,6 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions PickQuadTree::BoundingBox bbSelected; // Quadtree for picking objects. std::unique_ptr qt; - // Flags from args. - PassthroughFlags flags; }; std::unique_ptr rendererData_; QMutex rendererDataLock_; diff --git a/gui/quadtree.h b/gui/quadtree.h index a6c38a85..9fcddf73 100644 --- a/gui/quadtree.h +++ b/gui/quadtree.h @@ -266,20 +266,20 @@ template class QuadTreeNode splitx_ = (bound_.x1_ - bound_.x0_) / 2 + bound_.x0_; splity_ = (bound_.y1_ - bound_.y0_) / 2 + bound_.y0_; // Create the new children. - children_ = decltype(children_)(new QuadTreeNode[4] { - // Note: not using [NW] = QuadTreeNode because that seems to - // crash g++ 7.3.0. - /* NW */ QuadTreeNode(BoundingBox(bound_.x0_, bound_.y0_, splitx_, splity_), - depth_ + 1, max_elems_), - /* NE */ - QuadTreeNode(BoundingBox(splitx_, bound_.y0_, bound_.x1_, splity_), - depth_ + 1, max_elems_), - /* SW */ - QuadTreeNode(BoundingBox(bound_.x0_, splity_, splitx_, bound_.y1_), - depth_ + 1, max_elems_), - /* SE */ - QuadTreeNode(BoundingBox(splitx_, splity_, bound_.x1_, bound_.y1_), - depth_ + 1, max_elems_), + children_ = decltype(children_)(new QuadTreeNode[4]{ + // Note: not using [NW] = QuadTreeNode because that seems to + // crash g++ 7.3.0. + /* NW */ QuadTreeNode(BoundingBox(bound_.x0_, bound_.y0_, splitx_, splity_), + depth_ + 1, max_elems_), + /* NE */ + QuadTreeNode(BoundingBox(splitx_, bound_.y0_, bound_.x1_, splity_), + depth_ + 1, max_elems_), + /* SW */ + QuadTreeNode(BoundingBox(bound_.x0_, splity_, splitx_, bound_.y1_), + depth_ + 1, max_elems_), + /* SE */ + QuadTreeNode(BoundingBox(splitx_, splity_, bound_.x1_, bound_.y1_), + depth_ + 1, max_elems_), }); // Move all elements to where they belong. auto it = elems_.begin(); diff --git a/gui/treemodel.cc b/gui/treemodel.cc index 33dd6a96..b834c682 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -154,80 +154,21 @@ Model::Model(QObject *parent) : QAbstractItemModel(parent), root_(new Item("Elem Model::~Model() {} -void Model::loadContext(Context *ctx) +void Model::loadData(Context *ctx, std::unique_ptr data) { - if (!ctx) - return; - ctx_ = ctx; - beginResetModel(); - - // Currently we lack an API to get a proper hierarchy of bels/pip/wires - // cross-arch. So we only do this for ICE40 by querying the ChipDB - // directly. - // TODO(q3k): once AnyId and the tree API land in Arch, move this over. -#ifdef ARCH_ICE40 - { - std::map, std::vector> belMap; - for (auto bel : ctx->getBels()) { - auto loc = ctx->getBelLocation(bel); - belMap[std::pair(loc.x, loc.y)].push_back(bel); - } - auto belGetter = [](Context *ctx, BelId id) { return ctx->getBelName(id); }; - bel_root_ = std::unique_ptr( - new BelXYRoot(ctx, "Bels", root_.get(), belMap, belGetter, ElementType::BEL)); - - std::map, std::vector> wireMap; - for (int i = 0; i < ctx->chip_info->num_wires; i++) { - const auto wire = &ctx->chip_info->wire_data[i]; - WireId wireid; - wireid.index = i; - wireMap[std::pair(wire->x, wire->y)].push_back(wireid); - } - auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); }; - wire_root_ = std::unique_ptr( - new WireXYRoot(ctx, "Wires", root_.get(), wireMap, wireGetter, ElementType::WIRE)); - - std::map, std::vector> pipMap; - for (int i = 0; i < ctx->chip_info->num_pips; i++) { - const auto pip = &ctx->chip_info->pip_data[i]; - PipId pipid; - pipid.index = i; - pipMap[std::pair(pip->x, pip->y)].push_back(pipid); - } - auto pipGetter = [](Context *ctx, PipId id) { return ctx->getPipName(id); }; - pip_root_ = std::unique_ptr( - new PipXYRoot(ctx, "Pips", root_.get(), pipMap, pipGetter, ElementType::PIP)); - } -#endif - - cell_root_ = std::unique_ptr(new IdStringList(QString("Cells"), root_.get(), ElementType::CELL)); - net_root_ = std::unique_ptr(new IdStringList(QString("Nets"), root_.get(), ElementType::NET)); - + ctx_ = ctx; + root_ = std::move(data); endResetModel(); - - updateCellsNets(ctx); } -void Model::updateCellsNets(Context *ctx) +void Model::updateElements(std::vector elements) { - if (!ctx) + if (!ctx_) return; beginResetModel(); - - std::vector cells; - for (auto &pair : ctx->cells) { - cells.push_back(pair.first); - } - cell_root_->updateElements(ctx, cells); - - std::vector nets; - for (auto &pair : ctx->nets) { - nets.push_back(pair.first); - } - net_root_->updateElements(ctx, nets); - + root_->updateElements(ctx_, elements); endResetModel(); } @@ -302,11 +243,7 @@ QList Model::search(QString text) { const int limit = 500; QList list; - cell_root_->search(list, text, limit); - net_root_->search(list, text, limit); - bel_root_->search(list, text, limit); - wire_root_->search(list, text, limit); - pip_root_->search(list, text, limit); + root_->search(list, text, limit); QList res; for (auto i : list) { diff --git a/gui/treemodel.h b/gui/treemodel.h index 0236a715..d7f337a3 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -102,6 +102,10 @@ class Item virtual bool canFetchMore() const { return false; } virtual void fetchMore() {} + virtual boost::optional getById(IdString id) { return boost::none; } + virtual void search(QList &results, QString text, int limit) {} + virtual void updateElements(Context *ctx, std::vector elements) {} + virtual ~Item() { if (parent_ != nullptr) { @@ -143,20 +147,20 @@ class IdStringList : public Item public: // Create an IdStringList at given partent that will contain elements of // the given type. - IdStringList(QString name, Item *parent, ElementType type) : Item(name, parent), child_type_(type) {} + IdStringList(ElementType type) : Item("root", nullptr), child_type_(type) {} // Split a name into alpha/non-alpha parts, which is then used for sorting // of children. static std::vector alphaNumSplit(const QString &str); // getById finds a child for the given IdString. - IdStringItem *getById(IdString id) const { return managed_.at(id).get(); } + virtual boost::optional getById(IdString id) override { return managed_.at(id).get(); } // (Re-)create children from a list of IdStrings. - void updateElements(Context *ctx, std::vector elements); + virtual void updateElements(Context *ctx, std::vector elements) override; // Find children that contain the given text. - void search(QList &results, QString text, int limit); + virtual void search(QList &results, QString text, int limit) override; }; // ElementList is a dynamic list of ElementT (BelId,WireId,...) that are @@ -220,7 +224,7 @@ template class ElementList : public Item virtual void fetchMore() override { fetchMore(100); } // getById finds a child for the given IdString. - boost::optional getById(IdString id) + virtual boost::optional getById(IdString id) override { // Search requires us to load all our elements... while (canFetchMore()) @@ -234,7 +238,7 @@ template class ElementList : public Item } // Find children that contain the given text. - void search(QList &results, QString text, int limit) + virtual void search(QList &results, QString text, int limit) override { // Last chance to bail out from loading entire tree into memory. if (limit != -1 && results.size() > limit) @@ -278,8 +282,8 @@ template class ElementXYRoot : public Item ElementType child_type_; public: - ElementXYRoot(Context *ctx, QString name, Item *parent, ElementMap map, ElementGetter getter, ElementType type) - : Item(name, parent), ctx_(ctx), map_(map), getter_(getter), child_type_(type) + ElementXYRoot(Context *ctx, ElementMap map, ElementGetter getter, ElementType type) + : Item("root", nullptr), ctx_(ctx), map_(map), getter_(getter), child_type_(type) { // Create all X and Y label Items/ElementLists. @@ -315,7 +319,7 @@ template class ElementXYRoot : public Item } // getById finds a child for the given IdString. - boost::optional getById(IdString id) + virtual boost::optional getById(IdString id) override { // For now, scan linearly all ElementLists. // TODO(q3k) fix this once we have tree API from arch @@ -329,7 +333,7 @@ template class ElementXYRoot : public Item } // Find children that contain the given text. - void search(QList &results, QString text, int limit) + virtual void search(QList &results, QString text, int limit) override { for (auto &l : managed_lists_) { if (limit != -1 && results.size() > limit) @@ -345,15 +349,11 @@ class Model : public QAbstractItemModel Context *ctx_ = nullptr; public: - using BelXYRoot = ElementXYRoot; - using WireXYRoot = ElementXYRoot; - using PipXYRoot = ElementXYRoot; - Model(QObject *parent = nullptr); ~Model(); - void loadContext(Context *ctx); - void updateCellsNets(Context *ctx); + void loadData(Context *ctx, std::unique_ptr data); + void updateElements(std::vector elements); Item *nodeFromIndex(const QModelIndex &idx) const; QModelIndex indexFromNode(Item *node) { @@ -366,29 +366,7 @@ class Model : public QAbstractItemModel QList search(QString text); - boost::optional nodeForIdType(ElementType type, IdString id) const - { - switch (type) { - case ElementType::BEL: - if (bel_root_ == nullptr) - return boost::none; - return bel_root_->getById(id); - case ElementType::WIRE: - if (wire_root_ == nullptr) - return boost::none; - return wire_root_->getById(id); - case ElementType::PIP: - if (pip_root_ == nullptr) - return boost::none; - return pip_root_->getById(id); - case ElementType::CELL: - return cell_root_->getById(id); - case ElementType::NET: - return net_root_->getById(id); - default: - return boost::none; - } - } + boost::optional nodeForId(IdString id) const { return root_->getById(id); } // Override QAbstractItemModel methods int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; @@ -404,11 +382,6 @@ class Model : public QAbstractItemModel private: // Tree elements that we manage the memory for. std::unique_ptr root_; - std::unique_ptr bel_root_; - std::unique_ptr wire_root_; - std::unique_ptr pip_root_; - std::unique_ptr cell_root_; - std::unique_ptr net_root_; }; }; // namespace TreeModel diff --git a/ice40/arch.h b/ice40/arch.h index 27d5db9f..bdcee3b8 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -417,8 +417,8 @@ struct Arch : BaseCtx // ------------------------------------------------- - int getGridDimX() const { return 34; } - int getGridDimY() const { return 34; } + int getGridDimX() const { return chip_info->width; } + int getGridDimY() const { return chip_info->height; } int getTileBelDimZ(int, int) const { return 8; } int getTilePipDimZ(int, int) const { return 1; }