diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | common/pycontainers.h | 16 | ||||
-rw-r--r-- | ecp5/bitstream.cc | 7 | ||||
-rw-r--r-- | ecp5/family.cmake | 4 | ||||
-rw-r--r-- | frontend/frontend_base.h | 2 | ||||
-rw-r--r-- | gui/base.qrc | 1 | ||||
-rw-r--r-- | gui/basewindow.cc | 27 | ||||
-rw-r--r-- | gui/basewindow.h | 2 | ||||
-rw-r--r-- | gui/line_editor.cc | 30 | ||||
-rw-r--r-- | gui/line_editor.h | 1 | ||||
-rw-r--r-- | gui/resources/save_svg.png | bin | 0 -> 1569 bytes | |||
-rw-r--r-- | gui/treemodel.cc | 11 | ||||
-rw-r--r-- | ice40/family.cmake | 2 |
13 files changed, 83 insertions, 24 deletions
@@ -97,7 +97,9 @@ such as pack, place, route, and write output files.) ### nextpnr-ecp5 For ECP5 support, you must download [Project Trellis](https://github.com/SymbiFlow/prjtrellis), -then follow its instructions to download the latest database and build _libtrellis_. +then follow its instructions to download the latest database and build _libtrellis_. +(for example: `-DTRELLIS_INSTALL_PREFIX=/usr` tells nextpnr to look in `/usr/share/trellis` +and `/usr/lib/trellis`) ``` cmake -DARCH=ecp5 -DTRELLIS_INSTALL_PREFIX=/path/to/prjtrellis . diff --git a/common/pycontainers.h b/common/pycontainers.h index 5de2f6d2..04c670cc 100644 --- a/common/pycontainers.h +++ b/common/pycontainers.h @@ -35,7 +35,11 @@ NEXTPNR_NAMESPACE_BEGIN using namespace boost::python; -inline void KeyError() { PyErr_SetString(PyExc_KeyError, "Key not found"); } +inline void KeyError() +{ + PyErr_SetString(PyExc_KeyError, "Key not found"); + boost::python::throw_error_already_set(); +} /* A wrapper for a Pythonised nextpnr Iterator. The actual class wrapped is a @@ -325,7 +329,9 @@ template <typename T, typename value_conv> struct map_wrapper if (x.base.find(k) != x.base.end()) return value_conv()(x.ctx, x.base.at(k)); KeyError(); - std::terminate(); + + // Should be unreachable, but prevent control may reach end of non-void + throw std::runtime_error("unreachable"); } static void set(wrapped_map &x, std::string const &i, V const &v) @@ -342,7 +348,6 @@ template <typename T, typename value_conv> struct map_wrapper x.base.erase(k); else KeyError(); - std::terminate(); } static bool contains(wrapped_map &x, std::string const &i) @@ -452,7 +457,9 @@ template <typename T> struct map_wrapper_uptr if (x.base.find(k) != x.base.end()) return PythonConversion::ContextualWrapper<Vr>(x.ctx, *x.base.at(k).get()); KeyError(); - std::terminate(); + + // Should be unreachable, but prevent control may reach end of non-void + throw std::runtime_error("unreachable"); } static void set(wrapped_map &x, std::string const &i, V const &v) @@ -469,7 +476,6 @@ template <typename T> struct map_wrapper_uptr x.base.erase(k); else KeyError(); - std::terminate(); } static bool contains(wrapped_map &x, std::string const &i) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index bc8a6c55..1bdb4188 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -650,7 +650,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } // Find bank voltages std::unordered_map<int, IOVoltage> bankVcc; - std::unordered_map<int, bool> bankLvds, bankVref; + std::unordered_map<int, bool> bankLvds, bankVref, bankDiff; for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); @@ -675,6 +675,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex if (iotype == "LVDS") bankLvds[bank] = true; + if ((dir == "INPUT" || dir == "BIDIR") && is_differential(ioType_from_str(iotype))) + bankDiff[bank] = true; if ((dir == "INPUT" || dir == "BIDIR") && is_referenced(ioType_from_str(iotype))) bankVref[bank] = true; } @@ -698,6 +700,9 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON"); cc.tiles[tile.first].add_enum("BANK.LVDSO", "ON"); } + if (bankDiff[bank]) { + cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON"); + } if (bankVref[bank]) { cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON"); cc.tiles[tile.first].add_enum("BANK.VREF", "ON"); diff --git a/ecp5/family.cmake b/ecp5/family.cmake index 8756aa36..14112964 100644 --- a/ecp5/family.cmake +++ b/ecp5/family.cmake @@ -2,8 +2,8 @@ if (NOT EXTERNAL_CHIPDB) set(devices 25k 45k 85k) if (NOT DEFINED TRELLIS_INSTALL_PREFIX) - message(STATUS "TRELLIS_INSTALL_PREFIX not defined using -DTRELLIS_INSTALL_PREFIX=/path-prefix/to/prjtrellis-installation. Default to /usr/local or reset by -DCMAKE_INSTALL_PREFIX when building prjtrellis/libtrellis") - set(TRELLIS_INSTALL_PREFIX "/usr/local") + message(STATUS "TRELLIS_INSTALL_PREFIX not defined using -DTRELLIS_INSTALL_PREFIX=/path-prefix/to/prjtrellis-installation. Defaulted to ${CMAKE_INSTALL_PREFIX}") + set(TRELLIS_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) endif() if (NOT DEFINED PYTRELLIS_LIBDIR) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index fa0e30b9..e262c943 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -342,6 +342,7 @@ template <typename FrontendType> struct GenericFrontend // Add to the flat index of nets net->udata = int(net_flatindex.size()); net_flatindex.push_back(net); + net_old_indices.emplace_back(); // Add to the module-level index of netsd midx = net->udata; // Create aliases for all possible names @@ -508,6 +509,7 @@ template <typename FrontendType> struct GenericFrontend impl.get_vector_bit_constval(bits, i)); cnet->udata = int(net_flatindex.size()); net_flatindex.push_back(cnet); + net_old_indices.emplace_back(); net_ref = cnet->udata; } else { // Otherwise, lookup (creating if needed) the net with given in-module index diff --git a/gui/base.qrc b/gui/base.qrc index 0671fa9e..509a584f 100644 --- a/gui/base.qrc +++ b/gui/base.qrc @@ -30,5 +30,6 @@ <file>resources/group.png</file> <file>resources/camera.png</file> <file>resources/film.png</file> + <file>resources/save_svg.png</file> </qresource> </RCC> diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 7f767c58..08cbafe4 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -266,6 +266,11 @@ void BaseMainWindow::createMenusAndBars() actionMovie->setChecked(false);
connect(actionMovie, &QAction::triggered, this, &BaseMainWindow::saveMovie);
+ actionSaveSVG = new QAction("Save SVG", this);
+ actionSaveSVG->setIcon(QIcon(":/icons/resources/save_svg.png"));
+ actionSaveSVG->setStatusTip("Saving a SVG");
+ connect(actionSaveSVG, &QAction::triggered, this, &BaseMainWindow::saveSVG);
+
// set initial state
fpgaView->enableDisableDecals(actionDisplayBel->isChecked(), actionDisplayWire->isChecked(),
actionDisplayPip->isChecked(), actionDisplayGroups->isChecked());
@@ -334,6 +339,7 @@ void BaseMainWindow::createMenusAndBars() deviceViewToolBar->addSeparator();
deviceViewToolBar->addAction(actionScreenshot);
deviceViewToolBar->addAction(actionMovie);
+ deviceViewToolBar->addAction(actionSaveSVG);
// Add status bar with progress bar
statusBar = new QStatusBar();
@@ -416,6 +422,27 @@ void BaseMainWindow::saveMovie() fpgaView->movieStop();
}
}
+
+void BaseMainWindow::saveSVG()
+{
+ QString fileName = QFileDialog::getSaveFileName(this, QString("Save SVG"), QString(), QString("*.svg"));
+ if (!fileName.isEmpty()) {
+ if (!fileName.endsWith(".svg"))
+ fileName += ".svg";
+ bool ok;
+ QString options =
+ QInputDialog::getText(this, "Save SVG", tr("Save options:"), QLineEdit::Normal, "scale=500", &ok);
+ if (ok) {
+ try {
+ ctx->writeSVG(fileName.toStdString(), options.toStdString());
+ log("Saving SVG successful.\n");
+ } catch (const log_execution_error_exception &ex) {
+ log("Saving SVG failed.\n");
+ }
+ }
+ }
+}
+
void BaseMainWindow::pack_finished(bool status)
{
disableActions();
diff --git a/gui/basewindow.h b/gui/basewindow.h index fe9dfdf2..f90991e1 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -85,6 +85,7 @@ class BaseMainWindow : public QMainWindow void screenshot();
void saveMovie();
+ void saveSVG();
Q_SIGNALS:
void contextChanged(Context *ctx);
@@ -134,6 +135,7 @@ class BaseMainWindow : public QMainWindow QAction *actionScreenshot;
QAction *actionMovie;
+ QAction *actionSaveSVG;
};
NEXTPNR_NAMESPACE_END
diff --git a/gui/line_editor.cc b/gui/line_editor.cc index b25f4031..3e1000dc 100644 --- a/gui/line_editor.cc +++ b/gui/line_editor.cc @@ -19,6 +19,8 @@ */ #include "line_editor.h" +#include <QApplication> +#include <QClipboard> #include <QKeyEvent> #include <QToolTip> #include "ColumnFormatter.h" @@ -43,8 +45,19 @@ LineEditor::LineEditor(ParseHelper *helper, QWidget *parent) : QLineEdit(parent) void LineEditor::keyPressEvent(QKeyEvent *ev) { - - if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { + if (ev->matches(QKeySequence::Paste)) { + QString clipboard = QApplication::clipboard()->text(); + if (clipboard.isEmpty()) + return; + if (clipboard.contains('\n')) { + QStringList clipboard_lines = clipboard.split('\n'); + for (int i = 0; i < clipboard_lines.size(); i++) { + addLineToHistory(clipboard_lines[i]); + Q_EMIT textLineInserted(clipboard_lines[i]); + } + return; + } + } else if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { QToolTip::hideText(); if (lines.empty()) return; @@ -79,13 +92,18 @@ bool LineEditor::focusNextPrevChild(bool next) { return false; } void LineEditor::textInserted() { - if (lines.empty() || lines.back() != text()) - lines += text(); + addLineToHistory(text()); + clear(); + Q_EMIT textLineInserted(lines.back()); +} + +void LineEditor::addLineToHistory(QString line) +{ + if (lines.empty() || lines.back() != line) + lines += line; if (lines.size() > 100) lines.removeFirst(); index = lines.size(); - clear(); - Q_EMIT textLineInserted(lines.back()); } void LineEditor::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); } diff --git a/gui/line_editor.h b/gui/line_editor.h index a779072f..05a6cf1c 100644 --- a/gui/line_editor.h +++ b/gui/line_editor.h @@ -47,6 +47,7 @@ class LineEditor : public QLineEdit void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE; void autocomplete(); + void addLineToHistory(QString line); private: int index; diff --git a/gui/resources/save_svg.png b/gui/resources/save_svg.png Binary files differnew file mode 100644 index 00000000..c7a5da5e --- /dev/null +++ b/gui/resources/save_svg.png diff --git a/gui/treemodel.cc b/gui/treemodel.cc index 97cc8883..448c9794 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -97,12 +97,7 @@ void IdStringList::updateElements(Context *ctx, std::vector<IdString> elements) auto parts_a = alphaNumSplit(a->name()); auto parts_b = alphaNumSplit(b->name()); - // Short-circuit for different part count. - if (parts_a.size() != parts_b.size()) { - return parts_a.size() < parts_b.size(); - } - - for (size_t i = 0; i < parts_a.size(); i++) { + for (size_t i = 0; i < parts_a.size() && i < parts_b.size(); i++) { auto &part_a = parts_a.at(i); auto &part_b = parts_b.at(i); @@ -134,8 +129,8 @@ void IdStringList::updateElements(Context *ctx, std::vector<IdString> elements) return part_a < part_b; } - // Same string. - return true; + // One string is equal to or a subset of the other, compare length. + return parts_a.size() < parts_b.size(); }); } diff --git a/ice40/family.cmake b/ice40/family.cmake index 381fe96a..0252e36a 100644 --- a/ice40/family.cmake +++ b/ice40/family.cmake @@ -10,7 +10,7 @@ if (NOT EXTERNAL_CHIPDB) set(DB_PY ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdb.py) - set(ICEBOX_ROOT "/usr/local/share/icebox" CACHE STRING "icebox location root") + set(ICEBOX_ROOT ${CMAKE_INSTALL_PREFIX}/share/icebox CACHE STRING "icebox location root") file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ice40/chipdbs/) add_library(ice40_chipdb OBJECT ${CMAKE_CURRENT_BINARY_DIR}/ice40/chipdbs/) target_compile_definitions(ice40_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family}) |