Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bd69a7c113 | ||
![]() |
7f437cbf62 | ||
![]() |
5db6939a8e | ||
![]() |
0cb4f508f7 | ||
![]() |
1c9cf8adb1 | ||
![]() |
1d241b1d3f | ||
![]() |
71b9d146ce | ||
![]() |
270a0c4088 | ||
![]() |
14ce3d2029 | ||
![]() |
7fc7d5057b | ||
![]() |
b4ee8fc639 | ||
![]() |
20dfebb09f | ||
![]() |
1c9f7e2113 | ||
![]() |
048bae1d85 |
@ -44,7 +44,7 @@ if (EXTERNAL_CHIPDB)
|
||||
endif()
|
||||
|
||||
# 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_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
|
||||
@ -67,6 +67,11 @@ foreach(item ${ARCH})
|
||||
if (NOT item IN_LIST FAMILIES)
|
||||
message(FATAL_ERROR "Architecture '${item}' not in list of supported architectures")
|
||||
endif()
|
||||
if (item STREQUAL "leuctra")
|
||||
if (NOT EXTERNAL_CHIPDB)
|
||||
message(FATAL_ERROR "The leuctra backend requires setting -DEXTERNAL_CHIPDB=ON")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
@ -111,6 +111,7 @@ void disconnect_port(const Context *ctx, CellInfo *cell, IdString port_name)
|
||||
port.net->users.end());
|
||||
if (port.net->driver.cell == cell && port.net->driver.port == port_name)
|
||||
port.net->driver.cell = nullptr;
|
||||
port.net = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,6 +199,28 @@ struct Loc
|
||||
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
|
||||
{
|
||||
int32_t index = -1;
|
||||
@ -443,6 +465,7 @@ struct CellInfo : ArchCellInfo
|
||||
int constr_y = UNCONSTR; // this.y - parent.y
|
||||
int constr_z = UNCONSTR; // this.z - parent.z
|
||||
bool constr_abs_z = false; // parent.z := 0
|
||||
int constr_spec = -1;
|
||||
// parent.[xyz] := 0 when (constr_parent == nullptr)
|
||||
|
||||
Region *region = nullptr;
|
||||
|
@ -234,7 +234,7 @@ class ConstraintLegaliseWorker
|
||||
}
|
||||
if (!ctx->checkBelAvail(locBel)) {
|
||||
CellInfo *confCell = ctx->getConflictingBelCell(locBel);
|
||||
if (confCell->belStrength >= STRENGTH_STRONG) {
|
||||
if (confCell != cell && confCell->belStrength >= STRENGTH_STRONG) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -247,49 +247,59 @@ class ConstraintLegaliseWorker
|
||||
usedLocations.insert(loc);
|
||||
for (auto child : cell->constr_children) {
|
||||
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;
|
||||
while (!xSearch.done()) {
|
||||
Loc cloc;
|
||||
cloc.x = xSearch.get();
|
||||
cloc.y = ySearch.get();
|
||||
cloc.z = zSearch.get();
|
||||
|
||||
zSearch.next();
|
||||
if (zSearch.done()) {
|
||||
zSearch.reset();
|
||||
ySearch.next();
|
||||
if (ySearch.done()) {
|
||||
ySearch.reset();
|
||||
xSearch.next();
|
||||
if (child->constr_spec != -1) {
|
||||
BelId child_bel = ctx->getRelatedBel(locBel, child->constr_spec);
|
||||
if (child_bel != BelId()) {
|
||||
Loc cloc = ctx->getBelLocation(child_bel);
|
||||
if (!usedLocations.count(cloc) && valid_loc_for(child, cloc, solution, usedLocations)) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
while (!xSearch.done()) {
|
||||
Loc cloc;
|
||||
cloc.x = xSearch.get();
|
||||
cloc.y = ySearch.get();
|
||||
cloc.z = zSearch.get();
|
||||
|
||||
if (usedLocations.count(cloc))
|
||||
continue;
|
||||
if (valid_loc_for(child, cloc, solution, usedLocations)) {
|
||||
success = true;
|
||||
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) {
|
||||
usedLocations.erase(loc);
|
||||
return false;
|
||||
@ -316,7 +326,7 @@ class ConstraintLegaliseWorker
|
||||
return true; // Only process chain roots
|
||||
if (constraints_satisfied(cell)) {
|
||||
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);
|
||||
} else {
|
||||
IncreasingDiameterSearch xRootSearch, yRootSearch, zRootSearch;
|
||||
@ -513,16 +523,26 @@ int get_constraints_distance(const Context *ctx, const CellInfo *cell)
|
||||
if (cell->constr_parent->bel == BelId())
|
||||
return 100000;
|
||||
Loc parent_loc = ctx->getBelLocation(cell->constr_parent->bel);
|
||||
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));
|
||||
}
|
||||
if (cell->constr_spec != -1) {
|
||||
BelId child_bel = ctx->getRelatedBel(cell->constr_parent->bel, cell->constr_spec);
|
||||
if (child_bel == BelId())
|
||||
return 100000;
|
||||
Loc child_loc = ctx->getBelLocation(child_bel);
|
||||
dist += std::abs(child_loc.x - loc.x);
|
||||
dist += std::abs(child_loc.y - loc.y);
|
||||
dist += std::abs(child_loc.z - 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)
|
||||
dist += get_constraints_distance(ctx, child);
|
||||
|
@ -960,12 +960,18 @@ class HeAPPlacer
|
||||
targets.emplace_back(vc, target);
|
||||
for (auto child : vc->constr_children) {
|
||||
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)
|
||||
cloc.x += child->constr_x;
|
||||
if (child->constr_y != child->UNCONSTR)
|
||||
cloc.y += child->constr_y;
|
||||
if (child->constr_z != child->UNCONSTR)
|
||||
cloc.z = child->constr_abs_z ? child->constr_z : (ploc.z + child->constr_z);
|
||||
}
|
||||
visit.emplace(child, cloc);
|
||||
}
|
||||
}
|
||||
|
@ -497,7 +497,8 @@ struct Router1
|
||||
}
|
||||
qw.randtag = ctx->rng();
|
||||
|
||||
queue.push(qw);
|
||||
if (src_wire != dst_wire)
|
||||
queue.push(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_without_ripup = 0;
|
||||
|
||||
log_info(" | (re-)routed arcs | delta | remaining\n");
|
||||
log_info(" IterCnt | w/ripup wo/ripup | w/r wo/r | arcs\n");
|
||||
log_info(" | (re-)routed arcs | delta | remaining| time spent |\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()) {
|
||||
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 - 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_without_ripup = router.arcs_without_ripup;
|
||||
ctx->yield();
|
||||
@ -800,12 +806,13 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
|
||||
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();
|
||||
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();
|
||||
log_info("Route time %.02fs\n", std::chrono::duration<float>(rend - rstart).count());
|
||||
|
||||
|
1066
common/router2.cc
Normal file
1066
common/router2.cc
Normal file
File diff suppressed because it is too large
Load Diff
26
common/router2.h
Normal file
26
common/router2.h
Normal 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
|
@ -175,12 +175,23 @@ struct Timing
|
||||
|
||||
// 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
|
||||
int found = 0;
|
||||
for (auto i : input_ports) {
|
||||
DelayInfo comb_delay;
|
||||
bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay);
|
||||
if (is_path)
|
||||
if (is_path) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -607,6 +607,8 @@ struct Arch : BaseCtx
|
||||
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>> ret;
|
||||
|
@ -203,6 +203,7 @@ struct Arch : BaseCtx
|
||||
CellInfo *getConflictingBelCell(BelId bel) const;
|
||||
const std::vector<BelId> &getBels() 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;
|
||||
WireId getBelPinWire(BelId bel, IdString pin) const;
|
||||
PortType getBelPinType(BelId bel, IdString pin) const;
|
||||
|
@ -313,7 +313,7 @@ void DesignWidget::newContext(Context *ctx)
|
||||
wireMap[std::pair<int, int>(wire->x, wire->y)].push_back(wireid);
|
||||
}
|
||||
#endif
|
||||
#ifdef ARCH_ECP5
|
||||
#if defined(ARCH_ECP5) || defined(ARCH_LEUCTRA)
|
||||
for (const auto &wire : ctx->getWires()) {
|
||||
wireMap[std::pair<int, int>(wire.location.x, wire.location.y)].push_back(wire);
|
||||
}
|
||||
|
0
gui/leuctra/family.cmake
Normal file
0
gui/leuctra/family.cmake
Normal file
93
gui/leuctra/mainwindow.cc
Normal file
93
gui/leuctra/mainwindow.cc
Normal 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
49
gui/leuctra/mainwindow.h
Normal 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
2
gui/leuctra/nextpnr.qrc
Normal file
@ -0,0 +1,2 @@
|
||||
<RCC>
|
||||
</RCC>
|
@ -523,6 +523,8 @@ struct Arch : BaseCtx
|
||||
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;
|
||||
|
||||
WireId getBelPinWire(BelId bel, IdString pin) const;
|
||||
|
1026
leuctra/arch.cc
Normal file
1026
leuctra/arch.cc
Normal file
File diff suppressed because it is too large
Load Diff
1202
leuctra/arch.h
Normal file
1202
leuctra/arch.h
Normal file
File diff suppressed because it is too large
Load Diff
42
leuctra/arch_pybindings.cc
Normal file
42
leuctra/arch_pybindings.cc
Normal 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
83
leuctra/arch_pybindings.h
Normal 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
234
leuctra/archdefs.h
Normal 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
905
leuctra/cells.cc
Normal 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 ¶m : 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
98
leuctra/cells.h
Normal 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
0
leuctra/family.cmake
Normal file
124
leuctra/main.cc
Normal file
124
leuctra/main.cc
Normal 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
1245
leuctra/pack.cc
Normal file
File diff suppressed because it is too large
Load Diff
64
leuctra/textcfg.cc
Normal file
64
leuctra/textcfg.cc
Normal 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
32
leuctra/textcfg.h
Normal 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
146
leuctra/ucf.cc
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user