Merge branch 'master' into q3k/treemodel-fast
This commit is contained in:
commit
6241052e11
@ -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})
|
||||||
|
31
README.md
31
README.md
@ -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.
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
59
ecp5/arch.cc
59
ecp5/arch.cc
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
35
ecp5/gfx.h
Normal 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
|
10
ecp5/main.cc
10
ecp5/main.cc
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
189
gui/treemodel.cc
189
gui/treemodel.cc
@ -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
|
||||||
|
114
gui/treemodel.h
114
gui/treemodel.h
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
ice40/arch.h
21
ice40/arch.h
@ -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
|
||||||
|
@ -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))))
|
||||||
|
@ -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()"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
131
ice40/chipdb.py
131
ice40/chipdb.py
@ -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")
|
||||||
|
@ -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}
|
||||||
)
|
)
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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")
|
|
@ -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))
|
|
@ -1,2 +0,0 @@
|
|||||||
for wire in chip.getWires():
|
|
||||||
print(chip.getWireName(wire))
|
|
Loading…
Reference in New Issue
Block a user