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})
install(TARGETS nextpnr-${family} RUNTIME DESTINATION bin)
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
if (BUILD_TESTS)
aux_source_directory(tests/${family}/ ${ufamily}_TEST_FILES)
@ -210,10 +205,6 @@ foreach (family ${ARCH})
set(family_targets ${family_targets} nextpnr-${family}-test)
endif()
if (BUILD_PYTHON)
set(family_targets ${family_targets} nextpnrpy_${family})
endif()
# Include the family-specific CMakeFile
include(${family}/family.cmake)
foreach (target ${family_targets})

View File

@ -1,10 +1,21 @@
nextpnr -- a portable FPGA place and route tool
===============================================
nextpnr is an FPGA place and route tool with emphasis on supporting a wide
range of real-world FPGA devices. It currently supports Lattice iCE40 devices
and Lattice ECP5 devices, as well as a "generic" back-end for user-defined
architectures. (ECP5 and "generic" support are still experimental.)
nextpnr is an FPGA place and route tool with emphasis on supporting
timing-driven place and route for a wide range of real-world FPGA devices.
It currently supports Lattice iCE40 devices and Lattice ECP5 devices,
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
-------------
@ -144,9 +155,15 @@ Links and references
- [Project X-Ray (Xilinx 7-Series)](https://symbiflow.github.io/prjxray-db/)
- [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)
- [VPR/VTR](https://verilogtorouting.org/)
- [graywolf/timberwolf](https://github.com/rubund/graywolf)
- [qrouter](http://opencircuitdesign.com/qrouter/)
- [SymbiFlow](https://github.com/SymbiFlow/symbiflow-arch-defs)
- [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
for (auto port : user.cell->ports) {
if (port.second.type == PORT_OUT) {
delay_t comb_delay;
DelayInfo comb_delay;
// Look up delay through this path
bool is_path = ctx->getCellDelay(user.cell, user.port, port.first, comb_delay);
if (is_path) {
NetInfo *net = port.second.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);
}
}
@ -88,9 +88,9 @@ void assign_budget(Context *ctx)
IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
if (clock_domain != IdString()) {
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))
slack -= clkToQ;
slack -= clkToQ.maxDelay();
if (port.second.net)
follow_net(ctx, port.second.net, 0, slack);
}

View File

@ -21,6 +21,7 @@
#include <algorithm>
#include <cmath>
#include <cstring>
#include "gfx.h"
#include "log.h"
#include "nextpnr.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;
// 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;
}
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 {}; }
@ -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;
}

View File

@ -801,7 +801,7 @@ struct Arch : BaseCtx
// Get the delay through a cell from one port to another, returning false
// 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
IdString getPortClock(const CellInfo *cell, IdString port) const;
// Return true if a port is a clock

View File

@ -120,17 +120,21 @@ struct GroupId
struct DecalId
{
char type = 0; // Bel/Wire/Pip/Frame (b/w/p/f)
enum
{
TYPE_FRAME,
TYPE_BEL
} type;
Location location;
uint32_t z = 0;
bool active = false;
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
{
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<NEXTPNR_NAMESPACE_PREFIX Location>()(decal.location));
boost::hash_combine(seed, hash<int>()(decal.z));
boost::hash_combine(seed, hash<bool>()(decal.active));
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) {
std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git "
"sha1 " GIT_COMMIT_HASH_STR ")\n";
std::cout << boost::filesystem::basename(argv[0])
<< " -- Next Generation Place and Route (git "
"sha1 " GIT_COMMIT_HASH_STR ")\n";
std::cout << "\n";
std::cout << options << "\n";
return argc != 1;
}
if (vm.count("version")) {
std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git "
"sha1 " GIT_COMMIT_HASH_STR ")\n";
std::cout << boost::filesystem::basename(argv[0])
<< " -- Next Generation Place and Route (git "
"sha1 " GIT_COMMIT_HASH_STR ")\n";
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;
}

View File

@ -210,7 +210,7 @@ struct Arch : BaseCtx
DecalXY getPipDecal(PipId pip) 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;
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()->setSelectionMode(QAbstractItemView::ExtendedSelection);
QLineEdit *lineEdit = new QLineEdit();
lineEdit->setClearButtonEnabled(true);
lineEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition);
lineEdit->setPlaceholderText("Search...");
searchEdit = new QLineEdit();
searchEdit->setClearButtonEnabled(true);
searchEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition);
searchEdit->setPlaceholderText("Search...");
connect(searchEdit, SIGNAL(returnPressed()), this, SLOT(onSearchInserted()));
actionFirst = new QAction("", this);
actionFirst->setIcon(QIcon(":/icons/resources/resultset_first.png"));
@ -123,7 +124,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel
topWidget->setLayout(vbox1);
vbox1->setSpacing(5);
vbox1->setContentsMargins(0, 0, 0, 0);
vbox1->addWidget(lineEdit);
vbox1->addWidget(searchEdit);
vbox1->addWidget(treeView);
QWidget *toolbarWidget = new QWidget();
@ -214,7 +215,7 @@ void DesignWidget::newContext(Context *ctx)
highlightSelected.clear();
this->ctx = ctx;
treeModel->loadData(ctx);
treeModel->loadContext(ctx);
updateTree();
}
@ -234,7 +235,7 @@ void DesignWidget::updateTree()
}
}
treeModel->updateData(ctx);
treeModel->updateCellsNets(ctx);
}
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::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

View File

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

View File

@ -18,114 +18,48 @@
*/
#include "treemodel.h"
#include "log.h"
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) :
QAbstractItemModel(parent),
root_(new StaticTreeItem("Elements", nullptr)) {}
ContextTreeModel::~ContextTreeModel() {}
void ContextTreeModel::loadData(Context *ctx)
void ContextTreeModel::loadContext(Context *ctx)
{
if (!ctx)
return;
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;
for (auto bel : ctx->getBels()) {
auto loc = ctx->getBelLocation(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); };
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;
//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++) {
const auto wire = &ctx->chip_info->wire_data[i];
WireId wireid;
wireid.index = i;
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); };
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;
//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++) {
const auto pip = &ctx->chip_info->pip_data[i];
PipId pipid;
@ -136,80 +70,34 @@ void ContextTreeModel::loadData(Context *ctx)
auto pipGetter = [](Context *ctx, PipId id) { return ctx->getPipName(id); };
pip_root_ = std::unique_ptr<PipXYRoot>(new PipXYRoot(ctx, "Pips", root_.get(), pipMap, pipGetter));
}
#endif
//nets_root = new ContextTreeItem("Nets");
//root->addChild(nets_root);
//cells_root = new ContextTreeItem("Cells");
//root->addChild(cells_root);
cell_root_ = std::unique_ptr<IdStringList>(new IdStringList(QString("Cells"), root_.get()));
net_root_ = std::unique_ptr<IdStringList>(new IdStringList(QString("Nets"), root_.get()));
endResetModel();
updateCellsNets(ctx);
}
void ContextTreeModel::updateData(Context *ctx)
void ContextTreeModel::updateCellsNets(Context *ctx)
{
if (!ctx)
return;
beginResetModel();
//QModelIndex nets_index = indexFromNode(nets_root);
// Remove nets not existing any more
//QMap<QString, ContextTreeItem *>::iterator i = nameToItem[3].begin();
//while (i != nameToItem[3].end()) {
// QMap<QString, ContextTreeItem *>::iterator prev = i;
// ++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();
// }
//}
std::vector<IdString> cells;
for (auto &pair : ctx->cells) {
cells.push_back(pair.first);
}
cell_root_->updateElements(ctx, cells);
//nets_root->sort();
//QModelIndex cell_index = indexFromNode(cells_root);
// Remove cells not existing any more
//i = nameToItem[4].begin();
//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();
std::vector<IdString> nets;
for (auto &pair : ctx->nets) {
nets.push_back(pair.first);
}
net_root_->updateElements(ctx, nets);
endResetModel();
}
@ -277,22 +165,6 @@ static int getElementIndex(ElementType type)
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
{
LazyTreeItem *node = nodeFromIndex(index);
@ -310,4 +182,19 @@ bool ContextTreeModel::canFetchMore(const QModelIndex &parent) const
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

View File

@ -22,6 +22,7 @@
#include <QAbstractItemModel>
#include "nextpnr.h"
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
@ -36,31 +37,6 @@ enum class ElementType
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
{
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>
class ElementXYRoot : public StaticTreeItem
{
@ -217,7 +261,7 @@ class ElementXYRoot : public StaticTreeItem
private:
Context *ctx_;
std::vector<std::unique_ptr<LazyTreeItem>> bels_;
std::vector<std::unique_ptr<LazyTreeItem>> managed_;
ElementMap map_;
ElementGetter getter_;
@ -241,11 +285,11 @@ class ElementXYRoot : public StaticTreeItem
// create X item for tree
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) {
auto item2 = new ElementList<ElementT>(ctx_, QString("Y%1").arg(j), item, &map_, i, j, getter_);
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();
void loadData(Context *ctx);
void updateData(Context *ctx);
void loadContext(Context *ctx);
void updateCellsNets(Context *ctx);
LazyTreeItem *nodeFromIndex(const QModelIndex &idx) const;
//QModelIndex indexFromNode(ContextTreeItem *node);
//ContextTreeItem *nodeForIdType(const ElementType type, const QString name) const;
QList<QModelIndex> search(QString text);
// Override QAbstractItemModel methods
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<WireXYRoot> wire_root_;
std::unique_ptr<PipXYRoot> pip_root_;
//std::unique_ptr<ElementXYRoot> wires_root_;
//std::unique_ptr<ElementXYRoot> pips_root_;
//QMap<QString, ContextTreeItem *> nameToItem[6];
//ContextTreeItem *nets_root;
//ContextTreeItem *cells_root;
std::unique_ptr<IdStringList> cell_root_;
std::unique_ptr<IdStringList> net_root_;
};
NEXTPNR_NAMESPACE_END

View File

@ -141,18 +141,23 @@ Arch::Arch(ArchArgs args) : args(args)
#ifdef ICE40_HX1K_ONLY
if (args.type == ArchArgs::HX1K) {
fast_part = true;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_1k));
} else {
log_error("Unsupported iCE40 chip type.\n");
}
#else
if (args.type == ArchArgs::LP384) {
fast_part = false;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_384));
} 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));
} else if (args.type == ArchArgs::UP5K) {
fast_part = false;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_5k));
} 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));
} else {
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;
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
for (int i = 0; i < num_bel_wires; i++)
if (bel_wires[i].port == pin)
return PortType(bel_wires[i].type);
if (num_bel_wires < 7) {
for (int i = 0; i < num_bel_wires; i++) {
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;
}
@ -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;
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
for (int i = 0; i < num_bel_wires; i++) {
if (bel_wires[i].port == pin) {
ret.index = bel_wires[i].wire_index;
break;
if (num_bel_wires < 7) {
for (int i = 0; i < num_bel_wires; i++) {
if (bel_wires[i].port == pin) {
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 ((fromPort == id_i0 || fromPort == id_i1 || fromPort == id_i2 || fromPort == id_i3) &&
(toPort == id_o || toPort == id_lo)) {
delay = 450;
delay.delay = 450;
return true;
} else if (fromPort == id_cin && toPort == id_cout) {
delay = 120;
delay.delay = 120;
return true;
} else if (fromPort == id_i1 && toPort == id_cout) {
delay = 260;
delay.delay = 260;
return true;
} else if (fromPort == id_i2 && toPort == id_cout) {
delay = 230;
delay.delay = 230;
return true;
} else if (fromPort == id_clk && toPort == id_o) {
delay = 540;
delay.delay = 540;
return true;
}
} else if (cell->type == id_icestorm_ram) {
if (fromPort == id_rclk) {
delay = 2140;
delay.delay = 2140;
return true;
}
}

View File

@ -44,9 +44,9 @@ template <typename T> struct RelPtr
};
NPNR_PACKED_STRUCT(struct BelWirePOD {
int32_t wire_index;
PortPin port;
int32_t type;
int32_t wire_index;
});
NPNR_PACKED_STRUCT(struct BelInfoPOD {
@ -66,7 +66,8 @@ NPNR_PACKED_STRUCT(struct BelPortPOD {
NPNR_PACKED_STRUCT(struct PipInfoPOD {
// RelPtr<char> name;
int32_t src, dst;
int32_t delay;
int32_t fast_delay;
int32_t slow_delay;
int8_t x, y;
int16_t src_seg, dst_seg;
int16_t switch_mask;
@ -89,6 +90,9 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD {
int32_t num_segments;
RelPtr<WireSegmentPOD> segments;
int32_t fast_delay;
int32_t slow_delay;
int8_t x, y;
WireType type;
int8_t padding_0;
@ -344,6 +348,7 @@ struct ArchArgs
struct Arch : BaseCtx
{
bool fast_part;
const ChipInfoPOD *chip_info;
const PackageInfoPOD *package_info;
@ -524,6 +529,11 @@ struct Arch : BaseCtx
DelayInfo getWireDelay(WireId wire) const
{
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;
}
@ -637,7 +647,10 @@ struct Arch : BaseCtx
{
DelayInfo delay;
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;
}
@ -709,7 +722,7 @@ struct Arch : BaseCtx
// Get the delay through a cell from one port to another, returning false
// 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
IdString getPortClock(const CellInfo *cell, IdString port) const;
// Return true if a port is a clock

View File

@ -1,3 +1,5 @@
SHELL = /bin/bash
reports::
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
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
../../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
$(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",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import subprocess\n",
"import subprocess, re\n",
"\n",
"gitrev = subprocess.getoutput(\"git rev-parse --short HEAD\")\n",
"\n",
"data_a = 1 + np.zeros(10)\n",
"data_n = 1 + np.zeros(10)\n",
"data_a = np.zeros((10, 2))\n",
"data_n = np.zeros((10, 2))\n",
"\n",
"for i in range(10):\n",
" try:\n",
" with open(\"report_a%d.txt\" % i, \"r\") as f:\n",
" for line in f:\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",
" pass\n",
" data_a[i, 0] = 1.0\n",
" \n",
" try:\n",
" with open(\"report_n%d.txt\" % i, \"r\") as f:\n",
" for line in f:\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",
" 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",
"plt.figure(figsize=(9,3))\n",
"plt.title(\"nextpnr -- ice40/benchmark/ -- %s\" % gitrev)\n",
"plt.bar(np.arange(10), data_a, color='blue')\n",
"plt.bar(15+np.arange(10), data_n, color='red')\n",
"plt.bar(np.arange(10), data_a[:, 0], color='blue')\n",
"plt.bar(15+np.arange(10), data_n[:, 0], color='red')\n",
"plt.ylabel('Longest path (ns)')\n",
"plt.xticks([5, 20], [\"arachne-pnr\", \"nextpnr\"])\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()"
]
}

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("-p", "--portspins", type=str, help="path to portpins.inc")
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()
dev_name = None
@ -51,6 +53,9 @@ wiretypes = dict()
gfx_wire_ids = dict()
wire_segments = dict()
fast_timings = None
slow_timings = None
with open(args.portspins) as f:
for line in f:
line = line.replace("(", " ")
@ -77,6 +82,31 @@ with open(args.gfxh) as f:
name = line.strip().rstrip(",")
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_RAM"] = 2
beltypes["SB_IO"] = 3
@ -184,46 +214,75 @@ def wire_type(name):
assert 0
return wt
def pipdelay(src, dst):
src = wire_names_r[src]
dst = wire_names_r[dst]
def pipdelay(src_idx, dst_idx, db):
if db is None:
return 0
src = wire_names_r[src_idx]
dst = wire_names_r[dst_idx]
src_type = wire_type(src[2])
dst_type = wire_type(dst[2])
if src_type == "LOCAL" and dst_type == "LOCAL":
return 250
if dst[2].startswith("sp4_") or dst[2].startswith("span4_"):
if src[2].startswith("sp12_") or src[2].startswith("span12_"):
return db["Sp12to4.I.O"]
if src_type == "GLOBAL" and dst_type == "LOCAL":
return 400
if src[2].startswith("span4_"):
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"):
return 350
if dst[2].startswith("sp12_") or dst[2].startswith("span12_"):
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"):
return 500
if dst[2] in ("fabout", "clk"):
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":
return 300
if dst[2] == "carry_in_mux":
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"):
return 450
if dst[2] in ("lutff_global/s_r", "io_global/latch", "ram/RE", "ram/WE"):
return db["SRMux.I.O"]
if src_type in ("SP4_HORZ", "SP4_VERT") and dst_type in ("SP4_HORZ", "SP4_VERT"):
return 300
if dst[2] in ("lutff_global/cen", "io_global/cen", "ram/RCLKE", "ram/WCLKE"):
return db["CEMux.I.O"]
if src_type in ("SP12_HORZ", "SP12_VERT") and dst_type in ("SP4_HORZ", "SP4_VERT"):
return 380
if dst[2].startswith("local_"):
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
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):
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:
wire_belports[wire] = set()
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):
if wire not in wire_belports:
wire_belports[wire] = set()
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):
bel = len(bel_name)
@ -715,14 +774,12 @@ bba.post('NEXTPNR_NAMESPACE_END')
bba.push("chipdb_blob_%s" % dev_name)
bba.r("chip_info_%s" % dev_name, "chip_info")
index = 0
for bel in range(len(bel_name)):
bba.l("bel_wires_%d" % bel, "BelWirePOD")
for i in range(len(bel_wires[bel])):
bba.u32(bel_wires[bel][i][0], "wire_index")
bba.u32(portpins[bel_wires[bel][i][1]], "port")
bba.u32(bel_wires[bel][i][2], "type")
index += 1
for data in sorted(bel_wires[bel]):
bba.u32(data[0], "port")
bba.u32(data[1], "type")
bba.u32(data[2], "wire_index")
bba.l("bel_data_%s" % dev_name, "BelInfoPOD")
for bel in range(len(bel_name)):
@ -748,7 +805,8 @@ for wire in range(num_wires):
pi = dict()
pi["src"] = src
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["y"] = pip_xy[(src, wire)][1]
pi["switch_mask"] = pip_xy[(src, wire)][2]
@ -772,7 +830,8 @@ for wire in range(num_wires):
pi = dict()
pi["src"] = wire
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["y"] = pip_xy[(wire, dst)][1]
pi["switch_mask"] = pip_xy[(wire, dst)][2]
@ -891,6 +950,9 @@ for wire, info in enumerate(wireinfo):
else:
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["y"], "y")
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.u32(info["src"], "src")
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["y"], "y")
bba.u16(src_seg, "src_seg")

View File

@ -14,17 +14,28 @@ file(MAKE_DIRECTORY ice40/chipdbs/)
add_library(ice40_chipdb OBJECT ice40/chipdbs/)
target_compile_definitions(ice40_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family})
target_include_directories(ice40_chipdb PRIVATE ${family}/)
if (MSVC)
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)
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_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_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
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}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
@ -40,13 +51,23 @@ if (MSVC)
else()
target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w)
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_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_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
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}
DEPENDS ${DEV_TXT_DB} ${DB_PY}
)

View File

@ -114,17 +114,58 @@ class PlacementLegaliser
public:
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()
{
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();
bool legalised_carries = legalise_carries();
if (!legalised_carries && !ctx->force)
return false;
print_stats("carry legalisation");
legalise_others();
print_stats("misc. cell legalisation");
legalise_logic_tiles();
print_stats("logic cell legalisation");
bool replaced_cells = replace_cells();
print_stats("cell replacement");
ctx->assignArchInfo();
return legalised_carries && replaced_cells;
}
@ -501,6 +542,7 @@ class PlacementLegaliser
Context *ctx;
std::unordered_set<IdString> rippedCells;
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
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))