From 29df566a60e575462a8dd96a1d405f7d5632f38d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 16 Jul 2018 08:07:57 +0200 Subject: Fix MSVC build --- CMakeLists.txt | 5 ++++- gui/fpgaviewwidget.cc | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ca7935e..c8ee6805 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,7 +214,10 @@ foreach (family ${ARCH}) # Include family-specific source files to all family targets and set defines appropriately target_include_directories(${target} PRIVATE ${family}/ ${CMAKE_CURRENT_BINARY_DIR}/generated/) target_compile_definitions(${target} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family}) - target_link_libraries(${target} LINK_PUBLIC ${Boost_LIBRARIES} pthread) + target_link_libraries(${target} LINK_PUBLIC ${Boost_LIBRARIES}) + if (NOT MSVC) + target_link_libraries(${target} LINK_PUBLIC pthread) + endif() add_sanitizers(${target}) if (BUILD_GUI) target_include_directories(${target} PRIVATE gui/${family}/ gui/) diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 2d8d4cef..3ac69463 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -400,10 +400,10 @@ void FPGAViewWidget::paintGL() } lineShader_.draw(grid, gridColor_, thick1Px, matrix); - LineShaderData shaders[4] = {[GraphicElement::G_FRAME] = LineShaderData(), - [GraphicElement::G_HIDDEN] = LineShaderData(), - [GraphicElement::G_INACTIVE] = LineShaderData(), - [GraphicElement::G_ACTIVE] = LineShaderData()}; + LineShaderData shaders[4] = {LineShaderData(), + LineShaderData(), + LineShaderData(), + LineShaderData()}; if (ctx_) { // Draw Bels. -- cgit v1.2.3 From 56fa8cc669f07194527e171474a514ef80af48e1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 16 Jul 2018 17:13:40 +0200 Subject: refactor: Remove incorrect uses of the term 'wirelength' Signed-off-by: David Shah --- common/place_common.cc | 18 ++++++++-------- common/place_common.h | 12 ++++++++--- common/placer1.cc | 54 ++++++++++++++++++++++++------------------------ ice40/place_legaliser.cc | 8 +++---- 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/common/place_common.cc b/common/place_common.cc index 60735890..b2f0e849 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -25,7 +25,7 @@ NEXTPNR_NAMESPACE_BEGIN // Get the total estimated wirelength for a net -wirelen_t get_net_wirelength(const Context *ctx, const NetInfo *net, float &tns) +wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns) { wirelen_t wirelength = 0; int driver_x, driver_y; @@ -47,7 +47,7 @@ wirelen_t get_net_wirelength(const Context *ctx, const NetInfo *net, float &tns) CellInfo *load_cell = load.cell; if (load_cell->bel == BelId()) continue; - if (ctx->timing_driven) { + if (ctx->timing_driven && type == MetricType::COST) { WireId user_wire = ctx->getWireBelPin(load_cell->bel, ctx->portPinFromId(load.port)); delay_t raw_wl = ctx->estimateDelay(drv_wire, user_wire); float slack = ctx->getDelayNS(load.budget) - ctx->getDelayNS(raw_wl); @@ -66,7 +66,7 @@ wirelen_t get_net_wirelength(const Context *ctx, const NetInfo *net, float &tns) xmax = std::max(xmax, load_x); ymax = std::max(ymax, load_y); } - if (ctx->timing_driven) { + if (ctx->timing_driven && type == MetricType::COST) { wirelength = wirelen_t((((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-worst_slack / 5))))); } else { wirelength = wirelen_t((ymax - ymin) + (xmax - xmin)); @@ -76,7 +76,7 @@ wirelen_t get_net_wirelength(const Context *ctx, const NetInfo *net, float &tns) } // Get the total wirelength for a cell -wirelen_t get_cell_wirelength(const Context *ctx, const CellInfo *cell) +wirelen_t get_cell_metric(const Context *ctx, const CellInfo *cell, MetricType type) { std::set nets; for (auto p : cell->ports) { @@ -86,16 +86,16 @@ wirelen_t get_cell_wirelength(const Context *ctx, const CellInfo *cell) wirelen_t wirelength = 0; float tns = 0; for (auto n : nets) { - wirelength += get_net_wirelength(ctx, ctx->nets.at(n).get(), tns); + wirelength += get_net_metric(ctx, ctx->nets.at(n).get(), type, tns); } return wirelength; } -wirelen_t get_cell_wirelength_at_bel(const Context *ctx, CellInfo *cell, BelId bel) +wirelen_t get_cell_metric_at_bel(const Context *ctx, CellInfo *cell, BelId bel, MetricType type) { BelId oldBel = cell->bel; cell->bel = bel; - wirelen_t wirelen = get_cell_wirelength(ctx, cell); + wirelen_t wirelen = get_cell_metric(ctx, cell, type); cell->bel = oldBel; return wirelen; } @@ -118,7 +118,7 @@ bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality) for (auto bel : ctx->getBels()) { if (ctx->getBelType(bel) == targetType && (!require_legality || ctx->isValidBelForCell(cell, bel))) { if (ctx->checkBelAvail(bel)) { - wirelen_t wirelen = get_cell_wirelength_at_bel(ctx, cell, bel); + wirelen_t wirelen = get_cell_metric_at_bel(ctx, cell, bel, MetricType::COST); if (iters >= 4) wirelen += ctx->rng(25); if (wirelen <= best_wirelen) { @@ -126,7 +126,7 @@ bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality) best_bel = bel; } } else { - wirelen_t wirelen = get_cell_wirelength_at_bel(ctx, cell, bel); + wirelen_t wirelen = get_cell_metric_at_bel(ctx, cell, bel, MetricType::COST); if (iters >= 4) wirelen += ctx->rng(25); if (wirelen <= best_ripup_wirelen) { diff --git a/common/place_common.h b/common/place_common.h index 67956072..32250604 100644 --- a/common/place_common.h +++ b/common/place_common.h @@ -26,14 +26,20 @@ NEXTPNR_NAMESPACE_BEGIN typedef int64_t wirelen_t; +enum class MetricType +{ + COST, + WIRELENGTH +}; + // Return the wirelength of a net -wirelen_t get_net_wirelength(const Context *ctx, const NetInfo *net, float &tns); +wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns); // Return the wirelength of all nets connected to a cell -wirelen_t get_cell_wirelength(const Context *ctx, const CellInfo *cell); +wirelen_t get_cell_metric(const Context *ctx, const CellInfo *cell, MetricType type); // Return the wirelength of all nets connected to a cell, when the cell is at a given bel -wirelen_t get_cell_wirelength_at_bel(const Context *ctx, CellInfo *cell, BelId bel); +wirelen_t get_cell_metric_at_bel(const Context *ctx, CellInfo *cell, BelId bel, MetricType type); // Place a single cell in the lowest wirelength Bel available, optionally requiring validity check bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality); diff --git a/common/placer1.cc b/common/placer1.cc index 53295a91..74a11040 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -135,17 +135,17 @@ class SAPlacer log_info("Running simulated annealing placer.\n"); - // Calculate wirelength after initial placement - curr_wirelength = 0; + // Calculate metric after initial placement + curr_metric = 0; curr_tns = 0; for (auto &net : ctx->nets) { - wirelen_t wl = get_net_wirelength(ctx, net.second.get(), curr_tns); - wirelengths[net.first] = wl; - curr_wirelength += wl; + wirelen_t wl = get_net_metric(ctx, net.second.get(), MetricType::COST, curr_tns); + metrics[net.first] = wl; + curr_metric += wl; } int n_no_progress = 0; - double avg_wirelength = curr_wirelength; + double avg_metric = curr_metric; temp = 10000; // Main simulated annealing loop @@ -154,9 +154,9 @@ class SAPlacer improved = false; if (iter % 5 == 0 || iter == 1) - log_info(" at iteration #%d: temp = %f, wire length = " + log_info(" at iteration #%d: temp = %f, cost = " "%.0f, est tns = %.02fns\n", - iter, temp, double(curr_wirelength), curr_tns); + iter, temp, double(curr_metric), curr_tns); for (int m = 0; m < 15; ++m) { // Loop through all automatically placed cells @@ -177,7 +177,7 @@ class SAPlacer if (temp <= 1e-3 && n_no_progress >= 5) { if (iter % 5 != 0) - log_info(" at iteration #%d: temp = %f, wire length = %f\n", iter, temp, double(curr_wirelength)); + log_info(" at iteration #%d: temp = %f, cost = %f\n", iter, temp, double(curr_metric)); break; } @@ -187,8 +187,8 @@ class SAPlacer double upper = 0.6, lower = 0.4; - if (curr_wirelength < 0.95 * avg_wirelength) { - avg_wirelength = 0.8 * avg_wirelength + 0.2 * curr_wirelength; + if (curr_metric < 0.95 * avg_metric) { + avg_metric = 0.8 * avg_metric + 0.2 * curr_metric; } else { if (Raccept >= 0.8) { temp *= 0.7; @@ -223,14 +223,14 @@ class SAPlacer assign_budget(ctx); } - // Recalculate total wirelength entirely to avoid rounding errors + // Recalculate total metric entirely to avoid rounding errors // accumulating over time - curr_wirelength = 0; + curr_metric = 0; curr_tns = 0; for (auto &net : ctx->nets) { - wirelen_t wl = get_net_wirelength(ctx, net.second.get(), curr_tns); - wirelengths[net.first] = wl; - curr_wirelength += wl; + wirelen_t wl = get_net_metric(ctx, net.second.get(), MetricType::COST, curr_tns); + metrics[net.first] = wl; + curr_metric += wl; } } // Final post-pacement validitiy check @@ -320,7 +320,7 @@ class SAPlacer if (other_cell->belStrength > STRENGTH_WEAK) return false; } - wirelen_t new_wirelength = 0, delta; + wirelen_t new_metric = 0, delta; ctx->unbindBel(oldBel); if (other != IdString()) { ctx->unbindBel(newBel); @@ -350,17 +350,17 @@ class SAPlacer } } - new_wirelength = curr_wirelength; + new_metric = curr_metric; - // Recalculate wirelengths for all nets touched by the peturbation + // Recalculate metrics for all nets touched by the peturbation for (auto net : update) { - new_wirelength -= wirelengths.at(net->name); + new_metric -= metrics.at(net->name); float temp_tns = 0; - wirelen_t net_new_wl = get_net_wirelength(ctx, net, temp_tns); - new_wirelength += net_new_wl; + wirelen_t net_new_wl = get_net_metric(ctx, net, MetricType::COST, temp_tns); + new_metric += net_new_wl; new_lengths.push_back(std::make_pair(net->name, net_new_wl)); } - delta = new_wirelength - curr_wirelength; + delta = new_metric - curr_metric; n_move++; // SA acceptance criterea if (delta < 0 || (temp > 1e-6 && (ctx->rng() / float(0x3fffffff)) <= std::exp(-delta / temp))) { @@ -373,9 +373,9 @@ class SAPlacer ctx->unbindBel(newBel); goto swap_fail; } - curr_wirelength = new_wirelength; + curr_metric = new_metric; for (auto new_wl : new_lengths) - wirelengths.at(new_wl.first) = new_wl.second; + metrics.at(new_wl.first) = new_wl.second; return true; swap_fail: @@ -413,8 +413,8 @@ class SAPlacer } Context *ctx; - std::unordered_map wirelengths; - wirelen_t curr_wirelength = std::numeric_limits::max(); + std::unordered_map metrics; + wirelen_t curr_metric = std::numeric_limits::max(); float curr_tns = 0; float temp = 1000; bool improved = false; diff --git a/ice40/place_legaliser.cc b/ice40/place_legaliser.cc index 559358c7..5fffb4fb 100644 --- a/ice40/place_legaliser.cc +++ b/ice40/place_legaliser.cc @@ -246,7 +246,7 @@ class PlacementLegaliser std::tuple find_closest_bel(float target_x, float target_y, CellChain &chain) { std::tuple best_origin = std::make_tuple(-1, -1, -1); - wirelen_t best_wirelength = std::numeric_limits::max(); + wirelen_t best_metric = std::numeric_limits::max(); int width = ctx->chip_info->width, height = ctx->chip_info->height; // Slow, should radiate outwards from target position - TODO int chain_size = int(chain.cells.size()); @@ -260,11 +260,11 @@ class PlacementLegaliser valid = false; break; } else { - wirelen += get_cell_wirelength_at_bel(ctx, chain.cells.at(k), lb.first); + wirelen += get_cell_metric_at_bel(ctx, chain.cells.at(k), lb.first, MetricType::COST); } } - if (valid && wirelen < best_wirelength) { - best_wirelength = wirelen; + if (valid && wirelen < best_metric) { + best_metric = wirelen; best_origin = std::make_tuple(x, y, 0); } } -- cgit v1.2.3 From ee2ed461e6f2bab10234e437276c7b312437bf0c Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 16 Jul 2018 21:15:49 +0200 Subject: Added Yosys tab with interactive console --- gui/basewindow.cc | 23 +++++++++++++ gui/basewindow.h | 2 ++ gui/line_editor.cc | 1 - gui/yosys_edit.cc | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ gui/yosys_edit.h | 57 ++++++++++++++++++++++++++++++++ gui/yosystab.cc | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++ gui/yosystab.h | 59 +++++++++++++++++++++++++++++++++ 7 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 gui/yosys_edit.cc create mode 100644 gui/yosys_edit.h create mode 100644 gui/yosystab.cc create mode 100644 gui/yosystab.h diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 07b71105..af048f0e 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -29,6 +29,7 @@ #include "log.h" #include "mainwindow.h" #include "pythontab.h" +#include "yosystab.h" static void initBasenameResource() { Q_INIT_RESOURCE(base); } @@ -73,8 +74,12 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr context, QWidget *parent connect(this, SIGNAL(contextChanged(Context *)), console, SLOT(newContext(Context *))); centralTabWidget = new QTabWidget(); + centralTabWidget->setTabsClosable(true); + connect(centralTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); + FPGAViewWidget *fpgaView = new FPGAViewWidget(); centralTabWidget->addTab(fpgaView, "Graphics"); + centralTabWidget->tabBar()->tabButton(0, QTabBar::RightSide)->resize(0, 0); connect(this, SIGNAL(contextChanged(Context *)), fpgaView, SLOT(newContext(Context *))); connect(designview, SIGNAL(selected(std::vector)), fpgaView, @@ -95,6 +100,8 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr context, QWidget *parent BaseMainWindow::~BaseMainWindow() {} +void BaseMainWindow::closeTab(int index) { delete centralTabWidget->widget(index); } + void BaseMainWindow::displaySplash() { splash = new QSplashScreen(); @@ -140,6 +147,10 @@ void BaseMainWindow::createMenusAndBars() actionExit->setStatusTip("Exit the application"); connect(actionExit, SIGNAL(triggered()), this, SLOT(close())); + QAction *actionYosys = new QAction("Yosys", this); + actionYosys->setStatusTip("Run Yosys"); + connect(actionYosys, SIGNAL(triggered()), this, SLOT(yosys())); + QAction *actionAbout = new QAction("About", this); menuBar = new QMenuBar(); @@ -172,6 +183,18 @@ void BaseMainWindow::createMenusAndBars() mainToolBar->addAction(actionNew); mainToolBar->addAction(actionOpen); mainToolBar->addAction(actionSave); + mainToolBar->addAction(actionYosys); } +void BaseMainWindow::yosys() +{ + QString folder = QFileDialog::getExistingDirectory(0, ("Select Work Folder"), QDir::currentPath(), + QFileDialog::ShowDirsOnly); + if (!folder.isEmpty() && !folder.isNull()) { + YosysTab *yosysTab = new YosysTab(folder); + yosysTab->setAttribute(Qt::WA_DeleteOnClose); + centralTabWidget->addTab(yosysTab, "Yosys"); + centralTabWidget->setCurrentWidget(yosysTab); + } +} NEXTPNR_NAMESPACE_END diff --git a/gui/basewindow.h b/gui/basewindow.h index 18b5339e..087880ed 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -55,10 +55,12 @@ class BaseMainWindow : public QMainWindow protected Q_SLOTS: void writeInfo(std::string text); void displaySplashMessage(std::string msg); + void closeTab(int index); virtual void new_proj() = 0; virtual void open_proj() = 0; virtual bool save_proj() = 0; + void yosys(); Q_SIGNALS: void contextChanged(Context *ctx); diff --git a/gui/line_editor.cc b/gui/line_editor.cc index 425f2876..23415092 100644 --- a/gui/line_editor.cc +++ b/gui/line_editor.cc @@ -48,7 +48,6 @@ void LineEditor::keyPressEvent(QKeyEvent *ev) QToolTip::hideText(); if (lines.empty()) return; - printf("Key_Up\n"); if (ev->key() == Qt::Key_Up) index--; if (ev->key() == Qt::Key_Down) diff --git a/gui/yosys_edit.cc b/gui/yosys_edit.cc new file mode 100644 index 00000000..72d2430d --- /dev/null +++ b/gui/yosys_edit.cc @@ -0,0 +1,96 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * Copyright (C) 2018 Alex Tsui + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "yosys_edit.h" +#include +#include + +NEXTPNR_NAMESPACE_BEGIN + +YosysLineEditor::YosysLineEditor(QWidget *parent) : QLineEdit(parent), index(0) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + QAction *clearAction = new QAction("Clear &history", this); + clearAction->setStatusTip("Clears line edit history"); + connect(clearAction, SIGNAL(triggered()), this, SLOT(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))); +} + +void YosysLineEditor::keyPressEvent(QKeyEvent *ev) +{ + + if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { + QToolTip::hideText(); + if (lines.empty()) + return; + if (ev->key() == Qt::Key_Up) + index--; + if (ev->key() == Qt::Key_Down) + index++; + + if (index < 0) + index = 0; + if (index >= lines.size()) { + index = lines.size(); + clear(); + return; + } + setText(lines[index]); + } else if (ev->key() == Qt::Key_Escape) { + QToolTip::hideText(); + clear(); + return; + } else if (ev->key() == Qt::Key_Tab) { + return; + } + QToolTip::hideText(); + + QLineEdit::keyPressEvent(ev); +} + +// This makes TAB work +bool YosysLineEditor::focusNextPrevChild(bool next) { return false; } + +void YosysLineEditor::textInserted() +{ + if (lines.empty() || lines.back() != text()) + lines += text(); + if (lines.size() > 100) + lines.removeFirst(); + index = lines.size(); + clear(); + Q_EMIT textLineInserted(lines.back()); +} + +void YosysLineEditor::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); } + +void YosysLineEditor::clearHistory() +{ + lines.clear(); + index = 0; + clear(); +} + +NEXTPNR_NAMESPACE_END diff --git a/gui/yosys_edit.h b/gui/yosys_edit.h new file mode 100644 index 00000000..05e4ae36 --- /dev/null +++ b/gui/yosys_edit.h @@ -0,0 +1,57 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * Copyright (C) 2018 Alex Tsui + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef YOSYS_EDIT_H +#define YOSYS_EDIT_H + +#include +#include +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +class YosysLineEditor : public QLineEdit +{ + Q_OBJECT + + public: + explicit YosysLineEditor(QWidget *parent = 0); + + private Q_SLOTS: + void textInserted(); + void showContextMenu(const QPoint &pt); + void clearHistory(); + + Q_SIGNALS: + void textLineInserted(QString); + + protected: + void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; + bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE; + + private: + int index; + QStringList lines; + QMenu *contextMenu; +}; + +NEXTPNR_NAMESPACE_END + +#endif // YOSYS_EDIT_H diff --git a/gui/yosystab.cc b/gui/yosystab.cc new file mode 100644 index 00000000..1661c74f --- /dev/null +++ b/gui/yosystab.cc @@ -0,0 +1,91 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "yosystab.h" +#include + +NEXTPNR_NAMESPACE_BEGIN + +YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent) +{ + QFont f("unexistent"); + f.setStyleHint(QFont::Monospace); + + console = new QPlainTextEdit(); + console->setMinimumHeight(100); + console->setReadOnly(true); + console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + console->setFont(f); + + console->setContextMenuPolicy(Qt::CustomContextMenu); + QAction *clearAction = new QAction("Clear &buffer", this); + clearAction->setStatusTip("Clears display buffer"); + connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer())); + contextMenu = console->createStandardContextMenu(); + contextMenu->addSeparator(); + contextMenu->addAction(clearAction); + connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint))); + + lineEdit = new YosysLineEditor(); + lineEdit->setMinimumHeight(30); + lineEdit->setMaximumHeight(30); + lineEdit->setFont(f); + lineEdit->setFocus(); + lineEdit->setPlaceholderText("yosys>"); + connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString))); + + QGridLayout *mainLayout = new QGridLayout(); + mainLayout->addWidget(console, 0, 0); + mainLayout->addWidget(lineEdit, 1, 0); + setLayout(mainLayout); + + process = new QProcess(); + connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError())); + connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput())); + process->setWorkingDirectory(folder); + process->start("yosys"); +} + +YosysTab::~YosysTab() +{ + process->terminate(); + process->waitForFinished(1000); // in ms + process->kill(); + process->close(); +} + +void YosysTab::displayString(QString text) +{ + QTextCursor cursor = console->textCursor(); + cursor.movePosition(QTextCursor::End); + cursor.insertText(text); + cursor.movePosition(QTextCursor::End); + console->setTextCursor(cursor); +} + +void YosysTab::onReadyReadStandardOutput() { displayString(process->readAllStandardOutput()); } +void YosysTab::onReadyReadStandardError() { displayString(process->readAllStandardError()); } + +void YosysTab::editLineReturnPressed(QString text) { process->write(text.toLatin1() + "\n"); } + +void YosysTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); } + +void YosysTab::clearBuffer() { console->clear(); } + +NEXTPNR_NAMESPACE_END diff --git a/gui/yosystab.h b/gui/yosystab.h new file mode 100644 index 00000000..1c668d15 --- /dev/null +++ b/gui/yosystab.h @@ -0,0 +1,59 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef YOSYSTAB_H +#define YOSYSTAB_H + +#include +#include +#include +#include +#include "nextpnr.h" +#include "yosys_edit.h" + +NEXTPNR_NAMESPACE_BEGIN + +class YosysTab : public QWidget +{ + Q_OBJECT + + public: + explicit YosysTab(QString folder, QWidget *parent = 0); + ~YosysTab(); + + private: + void displayString(QString text); + private Q_SLOTS: + void showContextMenu(const QPoint &pt); + void editLineReturnPressed(QString text); + void onReadyReadStandardOutput(); + void onReadyReadStandardError(); + public Q_SLOTS: + void clearBuffer(); + + private: + QPlainTextEdit *console; + YosysLineEditor *lineEdit; + QMenu *contextMenu; + QProcess *process; +}; + +NEXTPNR_NAMESPACE_END + +#endif // YOSYSTAB_H -- cgit v1.2.3 From 6269d974749ff93c9df01a35bf1797116f045a66 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 16 Jul 2018 21:23:12 +0200 Subject: add tooltip for folder --- gui/basewindow.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/gui/basewindow.cc b/gui/basewindow.cc index af048f0e..e75298c0 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -195,6 +195,7 @@ void BaseMainWindow::yosys() yosysTab->setAttribute(Qt::WA_DeleteOnClose); centralTabWidget->addTab(yosysTab, "Yosys"); centralTabWidget->setCurrentWidget(yosysTab); + centralTabWidget->setTabToolTip(centralTabWidget->indexOf(yosysTab),folder); } } NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From a5f93343b622a43509f8a99a71ba37801d104ea7 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 17 Jul 2018 09:40:26 +0200 Subject: Added check for yosys availability --- gui/basewindow.cc | 2 +- gui/fpgaviewwidget.cc | 5 +---- gui/yosystab.cc | 11 +++++++++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/gui/basewindow.cc b/gui/basewindow.cc index e75298c0..81c89e45 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -195,7 +195,7 @@ void BaseMainWindow::yosys() yosysTab->setAttribute(Qt::WA_DeleteOnClose); centralTabWidget->addTab(yosysTab, "Yosys"); centralTabWidget->setCurrentWidget(yosysTab); - centralTabWidget->setTabToolTip(centralTabWidget->indexOf(yosysTab),folder); + centralTabWidget->setTabToolTip(centralTabWidget->indexOf(yosysTab), folder); } } NEXTPNR_NAMESPACE_END diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 3ac69463..c926e5fa 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -400,10 +400,7 @@ void FPGAViewWidget::paintGL() } lineShader_.draw(grid, gridColor_, thick1Px, matrix); - LineShaderData shaders[4] = {LineShaderData(), - LineShaderData(), - LineShaderData(), - LineShaderData()}; + LineShaderData shaders[4] = {LineShaderData(), LineShaderData(), LineShaderData(), LineShaderData()}; if (ctx_) { // Draw Bels. diff --git a/gui/yosystab.cc b/gui/yosystab.cc index 1661c74f..f2e99cbc 100644 --- a/gui/yosystab.cc +++ b/gui/yosystab.cc @@ -19,6 +19,7 @@ #include "yosystab.h" #include +#include NEXTPNR_NAMESPACE_BEGIN @@ -47,6 +48,7 @@ YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent) lineEdit->setMaximumHeight(30); lineEdit->setFont(f); lineEdit->setFocus(); + lineEdit->setEnabled(false); lineEdit->setPlaceholderText("yosys>"); connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString))); @@ -58,6 +60,15 @@ YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent) process = new QProcess(); connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError())); connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput())); + connect(process, &QProcess::started, this, [this] { lineEdit->setEnabled(true); }); + connect(process, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) { + if (error == QProcess::FailedToStart) { + QMessageBox::critical( + this, QString::fromUtf8("Yosys cannot be started!"), + QString::fromUtf8("

Please make sure you have Yosys installed and available in path

")); + Q_EMIT deleteLater(); + } + }); process->setWorkingDirectory(folder); process->start("yosys"); } -- cgit v1.2.3 From 924037129c711cba718927a6a8b1f17cc101f7c1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 11:00:18 +0200 Subject: Fix driver finder when net is undriven Signed-off-by: David Shah --- common/design_utils.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/design_utils.h b/common/design_utils.h index cb802a1a..95975179 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -73,6 +73,8 @@ template CellInfo *net_driven_by(const Context *ctx, const NetInfo { if (net == nullptr) return nullptr; + if (net->driver.cell == nullptr) + return nullptr; if (cell_pred(ctx, net->driver.cell) && net->driver.port == port) { return net->driver.cell; } else { -- cgit v1.2.3 From 889e1fc19fb7afbc747ae8a4b4ce68eae7f43391 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 11:28:59 +0200 Subject: ecp5: Adding cell definitions Signed-off-by: David Shah --- ecp5/cells.cc | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ecp5/cells.h | 59 ++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 ecp5/cells.cc create mode 100644 ecp5/cells.h diff --git a/ecp5/cells.cc b/ecp5/cells.cc new file mode 100644 index 00000000..ddd1130d --- /dev/null +++ b/ecp5/cells.cc @@ -0,0 +1,124 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "cells.h" +#include "log.h" +#include "util.h" +#include "design_utils.h" + +NEXTPNR_NAMESPACE_BEGIN + +void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir) +{ + IdString id = ctx->id(name); + cell->ports[id] = PortInfo{id, nullptr, dir}; +} + + +std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name = "") { + static int auto_idx = 0; + std::unique_ptr new_cell = std::unique_ptr(new CellInfo()); + if (name.empty()) { + new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)); + } else { + new_cell->name = ctx->id(name); + } + new_cell->type = type; + if (type == ctx->id("TRELLIS_LC")) { + new_cell->params[ctx->id("MODE")] = "LOGIC"; + new_cell->params[ctx->id("GSR")] = "DISABLED"; + new_cell->params[ctx->id("SRMODE")] = "LSR_OVER_CE"; + new_cell->params[ctx->id("CEMUX")] = "1"; + new_cell->params[ctx->id("CLKMUX")] = "CLK"; + new_cell->params[ctx->id("LSRMUX")] = "LSR"; + new_cell->params[ctx->id("LUT0_INITVAL")] = "0"; + new_cell->params[ctx->id("LUT1_INITVAL")] = "0"; + new_cell->params[ctx->id("REG0_SD")] = "0"; + new_cell->params[ctx->id("REG1_SD")] = "0"; + new_cell->params[ctx->id("REG0_REGSET")] = "RESET"; + new_cell->params[ctx->id("REG1_REGSET")] = "RESET"; + new_cell->params[ctx->id("CCU2_INJECT1_0")] = "NO"; + new_cell->params[ctx->id("CCU2_INJECT1_1")] = "NO"; + new_cell->params[ctx->id("WREMUX")] = "WRE"; + + add_port(ctx, new_cell.get(), "A0", PORT_IN); + add_port(ctx, new_cell.get(), "B0", PORT_IN); + add_port(ctx, new_cell.get(), "C0", PORT_IN); + add_port(ctx, new_cell.get(), "D0", PORT_IN); + + add_port(ctx, new_cell.get(), "A1", PORT_IN); + add_port(ctx, new_cell.get(), "B1", PORT_IN); + add_port(ctx, new_cell.get(), "C1", PORT_IN); + add_port(ctx, new_cell.get(), "D1", PORT_IN); + + add_port(ctx, new_cell.get(), "M0", PORT_IN); + add_port(ctx, new_cell.get(), "M1", PORT_IN); + + add_port(ctx, new_cell.get(), "FCI", PORT_IN); + add_port(ctx, new_cell.get(), "FXA", PORT_IN); + add_port(ctx, new_cell.get(), "FXB", PORT_IN); + + add_port(ctx, new_cell.get(), "CLK", PORT_IN); + add_port(ctx, new_cell.get(), "LSR", PORT_IN); + add_port(ctx, new_cell.get(), "CE", PORT_IN); + + add_port(ctx, new_cell.get(), "DI0", PORT_IN); + add_port(ctx, new_cell.get(), "DI1", PORT_IN); + + add_port(ctx, new_cell.get(), "WD0", PORT_IN); + add_port(ctx, new_cell.get(), "WD1", PORT_IN); + add_port(ctx, new_cell.get(), "WAD0", PORT_IN); + add_port(ctx, new_cell.get(), "WAD1", PORT_IN); + add_port(ctx, new_cell.get(), "WAD2", PORT_IN); + add_port(ctx, new_cell.get(), "WAD3", PORT_IN); + add_port(ctx, new_cell.get(), "WRE", PORT_IN); + add_port(ctx, new_cell.get(), "WCK", PORT_IN); + + add_port(ctx, new_cell.get(), "F0", PORT_OUT); + add_port(ctx, new_cell.get(), "Q0", PORT_OUT); + add_port(ctx, new_cell.get(), "F1", PORT_OUT); + add_port(ctx, new_cell.get(), "Q1", PORT_OUT); + + add_port(ctx, new_cell.get(), "FCO", PORT_OUT); + add_port(ctx, new_cell.get(), "OFX0", PORT_OUT); + add_port(ctx, new_cell.get(), "OFX1", PORT_OUT); + + add_port(ctx, new_cell.get(), "WDO0", PORT_OUT); + add_port(ctx, new_cell.get(), "WDO1", PORT_OUT); + add_port(ctx, new_cell.get(), "WDO2", PORT_OUT); + add_port(ctx, new_cell.get(), "WDO3", PORT_OUT); + add_port(ctx, new_cell.get(), "WADO0", PORT_OUT); + add_port(ctx, new_cell.get(), "WADO1", PORT_OUT); + add_port(ctx, new_cell.get(), "WADO2", PORT_OUT); + add_port(ctx, new_cell.get(), "WADO3", PORT_OUT); + } else if (type == ctx->id("TRELLIS_IO")) { + new_cell->params[ctx->id("DIR")] = "INPUT"; + new_cell->attrs[ctx->id("IO_TYPE")] = "LVCMOS33"; + + add_port(ctx, new_cell.get(), "B", PORT_INOUT); + add_port(ctx, new_cell.get(), "I", PORT_IN); + add_port(ctx, new_cell.get(), "T", PORT_IN); + add_port(ctx, new_cell.get(), "O", PORT_OUT); + } else { + log_error("unable to create ECP5 cell of type %s", type.c_str(ctx)); + } + return new_cell; +} + +NEXTPNR_NAMESPACE_END diff --git a/ecp5/cells.h b/ecp5/cells.h new file mode 100644 index 00000000..e2bda4e9 --- /dev/null +++ b/ecp5/cells.h @@ -0,0 +1,59 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef ECP5_CELLS_H +#define ECP5_CELLS_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + + +// Create a standard ECP5 cell and return it +// Name will be automatically assigned if not specified +std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name = ""); + +// Return true if a cell is a LUT +inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) +{ return cell->type == ctx->id("LUT4"); } + +// Return true if a cell is a flipflop +inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) +{ + return cell->type == ctx->id("TRELLIS_FF"); +} + +inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) +{ return cell->type == ctx->id("CCU2C"); } + +inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) +{ return cell->type == ctx->id("TRELLIS_LC"); } + +inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) +{ return cell->type == ctx->id("TRELLIS_DPR16X4"); } + +inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) +{ return cell->type == ctx->id("PFUMX"); } + +inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) +{ return cell->type == ctx->id("L6MUX21"); } + +NEXTPNR_NAMESPACE_END + +#endif -- cgit v1.2.3 From 044db02012f055fb92c6375a4dcb62cd666d5d59 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 12:21:24 +0200 Subject: ecp5: Major improvements to Trellis importer Signed-off-by: David Shah --- ecp5/trellis_import.py | 370 ++++++++++--------------------------------------- 1 file changed, 71 insertions(+), 299 deletions(-) diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 5eca057a..2bc32169 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -32,213 +32,6 @@ def get_tiletype_index(name): portpins = dict() -loc_wire_indices = dict() -loc_wires = dict() - -loc_bels = dict() -wire_bel_pins_uphill = dict() -wire_bel_pins_downhill = dict() - - -# Import all wire names at all locations -def import_location_wires(rg, x, y): - loc_wire_indices[x, y] = dict() - loc_wires[x, y] = list() - wire_bel_pins_uphill[x, y] = list() - wire_bel_pins_downhill[x, y] = list() - rtile = rg.tiles[pytrellis.Location(x, y)] - for wire in rtile.wires: - name = rg.to_str(wire.key()) - idx = len(loc_wires[x, y]) - loc_wires[x, y].append(name) - loc_wire_indices[x, y][name] = idx - wire_bel_pins_uphill[x, y].append([]) - wire_bel_pins_downhill[x, y].append([]) - - -# Take a RoutingId from Trellis and make into a (relx, rely, name) tuple -def resolve_wirename(rg, rid, cur_x, cur_y): - if is_global(rid.loc): - return (cur_x, cur_y, rg.to_str(rid.id)) - else: - x = rid.loc.x - y = rid.loc.y - widx = loc_wire_indices[x, y][rg.to_str(rid.id)] - return (x - cur_x, y - cur_y, widx) - - -loc_arc_indices = dict() # Map RoutingId index to nextpnr index -loc_arcs = dict() - - -# Import all arc indices at a location -def index_location_arcs(rg, x, y): - loc_arc_indices[x, y] = dict() - loc_arcs[x, y] = list() - rtile = rg.tiles[pytrellis.Location(x, y)] - for arc in rtile.arcs: - idx = len(loc_arcs[x, y]) - trid = arc.key() - loc_arcs[x, y].append(trid) - loc_arc_indices[x, y][trid] = idx - - -def add_bel_input(bel_x, bel_y, bel_idx, bel_pin, wire_x, wire_y, wire_name): - bel_pin = portpins[bel_pin] - loc_bels[bel_x, bel_y][bel_idx][2].append((bel_pin, (wire_x, wire_y, loc_wire_indices[wire_x, wire_y][wire_name]))) - wire_bel_pins_downhill[wire_x, wire_y][loc_wire_indices[wire_x, wire_y][wire_name]].append(( - (bel_x, bel_y, bel_idx), bel_pin)) - - -def add_bel_output(bel_x, bel_y, bel_idx, bel_pin, wire_x, wire_y, wire_name): - bel_pin = portpins[bel_pin] - loc_bels[bel_x, bel_y][bel_idx][2].append((bel_pin, (wire_x, wire_y, loc_wire_indices[wire_x, wire_y][wire_name]))) - wire_bel_pins_uphill[wire_x, wire_y][loc_wire_indices[wire_x, wire_y][wire_name]].append(( - (bel_x, bel_y, bel_idx), bel_pin)) - - -def add_slice(x, y, z): - idx = len(loc_bels[x, y]) - l = ("A", "B", "C", "D")[z] - name = "SLICE" + l - loc_bels[x, y].append((name, "SLICE", [])) - lc0 = z * 2 - lc1 = z * 2 + 1 - add_bel_input(x, y, idx, "A0", x, y, "A{}_SLICE".format(lc0)) - add_bel_input(x, y, idx, "B0", x, y, "B{}_SLICE".format(lc0)) - add_bel_input(x, y, idx, "C0", x, y, "C{}_SLICE".format(lc0)) - add_bel_input(x, y, idx, "D0", x, y, "D{}_SLICE".format(lc0)) - add_bel_input(x, y, idx, "M0", x, y, "M{}_SLICE".format(lc0)) - - add_bel_input(x, y, idx, "A1", x, y, "A{}_SLICE".format(lc1)) - add_bel_input(x, y, idx, "B1", x, y, "B{}_SLICE".format(lc1)) - add_bel_input(x, y, idx, "C1", x, y, "C{}_SLICE".format(lc1)) - add_bel_input(x, y, idx, "D1", x, y, "D{}_SLICE".format(lc1)) - add_bel_input(x, y, idx, "M1", x, y, "M{}_SLICE".format(lc1)) - - add_bel_input(x, y, idx, "FCI", x, y, "FCI{}_SLICE".format(l if z > 0 else "")) - add_bel_input(x, y, idx, "FXA", x, y, "FXA{}_SLICE".format(l)) - add_bel_input(x, y, idx, "FXB", x, y, "FXB{}_SLICE".format(l)) - - add_bel_input(x, y, idx, "CLK", x, y, "CLK{}_SLICE".format(z)) - add_bel_input(x, y, idx, "LSR", x, y, "LSR{}_SLICE".format(z)) - add_bel_input(x, y, idx, "CE", x, y, "CE{}_SLICE".format(z)) - - add_bel_input(x, y, idx, "DI0", x, y, "DI{}_SLICE".format(lc0)) - add_bel_input(x, y, idx, "DI1", x, y, "DI{}_SLICE".format(lc1)) - - if z == 0 or z == 1: - add_bel_input(x, y, idx, "WD0", x, y, "WD0{}_SLICE".format(l)) - add_bel_input(x, y, idx, "WD1", x, y, "WD1{}_SLICE".format(l)) - - add_bel_input(x, y, idx, "WAD0", x, y, "WAD0{}_SLICE".format(l)) - add_bel_input(x, y, idx, "WAD1", x, y, "WAD1{}_SLICE".format(l)) - add_bel_input(x, y, idx, "WAD2", x, y, "WAD2{}_SLICE".format(l)) - add_bel_input(x, y, idx, "WAD3", x, y, "WAD3{}_SLICE".format(l)) - - add_bel_input(x, y, idx, "WRE", x, y, "WRE{}_SLICE".format(z)) - add_bel_input(x, y, idx, "WCK", x, y, "WCK{}_SLICE".format(z)) - - add_bel_output(x, y, idx, "F0", x, y, "F{}_SLICE".format(lc0)) - add_bel_output(x, y, idx, "Q0", x, y, "Q{}_SLICE".format(lc0)) - - add_bel_output(x, y, idx, "F1", x, y, "F{}_SLICE".format(lc1)) - add_bel_output(x, y, idx, "Q1", x, y, "Q{}_SLICE".format(lc1)) - - add_bel_output(x, y, idx, "OFX0", x, y, "F5{}_SLICE".format(l)) - add_bel_output(x, y, idx, "OFX1", x, y, "FX{}_SLICE".format(l)) - - add_bel_output(x, y, idx, "FCO", x, y, "FCO{}_SLICE".format(l if z < 3 else "")) - - if z == 2: - add_bel_output(x, y, idx, "WDO0", x, y, "WDO0C_SLICE") - add_bel_output(x, y, idx, "WDO1", x, y, "WDO1C_SLICE") - add_bel_output(x, y, idx, "WDO2", x, y, "WDO2C_SLICE") - add_bel_output(x, y, idx, "WDO3", x, y, "WDO3C_SLICE") - - add_bel_output(x, y, idx, "WADO0", x, y, "WADO0C_SLICE") - add_bel_output(x, y, idx, "WADO1", x, y, "WADO1C_SLICE") - add_bel_output(x, y, idx, "WADO2", x, y, "WADO2C_SLICE") - add_bel_output(x, y, idx, "WADO3", x, y, "WADO3C_SLICE") - - -def add_pio(x, y, z): - idx = len(loc_bels[x, y]) - l = ("A", "B", "C", "D")[z] - name = "PIO" + l - loc_bels[x, y].append((name, "PIO", [])) - add_bel_input(x, y, idx, "I", x, y, "PADDO{}_PIO".format(l)) - add_bel_input(x, y, idx, "T", x, y, "PADDT{}_PIO".format(l)) - add_bel_output(x, y, idx, "O", x, y, "JPADDI{}_PIO".format(l)) - - -def add_bels(chip, x, y): - loc_bels[x, y] = [] - tiles = chip.get_tiles_by_position(y, x) - num_slices = 0 - num_pios = 0 - for tile in tiles: - tt = tile.info.type - if tt == "PLC2": - num_slices = 4 - elif "PICL0" in tt or "PICR0" in tt: - num_pios = 4 - elif "PIOT0" in tt or ("PICB0" in tt and "SPICB" not in tt): - num_pios = 2 - for i in range(num_slices): - add_slice(x, y, i) - for i in range(num_pios): - add_pio(x, y, i) - - -# Import a location, deduplicating if appropriate -def import_location(rg, x, y): - rtile = rg.tiles[pytrellis.Location(x, y)] - arcs = [] # (src, dst, configurable, tiletype) - wires = [] # (name, uphill, downhill, belpin_uphill, belpins_downhill) - bels = [] # (name, [(pin, wire)]) - for name in loc_wires[x, y]: - w = rtile.wires[rg.ident(name)] - arcs_uphill = [] - arcs_downhill = [] - belpins_uphill = [] - belpins_downhill = [] - for uh in w.uphill: - arcidx = loc_arc_indices[uh.loc.x, uh.loc.y][uh.id] - arcs_uphill.append((uh.loc.x - x, uh.loc.y - y, arcidx)) - for dh in w.downhill: - arcidx = loc_arc_indices[dh.loc.x, dh.loc.y][dh.id] - arcs_downhill.append((dh.loc.x - x, dh.loc.y - y, arcidx)) - for bp in wire_bel_pins_uphill[x, y][loc_wire_indices[x, y][name]]: - bel, pin = bp - bel_x, bel_y, bel_idx = bel - belpins_uphill.append(((bel_x - x, bel_y - y, bel_idx), pin)) - for bp in wire_bel_pins_downhill[x, y][loc_wire_indices[x, y][name]]: - bel, pin = bp - bel_x, bel_y, bel_idx = bel - belpins_downhill.append(((bel_x - x, bel_y - y, bel_idx), pin)) - assert len(belpins_uphill) <= 1 - wires.append((name, tuple(arcs_downhill), tuple(arcs_uphill), tuple(belpins_uphill), tuple(belpins_downhill))) - - for bel in loc_bels[x, y]: - name, beltype, pins = bel - xformed_pins = tuple((p[0], (p[1][0] - x, p[1][1] - y, p[1][2])) for p in pins) - bels.append((name, beltype, xformed_pins)) - - for arcidx in loc_arcs[x, y]: - a = rtile.arcs[arcidx] - source_wire = resolve_wirename(rg, a.source, x, y) - dest_wire = resolve_wirename(rg, a.sink, x, y) - arcs.append((source_wire, dest_wire, a.configurable, get_tiletype_index(rg.to_str(a.tiletype)))) - - tile_data = (tuple(wires), tuple(arcs), tuple(bels)) - if tile_data in location_types: - type_at_location[x, y] = location_types[tile_data] - else: - idx = len(location_types) - location_types[tile_data] = idx - type_at_location[x, y] = idx - class BinaryBlobAssembler: def __init__(self, cname, endianness, nodebug=False): @@ -526,100 +319,96 @@ bel_types = { "PIO": 2 } -def write_database(dev_name, endianness): - def write_loc(x, y, sym_name): - bba.s16(x, "%s.x" % sym_name) - bba.s16(y, "%s.y" % sym_name) + +def write_database(dev_name, ddrg, endianness): + def write_loc(loc, sym_name): + bba.s16(loc.x, "%s.x" % sym_name) + bba.s16(loc.y, "%s.y" % sym_name) bba = BinaryBlobAssembler("chipdb_blob_%s" % dev_name, endianness) bba.r("chip_info", "chip_info") - for loctype, idx in sorted(location_types.items(), key=lambda x: x[1]): - wires, arcs, bels = loctype - if len(arcs) > 0: + loctypes = list([_.key() for _ in ddrg.locationTypes]) + + for idx in range(len(loctypes)): + loctype = ddrg.locationTypes[loctypes[idx]] + if len(loctype.arcs) > 0: bba.l("loc%d_pips" % idx, "PipInfoPOD") - for arc in arcs: - src_wire, dst_wire, configurable, tile_type = arc - write_loc(src_wire[0], src_wire[1], "src") - write_loc(dst_wire[0], dst_wire[1], "dst") - bba.u32(src_wire[2], "src_idx") - bba.u32(dst_wire[2], "dst_idx") - bba.u32(1, "delay") # TODO:delay - bba.u16(tile_type, "tile_type") - bba.u8(1 if not configurable else 0, "pip_type") + for arc in loctype.arcs: + write_loc(arc.srcWire.rel, "src") + write_loc(arc.sinkWire.rel, "dst") + bba.u32(arc.srcWire.id, "src_idx") + bba.u32(arc.sinkWire.id, "dst_idx") + bba.u32(arc.delay, "delay") # TODO:delay + bba.u16(get_tiletype_index(ddrg.to_str(arc.tiletype)), "tile_type") + bba.u8(int(arc.cls), "pip_type") bba.u8(0, "padding") - if len(wires) > 0: - for wire_idx in range(len(wires)): - wire = wires[wire_idx] - name, downpips, uppips, downbels, upbels = wire - if len(downpips) > 0: + if len(loctype.wires) > 0: + for wire_idx in range(len(loctype.wires)): + wire = loctype.wires[wire_idx] + if len(wire.arcsDownhill) > 0: bba.l("loc%d_wire%d_downpips" % (idx, wire_idx), "PipLocatorPOD") - for dp in downpips: - write_loc(dp[0], dp[1], "rel_loc") - bba.u32(dp[2], "index") - if len(uppips) > 0: + for dp in wire.arcsDownhill: + write_loc(dp.rel, "rel_loc") + bba.u32(dp.id, "index") + if len(wire.arcsUphill) > 0: bba.l("loc%d_wire%d_uppips" % (idx, wire_idx), "PipLocatorPOD") - for up in uppips: - write_loc(up[0], up[1], "rel_loc") - bba.u32(up[2], "index") - if len(downbels) > 0: + for up in wire.arcsUphill: + write_loc(up.rel, "rel_loc") + bba.u32(up.id, "index") + if len(wire.belsDownhill) > 0: bba.l("loc%d_wire%d_downbels" % (idx, wire_idx), "BelPortPOD") - for db in downbels: - bel, pin = db - write_loc(bel[0], bel[1], "rel_bel_loc") - bba.u32(bel[2], "bel_index") - bba.u32(pin, "port") + for db in wire.belsDownhill: + write_loc(db.bel.rel, "rel_bel_loc") + bba.u32(db.bel.id, "bel_index") + bba.u32(portpins[ddrg.to_str(db.pin)], "port") bba.l("loc%d_wires" % idx, "WireInfoPOD") - for wire_idx in range(len(wires)): - wire = wires[wire_idx] - name, downpips, uppips, downbels, upbels = wire - bba.s(name, "name") - bba.u32(len(uppips), "num_uphill") - bba.u32(len(downpips), "num_downhill") - bba.r("loc%d_wire%d_uppips" % (idx, wire_idx) if len(uppips) > 0 else None, "pips_uphill") - bba.r("loc%d_wire%d_downpips" % (idx, wire_idx) if len(downpips) > 0 else None, "pips_downhill") - bba.u32(len(downbels), "num_bels_downhill") - if len(upbels) == 1: - bel, pin = upbels[0] - write_loc(bel[0], bel[1], "uphill_bel_loc") - bba.u32(bel[2], "uphill_bel_idx") - bba.u32(pin, "uphill_bel_pin") + for wire_idx in range(len(loctype.wires)): + wire = loctype.wires[wire_idx] + bba.s(ddrg.to_str(wire.name), "name") + bba.u32(len(wire.arcsUphill), "num_uphill") + bba.u32(len(wire.arcsDownhill), "num_downhill") + bba.r("loc%d_wire%d_uppips" % (idx, wire_idx) if len(wire.arcsUphill) > 0 else None, "pips_uphill") + bba.r("loc%d_wire%d_downpips" % (idx, wire_idx) if len(wire.arcsDownhill) > 0 else None, "pips_downhill") + bba.u32(len(wire.belsDownhill), "num_bels_downhill") + write_loc(wire.belUphill.bel.rel, "uphill_bel_loc") + if wire.belUphill.pin != -1: + bba.u32(wire.belUphill.bel.id, "uphill_bel_idx") + bba.u32(portpins[ddrg.to_str(wire.belUphill.pin)], "uphill_bel_pin") else: - write_loc(-1, -1, "bel_uphill.rel_bel_loc") bba.u32(0xFFFFFFFF, "bel_uphill.bel_index") bba.u32(0, "bel_uphill.port") - bba.r("loc%d_wire%d_downbels" % (idx, wire_idx) if len(downbels) > 0 else None, "bels_downhill") - if len(bels) > 0: - for bel_idx in range(len(bels)): - bel, beltype, pins = bels[bel_idx] + bba.r("loc%d_wire%d_downbels" % (idx, wire_idx) if len(wire.belsDownhill) > 0 else None, "bels_downhill") + if len(loctype.bels) > 0: + for bel_idx in range(len(loctype.bels)): + bel = loctype.bels[bel_idx] bba.l("loc%d_bel%d_wires" % (idx, bel_idx), "BelPortPOD") - for pin in pins: - port, wire = pin - write_loc(wire[0], wire[1], "rel_wire_loc") - bba.u32(wire[2], "wire_index") - bba.u32(port, "port") + for pin in bel.wires: + write_loc(pin.wire.rel, "rel_wire_loc") + bba.u32(pin.wire.id, "wire_index") + bba.u32(portpins[ddrg.to_str(pin.pin)], "port") bba.l("loc%d_bels" % idx, "BelInfoPOD") - for bel_idx in range(len(bels)): - bel, beltype, pins = bels[bel_idx] - bba.s(bel, "name") - bba.u32(bel_types[beltype], "type") - bba.u32(len(pins), "num_bel_wires") + for bel_idx in range(len(loctype.bels)): + bel = loctype.bels[bel_idx] + bba.s(ddrg.to_str(bel.name), "name") + bba.u32(bel_types[ddrg.to_str(bel.type)], "type") + bba.u32(len(bel.wires), "num_bel_wires") bba.r("loc%d_bel%d_wires" % (idx, bel_idx), "bel_wires") bba.l("locations", "LocationTypePOD") - for loctype, idx in sorted(location_types.items(), key=lambda x: x[1]): - wires, arcs, bels = loctype - bba.u32(len(bels), "num_bels") - bba.u32(len(wires), "num_wires") - bba.u32(len(arcs), "num_pips") - bba.r("loc%d_bels" % idx if len(bels) > 0 else None, "bel_data") - bba.r("loc%d_wires" % idx if len(wires) > 0 else None, "wire_data") - bba.r("loc%d_pips" % idx if len(arcs) > 0 else None, "pips_data") + for idx in range(len(loctypes)): + loctype = ddrg.locationTypes[loctypes[idx]] + bba.u32(len(loctype.bels), "num_bels") + bba.u32(len(loctype.wires), "num_wires") + bba.u32(len(loctype.arcs), "num_pips") + bba.r("loc%d_bels" % idx if len(loctype.bels) > 0 else None, "bel_data") + bba.r("loc%d_wires" % idx if len(loctype.wires) > 0 else None, "wire_data") + bba.r("loc%d_pips" % idx if len(loctype.arcs) > 0 else None, "pips_data") bba.l("location_types", "int32_t") for y in range(0, max_row+1): for x in range(0, max_col+1): - bba.u32(type_at_location[x, y], "loctype") + bba.u32(loctypes.index(ddrg.typeAtLocation[pytrellis.Location(x, y)]), "loctype") bba.l("tiletype_names", "RelPtr") for tt in tiletype_names: @@ -659,28 +448,11 @@ def main(): print("Initialising chip...") chip = pytrellis.Chip(dev_names[args.device]) print("Building routing graph...") - rg = chip.get_routing_graph() + ddrg = pytrellis.make_dedup_chipdb(chip) max_row = chip.get_max_row() max_col = chip.get_max_col() - print("Indexing wires...") - for y in range(0, max_row + 1): - for x in range(0, max_col + 1): - import_location_wires(rg, x, y) - print("Indexing arcs...") - for y in range(0, max_row + 1): - for x in range(0, max_col + 1): - index_location_arcs(rg, x, y) - print("Adding bels...") - for y in range(0, max_row + 1): - for x in range(0, max_col + 1): - add_bels(chip, x, y) - print("Importing tiles...") - for y in range(0, max_row + 1): - for x in range(0, max_col + 1): - print(" At R{}C{}".format(y, x)) - import_location(rg, x, y) - print("{} unique location types".format(len(location_types))) - bba = write_database(args.device, "le") + print("{} unique location types".format(len(ddrg.locationTypes))) + bba = write_database(args.device, ddrg, "le") if args.c_file: -- cgit v1.2.3 From e6725dcde4184d72b184a898ba63efe9188f518d Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 12:46:25 +0200 Subject: ecp5: Build all chip types Signed-off-by: David Shah --- ecp5/arch.cc | 2 +- ecp5/cells.cc | 2 +- ecp5/family.cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 51f4db84..cdffad69 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -94,7 +94,7 @@ static const ChipInfoPOD *get_chip_info(const RelPtr *ptr) { return void load_chipdb(); #endif -#define LFE5U_45F_ONLY +//#define LFE5U_45F_ONLY Arch::Arch(ArchArgs args) : args(args) { diff --git a/ecp5/cells.cc b/ecp5/cells.cc index ddd1130d..63916acf 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -31,7 +31,7 @@ void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir } -std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name = "") { +std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; std::unique_ptr new_cell = std::unique_ptr(new CellInfo()); if (name.empty()) { diff --git a/ecp5/family.cmake b/ecp5/family.cmake index f4d0bf87..97e5d66b 100644 --- a/ecp5/family.cmake +++ b/ecp5/family.cmake @@ -1,5 +1,5 @@ -set(devices 45k) +set(devices 25k 45k 85k) if (NOT DEFINED TRELLIS_ROOT) message(FATAL_ERROR "you must define TRELLIS_ROOT using -DTRELLIS_ROOT=/path/to/prjtrellis for ECP5 support") -- cgit v1.2.3 From 0e31a8e26646b86ffbeb409e95a13c0516a0e970 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 12:50:22 +0200 Subject: ecp5: Adding command line options for device type Signed-off-by: David Shah --- ecp5/cells.cc | 6 +++--- ecp5/cells.h | 24 +++++++----------------- ecp5/main.cc | 13 +++++++++++++ 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 63916acf..13025da9 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -18,9 +18,9 @@ */ #include "cells.h" +#include "design_utils.h" #include "log.h" #include "util.h" -#include "design_utils.h" NEXTPNR_NAMESPACE_BEGIN @@ -30,8 +30,8 @@ void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir cell->ports[id] = PortInfo{id, nullptr, dir}; } - -std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name) { +std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name) +{ static int auto_idx = 0; std::unique_ptr new_cell = std::unique_ptr(new CellInfo()); if (name.empty()) { diff --git a/ecp5/cells.h b/ecp5/cells.h index e2bda4e9..8c8e98a1 100644 --- a/ecp5/cells.h +++ b/ecp5/cells.h @@ -24,35 +24,25 @@ NEXTPNR_NAMESPACE_BEGIN - // Create a standard ECP5 cell and return it // Name will be automatically assigned if not specified std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name = ""); // Return true if a cell is a LUT -inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) -{ return cell->type == ctx->id("LUT4"); } +inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("LUT4"); } // Return true if a cell is a flipflop -inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) -{ - return cell->type == ctx->id("TRELLIS_FF"); -} +inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_FF"); } -inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) -{ return cell->type == ctx->id("CCU2C"); } +inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("CCU2C"); } -inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) -{ return cell->type == ctx->id("TRELLIS_LC"); } +inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_LC"); } -inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) -{ return cell->type == ctx->id("TRELLIS_DPR16X4"); } +inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_DPR16X4"); } -inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) -{ return cell->type == ctx->id("PFUMX"); } +inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("PFUMX"); } -inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) -{ return cell->type == ctx->id("L6MUX21"); } +inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("L6MUX21"); } NEXTPNR_NAMESPACE_END diff --git a/ecp5/main.cc b/ecp5/main.cc index 4cb2f10d..7521b88c 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -63,6 +63,11 @@ int main(int argc, char *argv[]) #ifndef NO_GUI options.add_options()("gui", "start gui"); #endif + + options.add_options()("25k", "set device type to LFE5U-25F"); + options.add_options()("45k", "set device type to LFE5U-45F"); + options.add_options()("85k", "set device type to LFE5U-85F"); + options.add_options()("json", po::value(), "JSON design file to ingest"); options.add_options()("seed", po::value(), "seed value for random number generator"); @@ -111,6 +116,14 @@ int main(int argc, char *argv[]) ArchArgs args; args.type = ArchArgs::LFE5U_45F; + + if (vm.count("25k")) + args.type = ArchArgs::LFE5U_25F; + if (vm.count("45k")) + args.type = ArchArgs::LFE5U_45F; + if (vm.count("85k")) + args.type = ArchArgs::LFE5U_85F; + args.package = "CABGA381"; args.speed = 6; std::unique_ptr ctx = std::unique_ptr(new Context(args)); -- cgit v1.2.3 From ac4cdd6604b95e293ac2a48f4b46fa0cab7cc94a Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 13:19:27 +0200 Subject: ecp5: Refactor skeleton of packer Signed-off-by: David Shah --- ecp5/cells.h | 2 ++ ecp5/pack.cc | 94 ++++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 55 insertions(+), 41 deletions(-) diff --git a/ecp5/cells.h b/ecp5/cells.h index 8c8e98a1..2d11da20 100644 --- a/ecp5/cells.h +++ b/ecp5/cells.h @@ -38,6 +38,8 @@ inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->ty inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_LC"); } +inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_IO"); } + inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_DPR16X4"); } inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("PFUMX"); } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index e3ddc07d..ba1a43ab 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -20,6 +20,7 @@ #include #include #include +#include "cells.h" #include "design_utils.h" #include "log.h" #include "util.h" @@ -32,55 +33,66 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) cell->type == ctx->id("$nextpnr_iobuf"); } -static bool is_trellis_io(const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_IO"); } - -// Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated -void pack_io(Context *ctx) +class Ecp5Packer { - std::unordered_set packed_cells; - std::vector> new_cells; - log_info("Packing IOs..\n"); + public: + Ecp5Packer(Context *ctx) : ctx(ctx){}; - for (auto cell : sorted(ctx->cells)) { - CellInfo *ci = cell.second; - if (is_nextpnr_iob(ctx, ci)) { - CellInfo *trio = nullptr; - if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { - trio = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_trellis_io, ctx->id("B"), true, ci); + private: + // Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated + void pack_io(Context *ctx) + { + std::unordered_set packed_cells; + std::vector> new_cells; + log_info("Packing IOs..\n"); - } else if (ci->type == ctx->id("$nextpnr_obuf")) { - trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci); - } - if (trio != nullptr) { - // Trivial case, TRELLIS_IO used. Just destroy the net and the - // iobuf - log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx), - ci->type.c_str(ctx), ci->name.c_str(ctx)); - NetInfo *net = trio->ports.at(ctx->id("B")).net; - if (net != nullptr) { - ctx->nets.erase(net->name); - trio->ports.at(ctx->id("B")).net = nullptr; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (is_nextpnr_iob(ctx, ci)) { + CellInfo *trio = nullptr; + if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + trio = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_trellis_io, ctx->id("B"), true, ci); + + } else if (ci->type == ctx->id("$nextpnr_obuf")) { + trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci); } - if (ci->type == ctx->id("$nextpnr_iobuf")) { - NetInfo *net2 = ci->ports.at(ctx->id("I")).net; - if (net2 != nullptr) { - ctx->nets.erase(net2->name); + if (trio != nullptr) { + // Trivial case, TRELLIS_IO used. Just destroy the net and the + // iobuf + log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx), + ci->type.c_str(ctx), ci->name.c_str(ctx)); + NetInfo *net = trio->ports.at(ctx->id("B")).net; + if (net != nullptr) { + ctx->nets.erase(net->name); + trio->ports.at(ctx->id("B")).net = nullptr; } + if (ci->type == ctx->id("$nextpnr_iobuf")) { + NetInfo *net2 = ci->ports.at(ctx->id("I")).net; + if (net2 != nullptr) { + ctx->nets.erase(net2->name); + } + } + } else { + log_error("TRELLIS_IO required on all top level IOs...\n"); } - } else { - log_error("TRELLIS_IO required on all top level IOs...\n"); + packed_cells.insert(ci->name); + std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(trio->attrs, trio->attrs.begin())); } - packed_cells.insert(ci->name); - std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(trio->attrs, trio->attrs.begin())); + } + for (auto pcell : packed_cells) { + ctx->cells.erase(pcell); + } + for (auto &ncell : new_cells) { + ctx->cells[ncell->name] = std::move(ncell); } } - for (auto pcell : packed_cells) { - ctx->cells.erase(pcell); - } - for (auto &ncell : new_cells) { - ctx->cells[ncell->name] = std::move(ncell); - } -} + + public: + void pack() { pack_io(ctx); } + + private: + Context *ctx; +}; // Main pack function bool Arch::pack() @@ -88,7 +100,7 @@ bool Arch::pack() Context *ctx = getCtx(); try { log_break(); - pack_io(ctx); + Ecp5Packer(ctx).pack(); log_info("Checksum: 0x%08x\n", ctx->checksum()); return true; } catch (log_execution_error_exception) { -- cgit v1.2.3 From eb773f246d3364ad9faab54680340aed1a664cee Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 13:41:33 +0200 Subject: ecp5: Working on packer Signed-off-by: David Shah --- ecp5/pack.cc | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index ba1a43ab..c44c0c4c 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -39,11 +39,22 @@ class Ecp5Packer Ecp5Packer(Context *ctx) : ctx(ctx){}; private: + // Process the contents of packed_cells and new_cells + void flush_cells() + { + for (auto pcell : packed_cells) { + ctx->cells.erase(pcell); + } + for (auto &ncell : new_cells) { + ctx->cells[ncell->name] = std::move(ncell); + } + packed_cells.clear(); + new_cells.clear(); + } + // Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated - void pack_io(Context *ctx) + void pack_io() { - std::unordered_set packed_cells; - std::vector> new_cells; log_info("Packing IOs..\n"); for (auto cell : sorted(ctx->cells)) { @@ -79,19 +90,73 @@ class Ecp5Packer std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(trio->attrs, trio->attrs.begin())); } } - for (auto pcell : packed_cells) { - ctx->cells.erase(pcell); - } - for (auto &ncell : new_cells) { - ctx->cells[ncell->name] = std::move(ncell); + flush_cells(); + } + + // Pass to pack LUT5s into a newly created slice + void pack_lut5s() + { + log_info("Packing LUT5s...\n"); + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (is_pfumx(ctx, ci)) { + std::unique_ptr packed = + create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "_SLICE"); + NetInfo *f0 = ci->ports.at(ctx->id("BLUT")).net; + if (f0 == nullptr) + log_error("PFUMX '%s' has disconnected port 'BLUT'\n", ci->name.c_str(ctx)); + NetInfo *f1 = ci->ports.at(ctx->id("ALUT")).net; + if (f1 == nullptr) + log_error("PFUMX '%s' has disconnected port 'ALUT'\n", ci->name.c_str(ctx)); + CellInfo *lc0 = net_driven_by(ctx, f0, is_lut, ctx->id("Z")); + CellInfo *lc1 = net_driven_by(ctx, f1, is_lut, ctx->id("Z")); + if (lc0 == nullptr) + log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); + if (lc1 == nullptr) + log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); + replace_port(lc0, ctx->id("A"), packed.get(), ctx->id("A0")); + replace_port(lc0, ctx->id("B"), packed.get(), ctx->id("B0")); + replace_port(lc0, ctx->id("C"), packed.get(), ctx->id("C0")); + replace_port(lc0, ctx->id("D"), packed.get(), ctx->id("D0")); + replace_port(lc1, ctx->id("A"), packed.get(), ctx->id("A1")); + replace_port(lc1, ctx->id("B"), packed.get(), ctx->id("B1")); + replace_port(lc1, ctx->id("C"), packed.get(), ctx->id("C1")); + replace_port(lc1, ctx->id("D"), packed.get(), ctx->id("D1")); + replace_port(ci, ctx->id("C0"), packed.get(), ctx->id("M0")); + replace_port(ci, ctx->id("Z"), packed.get(), ctx->id("OFX0")); + ctx->nets.erase(f0->name); + ctx->nets.erase(f1->name); + new_cells.push_back(std::move(packed)); + packed_cells.insert(lc0->name); + packed_cells.insert(lc1->name); + packed_cells.insert(ci->name); + } } + flush_cells(); } public: - void pack() { pack_io(ctx); } + void pack() + { + pack_io(); + pack_lut5s(); + } private: Context *ctx; + + std::unordered_set packed_cells; + std::vector> new_cells; + + struct SliceUsage + { + bool lut0_used = false, lut1_used = false; + bool ccu2_used = false, dpram_used = false, ramw_used = false; + bool ff0_used = false, ff1_used = false; + bool mux5_used = false, muxx_used = false; + }; + + std::unordered_map sliceUsage; }; // Main pack function -- cgit v1.2.3 From 7c89aed70e433c4d471960b07390e17f91ae3f84 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 14:19:21 +0200 Subject: ecp5: Infrastructure for FF packing Signed-off-by: David Shah --- ecp5/cells.cc | 44 ++++++++++++++++++++++++++++++++++++++++++++ ecp5/cells.h | 2 ++ ecp5/pack.cc | 25 +++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 13025da9..4beaabd2 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -18,6 +18,7 @@ */ #include "cells.h" +#include #include "design_utils.h" #include "log.h" #include "util.h" @@ -121,4 +122,47 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str return new_cell; } +static void set_param_safe(bool has_ff, CellInfo *lc, IdString name, const std::string &value) +{ + NPNR_ASSERT(!has_ff || lc->params.at(name) == value); + lc->params[name] = value; +} + +static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellInfo *lc, IdString lc_port) +{ + if (has_ff) { + assert(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net); + NetInfo *ffnet = ff->ports.at(ff_port).net; + if (ffnet != nullptr) + ffnet->users.erase( + std::remove_if(ffnet->users.begin(), ffnet->users.end(), + [ff, ff_port](PortRef port) { return port.cell == ff && port.port == ff_port; }), + ffnet->users.end()); + } else { + replace_port(ff, ff_port, lc, lc_port); + } +} + +void ff_to_lc(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut) +{ + bool has_ff = lc->ports.at(ctx->id("Q0")).net != nullptr || lc->ports.at(ctx->id("Q1")).net != nullptr; + std::string reg = "REG" + std::to_string(index); + set_param_safe(has_ff, lc, ctx->id("SRMODE"), str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE")); + set_param_safe(has_ff, lc, ctx->id("GSR"), str_or_default(ff->params, ctx->id("GSR"), "DISABLED")); + set_param_safe(has_ff, lc, ctx->id("CEMUX"), str_or_default(ff->params, ctx->id("CEMUX"), "1")); + set_param_safe(has_ff, lc, ctx->id("LSRMUX"), str_or_default(ff->params, ctx->id("LSRMUX"), "LSR")); + lc->params[ctx->id(reg + "_SD")] = driven_by_lut ? "1" : "0"; + lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, ctx->id("REGSET"), "RESET"); + replace_port_safe(has_ff, ff, ctx->id("CLK"), lc, ctx->id("CLK")); + replace_port_safe(has_ff, ff, ctx->id("LSR"), lc, ctx->id("LSR")); + replace_port_safe(has_ff, ff, ctx->id("CE"), lc, ctx->id("CE")); + + replace_port(ff, ctx->id("Q"), lc, ctx->id("Q" + std::to_string(index))); + if (driven_by_lut) { + replace_port(ff, ctx->id("DI"), lc, ctx->id("DI" + std::to_string(index))); + } else { + replace_port(ff, ctx->id("DI"), lc, ctx->id("M" + std::to_string(index))); + } +} + NEXTPNR_NAMESPACE_END diff --git a/ecp5/cells.h b/ecp5/cells.h index 2d11da20..d8d17061 100644 --- a/ecp5/cells.h +++ b/ecp5/cells.h @@ -46,6 +46,8 @@ inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->ty inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("L6MUX21"); } +void ff_to_lc(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut); + NEXTPNR_NAMESPACE_END #endif diff --git a/ecp5/pack.cc b/ecp5/pack.cc index c44c0c4c..13b24872 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -52,6 +52,23 @@ class Ecp5Packer new_cells.clear(); } + // Find FFs associated with LUTs, or LUT expansion muxes + void find_lutff_pairs() + { + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (is_lut(ctx, ci) || is_pfumx(ctx, ci) || is_l6mux(ctx, ci)) { + NetInfo *znet = ci->ports.at(ctx->id("Z")).net; + if (znet != nullptr) { + CellInfo *ff = net_only_drives(ctx, znet, is_ff, ctx->id("DI"), false); + if (ff != nullptr) { + lutffPairs[ci->name] = ff->name; + } + } + } + } + } + // Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated void pack_io() { @@ -126,6 +143,13 @@ class Ecp5Packer replace_port(ci, ctx->id("Z"), packed.get(), ctx->id("OFX0")); ctx->nets.erase(f0->name); ctx->nets.erase(f1->name); + + if (lutffPairs.find(ci->name) != lutffPairs.end()) { + CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get(); + ff_to_lc(ctx, ff, packed.get(), 0, true); + packed_cells.insert(ff->name); + } + new_cells.push_back(std::move(packed)); packed_cells.insert(lc0->name); packed_cells.insert(lc1->name); @@ -157,6 +181,7 @@ class Ecp5Packer }; std::unordered_map sliceUsage; + std::unordered_map lutffPairs; }; // Main pack function -- cgit v1.2.3 From c2fe09ec83409e342cfb8d2d7d861b602eb9254c Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 17 Jul 2018 14:29:16 +0200 Subject: QT 5.5 does not have this feature and it is version in Ubuntu 16.04 --- gui/yosystab.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gui/yosystab.cc b/gui/yosystab.cc index f2e99cbc..fba2362d 100644 --- a/gui/yosystab.cc +++ b/gui/yosystab.cc @@ -61,7 +61,11 @@ YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent) connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError())); connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput())); connect(process, &QProcess::started, this, [this] { lineEdit->setEnabled(true); }); +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) + connect(process, &QProcess::error, this, [this](QProcess::ProcessError error) { +#else connect(process, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) { +#endif if (error == QProcess::FailedToStart) { QMessageBox::critical( this, QString::fromUtf8("Yosys cannot be started!"), -- cgit v1.2.3 From c0567c0d30e2649d4f2b6d122caf43b28e0e80ce Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 15:25:10 +0200 Subject: ecp5: Working on packer LUT pairing functionality Signed-off-by: David Shah --- ecp5/pack.cc | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 13b24872..a7a2eeda 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -63,12 +63,127 @@ class Ecp5Packer CellInfo *ff = net_only_drives(ctx, znet, is_ff, ctx->id("DI"), false); if (ff != nullptr) { lutffPairs[ci->name] = ff->name; + fflutPairs[ff->name] = ci->name; } } } } } + // Return whether two FFs can be packed together in the same slice + bool can_pack_ffs(CellInfo *ff0, CellInfo *ff1) + { + if (str_or_default(ff0->params, ctx->id("GSR"), "DISABLED") != + str_or_default(ff1->params, ctx->id("GSR"), "DISABLED")) + return false; + if (str_or_default(ff0->params, ctx->id("SRMODE"), "LSR_OVER_CE") != + str_or_default(ff1->params, ctx->id("SRMODE"), "LSR_OVER_CE")) + return false; + if (str_or_default(ff0->params, ctx->id("CEMUX"), "1") != str_or_default(ff1->params, ctx->id("CEMUX"), "1")) + return false; + if (str_or_default(ff0->params, ctx->id("LSRMUX"), "LSR") != + str_or_default(ff1->params, ctx->id("LSRMUX"), "LSR")) + return false; + if (str_or_default(ff0->params, ctx->id("CLKMUX"), "CLK") != + str_or_default(ff1->params, ctx->id("CLKMUX"), "CLK")) + return false; + if (ff0->ports.at(ctx->id("CLK")).net != ff1->ports.at(ctx->id("CLK")).net) + return false; + if (ff0->ports.at(ctx->id("CE")).net != ff1->ports.at(ctx->id("CE")).net) + return false; + if (ff0->ports.at(ctx->id("LSR")).net != ff1->ports.at(ctx->id("LSR")).net) + return false; + return true; + } + + // Return true if two LUTs can be paired considering FF compatibility + bool can_pack_lutff(IdString lut0, IdString lut1) + { + auto ff0 = lutffPairs.find(lut0), ff1 = lutffPairs.find(lut1); + if (ff0 != lutffPairs.end() && ff1 != lutffPairs.end()) { + return can_pack_ffs(ctx->cells.at(ff0->second).get(), ctx->cells.at(ff1->second).get()); + } else { + return true; + } + } + + // Find "closely connected" LUTs and pair them together + void pair_luts(std::unordered_map &lutPairs) + { + std::unordered_set procdLuts; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (is_lut(ctx, ci) && procdLuts.find(cell.first) == procdLuts.end()) { + NetInfo *znet = ci->ports.at(ctx->id("Z")).net; + if (znet != nullptr) { + for (auto user : znet->users) { + if (is_lut(ctx, user.cell) && procdLuts.find(user.cell->name) == procdLuts.end()) { + if (can_pack_lutff(ci->name, user.cell->name)) { + procdLuts.insert(ci->name); + procdLuts.insert(user.cell->name); + lutPairs[ci->name] = user.cell->name; + goto paired; + } + } + } + if (false) { + paired: + continue; + } + } + if (lutffPairs.find(ci->name) != lutffPairs.end()) { + NetInfo *qnet = ctx->cells.at(lutffPairs[ci->name])->ports.at(ctx->id("Q")).net; + if (qnet != nullptr) { + for (auto user : qnet->users) { + if (is_lut(ctx, user.cell) && procdLuts.find(user.cell->name) == procdLuts.end()) { + if (can_pack_lutff(ci->name, user.cell->name)) { + procdLuts.insert(ci->name); + procdLuts.insert(user.cell->name); + lutPairs[ci->name] = user.cell->name; + goto paired_ff; + } + } + } + if (false) { + paired_ff: + continue; + } + } + } + for (char inp : "ABCD") { + NetInfo *innet = ci->ports.at(ctx->id(std::string("") + inp)).net; + if (innet != nullptr && innet->driver.cell != nullptr) { + CellInfo *drv = innet->driver.cell; + if (is_lut(ctx, drv) && innet->driver.port == ctx->id("Z")) { + if (procdLuts.find(drv->name) == procdLuts.end()) { + if (can_pack_lutff(ci->name, drv->name)) { + procdLuts.insert(ci->name); + procdLuts.insert(drv->name); + lutPairs[ci->name] = drv->name; + goto paired_inlut; + } + } + } else if (is_ff(ctx, drv) && innet->driver.port == ctx->id("Q")) { + auto fflut = fflutPairs.find(drv->name); + if (fflut != fflutPairs.end() && procdLuts.find(fflut->second) == procdLuts.end()) { + if (can_pack_lutff(ci->name, fflut->second)) { + procdLuts.insert(ci->name); + procdLuts.insert(fflut->second); + lutPairs[ci->name] = fflut->second; + goto paired_inlut; + } + } + } + } + } + if (false) { + paired_inlut: + continue; + } + } + } + } + // Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated void pack_io() { @@ -143,11 +258,15 @@ class Ecp5Packer replace_port(ci, ctx->id("Z"), packed.get(), ctx->id("OFX0")); ctx->nets.erase(f0->name); ctx->nets.erase(f1->name); + sliceUsage[packed->name].lut0_used = true; + sliceUsage[packed->name].lut1_used = true; + sliceUsage[packed->name].mux5_used = true; if (lutffPairs.find(ci->name) != lutffPairs.end()) { CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get(); ff_to_lc(ctx, ff, packed.get(), 0, true); packed_cells.insert(ff->name); + sliceUsage[packed->name].ff0_used = true; } new_cells.push_back(std::move(packed)); @@ -182,6 +301,7 @@ class Ecp5Packer std::unordered_map sliceUsage; std::unordered_map lutffPairs; + std::unordered_map fflutPairs; }; // Main pack function -- cgit v1.2.3 From b52269bc198eb8fcc647100d4891379b7fd7346a Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 15:44:36 +0200 Subject: ecp5: LUT packer Signed-off-by: David Shah --- ecp5/cells.cc | 12 +++++++- ecp5/cells.h | 3 +- ecp5/pack.cc | 99 +++++++++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 95 insertions(+), 19 deletions(-) diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 4beaabd2..4a20ce80 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -143,7 +143,7 @@ static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellI } } -void ff_to_lc(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut) +void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut) { bool has_ff = lc->ports.at(ctx->id("Q0")).net != nullptr || lc->ports.at(ctx->id("Q1")).net != nullptr; std::string reg = "REG" + std::to_string(index); @@ -165,4 +165,14 @@ void ff_to_lc(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_b } } +void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) +{ + lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = str_or_default(lc->params, ctx->id("INIT"), "0"); + replace_port(lut, ctx->id("A"), lc, ctx->id("A" + std::to_string(index))); + replace_port(lut, ctx->id("B"), lc, ctx->id("B" + std::to_string(index))); + replace_port(lut, ctx->id("C"), lc, ctx->id("C" + std::to_string(index))); + replace_port(lut, ctx->id("D"), lc, ctx->id("D" + std::to_string(index))); + replace_port(lut, ctx->id("Z"), lc, ctx->id("F" + std::to_string(index))); +} + NEXTPNR_NAMESPACE_END diff --git a/ecp5/cells.h b/ecp5/cells.h index d8d17061..b0c74ca9 100644 --- a/ecp5/cells.h +++ b/ecp5/cells.h @@ -46,7 +46,8 @@ inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->ty inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("L6MUX21"); } -void ff_to_lc(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut); +void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut); +void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index); NEXTPNR_NAMESPACE_END diff --git a/ecp5/pack.cc b/ecp5/pack.cc index a7a2eeda..056f457c 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -55,6 +55,7 @@ class Ecp5Packer // Find FFs associated with LUTs, or LUT expansion muxes void find_lutff_pairs() { + log_info("Finding LUTFF pairs...\n"); for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_lut(ctx, ci) || is_pfumx(ctx, ci) || is_l6mux(ctx, ci)) { @@ -108,8 +109,9 @@ class Ecp5Packer } // Find "closely connected" LUTs and pair them together - void pair_luts(std::unordered_map &lutPairs) + void pair_luts() { + log_info("Finding LUT-LUT pairs...\n"); std::unordered_set procdLuts; for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; @@ -240,22 +242,25 @@ class Ecp5Packer NetInfo *f1 = ci->ports.at(ctx->id("ALUT")).net; if (f1 == nullptr) log_error("PFUMX '%s' has disconnected port 'ALUT'\n", ci->name.c_str(ctx)); - CellInfo *lc0 = net_driven_by(ctx, f0, is_lut, ctx->id("Z")); - CellInfo *lc1 = net_driven_by(ctx, f1, is_lut, ctx->id("Z")); - if (lc0 == nullptr) + CellInfo *lut0 = net_driven_by(ctx, f0, is_lut, ctx->id("Z")); + CellInfo *lut1 = net_driven_by(ctx, f1, is_lut, ctx->id("Z")); + if (lut0 == nullptr) log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); - if (lc1 == nullptr) + if (lut1 == nullptr) log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); - replace_port(lc0, ctx->id("A"), packed.get(), ctx->id("A0")); - replace_port(lc0, ctx->id("B"), packed.get(), ctx->id("B0")); - replace_port(lc0, ctx->id("C"), packed.get(), ctx->id("C0")); - replace_port(lc0, ctx->id("D"), packed.get(), ctx->id("D0")); - replace_port(lc1, ctx->id("A"), packed.get(), ctx->id("A1")); - replace_port(lc1, ctx->id("B"), packed.get(), ctx->id("B1")); - replace_port(lc1, ctx->id("C"), packed.get(), ctx->id("C1")); - replace_port(lc1, ctx->id("D"), packed.get(), ctx->id("D1")); + replace_port(lut0, ctx->id("A"), packed.get(), ctx->id("A0")); + replace_port(lut0, ctx->id("B"), packed.get(), ctx->id("B0")); + replace_port(lut0, ctx->id("C"), packed.get(), ctx->id("C0")); + replace_port(lut0, ctx->id("D"), packed.get(), ctx->id("D0")); + replace_port(lut1, ctx->id("A"), packed.get(), ctx->id("A1")); + replace_port(lut1, ctx->id("B"), packed.get(), ctx->id("B1")); + replace_port(lut1, ctx->id("C"), packed.get(), ctx->id("C1")); + replace_port(lut1, ctx->id("D"), packed.get(), ctx->id("D1")); replace_port(ci, ctx->id("C0"), packed.get(), ctx->id("M0")); replace_port(ci, ctx->id("Z"), packed.get(), ctx->id("OFX0")); + packed->params[ctx->id("LUT0_INITVAL")] = str_or_default(lut0->params, ctx->id("INIT"), "0"); + packed->params[ctx->id("LUT1_INITVAL")] = str_or_default(lut1->params, ctx->id("INIT"), "0"); + ctx->nets.erase(f0->name); ctx->nets.erase(f1->name); sliceUsage[packed->name].lut0_used = true; @@ -264,14 +269,70 @@ class Ecp5Packer if (lutffPairs.find(ci->name) != lutffPairs.end()) { CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get(); - ff_to_lc(ctx, ff, packed.get(), 0, true); + ff_to_slice(ctx, ff, packed.get(), 0, true); packed_cells.insert(ff->name); sliceUsage[packed->name].ff0_used = true; } new_cells.push_back(std::move(packed)); - packed_cells.insert(lc0->name); - packed_cells.insert(lc1->name); + packed_cells.insert(lut0->name); + packed_cells.insert(lut1->name); + packed_cells.insert(ci->name); + } + } + flush_cells(); + } + + // Pack LUTs that have been paired together + void pack_lut_pairs() + { + log_info("Packing paired LUTs into a SLICE...\n"); + for (auto pair : lutPairs) { + CellInfo *lut0 = ctx->cells.at(pair.first).get(); + CellInfo *lut1 = ctx->cells.at(pair.second).get(); + std::unique_ptr slice = + create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), lut0->name.str(ctx) + "_SLICE"); + + lut_to_slice(ctx, lut0, slice.get(), 0); + lut_to_slice(ctx, lut1, slice.get(), 1); + + auto ff0 = lutffPairs.find(lut0->name), ff1 = lutffPairs.find(lut1->name); + + if (ff0 != lutffPairs.end()) { + ff_to_slice(ctx, ctx->cells.at(ff0->second).get(), slice.get(), 0, true); + packed_cells.insert(ff0->second); + } + if (ff1 != lutffPairs.end()) { + ff_to_slice(ctx, ctx->cells.at(ff1->second).get(), slice.get(), 1, true); + packed_cells.insert(ff1->second); + } + + new_cells.push_back(std::move(slice)); + packed_cells.insert(lut0->name); + packed_cells.insert(lut1->name); + } + flush_cells(); + } + + // Pack single LUTs that weren't paired into their own slice, + // with an optional FF also + void pack_remaining_luts() + { + log_info("Unpaired LUTs into a SLICE...\n"); + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (is_lut(ctx, ci)) { + std::unique_ptr slice = + create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "_SLICE"); + lut_to_slice(ctx, ci, slice.get(), 0); + auto ff = lutffPairs.find(ci->name); + + if (ff != lutffPairs.end()) { + ff_to_slice(ctx, ctx->cells.at(ff->second).get(), slice.get(), 0, true); + packed_cells.insert(ff->second); + } + + new_cells.push_back(std::move(slice)); packed_cells.insert(ci->name); } } @@ -282,7 +343,11 @@ class Ecp5Packer void pack() { pack_io(); + find_lutff_pairs(); pack_lut5s(); + pair_luts(); + pack_lut_pairs(); + pack_remaining_luts(); } private: @@ -302,8 +367,8 @@ class Ecp5Packer std::unordered_map sliceUsage; std::unordered_map lutffPairs; std::unordered_map fflutPairs; + std::unordered_map lutPairs; }; - // Main pack function bool Arch::pack() { -- cgit v1.2.3 From 5f64291ee14fb02caa0ff80a2e6ec6b52890357e Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 17 Jul 2018 15:48:09 +0200 Subject: remove non working code --- gui/yosystab.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/yosystab.cc b/gui/yosystab.cc index fba2362d..d83b969e 100644 --- a/gui/yosystab.cc +++ b/gui/yosystab.cc @@ -61,6 +61,7 @@ YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent) connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError())); connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput())); connect(process, &QProcess::started, this, [this] { lineEdit->setEnabled(true); }); +/* #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) connect(process, &QProcess::error, this, [this](QProcess::ProcessError error) { #else @@ -73,6 +74,7 @@ YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent) Q_EMIT deleteLater(); } }); +*/ process->setWorkingDirectory(folder); process->start("yosys"); } -- cgit v1.2.3 From 6c54d4f93c4f76db0280af73d9a84fcbde1d74f6 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 15:48:34 +0200 Subject: ecp5: FF packer Signed-off-by: David Shah --- ecp5/pack.cc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 056f457c..4f864b89 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -318,7 +318,7 @@ class Ecp5Packer // with an optional FF also void pack_remaining_luts() { - log_info("Unpaired LUTs into a SLICE...\n"); + log_info("Packing unpaired LUTs into a SLICE...\n"); for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_lut(ctx, ci)) { @@ -339,6 +339,23 @@ class Ecp5Packer flush_cells(); } + // Pack flipflops that weren't paired with a LUT + void pack_remaining_ffs() + { + log_info("Packing unpaired FFs into a SLICE...\n"); + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (is_ff(ctx, ci)) { + std::unique_ptr slice = + create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "_SLICE"); + ff_to_slice(ctx, ci, slice.get(), 0, false); + new_cells.push_back(std::move(slice)); + packed_cells.insert(ci->name); + } + } + flush_cells(); + } + public: void pack() { @@ -348,6 +365,7 @@ class Ecp5Packer pair_luts(); pack_lut_pairs(); pack_remaining_luts(); + pack_remaining_ffs(); } private: -- cgit v1.2.3 From 2b86800c0f7a66409d3d294b4e4d380594c2062e Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 16:18:06 +0200 Subject: ecp5: Adding a slow and conservative placement validity checker Signed-off-by: David Shah --- ecp5/arch.cc | 19 ++++++---- ecp5/arch.h | 8 ++++ ecp5/arch_place.cc | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 ecp5/arch_place.cc diff --git a/ecp5/arch.cc b/ecp5/arch.cc index cdffad69..0ffede3b 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -118,6 +118,14 @@ Arch::Arch(ArchArgs args) : args(args) log_error("Unsupported ECP5 chip type.\n"); } #endif + + id_trellis_slice = id("TRELLIS_SLICE"); + id_clk = id("CLK"); + id_lsr = id("LSR"); + id_clkmux = id("CLKMUX"); + id_lsrmux = id("LSRMUX"); + id_srmode = id("SRMODE"); + id_mode = id("MODE"); } // ----------------------------------------------------------------------- @@ -181,7 +189,10 @@ BelRange Arch::getBelsAtSameTile(BelId bel) const br.b.cursor_tile = bel.location.y * chip_info->width + bel.location.x; br.e.cursor_tile = bel.location.y * chip_info->width + bel.location.x; br.b.cursor_index = 0; - br.e.cursor_index = locInfo(bel)->num_bels; + br.e.cursor_index = locInfo(bel)->num_bels - 1; + br.b.chip = chip_info; + br.e.chip = chip_info; + ++br.e; return br; } @@ -315,12 +326,6 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; // ----------------------------------------------------------------------- -bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; } - -bool Arch::isBelLocationValid(BelId bel) const { return true; } - -// ----------------------------------------------------------------------- - bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const { return false; diff --git a/ecp5/arch.h b/ecp5/arch.h index 930c488e..4bb71b47 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -760,6 +760,14 @@ struct Arch : BaseCtx // Placement validity checks bool isValidBelForCell(CellInfo *cell, BelId bel) const; bool isBelLocationValid(BelId bel) const; + + // Helper function for above + bool slicesCompatible(const std::vector &cells) const; + + IdString id_trellis_slice; + IdString id_clk, id_lsr; + IdString id_clkmux, id_lsrmux; + IdString id_srmode, id_mode; }; NEXTPNR_NAMESPACE_END diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc new file mode 100644 index 00000000..22ebab67 --- /dev/null +++ b/ecp5/arch_place.cc @@ -0,0 +1,109 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "cells.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" +NEXTPNR_NAMESPACE_BEGIN + +inline NetInfo *port_or_nullptr(const CellInfo *cell, IdString name) +{ + auto found = cell->ports.find(name); + if (found == cell->ports.end()) + return nullptr; + return found->second.net; +} + +bool Arch::slicesCompatible(const std::vector &cells) const +{ + // TODO: allow different LSR/CLK and MUX/SRMODE settings once + // routing details are worked out + NetInfo *clk_sig = nullptr, *lsr_sig = nullptr; + std::string CLKMUX, LSRMUX, SRMODE; + bool first = true; + for (auto cell : cells) { + if (first) { + clk_sig = port_or_nullptr(cell, id_clk); + lsr_sig = port_or_nullptr(cell, id_lsr); + CLKMUX = str_or_default(cell->params, id_clkmux, "CLK"); + LSRMUX = str_or_default(cell->params, id_lsrmux, "LSR"); + SRMODE = str_or_default(cell->params, id_srmode, "CE_OVER_LSR"); + } else { + if (port_or_nullptr(cell, id_clk) != clk_sig) + return false; + if (port_or_nullptr(cell, id_lsr) != lsr_sig) + return false; + if (str_or_default(cell->params, id_clkmux, "CLK") != CLKMUX) + return false; + if (str_or_default(cell->params, id_lsrmux, "LSR") != LSRMUX) + return false; + if (str_or_default(cell->params, id_srmode, "CE_OVER_LSR") != SRMODE) + return false; + } + first = false; + } + return true; +} + +bool Arch::isBelLocationValid(BelId bel) const +{ + if (getBelType(bel) == TYPE_TRELLIS_SLICE) { + std::vector bel_cells; + for (auto bel_other : getBelsAtSameTile(bel)) { + IdString cell_other = getBoundBelCell(bel_other); + if (cell_other != IdString()) { + const CellInfo *ci_other = cells.at(cell_other).get(); + bel_cells.push_back(ci_other); + } + } + return slicesCompatible(bel_cells); + } else { + IdString cellId = getBoundBelCell(bel); + if (cellId == IdString()) + return true; + else + return isValidBelForCell(cells.at(cellId).get(), bel); + } +} + +bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const +{ + if (cell->type == id_trellis_slice) { + NPNR_ASSERT(getBelType(bel) == TYPE_TRELLIS_SLICE); + + std::vector bel_cells; + + for (auto bel_other : getBelsAtSameTile(bel)) { + IdString cell_other = getBoundBelCell(bel_other); + if (cell_other != IdString() && bel_other != bel) { + const CellInfo *ci_other = cells.at(cell_other).get(); + bel_cells.push_back(ci_other); + } + } + + bel_cells.push_back(cell); + return slicesCompatible(bel_cells); + } else { + // other checks + return true; + } +} + +NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From 2eb783d626a9a17baf70d2f7750be3c11623d5bc Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 17 Jul 2018 16:45:39 +0200 Subject: ecp5: Fixing packer bugs Signed-off-by: David Shah --- ecp5/bitstream.cc | 12 ++++++++++++ ecp5/cells.cc | 10 ++++++---- ecp5/pack.cc | 15 +++++++++------ ecp5/synth/blinky_nopack.ys | 2 ++ 4 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 ecp5/synth/blinky_nopack.ys diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index e70d6bb2..19ddb9f9 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -214,6 +214,18 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex cc.tiles[tname].add_enum(slice + ".REG1.REGSET", str_or_default(ci->params, ctx->id("REG1_REGSET"), "RESET")); cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, ctx->id("CEMUX"), "1")); + IdString lsrnet; + if (ci->ports.find(ctx->id("LSR")) != ci->ports.end() && ci->ports.at(ctx->id("LSR")).net != nullptr) + lsrnet = ci->ports.at(ctx->id("LSR")).net->name; + if (ctx->getBoundWireNet(ctx->getWireByName( + ctx->id(fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/LSR0")))) == lsrnet) { + cc.tiles[tname].add_enum("LSR0.SRMODE", str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); + cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); + } else if (ctx->getBoundWireNet(ctx->getWireByName(ctx->id( + fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/LSR1")))) == lsrnet) { + cc.tiles[tname].add_enum("LSR1.SRMODE", str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); + cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); + } // TODO: CLKMUX, CEMUX, carry } else if (ci->type == ctx->id("TRELLIS_IO")) { std::string pio = ctx->locInfo(bel)->bel_data[bel.index].name.get(); diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 4a20ce80..59504735 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -41,7 +41,7 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str new_cell->name = ctx->id(name); } new_cell->type = type; - if (type == ctx->id("TRELLIS_LC")) { + if (type == ctx->id("TRELLIS_SLICE")) { new_cell->params[ctx->id("MODE")] = "LOGIC"; new_cell->params[ctx->id("GSR")] = "DISABLED"; new_cell->params[ctx->id("SRMODE")] = "LSR_OVER_CE"; @@ -131,7 +131,7 @@ static void set_param_safe(bool has_ff, CellInfo *lc, IdString name, const std:: static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellInfo *lc, IdString lc_port) { if (has_ff) { - assert(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net); + NPNR_ASSERT(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net); NetInfo *ffnet = ff->ports.at(ff_port).net; if (ffnet != nullptr) ffnet->users.erase( @@ -154,8 +154,10 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive lc->params[ctx->id(reg + "_SD")] = driven_by_lut ? "1" : "0"; lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, ctx->id("REGSET"), "RESET"); replace_port_safe(has_ff, ff, ctx->id("CLK"), lc, ctx->id("CLK")); - replace_port_safe(has_ff, ff, ctx->id("LSR"), lc, ctx->id("LSR")); - replace_port_safe(has_ff, ff, ctx->id("CE"), lc, ctx->id("CE")); + if (ff->ports.find(ctx->id("LSR")) != ff->ports.end()) + replace_port_safe(has_ff, ff, ctx->id("LSR"), lc, ctx->id("LSR")); + if (ff->ports.find(ctx->id("CE")) != ff->ports.end()) + replace_port_safe(has_ff, ff, ctx->id("CE"), lc, ctx->id("CE")); replace_port(ff, ctx->id("Q"), lc, ctx->id("Q" + std::to_string(index))); if (driven_by_lut) { diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 4f864b89..1900eded 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -119,7 +119,8 @@ class Ecp5Packer NetInfo *znet = ci->ports.at(ctx->id("Z")).net; if (znet != nullptr) { for (auto user : znet->users) { - if (is_lut(ctx, user.cell) && procdLuts.find(user.cell->name) == procdLuts.end()) { + if (is_lut(ctx, user.cell) && user.cell != ci && + procdLuts.find(user.cell->name) == procdLuts.end()) { if (can_pack_lutff(ci->name, user.cell->name)) { procdLuts.insert(ci->name); procdLuts.insert(user.cell->name); @@ -137,7 +138,8 @@ class Ecp5Packer NetInfo *qnet = ctx->cells.at(lutffPairs[ci->name])->ports.at(ctx->id("Q")).net; if (qnet != nullptr) { for (auto user : qnet->users) { - if (is_lut(ctx, user.cell) && procdLuts.find(user.cell->name) == procdLuts.end()) { + if (is_lut(ctx, user.cell) && user.cell != ci && + procdLuts.find(user.cell->name) == procdLuts.end()) { if (can_pack_lutff(ci->name, user.cell->name)) { procdLuts.insert(ci->name); procdLuts.insert(user.cell->name); @@ -152,11 +154,11 @@ class Ecp5Packer } } } - for (char inp : "ABCD") { - NetInfo *innet = ci->ports.at(ctx->id(std::string("") + inp)).net; + for (const char *inp : {"A", "B", "C", "D"}) { + NetInfo *innet = ci->ports.at(ctx->id(inp)).net; if (innet != nullptr && innet->driver.cell != nullptr) { CellInfo *drv = innet->driver.cell; - if (is_lut(ctx, drv) && innet->driver.port == ctx->id("Z")) { + if (is_lut(ctx, drv) && drv != ci && innet->driver.port == ctx->id("Z")) { if (procdLuts.find(drv->name) == procdLuts.end()) { if (can_pack_lutff(ci->name, drv->name)) { procdLuts.insert(ci->name); @@ -167,7 +169,8 @@ class Ecp5Packer } } else if (is_ff(ctx, drv) && innet->driver.port == ctx->id("Q")) { auto fflut = fflutPairs.find(drv->name); - if (fflut != fflutPairs.end() && procdLuts.find(fflut->second) == procdLuts.end()) { + if (fflut != fflutPairs.end() && fflut->second != ci->name && + procdLuts.find(fflut->second) == procdLuts.end()) { if (can_pack_lutff(ci->name, fflut->second)) { procdLuts.insert(ci->name); procdLuts.insert(fflut->second); diff --git a/ecp5/synth/blinky_nopack.ys b/ecp5/synth/blinky_nopack.ys new file mode 100644 index 00000000..fb359380 --- /dev/null +++ b/ecp5/synth/blinky_nopack.ys @@ -0,0 +1,2 @@ +read_verilog blinky.v +synth_ecp5 -noccu2 -nomux -nodram -json blinky.json -- cgit v1.2.3