Compare commits

...

14 Commits

Author SHA1 Message Date
Marcin Kościelnicki
bd69a7c113 oddr2 fixes 2020-02-03 01:22:52 +01:00
Marcin Kościelnicki
7f437cbf62 ODDR2 2020-02-03 00:56:39 +01:00
Marcin Kościelnicki
5db6939a8e pll 2020-02-02 22:30:40 +01:00
Marcin Kościelnicki
0cb4f508f7 stolen router2 2020-02-02 17:34:36 +01:00
Marcin Kościelnicki
1c9cf8adb1 new packer 2020-02-02 16:19:50 +01:00
Miodrag Milanovic
1d241b1d3f Better formatting 2020-02-02 14:56:16 +01:00
Miodrag Milanovic
71b9d146ce Add spent time info to report 2020-02-02 14:56:15 +01:00
Marcin Kościelnicki
270a0c4088 fix 2020-02-02 14:55:59 +01:00
Marcin Kościelnicki
14ce3d2029 dco 2020-02-02 14:36:29 +01:00
Marcin Kościelnicki
7fc7d5057b RAM32M 2020-02-02 09:53:45 +01:00
Marcin Kościelnicki
b4ee8fc639 wip 2020-02-02 09:53:45 +01:00
Marcin Kościelnicki
20dfebb09f wip 2020-02-02 09:53:45 +01:00
Marcin Kościelnicki
1c9f7e2113 wip stuff 2020-02-02 09:53:45 +01:00
Marcin Kościelnicki
048bae1d85 wip 2020-02-02 09:53:45 +01:00
30 changed files with 6579 additions and 64 deletions

View File

@ -44,7 +44,7 @@ if (EXTERNAL_CHIPDB)
endif() endif()
# List of families to build # List of families to build
set(FAMILIES generic ice40 ecp5) set(FAMILIES generic ice40 ecp5 leuctra)
set(ARCH "" CACHE STRING "Architecture family for nextpnr build") set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES}) set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
@ -67,6 +67,11 @@ foreach(item ${ARCH})
if (NOT item IN_LIST FAMILIES) if (NOT item IN_LIST FAMILIES)
message(FATAL_ERROR "Architecture '${item}' not in list of supported architectures") message(FATAL_ERROR "Architecture '${item}' not in list of supported architectures")
endif() endif()
if (item STREQUAL "leuctra")
if (NOT EXTERNAL_CHIPDB)
message(FATAL_ERROR "The leuctra backend requires setting -DEXTERNAL_CHIPDB=ON")
endif()
endif()
endforeach() endforeach()
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)

View File

@ -111,6 +111,7 @@ void disconnect_port(const Context *ctx, CellInfo *cell, IdString port_name)
port.net->users.end()); port.net->users.end());
if (port.net->driver.cell == cell && port.net->driver.port == port_name) if (port.net->driver.cell == cell && port.net->driver.port == port_name)
port.net->driver.cell = nullptr; port.net->driver.cell = nullptr;
port.net = nullptr;
} }
} }

View File

@ -199,6 +199,28 @@ struct Loc
bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z != other.z); } bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z != other.z); }
}; };
struct ArcBounds
{
int x0 = -1, y0 = -1, x1 = -1, y1 = -1;
ArcBounds() {}
ArcBounds(int x0, int y0, int x1, int y1) : x0(x0), y0(y0), x1(x1), y1(y1){};
int distance(Loc loc) const
{
int dist = 0;
if (loc.x < x0)
dist += x0 - loc.x;
if (loc.x > x1)
dist += loc.x - x1;
if (loc.y < y0)
dist += y0 - loc.y;
if (loc.y > y1)
dist += loc.y - y1;
return dist;
};
};
struct TimingConstrObjectId struct TimingConstrObjectId
{ {
int32_t index = -1; int32_t index = -1;
@ -443,6 +465,7 @@ struct CellInfo : ArchCellInfo
int constr_y = UNCONSTR; // this.y - parent.y int constr_y = UNCONSTR; // this.y - parent.y
int constr_z = UNCONSTR; // this.z - parent.z int constr_z = UNCONSTR; // this.z - parent.z
bool constr_abs_z = false; // parent.z := 0 bool constr_abs_z = false; // parent.z := 0
int constr_spec = -1;
// parent.[xyz] := 0 when (constr_parent == nullptr) // parent.[xyz] := 0 when (constr_parent == nullptr)
Region *region = nullptr; Region *region = nullptr;

View File

@ -234,7 +234,7 @@ class ConstraintLegaliseWorker
} }
if (!ctx->checkBelAvail(locBel)) { if (!ctx->checkBelAvail(locBel)) {
CellInfo *confCell = ctx->getConflictingBelCell(locBel); CellInfo *confCell = ctx->getConflictingBelCell(locBel);
if (confCell->belStrength >= STRENGTH_STRONG) { if (confCell != cell && confCell->belStrength >= STRENGTH_STRONG) {
return false; return false;
} }
} }
@ -247,49 +247,59 @@ class ConstraintLegaliseWorker
usedLocations.insert(loc); usedLocations.insert(loc);
for (auto child : cell->constr_children) { for (auto child : cell->constr_children) {
IncreasingDiameterSearch xSearch, ySearch, zSearch; IncreasingDiameterSearch xSearch, ySearch, zSearch;
if (child->constr_x == child->UNCONSTR) {
xSearch = IncreasingDiameterSearch(loc.x, 0, ctx->getGridDimX() - 1);
} else {
xSearch = IncreasingDiameterSearch(loc.x + child->constr_x);
}
if (child->constr_y == child->UNCONSTR) {
ySearch = IncreasingDiameterSearch(loc.y, 0, ctx->getGridDimY() - 1);
} else {
ySearch = IncreasingDiameterSearch(loc.y + child->constr_y);
}
if (child->constr_z == child->UNCONSTR) {
zSearch = IncreasingDiameterSearch(loc.z, 0, ctx->getTileBelDimZ(loc.x, loc.y));
} else {
if (child->constr_abs_z) {
zSearch = IncreasingDiameterSearch(child->constr_z);
} else {
zSearch = IncreasingDiameterSearch(loc.z + child->constr_z);
}
}
bool success = false; bool success = false;
while (!xSearch.done()) { if (child->constr_spec != -1) {
Loc cloc; BelId child_bel = ctx->getRelatedBel(locBel, child->constr_spec);
cloc.x = xSearch.get(); if (child_bel != BelId()) {
cloc.y = ySearch.get(); Loc cloc = ctx->getBelLocation(child_bel);
cloc.z = zSearch.get(); if (!usedLocations.count(cloc) && valid_loc_for(child, cloc, solution, usedLocations)) {
success = true;
zSearch.next(); }
if (zSearch.done()) { }
zSearch.reset(); } else {
ySearch.next(); if (child->constr_x == child->UNCONSTR) {
if (ySearch.done()) { xSearch = IncreasingDiameterSearch(loc.x, 0, ctx->getGridDimX() - 1);
ySearch.reset(); } else {
xSearch.next(); xSearch = IncreasingDiameterSearch(loc.x + child->constr_x);
}
if (child->constr_y == child->UNCONSTR) {
ySearch = IncreasingDiameterSearch(loc.y, 0, ctx->getGridDimY() - 1);
} else {
ySearch = IncreasingDiameterSearch(loc.y + child->constr_y);
}
if (child->constr_z == child->UNCONSTR) {
zSearch = IncreasingDiameterSearch(loc.z, 0, ctx->getTileBelDimZ(loc.x, loc.y));
} else {
if (child->constr_abs_z) {
zSearch = IncreasingDiameterSearch(child->constr_z);
} else {
zSearch = IncreasingDiameterSearch(loc.z + child->constr_z);
} }
} }
while (!xSearch.done()) {
if (usedLocations.count(cloc)) Loc cloc;
continue; cloc.x = xSearch.get();
if (valid_loc_for(child, cloc, solution, usedLocations)) { cloc.y = ySearch.get();
success = true; cloc.z = zSearch.get();
break;
zSearch.next();
if (zSearch.done()) {
zSearch.reset();
ySearch.next();
if (ySearch.done()) {
ySearch.reset();
xSearch.next();
}
}
if (usedLocations.count(cloc))
continue;
if (valid_loc_for(child, cloc, solution, usedLocations)) {
success = true;
break;
}
} }
} }
if (!success) { if (!success) {
usedLocations.erase(loc); usedLocations.erase(loc);
return false; return false;
@ -316,7 +326,7 @@ class ConstraintLegaliseWorker
return true; // Only process chain roots return true; // Only process chain roots
if (constraints_satisfied(cell)) { if (constraints_satisfied(cell)) {
if (cell->constr_children.size() > 0 || cell->constr_x != cell->UNCONSTR || if (cell->constr_children.size() > 0 || cell->constr_x != cell->UNCONSTR ||
cell->constr_y != cell->UNCONSTR || cell->constr_z != cell->UNCONSTR) cell->constr_y != cell->UNCONSTR || cell->constr_z != cell->UNCONSTR || cell->constr_spec != -1)
lockdown_chain(cell); lockdown_chain(cell);
} else { } else {
IncreasingDiameterSearch xRootSearch, yRootSearch, zRootSearch; IncreasingDiameterSearch xRootSearch, yRootSearch, zRootSearch;
@ -513,16 +523,26 @@ int get_constraints_distance(const Context *ctx, const CellInfo *cell)
if (cell->constr_parent->bel == BelId()) if (cell->constr_parent->bel == BelId())
return 100000; return 100000;
Loc parent_loc = ctx->getBelLocation(cell->constr_parent->bel); Loc parent_loc = ctx->getBelLocation(cell->constr_parent->bel);
if (cell->constr_x != cell->UNCONSTR) if (cell->constr_spec != -1) {
dist += std::abs(cell->constr_x - (loc.x - parent_loc.x)); BelId child_bel = ctx->getRelatedBel(cell->constr_parent->bel, cell->constr_spec);
if (cell->constr_y != cell->UNCONSTR) if (child_bel == BelId())
dist += std::abs(cell->constr_y - (loc.y - parent_loc.y)); return 100000;
if (cell->constr_z != cell->UNCONSTR) { Loc child_loc = ctx->getBelLocation(child_bel);
if (cell->constr_abs_z) dist += std::abs(child_loc.x - loc.x);
dist += std::abs(cell->constr_z - loc.z); dist += std::abs(child_loc.y - loc.y);
else dist += std::abs(child_loc.z - loc.z);
dist += std::abs(cell->constr_z - (loc.z - parent_loc.z)); } else {
} if (cell->constr_x != cell->UNCONSTR)
dist += std::abs(cell->constr_x - (loc.x - parent_loc.x));
if (cell->constr_y != cell->UNCONSTR)
dist += std::abs(cell->constr_y - (loc.y - parent_loc.y));
if (cell->constr_z != cell->UNCONSTR) {
if (cell->constr_abs_z)
dist += std::abs(cell->constr_z - loc.z);
else
dist += std::abs(cell->constr_z - (loc.z - parent_loc.z));
}
}
} }
for (auto child : cell->constr_children) for (auto child : cell->constr_children)
dist += get_constraints_distance(ctx, child); dist += get_constraints_distance(ctx, child);

View File

@ -960,12 +960,18 @@ class HeAPPlacer
targets.emplace_back(vc, target); targets.emplace_back(vc, target);
for (auto child : vc->constr_children) { for (auto child : vc->constr_children) {
Loc cloc = ploc; Loc cloc = ploc;
if (child->constr_spec != -1) {
BelId base_bel = ctx->getBelByLocation(ploc);
BelId child_bel = ctx->getRelatedBel(base_bel, child->constr_spec);
cloc = ctx->getBelLocation(child_bel);
} else {
if (child->constr_x != child->UNCONSTR) if (child->constr_x != child->UNCONSTR)
cloc.x += child->constr_x; cloc.x += child->constr_x;
if (child->constr_y != child->UNCONSTR) if (child->constr_y != child->UNCONSTR)
cloc.y += child->constr_y; cloc.y += child->constr_y;
if (child->constr_z != child->UNCONSTR) if (child->constr_z != child->UNCONSTR)
cloc.z = child->constr_abs_z ? child->constr_z : (ploc.z + child->constr_z); cloc.z = child->constr_abs_z ? child->constr_z : (ploc.z + child->constr_z);
}
visit.emplace(child, cloc); visit.emplace(child, cloc);
} }
} }

View File

@ -497,7 +497,8 @@ struct Router1
} }
qw.randtag = ctx->rng(); qw.randtag = ctx->rng();
queue.push(qw); if (src_wire != dst_wire)
queue.push(qw);
visited[qw.wire] = qw; visited[qw.wire] = qw;
} }
@ -769,14 +770,19 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
int last_arcs_with_ripup = 0; int last_arcs_with_ripup = 0;
int last_arcs_without_ripup = 0; int last_arcs_without_ripup = 0;
log_info(" | (re-)routed arcs | delta | remaining\n"); log_info(" | (re-)routed arcs | delta | remaining| time spent |\n");
log_info(" IterCnt | w/ripup wo/ripup | w/r wo/r | arcs\n"); log_info(" IterCnt | w/ripup wo/ripup | w/r wo/r | arcs| batch(sec)| total(sec)|\n");
auto prev_time = rstart;
while (!router.arc_queue.empty()) { while (!router.arc_queue.empty()) {
if (++iter_cnt % 1000 == 0) { if (++iter_cnt % 1000 == 0) {
log_info("%10d | %8d %10d | %4d %5d | %9d\n", iter_cnt, router.arcs_with_ripup, auto curr_time = std::chrono::high_resolution_clock::now();
log_info("%10d | %8d %10d | %4d %5d | %9d| %10.02f| %10.02f|\n", iter_cnt, router.arcs_with_ripup,
router.arcs_without_ripup, router.arcs_with_ripup - last_arcs_with_ripup, router.arcs_without_ripup, router.arcs_with_ripup - last_arcs_with_ripup,
router.arcs_without_ripup - last_arcs_without_ripup, int(router.arc_queue.size())); router.arcs_without_ripup - last_arcs_without_ripup, int(router.arc_queue.size()),
std::chrono::duration<float>(curr_time - prev_time).count(),
std::chrono::duration<float>(curr_time - rstart).count());
prev_time = curr_time;
last_arcs_with_ripup = router.arcs_with_ripup; last_arcs_with_ripup = router.arcs_with_ripup;
last_arcs_without_ripup = router.arcs_without_ripup; last_arcs_without_ripup = router.arcs_without_ripup;
ctx->yield(); ctx->yield();
@ -800,12 +806,13 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
return false; return false;
} }
} }
log_info("%10d | %8d %10d | %4d %5d | %9d\n", iter_cnt, router.arcs_with_ripup, router.arcs_without_ripup,
router.arcs_with_ripup - last_arcs_with_ripup, router.arcs_without_ripup - last_arcs_without_ripup,
int(router.arc_queue.size()));
log_info("Routing complete.\n");
auto rend = std::chrono::high_resolution_clock::now(); auto rend = std::chrono::high_resolution_clock::now();
log_info("%10d | %8d %10d | %4d %5d | %9d| %10.02f| %10.02f|\n", iter_cnt, router.arcs_with_ripup,
router.arcs_without_ripup, router.arcs_with_ripup - last_arcs_with_ripup,
router.arcs_without_ripup - last_arcs_without_ripup, int(router.arc_queue.size()),
std::chrono::duration<float>(rend - prev_time).count(),
std::chrono::duration<float>(rend - rstart).count());
log_info("Routing complete.\n");
ctx->yield(); ctx->yield();
log_info("Route time %.02fs\n", std::chrono::duration<float>(rend - rstart).count()); log_info("Route time %.02fs\n", std::chrono::duration<float>(rend - rstart).count());

1066
common/router2.cc Normal file

File diff suppressed because it is too large Load Diff

26
common/router2.h Normal file
View File

@ -0,0 +1,26 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2019 David Shah <dave@ds0.me>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
void router2(Context *ctx);
NEXTPNR_NAMESPACE_END

View File

@ -175,12 +175,23 @@ struct Timing
// Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and // Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and
// the current output port, increment fanin counter // the current output port, increment fanin counter
int found = 0;
for (auto i : input_ports) { for (auto i : input_ports) {
DelayInfo comb_delay; DelayInfo comb_delay;
bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay);
if (is_path) if (is_path) {
port_fanin[o]++; port_fanin[o]++;
found++;
}
} }
if (!found && portClass == TMG_COMB_OUTPUT) {
// log_warning("fake root %s\n", o->net->name.c_str(ctx));
topographical_order.emplace_back(o->net);
TimingData td;
td.false_startpoint = true;
td.max_arrival = 0;
net_data[o->net][ClockEvent{async_clock, RISING_EDGE}] = td;
}
} }
} }
} }

View File

@ -607,6 +607,8 @@ struct Arch : BaseCtx
return id; return id;
} }
BelId getRelatedBel(BelId bel, int relation) const { return BelId(); }
std::vector<std::pair<IdString, std::string>> getBelAttrs(BelId) const std::vector<std::pair<IdString, std::string>> getBelAttrs(BelId) const
{ {
std::vector<std::pair<IdString, std::string>> ret; std::vector<std::pair<IdString, std::string>> ret;

View File

@ -203,6 +203,7 @@ struct Arch : BaseCtx
CellInfo *getConflictingBelCell(BelId bel) const; CellInfo *getConflictingBelCell(BelId bel) const;
const std::vector<BelId> &getBels() const; const std::vector<BelId> &getBels() const;
IdString getBelType(BelId bel) const; IdString getBelType(BelId bel) const;
BelId getRelatedBel(BelId bel, int relation) const { return BelId(); }
const std::map<IdString, std::string> &getBelAttrs(BelId bel) const; const std::map<IdString, std::string> &getBelAttrs(BelId bel) const;
WireId getBelPinWire(BelId bel, IdString pin) const; WireId getBelPinWire(BelId bel, IdString pin) const;
PortType getBelPinType(BelId bel, IdString pin) const; PortType getBelPinType(BelId bel, IdString pin) const;

View File

@ -313,7 +313,7 @@ void DesignWidget::newContext(Context *ctx)
wireMap[std::pair<int, int>(wire->x, wire->y)].push_back(wireid); wireMap[std::pair<int, int>(wire->x, wire->y)].push_back(wireid);
} }
#endif #endif
#ifdef ARCH_ECP5 #if defined(ARCH_ECP5) || defined(ARCH_LEUCTRA)
for (const auto &wire : ctx->getWires()) { for (const auto &wire : ctx->getWires()) {
wireMap[std::pair<int, int>(wire.location.x, wire.location.y)].push_back(wire); wireMap[std::pair<int, int>(wire.location.x, wire.location.y)].push_back(wire);
} }

0
gui/leuctra/family.cmake Normal file
View File

93
gui/leuctra/mainwindow.cc Normal file
View File

@ -0,0 +1,93 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@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.
*
*/
#include "mainwindow.h"
#include <fstream>
#include "log.h"
#include <QFileDialog>
#include <QInputDialog>
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN
MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
: BaseMainWindow(std::move(context), handler, parent)
{
initMainResource();
std::string title = "nextpnr-leuctra - [EMPTY]";
setWindowTitle(title.c_str());
connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext);
createMenu();
}
MainWindow::~MainWindow() {}
void MainWindow::newContext(Context *ctx)
{
std::string title = "nextpnr-leuctra - " + ctx->getChipName();
setWindowTitle(title.c_str());
}
void MainWindow::createMenu() {
// Add arch specific actions
actionLoadUCF = new QAction("Open UCF", this);
actionLoadUCF->setIcon(QIcon(":/icons/resources/open_ucf.png"));
actionLoadUCF->setStatusTip("Open UCF file");
actionLoadUCF->setEnabled(true);
connect(actionLoadUCF, &QAction::triggered, this, &MainWindow::open_ucf);
// Add actions in menus
mainActionBar->addSeparator();
mainActionBar->addAction(actionLoadUCF);
menuDesign->addSeparator();
menuDesign->addAction(actionLoadUCF);
}
void MainWindow::new_proj() {
QMap<QString, int> family;
family.insert("Spartan 6", Arch::FAMILY_SPARTAN6);
bool ok;
QString item = QInputDialog::getItem(this, "Select new context", "Family:", family.keys(), 0, false, &ok);
if (ok && !item.isEmpty()) {
}
}
void MainWindow::open_ucf()
{
QString fileName = QFileDialog::getOpenFileName(this, QString("Open UCF"), QString(), QString("*.ucf"));
if (!fileName.isEmpty()) {
std::ifstream in(fileName.toStdString());
if (ctx->applyUCF(fileName.toStdString(), in)) {
log("Loading UCF successful.\n");
actionPack->setEnabled(true);
actionLoadUCF->setEnabled(false);
} else {
actionLoadUCF->setEnabled(true);
log("Loading UCF failed.\n");
}
}
}
NEXTPNR_NAMESPACE_END

49
gui/leuctra/mainwindow.h Normal file
View File

@ -0,0 +1,49 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@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 MAINWINDOW_H
#define MAINWINDOW_H
#include "../basewindow.h"
NEXTPNR_NAMESPACE_BEGIN
class MainWindow : public BaseMainWindow
{
Q_OBJECT
public:
explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~MainWindow();
public:
void createMenu();
protected Q_SLOTS:
void new_proj() override;
void newContext(Context *ctx);
void open_ucf();
private:
QAction *actionLoadUCF;
};
NEXTPNR_NAMESPACE_END
#endif // MAINWINDOW_H

2
gui/leuctra/nextpnr.qrc Normal file
View File

@ -0,0 +1,2 @@
<RCC>
</RCC>

View File

@ -523,6 +523,8 @@ struct Arch : BaseCtx
return IdString(chip_info->bel_data[bel.index].type); return IdString(chip_info->bel_data[bel.index].type);
} }
BelId getRelatedBel(BelId bel, int relation) const { return BelId(); }
std::vector<std::pair<IdString, std::string>> getBelAttrs(BelId bel) const; std::vector<std::pair<IdString, std::string>> getBelAttrs(BelId bel) const;
WireId getBelPinWire(BelId bel, IdString pin) const; WireId getBelPinWire(BelId bel, IdString pin) const;

1026
leuctra/arch.cc Normal file

File diff suppressed because it is too large Load Diff

1202
leuctra/arch.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <dave@ds0.me>
*
* 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 NO_PYTHON
#include "arch_pybindings.h"
#include "nextpnr.h"
#include "pybindings.h"
NEXTPNR_NAMESPACE_BEGIN
void arch_wrap_python()
{
using namespace PythonConversion;
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
.def("checksum", &Context::checksum)
.def("pack", &Context::pack)
.def("place", &Context::place)
.def("route", &Context::route);
}
NEXTPNR_NAMESPACE_END
#endif

83
leuctra/arch_pybindings.h Normal file
View File

@ -0,0 +1,83 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* 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 ARCH_PYBINDINGS_H
#define ARCH_PYBINDINGS_H
#ifndef NO_PYTHON
#include "nextpnr.h"
#include "pybindings.h"
NEXTPNR_NAMESPACE_BEGIN
namespace PythonConversion {
template <> struct string_converter<BelId>
{
BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); }
std::string to_str(Context *ctx, BelId id)
{
if (id == BelId())
throw bad_wrap();
return ctx->getBelName(id).str(ctx);
}
};
template <> struct string_converter<WireId>
{
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id)
{
if (id == WireId())
throw bad_wrap();
return ctx->getWireName(id).str(ctx);
}
};
template <> struct string_converter<const WireId>
{
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id)
{
if (id == WireId())
throw bad_wrap();
return ctx->getWireName(id).str(ctx);
}
};
template <> struct string_converter<PipId>
{
PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
std::string to_str(Context *ctx, PipId id)
{
if (id == PipId())
throw bad_wrap();
return ctx->getPipName(id).str(ctx);
}
};
} // namespace PythonConversion
NEXTPNR_NAMESPACE_END
#endif
#endif

234
leuctra/archdefs.h Normal file
View File

@ -0,0 +1,234 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Clifford Wolf <clifford@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 NEXTPNR_H
#error Include "archdefs.h" via "nextpnr.h" only.
#endif
#include <boost/functional/hash.hpp>
NEXTPNR_NAMESPACE_BEGIN
typedef int delay_t;
struct DelayInfo
{
delay_t min_delay = 0, max_delay = 0;
delay_t minRaiseDelay() const { return min_delay; }
delay_t maxRaiseDelay() const { return max_delay; }
delay_t minFallDelay() const { return min_delay; }
delay_t maxFallDelay() const { return max_delay; }
delay_t minDelay() const { return min_delay; }
delay_t maxDelay() const { return max_delay; }
DelayInfo operator+(const DelayInfo &other) const
{
DelayInfo ret;
ret.min_delay = this->min_delay + other.min_delay;
ret.max_delay = this->max_delay + other.max_delay;
return ret;
}
};
// -----------------------------------------------------------------------
NPNR_PACKED_STRUCT(struct LocationPOD { int16_t x, y; });
struct Location
{
int16_t x = -1, y = -1;
Location() : x(-1), y(-1){};
Location(int16_t x, int16_t y) : x(x), y(y){};
Location(const LocationPOD &pod) : x(pod.x), y(pod.y){};
Location(const Location &loc) : x(loc.x), y(loc.y){};
bool operator==(const Location &other) const { return x == other.x && y == other.y; }
bool operator!=(const Location &other) const { return x != other.x || y != other.y; }
bool operator<(const Location &other) const { return y == other.y ? x < other.x : y < other.y; }
};
inline Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); }
struct BelId
{
Location location;
int32_t index = -1;
bool operator==(const BelId &other) const { return index == other.index && location == other.location; }
bool operator!=(const BelId &other) const { return index != other.index || location != other.location; }
bool operator<(const BelId &other) const
{
return location == other.location ? index < other.index : location < other.location;
}
};
struct WireId
{
Location location;
int32_t index = -1;
bool operator==(const WireId &other) const { return index == other.index && location == other.location; }
bool operator!=(const WireId &other) const { return index != other.index || location != other.location; }
bool operator<(const WireId &other) const
{
return location == other.location ? index < other.index : location < other.location;
}
};
enum PipKind {
PIP_KIND_PIP,
PIP_KIND_PORT,
};
struct PipId
{
Location location;
// Is it a plain pip, or a pseudo-pip for a port.
PipKind kind;
// Index of the pip or the port.
int32_t index = -1;
// For ports, index of the wire in the port.
int32_t subindex = -1;
bool operator==(const PipId &other) const { return subindex == other.subindex && index == other.index && kind == other.kind && location == other.location; }
bool operator!=(const PipId &other) const { return subindex != other.subindex || index != other.index || kind != other.kind || location != other.location; }
bool operator<(const PipId &other) const
{
if (location != other.location)
return location < other.location;
if (kind != other.kind)
return kind < other.kind;
if (index != other.index)
return index < other.index;
return subindex < other.subindex;
}
};
struct GroupId
{
int32_t index = -1;
bool operator==(const GroupId &other) const { return index == other.index; }
bool operator!=(const GroupId &other) const { return index != other.index; }
};
struct DecalId
{
enum
{
TYPE_NONE,
TYPE_BEL
} type;
Location location;
uint32_t z = 0;
bool active = false;
bool operator==(const DecalId &other) const
{
return type == other.type && location == other.location && z == other.z && active == other.active;
}
bool operator!=(const DecalId &other) const
{
return type != other.type || location != other.location || z != other.z || active != other.active;
}
};
struct ArchNetInfo
{
bool is_global = false;
};
struct ArchCellInfo
{
struct
{
bool using_dff;
bool has_l6mux;
IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode;
} sliceInfo;
};
NEXTPNR_NAMESPACE_END
namespace std {
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX Location>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Location &loc) const noexcept
{
std::size_t seed = std::hash<int>()(loc.x);
seed ^= std::hash<int>()(loc.y) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept
{
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(bel.location);
seed ^= std::hash<int>()(bel.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX WireId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept
{
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(wire.location);
seed ^= std::hash<int>()(wire.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PipId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept
{
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(pip.location);
seed ^= std::hash<int>()(pip.index ^ pip.subindex) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX GroupId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept
{
return std::hash<int>()(group.index);
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<int>()(decal.type));
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX Location>()(decal.location));
boost::hash_combine(seed, hash<int>()(decal.z));
boost::hash_combine(seed, hash<bool>()(decal.active));
return seed;
}
};
} // namespace std

905
leuctra/cells.cc Normal file
View File

@ -0,0 +1,905 @@
/*
* 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.
*
*/
#include "cells.h"
#include <algorithm>
#include <iostream>
#include "design_utils.h"
#include "log.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
struct LutData {
int nbits;
NetInfo *nets[6];
Property init;
CellInfo *cell;
};
void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
{
IdString id = ctx->id(name);
cell->ports[id] = PortInfo{id, nullptr, dir};
}
std::unique_ptr<CellInfo> create_leuctra_cell(Context *ctx, IdString type, std::string name)
{
static int auto_idx = 0;
std::unique_ptr<CellInfo> new_cell = std::unique_ptr<CellInfo>(new CellInfo());
if (name.empty()) {
new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++));
} else {
new_cell->name = ctx->id(name);
}
new_cell->type = type;
auto copy_bel_ports = [&]() {
// First find a Bel of the target type
BelId tgt;
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) == type) {
tgt = bel;
break;
}
}
NPNR_ASSERT(tgt != BelId());
for (auto port : ctx->getBelPins(tgt)) {
add_port(ctx, new_cell.get(), port.str(ctx), ctx->getBelPinType(tgt, port));
}
};
if (type == ctx->id("LEUCTRA_FF")) {
new_cell->params[ctx->id("MODE")] = Property("FF_SYNC");
add_port(ctx, new_cell.get(), "D", PORT_IN);
add_port(ctx, new_cell.get(), "CLK", PORT_IN);
add_port(ctx, new_cell.get(), "CE", PORT_IN);
add_port(ctx, new_cell.get(), "SR", PORT_IN);
add_port(ctx, new_cell.get(), "Q", PORT_OUT);
} else if (type == ctx->id("LEUCTRA_LC")) {
new_cell->params[ctx->id("MODE")] = Property("ROM");
new_cell->params[ctx->id("INIT")] = Property(0, 64);
new_cell->attrs[ctx->id("NEEDS_L")] = Property(false);
new_cell->attrs[ctx->id("NEEDS_M")] = Property(false);
new_cell->attrs[ctx->id("LOCMASK")] = Property(0xf, 4);
add_port(ctx, new_cell.get(), "I1", PORT_IN);
add_port(ctx, new_cell.get(), "I2", PORT_IN);
add_port(ctx, new_cell.get(), "I3", PORT_IN);
add_port(ctx, new_cell.get(), "I4", PORT_IN);
add_port(ctx, new_cell.get(), "I5", PORT_IN);
add_port(ctx, new_cell.get(), "I6", PORT_IN);
add_port(ctx, new_cell.get(), "RA1", PORT_IN);
add_port(ctx, new_cell.get(), "RA2", PORT_IN);
add_port(ctx, new_cell.get(), "RA3", PORT_IN);
add_port(ctx, new_cell.get(), "RA4", PORT_IN);
add_port(ctx, new_cell.get(), "RA5", PORT_IN);
add_port(ctx, new_cell.get(), "RA6", PORT_IN);
add_port(ctx, new_cell.get(), "WA1", PORT_IN);
add_port(ctx, new_cell.get(), "WA2", PORT_IN);
add_port(ctx, new_cell.get(), "WA3", PORT_IN);
add_port(ctx, new_cell.get(), "WA4", PORT_IN);
add_port(ctx, new_cell.get(), "WA5", PORT_IN);
add_port(ctx, new_cell.get(), "WA6", PORT_IN);
add_port(ctx, new_cell.get(), "WA7", PORT_IN);
add_port(ctx, new_cell.get(), "WA8", PORT_IN);
add_port(ctx, new_cell.get(), "WE", PORT_IN);
add_port(ctx, new_cell.get(), "CLK", PORT_IN);
add_port(ctx, new_cell.get(), "O6", PORT_OUT);
add_port(ctx, new_cell.get(), "O5", PORT_OUT);
add_port(ctx, new_cell.get(), "DMI0", PORT_IN);
add_port(ctx, new_cell.get(), "DMI1", PORT_IN);
add_port(ctx, new_cell.get(), "XI", PORT_IN);
add_port(ctx, new_cell.get(), "DCI", PORT_IN);
add_port(ctx, new_cell.get(), "MO", PORT_OUT);
add_port(ctx, new_cell.get(), "XO", PORT_OUT);
add_port(ctx, new_cell.get(), "CO", PORT_OUT);
add_port(ctx, new_cell.get(), "DCO", PORT_OUT);
add_port(ctx, new_cell.get(), "DMO", PORT_OUT);
add_port(ctx, new_cell.get(), "DDI5", PORT_IN);
add_port(ctx, new_cell.get(), "DDI7", PORT_IN);
add_port(ctx, new_cell.get(), "DDI8", PORT_IN);
} else if (type == ctx->id("IOB")) {
add_port(ctx, new_cell.get(), "O", PORT_IN);
add_port(ctx, new_cell.get(), "T", PORT_IN);
add_port(ctx, new_cell.get(), "I", PORT_OUT);
add_port(ctx, new_cell.get(), "PADOUT", PORT_OUT);
add_port(ctx, new_cell.get(), "DIFFO_OUT", PORT_OUT);
add_port(ctx, new_cell.get(), "DIFFO_IN", PORT_IN);
add_port(ctx, new_cell.get(), "DIFFI_IN", PORT_IN);
} else if (type == ctx->id("ILOGIC2")) {
add_port(ctx, new_cell.get(), "D", PORT_IN);
add_port(ctx, new_cell.get(), "FABRICOUT", PORT_OUT);
} else if (type == ctx->id("OLOGIC2")) {
add_port(ctx, new_cell.get(), "D1", PORT_IN);
add_port(ctx, new_cell.get(), "D2", PORT_IN);
add_port(ctx, new_cell.get(), "D3", PORT_IN);
add_port(ctx, new_cell.get(), "D4", PORT_IN);
add_port(ctx, new_cell.get(), "OQ", PORT_OUT);
add_port(ctx, new_cell.get(), "T1", PORT_IN);
add_port(ctx, new_cell.get(), "T2", PORT_IN);
add_port(ctx, new_cell.get(), "T3", PORT_IN);
add_port(ctx, new_cell.get(), "T4", PORT_IN);
add_port(ctx, new_cell.get(), "TQ", PORT_OUT);
add_port(ctx, new_cell.get(), "SR", PORT_IN);
add_port(ctx, new_cell.get(), "REV", PORT_IN);
add_port(ctx, new_cell.get(), "OCE", PORT_IN);
add_port(ctx, new_cell.get(), "TCE", PORT_IN);
add_port(ctx, new_cell.get(), "IOCE", PORT_IN);
add_port(ctx, new_cell.get(), "TRAIN", PORT_IN);
add_port(ctx, new_cell.get(), "CLK0", PORT_IN);
add_port(ctx, new_cell.get(), "CLK1", PORT_IN);
add_port(ctx, new_cell.get(), "CLKDIV", PORT_IN);
add_port(ctx, new_cell.get(), "SHIFTIN1", PORT_IN);
add_port(ctx, new_cell.get(), "SHIFTIN2", PORT_IN);
add_port(ctx, new_cell.get(), "SHIFTIN3", PORT_IN);
add_port(ctx, new_cell.get(), "SHIFTIN4", PORT_IN);
add_port(ctx, new_cell.get(), "SHIFTOUT1", PORT_OUT);
add_port(ctx, new_cell.get(), "SHIFTOUT2", PORT_OUT);
add_port(ctx, new_cell.get(), "SHIFTOUT3", PORT_OUT);
add_port(ctx, new_cell.get(), "SHIFTOUT4", PORT_OUT);
} else {
log_error("unable to create Leuctra cell of type %s", type.c_str(ctx));
}
return new_cell;
}
void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells)
{
if (nxio->type == ctx->id("$nextpnr_ibuf")) {
iob->params[ctx->id("DIR")] = Property("INPUT");
replace_port(nxio, ctx->id("O"), iob, ctx->id("I"));
} else if (nxio->type == ctx->id("$nextpnr_obuf")) {
iob->params[ctx->id("DIR")] = Property("OUTPUT");
replace_port(nxio, ctx->id("I"), iob, ctx->id("O"));
} else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
// N.B. tristate will be dealt with below
iob->params[ctx->id("DIR")] = Property("BIDIR");
replace_port(nxio, ctx->id("I"), iob, ctx->id("O"));
replace_port(nxio, ctx->id("O"), iob, ctx->id("I"));
} else {
NPNR_ASSERT(false);
}
NetInfo *donet = iob->ports.at(ctx->id("O")).net;
CellInfo *tbuf = net_driven_by(
ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
ctx->id("Y"));
if (tbuf) {
replace_port(tbuf, ctx->id("A"), iob, ctx->id("O"));
// Need to invert E to form T
std::unique_ptr<CellInfo> inv_lut = create_leuctra_cell(ctx, ctx->id("LUT1"), iob->name.str(ctx) + "$invert_T");
replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("I0"));
inv_lut->params[ctx->id("INIT")] = Property(1, 2);
connect_ports(ctx, inv_lut.get(), ctx->id("O"), iob, ctx->id("T"));
created_cells.push_back(std::move(inv_lut));
if (donet->users.size() > 1) {
for (auto user : donet->users)
log_info(" remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx));
log_error("unsupported tristate IO pattern for IO buffer '%s', "
"instantiate IOBUF manually to ensure correct behaviour\n",
nxio->name.c_str(ctx));
}
ctx->nets.erase(donet->name);
todelete_cells.insert(tbuf->name);
}
}
void convert_ff(Context *ctx, CellInfo *orig, CellInfo *ff, std::vector<std::unique_ptr<CellInfo>> &new_cells,
std::unordered_set<IdString> &todelete_cells)
{
bool is_latch;
IdString sr_pin;
IdString ce_pin;
IdString clk_pin;
bool clk_inv;
Property mode;
bool srval;
if (orig->type == ctx->id("FDRE")) {
mode = Property("FF_SYNC");
srval = false;
sr_pin = ctx->id("R");
is_latch = false;
clk_inv = false;
} else if (orig->type == ctx->id("FDSE")) {
mode = Property("FF_SYNC");
srval = true;
sr_pin = ctx->id("S");
is_latch = false;
clk_inv = false;
} else if (orig->type == ctx->id("FDCE")) {
mode = Property("FF_ASYNC");
srval = false;
sr_pin = ctx->id("CLR");
is_latch = false;
clk_inv = false;
} else if (orig->type == ctx->id("FDPE")) {
mode = Property("FF_ASYNC");
srval = true;
sr_pin = ctx->id("PRE");
is_latch = false;
clk_inv = false;
} else if (orig->type == ctx->id("LDCE")) {
mode = Property("LATCH");
srval = false;
sr_pin = ctx->id("CLR");
is_latch = true;
clk_inv = true;
} else if (orig->type == ctx->id("LDPE")) {
mode = Property("LATCH");
srval = true;
sr_pin = ctx->id("PRE");
is_latch = true;
clk_inv = true;
} else if (orig->type == ctx->id("FDRE_1")) {
mode = Property("FF_SYNC");
srval = false;
sr_pin = ctx->id("R");
is_latch = false;
clk_inv = true;
} else if (orig->type == ctx->id("FDSE_1")) {
mode = Property("FF_SYNC");
srval = true;
sr_pin = ctx->id("S");
is_latch = false;
clk_inv = true;
} else if (orig->type == ctx->id("FDCE_1")) {
mode = Property("FF_ASYNC");
srval = false;
sr_pin = ctx->id("CLR");
is_latch = false;
clk_inv = true;
} else if (orig->type == ctx->id("FDPE_1")) {
mode = Property("FF_ASYNC");
srval = true;
sr_pin = ctx->id("PRE");
is_latch = false;
clk_inv = true;
} else if (orig->type == ctx->id("LDCE_1")) {
mode = Property("LATCH");
srval = false;
sr_pin = ctx->id("CLR");
is_latch = true;
clk_inv = false;
} else if (orig->type == ctx->id("LDPE_1")) {
mode = Property("LATCH");
srval = true;
sr_pin = ctx->id("PRE");
is_latch = true;
clk_inv = false;
} else {
NPNR_ASSERT_FALSE("WEIRD FF TYPE");
}
if (is_latch) {
clk_pin = ctx->id("G");
ce_pin = ctx->id("GE");
} else {
clk_pin = ctx->id("C");
ce_pin = ctx->id("CE");
}
ff->params[ctx->id("MODE")] = mode;
ff->params[ctx->id("SRVAL")] = Property(srval, 1);
if (orig->params.count(ctx->id("INIT"))) {
if (orig->params[ctx->id("INIT")].str[0] != 'x')
ff->params[ctx->id("INIT")] = orig->params[ctx->id("INIT")];
orig->params.erase(ctx->id("INIT"));
}
NetInfo *net;
bool net_inv = false, cval = false;
if (get_invertible_port(ctx, orig, ctx->id("D"), false, false, net, net_inv))
set_invertible_port(ctx, ff, ctx->id("D"), net, net_inv, false, new_cells);
if (get_invertible_port(ctx, orig, sr_pin, false, false, net, net_inv)) {
if (get_const_val(ctx, net, cval) && (cval ^ net_inv) == false) {
// SR tied to 0 — remove it.
} else {
set_invertible_port(ctx, ff, ctx->id("SR"), net, net_inv, false, new_cells);
ff->params[ctx->id("SRUSED")] = Property(true);
}
}
if (get_invertible_port(ctx, orig, ce_pin, false, false, net, net_inv)) {
if (get_const_val(ctx, net, cval) && (cval ^ net_inv) == true) {
// CE tied to 1 — remove it.
} else {
set_invertible_port(ctx, ff, ctx->id("CE"), net, net_inv, false, new_cells);
ff->params[ctx->id("CEUSED")] = Property(true);
}
}
if (get_invertible_port(ctx, orig, clk_pin, clk_inv, true, net, net_inv)) {
set_invertible_port(ctx, ff, ctx->id("CLK"), net, net_inv, true, new_cells);
}
replace_port(orig, ctx->id("Q"), ff, ctx->id("Q"));
for (auto &param : orig->params) {
log_error("FF %s has leftover param %s = %s\n", orig->name.c_str(ctx), param.first.c_str(ctx), param.second.str.c_str());
}
}
LutData get_lut(Context *ctx, NetInfo *net) {
LutData res;
res.cell = net->driver.cell;
if (!res.cell || res.cell->type == ctx->id("GND")) {
res.nbits = 0;
res.init = Property(0, 1);
} else if (res.cell->type == ctx->id("VCC")) {
res.nbits = 0;
res.init = Property(1, 1);
} else if (res.cell->type == ctx->id("INV")) {
res.nbits = 1;
res.nets[0] = res.cell->ports[ctx->id("I")].net;
res.init = Property(1, 2);
} else if (res.cell->type == ctx->id("LUT1")) {
res.nbits = 1;
res.nets[0] = res.cell->ports[ctx->id("I0")].net;
res.init = res.cell->params[ctx->id("INIT")];
} else if (res.cell->type == ctx->id("LUT2")) {
res.nbits = 2;
res.nets[0] = res.cell->ports[ctx->id("I0")].net;
res.nets[1] = res.cell->ports[ctx->id("I1")].net;
res.init = res.cell->params[ctx->id("INIT")];
} else if (res.cell->type == ctx->id("LUT3")) {
res.nbits = 3;
res.nets[0] = res.cell->ports[ctx->id("I0")].net;
res.nets[1] = res.cell->ports[ctx->id("I1")].net;
res.nets[2] = res.cell->ports[ctx->id("I2")].net;
res.init = res.cell->params[ctx->id("INIT")];
} else if (res.cell->type == ctx->id("LUT4")) {
res.nbits = 4;
res.nets[0] = res.cell->ports[ctx->id("I0")].net;
res.nets[1] = res.cell->ports[ctx->id("I1")].net;
res.nets[2] = res.cell->ports[ctx->id("I2")].net;
res.nets[3] = res.cell->ports[ctx->id("I3")].net;
res.init = res.cell->params[ctx->id("INIT")];
} else if (res.cell->type == ctx->id("LUT5")) {
res.nbits = 5;
res.nets[0] = res.cell->ports[ctx->id("I0")].net;
res.nets[1] = res.cell->ports[ctx->id("I1")].net;
res.nets[2] = res.cell->ports[ctx->id("I2")].net;
res.nets[3] = res.cell->ports[ctx->id("I3")].net;
res.nets[4] = res.cell->ports[ctx->id("I4")].net;
res.init = res.cell->params[ctx->id("INIT")];
} else if (res.cell->type == ctx->id("LUT6")) {
res.nbits = 6;
res.nets[0] = res.cell->ports[ctx->id("I0")].net;
res.nets[1] = res.cell->ports[ctx->id("I1")].net;
res.nets[2] = res.cell->ports[ctx->id("I2")].net;
res.nets[3] = res.cell->ports[ctx->id("I3")].net;
res.nets[4] = res.cell->ports[ctx->id("I4")].net;
res.nets[5] = res.cell->ports[ctx->id("I5")].net;
res.init = res.cell->params[ctx->id("INIT")];
} else {
res.cell = nullptr;
res.nbits = 1;
res.nets[0] = net;
res.init = Property(2, 2);
}
return res;
}
void kill_lut(Context *ctx, const LutData &ld, std::unordered_set<IdString> &todelete_cells) {
if (!ld.cell)
return;
todelete_cells.insert(ld.cell->name);
for (auto &port : ld.cell->ports)
if (port.second.net)
disconnect_port(ctx, ld.cell, port.first);
}
CellInfo *convert_lut(Context *ctx, NetInfo *net, std::string name, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells)
{
LutData ld = get_lut(ctx, net);
Property new_init(0, 64);
for (int i = 0; i < 64; i++) {
new_init.str[i] = ld.init.str[i % (1 << ld.nbits)];
}
new_init.update_intval();
kill_lut(ctx, ld, todelete_cells);
std::unique_ptr<CellInfo> lut_cell =
create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), name);
created_cells.push_back(std::move(lut_cell));
CellInfo *lut = created_cells.back().get();
lut->params[ctx->id("INIT")] = std::move(new_init);
if (ld.nbits >= 1)
connect_port(ctx, ld.nets[0], lut, ctx->id("I1"));
if (ld.nbits >= 2)
connect_port(ctx, ld.nets[1], lut, ctx->id("I2"));
if (ld.nbits >= 3)
connect_port(ctx, ld.nets[2], lut, ctx->id("I3"));
if (ld.nbits >= 4)
connect_port(ctx, ld.nets[3], lut, ctx->id("I4"));
if (ld.nbits >= 5)
connect_port(ctx, ld.nets[4], lut, ctx->id("I5"));
if (ld.nbits >= 6)
connect_port(ctx, ld.nets[5], lut, ctx->id("I6"));
if (ld.cell)
connect_port(ctx, net, lut, ctx->id("O6"));
return lut;
}
std::pair<CellInfo *, CellInfo *> convert_muxf7(Context *ctx, NetInfo *net, std::string name, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells)
{
CellInfo *drv = net->driver.cell;
if (drv && drv->type == ctx->id("MUXF7")) {
// Good.
NetInfo *net0 = drv->ports.at(ctx->id("I0")).net;
NetInfo *net1 = drv->ports.at(ctx->id("I1")).net;
NetInfo *netsel = drv->ports.at(ctx->id("S")).net;
CellInfo *lc0 = convert_lut(ctx, net0, name + "$i0", created_cells, todelete_cells);
CellInfo *lc1 = convert_lut(ctx, net1, name + "$i1", created_cells, todelete_cells);
connect_ports(ctx, lc0, ctx->id("O6"), lc1, ctx->id("DMI0"));
connect_ports(ctx, lc1, ctx->id("O6"), lc1, ctx->id("DMI1"));
disconnect_port(ctx, drv, ctx->id("I0"));
disconnect_port(ctx, drv, ctx->id("I1"));
disconnect_port(ctx, drv, ctx->id("S"));
disconnect_port(ctx, drv, ctx->id("O"));
todelete_cells.insert(drv->name);
connect_port(ctx, netsel, lc1, ctx->id("XI"));
connect_port(ctx, net, lc1, ctx->id("MO"));
lc1->attrs[ctx->id("LOCMASK")] = Property(0x5, 4);
lc1->attrs[ctx->id("NEEDS_L")] = Property(true);
lc0->constr_parent = lc1;
lc0->constr_z = 3;
lc1->constr_children.push_back(lc0);
return std::make_pair(lc0, lc1);
} else {
// Not so good.
CellInfo *lc1 = convert_lut(ctx, net, name, created_cells, todelete_cells);
connect_ports(ctx, lc1, ctx->id("O6"), lc1, ctx->id("DMI1"));
set_const_port(ctx, lc1, ctx->id("XI"), true, created_cells);
lc1->attrs[ctx->id("LOCMASK")] = Property(0x5, 4);
lc1->attrs[ctx->id("NEEDS_L")] = Property(true);
return std::make_pair(nullptr, lc1);
}
}
void convert_muxf8(Context *ctx, NetInfo *net, std::string name, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells)
{
CellInfo *drv = net->driver.cell;
CellInfo *lc00, *lc01, *lc10, *lc11;
if (drv && drv->type == ctx->id("MUXF8")) {
// Good.
NetInfo *net0 = drv->ports.at(ctx->id("I0")).net;
NetInfo *net1 = drv->ports.at(ctx->id("I1")).net;
NetInfo *netsel = drv->ports.at(ctx->id("S")).net;
std::tie(lc00, lc01) = convert_muxf7(ctx, net0, name + "$i0", created_cells, todelete_cells);
std::tie(lc10, lc11) = convert_muxf7(ctx, net1, name + "$i1", created_cells, todelete_cells);
if (!lc10) {
lc10 = convert_lut(ctx, nullptr, name + "$f8", created_cells, todelete_cells);
lc10->constr_parent = lc11;
lc10->constr_z = 3;
lc11->constr_children.push_back(lc10);
}
connect_ports(ctx, lc01, ctx->id("DMO"), lc10, ctx->id("DMI0"));
connect_ports(ctx, lc11, ctx->id("DMO"), lc10, ctx->id("DMI1"));
disconnect_port(ctx, drv, ctx->id("I0"));
disconnect_port(ctx, drv, ctx->id("I1"));
disconnect_port(ctx, drv, ctx->id("S"));
disconnect_port(ctx, drv, ctx->id("O"));
todelete_cells.insert(drv->name);
connect_port(ctx, netsel, lc10, ctx->id("XI"));
connect_port(ctx, net, lc10, ctx->id("MO"));
lc11->attrs[ctx->id("LOCMASK")] = Property(0x1, 4);
lc01->constr_parent = lc11;
lc01->constr_z = 6;
lc11->constr_children.push_back(lc01);
} else {
NPNR_ASSERT_FALSE("WEIRD MUXF8");
}
}
CellInfo *convert_carry4(Context *ctx, CellInfo *c4, CellInfo *link, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells)
{
NetInfo *co[4];
NetInfo *xo[4];
NetInfo *di[4];
NetInfo *s[4];
NetInfo *cyinit;
cyinit = c4->ports[ctx->id("CYINIT")].net;
s[0] = c4->ports[ctx->id("S[0]")].net;
s[1] = c4->ports[ctx->id("S[1]")].net;
s[2] = c4->ports[ctx->id("S[2]")].net;
s[3] = c4->ports[ctx->id("S[3]")].net;
di[0] = c4->ports[ctx->id("DI[0]")].net;
di[1] = c4->ports[ctx->id("DI[1]")].net;
di[2] = c4->ports[ctx->id("DI[2]")].net;
di[3] = c4->ports[ctx->id("DI[3]")].net;
co[0] = c4->ports[ctx->id("CO[0]")].net;
co[1] = c4->ports[ctx->id("CO[1]")].net;
co[2] = c4->ports[ctx->id("CO[2]")].net;
co[3] = c4->ports[ctx->id("CO[3]")].net;
xo[0] = c4->ports[ctx->id("O[0]")].net;
xo[1] = c4->ports[ctx->id("O[1]")].net;
xo[2] = c4->ports[ctx->id("O[2]")].net;
xo[3] = c4->ports[ctx->id("O[3]")].net;
disconnect_port(ctx, c4, ctx->id("CO[0]"));
disconnect_port(ctx, c4, ctx->id("CO[1]"));
disconnect_port(ctx, c4, ctx->id("CO[2]"));
disconnect_port(ctx, c4, ctx->id("CO[3]"));
disconnect_port(ctx, c4, ctx->id("O[0]"));
disconnect_port(ctx, c4, ctx->id("O[1]"));
disconnect_port(ctx, c4, ctx->id("O[2]"));
disconnect_port(ctx, c4, ctx->id("O[3]"));
disconnect_port(ctx, c4, ctx->id("S[0]"));
disconnect_port(ctx, c4, ctx->id("S[1]"));
disconnect_port(ctx, c4, ctx->id("S[2]"));
disconnect_port(ctx, c4, ctx->id("S[3]"));
disconnect_port(ctx, c4, ctx->id("DI[0]"));
disconnect_port(ctx, c4, ctx->id("DI[1]"));
disconnect_port(ctx, c4, ctx->id("DI[2]"));
disconnect_port(ctx, c4, ctx->id("DI[3]"));
disconnect_port(ctx, c4, ctx->id("CI"));
disconnect_port(ctx, c4, ctx->id("CYINIT"));
todelete_cells.insert(c4->name);
CellInfo *lcs[4];
int num = 0;
for (int i = 0; i < 4; i++) {
if (!co[i]->users.size())
co[i] = nullptr;
if (!xo[i]->users.size())
xo[i] = nullptr;
if (co[i] || xo[i])
num = i + 1;
}
NetInfo *vcc_net = nullptr;
for (int i = 0; i < num; i++) {
// XXX more cases
if (i == 0 && !link) {
bool cval, is_const;
is_const = get_const_val(ctx, cyinit, cval);
if (is_const) {
lcs[i] = convert_lut(ctx, s[i], c4->name.str(ctx) + "$lc" + std::to_string(i), created_cells, todelete_cells);
connect_port(ctx, di[i], lcs[i], ctx->id("XI"));
lcs[i]->params[ctx->id("CYMUX")] = Property("XI");
if (cval)
lcs[i]->params[ctx->id("CYINIT")] = Property("1");
else
lcs[i]->params[ctx->id("CYINIT")] = Property("0");
} else {
std::unique_ptr<CellInfo> lut_cell =
create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), c4->name.str(ctx) + "$lc" + std::to_string(i));
created_cells.push_back(std::move(lut_cell));
lcs[i] = created_cells.back().get();
lcs[i]->params[ctx->id("INIT")] = Property::from_string("1010101010101010101010101010101011001100110011001100110011001100");
connect_port(ctx, s[i], lcs[i], ctx->id("I1"));
connect_port(ctx, di[i], lcs[i], ctx->id("I2"));
set_const_port(ctx, lcs[i], ctx->id("RA6"), true, created_cells);
connect_port(ctx, cyinit, lcs[i], ctx->id("XI"));
lcs[i]->params[ctx->id("CYINIT")] = Property("XI");
lcs[i]->params[ctx->id("CYMUX")] = Property("O5");
}
lcs[0]->attrs[ctx->id("LOCMASK")] = Property(1, 4);
lcs[0]->attrs[ctx->id("NEEDS_L")] = Property(true);
} else {
lcs[i] = convert_lut(ctx, s[i], c4->name.str(ctx) + "$lc" + std::to_string(i), created_cells, todelete_cells);
connect_port(ctx, di[i], lcs[i], ctx->id("XI"));
lcs[i]->params[ctx->id("CYMUX")] = Property("XI");
connect_ports(ctx, link, ctx->id("DCO"), lcs[i], ctx->id("DCI"));
lcs[i]->constr_parent = link;
if (i == 0) {
// XXX does this work lol
//lcs[i]->constr_spec = 0;
lcs[i]->constr_z = -9;
lcs[i]->constr_y = 1;
} else {
lcs[i]->constr_z = 3;
}
link->constr_children.push_back(lcs[i]);
}
if (xo[i])
connect_port(ctx, xo[i], lcs[i], ctx->id("XO"));
if (co[i]) {
if (!xo[i]) {
connect_port(ctx, co[i], lcs[i], ctx->id("CO"));
} else {
std::unique_ptr<CellInfo> ff_cell =
create_leuctra_cell(ctx, ctx->id("LEUCTRA_FF"), c4->name.str(ctx) + "$lc" + std::to_string(i) + "$ff");
created_cells.push_back(std::move(ff_cell));
CellInfo *ff = created_cells.back().get();
ff->constr_parent = lcs[i];
ff->constr_z = 1;
lcs[i]->constr_children.push_back(ff);
ff->params[ctx->id("MODE")] = Property("COMB");
ff->params[ctx->id("CLKINV")] = Property("CLK_B");
if (vcc_net) {
connect_port(ctx, vcc_net, ff, ctx->id("CLK"));
} else {
set_const_port(ctx, ff, ctx->id("CLK"), true, created_cells);
vcc_net = ff->ports[ctx->id("CLK")].net;
}
connect_ports(ctx, lcs[i], ctx->id("CO"), ff, ctx->id("D"));
connect_port(ctx, co[i], ff, ctx->id("Q"));
}
}
link = lcs[i];
}
return link;
}
void insert_ilogic_pass(Context *ctx, CellInfo *iob, CellInfo *ilogic)
{
replace_port(iob, ctx->id("I"), ilogic, ctx->id("FABRICOUT"));
connect_ports(ctx, iob, ctx->id("I"), ilogic, ctx->id("D"));
ilogic->params[ctx->id("IMUX")] = Property("1");
ilogic->params[ctx->id("FABRICOUTUSED")] = Property("0");
ilogic->constr_parent = iob;
iob->constr_children.push_back(ilogic);
// XXX enum
ilogic->constr_spec = 1;
}
void insert_ologic_pass(Context *ctx, CellInfo *iob, CellInfo *ologic)
{
replace_port(iob, ctx->id("O"), ologic, ctx->id("D1"));
connect_ports(ctx, ologic, ctx->id("OQ"), iob, ctx->id("O"));
NetInfo *net_t = iob->ports.at(ctx->id("T")).net;
ologic->params[ctx->id("OMUX")] = Property("D1");
ologic->params[ctx->id("D1USED")] = Property("0");
ologic->params[ctx->id("O1USED")] = Property("0");
if (net_t != nullptr) {
replace_port(iob, ctx->id("T"), ologic, ctx->id("T1"));
connect_ports(ctx, ologic, ctx->id("TQ"), iob, ctx->id("T"));
ologic->params[ctx->id("TMUX")] = Property("T1");
ologic->params[ctx->id("T1USED")] = Property("0");
}
ologic->constr_parent = iob;
iob->constr_children.push_back(ologic);
// XXX enum
ologic->constr_spec = 2;
}
bool get_const_val(Context *ctx, NetInfo *net, bool &out) {
if (!net->driver.cell)
return false;
if (net->driver.cell->type == ctx->id("GND")) {
out = false;
return true;
}
if (net->driver.cell->type == ctx->id("VCC")) {
out = true;
return true;
}
return false;
}
void set_const_port(Context *ctx, CellInfo *cell, IdString port, bool val, std::vector<std::unique_ptr<CellInfo>> &new_cells) {
if (!cell->ports.count(port)) {
cell->ports[port].name = port;
cell->ports[port].type = PORT_IN;
}
std::unique_ptr<CellInfo> const_cell{new CellInfo};
std::unique_ptr<NetInfo> const_net{new NetInfo};
IdString name = ctx->id(cell->name.str(ctx) + "$const$" + port.str(ctx));
IdString const_port;
if (val) {
const_cell->type = ctx->id("VCC");
const_port = ctx->id("P");
} else {
const_cell->type = ctx->id("GND");
const_port = ctx->id("G");
}
const_cell->name = name;
const_net->name = name;
const_cell->ports[const_port].type = PORT_OUT;
connect_port(ctx, const_net.get(), const_cell.get(), const_port);
connect_port(ctx, const_net.get(), cell, port);
ctx->nets[name] = std::move(const_net);
new_cells.push_back(std::move(const_cell));
}
bool get_invertible_port(Context *ctx, CellInfo *cell, IdString port, bool invert, bool invertible, NetInfo *&net, bool &invert_out) {
invert_out = invert;
if (!cell->ports.count(port))
return false;
net = cell->ports.at(port).net;
if (!net)
return false;
disconnect_port(ctx, cell, port);
// XXX support buses
IdString param_name = ctx->id("IS_" + port.str(ctx) + "_INVERTED");
if (cell->params.count(param_name)) {
Property val = cell->params[param_name];
invert_out ^= val.as_bool();
cell->params.erase(param_name);
}
if (invertible) {
while (net->driver.cell && net->driver.cell->type == ctx->id("INV")) {
CellInfo *icell = net->driver.cell;
if (!icell->ports.count(ctx->id("I")))
return false;
net = icell->ports.at(ctx->id("I")).net;
if (!net)
return false;
invert_out ^= true;
}
}
return true;
}
void set_invertible_port(Context *ctx, CellInfo *cell, IdString port, NetInfo *net, bool invert, bool invertible, std::vector<std::unique_ptr<CellInfo>> &new_cells) {
if (!net)
return;
cell->ports[port].name = port;
cell->ports[port].type = PORT_IN;
if (invert && !invertible) {
std::unique_ptr<CellInfo> inv_cell{new CellInfo};
std::unique_ptr<NetInfo> inv_net{new NetInfo};
IdString name = ctx->id(cell->name.str(ctx) + "$inv$" + port.str(ctx));
inv_cell->type = ctx->id("INV");
inv_cell->name = name;
inv_net->name = name;
add_port(ctx, inv_cell.get(), "O", PORT_OUT);
add_port(ctx, inv_cell.get(), "I", PORT_IN);
connect_port(ctx, inv_net.get(), inv_cell.get(), ctx->id("O"));
connect_port(ctx, inv_net.get(), cell, port);
connect_port(ctx, net, inv_cell.get(), ctx->id("I"));
ctx->nets[name] = std::move(inv_net);
new_cells.push_back(std::move(inv_cell));
} else {
connect_port(ctx, net, cell, port);
if (invertible) {
std::string val;
if (invert)
val = port.str(ctx) + "_B";
else
val = port.str(ctx);
IdString param = ctx->id(port.str(ctx) + "INV");
cell->params[param] = Property(val);
}
}
}
bool handle_invertible_port(Context *ctx, CellInfo *cell, IdString port, bool invert, bool invertible, std::vector<std::unique_ptr<CellInfo>> &new_cells) {
NetInfo *net;
bool net_inv;
if (get_invertible_port(ctx, cell, port, invert, invertible, net, net_inv)) {
set_invertible_port(ctx, cell, port, net, net_inv, invertible, new_cells);
return true;
} else {
return false;
}
}
void fixup_ramb16(Context *ctx, CellInfo *cell, std::vector<std::unique_ptr<CellInfo>> &new_cells,
std::unordered_set<IdString> &todelete_cells) {
bool swizzle = false;
if (cell->params.count(ctx->id("DATA_WIDTH_A")) && cell->params.at(ctx->id("DATA_WIDTH_A")).as_int64() == 36)
swizzle = true;
if (cell->params.count(ctx->id("DATA_WIDTH_B")) && cell->params.at(ctx->id("DATA_WIDTH_B")).as_int64() == 36)
swizzle = true;
if (!cell->params.count(ctx->id("RAM_MODE")))
cell->params[ctx->id("RAM_MODE")] = Property("TDP");
if (!cell->params.count(ctx->id("EN_RSTRAM_A")))
cell->params[ctx->id("EN_RSTRAM_A")] = Property("TRUE");
if (!cell->params.count(ctx->id("EN_RSTRAM_B")))
cell->params[ctx->id("EN_RSTRAM_B")] = Property("TRUE");
if (!cell->params.count(ctx->id("RST_PRIORITY_A")))
cell->params[ctx->id("RST_PRIORITY_A")] = Property("CE");
if (!cell->params.count(ctx->id("RST_PRIORITY_B")))
cell->params[ctx->id("RST_PRIORITY_B")] = Property("CE");
handle_invertible_port(ctx, cell, ctx->id("CLKA"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("CLKB"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("ENA"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("ENB"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("REGCEA"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("REGCEB"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("RSTA"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("RSTB"), false, true, new_cells);
for (int i = 0; i < 32; i++) {
rename_port(ctx, cell, ctx->id("DOA[" + std::to_string(i) + "]"), ctx->id("DOA" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DOB[" + std::to_string(i) + "]"), ctx->id("DOB" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DIA[" + std::to_string(i) + "]"), ctx->id("DIA" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DIB[" + std::to_string(i) + "]"), ctx->id("DIB" + std::to_string(i)));
}
for (int i = 0; i < 4; i++) {
rename_port(ctx, cell, ctx->id("DOPA[" + std::to_string(i) + "]"), ctx->id("DOPA" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DOPB[" + std::to_string(i) + "]"), ctx->id("DOPB" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DIPA[" + std::to_string(i) + "]"), ctx->id("DIPA" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DIPB[" + std::to_string(i) + "]"), ctx->id("DIPB" + std::to_string(i)));
}
for (int i = 0; i < 14; i++) {
int si = i;
if (swizzle) {
if (i == 4)
si = 13;
else if (i == 13)
si = 4;
}
rename_port(ctx, cell, ctx->id("ADDRA[" + std::to_string(i) + "]"), ctx->id("ADDRA" + std::to_string(si)));
rename_port(ctx, cell, ctx->id("ADDRB[" + std::to_string(i) + "]"), ctx->id("ADDRB" + std::to_string(si)));
}
for (int i = 0; i < 4; i++) {
NetInfo *net;
bool net_inv;
if (get_invertible_port(ctx, cell, ctx->id("WEA[" + std::to_string(i) + "]"), false, true, net, net_inv)) {
set_invertible_port(ctx, cell, ctx->id("WEA" + std::to_string(i)), net, net_inv, true, new_cells);
}
if (get_invertible_port(ctx, cell, ctx->id("WEB[" + std::to_string(i) + "]"), false, true, net, net_inv)) {
set_invertible_port(ctx, cell, ctx->id("WEB" + std::to_string(i)), net, net_inv, true, new_cells);
}
}
}
void fixup_ramb8(Context *ctx, CellInfo *cell, std::vector<std::unique_ptr<CellInfo>> &new_cells,
std::unordered_set<IdString> &todelete_cells) {
if (!cell->params.count(ctx->id("RAM_MODE")))
cell->params[ctx->id("RAM_MODE")] = Property("TDP");
if (!cell->params.count(ctx->id("EN_RSTRAM_A")))
cell->params[ctx->id("EN_RSTRAM_A")] = Property("TRUE");
if (!cell->params.count(ctx->id("EN_RSTRAM_B")))
cell->params[ctx->id("EN_RSTRAM_B")] = Property("TRUE");
if (!cell->params.count(ctx->id("RST_PRIORITY_A")))
cell->params[ctx->id("RST_PRIORITY_A")] = Property("CE");
if (!cell->params.count(ctx->id("RST_PRIORITY_B")))
cell->params[ctx->id("RST_PRIORITY_B")] = Property("CE");
handle_invertible_port(ctx, cell, ctx->id("CLKAWRCLK"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("CLKBRDCLK"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("ENAWREN"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("ENBRDEN"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("REGCEA"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("REGCEBREGCE"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("RSTA"), false, true, new_cells);
handle_invertible_port(ctx, cell, ctx->id("RSTBRST"), false, true, new_cells);
for (int i = 0; i < 16; i++) {
rename_port(ctx, cell, ctx->id("DOADO[" + std::to_string(i) + "]"), ctx->id("DOADO" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DOBDO[" + std::to_string(i) + "]"), ctx->id("DOBDO" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DIADI[" + std::to_string(i) + "]"), ctx->id("DIADI" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DIBDI[" + std::to_string(i) + "]"), ctx->id("DIBDI" + std::to_string(i)));
}
for (int i = 0; i < 2; i++) {
rename_port(ctx, cell, ctx->id("DOPADOP[" + std::to_string(i) + "]"), ctx->id("DOPADOP" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DOPBDOP[" + std::to_string(i) + "]"), ctx->id("DOPBDOP" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DIPADIP[" + std::to_string(i) + "]"), ctx->id("DIPADIP" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("DIPBDIP[" + std::to_string(i) + "]"), ctx->id("DIPBDIP" + std::to_string(i)));
}
for (int i = 0; i < 13; i++) {
rename_port(ctx, cell, ctx->id("ADDRAWRADDR[" + std::to_string(i) + "]"), ctx->id("ADDRAWRADDR" + std::to_string(i)));
rename_port(ctx, cell, ctx->id("ADDRBRDADDR[" + std::to_string(i) + "]"), ctx->id("ADDRBRDADDR" + std::to_string(i)));
}
for (int i = 0; i < 2; i++) {
NetInfo *net;
bool net_inv;
if (get_invertible_port(ctx, cell, ctx->id("WEAWEL[" + std::to_string(i) + "]"), false, true, net, net_inv)) {
set_invertible_port(ctx, cell, ctx->id("WEAWEL" + std::to_string(i)), net, net_inv, true, new_cells);
}
if (get_invertible_port(ctx, cell, ctx->id("WEBWEU[" + std::to_string(i) + "]"), false, true, net, net_inv)) {
set_invertible_port(ctx, cell, ctx->id("WEBWEU" + std::to_string(i)), net, net_inv, true, new_cells);
}
}
}
NEXTPNR_NAMESPACE_END

98
leuctra/cells.h Normal file
View File

@ -0,0 +1,98 @@
/*
* 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 LEUCTRA_CELLS_H
#define LEUCTRA_CELLS_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
// Create a standard cell and return it
// Name will be automatically assigned if not specified
std::unique_ptr<CellInfo> create_leuctra_cell(Context *ctx, IdString type, std::string name = "");
inline bool is_xilinx_iobuf(const BaseCtx *ctx, const CellInfo *cell) {
return cell->type == ctx->id("IBUF")
|| cell->type == ctx->id("IBUFDS")
|| cell->type == ctx->id("IBUFDS_DIFF_OUT")
|| cell->type == ctx->id("OBUF")
|| cell->type == ctx->id("OBUFDS")
|| cell->type == ctx->id("OBUFT")
|| cell->type == ctx->id("OBUFTDS")
|| cell->type == ctx->id("IOBUF")
|| cell->type == ctx->id("IOBUFDS");
}
inline bool is_xilinx_ff(const BaseCtx *ctx, const CellInfo *cell) {
return cell->type == ctx->id("FDRE")
|| cell->type == ctx->id("FDSE")
|| cell->type == ctx->id("FDCE")
|| cell->type == ctx->id("FDPE")
|| cell->type == ctx->id("LDCE")
|| cell->type == ctx->id("LDPE");
}
inline bool is_xilinx_lut(const BaseCtx *ctx, const CellInfo *cell) {
return cell->type == ctx->id("INV")
|| cell->type == ctx->id("LUT1")
|| cell->type == ctx->id("LUT2")
|| cell->type == ctx->id("LUT3")
|| cell->type == ctx->id("LUT4")
|| cell->type == ctx->id("LUT5")
|| cell->type == ctx->id("LUT6");
}
// Convert a nextpnr IO buffer to an IOB
void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells);
void convert_ff(Context *ctx, CellInfo *orig, CellInfo *ff, std::vector<std::unique_ptr<CellInfo>> &new_cells,
std::unordered_set<IdString> &todelete_cells);
CellInfo *convert_lut(Context *ctx, NetInfo *net, std::string name, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells);
std::pair<CellInfo *, CellInfo *> convert_muxf7(Context *ctx, NetInfo *net, std::string name, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells);
void convert_muxf8(Context *ctx, NetInfo *net, std::string name, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells);
CellInfo *convert_carry4(Context *ctx, CellInfo *c4, CellInfo *link, std::vector<std::unique_ptr<CellInfo>> &created_cells,
std::unordered_set<IdString> &todelete_cells);
void fixup_ramb16(Context *ctx, CellInfo *cell, std::vector<std::unique_ptr<CellInfo>> &new_cells,
std::unordered_set<IdString> &todelete_cells);
void fixup_ramb8(Context *ctx, CellInfo *cell, std::vector<std::unique_ptr<CellInfo>> &new_cells,
std::unordered_set<IdString> &todelete_cells);
void insert_ilogic_pass(Context *ctx, CellInfo *iob, CellInfo *ilogic);
void insert_ologic_pass(Context *ctx, CellInfo *iob, CellInfo *ologic);
// Gets the constant value driving the given net, returns truee iff it really was a constant.
bool get_const_val(Context *ctx, NetInfo *net, bool &out);
// Connects a given port to a constant driver.
void set_const_port(Context *ctx, CellInfo *cell, IdString port, bool val, std::vector<std::unique_ptr<CellInfo>> &new_cells);
// Takes a port, finds the net driving it, passes through INV cells, collects IS_<port>_INVERTED, collects the final driver and inversion value, returns true iff net connected.
bool get_invertible_port(Context *ctx, CellInfo *cell, IdString port, bool invert, bool invertible, NetInfo *&net, bool &invert_out);
// Takes a port and connects it to the given net, possibly with an inversion. If invertible is false, emulate it by inserting an INV cell if necessary.
void set_invertible_port(Context *ctx, CellInfo *cell, IdString port, NetInfo *net, bool invert, bool invertible, std::vector<std::unique_ptr<CellInfo>> &new_cells);
// Runs get_inversion, then set_inversion.
bool handle_invertible_port(Context *ctx, CellInfo *cell, IdString port, bool invert, bool invertible, std::vector<std::unique_ptr<CellInfo>> &new_cells);
void handle_invertible_bus(Context *ctx, CellInfo *cell, IdString port, int len, std::vector<std::unique_ptr<CellInfo>> &new_cells);
void handle_bus(Context *ctx, CellInfo *cell, IdString port, int len);
NEXTPNR_NAMESPACE_END
#endif

0
leuctra/family.cmake Normal file
View File

124
leuctra/main.cc Normal file
View File

@ -0,0 +1,124 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@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.
*
*/
#ifdef MAIN_EXECUTABLE
#include <fstream>
#include "command.h"
#include "design_utils.h"
#include "log.h"
#include "timing.h"
#include "util.h"
#include "textcfg.h"
USING_NEXTPNR_NAMESPACE
class LeuctraCommandHandler : public CommandHandler
{
public:
LeuctraCommandHandler(int argc, char **argv);
virtual ~LeuctraCommandHandler(){};
std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) override;
void setupArchContext(Context *ctx) override{};
void customAfterLoad(Context *ctx) override;
void customBitstream(Context *ctx) override;
protected:
po::options_description getArchOptions() override;
};
LeuctraCommandHandler::LeuctraCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}
po::options_description LeuctraCommandHandler::getArchOptions()
{
po::options_description specific("Architecture specific options");
specific.add_options()("device", po::value<std::string>(), "select device");
specific.add_options()("package", po::value<std::string>(), "select device package");
specific.add_options()("speed", po::value<std::string>(), "select device speedgrade");
specific.add_options()("ucf", po::value<std::vector<std::string>>(), "UCF pin constraint file(s)");
specific.add_options()("ucf-allow-unconstrained", "don't require UCF file(s) to constrain all IO");
specific.add_options()("textcfg", po::value<std::string>(), "textual configuration in Leuctra format to write");
return specific;
}
void LeuctraCommandHandler::customBitstream(Context *ctx) {
if (vm.count("textcfg")) {
std::string filename = vm["textcfg"].as<std::string>();
std::ofstream f(filename);
write_textcfg(ctx, f);
}
}
std::unique_ptr<Context> LeuctraCommandHandler::createContext(std::unordered_map<std::string, Property> &values)
{
ArchArgs chipArgs;
if (vm.count("device"))
chipArgs.device = vm["device"].as<std::string>();
else
chipArgs.device = "xc6slx9";
if (vm.count("package"))
chipArgs.package = vm["package"].as<std::string>();
if (vm.count("speed"))
chipArgs.speed = vm["speed"].as<std::string>();
// XXX parse values
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
for (auto &val : values)
ctx->settings[ctx->id(val.first)] = val.second;
return ctx;
}
void LeuctraCommandHandler::customAfterLoad(Context *ctx)
{
if (vm.count("ucf")) {
std::vector<std::string> files = vm["ucf"].as<std::vector<std::string>>();
for (const auto &filename : files) {
std::ifstream in(filename);
if (!in)
log_error("failed to open UCF file '%s'\n", filename.c_str());
if (!ctx->applyUCF(filename, in))
log_error("failed to parse UCF file '%s'\n", filename.c_str());
}
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_obuf") ||
ci->type == ctx->id("$nextpnr_iobuf")) {
if (!ci->attrs.count(ctx->id("LOC"))) {
if (vm.count("ucf-allow-unconstrained"))
log_warning("IO '%s' is unconstrained in UCF and will be automatically placed\n",
cell.first.c_str(ctx));
else
log_error("IO '%s' is unconstrained in UCF (override this error with "
"--ucf-allow-unconstrained)\n",
cell.first.c_str(ctx));
}
}
}
}
}
int main(int argc, char *argv[])
{
LeuctraCommandHandler handler(argc, argv);
return handler.exec();
}
#endif

1245
leuctra/pack.cc Normal file

File diff suppressed because it is too large Load Diff

64
leuctra/textcfg.cc Normal file
View File

@ -0,0 +1,64 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@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.
*
*/
#include "nextpnr.h"
#include "textcfg.h"
NEXTPNR_NAMESPACE_BEGIN
void write_textcfg(const Context *ctx, std::ostream &out)
{
out << "DEVICE " << ctx->args.device << " " << ctx->args.package << " " << ctx->args.speed << std::endl;
for (auto &cell : ctx->cells) {
auto &belid = cell.second->bel;
auto &bel = ctx->getTileTypeBel(belid);
auto name = IdString(bel.name_id);
out << "PRIM " << belid.location.x << " " << belid.location.y << " " << name.str(ctx) << " " << cell.second->name.str(ctx) << std::endl;
for (auto &attr : cell.second->params) {
if (attr.second.is_string)
out << "PARAMSTR " << attr.first.str(ctx) << " " << attr.second.as_string() << std::endl;
else {
std::string sv;
for (auto bit: attr.second.as_bits())
sv.push_back(bit ? '1' : '0');
out << "PARAM " << attr.first.str(ctx) << " " << sv << std::endl;
}
}
}
for (auto &net : ctx->nets) {
out << "NET " << net.second->name.str(ctx) << std::endl;
if (net.second->driver.cell)
out << "FROM " << net.second->driver.cell->name.str(ctx) << " " << net.second->driver.port.str(ctx) << std::endl;
for (auto &user : net.second->users) {
out << "TO " << user.cell->name.str(ctx) << " " << user.port.str(ctx) << std::endl;
}
for (auto &wire : net.second->wires) {
auto &pip = wire.second.pip;
if (pip != PipId() && pip.kind == PIP_KIND_PIP) {
WireId dst = ctx->getPipDstWire(pip);
WireId src = ctx->getPipSrcWire(pip);
out << "PIP " << pip.location.x << " " << pip.location.y << " " << ctx->getWireBasename(dst).str(ctx) << " " << ctx->getWireBasename(src).str(ctx) << std::endl;
}
}
}
}
NEXTPNR_NAMESPACE_END

32
leuctra/textcfg.h Normal file
View File

@ -0,0 +1,32 @@
/*
* 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 TEXTCFG_H
#define TEXTCFG_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
void write_textcfg(const Context *ctx, std::ostream &out);
NEXTPNR_NAMESPACE_END
#endif // TEXTCFG_H

146
leuctra/ucf.cc Normal file
View File

@ -0,0 +1,146 @@
/*
* 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.
*
*/
#include <boost/algorithm/string.hpp>
#include <sstream>
#include <cctype>
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
bool Arch::applyUCF(std::string filename, std::istream &in)
{
auto isempty = [](const std::string &str) {
return std::all_of(str.begin(), str.end(), [](char c) { return isblank(c) || c == '\r' || c == '\n'; });
};
auto strip_quotes = [](const std::string &str) {
if (str.at(0) == '"') {
NPNR_ASSERT(str.back() == '"');
return str.substr(1, str.size() - 2);
} else {
return str;
}
};
try {
if (!in)
log_error("failed to open UCF file\n");
std::string line;
std::string linebuf;
int lineno = 0;
while (std::getline(in, line)) {
++lineno;
size_t cstart = line.find('#');
if (cstart != std::string::npos)
line = line.substr(0, cstart);
if (isempty(line))
continue;
linebuf += line;
// Look for a command up to a semicolon
size_t scpos = linebuf.find(';');
while (scpos != std::string::npos) {
std::string command = linebuf.substr(0, scpos);
// Split command into words
std::stringstream ss(command);
std::vector<std::string> words;
std::string tmp;
while (ss >> tmp)
words.push_back(tmp);
if (words.size() >= 0) {
std::string verb = words.at(0);
if (verb == "CONFIG") {
log_warning(" ignoring unsupported UCF command '%s' (on line %d)\n", command.c_str(),
lineno);
} else if (verb == "NET") {
if (words.size() < 2)
log_error("expected name after NET (on line %d)\n", lineno);
std::string target = strip_quotes(words.at(1));
int pos = 2;
while (pos < words.size()) {
std::string attr = words.at(pos);
pos++;
if (attr == "LOC" || attr == "IOSTANDARD" || attr == "DRIVE" || attr == "SLEW") {
if (pos + 2 > words.size() || words.at(pos) != "=")
log_error("expected %s = value (on line %d)\n", attr.c_str(), lineno);
std::string value = strip_quotes(words.at(pos + 1));
pos += 2;
auto fnd_cell = cells.find(id(target));
if (fnd_cell != cells.end()) {
if (attr == "LOC")
fnd_cell->second->attrs[id(attr)] = value;
else
fnd_cell->second->params[id(attr)] = value;
}
} else if (attr == "PULLUP" || attr == "PULLDOWN" || attr == "KEEPER") {
auto fnd_cell = cells.find(id(target));
if (fnd_cell != cells.end()) {
fnd_cell->second->params[id("PULLTYPE")] = attr;
}
} else if (attr == "PERIOD") {
if (pos + 2 > words.size() || words.at(pos) != "=")
log_error("expected PERIOD = value (on line %d)\n", lineno);
std::string value = words.at(pos + 1);
pos += 2;
int upos = 0;
while (upos < value.size() && (std::isdigit(value[upos]) || value[upos] == '.'))
upos++;
float freq = std::stof(value.substr(0, upos));
std::string unit = value.substr(upos);
boost::algorithm::to_upper(unit);
if (unit == "MHZ")
;
else if (unit == "KHZ")
freq /= 1.0e3;
else if (unit == "HZ")
freq /= 1.0e6;
else
log_error("unsupported frequency unit '%s' (on line %d)\n", unit.c_str(), lineno);
addClock(id(target), freq);
} else {
log_warning(" ignoring unsupported NET attribute '%s' (on line %d)\n", attr.c_str(),
lineno);
}
if (pos < words.size()) {
std::string cur = words.at(pos);
if (cur != "|")
log_error("expected | before %s (on line %d)\n", cur.c_str(), lineno);
pos++;
}
}
} else {
log_warning(" ignoring unsupported UCF command '%s' (on line %d)\n", verb.c_str(),
lineno);
}
}
linebuf = linebuf.substr(scpos + 1);
scpos = linebuf.find(';');
}
}
if (!isempty(linebuf))
log_error("unexpected end of UCF file\n");
settings.emplace(id("input/ucf"), filename);
return true;
} catch (log_execution_error_exception) {
return false;
}
}
NEXTPNR_NAMESPACE_END