diff options
-rw-r--r-- | ecp5/arch.h | 3 | ||||
-rw-r--r-- | ecp5/config.h | 1 | ||||
-rw-r--r-- | ecp5/resource/chipdb.rc | 5 | ||||
-rw-r--r-- | ecp5/resource/embed.cc | 28 | ||||
-rw-r--r-- | ecp5/resource/resource.h | 4 | ||||
-rwxr-xr-x | ecp5/trellis_import.py | 2 | ||||
-rw-r--r-- | generic/arch.cc | 12 | ||||
-rw-r--r-- | generic/arch.h | 1 | ||||
-rw-r--r-- | gui/application.cc | 13 | ||||
-rw-r--r-- | gui/base.qrc | 8 | ||||
-rw-r--r-- | gui/basewindow.cc | 360 | ||||
-rw-r--r-- | gui/basewindow.h | 51 | ||||
-rw-r--r-- | gui/designwidget.cc | 11 | ||||
-rw-r--r-- | gui/designwidget.h | 1 | ||||
-rw-r--r-- | gui/ecp5/mainwindow.cc | 8 | ||||
-rw-r--r-- | gui/generic/mainwindow.cc | 8 | ||||
-rw-r--r-- | gui/ice40/mainwindow.cc | 286 | ||||
-rw-r--r-- | gui/ice40/mainwindow.h | 36 | ||||
-rw-r--r-- | gui/ice40/nextpnr.qrc | 8 | ||||
-rw-r--r-- | gui/line_editor.cc | 6 | ||||
-rw-r--r-- | gui/pythontab.cc | 6 | ||||
-rw-r--r-- | gui/resources/control_pause.png (renamed from gui/ice40/resources/control_pause.png) | bin | 721 -> 721 bytes | |||
-rw-r--r-- | gui/resources/control_play.png (renamed from gui/ice40/resources/control_play.png) | bin | 717 -> 717 bytes | |||
-rw-r--r-- | gui/resources/control_stop.png (renamed from gui/ice40/resources/control_stop.png) | bin | 695 -> 695 bytes | |||
-rw-r--r-- | gui/resources/open_json.png (renamed from gui/ice40/resources/open_json.png) | bin | 2093 -> 2093 bytes | |||
-rw-r--r-- | gui/resources/pack.png (renamed from gui/ice40/resources/pack.png) | bin | 853 -> 853 bytes | |||
-rw-r--r-- | gui/resources/place.png (renamed from gui/ice40/resources/place.png) | bin | 825 -> 825 bytes | |||
-rw-r--r-- | gui/resources/route.png (renamed from gui/ice40/resources/route.png) | bin | 683 -> 683 bytes | |||
-rw-r--r-- | gui/resources/time_add.png (renamed from gui/ice40/resources/time_add.png) | bin | 827 -> 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.cc | 39 | ||||
-rw-r--r-- | ice40/arch.h | 15 | ||||
-rw-r--r-- | ice40/chipdb.py | 73 | ||||
-rw-r--r-- | ice40/main.cc | 15 |
35 files changed, 576 insertions, 482 deletions
diff --git a/ecp5/arch.h b/ecp5/arch.h index 1ddc4003..cd103b12 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -22,6 +22,7 @@ #error Include "arch.h" via "nextpnr.h" only. #endif +#include <set> #include <sstream> NEXTPNR_NAMESPACE_BEGIN @@ -453,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) @@ -462,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/ecp5/config.h b/ecp5/config.h index 637069e9..038ddbf0 100644 --- a/ecp5/config.h +++ b/ecp5/config.h @@ -21,6 +21,7 @@ #define ECP5_CONFIG_H #include "nextpnr.h" +#include <map> NEXTPNR_NAMESPACE_BEGIN diff --git a/ecp5/resource/chipdb.rc b/ecp5/resource/chipdb.rc new file mode 100644 index 00000000..7191f581 --- /dev/null +++ b/ecp5/resource/chipdb.rc @@ -0,0 +1,5 @@ +#include "resource.h" + +IDR_CHIPDB_25K BINARYFILE "..\chipdbs\chipdb-25k.bin" +IDR_CHIPDB_45K BINARYFILE "..\chipdbs\chipdb-45k.bin" +IDR_CHIPDB_88K BINARYFILE "..\chipdbs\chipdb-85k.bin" diff --git a/ecp5/resource/embed.cc b/ecp5/resource/embed.cc new file mode 100644 index 00000000..adbb7781 --- /dev/null +++ b/ecp5/resource/embed.cc @@ -0,0 +1,28 @@ +#include <cstdio> +#include <windows.h> +#include "nextpnr.h" +#include "resource.h" + +NEXTPNR_NAMESPACE_BEGIN + +const char *chipdb_blob_25k; +const char *chipdb_blob_45k; +const char *chipdb_blob_85k; + +const char *LoadFileInResource(int name, int type, DWORD &size) +{ + HMODULE handle = ::GetModuleHandle(NULL); + HRSRC rc = ::FindResource(handle, MAKEINTRESOURCE(name), MAKEINTRESOURCE(type)); + HGLOBAL rcData = ::LoadResource(handle, rc); + size = ::SizeofResource(handle, rc); + return static_cast<const char *>(::LockResource(rcData)); +} +void load_chipdb() +{ + DWORD size = 0; + chipdb_blob_25k = LoadFileInResource(IDR_CHIPDB_25K, BINARYFILE, size); + chipdb_blob_45k = LoadFileInResource(IDR_CHIPDB_45K, BINARYFILE, size); + chipdb_blob_85k = LoadFileInResource(IDR_CHIPDB_85K, BINARYFILE, size); +} + +NEXTPNR_NAMESPACE_END
\ No newline at end of file diff --git a/ecp5/resource/resource.h b/ecp5/resource/resource.h new file mode 100644 index 00000000..1a18bee2 --- /dev/null +++ b/ecp5/resource/resource.h @@ -0,0 +1,4 @@ +#define BINARYFILE 256 +#define IDR_CHIPDB_25K 101 +#define IDR_CHIPDB_45K 102 +#define IDR_CHIPDB_85K 103 diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index d60ab6f4..99e9078f 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -275,7 +275,7 @@ def write_database(dev_name, chip, ddrg, endianness): bba.l("tiletype_names", "RelPtr<char>") - for tt in tiletype_names: + for tt, idx in sorted(tiletype_names.items(), key=lambda x: x[1]): bba.s(tt, "name") bba.l("chip_info") diff --git a/generic/arch.cc b/generic/arch.cc index cff638df..1c22dbf7 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -149,12 +149,6 @@ void Arch::addDecalGraphic(DecalId decal, const GraphicElement &graphic) refreshUi(); } -void Arch::setFrameDecal(DecalXY decalxy) -{ - frame_decalxy = decalxy; - refreshUiFrame(); -} - void Arch::setWireDecal(WireId wire, DecalXY decalxy) { wires.at(wire).decalxy = decalxy; @@ -403,18 +397,18 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const return (dx + dy) * grid_distance_to_delay; } -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const; +delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const { const auto &driver = net_info->driver; auto driver_loc = getBelLocation(driver.cell->bel); auto sink_loc = getBelLocation(sink.cell->bel); int dx = abs(driver_loc.x - driver_loc.x); - int dy = abs(sink_loc.y - sink_locy); + int dy = abs(sink_loc.y - sink_loc.y); return (dx + dy) * grid_distance_to_delay; } -delay_t getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const { return budget; } +delay_t Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const { return budget; } // --------------------------------------------------------------- diff --git a/generic/arch.h b/generic/arch.h index 0c756d3a..d3e8f69e 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -110,7 +110,6 @@ struct Arch : BaseCtx void addGroupGroup(IdString group, IdString grp); void addDecalGraphic(DecalId decal, const GraphicElement &graphic); - void setFrameDecal(DecalXY decalxy); void setWireDecal(WireId wire, DecalXY decalxy); void setPipDecal(PipId pip, DecalXY decalxy); void setBelDecal(BelId bel, DecalXY decalxy); diff --git a/gui/application.cc b/gui/application.cc index 58dc23eb..aece5d2a 100644 --- a/gui/application.cc +++ b/gui/application.cc @@ -27,11 +27,24 @@ NEXTPNR_NAMESPACE_BEGIN +#ifdef _WIN32 +#include <windows.h> +BOOL WINAPI WinHandler(DWORD dwCtrlType) +{ + if (dwCtrlType == CTRL_C_EVENT) + qApp->quit(); + return TRUE; +} +#endif + Application::Application(int &argc, char **argv) : QApplication(argc, argv) { QSurfaceFormat fmt; fmt.setSamples(10); QSurfaceFormat::setDefaultFormat(fmt); +#ifdef _WIN32 + SetConsoleCtrlHandler((PHANDLER_ROUTINE)WinHandler, TRUE); +#endif } bool Application::notify(QObject *receiver, QEvent *event) 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 b8bdd36a..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,37 +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()->tabButton(0, QTabBar::RightSide)->resize(0, 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)));
+ centralTabWidget->tabBar()->setTabButton(0, QTabBar::RightSide, 0);
+ centralTabWidget->tabBar()->setTabButton(0, QTabBar::LeftSide, 0);
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); }
@@ -108,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);
@@ -154,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 Binary files differindex ec61099b..ec61099b 100644 --- a/gui/ice40/resources/control_pause.png +++ b/gui/resources/control_pause.png diff --git a/gui/ice40/resources/control_play.png b/gui/resources/control_play.png Binary files differindex f8c8ec68..f8c8ec68 100644 --- a/gui/ice40/resources/control_play.png +++ b/gui/resources/control_play.png diff --git a/gui/ice40/resources/control_stop.png b/gui/resources/control_stop.png Binary files differindex e6f75d23..e6f75d23 100644 --- a/gui/ice40/resources/control_stop.png +++ b/gui/resources/control_stop.png diff --git a/gui/ice40/resources/open_json.png b/gui/resources/open_json.png Binary files differindex 90c07267..90c07267 100644 --- a/gui/ice40/resources/open_json.png +++ b/gui/resources/open_json.png diff --git a/gui/ice40/resources/pack.png b/gui/resources/pack.png Binary files differindex da3c2a2d..da3c2a2d 100644 --- a/gui/ice40/resources/pack.png +++ b/gui/resources/pack.png diff --git a/gui/ice40/resources/place.png b/gui/resources/place.png Binary files differindex 0905f933..0905f933 100644 --- a/gui/ice40/resources/place.png +++ b/gui/resources/place.png diff --git a/gui/ice40/resources/route.png b/gui/resources/route.png Binary files differindex 258c16c6..258c16c6 100644 --- a/gui/ice40/resources/route.png +++ b/gui/resources/route.png diff --git a/gui/ice40/resources/time_add.png b/gui/resources/time_add.png Binary files differindex dcc45cb2..dcc45cb2 100644 --- a/gui/ice40/resources/time_add.png +++ b/gui/resources/time_add.png 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.cc b/ice40/arch.cc index 2867f591..eff1d9b9 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -834,28 +834,23 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { - if (cell->type == id_icestorm_lc) { - if ((fromPort == id_i0 || fromPort == id_i1 || fromPort == id_i2 || fromPort == id_i3) && - (toPort == id_o || toPort == id_lo)) { - delay.delay = 450; - return true; - } else if (fromPort == id_cin && toPort == id_cout) { - delay.delay = 120; - return true; - } else if (fromPort == id_i1 && toPort == id_cout) { - delay.delay = 260; - return true; - } else if (fromPort == id_i2 && toPort == id_cout) { - delay.delay = 230; - return true; - } else if (fromPort == id_clk && toPort == id_o) { - delay.delay = 540; - return true; - } - } else if (cell->type == id_icestorm_ram) { - if (fromPort == id_rclk) { - delay.delay = 2140; - return true; + BelType type = belTypeFromId(cell->type); + for (int i = 0; i < chip_info->num_timing_cells; i++) { + const auto &tc = chip_info->cell_timing[i]; + if (tc.type == type) { + PortPin fromPin = portPinFromId(fromPort); + PortPin toPin = portPinFromId(toPort); + for (int j = 0; j < tc.num_paths; j++) { + const auto &path = tc.path_delays[j]; + if (path.from_port == fromPin && path.to_port == toPin) { + if (fast_part) + delay.delay = path.fast_delay; + else + delay.delay = path.slow_delay; + return true; + } + } + break; } } return false; diff --git a/ice40/arch.h b/ice40/arch.h index 4459af60..98361132 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -173,10 +173,24 @@ NPNR_PACKED_STRUCT(struct BelConfigPOD { RelPtr<BelConfigEntryPOD> entries; }); +NPNR_PACKED_STRUCT(struct CellPathDelayPOD { + PortPin from_port; + PortPin to_port; + int32_t fast_delay; + int32_t slow_delay; +}); + +NPNR_PACKED_STRUCT(struct CellTimingPOD { + int32_t type; + int32_t num_paths; + RelPtr<CellPathDelayPOD> path_delays; +}); + NPNR_PACKED_STRUCT(struct ChipInfoPOD { int32_t width, height; int32_t num_bels, num_wires, num_pips; int32_t num_switches, num_belcfgs, num_packages; + int32_t num_timing_cells; RelPtr<BelInfoPOD> bel_data; RelPtr<WireInfoPOD> wire_data; RelPtr<PipInfoPOD> pip_data; @@ -184,6 +198,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { RelPtr<BitstreamInfoPOD> bits_info; RelPtr<BelConfigPOD> bel_config; RelPtr<PackageInfoPOD> packages_data; + RelPtr<CellTimingPOD> cell_timing; }); #if defined(_MSC_VER) diff --git a/ice40/chipdb.py b/ice40/chipdb.py index cb336d0a..a0d7f03c 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -696,6 +696,60 @@ def add_bel_ec(ec): else: extra_cell_config[bel].append(entry) +cell_timings = {} +tmport_to_portpin = { + "posedge:clk": "CLK", + "ce": "CEN", + "sr": "SR", + "in0": "I0", + "in1": "I1", + "in2": "I2", + "in3": "I3", + "carryin": "CIN", + "carryout": "COUT", + "lcout": "O", + "ltout": "LO", + "posedge:RCLK": "RCLK", + "posedge:WCLK": "WCLK", + "RCLKE": "RCLKE", + "RE": "RE", + "WCLKE": "WCLKE", + "WE": "WE", + "posedge:CLOCK": "CLOCK", + "posedge:SLEEP": "SLEEP" +} + +for i in range(16): + tmport_to_portpin["RDATA[%d]" % i] = "RDATA_%d" % i + tmport_to_portpin["WDATA[%d]" % i] = "WDATA_%d" % i + tmport_to_portpin["MASK[%d]" % i] = "MASK_%d" % i + tmport_to_portpin["DATAOUT[%d]" % i] = "DATAOUT_%d" % i + +for i in range(11): + tmport_to_portpin["RADDR[%d]" % i] = "RADDR_%d" % i + tmport_to_portpin["WADDR[%d]" % i] = "WADDR_%d" % i + +def add_cell_timingdata(bel_type, timing_cell, fast_db, slow_db): + timing_entries = [] + database = slow_db if slow_db is not None else fast_db + for key in database.keys(): + skey = key.split(".") + if skey[0] == timing_cell: + if skey[1] in tmport_to_portpin and skey[2] in tmport_to_portpin: + iport = tmport_to_portpin[skey[1]] + oport = tmport_to_portpin[skey[2]] + fastdel = fast_db[key] if fast_db is not None else 0 + slowdel = slow_db[key] if slow_db is not None else 0 + timing_entries.append((iport, oport, fastdel, slowdel)) + cell_timings[bel_type] = timing_entries + +add_cell_timingdata("ICESTORM_LC", "LogicCell40", fast_timings, slow_timings) +if dev_name != "384": + add_cell_timingdata("ICESTORM_RAM", "SB_RAM40_4K", fast_timings, slow_timings) +if dev_name == "5k": + add_cell_timingdata("SPRAM", "SB_SPRAM256KA", fast_timings, slow_timings) + + for tile_xy, tile_type in sorted(tiles.items()): if tile_type == "logic": for i in range(8): @@ -1109,6 +1163,23 @@ for info in packageinfo: bba.u32(info[1], "num_pins") bba.r(info[2], "pins") +for cell, timings in sorted(cell_timings.items()): + beltype = beltypes[cell] + bba.l("cell_paths_%d" % beltype, "CellPathDelayPOD") + for entry in timings: + fromport, toport, fast, slow = entry + bba.u32(portpins[fromport], "from_port") + bba.u32(portpins[toport], "to_port") + bba.u32(fast, "fast_delay") + bba.u32(slow, "slow_delay") + +bba.l("cell_timings_%s" % dev_name, "CellTimingPOD") +for cell, timings in sorted(cell_timings.items()): + beltype = beltypes[cell] + bba.u32(beltype, "type") + bba.u32(len(timings), "num_paths") + bba.r("cell_paths_%d" % beltype, "path_delays") + bba.l("chip_info_%s" % dev_name) bba.u32(dev_width, "dev_width") bba.u32(dev_height, "dev_height") @@ -1118,6 +1189,7 @@ bba.u32(len(pipinfo), "num_pips") bba.u32(len(switchinfo), "num_switches") bba.u32(len(extra_cell_config), "num_belcfgs") bba.u32(len(packageinfo), "num_packages") +bba.u32(len(cell_timings), "num_timing_cells") bba.r("bel_data_%s" % dev_name, "bel_data") bba.r("wire_data_%s" % dev_name, "wire_data") bba.r("pip_data_%s" % dev_name, "pip_data") @@ -1125,5 +1197,6 @@ bba.r("tile_grid_%s" % dev_name, "tile_grid") bba.r("bits_info_%s" % dev_name, "bits_info") bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config") bba.r("package_info_%s" % dev_name, "packages_data") +bba.r("cell_timings_%s" % dev_name, "cell_timing") bba.pop() diff --git a/ice40/main.cc b/ice40/main.cc index 0724acdf..4a2e9532 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -168,6 +168,11 @@ int main(int argc, char *argv[]) pt::read_json(filename, root); log_info("Loading project %s...\n", filename.c_str()); log_break(); + + bool isLoadingGui = vm.count("gui") > 0; + std::string ascOutput; + if (vm.count("asc")) + ascOutput = vm["asc"].as<std::string>(); vm.clear(); int version = root.get<int>("project.version"); @@ -199,6 +204,10 @@ int main(int argc, char *argv[]) if (params.count("seed")) vm.insert(std::make_pair("seed", po::variable_value(params.get<int>("seed"), false))); } + if (!ascOutput.empty()) + vm.insert(std::make_pair("asc", po::variable_value(ascOutput, false))); + if (isLoadingGui) + vm.insert(std::make_pair("gui", po::variable_value())); po::notify(vm); } catch (...) { log_error("Error loading project file.\n"); @@ -397,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(); |