Merge remote-tracking branch 'origin/master' into redist_slack

This commit is contained in:
Eddie Hung 2018-07-30 18:14:40 -07:00
commit 46b7469652
22 changed files with 259 additions and 91 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,10 @@ 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)

View File

@ -91,6 +91,8 @@ WireId Context::getNetinfoSinkWire(NetInfo *net_info, int user_idx) const
delay_t Context::getNetinfoRouteDelay(NetInfo *net_info, int user_idx) const delay_t Context::getNetinfoRouteDelay(NetInfo *net_info, int user_idx) const
{ {
WireId src_wire = getNetinfoSourceWire(net_info); WireId src_wire = getNetinfoSourceWire(net_info);
if (src_wire == WireId())
return 0;
WireId cursor = getNetinfoSinkWire(net_info, user_idx); WireId cursor = getNetinfoSinkWire(net_info, user_idx);
delay_t delay = 0; delay_t delay = 0;

View File

@ -51,13 +51,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, update, min_slack, delay_t path_budget = follow_net(ctx, net, path_length, slack - comb_delay.maxDelay(), update, min_slack,
current_path, crit_path); current_path, crit_path);
value = std::min(value, path_budget); value = std::min(value, path_budget);
} }
@ -110,9 +110,9 @@ static delay_t walk_paths(Context *ctx, bool update, PortRefList *crit_path)
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 = default_slack; // TODO: clock constraints delay_t slack = default_slack; // 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, update, min_slack, &current_path, crit_path); follow_net(ctx, port.second.net, 0, slack, update, min_slack, &current_path, crit_path);
} }
@ -197,12 +197,12 @@ delay_t timing_analysis(Context *ctx, bool print_fmax, bool print_path)
++i; ++i;
auto &driver = net->driver; auto &driver = net->driver;
auto driver_cell = driver.cell; auto driver_cell = driver.cell;
delay_t comb_delay; DelayInfo comb_delay;
ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay); ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
total += comb_delay; total += comb_delay.maxDelay();
log_info("%4d %4d Source %s.%s\n", comb_delay, total, driver_cell->name.c_str(ctx), log_info("%4d %4d Source %s.%s\n", comb_delay.maxDelay(), total, driver_cell->name.c_str(ctx),
driver.port.c_str(ctx)); driver.port.c_str(ctx));
delay_t net_delay = ctx->getNetinfoRouteDelay(net, i); auto net_delay = ctx->getNetinfoRouteDelay(net, i);
total += net_delay; total += net_delay;
auto driver_loc = ctx->getBelLocation(driver_cell->bel); auto driver_loc = ctx->getBelLocation(driver_cell->bel);
auto sink_loc = ctx->getBelLocation(sink_cell->bel); auto sink_loc = ctx->getBelLocation(sink_cell->bel);

View File

@ -442,7 +442,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

@ -802,7 +802,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

@ -427,7 +427,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

@ -211,7 +211,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();
@ -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<ContextTreeItem *, int> highlightSelected; QMap<ContextTreeItem *, int> highlightSelected;
QString currentSearch;
QList<QModelIndex> currentSearchIndexes;
int currentIndex;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -326,4 +326,19 @@ Qt::ItemFlags ContextTreeModel::flags(const QModelIndex &index) const
ContextTreeItem *node = nodeFromIndex(index); ContextTreeItem *node = nodeFromIndex(index);
return Qt::ItemIsEnabled | (node->type() != ElementType::NONE ? Qt::ItemIsSelectable : Qt::NoItemFlags); return Qt::ItemIsEnabled | (node->type() != ElementType::NONE ? Qt::ItemIsSelectable : Qt::NoItemFlags);
} }
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

@ -72,6 +72,7 @@ class ContextTreeModel : public QAbstractItemModel
ContextTreeItem *nodeFromIndex(const QModelIndex &idx) const; ContextTreeItem *nodeFromIndex(const QModelIndex &idx) const;
QModelIndex indexFromNode(ContextTreeItem *node); QModelIndex indexFromNode(ContextTreeItem *node);
ContextTreeItem *nodeForIdType(const ElementType type, const QString name) const; ContextTreeItem *nodeForIdType(const ElementType type, const QString name) const;
QList<QModelIndex> search(QString text);
// 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;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;

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");
@ -801,29 +806,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

@ -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;
} }
@ -710,7 +723,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,12 +214,18 @@ def wire_type(name):
assert 0 assert 0
return wt return wt
def pipdelay(src, dst): def pipdelay(src, dst, db):
if db is None:
return 0
src = wire_names_r[src] src = wire_names_r[src]
dst = wire_names_r[dst] dst = wire_names_r[dst]
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 dst[2].startswith("local_"):
return db["LocalMux.I.O"]
if src_type == "LOCAL" and dst_type == "LOCAL": if src_type == "LOCAL" and dst_type == "LOCAL":
return 250 return 250
@ -223,7 +259,15 @@ def pipdelay(src, dst):
# print(src, dst, src_type, dst_type, file=sys.stderr) # print(src, dst, src_type, dst_type, file=sys.stderr)
assert 0 assert 0
def wiredelay(wire, db):
if db is None:
return 0
wire = wire_names_r[wire]
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
@ -748,7 +792,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 +817,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 +937,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 +972,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))