Merge branch 'master' into q3k/treemodel-fast

This commit is contained in:
Sergiusz Bazanski 2018-08-01 00:22:09 +01:00
commit 6241052e11
24 changed files with 542 additions and 327 deletions

View File

@ -182,12 +182,7 @@ foreach (family ${ARCH})
add_executable(nextpnr-${family} ${COMMON_FILES} ${${ufamily}_FILES}) add_executable(nextpnr-${family} ${COMMON_FILES} ${${ufamily}_FILES})
install(TARGETS nextpnr-${family} RUNTIME DESTINATION bin) install(TARGETS nextpnr-${family} RUNTIME DESTINATION bin)
target_compile_definitions(nextpnr-${family} PRIVATE MAIN_EXECUTABLE) target_compile_definitions(nextpnr-${family} PRIVATE MAIN_EXECUTABLE)
if (BUILD_PYTHON)
# Add the importable Python module target
PYTHON_ADD_MODULE(nextpnrpy_${family} ${COMMON_FILES} ${${ufamily}_FILES})
endif()
# Add any new per-architecture targets here # Add any new per-architecture targets here
if (BUILD_TESTS) if (BUILD_TESTS)
aux_source_directory(tests/${family}/ ${ufamily}_TEST_FILES) aux_source_directory(tests/${family}/ ${ufamily}_TEST_FILES)
@ -210,10 +205,6 @@ foreach (family ${ARCH})
set(family_targets ${family_targets} nextpnr-${family}-test) set(family_targets ${family_targets} nextpnr-${family}-test)
endif() endif()
if (BUILD_PYTHON)
set(family_targets ${family_targets} nextpnrpy_${family})
endif()
# Include the family-specific CMakeFile # Include the family-specific CMakeFile
include(${family}/family.cmake) include(${family}/family.cmake)
foreach (target ${family_targets}) foreach (target ${family_targets})

View File

@ -1,10 +1,21 @@
nextpnr -- a portable FPGA place and route tool nextpnr -- a portable FPGA place and route tool
=============================================== ===============================================
nextpnr is an FPGA place and route tool with emphasis on supporting a wide nextpnr is an FPGA place and route tool with emphasis on supporting
range of real-world FPGA devices. It currently supports Lattice iCE40 devices timing-driven place and route for a wide range of real-world FPGA devices.
and Lattice ECP5 devices, as well as a "generic" back-end for user-defined It currently supports Lattice iCE40 devices and Lattice ECP5 devices,
architectures. (ECP5 and "generic" support are still experimental.) as well as a "generic" back-end for user-defined architectures.
(ECP5 and "generic" support are still experimental.)
Currently nextpnr is beta software at best. But we aim at replacing
arachne-pnr as official place-and-route tool for the icestorm flow soon.
Here is a screenshot of nextpnr for iCE40. Build instructions and getting
started notes can be found below.
<img src="https://i.imgur.com/0spmlBa.png" width="640"/>
Prerequisites Prerequisites
------------- -------------
@ -144,9 +155,15 @@ Links and references
- [Project X-Ray (Xilinx 7-Series)](https://symbiflow.github.io/prjxray-db/) - [Project X-Ray (Xilinx 7-Series)](https://symbiflow.github.io/prjxray-db/)
- [Project Chibi (Intel MAX-V)](https://github.com/rqou/project-chibi) - [Project Chibi (Intel MAX-V)](https://github.com/rqou/project-chibi)
### Other FOSS place and route tools (FPGA and ASIC) ### Other FOSS FPGA place and route projects
- [Arachne PNR](https://github.com/cseed/arachne-pnr) - [Arachne PNR](https://github.com/cseed/arachne-pnr)
- [VPR/VTR](https://verilogtorouting.org/) - [VPR/VTR](https://verilogtorouting.org/)
- [graywolf/timberwolf](https://github.com/rubund/graywolf) - [SymbiFlow](https://github.com/SymbiFlow/symbiflow-arch-defs)
- [qrouter](http://opencircuitdesign.com/qrouter/) - [Gaffe](https://github.com/kc8apf/gaffe)
- [KinglerPAR](https://github.com/rqou/KinglerPAR)
> SymbiFlow is working with the Verilog to Routing tool to extend the current
research tool to support real architectures. VtR is strongly focused on
architecture research but having support for real architectures might enable
research nextpnr zu providing documentation and explanation.

View File

@ -41,13 +41,13 @@ static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, de
// Follow outputs of the user // Follow outputs of the user
for (auto port : user.cell->ports) { for (auto port : user.cell->ports) {
if (port.second.type == PORT_OUT) { if (port.second.type == PORT_OUT) {
delay_t comb_delay; DelayInfo comb_delay;
// Look up delay through this path // Look up delay through this path
bool is_path = ctx->getCellDelay(user.cell, user.port, port.first, comb_delay); bool is_path = ctx->getCellDelay(user.cell, user.port, port.first, comb_delay);
if (is_path) { if (is_path) {
NetInfo *net = port.second.net; NetInfo *net = port.second.net;
if (net) { if (net) {
delay_t path_budget = follow_net(ctx, net, path_length, slack - comb_delay); delay_t path_budget = follow_net(ctx, net, path_length, slack - comb_delay.maxDelay());
value = std::min(value, path_budget); value = std::min(value, path_budget);
} }
} }
@ -88,9 +88,9 @@ void assign_budget(Context *ctx)
IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first); IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
if (clock_domain != IdString()) { if (clock_domain != IdString()) {
delay_t slack = delay_t(1.0e12 / ctx->target_freq); // TODO: clock constraints delay_t slack = delay_t(1.0e12 / ctx->target_freq); // TODO: clock constraints
delay_t clkToQ; DelayInfo clkToQ;
if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ)) if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ))
slack -= clkToQ; slack -= clkToQ.maxDelay();
if (port.second.net) if (port.second.net)
follow_net(ctx, port.second.net, 0, slack); follow_net(ctx, port.second.net, 0, slack);
} }

View File

@ -21,6 +21,7 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include "gfx.h"
#include "log.h" #include "log.h"
#include "nextpnr.h" #include "nextpnr.h"
#include "placer1.h" #include "placer1.h"
@ -421,16 +422,64 @@ bool Arch::route() { return router1(getCtx()); }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decalId) const std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
{ {
std::vector<GraphicElement> ret; std::vector<GraphicElement> ret;
// FIXME
if (decal.type == DecalId::TYPE_FRAME) {
/* nothing */
}
if (decal.type == DecalId::TYPE_BEL) {
BelId bel;
bel.index = decal.z;
bel.location = decal.location;
int z = locInfo(bel)->bel_data[bel.index].z;
auto bel_type = getBelType(bel);
if (bel_type == TYPE_TRELLIS_SLICE) {
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = bel.location.x + logic_cell_x1;
el.x2 = bel.location.x + logic_cell_x2;
el.y1 = bel.location.y + logic_cell_y1 + (z)*logic_cell_pitch;
el.y2 = bel.location.y + logic_cell_y2 + (z)*logic_cell_pitch;
ret.push_back(el);
}
if (bel_type == TYPE_TRELLIS_IO) {
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = bel.location.x + logic_cell_x1;
el.x2 = bel.location.x + logic_cell_x2;
el.y1 = bel.location.y + logic_cell_y1 + (2 * z) * logic_cell_pitch;
el.y2 = bel.location.y + logic_cell_y2 + (2 * z + 1) * logic_cell_pitch;
ret.push_back(el);
}
}
return ret; return ret;
} }
DecalXY Arch::getFrameDecal() const { return {}; } DecalXY Arch::getFrameDecal() const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_FRAME;
decalxy.decal.active = true;
return decalxy;
}
DecalXY Arch::getBelDecal(BelId bel) const { return {}; } DecalXY Arch::getBelDecal(BelId bel) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_BEL;
decalxy.decal.location = bel.location;
decalxy.decal.z = bel.index;
decalxy.decal.active = bel_to_cell.count(bel) && (bel_to_cell.at(bel) != IdString());
return decalxy;
}
DecalXY Arch::getWireDecal(WireId wire) const { return {}; } DecalXY Arch::getWireDecal(WireId wire) const { return {}; }
@ -440,7 +489,7 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; };
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{ {
return false; return false;
} }

View File

@ -801,7 +801,7 @@ struct Arch : BaseCtx
// Get the delay through a cell from one port to another, returning false // Get the delay through a cell from one port to another, returning false
// if no path exists // if no path exists
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const; bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
// Get the associated clock to a port, or empty if the port is combinational // Get the associated clock to a port, or empty if the port is combinational
IdString getPortClock(const CellInfo *cell, IdString port) const; IdString getPortClock(const CellInfo *cell, IdString port) const;
// Return true if a port is a clock // Return true if a port is a clock

View File

@ -120,17 +120,21 @@ struct GroupId
struct DecalId struct DecalId
{ {
char type = 0; // Bel/Wire/Pip/Frame (b/w/p/f) enum
{
TYPE_FRAME,
TYPE_BEL
} type;
Location location; Location location;
uint32_t z = 0; uint32_t z = 0;
bool active = false;
bool operator==(const DecalId &other) const bool operator==(const DecalId &other) const
{ {
return type == other.type && location == other.location && z == other.z; return type == other.type && location == other.location && z == other.z && active == other.active;
} }
bool operator!=(const DecalId &other) const bool operator!=(const DecalId &other) const
{ {
return type != other.type || location != other.location || z != other.z; return type != other.type || location != other.location || z != other.z || active != other.active;
} }
}; };
@ -200,6 +204,7 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
boost::hash_combine(seed, hash<int>()(decal.type)); boost::hash_combine(seed, hash<int>()(decal.type));
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX Location>()(decal.location)); boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX Location>()(decal.location));
boost::hash_combine(seed, hash<int>()(decal.z)); boost::hash_combine(seed, hash<int>()(decal.z));
boost::hash_combine(seed, hash<bool>()(decal.active));
return seed; return seed;
} }
}; };

35
ecp5/gfx.h Normal file
View File

@ -0,0 +1,35 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* 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_GFX_H
#define ECP5_GFX_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
const float logic_cell_x1 = 0.76;
const float logic_cell_x2 = 0.95;
const float logic_cell_y1 = 0.05;
const float logic_cell_y2 = 0.15;
const float logic_cell_pitch = 0.125;
NEXTPNR_NAMESPACE_END
#endif

View File

@ -100,16 +100,18 @@ int main(int argc, char *argv[])
} }
if (vm.count("help") || argc == 1) { if (vm.count("help") || argc == 1) {
std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git " std::cout << boost::filesystem::basename(argv[0])
"sha1 " GIT_COMMIT_HASH_STR ")\n"; << " -- Next Generation Place and Route (git "
"sha1 " GIT_COMMIT_HASH_STR ")\n";
std::cout << "\n"; std::cout << "\n";
std::cout << options << "\n"; std::cout << options << "\n";
return argc != 1; return argc != 1;
} }
if (vm.count("version")) { if (vm.count("version")) {
std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git " std::cout << boost::filesystem::basename(argv[0])
"sha1 " GIT_COMMIT_HASH_STR ")\n"; << " -- Next Generation Place and Route (git "
"sha1 " GIT_COMMIT_HASH_STR ")\n";
return 1; return 1;
} }

View File

@ -425,7 +425,7 @@ DecalXY Arch::getGroupDecal(GroupId group) const { return groups.at(group).decal
// --------------------------------------------------------------- // ---------------------------------------------------------------
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{ {
return false; return false;
} }

View File

@ -210,7 +210,7 @@ struct Arch : BaseCtx
DecalXY getPipDecal(PipId pip) const; DecalXY getPipDecal(PipId pip) const;
DecalXY getGroupDecal(GroupId group) const; DecalXY getGroupDecal(GroupId group) const;
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const; bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
IdString getPortClock(const CellInfo *cell, IdString port) const; IdString getPortClock(const CellInfo *cell, IdString port) const;
bool isClockPort(const CellInfo *cell, IdString port) const; bool isClockPort(const CellInfo *cell, IdString port) const;

View File

@ -51,10 +51,11 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel
propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu); propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu);
propertyEditor->treeWidget()->setSelectionMode(QAbstractItemView::ExtendedSelection); propertyEditor->treeWidget()->setSelectionMode(QAbstractItemView::ExtendedSelection);
QLineEdit *lineEdit = new QLineEdit(); searchEdit = new QLineEdit();
lineEdit->setClearButtonEnabled(true); searchEdit->setClearButtonEnabled(true);
lineEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition); searchEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition);
lineEdit->setPlaceholderText("Search..."); searchEdit->setPlaceholderText("Search...");
connect(searchEdit, SIGNAL(returnPressed()), this, SLOT(onSearchInserted()));
actionFirst = new QAction("", this); actionFirst = new QAction("", this);
actionFirst->setIcon(QIcon(":/icons/resources/resultset_first.png")); actionFirst->setIcon(QIcon(":/icons/resources/resultset_first.png"));
@ -123,7 +124,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel
topWidget->setLayout(vbox1); topWidget->setLayout(vbox1);
vbox1->setSpacing(5); vbox1->setSpacing(5);
vbox1->setContentsMargins(0, 0, 0, 0); vbox1->setContentsMargins(0, 0, 0, 0);
vbox1->addWidget(lineEdit); vbox1->addWidget(searchEdit);
vbox1->addWidget(treeView); vbox1->addWidget(treeView);
QWidget *toolbarWidget = new QWidget(); QWidget *toolbarWidget = new QWidget();
@ -214,7 +215,7 @@ void DesignWidget::newContext(Context *ctx)
highlightSelected.clear(); highlightSelected.clear();
this->ctx = ctx; this->ctx = ctx;
treeModel->loadData(ctx); treeModel->loadContext(ctx);
updateTree(); updateTree();
} }
@ -234,7 +235,7 @@ void DesignWidget::updateTree()
} }
} }
treeModel->updateData(ctx); treeModel->updateCellsNets(ctx);
} }
QtProperty *DesignWidget::addTopLevelProperty(const QString &id) QtProperty *DesignWidget::addTopLevelProperty(const QString &id)
{ {
@ -714,4 +715,19 @@ void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column)
} }
void DesignWidget::onDoubleClicked(const QModelIndex &index) { Q_EMIT zoomSelected(); } void DesignWidget::onDoubleClicked(const QModelIndex &index) { Q_EMIT zoomSelected(); }
void DesignWidget::onSearchInserted()
{
if (currentSearch == searchEdit->text()) {
currentIndex++;
if (currentIndex >= currentSearchIndexes.size())
currentIndex = 0;
} else {
currentSearch = searchEdit->text();
currentSearchIndexes = treeModel->search(searchEdit->text());
currentIndex = 0;
}
if (currentSearchIndexes.size() > 0 && currentIndex < currentSearchIndexes.size())
selectionModel->setCurrentIndex(currentSearchIndexes.at(currentIndex), QItemSelectionModel::ClearAndSelect);
}
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -64,6 +64,7 @@ class DesignWidget : public QWidget
void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
void onItemDoubleClicked(QTreeWidgetItem *item, int column); void onItemDoubleClicked(QTreeWidgetItem *item, int column);
void onDoubleClicked(const QModelIndex &index); void onDoubleClicked(const QModelIndex &index);
void onSearchInserted();
public Q_SLOTS: public Q_SLOTS:
void newContext(Context *ctx); void newContext(Context *ctx);
void updateTree(); void updateTree();
@ -77,6 +78,7 @@ class DesignWidget : public QWidget
QTreeView *treeView; QTreeView *treeView;
QItemSelectionModel *selectionModel; QItemSelectionModel *selectionModel;
ContextTreeModel *treeModel; ContextTreeModel *treeModel;
QLineEdit *searchEdit;
QtVariantPropertyManager *variantManager; QtVariantPropertyManager *variantManager;
QtVariantPropertyManager *readOnlyManager; QtVariantPropertyManager *readOnlyManager;
QtGroupPropertyManager *groupManager; QtGroupPropertyManager *groupManager;
@ -98,6 +100,10 @@ class DesignWidget : public QWidget
QColor highlightColors[8]; QColor highlightColors[8];
QMap<LazyTreeItem *, int> highlightSelected; QMap<LazyTreeItem *, int> highlightSelected;
QString currentSearch;
QList<QModelIndex> currentSearchIndexes;
int currentIndex;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -18,114 +18,48 @@
*/ */
#include "treemodel.h" #include "treemodel.h"
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
ContextTreeItem::ContextTreeItem() { parentNode = nullptr; }
ContextTreeItem::ContextTreeItem(QString name)
: parentNode(nullptr), itemId(IdString()), itemType(ElementType::NONE), itemName(name)
{
}
ContextTreeItem::ContextTreeItem(IdString id, ElementType type, QString name)
: parentNode(nullptr), itemId(id), itemType(type), itemName(name)
{
}
ContextTreeItem::~ContextTreeItem()
{
if (parentNode)
parentNode->children.removeOne(this);
qDeleteAll(children);
}
//void ContextTreeItem::addChild(ContextTreeItem *item)
//{
// item->parentNode = this;
// children.append(item);
//}
void ContextTreeItem::sort()
{
for (auto item : children)
if (item->count()>1) item->sort();
qSort(children.begin(), children.end(), [&](const ContextTreeItem *a, const ContextTreeItem *b){
QString name_a = a->name();
QString name_b = b->name();
// Try to extract a common prefix from both strings.
QString common;
for (int i = 0; i < std::min(name_a.size(), name_b.size()); i++) {
const QChar c_a = name_a[i];
const QChar c_b = name_b[i];
if (c_a == c_b) {
common.push_back(c_a);
} else {
break;
}
}
// No common part? lexical sort.
if (common.size() == 0) {
return a->name() < b->name();
}
// Get the non-common parts.
name_a.remove(0, common.size());
name_b.remove(0, common.size());
// And see if they're strings.
bool ok = true;
int num_a = name_a.toInt(&ok);
if (!ok) {
return a->name() < b->name();
}
int num_b = name_b.toInt(&ok);
if (!ok) {
return a->name() < b->name();
}
return num_a < num_b;
});
}
ContextTreeModel::ContextTreeModel(QObject *parent) : ContextTreeModel::ContextTreeModel(QObject *parent) :
QAbstractItemModel(parent), QAbstractItemModel(parent),
root_(new StaticTreeItem("Elements", nullptr)) {} root_(new StaticTreeItem("Elements", nullptr)) {}
ContextTreeModel::~ContextTreeModel() {} ContextTreeModel::~ContextTreeModel() {}
void ContextTreeModel::loadData(Context *ctx) void ContextTreeModel::loadContext(Context *ctx)
{ {
if (!ctx) if (!ctx)
return; return;
beginResetModel(); beginResetModel();
// Currently we lack an API to get a proper hierarchy of bels/pip/wires
// cross-arch. So we only do this for ICE40 by querying the ChipDB
// directly.
// TODO(q3k): once AnyId and the tree API land in Arch, move this over.
#ifdef ARCH_ICE40
{ {
printf("generating bel map...\n");
std::map<std::pair<int, int>, std::vector<BelId>> belMap; std::map<std::pair<int, int>, std::vector<BelId>> belMap;
for (auto bel : ctx->getBels()) { for (auto bel : ctx->getBels()) {
auto loc = ctx->getBelLocation(bel); auto loc = ctx->getBelLocation(bel);
belMap[std::pair<int, int>(loc.x, loc.y)].push_back(bel); belMap[std::pair<int, int>(loc.x, loc.y)].push_back(bel);
} }
printf("generating bel static tree...\n");
auto belGetter = [](Context *ctx, BelId id) { return ctx->getBelName(id); }; auto belGetter = [](Context *ctx, BelId id) { return ctx->getBelName(id); };
bel_root_ = std::unique_ptr<BelXYRoot>(new BelXYRoot(ctx, "Bels", root_.get(), belMap, belGetter)); bel_root_ = std::unique_ptr<BelXYRoot>(new BelXYRoot(ctx, "Bels", root_.get(), belMap, belGetter));
printf("generating wire map...\n");
std::map<std::pair<int, int>, std::vector<WireId>> wireMap; std::map<std::pair<int, int>, std::vector<WireId>> wireMap;
//TODO(q3k): change this once we have an API to get wire categories/locations/labels
for (int i = 0; i < ctx->chip_info->num_wires; i++) { for (int i = 0; i < ctx->chip_info->num_wires; i++) {
const auto wire = &ctx->chip_info->wire_data[i]; const auto wire = &ctx->chip_info->wire_data[i];
WireId wireid; WireId wireid;
wireid.index = i; wireid.index = i;
wireMap[std::pair<int, int>(wire->x, wire->y)].push_back(wireid); wireMap[std::pair<int, int>(wire->x, wire->y)].push_back(wireid);
} }
printf("generating wire static tree...\n");
auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); }; auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); };
wire_root_ = std::unique_ptr<WireXYRoot>(new WireXYRoot(ctx, "Wires", root_.get(), wireMap, wireGetter)); wire_root_ = std::unique_ptr<WireXYRoot>(new WireXYRoot(ctx, "Wires", root_.get(), wireMap, wireGetter));
printf("generating pip map...\n");
std::map<std::pair<int, int>, std::vector<PipId>> pipMap; std::map<std::pair<int, int>, std::vector<PipId>> pipMap;
//TODO(q3k): change this once we have an API to get wire categories/locations/labels
for (int i = 0; i < ctx->chip_info->num_pips; i++) { for (int i = 0; i < ctx->chip_info->num_pips; i++) {
const auto pip = &ctx->chip_info->pip_data[i]; const auto pip = &ctx->chip_info->pip_data[i];
PipId pipid; PipId pipid;
@ -136,80 +70,34 @@ void ContextTreeModel::loadData(Context *ctx)
auto pipGetter = [](Context *ctx, PipId id) { return ctx->getPipName(id); }; auto pipGetter = [](Context *ctx, PipId id) { return ctx->getPipName(id); };
pip_root_ = std::unique_ptr<PipXYRoot>(new PipXYRoot(ctx, "Pips", root_.get(), pipMap, pipGetter)); pip_root_ = std::unique_ptr<PipXYRoot>(new PipXYRoot(ctx, "Pips", root_.get(), pipMap, pipGetter));
} }
#endif
//nets_root = new ContextTreeItem("Nets"); cell_root_ = std::unique_ptr<IdStringList>(new IdStringList(QString("Cells"), root_.get()));
//root->addChild(nets_root); net_root_ = std::unique_ptr<IdStringList>(new IdStringList(QString("Nets"), root_.get()));
//cells_root = new ContextTreeItem("Cells");
//root->addChild(cells_root);
endResetModel(); endResetModel();
updateCellsNets(ctx);
} }
void ContextTreeModel::updateData(Context *ctx) void ContextTreeModel::updateCellsNets(Context *ctx)
{ {
if (!ctx) if (!ctx)
return; return;
beginResetModel(); beginResetModel();
//QModelIndex nets_index = indexFromNode(nets_root); std::vector<IdString> cells;
// Remove nets not existing any more for (auto &pair : ctx->cells) {
//QMap<QString, ContextTreeItem *>::iterator i = nameToItem[3].begin(); cells.push_back(pair.first);
//while (i != nameToItem[3].end()) { }
// QMap<QString, ContextTreeItem *>::iterator prev = i; cell_root_->updateElements(ctx, cells);
// ++i;
// if (ctx->nets.find(ctx->id(prev.key().toStdString())) == ctx->nets.end()) {
// //int pos = prev.value()->parent()->indexOf(prev.value());
// //beginRemoveRows(nets_index, pos, pos);
// delete prev.value();
// nameToItem[3].erase(prev);
// //endRemoveRows();
// }
//}
//// Add nets to tree
//for (auto &item : ctx->nets) {
// auto id = item.first;
// QString name = QString(id.c_str(ctx));
// if (!nameToItem[3].contains(name)) {
// //beginInsertRows(nets_index, nets_root->count() + 1, nets_root->count() + 1);
// ContextTreeItem *newItem = new ContextTreeItem(id, ElementType::NET, name);
// nets_root->addChild(newItem);
// nameToItem[3].insert(name, newItem);
// //endInsertRows();
// }
//}
//nets_root->sort(); std::vector<IdString> nets;
for (auto &pair : ctx->nets) {
//QModelIndex cell_index = indexFromNode(cells_root); nets.push_back(pair.first);
// Remove cells not existing any more }
//i = nameToItem[4].begin(); net_root_->updateElements(ctx, nets);
//while (i != nameToItem[4].end()) {
// QMap<QString, ContextTreeItem *>::iterator prev = i;
// ++i;
// if (ctx->cells.find(ctx->id(prev.key().toStdString())) == ctx->cells.end()) {
// //int pos = prev.value()->parent()->indexOf(prev.value());
// //beginRemoveRows(cell_index, pos, pos);
// delete prev.value();
// nameToItem[4].erase(prev);
// //endRemoveRows();
// }
//}
//// Add cells to tree
//for (auto &item : ctx->cells) {
// auto id = item.first;
// QString name = QString(id.c_str(ctx));
// if (!nameToItem[4].contains(name)) {
// //beginInsertRows(cell_index, cells_root->count() + 1, cells_root->count() + 1);
// ContextTreeItem *newItem = new ContextTreeItem(id, ElementType::CELL, name);
// cells_root->addChild(newItem);
// nameToItem[4].insert(name, newItem);
// //endInsertRows();
// }
//}
//cells_root->sort();
endResetModel(); endResetModel();
} }
@ -277,22 +165,6 @@ static int getElementIndex(ElementType type)
return -1; return -1;
} }
//ContextTreeItem *ContextTreeModel::nodeForIdType(const ElementType type, const QString name) const
//{
// int index = getElementIndex(type);
// if (type != ElementType::NONE && nameToItem[index].contains(name))
// return nameToItem[index].value(name);
// return nullptr;
//}
//QModelIndex ContextTreeModel::indexFromNode(ContextTreeItem *node)
//{
// ContextTreeItem *parent = node->parent();
// if (parent == root)
// return QModelIndex();
// return createIndex(parent->indexOf(node), 0, node);
//}
Qt::ItemFlags ContextTreeModel::flags(const QModelIndex &index) const Qt::ItemFlags ContextTreeModel::flags(const QModelIndex &index) const
{ {
LazyTreeItem *node = nodeFromIndex(index); LazyTreeItem *node = nodeFromIndex(index);
@ -310,4 +182,19 @@ bool ContextTreeModel::canFetchMore(const QModelIndex &parent) const
return nodeFromIndex(parent)->canFetchMore(); return nodeFromIndex(parent)->canFetchMore();
} }
QList<QModelIndex> ContextTreeModel::search(QString text)
{
QList<QModelIndex> list;
//for (int i = 0; i < 6; i++) {
// for (auto key : nameToItem[i].keys()) {
// if (key.contains(text, Qt::CaseInsensitive)) {
// list.append(indexFromNode(nameToItem[i].value(key)));
// if (list.count() > 500)
// break; // limit to 500 results
// }
// }
//}
return list;
}
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -22,6 +22,7 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include "nextpnr.h" #include "nextpnr.h"
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -36,31 +37,6 @@ enum class ElementType
GROUP GROUP
}; };
class ContextTreeItem
{
public:
ContextTreeItem();
ContextTreeItem(QString name);
ContextTreeItem(IdString id, ElementType type, QString name);
~ContextTreeItem();
void addChild(ContextTreeItem *item);
int indexOf(ContextTreeItem *n) const { return children.indexOf(n); }
ContextTreeItem *at(int idx) const { return children.at(idx); }
int count() const { return children.count(); }
ContextTreeItem *parent() const { return parentNode; }
IdString id() const { return itemId; }
ElementType type() const { return itemType; }
QString name() const { return itemName; }
void sort();
private:
ContextTreeItem *parentNode;
QList<ContextTreeItem *> children;
IdString itemId;
ElementType itemType;
QString itemName;
};
class LazyTreeItem class LazyTreeItem
{ {
protected: protected:
@ -207,6 +183,74 @@ class ElementList : public LazyTreeItem
} }
}; };
class IdStringList : public StaticTreeItem
{
private:
std::unordered_map<IdString, std::unique_ptr<StaticTreeItem>> managed_;
public:
using StaticTreeItem::StaticTreeItem;
void updateElements(Context *ctx, std::vector<IdString> elements)
{
// for any elements that are not yet in managed_, created them.
std::unordered_set<IdString> element_set;
for (auto elem : elements) {
element_set.insert(elem);
auto existing = managed_.find(elem);
if (existing == managed_.end()) {
auto item = new StaticTreeItem(elem.c_str(ctx), this);
managed_.emplace(elem, std::unique_ptr<StaticTreeItem>(item));
}
}
children_.clear();
// for any elements that are in managed_ but not in new, delete them.
for (auto &pair : managed_) {
if (element_set.count(pair.first) != 0) {
children_.push_back(pair.second.get());
continue;
}
managed_.erase(pair.first);
}
// sort new children
qSort(children_.begin(), children_.end(), [&](const LazyTreeItem *a, const LazyTreeItem *b){
QString name_a = a->name();
QString name_b = b->name();
// Try to extract a common prefix from both strings.
QString common;
for (int i = 0; i < std::min(name_a.size(), name_b.size()); i++) {
const QChar c_a = name_a[i];
const QChar c_b = name_b[i];
if (c_a == c_b) {
common.push_back(c_a);
} else {
break;
}
}
// No common part? lexical sort.
if (common.size() == 0) {
return a->name() < b->name();
}
// Get the non-common parts.
name_a.remove(0, common.size());
name_b.remove(0, common.size());
// And see if they're strings.
bool ok = true;
int num_a = name_a.toInt(&ok);
if (!ok) {
return a->name() < b->name();
}
int num_b = name_b.toInt(&ok);
if (!ok) {
return a->name() < b->name();
}
return num_a < num_b;
});
}
};
template <typename ElementT> template <typename ElementT>
class ElementXYRoot : public StaticTreeItem class ElementXYRoot : public StaticTreeItem
{ {
@ -217,7 +261,7 @@ class ElementXYRoot : public StaticTreeItem
private: private:
Context *ctx_; Context *ctx_;
std::vector<std::unique_ptr<LazyTreeItem>> bels_; std::vector<std::unique_ptr<LazyTreeItem>> managed_;
ElementMap map_; ElementMap map_;
ElementGetter getter_; ElementGetter getter_;
@ -241,11 +285,11 @@ class ElementXYRoot : public StaticTreeItem
// create X item for tree // create X item for tree
auto item = new StaticTreeItem(QString("X%1").arg(i), this); auto item = new StaticTreeItem(QString("X%1").arg(i), this);
bels_.push_back(std::move(std::unique_ptr<LazyTreeItem>(item))); managed_.push_back(std::move(std::unique_ptr<LazyTreeItem>(item)));
for (auto j : y_present) { for (auto j : y_present) {
auto item2 = new ElementList<ElementT>(ctx_, QString("Y%1").arg(j), item, &map_, i, j, getter_); auto item2 = new ElementList<ElementT>(ctx_, QString("Y%1").arg(j), item, &map_, i, j, getter_);
item2->fetchMore(1); item2->fetchMore(1);
bels_.push_back(std::move(std::unique_ptr<LazyTreeItem>(item2))); managed_.push_back(std::move(std::unique_ptr<LazyTreeItem>(item2)));
} }
} }
} }
@ -261,11 +305,10 @@ class ContextTreeModel : public QAbstractItemModel
ContextTreeModel(QObject *parent = nullptr); ContextTreeModel(QObject *parent = nullptr);
~ContextTreeModel(); ~ContextTreeModel();
void loadData(Context *ctx); void loadContext(Context *ctx);
void updateData(Context *ctx); void updateCellsNets(Context *ctx);
LazyTreeItem *nodeFromIndex(const QModelIndex &idx) const; LazyTreeItem *nodeFromIndex(const QModelIndex &idx) const;
//QModelIndex indexFromNode(ContextTreeItem *node); QList<QModelIndex> search(QString text);
//ContextTreeItem *nodeForIdType(const ElementType type, const QString name) const;
// Override QAbstractItemModel methods // Override QAbstractItemModel methods
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
@ -283,11 +326,8 @@ class ContextTreeModel : public QAbstractItemModel
std::unique_ptr<BelXYRoot> bel_root_; std::unique_ptr<BelXYRoot> bel_root_;
std::unique_ptr<WireXYRoot> wire_root_; std::unique_ptr<WireXYRoot> wire_root_;
std::unique_ptr<PipXYRoot> pip_root_; std::unique_ptr<PipXYRoot> pip_root_;
//std::unique_ptr<ElementXYRoot> wires_root_; std::unique_ptr<IdStringList> cell_root_;
//std::unique_ptr<ElementXYRoot> pips_root_; std::unique_ptr<IdStringList> net_root_;
//QMap<QString, ContextTreeItem *> nameToItem[6];
//ContextTreeItem *nets_root;
//ContextTreeItem *cells_root;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -141,18 +141,23 @@ Arch::Arch(ArchArgs args) : args(args)
#ifdef ICE40_HX1K_ONLY #ifdef ICE40_HX1K_ONLY
if (args.type == ArchArgs::HX1K) { if (args.type == ArchArgs::HX1K) {
fast_part = true;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_1k)); chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_1k));
} else { } else {
log_error("Unsupported iCE40 chip type.\n"); log_error("Unsupported iCE40 chip type.\n");
} }
#else #else
if (args.type == ArchArgs::LP384) { if (args.type == ArchArgs::LP384) {
fast_part = false;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_384)); chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_384));
} else if (args.type == ArchArgs::LP1K || args.type == ArchArgs::HX1K) { } else if (args.type == ArchArgs::LP1K || args.type == ArchArgs::HX1K) {
fast_part = args.type == ArchArgs::HX1K;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_1k)); chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_1k));
} else if (args.type == ArchArgs::UP5K) { } else if (args.type == ArchArgs::UP5K) {
fast_part = false;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_5k)); chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_5k));
} else if (args.type == ArchArgs::LP8K || args.type == ArchArgs::HX8K) { } else if (args.type == ArchArgs::LP8K || args.type == ArchArgs::HX8K) {
fast_part = args.type == ArchArgs::HX8K;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_8k)); chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_8k));
} else { } else {
log_error("Unsupported iCE40 chip type.\n"); log_error("Unsupported iCE40 chip type.\n");
@ -306,9 +311,23 @@ PortType Arch::getBelPinType(BelId bel, PortPin pin) const
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires; int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get(); const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
for (int i = 0; i < num_bel_wires; i++) if (num_bel_wires < 7) {
if (bel_wires[i].port == pin) for (int i = 0; i < num_bel_wires; i++) {
return PortType(bel_wires[i].type); if (bel_wires[i].port == pin)
return PortType(bel_wires[i].type);
}
} else {
int b = 0, e = num_bel_wires-1;
while (b <= e) {
int i = (b+e) / 2;
if (bel_wires[i].port == pin)
return PortType(bel_wires[i].type);
if (bel_wires[i].port > pin)
e = i-1;
else
b = i+1;
}
}
return PORT_INOUT; return PORT_INOUT;
} }
@ -322,10 +341,25 @@ WireId Arch::getBelPinWire(BelId bel, PortPin pin) const
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires; int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get(); const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
for (int i = 0; i < num_bel_wires; i++) { if (num_bel_wires < 7) {
if (bel_wires[i].port == pin) { for (int i = 0; i < num_bel_wires; i++) {
ret.index = bel_wires[i].wire_index; if (bel_wires[i].port == pin) {
break; ret.index = bel_wires[i].wire_index;
break;
}
}
} else {
int b = 0, e = num_bel_wires-1;
while (b <= e) {
int i = (b+e) / 2;
if (bel_wires[i].port == pin) {
ret.index = bel_wires[i].wire_index;
break;
}
if (bel_wires[i].port > pin)
e = i-1;
else
b = i+1;
} }
} }
@ -770,29 +804,29 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{ {
if (cell->type == id_icestorm_lc) { if (cell->type == id_icestorm_lc) {
if ((fromPort == id_i0 || fromPort == id_i1 || fromPort == id_i2 || fromPort == id_i3) && if ((fromPort == id_i0 || fromPort == id_i1 || fromPort == id_i2 || fromPort == id_i3) &&
(toPort == id_o || toPort == id_lo)) { (toPort == id_o || toPort == id_lo)) {
delay = 450; delay.delay = 450;
return true; return true;
} else if (fromPort == id_cin && toPort == id_cout) { } else if (fromPort == id_cin && toPort == id_cout) {
delay = 120; delay.delay = 120;
return true; return true;
} else if (fromPort == id_i1 && toPort == id_cout) { } else if (fromPort == id_i1 && toPort == id_cout) {
delay = 260; delay.delay = 260;
return true; return true;
} else if (fromPort == id_i2 && toPort == id_cout) { } else if (fromPort == id_i2 && toPort == id_cout) {
delay = 230; delay.delay = 230;
return true; return true;
} else if (fromPort == id_clk && toPort == id_o) { } else if (fromPort == id_clk && toPort == id_o) {
delay = 540; delay.delay = 540;
return true; return true;
} }
} else if (cell->type == id_icestorm_ram) { } else if (cell->type == id_icestorm_ram) {
if (fromPort == id_rclk) { if (fromPort == id_rclk) {
delay = 2140; delay.delay = 2140;
return true; return true;
} }
} }

View File

@ -44,9 +44,9 @@ template <typename T> struct RelPtr
}; };
NPNR_PACKED_STRUCT(struct BelWirePOD { NPNR_PACKED_STRUCT(struct BelWirePOD {
int32_t wire_index;
PortPin port; PortPin port;
int32_t type; int32_t type;
int32_t wire_index;
}); });
NPNR_PACKED_STRUCT(struct BelInfoPOD { NPNR_PACKED_STRUCT(struct BelInfoPOD {
@ -66,7 +66,8 @@ NPNR_PACKED_STRUCT(struct BelPortPOD {
NPNR_PACKED_STRUCT(struct PipInfoPOD { NPNR_PACKED_STRUCT(struct PipInfoPOD {
// RelPtr<char> name; // RelPtr<char> name;
int32_t src, dst; int32_t src, dst;
int32_t delay; int32_t fast_delay;
int32_t slow_delay;
int8_t x, y; int8_t x, y;
int16_t src_seg, dst_seg; int16_t src_seg, dst_seg;
int16_t switch_mask; int16_t switch_mask;
@ -89,6 +90,9 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD {
int32_t num_segments; int32_t num_segments;
RelPtr<WireSegmentPOD> segments; RelPtr<WireSegmentPOD> segments;
int32_t fast_delay;
int32_t slow_delay;
int8_t x, y; int8_t x, y;
WireType type; WireType type;
int8_t padding_0; int8_t padding_0;
@ -344,6 +348,7 @@ struct ArchArgs
struct Arch : BaseCtx struct Arch : BaseCtx
{ {
bool fast_part;
const ChipInfoPOD *chip_info; const ChipInfoPOD *chip_info;
const PackageInfoPOD *package_info; const PackageInfoPOD *package_info;
@ -524,6 +529,11 @@ struct Arch : BaseCtx
DelayInfo getWireDelay(WireId wire) const DelayInfo getWireDelay(WireId wire) const
{ {
DelayInfo delay; DelayInfo delay;
NPNR_ASSERT(wire != WireId());
if (fast_part)
delay.delay = chip_info->wire_data[wire.index].fast_delay;
else
delay.delay = chip_info->wire_data[wire.index].slow_delay;
return delay; return delay;
} }
@ -637,7 +647,10 @@ struct Arch : BaseCtx
{ {
DelayInfo delay; DelayInfo delay;
NPNR_ASSERT(pip != PipId()); NPNR_ASSERT(pip != PipId());
delay.delay = chip_info->pip_data[pip.index].delay; if (fast_part)
delay.delay = chip_info->pip_data[pip.index].fast_delay;
else
delay.delay = chip_info->pip_data[pip.index].slow_delay;
return delay; return delay;
} }
@ -709,7 +722,7 @@ struct Arch : BaseCtx
// Get the delay through a cell from one port to another, returning false // Get the delay through a cell from one port to another, returning false
// if no path exists // if no path exists
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const; bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
// Get the associated clock to a port, or empty if the port is combinational // Get the associated clock to a port, or empty if the port is combinational
IdString getPortClock(const CellInfo *cell, IdString port) const; IdString getPortClock(const CellInfo *cell, IdString port) const;
// Return true if a port is a clock // Return true if a port is a clock

View File

@ -1,3 +1,5 @@
SHELL = /bin/bash
reports:: reports::
define mkreport define mkreport
@ -10,10 +12,10 @@ report_n$1.txt: hx8kdemo_n$1.asc
icetime -m -r report_n$1.txt -d hx8k hx8kdemo_n$1.asc icetime -m -r report_n$1.txt -d hx8k hx8kdemo_n$1.asc
hx8kdemo_a$1.asc: hx8kdemo.blif hx8kdemo_a$1.asc: hx8kdemo.blif
arachne-pnr -d 8k -p hx8kdemo.pcf -o hx8kdemo_a$1.asc -s 1$1 hx8kdemo.blif > hx8kdemo_a$1.log 2>&1 { time arachne-pnr -d 8k -p hx8kdemo.pcf -o hx8kdemo_a$1.asc -s 1$1 hx8kdemo.blif; } > hx8kdemo_a$1.log 2>&1
hx8kdemo_n$1.asc: hx8kdemo.json hx8kdemo_n$1.asc: hx8kdemo.json
../../nextpnr-ice40 --asc hx8kdemo_n$1.asc --json hx8kdemo.json --pcf hx8kdemo.pcf --hx8k --seed 1$1 > hx8kdemo_n$1.log 2>&1 { time ../../nextpnr-ice40 --asc hx8kdemo_n$1.asc --json hx8kdemo.json --pcf hx8kdemo.pcf --hx8k --seed 1$1; } > hx8kdemo_n$1.log 2>&1
endef endef
$(foreach i,0 1 2 3 4 5 6 7 8 9,$(eval $(call mkreport,$(i)))) $(foreach i,0 1 2 3 4 5 6 7 8 9,$(eval $(call mkreport,$(i))))

View File

@ -11,36 +11,58 @@
"%matplotlib inline\n", "%matplotlib inline\n",
"import numpy as np\n", "import numpy as np\n",
"import matplotlib.pyplot as plt\n", "import matplotlib.pyplot as plt\n",
"import subprocess\n", "import subprocess, re\n",
"\n", "\n",
"gitrev = subprocess.getoutput(\"git rev-parse --short HEAD\")\n", "gitrev = subprocess.getoutput(\"git rev-parse --short HEAD\")\n",
"\n", "\n",
"data_a = 1 + np.zeros(10)\n", "data_a = np.zeros((10, 2))\n",
"data_n = 1 + np.zeros(10)\n", "data_n = np.zeros((10, 2))\n",
"\n", "\n",
"for i in range(10):\n", "for i in range(10):\n",
" try:\n", " try:\n",
" with open(\"report_a%d.txt\" % i, \"r\") as f:\n", " with open(\"report_a%d.txt\" % i, \"r\") as f:\n",
" for line in f:\n", " for line in f:\n",
" if line.startswith(\"Total path delay:\"):\n", " if line.startswith(\"Total path delay:\"):\n",
" data_a[i] = float(line.split()[3])\n", " data_a[i, 0] = float(line.split()[3])\n",
" except:\n", " except:\n",
" pass\n", " data_a[i, 0] = 1.0\n",
" \n",
" try:\n", " try:\n",
" with open(\"report_n%d.txt\" % i, \"r\") as f:\n", " with open(\"report_n%d.txt\" % i, \"r\") as f:\n",
" for line in f:\n", " for line in f:\n",
" if line.startswith(\"Total path delay:\"):\n", " if line.startswith(\"Total path delay:\"):\n",
" data_n[i] = float(line.split()[3])\n", " data_n[i, 0] = float(line.split()[3])\n",
" except:\n", " except:\n",
" pass\n", " data_n[i, 0] = 1.0\n",
" \n",
" with open(\"hx8kdemo_a%d.log\" % i, \"r\") as f:\n",
" for line in f:\n",
" match = re.match(r\"real\\s+(\\d+)m(\\d+)\", line)\n",
" if match:\n",
" data_a[i, 1] = float(match.group(1)) + float(match.group(2))/60\n",
" \n",
" with open(\"hx8kdemo_n%d.log\" % i, \"r\") as f:\n",
" for line in f:\n",
" match = re.match(r\"real\\s+(\\d+)m(\\d+)\", line)\n",
" if match:\n",
" data_n[i, 1] = float(match.group(1)) + float(match.group(2))/60\n",
"\n", "\n",
"plt.figure(figsize=(9,3))\n", "plt.figure(figsize=(9,3))\n",
"plt.title(\"nextpnr -- ice40/benchmark/ -- %s\" % gitrev)\n", "plt.title(\"nextpnr -- ice40/benchmark/ -- %s\" % gitrev)\n",
"plt.bar(np.arange(10), data_a, color='blue')\n", "plt.bar(np.arange(10), data_a[:, 0], color='blue')\n",
"plt.bar(15+np.arange(10), data_n, color='red')\n", "plt.bar(15+np.arange(10), data_n[:, 0], color='red')\n",
"plt.ylabel('Longest path (ns)')\n", "plt.ylabel('Longest path (ns)')\n",
"plt.xticks([5, 20], [\"arachne-pnr\", \"nextpnr\"])\n", "plt.xticks([5, 20], [\"arachne-pnr\", \"nextpnr\"])\n",
"plt.xlim(-2, 27)\n", "plt.xlim(-2, 27)\n",
"plt.show()\n",
"\n",
"plt.figure(figsize=(9,3))\n",
"plt.title(\"nextpnr -- ice40/benchmark/ -- %s\" % gitrev)\n",
"plt.bar(np.arange(10), data_a[:, 1], color='blue')\n",
"plt.bar(15+np.arange(10), data_n[:, 1], color='red')\n",
"plt.ylabel('Runtime (minutes)')\n",
"plt.xticks([5, 20], [\"arachne-pnr\", \"nextpnr\"])\n",
"plt.xlim(-2, 27)\n",
"plt.show()" "plt.show()"
] ]
} }

View File

@ -9,6 +9,8 @@ parser = argparse.ArgumentParser(description="convert ICE40 chip database")
parser.add_argument("filename", type=str, help="chipdb input filename") parser.add_argument("filename", type=str, help="chipdb input filename")
parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc") parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc")
parser.add_argument("-g", "--gfxh", type=str, help="path to gfx.h") parser.add_argument("-g", "--gfxh", type=str, help="path to gfx.h")
parser.add_argument("--fast", type=str, help="path to timing data for fast part")
parser.add_argument("--slow", type=str, help="path to timing data for slow part")
args = parser.parse_args() args = parser.parse_args()
dev_name = None dev_name = None
@ -51,6 +53,9 @@ wiretypes = dict()
gfx_wire_ids = dict() gfx_wire_ids = dict()
wire_segments = dict() wire_segments = dict()
fast_timings = None
slow_timings = None
with open(args.portspins) as f: with open(args.portspins) as f:
for line in f: for line in f:
line = line.replace("(", " ") line = line.replace("(", " ")
@ -77,6 +82,31 @@ with open(args.gfxh) as f:
name = line.strip().rstrip(",") name = line.strip().rstrip(",")
gfx_wire_ids[name] = idx gfx_wire_ids[name] = idx
def read_timings(filename):
db = dict()
with open(filename) as f:
cell = None
for line in f:
line = line.split()
if len(line) == 0:
continue
if line[0] == "CELL":
cell = line[1]
if line[0] == "IOPATH":
key = "%s.%s.%s" % (cell, line[1], line[2])
v1 = line[3].split(":")[2]
v2 = line[4].split(":")[2]
v1 = 0 if v1 == "*" else float(v1)
v2 = 0 if v2 == "*" else float(v2)
db[key] = max(v1, v2)
return db
if args.fast is not None:
fast_timings = read_timings(args.fast)
if args.slow is not None:
slow_timings = read_timings(args.slow)
beltypes["ICESTORM_LC"] = 1 beltypes["ICESTORM_LC"] = 1
beltypes["ICESTORM_RAM"] = 2 beltypes["ICESTORM_RAM"] = 2
beltypes["SB_IO"] = 3 beltypes["SB_IO"] = 3
@ -184,46 +214,75 @@ def wire_type(name):
assert 0 assert 0
return wt return wt
def pipdelay(src, dst): def pipdelay(src_idx, dst_idx, db):
src = wire_names_r[src] if db is None:
dst = wire_names_r[dst] return 0
src = wire_names_r[src_idx]
dst = wire_names_r[dst_idx]
src_type = wire_type(src[2]) src_type = wire_type(src[2])
dst_type = wire_type(dst[2]) dst_type = wire_type(dst[2])
if src_type == "LOCAL" and dst_type == "LOCAL": if dst[2].startswith("sp4_") or dst[2].startswith("span4_"):
return 250 if src[2].startswith("sp12_") or src[2].startswith("span12_"):
return db["Sp12to4.I.O"]
if src_type == "GLOBAL" and dst_type == "LOCAL": if src[2].startswith("span4_"):
return 400 return db["IoSpan4Mux.I.O"]
# Local -> Span if dst[2].startswith("sp4_h_"):
return db["Span4Mux_h4.I.O"]
else:
return db["Span4Mux_v4.I.O"]
if src_type == "LOCAL" and dst_type in ("SP4_HORZ", "SP4_VERT"): if dst[2].startswith("sp12_") or dst[2].startswith("span12_"):
return 350 if dst[2].startswith("sp12_h_"):
return db["Span12Mux_h12.I.O"]
else:
return db["Span12Mux_v12.I.O"]
if src_type == "LOCAL" and dst_type in ("SP12_HORZ", "SP12_VERT"): if dst[2] in ("fabout", "clk"):
return 500 return 0 # FIXME?
# Span -> Local if src[2].startswith("glb_netwk_") and dst[2].startswith("glb2local_"):
return 0 # FIXME?
if src_type in ("SP4_HORZ", "SP4_VERT", "SP12_HORZ", "SP12_VERT") and dst_type == "LOCAL": if dst[2] == "carry_in_mux":
return 300 return db["ICE_CARRY_IN_MUX.carryinitin.carryinitout"]
# Span -> Span if dst[2] in ("lutff_global/clk", "io_global/inclk", "io_global/outclk", "ram/RCLK", "ram/WCLK"):
return db["ClkMux.I.O"]
if src_type in ("SP12_HORZ", "SP12_VERT") and dst_type in ("SP12_HORZ", "SP12_VERT"): if dst[2] in ("lutff_global/s_r", "io_global/latch", "ram/RE", "ram/WE"):
return 450 return db["SRMux.I.O"]
if src_type in ("SP4_HORZ", "SP4_VERT") and dst_type in ("SP4_HORZ", "SP4_VERT"): if dst[2] in ("lutff_global/cen", "io_global/cen", "ram/RCLKE", "ram/WCLKE"):
return 300 return db["CEMux.I.O"]
if src_type in ("SP12_HORZ", "SP12_VERT") and dst_type in ("SP4_HORZ", "SP4_VERT"): if dst[2].startswith("local_"):
return 380 return db["LocalMux.I.O"]
# print(src, dst, src_type, dst_type, file=sys.stderr) if src[2].startswith("local_") and dst[2] in ("io_0/D_OUT_0", "io_0/D_OUT_1", "io_0/OUT_ENB", "io_1/D_OUT_0", "io_1/D_OUT_1", "io_1/OUT_ENB"):
return db["IoInMux.I.O"]
if re.match(r"lutff_\d+/in_\d+", dst[2]):
return db["InMux.I.O"]
if re.match(r"ram/(MASK|RADDR|WADDR|WDATA)_", dst[2]):
return db["InMux.I.O"]
print(src, dst, src_idx, dst_idx, src_type, dst_type, file=sys.stderr)
assert 0 assert 0
def wiredelay(wire_idx, db):
if db is None:
return 0
wire = wire_names_r[wire_idx]
wtype = wire_type(wire[2])
# FIXME
return 0
def init_tiletypes(device): def init_tiletypes(device):
global num_tile_types, tile_sizes, tile_bits global num_tile_types, tile_sizes, tile_bits
@ -448,13 +507,13 @@ def add_bel_input(bel, wire, port):
if wire not in wire_belports: if wire not in wire_belports:
wire_belports[wire] = set() wire_belports[wire] = set()
wire_belports[wire].add((bel, port)) wire_belports[wire].add((bel, port))
bel_wires[bel].append((wire, port, 0)) bel_wires[bel].append((portpins[port], 0, wire))
def add_bel_output(bel, wire, port): def add_bel_output(bel, wire, port):
if wire not in wire_belports: if wire not in wire_belports:
wire_belports[wire] = set() wire_belports[wire] = set()
wire_belports[wire].add((bel, port)) wire_belports[wire].add((bel, port))
bel_wires[bel].append((wire, port, 1)) bel_wires[bel].append((portpins[port], 1, wire))
def add_bel_lc(x, y, z): def add_bel_lc(x, y, z):
bel = len(bel_name) bel = len(bel_name)
@ -715,14 +774,12 @@ bba.post('NEXTPNR_NAMESPACE_END')
bba.push("chipdb_blob_%s" % dev_name) bba.push("chipdb_blob_%s" % dev_name)
bba.r("chip_info_%s" % dev_name, "chip_info") bba.r("chip_info_%s" % dev_name, "chip_info")
index = 0
for bel in range(len(bel_name)): for bel in range(len(bel_name)):
bba.l("bel_wires_%d" % bel, "BelWirePOD") bba.l("bel_wires_%d" % bel, "BelWirePOD")
for i in range(len(bel_wires[bel])): for data in sorted(bel_wires[bel]):
bba.u32(bel_wires[bel][i][0], "wire_index") bba.u32(data[0], "port")
bba.u32(portpins[bel_wires[bel][i][1]], "port") bba.u32(data[1], "type")
bba.u32(bel_wires[bel][i][2], "type") bba.u32(data[2], "wire_index")
index += 1
bba.l("bel_data_%s" % dev_name, "BelInfoPOD") bba.l("bel_data_%s" % dev_name, "BelInfoPOD")
for bel in range(len(bel_name)): for bel in range(len(bel_name)):
@ -748,7 +805,8 @@ for wire in range(num_wires):
pi = dict() pi = dict()
pi["src"] = src pi["src"] = src
pi["dst"] = wire pi["dst"] = wire
pi["delay"] = pipdelay(src, wire) pi["fast_delay"] = pipdelay(src, wire, fast_timings)
pi["slow_delay"] = pipdelay(src, wire, slow_timings)
pi["x"] = pip_xy[(src, wire)][0] pi["x"] = pip_xy[(src, wire)][0]
pi["y"] = pip_xy[(src, wire)][1] pi["y"] = pip_xy[(src, wire)][1]
pi["switch_mask"] = pip_xy[(src, wire)][2] pi["switch_mask"] = pip_xy[(src, wire)][2]
@ -772,7 +830,8 @@ for wire in range(num_wires):
pi = dict() pi = dict()
pi["src"] = wire pi["src"] = wire
pi["dst"] = dst pi["dst"] = dst
pi["delay"] = pipdelay(wire, dst) pi["fast_delay"] = pipdelay(wire, dst, fast_timings)
pi["slow_delay"] = pipdelay(wire, dst, slow_timings)
pi["x"] = pip_xy[(wire, dst)][0] pi["x"] = pip_xy[(wire, dst)][0]
pi["y"] = pip_xy[(wire, dst)][1] pi["y"] = pip_xy[(wire, dst)][1]
pi["switch_mask"] = pip_xy[(wire, dst)][2] pi["switch_mask"] = pip_xy[(wire, dst)][2]
@ -891,6 +950,9 @@ for wire, info in enumerate(wireinfo):
else: else:
bba.u32(0, "segments") bba.u32(0, "segments")
bba.u32(wiredelay(wire, fast_timings), "fast_delay")
bba.u32(wiredelay(wire, slow_timings), "slow_delay")
bba.u8(info["x"], "x") bba.u8(info["x"], "x")
bba.u8(info["y"], "y") bba.u8(info["y"], "y")
bba.u8(wiretypes[wire_type(info["name"])], "type") bba.u8(wiretypes[wire_type(info["name"])], "type")
@ -923,7 +985,8 @@ for info in pipinfo:
# bba.s("X%d/Y%d/%s->%s" % (info["x"], info["y"], src_segname, dst_segname), "name") # bba.s("X%d/Y%d/%s->%s" % (info["x"], info["y"], src_segname, dst_segname), "name")
bba.u32(info["src"], "src") bba.u32(info["src"], "src")
bba.u32(info["dst"], "dst") bba.u32(info["dst"], "dst")
bba.u32(info["delay"], "delay") bba.u32(info["fast_delay"], "fast_delay")
bba.u32(info["slow_delay"], "slow_delay")
bba.u8(info["x"], "x") bba.u8(info["x"], "x")
bba.u8(info["y"], "y") bba.u8(info["y"], "y")
bba.u16(src_seg, "src_seg") bba.u16(src_seg, "src_seg")

View File

@ -14,17 +14,28 @@ file(MAKE_DIRECTORY ice40/chipdbs/)
add_library(ice40_chipdb OBJECT ice40/chipdbs/) add_library(ice40_chipdb OBJECT ice40/chipdbs/)
target_compile_definitions(ice40_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family}) target_compile_definitions(ice40_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family})
target_include_directories(ice40_chipdb PRIVATE ${family}/) target_include_directories(ice40_chipdb PRIVATE ${family}/)
if (MSVC) if (MSVC)
target_sources(ice40_chipdb PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ice40/resource/embed.cc) target_sources(ice40_chipdb PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ice40/resource/embed.cc)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ice40/resources/chipdb.rc PROPERTIES LANGUAGE RC) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ice40/resources/chipdb.rc PROPERTIES LANGUAGE RC)
foreach (dev ${devices}) foreach (dev ${devices})
if (dev EQUAL "5k")
set(OPT_FAST "")
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings-up5k.txt)
elseif(dev EQUAL "384")
set(OPT_FAST "")
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings-lp384.txt)
else()
set(OPT_FAST --fast ${ICEBOX_ROOT}/timings-hx${dev}.txt)
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings-lp${dev}.txt)
endif()
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt) set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba) set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h) set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB} add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB} COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${OPT_FAST} ${OPT_SLOW} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY} DEPENDS ${DEV_TXT_DB} ${DB_PY}
) )
add_custom_command(OUTPUT ${DEV_CC_DB} add_custom_command(OUTPUT ${DEV_CC_DB}
@ -40,13 +51,23 @@ if (MSVC)
else() else()
target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w) target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w)
foreach (dev ${devices}) foreach (dev ${devices})
if (dev EQUAL "5k")
set(OPT_FAST "")
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings_up5k.txt)
elseif(dev EQUAL "384")
set(OPT_FAST "")
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings_lp384.txt)
else()
set(OPT_FAST --fast ${ICEBOX_ROOT}/timings_hx${dev}.txt)
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings_lp${dev}.txt)
endif()
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt) set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba) set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h) set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB} add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${OPT_FAST} ${OPT_SLOW} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new
COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB} COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY} DEPENDS ${DEV_TXT_DB} ${DB_PY}
) )

View File

@ -114,17 +114,58 @@ class PlacementLegaliser
public: public:
PlacementLegaliser(Context *ctx) : ctx(ctx){}; PlacementLegaliser(Context *ctx) : ctx(ctx){};
void print_stats(const char *point)
{
float distance_sum = 0;
float max_distance = 0;
int moved_cells = 0;
int unplaced_cells = 0;
for (auto orig : originalPositions) {
if (ctx->cells.at(orig.first)->bel == BelId()) {
unplaced_cells++;
continue;
}
Loc newLoc = ctx->getBelLocation(ctx->cells.at(orig.first)->bel);
if (newLoc != orig.second) {
float distance = std::sqrt(std::pow(newLoc.x - orig.second.x, 2) + pow(newLoc.y - orig.second.y, 2));
moved_cells++;
distance_sum += distance;
if (distance > max_distance)
max_distance = distance;
}
}
log_info(" moved %d cells, %d unplaced (after %s)\n", moved_cells, unplaced_cells, point);
if (moved_cells > 0) {
log_info(" average distance %f\n", (distance_sum / moved_cells));
log_info(" maximum distance %f\n", max_distance);
}
}
bool legalise() bool legalise()
{ {
log_info("Legalising design..\n"); log_info("Legalising design..\n");
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (!ctx->getBelGlobalBuf(ci->bel) && cell.second->type == ctx->id("ICESTORM_LC")) {
originalPositions[cell.first] = ctx->getBelLocation(ci->bel);
}
}
init_logic_cells(); init_logic_cells();
bool legalised_carries = legalise_carries(); bool legalised_carries = legalise_carries();
if (!legalised_carries && !ctx->force) if (!legalised_carries && !ctx->force)
return false; return false;
print_stats("carry legalisation");
legalise_others(); legalise_others();
print_stats("misc. cell legalisation");
legalise_logic_tiles(); legalise_logic_tiles();
print_stats("logic cell legalisation");
bool replaced_cells = replace_cells(); bool replaced_cells = replace_cells();
print_stats("cell replacement");
ctx->assignArchInfo(); ctx->assignArchInfo();
return legalised_carries && replaced_cells; return legalised_carries && replaced_cells;
} }
@ -501,6 +542,7 @@ class PlacementLegaliser
Context *ctx; Context *ctx;
std::unordered_set<IdString> rippedCells; std::unordered_set<IdString> rippedCells;
std::unordered_set<IdString> createdCells; std::unordered_set<IdString> createdCells;
std::unordered_map<IdString, Loc> originalPositions;
// Go from X and Y position to logic cells, setting occupied to true if a Bel is unavailable // Go from X and Y position to logic cells, setting occupied to true if a Bel is unavailable
std::vector<std::vector<std::vector<std::pair<BelId, bool>>>> logic_bels; std::vector<std::vector<std::vector<std::pair<BelId, bool>>>> logic_bels;
}; };

View File

@ -1,21 +0,0 @@
def get_drivers(wire):
wid = chip.getWireByName(wire)
assert not wid.nil(), "wire {} not found".format(wire)
bp = chip.getBelPinUphill(wid)
if not bp.bel.nil():
print("Bel pin: {}.{}".format(chip.getBelName(bp.bel), str(bp.pin)))
for pip in sorted(chip.getPipsUphill(wid), key=lambda x: x.index):
print("Pip: {}".format(chip.getWireName(chip.getPipSrcWire(pip))))
def get_loads(wire):
wid = chip.getWireByName(wire)
assert not wid.nil(), "wire {} not found".format(wire)
for bp in sorted(chip.getBelPinsDownhill(wid), key=lambda x: (x.bel.index, x.pin)):
print("Bel pin: {}.{}".format(chip.getBelName(bp.bel), str(bp.pin)))
for pip in sorted(chip.getPipsDownhill(wid), key=lambda x: x.index):
print("Pip: {}".format(chip.getWireName(chip.getPipDstWire(pip))))
#get_drivers("12_14_lutff_7/in_3")
#get_loads("12_14_lutff_global/clk")

View File

@ -1,7 +0,0 @@
# Run: PYTHONPATH=. python3 python/python_mod_test.py
from nextpnrpy_ice40 import Chip, ChipArgs, iCE40Type
args = ChipArgs()
args.type = iCE40Type.HX1K
chip = Chip(args)
for wire in chip.getWires():
print(chip.getWireName(wire))

View File

@ -1,2 +0,0 @@
for wire in chip.getWires():
print(chip.getWireName(wire))