aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ecp5/arch.h2
-rw-r--r--gui/base.qrc8
-rw-r--r--gui/basewindow.cc357
-rw-r--r--gui/basewindow.h51
-rw-r--r--gui/designwidget.cc11
-rw-r--r--gui/designwidget.h1
-rw-r--r--gui/ecp5/mainwindow.cc8
-rw-r--r--gui/generic/mainwindow.cc8
-rw-r--r--gui/ice40/mainwindow.cc286
-rw-r--r--gui/ice40/mainwindow.h36
-rw-r--r--gui/ice40/nextpnr.qrc8
-rw-r--r--gui/line_editor.cc6
-rw-r--r--gui/pythontab.cc6
-rw-r--r--gui/resources/control_pause.png (renamed from gui/ice40/resources/control_pause.png)bin721 -> 721 bytes
-rw-r--r--gui/resources/control_play.png (renamed from gui/ice40/resources/control_play.png)bin717 -> 717 bytes
-rw-r--r--gui/resources/control_stop.png (renamed from gui/ice40/resources/control_stop.png)bin695 -> 695 bytes
-rw-r--r--gui/resources/open_json.png (renamed from gui/ice40/resources/open_json.png)bin2093 -> 2093 bytes
-rw-r--r--gui/resources/pack.png (renamed from gui/ice40/resources/pack.png)bin853 -> 853 bytes
-rw-r--r--gui/resources/place.png (renamed from gui/ice40/resources/place.png)bin825 -> 825 bytes
-rw-r--r--gui/resources/route.png (renamed from gui/ice40/resources/route.png)bin683 -> 683 bytes
-rw-r--r--gui/resources/time_add.png (renamed from gui/ice40/resources/time_add.png)bin827 -> 827 bytes
-rw-r--r--gui/worker.cc (renamed from gui/ice40/worker.cc)46
-rw-r--r--gui/worker.h (renamed from gui/ice40/worker.h)12
-rw-r--r--ice40/arch.h12
-rw-r--r--ice40/bitstream.cc67
-rw-r--r--ice40/chipdb.py41
-rw-r--r--ice40/gfx.cc24
-rw-r--r--ice40/main.cc6
28 files changed, 533 insertions, 463 deletions
diff --git a/ecp5/arch.h b/ecp5/arch.h
index d450321d..cd103b12 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -454,6 +454,7 @@ struct Arch : BaseCtx
bel_to_cell[bel] = cell;
cells[cell]->bel = bel;
cells[cell]->belStrength = strength;
+ refreshUiBel(bel);
}
void unbindBel(BelId bel)
@@ -463,6 +464,7 @@ struct Arch : BaseCtx
cells[bel_to_cell[bel]]->bel = BelId();
cells[bel_to_cell[bel]]->belStrength = STRENGTH_NONE;
bel_to_cell[bel] = IdString();
+ refreshUiBel(bel);
}
Loc getBelLocation(BelId bel) const
diff --git a/gui/base.qrc b/gui/base.qrc
index 7b3fa55c..8f58f585 100644
--- a/gui/base.qrc
+++ b/gui/base.qrc
@@ -14,5 +14,13 @@
<file>resources/zoom_out.png</file>
<file>resources/shape_handles.png</file>
<file>resources/shape_square.png</file>
+ <file>resources/control_play.png</file>
+ <file>resources/control_pause.png</file>
+ <file>resources/control_stop.png</file>
+ <file>resources/pack.png</file>
+ <file>resources/place.png</file>
+ <file>resources/route.png</file>
+ <file>resources/time_add.png</file>
+ <file>resources/open_json.png</file>
</qresource>
</RCC>
diff --git a/gui/basewindow.cc b/gui/basewindow.cc
index 685a205f..37141fd6 100644
--- a/gui/basewindow.cc
+++ b/gui/basewindow.cc
@@ -23,9 +23,12 @@
#include <QFileDialog>
#include <QGridLayout>
#include <QIcon>
+#include <QInputDialog>
#include <QSplitter>
+#include <fstream>
#include "designwidget.h"
#include "fpgaviewwidget.h"
+#include "jsonparse.h"
#include "log.h"
#include "mainwindow.h"
#include "pythontab.h"
@@ -35,7 +38,7 @@ static void initBasenameResource() { Q_INIT_RESOURCE(base); }
NEXTPNR_NAMESPACE_BEGIN
BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent)
- : QMainWindow(parent), ctx(std::move(context))
+ : QMainWindow(parent), ctx(std::move(context)), timing_driven(false)
{
initBasenameResource();
qRegisterMetaType<std::string>();
@@ -46,10 +49,10 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent
setObjectName("BaseMainWindow");
resize(1024, 768);
- createMenusAndBars();
+ task = new TaskManager();
+ // Create and deploy widgets on main screen
QWidget *centralWidget = new QWidget(this);
-
QGridLayout *gridLayout = new QGridLayout(centralWidget);
gridLayout->setSpacing(6);
gridLayout->setContentsMargins(11, 11, 11, 11);
@@ -70,38 +73,54 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent
console = new PythonTab();
tabWidget->addTab(console, "Console");
- connect(this, SIGNAL(contextChanged(Context *)), console, SLOT(newContext(Context *)));
centralTabWidget = new QTabWidget();
centralTabWidget->setTabsClosable(true);
- connect(centralTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
fpgaView = new FPGAViewWidget();
centralTabWidget->addTab(fpgaView, "Device");
centralTabWidget->tabBar()->setTabButton(0, QTabBar::RightSide, 0);
centralTabWidget->tabBar()->setTabButton(0, QTabBar::LeftSide, 0);
- connect(this, SIGNAL(contextChanged(Context *)), fpgaView, SLOT(newContext(Context *)));
- connect(designview, SIGNAL(selected(std::vector<DecalXY>, bool)), fpgaView,
- SLOT(onSelectedArchItem(std::vector<DecalXY>, bool)));
- connect(fpgaView, SIGNAL(clickedBel(BelId, bool)), designview, SLOT(onClickedBel(BelId, bool)));
- connect(fpgaView, SIGNAL(clickedWire(WireId, bool)), designview, SLOT(onClickedWire(WireId, bool)));
- connect(fpgaView, SIGNAL(clickedPip(PipId, bool)), designview, SLOT(onClickedPip(PipId, bool)));
- connect(designview, SIGNAL(zoomSelected()), fpgaView, SLOT(zoomSelected()));
-
- connect(designview, SIGNAL(highlight(std::vector<DecalXY>, int)), fpgaView,
- SLOT(onHighlightGroupChanged(std::vector<DecalXY>, 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);
+
+ // Connect Worker
+ connect(task, &TaskManager::log, this, &BaseMainWindow::writeInfo);
+ connect(task, &TaskManager::pack_finished, this, &BaseMainWindow::pack_finished);
+ connect(task, &TaskManager::budget_finish, this, &BaseMainWindow::budget_finish);
+ connect(task, &TaskManager::place_finished, this, &BaseMainWindow::place_finished);
+ connect(task, &TaskManager::route_finished, this, &BaseMainWindow::route_finished);
+ connect(task, &TaskManager::taskCanceled, this, &BaseMainWindow::taskCanceled);
+ connect(task, &TaskManager::taskStarted, this, &BaseMainWindow::taskStarted);
+ connect(task, &TaskManager::taskPaused, this, &BaseMainWindow::taskPaused);
+
+ // Events for context change
+ connect(this, &BaseMainWindow::contextChanged, task, &TaskManager::contextChanged);
+ connect(this, &BaseMainWindow::contextChanged, console, &PythonTab::newContext);
+ connect(this, &BaseMainWindow::contextChanged, fpgaView, &FPGAViewWidget::newContext);
+ connect(this, &BaseMainWindow::contextChanged, designview, &DesignWidget::newContext);
+
+ // Catch close tab events
+ connect(centralTabWidget, &QTabWidget::tabCloseRequested, this, &BaseMainWindow::closeTab);
+
+ // Propagate events from design view to device view
+ connect(designview, &DesignWidget::selected, fpgaView, &FPGAViewWidget::onSelectedArchItem);
+ connect(designview, &DesignWidget::zoomSelected, fpgaView, &FPGAViewWidget::zoomSelected);
+ connect(designview, &DesignWidget::highlight, fpgaView, &FPGAViewWidget::onHighlightGroupChanged);
+
+ // Click event on device view
+ connect(fpgaView, &FPGAViewWidget::clickedBel, designview, &DesignWidget::onClickedBel);
+ connect(fpgaView, &FPGAViewWidget::clickedWire, designview, &DesignWidget::onClickedWire);
+ connect(fpgaView, &FPGAViewWidget::clickedPip, designview, &DesignWidget::onClickedPip);
+
+ // Update tree event
+ connect(this, &BaseMainWindow::updateTreeView, designview, &DesignWidget::updateTree);
+
+ createMenusAndBars();
}
-BaseMainWindow::~BaseMainWindow() {}
+BaseMainWindow::~BaseMainWindow() { delete task; }
void BaseMainWindow::closeTab(int index) { delete centralTabWidget->widget(index); }
@@ -109,44 +128,162 @@ void BaseMainWindow::writeInfo(std::string text) { console->info(text); }
void BaseMainWindow::createMenusAndBars()
{
+ // File menu / project toolbar actions
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()));
+ connect(actionNew, &QAction::triggered, this, &BaseMainWindow::new_proj);
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()));
+ connect(actionOpen, &QAction::triggered, this, &BaseMainWindow::open_proj);
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()));
+ connect(actionSave, &QAction::triggered, this, &BaseMainWindow::save_proj);
QAction *actionExit = new QAction("Exit", this);
actionExit->setIcon(QIcon(":/icons/resources/exit.png"));
actionExit->setShortcuts(QKeySequence::Quit);
actionExit->setStatusTip("Exit the application");
- connect(actionExit, SIGNAL(triggered()), this, SLOT(close()));
+ connect(actionExit, &QAction::triggered, this, &BaseMainWindow::close);
+ // Help menu actions
QAction *actionAbout = new QAction("About", this);
+ // Design menu options
+ 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, &QAction::triggered, this, &BaseMainWindow::open_json);
+
+ actionPack = new QAction("Pack", this);
+ actionPack->setIcon(QIcon(":/icons/resources/pack.png"));
+ actionPack->setStatusTip("Pack current design");
+ actionPack->setEnabled(false);
+ connect(actionPack, &QAction::triggered, task, &TaskManager::pack);
+
+ 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, &QAction::triggered, this, &BaseMainWindow::budget);
+
+ actionPlace = new QAction("Place", this);
+ actionPlace->setIcon(QIcon(":/icons/resources/place.png"));
+ actionPlace->setStatusTip("Place current design");
+ actionPlace->setEnabled(false);
+ connect(actionPlace, &QAction::triggered, this, &BaseMainWindow::place);
+
+ actionRoute = new QAction("Route", this);
+ actionRoute->setIcon(QIcon(":/icons/resources/route.png"));
+ actionRoute->setStatusTip("Route current design");
+ actionRoute->setEnabled(false);
+ connect(actionRoute, &QAction::triggered, task, &TaskManager::route);
+
+ // Worker control toolbar actions
+ actionPlay = new QAction("Play", this);
+ actionPlay->setIcon(QIcon(":/icons/resources/control_play.png"));
+ actionPlay->setStatusTip("Continue running task");
+ actionPlay->setEnabled(false);
+ connect(actionPlay, &QAction::triggered, task, &TaskManager::continue_thread);
+
+ actionPause = new QAction("Pause", this);
+ actionPause->setIcon(QIcon(":/icons/resources/control_pause.png"));
+ actionPause->setStatusTip("Pause running task");
+ actionPause->setEnabled(false);
+ connect(actionPause, &QAction::triggered, task, &TaskManager::pause_thread);
+
+ actionStop = new QAction("Stop", this);
+ actionStop->setIcon(QIcon(":/icons/resources/control_stop.png"));
+ actionStop->setStatusTip("Stop running task");
+ actionStop->setEnabled(false);
+ connect(actionStop, &QAction::triggered, task, &TaskManager::terminate_thread);
+
+ // Device view control toolbar actions
+ QAction *actionZoomIn = new QAction("Zoom In", this);
+ actionZoomIn->setIcon(QIcon(":/icons/resources/zoom_in.png"));
+ connect(actionZoomIn, &QAction::triggered, fpgaView, &FPGAViewWidget::zoomIn);
+
+ QAction *actionZoomOut = new QAction("Zoom Out", this);
+ actionZoomOut->setIcon(QIcon(":/icons/resources/zoom_out.png"));
+ connect(actionZoomOut, &QAction::triggered, fpgaView, &FPGAViewWidget::zoomOut);
+
+ QAction *actionZoomSelected = new QAction("Zoom Selected", this);
+ actionZoomSelected->setIcon(QIcon(":/icons/resources/shape_handles.png"));
+ connect(actionZoomSelected, &QAction::triggered, fpgaView, &FPGAViewWidget::zoomSelected);
+
+ QAction *actionZoomOutbound = new QAction("Zoom Outbound", this);
+ actionZoomOutbound->setIcon(QIcon(":/icons/resources/shape_square.png"));
+ connect(actionZoomOutbound, &QAction::triggered, fpgaView, &FPGAViewWidget::zoomOutbound);
+
+ // Add main menu
menuBar = new QMenuBar();
menuBar->setGeometry(QRect(0, 0, 1024, 27));
- QMenu *menu_File = new QMenu("&File", menuBar);
- QMenu *menu_Help = new QMenu("&Help", menuBar);
- menuBar->addAction(menu_File->menuAction());
- menuBar->addAction(menu_Help->menuAction());
setMenuBar(menuBar);
-
- mainToolBar = new QToolBar();
- addToolBar(Qt::TopToolBarArea, mainToolBar);
-
+ QMenu *menuFile = new QMenu("&File", menuBar);
+ QMenu *menuHelp = new QMenu("&Help", menuBar);
+ menuDesign = new QMenu("&Design", menuBar);
+ menuBar->addAction(menuFile->menuAction());
+ menuBar->addAction(menuDesign->menuAction());
+ menuBar->addAction(menuHelp->menuAction());
+
+ // Add File menu actions
+ menuFile->addAction(actionNew);
+ menuFile->addAction(actionOpen);
+ menuFile->addAction(actionSave);
+ menuFile->addSeparator();
+ menuFile->addAction(actionExit);
+
+ // Add Design menu actions
+ menuDesign->addAction(actionLoadJSON);
+ menuDesign->addAction(actionPack);
+ menuDesign->addAction(actionAssignBudget);
+ menuDesign->addAction(actionPlace);
+ menuDesign->addAction(actionRoute);
+
+ // Add Help menu actions
+ menuHelp->addAction(actionAbout);
+
+ // Project toolbar
+ QToolBar *projectToolBar = new QToolBar("Project");
+ addToolBar(Qt::TopToolBarArea, projectToolBar);
+ projectToolBar->addAction(actionNew);
+ projectToolBar->addAction(actionOpen);
+ projectToolBar->addAction(actionSave);
+
+ // Main action bar
+ mainActionBar = new QToolBar("Main");
+ addToolBar(Qt::TopToolBarArea, mainActionBar);
+ mainActionBar->addAction(actionLoadJSON);
+ mainActionBar->addAction(actionPack);
+ mainActionBar->addAction(actionAssignBudget);
+ mainActionBar->addAction(actionPlace);
+ mainActionBar->addAction(actionRoute);
+
+ // Add worker control toolbar
+ QToolBar *workerControlToolBar = new QToolBar("Worker");
+ addToolBar(Qt::TopToolBarArea, workerControlToolBar);
+ workerControlToolBar->addAction(actionPlay);
+ workerControlToolBar->addAction(actionPause);
+ workerControlToolBar->addAction(actionStop);
+
+ // Add device view control toolbar
+ QToolBar *deviceViewToolBar = new QToolBar("Device");
+ addToolBar(Qt::TopToolBarArea, deviceViewToolBar);
+ deviceViewToolBar->addAction(actionZoomIn);
+ deviceViewToolBar->addAction(actionZoomOut);
+ deviceViewToolBar->addAction(actionZoomSelected);
+ deviceViewToolBar->addAction(actionZoomOutbound);
+
+ // Add status bar with progress bar
statusBar = new QStatusBar();
progressBar = new QProgressBar(statusBar);
progressBar->setAlignment(Qt::AlignRight);
@@ -155,43 +292,137 @@ void BaseMainWindow::createMenusAndBars()
progressBar->setValue(0);
progressBar->setEnabled(false);
setStatusBar(statusBar);
+}
- menu_File->addAction(actionNew);
- menu_File->addAction(actionOpen);
- menu_File->addAction(actionSave);
- menu_File->addSeparator();
- menu_File->addAction(actionExit);
- menu_Help->addAction(actionAbout);
+void BaseMainWindow::load_json(std::string filename)
+{
+ disableActions();
+ currentJson = filename;
+ std::ifstream f(filename);
+ if (parse_json_file(f, filename, ctx.get())) {
+ log("Loading design successful.\n");
+ Q_EMIT updateTreeView();
+ actionPack->setEnabled(true);
+ onJsonLoaded();
+ } else {
+ actionLoadJSON->setEnabled(true);
+ log("Loading design failed.\n");
+ }
+}
- mainToolBar->addAction(actionNew);
- mainToolBar->addAction(actionOpen);
- mainToolBar->addAction(actionSave);
+void BaseMainWindow::open_json()
+{
+ QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
+ if (!fileName.isEmpty()) {
+ load_json(fileName.toStdString());
+ }
}
-void BaseMainWindow::createGraphicsBar()
+void BaseMainWindow::pack_finished(bool status)
{
- QAction *actionZoomIn = new QAction("Zoom In", this);
- actionZoomIn->setIcon(QIcon(":/icons/resources/zoom_in.png"));
- connect(actionZoomIn, SIGNAL(triggered()), fpgaView, SLOT(zoomIn()));
+ disableActions();
+ if (status) {
+ log("Packing design successful.\n");
+ Q_EMIT updateTreeView();
+ actionPlace->setEnabled(true);
+ actionAssignBudget->setEnabled(true);
+ onPackFinished();
+ } else {
+ log("Packing design failed.\n");
+ }
+}
- QAction *actionZoomOut = new QAction("Zoom Out", this);
- actionZoomOut->setIcon(QIcon(":/icons/resources/zoom_out.png"));
- connect(actionZoomOut, SIGNAL(triggered()), fpgaView, SLOT(zoomOut()));
+void BaseMainWindow::budget_finish(bool status)
+{
+ disableActions();
+ if (status) {
+ log("Assigning timing budget successful.\n");
+ actionPlace->setEnabled(true);
+ onBudgetFinished();
+ } else {
+ log("Assigning timing budget failed.\n");
+ }
+}
- QAction *actionZoomSelected = new QAction("Zoom Selected", this);
- actionZoomSelected->setIcon(QIcon(":/icons/resources/shape_handles.png"));
- connect(actionZoomSelected, SIGNAL(triggered()), fpgaView, SLOT(zoomSelected()));
+void BaseMainWindow::place_finished(bool status)
+{
+ disableActions();
+ if (status) {
+ log("Placing design successful.\n");
+ Q_EMIT updateTreeView();
+ actionRoute->setEnabled(true);
+ onPlaceFinished();
+ } else {
+ log("Placing design failed.\n");
+ }
+}
+void BaseMainWindow::route_finished(bool status)
+{
+ disableActions();
+ if (status) {
+ log("Routing design successful.\n");
+ Q_EMIT updateTreeView();
+ onRouteFinished();
+ } else
+ log("Routing design failed.\n");
+}
- QAction *actionZoomOutbound = new QAction("Zoom Outbound", this);
- actionZoomOutbound->setIcon(QIcon(":/icons/resources/shape_square.png"));
- connect(actionZoomOutbound, SIGNAL(triggered()), fpgaView, SLOT(zoomOutbound()));
-
- graphicsToolBar = new QToolBar();
- addToolBar(Qt::TopToolBarArea, graphicsToolBar);
- graphicsToolBar->addAction(actionZoomIn);
- graphicsToolBar->addAction(actionZoomOut);
- graphicsToolBar->addAction(actionZoomSelected);
- graphicsToolBar->addAction(actionZoomOutbound);
+void BaseMainWindow::taskCanceled()
+{
+ log("CANCELED\n");
+ disableActions();
+}
+
+void BaseMainWindow::taskStarted()
+{
+ disableActions();
+ actionPause->setEnabled(true);
+ actionStop->setEnabled(true);
+
+ actionNew->setEnabled(false);
+ actionOpen->setEnabled(false);
+}
+
+void BaseMainWindow::taskPaused()
+{
+ disableActions();
+ actionPlay->setEnabled(true);
+ actionStop->setEnabled(true);
+
+ actionNew->setEnabled(false);
+ actionOpen->setEnabled(false);
+}
+
+void BaseMainWindow::budget()
+{
+ bool ok;
+ double freq = QInputDialog::getDouble(this, "Assign timing budget", "Frequency [MHz]:", 50, 0, 250, 2, &ok);
+ if (ok) {
+ freq *= 1e6;
+ timing_driven = true;
+ Q_EMIT task->budget(freq);
+ }
+}
+
+void BaseMainWindow::place() { Q_EMIT task->place(timing_driven); }
+
+void BaseMainWindow::disableActions()
+{
+ actionLoadJSON->setEnabled(false);
+ actionPack->setEnabled(false);
+ actionAssignBudget->setEnabled(false);
+ actionPlace->setEnabled(false);
+ actionRoute->setEnabled(false);
+
+ actionPlay->setEnabled(false);
+ actionPause->setEnabled(false);
+ actionStop->setEnabled(false);
+
+ actionNew->setEnabled(true);
+ actionOpen->setEnabled(true);
+ actionSave->setEnabled(!currentJson.empty());
+
+ onDisableActions();
}
NEXTPNR_NAMESPACE_END
diff --git a/gui/basewindow.h b/gui/basewindow.h
index a25a2854..5cec24c5 100644
--- a/gui/basewindow.h
+++ b/gui/basewindow.h
@@ -21,6 +21,7 @@
#define BASEMAINWINDOW_H
#include "nextpnr.h"
+#include "worker.h"
#include <QMainWindow>
#include <QMenu>
@@ -48,9 +49,17 @@ class BaseMainWindow : public QMainWindow
virtual ~BaseMainWindow();
Context *getContext() { return ctx.get(); }
+ void load_json(std::string filename);
+
protected:
void createMenusAndBars();
- void createGraphicsBar();
+ void disableActions();
+ virtual void onDisableActions(){};
+ virtual void onJsonLoaded(){};
+ virtual void onPackFinished(){};
+ virtual void onBudgetFinished(){};
+ virtual void onPlaceFinished(){};
+ virtual void onRouteFinished(){};
protected Q_SLOTS:
void writeInfo(std::string text);
@@ -60,26 +69,56 @@ class BaseMainWindow : public QMainWindow
virtual void open_proj() = 0;
virtual bool save_proj() = 0;
+ void open_json();
+ void budget();
+ void place();
+
+ void pack_finished(bool status);
+ void budget_finish(bool status);
+ void place_finished(bool status);
+ void route_finished(bool status);
+
+ void taskCanceled();
+ void taskStarted();
+ void taskPaused();
+
Q_SIGNALS:
void contextChanged(Context *ctx);
void updateTreeView();
protected:
+ // state variables
std::unique_ptr<Context> ctx;
+ TaskManager *task;
+ bool timing_driven;
+ std::string currentJson;
+
+ // main widgets
QTabWidget *tabWidget;
QTabWidget *centralTabWidget;
PythonTab *console;
+ DesignWidget *designview;
+ FPGAViewWidget *fpgaView;
+ // Menus, bars and actions
QMenuBar *menuBar;
- QToolBar *mainToolBar;
- QToolBar *graphicsToolBar;
+ QMenu *menuDesign;
QStatusBar *statusBar;
+ QToolBar *mainActionBar;
+ QProgressBar *progressBar;
+
QAction *actionNew;
QAction *actionOpen;
QAction *actionSave;
- QProgressBar *progressBar;
- DesignWidget *designview;
- FPGAViewWidget *fpgaView;
+
+ QAction *actionLoadJSON;
+ QAction *actionPack;
+ QAction *actionAssignBudget;
+ QAction *actionPlace;
+ QAction *actionRoute;
+ QAction *actionPlay;
+ QAction *actionPause;
+ QAction *actionStop;
};
NEXTPNR_NAMESPACE_END
diff --git a/gui/designwidget.cc b/gui/designwidget.cc
index 276b8b20..5a448235 100644
--- a/gui/designwidget.cc
+++ b/gui/designwidget.cc
@@ -55,7 +55,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel
searchEdit->setClearButtonEnabled(true);
searchEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition);
searchEdit->setPlaceholderText("Search...");
- connect(searchEdit, SIGNAL(returnPressed()), this, SLOT(onSearchInserted()));
+ connect(searchEdit, &QLineEdit::returnPressed, this, &DesignWidget::onSearchInserted);
actionFirst = new QAction("", this);
actionFirst->setIcon(QIcon(":/icons/resources/resultset_first.png"));
@@ -162,8 +162,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel
connect(treeView, &QTreeView::customContextMenuRequested, this, &DesignWidget::prepareMenuTree);
connect(treeView, &QTreeView::doubleClicked, this, &DesignWidget::onDoubleClicked);
selectionModel = treeView->selectionModel();
- connect(selectionModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
- SLOT(onSelectionChanged(const QItemSelection &, const QItemSelection &)));
+ connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &DesignWidget::onSelectionChanged);
history_index = -1;
history_ignore = false;
@@ -317,7 +316,7 @@ QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name)
void DesignWidget::onClickedBel(BelId bel, bool keep)
{
- boost::optional<TreeModel::Item*> item;
+ boost::optional<TreeModel::Item *> item;
{
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
std::lock_guard<std::mutex> lock(ctx->mutex);
@@ -334,7 +333,7 @@ void DesignWidget::onClickedBel(BelId bel, bool keep)
void DesignWidget::onClickedWire(WireId wire, bool keep)
{
- boost::optional<TreeModel::Item*> item;
+ boost::optional<TreeModel::Item *> item;
{
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
std::lock_guard<std::mutex> lock(ctx->mutex);
@@ -351,7 +350,7 @@ void DesignWidget::onClickedWire(WireId wire, bool keep)
void DesignWidget::onClickedPip(PipId pip, bool keep)
{
- boost::optional<TreeModel::Item*> item;
+ boost::optional<TreeModel::Item *> item;
{
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
std::lock_guard<std::mutex> lock(ctx->mutex);
diff --git a/gui/designwidget.h b/gui/designwidget.h
index 628586f4..c78d7232 100644
--- a/gui/designwidget.h
+++ b/gui/designwidget.h
@@ -53,7 +53,6 @@ class DesignWidget : public QWidget
std::vector<DecalXY> getDecals(ElementType type, IdString value);
void updateHighlightGroup(QList<TreeModel::Item *> item, int group);
Q_SIGNALS:
- void info(std::string text);
void selected(std::vector<DecalXY> decal, bool keep);
void highlight(std::vector<DecalXY> decal, int group);
void zoomSelected();
diff --git a/gui/ecp5/mainwindow.cc b/gui/ecp5/mainwindow.cc
index 4b1c7e3b..935daefd 100644
--- a/gui/ecp5/mainwindow.cc
+++ b/gui/ecp5/mainwindow.cc
@@ -36,13 +36,7 @@ MainWindow::MainWindow(std::unique_ptr<Context> context, QWidget *parent) : Base
MainWindow::~MainWindow() {}
-void MainWindow::createMenu()
-{
- QMenu *menu_Custom = new QMenu("&Generic", menuBar);
- menuBar->addAction(menu_Custom->menuAction());
-
- createGraphicsBar();
-}
+void MainWindow::createMenu() {}
void MainWindow::new_proj() {}
diff --git a/gui/generic/mainwindow.cc b/gui/generic/mainwindow.cc
index 1efc73bb..70ee600d 100644
--- a/gui/generic/mainwindow.cc
+++ b/gui/generic/mainwindow.cc
@@ -36,13 +36,7 @@ MainWindow::MainWindow(std::unique_ptr<Context> context, QWidget *parent) : Base
MainWindow::~MainWindow() {}
-void MainWindow::createMenu()
-{
- QMenu *menu_Custom = new QMenu("&Generic", menuBar);
- menuBar->addAction(menu_Custom->menuAction());
-
- createGraphicsBar();
-}
+void MainWindow::createMenu() {}
void MainWindow::new_proj() {}
diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc
index f06971b6..2fa2e561 100644
--- a/gui/ice40/mainwindow.cc
+++ b/gui/ice40/mainwindow.cc
@@ -37,130 +37,45 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN
MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
- : BaseMainWindow(std::move(context), parent), timing_driven(false), chipArgs(args)
+ : BaseMainWindow(std::move(context), parent), chipArgs(args)
{
initMainResource();
std::string title = "nextpnr-ice40 - [EMPTY]";
setWindowTitle(title.c_str());
- task = new TaskManager();
- connect(task, SIGNAL(log(std::string)), this, SLOT(writeInfo(std::string)));
-
- connect(task, SIGNAL(loadfile_finished(bool)), this, SLOT(loadfile_finished(bool)));
- connect(task, SIGNAL(loadpcf_finished(bool)), this, SLOT(loadpcf_finished(bool)));
- connect(task, SIGNAL(saveasc_finished(bool)), this, SLOT(saveasc_finished(bool)));
- connect(task, SIGNAL(pack_finished(bool)), this, SLOT(pack_finished(bool)));
- connect(task, SIGNAL(budget_finish(bool)), this, SLOT(budget_finish(bool)));
- connect(task, SIGNAL(place_finished(bool)), this, SLOT(place_finished(bool)));
- connect(task, SIGNAL(route_finished(bool)), this, SLOT(route_finished(bool)));
-
- connect(task, SIGNAL(taskCanceled()), this, SLOT(taskCanceled()));
- connect(task, SIGNAL(taskStarted()), this, SLOT(taskStarted()));
- connect(task, SIGNAL(taskPaused()), this, SLOT(taskPaused()));
-
- connect(this, SIGNAL(contextChanged(Context *)), this, SLOT(newContext(Context *)));
- connect(this, SIGNAL(contextChanged(Context *)), task, SIGNAL(contextChanged(Context *)));
+ connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext);
createMenu();
Q_EMIT contextChanged(ctx.get());
}
-MainWindow::~MainWindow() { delete task; }
+MainWindow::~MainWindow() {}
void MainWindow::createMenu()
{
- QMenu *menu_Design = new QMenu("&Design", menuBar);
- menuBar->addAction(menu_Design->menuAction());
-
- 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()));
-
+ // Add arch specific actions
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->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->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->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->setIcon(QIcon(":/icons/resources/route.png"));
- actionRoute->setStatusTip("Route current design");
- actionRoute->setEnabled(false);
- connect(actionRoute, SIGNAL(triggered()), task, SIGNAL(route()));
+ connect(actionLoadPCF, &QAction::triggered, this, &MainWindow::open_pcf);
actionSaveAsc = new QAction("Save ASC", this);
actionSaveAsc->setIcon(QIcon(":/icons/resources/save_asc.png"));
actionSaveAsc->setStatusTip("Save ASC file");
actionSaveAsc->setEnabled(false);
- connect(actionSaveAsc, SIGNAL(triggered()), this, SLOT(save_asc()));
-
- QToolBar *taskFPGABar = new QToolBar();
- addToolBar(Qt::TopToolBarArea, taskFPGABar);
-
- taskFPGABar->addAction(actionLoadJSON);
- taskFPGABar->addAction(actionLoadPCF);
- taskFPGABar->addAction(actionPack);
- taskFPGABar->addAction(actionAssignBudget);
- taskFPGABar->addAction(actionPlace);
- taskFPGABar->addAction(actionRoute);
- taskFPGABar->addAction(actionSaveAsc);
-
- menu_Design->addAction(actionLoadJSON);
- menu_Design->addAction(actionLoadPCF);
- menu_Design->addAction(actionPack);
- menu_Design->addAction(actionAssignBudget);
- menu_Design->addAction(actionPlace);
- menu_Design->addAction(actionRoute);
- menu_Design->addAction(actionSaveAsc);
-
- 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->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->setIcon(QIcon(":/icons/resources/control_stop.png"));
- actionStop->setStatusTip("Stop running task");
- actionStop->setEnabled(false);
- connect(actionStop, SIGNAL(triggered()), task, SLOT(terminate_thread()));
-
- QToolBar *taskToolBar = new QToolBar();
- addToolBar(Qt::TopToolBarArea, taskToolBar);
-
- taskToolBar->addAction(actionPlay);
- taskToolBar->addAction(actionPause);
- taskToolBar->addAction(actionStop);
-
- createGraphicsBar();
+ connect(actionSaveAsc, &QAction::triggered, this, &MainWindow::save_asc);
+
+ // Add actions in menus
+ mainActionBar->addSeparator();
+ mainActionBar->addAction(actionLoadPCF);
+ mainActionBar->addAction(actionSaveAsc);
+
+ menuDesign->addSeparator();
+ menuDesign->addAction(actionLoadPCF);
+ menuDesign->addAction(actionSaveAsc);
}
#if defined(_MSC_VER)
@@ -238,19 +153,18 @@ void MainWindow::new_proj()
}
}
-void MainWindow::load_json(std::string filename, std::string pcf)
-{
- disableActions();
- currentJson = filename;
- currentPCF = pcf;
- Q_EMIT task->loadfile(filename);
-}
-
void MainWindow::load_pcf(std::string filename)
{
disableActions();
currentPCF = filename;
- Q_EMIT task->loadpcf(filename);
+ std::ifstream f(filename);
+ if (apply_pcf(ctx.get(), f)) {
+ log("Loading PCF successful.\n");
+ actionPack->setEnabled(true);
+ } else {
+ actionLoadPCF->setEnabled(true);
+ log("Loading PCF failed.\n");
+ }
}
void MainWindow::newContext(Context *ctx)
@@ -331,20 +245,14 @@ void MainWindow::open_proj()
}
log_info("Loading json: %s...\n", json.c_str());
- load_json(json, pcf);
+ load_json(json);
+ if (!pcf.empty())
+ load_pcf(json);
} catch (log_execution_error_exception) {
}
}
}
-void MainWindow::open_json()
-{
- QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
- if (!fileName.isEmpty()) {
- load_json(fileName.toStdString(), "");
- }
-}
-
void MainWindow::open_pcf()
{
QString fileName = QFileDialog::getOpenFileName(this, QString("Open PCF"), QString(), QString("*.pcf"));
@@ -389,149 +297,19 @@ void MainWindow::save_asc()
if (!fileName.isEmpty()) {
std::string fn = fileName.toStdString();
disableActions();
- Q_EMIT task->saveasc(fn);
+ std::ofstream f(fn);
+ write_asc(ctx.get(), f);
+ log("Saving ASC successful.\n");
}
}
-void MainWindow::disableActions()
+void MainWindow::onDisableActions()
{
- actionLoadJSON->setEnabled(false);
actionLoadPCF->setEnabled(false);
- actionPack->setEnabled(false);
- actionAssignBudget->setEnabled(false);
- actionPlace->setEnabled(false);
- actionRoute->setEnabled(false);
actionSaveAsc->setEnabled(false);
-
- actionPlay->setEnabled(false);
- actionPause->setEnabled(false);
- actionStop->setEnabled(false);
-
- actionNew->setEnabled(true);
- actionOpen->setEnabled(true);
- actionSave->setEnabled(!currentJson.empty());
-}
-
-void MainWindow::loadfile_finished(bool status)
-{
- disableActions();
- if (status) {
- log("Loading design successful.\n");
- actionLoadPCF->setEnabled(true);
- actionPack->setEnabled(true);
- if (!currentPCF.empty())
- load_pcf(currentPCF);
- Q_EMIT updateTreeView();
- } else {
- log("Loading design failed.\n");
- currentPCF = "";
- }
-}
-
-void MainWindow::loadpcf_finished(bool status)
-{
- disableActions();
- if (status) {
- log("Loading PCF successful.\n");
- actionPack->setEnabled(true);
- } else {
- log("Loading PCF failed.\n");
- }
-}
-
-void MainWindow::saveasc_finished(bool status)
-{
- disableActions();
- if (status) {
- log("Saving ASC successful.\n");
- } else {
- log("Saving ASC failed.\n");
- }
-}
-
-void MainWindow::pack_finished(bool status)
-{
- disableActions();
- if (status) {
- log("Packing design successful.\n");
- Q_EMIT updateTreeView();
- actionPlace->setEnabled(true);
- actionAssignBudget->setEnabled(true);
- } else {
- log("Packing design failed.\n");
- }
-}
-
-void MainWindow::budget_finish(bool status)
-{
- disableActions();
- if (status) {
- log("Assigning timing budget successful.\n");
- actionPlace->setEnabled(true);
- } else {
- log("Assigning timing budget failed.\n");
- }
-}
-
-void MainWindow::place_finished(bool status)
-{
- disableActions();
- if (status) {
- log("Placing design successful.\n");
- Q_EMIT updateTreeView();
- actionRoute->setEnabled(true);
- } else {
- log("Placing design failed.\n");
- }
-}
-void MainWindow::route_finished(bool status)
-{
- disableActions();
- if (status) {
- log("Routing design successful.\n");
- Q_EMIT updateTreeView();
- actionSaveAsc->setEnabled(true);
- } else
- log("Routing design failed.\n");
-}
-
-void MainWindow::taskCanceled()
-{
- log("CANCELED\n");
- disableActions();
-}
-
-void MainWindow::taskStarted()
-{
- disableActions();
- actionPause->setEnabled(true);
- actionStop->setEnabled(true);
-
- actionNew->setEnabled(false);
- actionOpen->setEnabled(false);
-}
-
-void MainWindow::taskPaused()
-{
- disableActions();
- actionPlay->setEnabled(true);
- actionStop->setEnabled(true);
-
- actionNew->setEnabled(false);
- actionOpen->setEnabled(false);
-}
-
-void MainWindow::budget()
-{
- bool ok;
- double freq = QInputDialog::getDouble(this, "Assign timing budget", "Frequency [MHz]:", 50, 0, 250, 2, &ok);
- if (ok) {
- freq *= 1e6;
- timing_driven = true;
- Q_EMIT task->budget(freq);
- }
}
-void MainWindow::place() { Q_EMIT task->place(timing_driven); }
+void MainWindow::onJsonLoaded() { actionLoadPCF->setEnabled(true); }
+void MainWindow::onRouteFinished() { actionSaveAsc->setEnabled(true); }
NEXTPNR_NAMESPACE_END
diff --git a/gui/ice40/mainwindow.h b/gui/ice40/mainwindow.h
index 4600d1da..230ccc4e 100644
--- a/gui/ice40/mainwindow.h
+++ b/gui/ice40/mainwindow.h
@@ -21,7 +21,6 @@
#define MAINWINDOW_H
#include "../basewindow.h"
-#include "worker.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -35,53 +34,30 @@ class MainWindow : public BaseMainWindow
public:
void createMenu();
- void load_json(std::string filename, std::string pcf);
void load_pcf(std::string filename);
+
+ protected:
+ void onDisableActions() override;
+ void onJsonLoaded() override;
+ void onRouteFinished() override;
+
protected Q_SLOTS:
virtual void new_proj();
virtual void open_proj();
virtual bool save_proj();
- void open_json();
void open_pcf();
- void budget();
- void place();
void save_asc();
- void loadfile_finished(bool status);
- void loadpcf_finished(bool status);
- void saveasc_finished(bool status);
- void pack_finished(bool status);
- void budget_finish(bool status);
- void place_finished(bool status);
- void route_finished(bool status);
-
- void taskCanceled();
- void taskStarted();
- void taskPaused();
-
void newContext(Context *ctx);
private:
- void disableActions();
-
- TaskManager *task;
- QAction *actionLoadJSON;
QAction *actionLoadPCF;
- QAction *actionPack;
- QAction *actionAssignBudget;
- QAction *actionPlace;
- QAction *actionRoute;
QAction *actionSaveAsc;
- QAction *actionPlay;
- QAction *actionPause;
- QAction *actionStop;
- bool timing_driven;
ArchArgs chipArgs;
std::string currentProj;
- std::string currentJson;
std::string currentPCF;
};
diff --git a/gui/ice40/nextpnr.qrc b/gui/ice40/nextpnr.qrc
index 3c86057d..8eca1e09 100644
--- a/gui/ice40/nextpnr.qrc
+++ b/gui/ice40/nextpnr.qrc
@@ -1,14 +1,6 @@
<RCC>
<qresource prefix="/icons">
- <file>resources/control_play.png</file>
- <file>resources/control_pause.png</file>
- <file>resources/control_stop.png</file>
- <file>resources/pack.png</file>
- <file>resources/place.png</file>
- <file>resources/route.png</file>
- <file>resources/time_add.png</file>
<file>resources/open_pcf.png</file>
<file>resources/save_asc.png</file>
- <file>resources/open_json.png</file>
</qresource>
</RCC>
diff --git a/gui/line_editor.cc b/gui/line_editor.cc
index 23415092..b25f4031 100644
--- a/gui/line_editor.cc
+++ b/gui/line_editor.cc
@@ -32,13 +32,13 @@ LineEditor::LineEditor(ParseHelper *helper, QWidget *parent) : QLineEdit(parent)
setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &history", this);
clearAction->setStatusTip("Clears line edit history");
- connect(clearAction, SIGNAL(triggered()), this, SLOT(clearHistory()));
+ connect(clearAction, &QAction::triggered, this, &LineEditor::clearHistory);
contextMenu = createStandardContextMenu();
contextMenu->addSeparator();
contextMenu->addAction(clearAction);
- connect(this, SIGNAL(returnPressed()), SLOT(textInserted()));
- connect(this, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
+ connect(this, &LineEditor::returnPressed, this, &LineEditor::textInserted);
+ connect(this, &LineEditor::customContextMenuRequested, this, &LineEditor::showContextMenu);
}
void LineEditor::keyPressEvent(QKeyEvent *ev)
diff --git a/gui/pythontab.cc b/gui/pythontab.cc
index e761128d..80d731e9 100644
--- a/gui/pythontab.cc
+++ b/gui/pythontab.cc
@@ -42,18 +42,18 @@ PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
console->setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &buffer", this);
clearAction->setStatusTip("Clears display buffer");
- connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer()));
+ connect(clearAction, &QAction::triggered, this, &PythonTab::clearBuffer);
contextMenu = console->createStandardContextMenu();
contextMenu->addSeparator();
contextMenu->addAction(clearAction);
- connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
+ connect(console, &PythonConsole::customContextMenuRequested, this, &PythonTab::showContextMenu);
lineEdit = new LineEditor(&parseHelper);
lineEdit->setMinimumHeight(30);
lineEdit->setMaximumHeight(30);
lineEdit->setFont(f);
lineEdit->setPlaceholderText(PythonTab::PROMPT);
- connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString)));
+ connect(lineEdit, &LineEditor::textLineInserted, this, &PythonTab::editLineReturnPressed);
QGridLayout *mainLayout = new QGridLayout();
mainLayout->addWidget(console, 0, 0);
diff --git a/gui/ice40/resources/control_pause.png b/gui/resources/control_pause.png
index ec61099b..ec61099b 100644
--- a/gui/ice40/resources/control_pause.png
+++ b/gui/resources/control_pause.png
Binary files differ
diff --git a/gui/ice40/resources/control_play.png b/gui/resources/control_play.png
index f8c8ec68..f8c8ec68 100644
--- a/gui/ice40/resources/control_play.png
+++ b/gui/resources/control_play.png
Binary files differ
diff --git a/gui/ice40/resources/control_stop.png b/gui/resources/control_stop.png
index e6f75d23..e6f75d23 100644
--- a/gui/ice40/resources/control_stop.png
+++ b/gui/resources/control_stop.png
Binary files differ
diff --git a/gui/ice40/resources/open_json.png b/gui/resources/open_json.png
index 90c07267..90c07267 100644
--- a/gui/ice40/resources/open_json.png
+++ b/gui/resources/open_json.png
Binary files differ
diff --git a/gui/ice40/resources/pack.png b/gui/resources/pack.png
index da3c2a2d..da3c2a2d 100644
--- a/gui/ice40/resources/pack.png
+++ b/gui/resources/pack.png
Binary files differ
diff --git a/gui/ice40/resources/place.png b/gui/resources/place.png
index 0905f933..0905f933 100644
--- a/gui/ice40/resources/place.png
+++ b/gui/resources/place.png
Binary files differ
diff --git a/gui/ice40/resources/route.png b/gui/resources/route.png
index 258c16c6..258c16c6 100644
--- a/gui/ice40/resources/route.png
+++ b/gui/resources/route.png
Binary files differ
diff --git a/gui/ice40/resources/time_add.png b/gui/resources/time_add.png
index dcc45cb2..dcc45cb2 100644
--- a/gui/ice40/resources/time_add.png
+++ b/gui/resources/time_add.png
Binary files differ
diff --git a/gui/ice40/worker.cc b/gui/worker.cc
index 09093ec8..b009ecd3 100644
--- a/gui/ice40/worker.cc
+++ b/gui/worker.cc
@@ -19,11 +19,8 @@
#include "worker.h"
#include <fstream>
-#include "bitstream.h"
#include "design_utils.h"
-#include "jsonparse.h"
#include "log.h"
-#include "pcf.h"
#include "timing.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -55,43 +52,6 @@ Worker::Worker(TaskManager *parent) : ctx(nullptr)
void Worker::newContext(Context *ctx_) { ctx = ctx_; }
-void Worker::loadfile(const std::string &filename)
-{
- Q_EMIT taskStarted();
- std::string fn = filename;
- std::ifstream f(fn);
- try {
- Q_EMIT loadfile_finished(parse_json_file(f, fn, ctx));
- } catch (WorkerInterruptionRequested) {
- Q_EMIT taskCanceled();
- }
-}
-
-void Worker::loadpcf(const std::string &filename)
-{
- Q_EMIT taskStarted();
- std::string fn = filename;
- std::ifstream f(fn);
- try {
- Q_EMIT loadpcf_finished(apply_pcf(ctx, f));
- } catch (WorkerInterruptionRequested) {
- Q_EMIT taskCanceled();
- }
-}
-
-void Worker::saveasc(const std::string &filename)
-{
- Q_EMIT taskStarted();
- std::string fn = filename;
- std::ofstream f(fn);
- try {
- write_asc(ctx, f);
- Q_EMIT saveasc_finished(true);
- } catch (WorkerInterruptionRequested) {
- Q_EMIT taskCanceled();
- }
-}
-
void Worker::pack()
{
Q_EMIT taskStarted();
@@ -144,9 +104,6 @@ TaskManager::TaskManager() : toTerminate(false), toPause(false)
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
- connect(this, &TaskManager::loadfile, worker, &Worker::loadfile);
- connect(this, &TaskManager::loadpcf, worker, &Worker::loadpcf);
- connect(this, &TaskManager::saveasc, worker, &Worker::saveasc);
connect(this, &TaskManager::pack, worker, &Worker::pack);
connect(this, &TaskManager::budget, worker, &Worker::budget);
connect(this, &TaskManager::place, worker, &Worker::place);
@@ -155,9 +112,6 @@ TaskManager::TaskManager() : toTerminate(false), toPause(false)
connect(this, &TaskManager::contextChanged, worker, &Worker::newContext);
connect(worker, &Worker::log, this, &TaskManager::info);
- connect(worker, &Worker::loadfile_finished, this, &TaskManager::loadfile_finished);
- connect(worker, &Worker::loadpcf_finished, this, &TaskManager::loadpcf_finished);
- connect(worker, &Worker::saveasc_finished, this, &TaskManager::saveasc_finished);
connect(worker, &Worker::pack_finished, this, &TaskManager::pack_finished);
connect(worker, &Worker::budget_finish, this, &TaskManager::budget_finish);
connect(worker, &Worker::place_finished, this, &TaskManager::place_finished);
diff --git a/gui/ice40/worker.h b/gui/worker.h
index f4369535..12a11977 100644
--- a/gui/ice40/worker.h
+++ b/gui/worker.h
@@ -35,18 +35,12 @@ class Worker : public QObject
explicit Worker(TaskManager *parent);
public Q_SLOTS:
void newContext(Context *);
- void loadfile(const std::string &);
- void loadpcf(const std::string &);
- void saveasc(const std::string &);
void pack();
void budget(double freq);
void place(bool timing_driven);
void route();
Q_SIGNALS:
void log(const std::string &text);
- void loadfile_finished(bool status);
- void loadpcf_finished(bool status);
- void saveasc_finished(bool status);
void pack_finished(bool status);
void budget_finish(bool status);
void place_finished(bool status);
@@ -78,9 +72,6 @@ class TaskManager : public QObject
Q_SIGNALS:
void contextChanged(Context *ctx);
void terminate();
- void loadfile(const std::string &);
- void loadpcf(const std::string &);
- void saveasc(const std::string &);
void pack();
void budget(double freq);
void place(bool timing_driven);
@@ -88,9 +79,6 @@ class TaskManager : public QObject
// redirected signals
void log(const std::string &text);
- void loadfile_finished(bool status);
- void loadpcf_finished(bool status);
- void saveasc_finished(bool status);
void pack_finished(bool status);
void budget_finish(bool status);
void place_finished(bool status);
diff --git a/ice40/arch.h b/ice40/arch.h
index e67f2aa9..98361132 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -141,6 +141,7 @@ static const int max_switch_bits = 5;
NPNR_PACKED_STRUCT(struct SwitchInfoPOD {
int32_t num_bits;
+ int32_t bel;
int8_t x, y;
ConfigBitPOD cbits[max_switch_bits];
});
@@ -613,7 +614,16 @@ struct Arch : BaseCtx
bool checkPipAvail(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
- return switches_locked[chip_info->pip_data[pip.index].switch_index] == IdString();
+ int switch_idx = chip_info->pip_data[pip.index].switch_index;
+
+ if (switches_locked[switch_idx] != IdString())
+ return false;
+
+ int bel_idx = chip_info->bits_info->switches[switch_idx].bel;
+ if (bel_idx >= 0 && bel_to_cell[bel_idx] != IdString())
+ return false;
+
+ return true;
}
IdString getBoundPipNet(PipId pip) const
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index d0d6b205..8b00e878 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -221,8 +221,21 @@ std::string tagTileType(TileType &tile)
NPNR_ASSERT(false);
}
}
+
+static BelPin get_one_bel_pin(const Context *ctx, WireId wire)
+{
+ auto pins = ctx->getWireBelPins(wire);
+ NPNR_ASSERT(pins.begin() != pins.end());
+ return *pins.begin();
+}
+
void write_asc(const Context *ctx, std::ostream &out)
{
+
+ static const std::vector<int> lut_perm = {
+ 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
+ };
+
// [y][x][row][col]
const ChipInfoPOD &ci = *ctx->chip_info;
const BitstreamInfoPOD &bi = *ci.bits_info;
@@ -262,12 +275,50 @@ void write_asc(const Context *ctx, std::ostream &out)
if (ctx->pip_to_net[pip.index] != IdString()) {
const PipInfoPOD &pi = ci.pip_data[pip.index];
const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
- for (int i = 0; i < swi.num_bits; i++) {
- bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
- int8_t &cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
- if (bool(cbit) != 0)
- NPNR_ASSERT(false);
- cbit = val;
+ int sw_bel_idx = swi.bel;
+ if (sw_bel_idx >= 0) {
+ const BelInfoPOD &beli = ci.bel_data[sw_bel_idx];
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
+ BelId sw_bel;
+ sw_bel.index = sw_bel_idx;
+ NPNR_ASSERT(ctx->getBelType(sw_bel) == TYPE_ICESTORM_LC);
+ BelPin input = get_one_bel_pin(ctx, ctx->getPipSrcWire(pip));
+ BelPin output = get_one_bel_pin(ctx, ctx->getPipDstWire(pip));
+ NPNR_ASSERT(input.bel == sw_bel);
+ NPNR_ASSERT(output.bel == sw_bel && output.pin == PIN_O);
+ unsigned lut_init;
+ switch (input.pin) {
+ case PIN_I0:
+ lut_init = 2;
+ break;
+ case PIN_I1:
+ lut_init = 4;
+ break;
+ case PIN_I2:
+ lut_init = 16;
+ break;
+ case PIN_I3:
+ lut_init = 256;
+ break;
+ default:
+ NPNR_ASSERT_FALSE("bad feedthru LUT input");
+ }
+ std::vector<bool> lc(20, false);
+ for (int i = 0; i < 16; i++) {
+ if ((lut_init >> i) & 0x1)
+ lc.at(lut_perm.at(i)) = true;
+ }
+
+ for (int i = 0; i < 20; i++)
+ set_config(ti, config.at(beli.y).at(beli.x), "LC_" + std::to_string(beli.z), lc.at(i), i);
+ } else {
+ for (int i = 0; i < swi.num_bits; i++) {
+ bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
+ int8_t &cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
+ if (bool(cbit) != 0)
+ NPNR_ASSERT(false);
+ cbit = val;
+ }
}
}
}
@@ -295,9 +346,7 @@ void write_asc(const Context *ctx, std::ostream &out)
bool carry_enable = get_param_or_def(cell.second.get(), ctx->id("CARRY_ENABLE"));
std::vector<bool> lc(20, false);
// From arachne-pnr
- static std::vector<int> lut_perm = {
- 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
- };
+
for (int i = 0; i < 16; i++) {
if ((lut_init >> i) & 0x1)
lc.at(lut_perm.at(i)) = true;
diff --git a/ice40/chipdb.py b/ice40/chipdb.py
index 3f9f4e65..a0d7f03c 100644
--- a/ice40/chipdb.py
+++ b/ice40/chipdb.py
@@ -271,6 +271,16 @@ def pipdelay(src_idx, dst_idx, db):
if re.match(r"ram/(MASK|RADDR|WADDR|WDATA)_", dst[2]):
return db["InMux.I.O"]
+ if re.match(r"lutff_\d+/out", dst[2]):
+ if re.match(r"lutff_\d+/in_0", src[2]):
+ return db["LogicCell40.in0.lcout"]
+ if re.match(r"lutff_\d+/in_1", src[2]):
+ return db["LogicCell40.in1.lcout"]
+ if re.match(r"lutff_\d+/in_2", src[2]):
+ return db["LogicCell40.in2.lcout"]
+ if re.match(r"lutff_\d+/in_3", src[2]):
+ return db["LogicCell40.in3.lcout"]
+
print(src, dst, src_idx, dst_idx, src_type, dst_type, file=sys.stderr)
assert 0
@@ -316,12 +326,12 @@ with open(args.filename, "r") as f:
if line[0] == ".buffer":
mode = ("buffer", int(line[3]), int(line[1]), int(line[2]))
- switches.append((line[3], int(line[1]), int(line[2]), line[4:]))
+ switches.append((int(line[1]), int(line[2]), line[4:], -1))
continue
if line[0] == ".routing":
mode = ("routing", int(line[3]), int(line[1]), int(line[2]))
- switches.append((line[3], int(line[1]), int(line[2]), line[4:]))
+ switches.append((int(line[1]), int(line[2]), line[4:], -1))
continue
if line[0] == ".io_tile":
@@ -499,6 +509,22 @@ def add_wire(x, y, name):
wire_names_r[wire_idx] = wname
wire_segments[wire_idx] = dict()
+def add_switch(x, y, bel=-1):
+ switches.append((x, y, [], bel))
+
+def add_pip(src, dst):
+ x, y, _, _ = switches[-1]
+
+ if src not in wire_downhill:
+ wire_downhill[src] = set()
+ wire_downhill[src].add(dst)
+
+ if dst not in wire_uphill:
+ wire_uphill[dst] = set()
+ wire_uphill[dst].add(src)
+
+ pip_xy[(src, dst)] = (x, y, 0, len(switches) - 1)
+
# Add virtual padin wires
for i in range(8):
add_wire(0, 0, "padin_%d" % i)
@@ -555,6 +581,13 @@ def add_bel_lc(x, y, z):
if wire_lout is not None:
add_bel_output(bel, wire_lout, "LO")
+ # route-through LUTs
+ add_switch(x, y, bel)
+ add_pip(wire_in_0, wire_out)
+ add_pip(wire_in_1, wire_out)
+ add_pip(wire_in_2, wire_out)
+ add_pip(wire_in_3, wire_out)
+
def add_bel_io(x, y, z):
bel = len(bel_name)
bel_name.append("X%d/Y%d/io%d" % (x, y, z))
@@ -1050,7 +1083,7 @@ for info in pipinfo:
switchinfo = []
for switch in switches:
- dst, x, y, bits = switch
+ x, y, bits, bel = switch
bitlist = []
for b in bits:
m = cbit_re.match(b)
@@ -1060,11 +1093,13 @@ for switch in switches:
si["x"] = x
si["y"] = y
si["bits"] = bitlist
+ si["bel"] = bel
switchinfo.append(si)
bba.l("switch_data_%s" % dev_name, "SwitchInfoPOD")
for info in switchinfo:
bba.u32(len(info["bits"]), "num_bits")
+ bba.u32(info["bel"], "bel")
bba.u8(info["x"], "x")
bba.u8(info["y"], "y")
for i in range(5):
diff --git a/ice40/gfx.cc b/ice40/gfx.cc
index 0798862a..924fe964 100644
--- a/ice40/gfx.cc
+++ b/ice40/gfx.cc
@@ -696,11 +696,30 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src,
{
float x1, y1, x2, y2;
- if (getWireXY_main(src, x1, y1) && getWireXY_main(dst, 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);
+ return;
+ }
- if (getWireXY_local(src, x1, y1) && getWireXY_local(dst, x2, y2))
+ 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);
+ return;
+ }
+
+ if (TILE_WIRE_LUTFF_0_IN_0 <= src && src <= TILE_WIRE_LUTFF_7_IN_3 && TILE_WIRE_LUTFF_0_OUT <= dst && dst <= TILE_WIRE_LUTFF_7_OUT) {
+ int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0) / 4;
+ int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0) % 4;
+
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_ARROW;
+ el.style = style;
+ el.x1 = x + logic_cell_x1;
+ el.x2 = x + logic_cell_x2;
+ el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch;
+ el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + lut_idx * logic_cell_pitch;
+ g.push_back(el);
+ return;
+ }
if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) {
GraphicElement el;
@@ -711,6 +730,7 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src,
el.y1 = y + 0.01;
el.y2 = y + 0.02;
g.push_back(el);
+ return;
}
}
diff --git a/ice40/main.cc b/ice40/main.cc
index 358b46ba..4a2e9532 100644
--- a/ice40/main.cc
+++ b/ice40/main.cc
@@ -406,9 +406,11 @@ int main(int argc, char *argv[])
if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>();
std::string pcf = "";
- if (vm.count("pcf"))
+ w.load_json(filename);
+ if (vm.count("pcf")) {
pcf = vm["pcf"].as<std::string>();
- w.load_json(filename, pcf);
+ w.load_pcf(pcf);
+ }
}
w.show();