aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--common/pycontainers.h16
-rw-r--r--ecp5/bitstream.cc7
-rw-r--r--ecp5/family.cmake4
-rw-r--r--frontend/frontend_base.h2
-rw-r--r--gui/base.qrc1
-rw-r--r--gui/basewindow.cc27
-rw-r--r--gui/basewindow.h2
-rw-r--r--gui/line_editor.cc30
-rw-r--r--gui/line_editor.h1
-rw-r--r--gui/resources/save_svg.pngbin0 -> 1569 bytes
-rw-r--r--gui/treemodel.cc11
-rw-r--r--ice40/family.cmake2
13 files changed, 83 insertions, 24 deletions
diff --git a/README.md b/README.md
index 5eaa3449..94e37f90 100644
--- a/README.md
+++ b/README.md
@@ -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
new file mode 100644
index 00000000..c7a5da5e
--- /dev/null
+++ b/gui/resources/save_svg.png
Binary files differ
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})