wip
This commit is contained in:
parent
1c9f7e2113
commit
20dfebb09f
@ -443,6 +443,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);
|
||||
}
|
||||
}
|
||||
|
||||
if (usedLocations.count(cloc))
|
||||
continue;
|
||||
if (valid_loc_for(child, cloc, solution, usedLocations)) {
|
||||
success = true;
|
||||
break;
|
||||
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 (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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -18,6 +18,10 @@
|
||||
*/
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include <fstream>
|
||||
#include "log.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
|
||||
|
||||
@ -44,8 +48,38 @@ void MainWindow::newContext(Context *ctx)
|
||||
setWindowTitle(title.c_str());
|
||||
}
|
||||
|
||||
void MainWindow::createMenu() {}
|
||||
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(false);
|
||||
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() {}
|
||||
|
||||
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
|
||||
|
@ -38,6 +38,10 @@ class MainWindow : public BaseMainWindow
|
||||
protected Q_SLOTS:
|
||||
void new_proj() override;
|
||||
void newContext(Context *ctx);
|
||||
void open_ucf();
|
||||
|
||||
private:
|
||||
QAction *actionLoadUCF;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -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;
|
||||
|
@ -20,7 +20,9 @@
|
||||
|
||||
#include "nextpnr.h"
|
||||
#include "placer1.h"
|
||||
#include "placer_heap.h"
|
||||
#include "router1.h"
|
||||
#include "util.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
@ -451,8 +453,21 @@ BelPin BelPinIterator::operator*() const {
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// XXX package pins
|
||||
|
||||
BelId Arch::getPackagePinBel(const std::string &pin) const
|
||||
{
|
||||
IdString pin_id = id(pin);
|
||||
for (int i = 0; i < package_info->num_pins; i++) {
|
||||
if (package_info->pin_data[i].name_id == pin_id.index) {
|
||||
BelId bel;
|
||||
bel.location.x = package_info->pin_data[i].bel.tile_x;
|
||||
bel.location.y = package_info->pin_data[i].bel.tile_y;
|
||||
bel.index = package_info->pin_data[i].bel.bel_idx;
|
||||
return bel;
|
||||
}
|
||||
}
|
||||
return BelId();
|
||||
}
|
||||
|
||||
std::vector<IdString> Arch::getBelPins(BelId bel) const
|
||||
{
|
||||
@ -490,7 +505,24 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); }
|
||||
bool Arch::place()
|
||||
{
|
||||
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
|
||||
|
||||
if (placer == "heap") {
|
||||
PlacerHeapCfg cfg(getCtx());
|
||||
cfg.criticalityExponent = 7;
|
||||
cfg.ioBufTypes.insert(id("IOB"));
|
||||
if (!placer_heap(getCtx(), cfg))
|
||||
return false;
|
||||
} else if (placer == "sa") {
|
||||
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
|
||||
return false;
|
||||
} else {
|
||||
log_error("Leuctra architecture does not support placer '%s'\n", placer.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
|
||||
|
||||
@ -563,4 +595,16 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
||||
return info;
|
||||
}
|
||||
|
||||
#ifdef WITH_HEAP
|
||||
const std::string Arch::defaultPlacer = "heap";
|
||||
#else
|
||||
const std::string Arch::defaultPlacer = "sa";
|
||||
#endif
|
||||
|
||||
const std::vector<std::string> Arch::availablePlacers = {"sa",
|
||||
#ifdef WITH_HEAP
|
||||
"heap"
|
||||
#endif
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -789,6 +789,16 @@ struct Arch : BaseCtx
|
||||
|
||||
std::vector<IdString> getBelPins(BelId bel) const;
|
||||
|
||||
BelId getRelatedBel(BelId bel, int relation) const {
|
||||
auto &tile = getTile(bel.location);
|
||||
auto &related = tile.bels[bel.index].related[relation];
|
||||
BelId res;
|
||||
res.location.x = related.tile_x;
|
||||
res.location.y = related.tile_y;
|
||||
res.index = related.bel_idx;
|
||||
return res;
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
WireId getWireByName(IdString name) const;
|
||||
@ -1107,11 +1117,6 @@ struct Arch : BaseCtx
|
||||
}
|
||||
|
||||
BelId getPackagePinBel(const std::string &pin) const;
|
||||
std::string getBelPackagePin(BelId bel) const;
|
||||
int getPioBelBank(BelId bel) const;
|
||||
// For getting GCLK, PLL, Vref, etc, pins
|
||||
std::string getPioFunctionName(BelId bel) const;
|
||||
BelId getPioByFunctionName(const std::string &name) const;
|
||||
|
||||
PortType getBelPinType(BelId bel, IdString pin) const;
|
||||
|
||||
@ -1170,10 +1175,25 @@ struct Arch : BaseCtx
|
||||
// -------------------------------------------------
|
||||
// Placement validity checks
|
||||
// TODO: validate bel subtype (SLICEM vs SLICEL, IOBM vs IOBS, ...).
|
||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const { return true; }
|
||||
bool isBelLocationValid(BelId bel) const { return true; }
|
||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const {
|
||||
if (cell->type == id("LEUCTRA_FF") && (0x924924ull & 1ull << bel.index))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool isBelLocationValid(BelId bel) const {
|
||||
CellInfo *cell = getBoundBelCell(bel);
|
||||
if (cell == nullptr)
|
||||
return true;
|
||||
else
|
||||
return isValidBelForCell(cell, bel);
|
||||
}
|
||||
|
||||
// Apply UCF constraints to the context
|
||||
bool applyUCF(std::string filename, std::istream &in);
|
||||
|
||||
//void assignArchInfo();
|
||||
static const std::string defaultPlacer;
|
||||
static const std::vector<std::string> availablePlacers;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
244
leuctra/cells.cc
Normal file
244
leuctra/cells.cc
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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 "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
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")] = "FFSYNC";
|
||||
new_cell->params[ctx->id("INIT")] = "0";
|
||||
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("INIT")] = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
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(), "O6", PORT_OUT);
|
||||
} else if (type == ctx->id("IOB")) {
|
||||
new_cell->params[ctx->id("DIR")] = "INPUT";
|
||||
new_cell->attrs[ctx->id("IOSTANDARD")] = "LVCMOS33";
|
||||
|
||||
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);
|
||||
} 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(), "OQ", PORT_OUT);
|
||||
add_port(ctx, new_cell.get(), "T1", PORT_IN);
|
||||
add_port(ctx, new_cell.get(), "TQ", PORT_OUT);
|
||||
} else if (type == ctx->id("LUT1")) {
|
||||
new_cell->params[ctx->id("INIT")] = "0";
|
||||
|
||||
add_port(ctx, new_cell.get(), "I0", PORT_IN);
|
||||
add_port(ctx, new_cell.get(), "O", 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")] = "INPUT";
|
||||
replace_port(nxio, ctx->id("O"), iob, ctx->id("I"));
|
||||
} else if (nxio->type == ctx->id("$nextpnr_obuf")) {
|
||||
iob->params[ctx->id("DIR")] = "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")] = "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")] = "1";
|
||||
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>> &created_cells,
|
||||
std::unordered_set<IdString> &todelete_cells)
|
||||
{
|
||||
if (orig->type == ctx->id("FDRE")) {
|
||||
ff->params[ctx->id("MODE")] = "FF_SYNC";
|
||||
ff->params[ctx->id("INIT")] = "0";
|
||||
replace_port(orig, ctx->id("D"), ff, ctx->id("D"));
|
||||
replace_port(orig, ctx->id("C"), ff, ctx->id("CLK"));
|
||||
replace_port(orig, ctx->id("CE"), ff, ctx->id("CE"));
|
||||
replace_port(orig, ctx->id("R"), ff, ctx->id("SR"));
|
||||
replace_port(orig, ctx->id("Q"), ff, ctx->id("Q"));
|
||||
} else {
|
||||
NPNR_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void convert_lut(Context *ctx, CellInfo *orig, CellInfo *lut, std::vector<std::unique_ptr<CellInfo>> &created_cells,
|
||||
std::unordered_set<IdString> &todelete_cells)
|
||||
{
|
||||
auto &orig_init = orig->params[ctx->id("INIT")];
|
||||
std::string new_init = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
int nbits = 0;
|
||||
if (orig->type == ctx->id("LUT1")) {
|
||||
nbits = 2;
|
||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
||||
} else if (orig->type == ctx->id("LUT2")) {
|
||||
nbits = 4;
|
||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
||||
} else if (orig->type == ctx->id("LUT3")) {
|
||||
nbits = 8;
|
||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
||||
replace_port(orig, ctx->id("I2"), lut, ctx->id("I3"));
|
||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
||||
} else if (orig->type == ctx->id("LUT4")) {
|
||||
nbits = 16;
|
||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
||||
replace_port(orig, ctx->id("I2"), lut, ctx->id("I3"));
|
||||
replace_port(orig, ctx->id("I3"), lut, ctx->id("I4"));
|
||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
||||
} else if (orig->type == ctx->id("LUT5")) {
|
||||
nbits = 32;
|
||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
||||
replace_port(orig, ctx->id("I2"), lut, ctx->id("I3"));
|
||||
replace_port(orig, ctx->id("I3"), lut, ctx->id("I4"));
|
||||
replace_port(orig, ctx->id("I4"), lut, ctx->id("I5"));
|
||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
||||
} else if (orig->type == ctx->id("LUT6")) {
|
||||
new_init = orig_init;
|
||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
||||
replace_port(orig, ctx->id("I2"), lut, ctx->id("I3"));
|
||||
replace_port(orig, ctx->id("I3"), lut, ctx->id("I4"));
|
||||
replace_port(orig, ctx->id("I4"), lut, ctx->id("I5"));
|
||||
replace_port(orig, ctx->id("I5"), lut, ctx->id("I6"));
|
||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
||||
} else {
|
||||
NPNR_ASSERT(false);
|
||||
}
|
||||
if (nbits) {
|
||||
unsigned long init = std::stoul(orig_init);
|
||||
for (int i = 0; i < 64; i++) {
|
||||
int obit = i % nbits;
|
||||
new_init[63-i] = '0' + (init >> obit & 1);
|
||||
}
|
||||
}
|
||||
lut->params[ctx->id("INIT")] = new_init;
|
||||
}
|
||||
|
||||
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->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;
|
||||
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->constr_parent = iob;
|
||||
iob->constr_children.push_back(ologic);
|
||||
// XXX enum
|
||||
ologic->constr_spec = 2;
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
@ -24,6 +24,10 @@
|
||||
|
||||
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")
|
||||
@ -36,6 +40,30 @@ inline bool is_xilinx_iobuf(const BaseCtx *ctx, const CellInfo *cell) {
|
||||
|| cell->type == ctx->id("IOBUFDS");
|
||||
}
|
||||
|
||||
inline bool is_xilinx_ff(const BaseCtx *ctx, const CellInfo *cell) {
|
||||
return cell->type == ctx->id("FDRE");
|
||||
}
|
||||
|
||||
inline bool is_xilinx_lut(const BaseCtx *ctx, const CellInfo *cell) {
|
||||
return 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>> &created_cells,
|
||||
std::unordered_set<IdString> &todelete_cells);
|
||||
void convert_lut(Context *ctx, CellInfo *orig, CellInfo *lc, std::vector<std::unique_ptr<CellInfo>> &created_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);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "timing.h"
|
||||
#include "util.h"
|
||||
#include "textcfg.h"
|
||||
|
||||
USING_NEXTPNR_NAMESPACE
|
||||
|
||||
@ -34,6 +36,7 @@ class LeuctraCommandHandler : public CommandHandler
|
||||
virtual ~LeuctraCommandHandler(){};
|
||||
std::unique_ptr<Context> createContext() override;
|
||||
void setupArchContext(Context *ctx) override{};
|
||||
void customAfterLoad(Context *ctx) override;
|
||||
void customBitstream(Context *ctx) override;
|
||||
|
||||
protected:
|
||||
@ -48,10 +51,19 @@ po::options_description LeuctraCommandHandler::getArchOptions()
|
||||
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) { log_error("Here is when bitstream gets created"); }
|
||||
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()
|
||||
{
|
||||
@ -66,6 +78,36 @@ std::unique_ptr<Context> LeuctraCommandHandler::createContext()
|
||||
return std::unique_ptr<Context>(new Context(chipArgs));
|
||||
}
|
||||
|
||||
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);
|
||||
|
201
leuctra/pack.cc
201
leuctra/pack.cc
@ -18,7 +18,9 @@
|
||||
*/
|
||||
|
||||
#include "nextpnr.h"
|
||||
#include "cells.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
@ -28,6 +30,11 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell)
|
||||
cell->type == ctx->id("$nextpnr_iobuf");
|
||||
}
|
||||
|
||||
static bool is_iob(Context *ctx, CellInfo *cell)
|
||||
{
|
||||
return cell->type == ctx->id("IOB");
|
||||
}
|
||||
|
||||
class LeuctraPacker
|
||||
{
|
||||
public:
|
||||
@ -50,17 +57,207 @@ class LeuctraPacker
|
||||
// Remove nextpnr iob cells, insert Xilinx primitives instead.
|
||||
void pack_iob()
|
||||
{
|
||||
log_info("Packing IOBs..\n");
|
||||
log_info("Packing IOBs...\n");
|
||||
|
||||
// XXX
|
||||
for (auto cell : sorted(ctx->cells)) {
|
||||
CellInfo *ci = cell.second;
|
||||
if (is_nextpnr_iob(ctx, ci)) {
|
||||
CellInfo *iob = nullptr;
|
||||
std::unique_ptr<CellInfo> io_cell =
|
||||
create_leuctra_cell(ctx, ctx->id("IOB"), ci->name.str(ctx) + "$iob");
|
||||
nxio_to_iob(ctx, ci, io_cell.get(), new_cells, packed_cells);
|
||||
new_cells.push_back(std::move(io_cell));
|
||||
iob = new_cells.back().get();
|
||||
|
||||
packed_cells.insert(ci->name);
|
||||
if (iob != nullptr) {
|
||||
for (const auto &attr : ci->attrs)
|
||||
iob->attrs[attr.first] = attr.second;
|
||||
|
||||
auto loc_attr = iob->attrs.find(ctx->id("LOC"));
|
||||
if (loc_attr != iob->attrs.end()) {
|
||||
std::string pin = loc_attr->second;
|
||||
BelId pinBel = ctx->getPackagePinBel(pin);
|
||||
if (pinBel == BelId()) {
|
||||
log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n",
|
||||
iob->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str());
|
||||
} else {
|
||||
log_info("pin '%s' constrained to Bel '%s'.\n", iob->name.c_str(ctx),
|
||||
ctx->getBelName(pinBel).c_str(ctx));
|
||||
}
|
||||
iob->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
// Ensure ilogic/ologic cell for every IOB that needs one.
|
||||
void pack_iologic()
|
||||
{
|
||||
log_info("Packing ILOGICs/OLOGICs...\n");
|
||||
|
||||
for (auto cell : sorted(ctx->cells)) {
|
||||
CellInfo *ci = cell.second;
|
||||
if (is_iob(ctx, ci)) {
|
||||
NetInfo *net_i = ci->ports.at(ctx->id("I")).net;
|
||||
if (net_i != nullptr) {
|
||||
// Insert ILOGIC.
|
||||
std::unique_ptr<CellInfo> ilogic =
|
||||
create_leuctra_cell(ctx, ctx->id("ILOGIC2"), ci->name.str(ctx) + "$ilogic");
|
||||
insert_ilogic_pass(ctx, ci, ilogic.get());
|
||||
|
||||
new_cells.push_back(std::move(ilogic));
|
||||
}
|
||||
NetInfo *net_o = ci->ports.at(ctx->id("O")).net;
|
||||
if (net_o != nullptr) {
|
||||
// Insert OLOGIC.
|
||||
std::unique_ptr<CellInfo> ologic =
|
||||
create_leuctra_cell(ctx, ctx->id("OLOGIC2"), ci->name.str(ctx) + "$ologic");
|
||||
insert_ologic_pass(ctx, ci, ologic.get());
|
||||
|
||||
new_cells.push_back(std::move(ologic));
|
||||
}
|
||||
auto bel_attr = ci->attrs.find(ctx->id("BEL"));
|
||||
if (bel_attr != ci->attrs.end()) {
|
||||
BelId bel = ctx->getBelByName(ctx->id(ci->attrs[ctx->id("BEL")]));
|
||||
for (auto &child : ci->constr_children) {
|
||||
BelId child_bel = ctx->getRelatedBel(bel, child->constr_spec);
|
||||
child->attrs[ctx->id("BEL")] = ctx->getBelName(child_bel).str(ctx);
|
||||
child->constr_parent = nullptr;
|
||||
child->constr_spec = -1;
|
||||
|
||||
}
|
||||
ci->constr_children.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
// Convert FFs/latches to LEUCTRA_FFs.
|
||||
void pack_ff()
|
||||
{
|
||||
log_info("Packing FFs...\n");
|
||||
|
||||
for (auto cell : sorted(ctx->cells)) {
|
||||
CellInfo *ci = cell.second;
|
||||
if (is_xilinx_ff(ctx, ci)) {
|
||||
std::unique_ptr<CellInfo> ff_cell =
|
||||
create_leuctra_cell(ctx, ctx->id("LEUCTRA_FF"), ci->name.str(ctx) + "$ff");
|
||||
convert_ff(ctx, ci, ff_cell.get(), new_cells, packed_cells);
|
||||
new_cells.push_back(std::move(ff_cell));
|
||||
|
||||
packed_cells.insert(ci->name);
|
||||
}
|
||||
}
|
||||
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
// Convert FFs/latches to LEUCTRA_FFs.
|
||||
void pack_lut()
|
||||
{
|
||||
log_info("Packing LUTs...\n");
|
||||
|
||||
for (auto cell : sorted(ctx->cells)) {
|
||||
CellInfo *ci = cell.second;
|
||||
if (is_xilinx_lut(ctx, ci)) {
|
||||
std::unique_ptr<CellInfo> lut_cell =
|
||||
create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), ci->name.str(ctx) + "$lc");
|
||||
convert_lut(ctx, ci, lut_cell.get(), new_cells, packed_cells);
|
||||
new_cells.push_back(std::move(lut_cell));
|
||||
|
||||
packed_cells.insert(ci->name);
|
||||
}
|
||||
}
|
||||
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
// Merge a net into a constant net
|
||||
void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constnet, bool constval)
|
||||
{
|
||||
orig->driver.cell = nullptr;
|
||||
for (auto user : orig->users) {
|
||||
if (user.cell != nullptr) {
|
||||
CellInfo *uc = user.cell;
|
||||
if (ctx->verbose)
|
||||
log_info("%s user %s\n", orig->name.c_str(ctx), uc->name.c_str(ctx));
|
||||
uc->ports[user.port].net = constnet;
|
||||
constnet->users.push_back(user);
|
||||
}
|
||||
}
|
||||
orig->users.clear();
|
||||
}
|
||||
|
||||
// Pack constants (simple implementation)
|
||||
void pack_constants()
|
||||
{
|
||||
log_info("Packing constants..\n");
|
||||
|
||||
std::unique_ptr<CellInfo> gnd_cell = create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), "$PACKER_GND");
|
||||
gnd_cell->params[ctx->id("INIT")] = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||
gnd_net->name = ctx->id("$PACKER_GND_NET");
|
||||
gnd_net->driver.cell = gnd_cell.get();
|
||||
gnd_net->driver.port = ctx->id("O6");
|
||||
gnd_cell->ports.at(ctx->id("O6")).net = gnd_net.get();
|
||||
|
||||
std::unique_ptr<CellInfo> vcc_cell = create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), "$PACKER_VCC");
|
||||
vcc_cell->params[ctx->id("INIT")] = "1111111111111111111111111111111111111111111111111111111111111111";
|
||||
std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||
vcc_net->name = ctx->id("$PACKER_VCC_NET");
|
||||
vcc_net->driver.cell = vcc_cell.get();
|
||||
vcc_net->driver.port = ctx->id("O6");
|
||||
vcc_cell->ports.at(ctx->id("O6")).net = vcc_net.get();
|
||||
|
||||
std::vector<IdString> dead_nets;
|
||||
|
||||
bool gnd_used = false, vcc_used = false;
|
||||
|
||||
for (auto net : sorted(ctx->nets)) {
|
||||
NetInfo *ni = net.second;
|
||||
if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) {
|
||||
IdString drv_cell = ni->driver.cell->name;
|
||||
set_net_constant(ctx, ni, gnd_net.get(), false);
|
||||
gnd_used = true;
|
||||
dead_nets.push_back(net.first);
|
||||
ctx->cells.erase(drv_cell);
|
||||
} else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) {
|
||||
IdString drv_cell = ni->driver.cell->name;
|
||||
set_net_constant(ctx, ni, vcc_net.get(), true);
|
||||
vcc_used = true;
|
||||
dead_nets.push_back(net.first);
|
||||
ctx->cells.erase(drv_cell);
|
||||
}
|
||||
}
|
||||
|
||||
if (gnd_used) {
|
||||
ctx->cells[gnd_cell->name] = std::move(gnd_cell);
|
||||
ctx->nets[gnd_net->name] = std::move(gnd_net);
|
||||
}
|
||||
if (vcc_used) {
|
||||
ctx->cells[vcc_cell->name] = std::move(vcc_cell);
|
||||
ctx->nets[vcc_net->name] = std::move(vcc_net);
|
||||
}
|
||||
|
||||
for (auto dn : dead_nets) {
|
||||
ctx->nets.erase(dn);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void pack()
|
||||
{
|
||||
pack_iob();
|
||||
pack_iologic();
|
||||
pack_ff();
|
||||
pack_lut();
|
||||
pack_constants();
|
||||
}
|
||||
|
||||
private:
|
||||
|
59
leuctra/textcfg.cc
Normal file
59
leuctra/textcfg.cc
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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->attrs) {
|
||||
out << "OPT " << attr.first.str(ctx) << " " << attr.second << std::endl;
|
||||
}
|
||||
for (auto ¶m : cell.second->params) {
|
||||
out << "OPT " << param.first.str(ctx) << " " << param.second << std::endl;
|
||||
}
|
||||
}
|
||||
for (auto &net : ctx->nets) {
|
||||
out << "NET " << net.second->name.str(ctx) << std::endl;
|
||||
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
|
||||
|
143
leuctra/ucf.cc
Normal file
143
leuctra/ucf.cc
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 LPF 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()) {
|
||||
fnd_cell->second->attrs[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->attrs[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