This commit is contained in:
Marcin Kościelnicki 2020-02-01 11:53:56 +01:00
parent 20dfebb09f
commit b4ee8fc639
15 changed files with 1772 additions and 182 deletions

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -175,12 +175,23 @@ struct Timing
// Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and
// 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;
}
}
}
}

View File

@ -22,13 +22,14 @@
#include "log.h"
#include <QFileDialog>
#include <QInputDialog>
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN
MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
: BaseMainWindow(std::move(context), args, parent)
MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
: BaseMainWindow(std::move(context), handler, parent)
{
initMainResource();
@ -53,7 +54,7 @@ void MainWindow::createMenu() {
actionLoadUCF = new QAction("Open UCF", this);
actionLoadUCF->setIcon(QIcon(":/icons/resources/open_ucf.png"));
actionLoadUCF->setStatusTip("Open UCF file");
actionLoadUCF->setEnabled(false);
actionLoadUCF->setEnabled(true);
connect(actionLoadUCF, &QAction::triggered, this, &MainWindow::open_ucf);
// Add actions in menus
@ -64,7 +65,14 @@ void MainWindow::createMenu() {
menuDesign->addAction(actionLoadUCF);
}
void MainWindow::new_proj() {}
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()
{

View File

@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow
Q_OBJECT
public:
explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
virtual ~MainWindow();
public:

View File

@ -490,14 +490,20 @@ std::vector<IdString> Arch::getBelPins(BelId bel) const
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
// TODO
return 13;
// XXX
int dx = std::abs(src.location.x - dst.location.x);
int dy = std::abs(src.location.y - dst.location.y);
return (dx + dy + 10) * 300;
}
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
{
// TODO
return 13;
// XXX
auto &src_loc = net_info->driver.cell->bel.location;
auto &dst_loc = sink.cell->bel.location;
int dx = std::abs(src_loc.x - dst_loc.x);
int dy = std::abs(src_loc.y - dst_loc.y);
return (dx + dy + 10) * 300;
}
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
@ -515,16 +521,23 @@ bool Arch::place()
cfg.ioBufTypes.insert(id("IOB"));
if (!placer_heap(getCtx(), cfg))
return false;
getCtx()->settings[getCtx()->id("place")] = 1;
} else if (placer == "sa") {
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
return false;
getCtx()->settings[getCtx()->id("place")] = 1;
} else {
log_error("Leuctra architecture does not support placer '%s'\n", placer.c_str());
}
return true;
}
bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
bool Arch::route() {
bool retVal = router1(getCtx(), Router1Cfg(getCtx()));
if (retVal)
getCtx()->settings[getCtx()->id("route")] = 1;
return retVal;
}
// -----------------------------------------------------------------------
@ -574,14 +587,182 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; };
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{
// XXX
delay.min_delay = 11;
delay.max_delay = 13;
return true;
delay.min_delay = 150;
delay.max_delay = 150;
if (cell->type == id("LEUCTRA_LC")) {
if (toPort == id("O6") || toPort == id("O5") || toPort == id("CO") || toPort == id("DCO") || toPort == id("XO")) {
if (fromPort == id("I1") || fromPort == id("RA1"))
return true;
if (fromPort == id("I2") || fromPort == id("RA2"))
return true;
if (fromPort == id("I3") || fromPort == id("RA3"))
return true;
if (fromPort == id("I4") || fromPort == id("RA4"))
return true;
if (fromPort == id("I5") || fromPort == id("RA5"))
return true;
if (toPort != id("O5") && (fromPort == id("I6") || fromPort == id("RA6")))
return true;
}
if (toPort == id("CO") || toPort == id("DCO") || toPort == id("XO")) {
if (fromPort == id("XI")) {
if (cell->params.count(id("CYINIT")) && cell->params.at(id("CYINIT")) == Property("XI"))
return true;
if (cell->params.count(id("CYMUX")) && cell->params.at(id("CYMUX")) == Property("XI"))
return true;
}
if (fromPort == id("DCI"))
return true;
}
if ((toPort == id("MO") || toPort == id("DMO")) && (fromPort == id("DMI0") || fromPort == id("DMI1") || fromPort == id("XI")))
return true;
return false;
} else if (cell->type == id("LEUCTRA_FF")) {
if (cell->params.at(id("MODE")).as_string() == "COMB") {
return true;
}
return false;
} else if (cell->type == id("BUFGMUX")) {
if (toPort == id("O")) {
if (fromPort == id("I0"))
return true;
if (fromPort == id("I1"))
return true;
}
return false;
} else if (cell->type == id("OLOGIC2")) {
if (toPort == id("OQ") && fromPort == id("D1"))
return true;
return false;
} else if (cell->type == id("ILOGIC2")) {
if (toPort == id("FABRICOUT") && fromPort == id("D"))
return true;
return false;
} else if (cell->type == id("RAMB16BWER")) {
return false;
} else if (cell->type == id("RAMB8BWER")) {
return false;
}
log_warning("cell type '%s' arc '%s' '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this), fromPort.c_str(this), toPort.c_str(this), cell->name.c_str(this));
return false;
}
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
{
if (cell->type == id("LEUCTRA_LC")) {
if (cell->attrs.count(id("CONST")))
return TMG_IGNORE;
if (port == id("O6") || port == id("O5") || port == id("MO") || port == id("DMO") || port == id("DCO") || port == id("CO") || port == id("XO"))
return TMG_COMB_OUTPUT;
if (port == id("I1") || port == id("RA1"))
return TMG_COMB_INPUT;
if (port == id("I2") || port == id("RA2"))
return TMG_COMB_INPUT;
if (port == id("I3") || port == id("RA3"))
return TMG_COMB_INPUT;
if (port == id("I4") || port == id("RA4"))
return TMG_COMB_INPUT;
if (port == id("I5") || port == id("RA5"))
return TMG_COMB_INPUT;
if (port == id("I6") || port == id("RA6"))
return TMG_COMB_INPUT;
if (port == id("WA1") || port == id("WA2") || port == id("WA3") || port == id("WA4") || port == id("WA5") || port == id("WA6") || port == id("WA7") || port == id("WA8") || port == id("WE") || port == id("DDI5") || port == id("DDI7") || port == id("DDI8")) {
clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
if (port == id("DMI0") || port == id("DMI1") || port == id("DCI"))
return TMG_COMB_INPUT;
if (port == id("XI")) {
if (cell->params.count(id("DIMUX")) && cell->params.at(id("DIMUX")).as_string() == "XI") {
clockInfoCount = 1;
return TMG_REGISTER_INPUT;
} else {
return TMG_COMB_INPUT;
}
}
if (port == id("CLK"))
return TMG_CLOCK_INPUT;
}
if (cell->type == id("LEUCTRA_FF")) {
if (cell->params.at(id("MODE")).as_string() == "COMB") {
if (port == id("D") ||
port == id("SR") ||
port == id("CLK") ||
port == id("CE")) {
return TMG_COMB_INPUT;
}
if (port == id("Q")) {
return TMG_COMB_OUTPUT;
}
} else {
if (port == id("D") ||
port == id("SR") ||
port == id("CE")) {
clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
if (port == id("CLK"))
return TMG_CLOCK_INPUT;
if (port == id("Q")) {
clockInfoCount = 1;
return TMG_REGISTER_OUTPUT;
}
}
}
if (cell->type == id("BUFGMUX")) {
if (port == id("O"))
return TMG_COMB_OUTPUT;
if (port == id("I0"))
return TMG_COMB_INPUT;
if (port == id("I1"))
return TMG_COMB_INPUT;
if (port == id("S"))
return TMG_IGNORE;
}
if (cell->type == id("IOB")) {
if (port == id("I"))
return TMG_STARTPOINT;
if (port == id("O"))
return TMG_ENDPOINT;
if (port == id("T"))
return TMG_ENDPOINT;
}
if (cell->type == id("ILOGIC2")) {
if (port == id("D"))
return TMG_COMB_INPUT;
if (port == id("FABRICOUT"))
return TMG_COMB_OUTPUT;
}
if (cell->type == id("OLOGIC2")) {
if (port == id("D1"))
return TMG_COMB_INPUT;
if (port == id("OQ"))
return TMG_COMB_OUTPUT;
}
if (cell->type == id("RAMB8BWER")) {
if (port == id("CLKAWRCLK"))
return TMG_CLOCK_INPUT;
if (port == id("CLKBRDCLK"))
return TMG_CLOCK_INPUT;
clockInfoCount = 1;
if (cell->ports.at(port).type == PORT_IN)
return TMG_REGISTER_INPUT;
else
return TMG_REGISTER_OUTPUT;
}
if (cell->type == id("RAMB16BWER")) {
if (port == id("CLKA"))
return TMG_CLOCK_INPUT;
if (port == id("CLKB"))
return TMG_CLOCK_INPUT;
clockInfoCount = 1;
if (cell->ports.at(port).type == PORT_IN)
return TMG_REGISTER_INPUT;
else
return TMG_REGISTER_OUTPUT;
}
// XXX
log_warning("cell type '%s' port '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this), port.c_str(this), cell->name.c_str(this));
return TMG_IGNORE;
}
@ -591,10 +772,56 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
info.setup = getDelayFromNS(0);
info.hold = getDelayFromNS(0);
info.clockToQ = getDelayFromNS(0);
// XXX
if (cell->type == id("LEUCTRA_LC") || cell->type == id("LEUCTRA_FF")) {
info.clock_port = id("CLK");
if (cell->params.count(id("CLKINV")) && cell->params.at(id("CLKINV")) == Property("CLK_B"))
info.edge = FALLING_EDGE;
else
info.edge = RISING_EDGE;
}
if (cell->type == id("RAMB8BWER")) {
// XXX wrong wrong wrong
info.clock_port = id("CLKAWRCLK");
if (cell->params.count(id("CLKAWRCLKINV")) && cell->params.at(id("CLKAWRCLKINV")) == Property("CLKAWRCLK_B"))
info.edge = FALLING_EDGE;
else
info.edge = RISING_EDGE;
}
if (cell->type == id("RAMB16BWER")) {
// XXX wrong wrong wrong
info.clock_port = id("CLKA");
if (cell->params.count(id("CLKAINV")) && cell->params.at(id("CLKAINV")) == Property("CLKA_B"))
info.edge = FALLING_EDGE;
else
info.edge = RISING_EDGE;
}
return info;
}
// Assign arch arg info
void Arch::assignArchInfo()
{
#if 0
for (auto &net : getCtx()->nets) {
NetInfo *ni = net.second.get();
if (isGlobalNet(ni))
ni->is_global = true;
ni->is_enable = false;
ni->is_reset = false;
for (auto usr : ni->users) {
if (is_enable_port(this, usr))
ni->is_enable = true;
if (is_reset_port(this, usr))
ni->is_reset = true;
}
}
for (auto &cell : getCtx()->cells) {
CellInfo *ci = cell.second.get();
assignCellInfo(ci);
}
#endif
}
#ifdef WITH_HEAP
const std::string Arch::defaultPlacer = "heap";
#else

View File

@ -685,6 +685,10 @@ struct Arch : BaseCtx
BelId getBelByLocation(Loc loc) const
{
BelId res;
if (loc.x >= device_info->width || loc.y >= device_info->height)
return BelId();
if (loc.z >= getTileType(loc.x, loc.y).num_bels)
return BelId();
res.location.x = loc.x;
res.location.y = loc.y;
res.index = loc.z;
@ -789,6 +793,11 @@ struct Arch : BaseCtx
std::vector<IdString> getBelPins(BelId bel) const;
BelPOD::BelFlags getBelFlags(BelId bel) const {
auto &tile = getTile(bel.location);
return tile.bels[bel.index].flags;
}
BelId getRelatedBel(BelId bel, int relation) const {
auto &tile = getTile(bel.location);
auto &related = tile.bels[bel.index].related[relation];
@ -1050,8 +1059,8 @@ struct Arch : BaseCtx
{
// TODO: delays.
DelayInfo del;
del.min_delay = 11;
del.max_delay = 13;
del.min_delay = 150;
del.max_delay = 150;
return del;
}
@ -1135,7 +1144,7 @@ struct Arch : BaseCtx
delay_t estimateDelay(WireId src, WireId dst) const;
delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const;
delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; }
delay_t getRipupDelayPenalty() const { return 80; }
float getDelayNS(delay_t v) const { return v * 0.001; }
DelayInfo getDelayFromNS(float ns) const
{
@ -1176,22 +1185,43 @@ struct Arch : BaseCtx
// Placement validity checks
// TODO: validate bel subtype (SLICEM vs SLICEL, IOBM vs IOBS, ...).
bool isValidBelForCell(CellInfo *cell, BelId bel) const {
if (cell->type == id("LEUCTRA_FF") && (0x924924ull & 1ull << bel.index))
return false;
if (cell->type == id("LEUCTRA_FF")) {
if (!cell->constr_parent) {
if (0x924924ull & 1ull << bel.index)
return false;
// XXX FIX FIX FIX
if (0xffcfffull & 1ull << bel.index)
return false;
}
}
if (cell->type == id("LEUCTRA_LC")) {
int mask = cell->attrs[id("LOCMASK")].as_int64();
int lci = bel.index / 3 % 4;
if (!(mask & 1 << lci))
return false;
if (cell->attrs[id("NEEDS_L")].as_bool()) {
if (!(getBelFlags(bel) & (BelPOD::FLAG_SLICEL | BelPOD::FLAG_SLICEM)))
return false;
}
if (cell->attrs[id("NEEDS_M")].as_bool()) {
if (!(getBelFlags(bel) & BelPOD::FLAG_SLICEM))
return false;
}
// XXX more?
}
return true;
}
bool isBelLocationValid(BelId bel) const {
CellInfo *cell = getBoundBelCell(bel);
if (cell == nullptr)
return true;
else
return isValidBelForCell(cell, bel);
if (cell && !isValidBelForCell(cell, bel))
return false;
return true;
}
// Apply UCF constraints to the context
bool applyUCF(std::string filename, std::istream &in);
//void assignArchInfo();
void assignArchInfo();
static const std::string defaultPlacer;
static const std::vector<std::string> availablePlacers;
};

View File

@ -205,7 +205,7 @@ 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) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<int>()(pip.index ^ pip.subindex) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};

View File

@ -19,12 +19,20 @@
#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);
@ -58,29 +66,62 @@ std::unique_ptr<CellInfo> create_leuctra_cell(Context *ctx, IdString type, std::
};
if (type == ctx->id("LEUCTRA_FF")) {
new_cell->params[ctx->id("MODE")] = "FFSYNC";
new_cell->params[ctx->id("INIT")] = "0";
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("INIT")] = "0000000000000000000000000000000000000000000000000000000000000000";
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")) {
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);
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);
@ -89,11 +130,6 @@ std::unique_ptr<CellInfo> create_leuctra_cell(Context *ctx, IdString type, std::
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));
}
@ -104,14 +140,14 @@ void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector<std::u
std::unordered_set<IdString> &todelete_cells)
{
if (nxio->type == ctx->id("$nextpnr_ibuf")) {
iob->params[ctx->id("DIR")] = "INPUT";
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")] = "OUTPUT";
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")] = "BIDIR";
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 {
@ -126,7 +162,7 @@ void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector<std::u
// 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";
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));
@ -142,84 +178,451 @@ void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector<std::u
}
}
void convert_ff(Context *ctx, CellInfo *orig, CellInfo *ff, std::vector<std::unique_ptr<CellInfo>> &created_cells,
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")) {
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"));
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);
NPNR_ASSERT_FALSE("WEIRD FF TYPE");
}
if (is_latch) {
clk_pin = ctx->id("G");
ce_pin = ctx->id("GE");
} else {
clk_pin = ctx->id("C");
ce_pin = ctx->id("CE");
}
ff->params[ctx->id("MODE")] = mode;
ff->params[ctx->id("SRVAL")] = Property(srval, 1);
if (orig->params.count(ctx->id("INIT"))) {
if (orig->params[ctx->id("INIT")].str[0] != 'x')
ff->params[ctx->id("INIT")] = orig->params[ctx->id("INIT")];
orig->params.erase(ctx->id("INIT"));
}
NetInfo *net;
bool net_inv = false, cval = false;
if (get_invertible_port(ctx, orig, ctx->id("D"), false, false, net, net_inv))
set_invertible_port(ctx, ff, ctx->id("D"), net, net_inv, false, new_cells);
if (get_invertible_port(ctx, orig, sr_pin, false, false, net, net_inv)) {
if (get_const_val(ctx, net, cval) && (cval ^ net_inv) == false) {
// SR tied to 0 — remove it.
} else {
set_invertible_port(ctx, ff, ctx->id("SR"), net, net_inv, false, new_cells);
ff->params[ctx->id("SRUSED")] = Property(true);
}
}
if (get_invertible_port(ctx, orig, ce_pin, false, false, net, net_inv)) {
if (get_const_val(ctx, net, cval) && (cval ^ net_inv) == true) {
// CE tied to 1 — remove it.
} else {
set_invertible_port(ctx, ff, ctx->id("CE"), net, net_inv, false, new_cells);
ff->params[ctx->id("CEUSED")] = Property(true);
}
}
if (get_invertible_port(ctx, orig, clk_pin, clk_inv, true, net, net_inv)) {
set_invertible_port(ctx, ff, ctx->id("CLK"), net, net_inv, true, new_cells);
}
replace_port(orig, ctx->id("Q"), ff, ctx->id("Q"));
for (auto &param : orig->params) {
log_error("FF %s has leftover param %s = %s\n", orig->name.c_str(ctx), param.first.c_str(ctx), param.second.str.c_str());
}
}
void convert_lut(Context *ctx, CellInfo *orig, CellInfo *lut, std::vector<std::unique_ptr<CellInfo>> &created_cells,
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)
{
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"));
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 {
NPNR_ASSERT(false);
// 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);
}
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);
}
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");
}
lut->params[ctx->id("INIT")] = new_init;
}
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;
}
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("I6"), 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");
set_const_port(ctx, ff, ctx->id("CLK"), true, created_cells);
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
@ -231,9 +634,14 @@ 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);
@ -241,4 +649,228 @@ void insert_ologic_pass(Context *ctx, CellInfo *iob, CellInfo *ologic)
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

View File

@ -41,11 +41,17 @@ inline bool is_xilinx_iobuf(const BaseCtx *ctx, const CellInfo *cell) {
}
inline bool is_xilinx_ff(const BaseCtx *ctx, const CellInfo *cell) {
return cell->type == ctx->id("FDRE");
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("LUT1")
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")
@ -56,14 +62,37 @@ inline bool is_xilinx_lut(const BaseCtx *ctx, const CellInfo *cell) {
// 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,
void convert_ff(Context *ctx, CellInfo *orig, CellInfo *ff, std::vector<std::unique_ptr<CellInfo>> &new_cells,
std::unordered_set<IdString> &todelete_cells);
void convert_lut(Context *ctx, CellInfo *orig, CellInfo *lc, std::vector<std::unique_ptr<CellInfo>> &created_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

View File

@ -34,7 +34,7 @@ class LeuctraCommandHandler : public CommandHandler
public:
LeuctraCommandHandler(int argc, char **argv);
virtual ~LeuctraCommandHandler(){};
std::unique_ptr<Context> createContext() override;
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;
@ -65,8 +65,9 @@ void LeuctraCommandHandler::customBitstream(Context *ctx) {
}
}
std::unique_ptr<Context> LeuctraCommandHandler::createContext()
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
@ -75,7 +76,13 @@ std::unique_ptr<Context> LeuctraCommandHandler::createContext()
chipArgs.package = vm["package"].as<std::string>();
if (vm.count("speed"))
chipArgs.speed = vm["speed"].as<std::string>();
return std::unique_ptr<Context>(new Context(chipArgs));
// 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)

View File

@ -18,6 +18,7 @@
*/
#include "nextpnr.h"
#include "design_utils.h"
#include "cells.h"
#include "log.h"
#include "util.h"
@ -54,14 +55,52 @@ class LeuctraPacker
new_cells.clear();
}
// Remove nextpnr iob cells, insert Xilinx primitives instead.
void pack_iob()
{
log_info("Packing IOBs...\n");
CellInfo *fetch_nxio(CellInfo *cell, IdString port) {
PortInfo pi = cell->ports.at(port);
if (pi.net == nullptr)
return nullptr;
CellInfo *res = nullptr;
IdString res_port;
if (is_nextpnr_iob(ctx, pi.net->driver.cell)) {
res = pi.net->driver.cell;
res_port = pi.net->driver.port;
} else if (pi.net->driver.cell) {
if (pi.net->driver.cell != cell || pi.net->driver.port != port) {
log_error("Stray driver on net %s: %s %s\n", pi.net->name.c_str(ctx), pi.net->driver.cell->name.c_str(ctx), pi.net->driver.port.c_str(ctx));
}
}
for (auto &usr : pi.net->users)
if (usr.cell == cell && usr.port == port) {
continue;
} else if (is_nextpnr_iob(ctx, usr.cell)) {
if (res) {
log_error("Two nextpnr bufs on net %s: %s %s\n", pi.net->name.c_str(ctx), usr.cell->name.c_str(ctx), res->name.c_str(ctx));
}
res = usr.cell;
res_port = usr.port;
} else {
log_error("Stray load on net %s: %s %s\n", pi.net->name.c_str(ctx), usr.cell->name.c_str(ctx), usr.port.c_str(ctx));
}
if (!res)
return nullptr;
// Kill the connection.
disconnect_port(ctx, res, res_port);
disconnect_port(ctx, cell, port);
return res;
}
// Insert IOB cells for ports that don't have one yet.
void insert_iob()
{
log_info("Inserting IOBs...\n");
/* TODO: get this working maybe? */
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_nextpnr_iob(ctx, ci)) {
abort();
NPNR_ASSERT_FALSE("SURVIVED");
CellInfo *iob = nullptr;
std::unique_ptr<CellInfo> io_cell =
create_leuctra_cell(ctx, ctx->id("IOB"), ci->name.str(ctx) + "$iob");
@ -73,10 +112,12 @@ class LeuctraPacker
if (iob != nullptr) {
for (const auto &attr : ci->attrs)
iob->attrs[attr.first] = attr.second;
for (const auto &param : ci->params)
iob->params[param.first] = param.second;
auto loc_attr = iob->attrs.find(ctx->id("LOC"));
if (loc_attr != iob->attrs.end()) {
std::string pin = loc_attr->second;
std::string pin = loc_attr->second.as_string();
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",
@ -85,7 +126,7 @@ class LeuctraPacker
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);
iob->attrs[ctx->id("BEL")] = Property(ctx->getBelName(pinBel).str(ctx));
}
}
}
@ -94,6 +135,311 @@ class LeuctraPacker
flush_cells();
}
// Convert Xilinx IO buffer primitives into IOB cells.
void convert_iob()
{
enum IoStdKind {
// Single ended, settable drive.
IOSTD_SINGLE_DRIVE,
// Single ended.
IOSTD_SINGLE,
// Pseudo-differential.
IOSTD_PSEUDO_DIFF,
// True differential.
IOSTD_DIFF,
};
std::map<std::string, IoStdKind> iostds;
iostds["LVTTL"] = IOSTD_SINGLE_DRIVE;
iostds["LVCMOS33"] = IOSTD_SINGLE_DRIVE;
iostds["LVCMOS25"] = IOSTD_SINGLE_DRIVE;
iostds["LVCMOS18"] = IOSTD_SINGLE_DRIVE;
iostds["LVCMOS15"] = IOSTD_SINGLE_DRIVE;
iostds["LVCMOS12"] = IOSTD_SINGLE_DRIVE;
iostds["LVCMOS18_JEDEC"] = IOSTD_SINGLE_DRIVE;
iostds["LVCMOS15_JEDEC"] = IOSTD_SINGLE_DRIVE;
iostds["LVCMOS12_JEDEC"] = IOSTD_SINGLE_DRIVE;
iostds["PCI33_3"] = IOSTD_SINGLE;
iostds["PCI66_3"] = IOSTD_SINGLE;
iostds["SDIO"] = IOSTD_SINGLE;
iostds["MOBILE_DDR"] = IOSTD_SINGLE;
iostds["DIFF_MOBILE_DDR"] = IOSTD_PSEUDO_DIFF;
iostds["I2C"] = IOSTD_SINGLE;
iostds["SMBUS"] = IOSTD_SINGLE;
iostds["HSTL_I"] = IOSTD_SINGLE;
iostds["DIFF_HSTL_I"] = IOSTD_PSEUDO_DIFF;
iostds["HSTL_I_18"] = IOSTD_SINGLE;
iostds["DIFF_HSTL_I_18"] = IOSTD_PSEUDO_DIFF;
iostds["HSTL_II"] = IOSTD_SINGLE;
iostds["DIFF_HSTL_II"] = IOSTD_PSEUDO_DIFF;
iostds["HSTL_II_18"] = IOSTD_SINGLE;
iostds["DIFF_HSTL_II_18"] = IOSTD_PSEUDO_DIFF;
iostds["HSTL_III"] = IOSTD_SINGLE;
iostds["DIFF_HSTL_III"] = IOSTD_PSEUDO_DIFF;
iostds["HSTL_III_18"] = IOSTD_SINGLE;
iostds["DIFF_HSTL_III_18"] = IOSTD_PSEUDO_DIFF;
iostds["SSTL3_I"] = IOSTD_SINGLE;
iostds["DIFF_SSTL3_I"] = IOSTD_PSEUDO_DIFF;
iostds["SSTL2_I"] = IOSTD_SINGLE;
iostds["DIFF_SSTL2_I"] = IOSTD_PSEUDO_DIFF;
iostds["SSTL18_I"] = IOSTD_SINGLE;
iostds["DIFF_SSTL18_I"] = IOSTD_PSEUDO_DIFF;
iostds["SSTL3_II"] = IOSTD_SINGLE;
iostds["DIFF_SSTL3_II"] = IOSTD_PSEUDO_DIFF;
iostds["SSTL2_II"] = IOSTD_SINGLE;
iostds["DIFF_SSTL2_II"] = IOSTD_PSEUDO_DIFF;
iostds["SSTL18_II"] = IOSTD_SINGLE;
iostds["DIFF_SSTL18_II"] = IOSTD_PSEUDO_DIFF;
iostds["SSTL15_II"] = IOSTD_SINGLE;
iostds["DIFF_SSTL15_II"] = IOSTD_PSEUDO_DIFF;
iostds["BLVDS_25"] = IOSTD_PSEUDO_DIFF;
iostds["LVPECL_25"] = IOSTD_PSEUDO_DIFF;
iostds["LVPECL_33"] = IOSTD_PSEUDO_DIFF;
iostds["DISPLAY_PORT"] = IOSTD_PSEUDO_DIFF;
iostds["LVDS_33"] = IOSTD_DIFF;
iostds["LVDS_25"] = IOSTD_DIFF;
iostds["MINI_LVDS_33"] = IOSTD_DIFF;
iostds["MINI_LVDS_25"] = IOSTD_DIFF;
iostds["RSDS_33"] = IOSTD_DIFF;
iostds["RSDS_25"] = IOSTD_DIFF;
iostds["PPDS_33"] = IOSTD_DIFF;
iostds["PPDS_25"] = IOSTD_DIFF;
iostds["TMDS_33"] = IOSTD_DIFF;
iostds["TML_33"] = IOSTD_DIFF;
log_info("Converting IOBs...\n");
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
IdString port_i, port_o, port_t, port_ib;
IdString port_pad_m, port_pad_s;
bool diff = false;
if (ci->type == ctx->id("IBUF") || ci->type == ctx->id("IBUFG")) {
port_i = ctx->id("O");
port_pad_m = ctx->id("I");
} else if (ci->type == ctx->id("IBUFDS") || ci->type == ctx->id("IBUFGDS")) {
port_i = ctx->id("O");
port_pad_m = ctx->id("I");
port_pad_s = ctx->id("IB");
diff = true;
} else if (ci->type == ctx->id("IBUFDS_DIFF_OUT") || ci->type == ctx->id("IBUFGDS_DIFF_OUT")) {
port_i = ctx->id("O");
port_ib = ctx->id("OB");
port_pad_m = ctx->id("I");
port_pad_s = ctx->id("IB");
diff = true;
} else if (ci->type == ctx->id("IOBUF")) {
port_i = ctx->id("O");
port_o = ctx->id("I");
port_t = ctx->id("T");
port_pad_m = ctx->id("IO");
} else if (ci->type == ctx->id("IOBUFDS")) {
port_i = ctx->id("O");
port_o = ctx->id("I");
port_t = ctx->id("T");
port_pad_m = ctx->id("IO");
port_pad_s = ctx->id("IOB");
diff = true;
} else if (ci->type == ctx->id("OBUF")) {
port_o = ctx->id("I");
port_pad_m = ctx->id("O");
} else if (ci->type == ctx->id("OBUFDS")) {
port_o = ctx->id("I");
port_pad_m = ctx->id("O");
port_pad_s = ctx->id("OB");
diff = true;
} else if (ci->type == ctx->id("OBUFT")) {
port_o = ctx->id("I");
port_t = ctx->id("T");
port_pad_m = ctx->id("O");
} else if (ci->type == ctx->id("OBUFTDS")) {
port_o = ctx->id("I");
port_t = ctx->id("T");
port_pad_m = ctx->id("O");
port_pad_s = ctx->id("OB");
diff = true;
} else {
// Not an IO buffer.
continue;
}
// Get the nextpnr-inserted buffers.
CellInfo *nxb_m = fetch_nxio(ci, port_pad_m);
CellInfo *nxb_s = nullptr;
if (port_pad_s != IdString())
nxb_s = fetch_nxio(ci, port_pad_s);
if (!nxb_m)
log_error("Buffer %s not connected to port.\n", ci->name.c_str(ctx));
packed_cells.insert(nxb_m->name);
if (nxb_s)
packed_cells.insert(nxb_s->name);
// Merge UCF constraints into the buffer.
for (const auto &param : nxb_m->params)
ci->params[param.first] = param.second;
for (const auto &attr : nxb_m->attrs)
ci->attrs[attr.first] = attr.second;
std::string iostd;
auto iostd_attr = ci->params.find(ctx->id("IOSTANDARD"));
if (iostd_attr != ci->params.end()) {
iostd = iostd_attr->second.as_string();
} else {
// Hm.
if (diff)
iostd = "LVDS_33";
else
iostd = "LVCMOS33";
}
if (iostds.find(iostd) == iostds.end())
log_error("Unknown IO standard %s for buffer %s", iostd.c_str(), ci->name.c_str(ctx));
enum IoStdKind kind = iostds[iostd];
if (kind == IOSTD_PSEUDO_DIFF || kind == IOSTD_DIFF) {
diff = true;
} else {
if (diff) {
log_error("Single-ended IO standard %s for differential buffer %s", iostd.c_str(), ci->name.c_str(ctx));
}
}
// Create the buffer cells.
std::unique_ptr<CellInfo> iobm_cell =
create_leuctra_cell(ctx, ctx->id("IOB"), ci->name.str(ctx) + "$iob");
new_cells.push_back(std::move(iobm_cell));
CellInfo *iobm = new_cells.back().get();
CellInfo *iobs = nullptr;
if (diff) {
std::unique_ptr<CellInfo> iobs_cell =
create_leuctra_cell(ctx, ctx->id("IOB"), ci->name.str(ctx) + "$iobs");
new_cells.push_back(std::move(iobm_cell));
iobs = new_cells.back().get();
if (kind == IOSTD_DIFF) {
iobm->params[ctx->id("__MODE__")] = Property("IOBM");
iobs->params[ctx->id("__MODE__")] = Property("IOBS");
} else {
iobm->params[ctx->id("__MODE__")] = Property("IOB");
iobs->params[ctx->id("__MODE__")] = Property("IOB");
}
} else {
iobm->params[ctx->id("__MODE__")] = Property("IOB");
}
// Deal with input path.
if (port_i != IdString()) {
replace_port(ci, port_i, iobm, ctx->id("I"));
if (diff) {
iobs->params[ctx->id("PADOUTUSED")] = Property("0");
connect_ports(ctx, iobs, ctx->id("PADOUT"), iobm, ctx->id("DIFFI_IN"));
}
iobm->params[ctx->id("IMUX")] = Property("I");
iobm->params[ctx->id("BYPASS_MUX")] = Property("I");
iobm->params[ctx->id("ISTANDARD")] = Property(iostd);
if (iobs)
iobm->params[ctx->id("ISTANDARD")] = Property(iostd);
}
if (port_ib != IdString()) {
replace_port(ci, port_ib, iobs, ctx->id("I"));
if (diff) {
iobm->params[ctx->id("PADOUTUSED")] = Property("0");
connect_ports(ctx, iobm, ctx->id("PADOUT"), iobs, ctx->id("DIFFI_IN"));
}
iobs->params[ctx->id("IMUX")] = Property("I");
iobs->params[ctx->id("BYPASS_MUX")] = Property("I");
}
// Deal with output path.
if (port_o != IdString()) {
NetInfo *net_o = ci->ports.at(port_o).net;
NetInfo *net_t = nullptr;
if (port_t != IdString())
net_t = ci->ports.at(port_t).net;
connect_port(ctx, net_o, iobm, ctx->id("O"));
if (kind == IOSTD_PSEUDO_DIFF) {
// XXX
NPNR_ASSERT_FALSE("PSEUDO DIFF OUTPUT");
connect_port(ctx, net_o, iobs, ctx->id("O"));
}
disconnect_port(ctx, ci, port_o);
if (net_t) {
connect_port(ctx, net_t, iobm, ctx->id("T"));
if (kind == IOSTD_PSEUDO_DIFF)
connect_port(ctx, net_t, iobs, ctx->id("T"));
disconnect_port(ctx, ci, port_t);
}
iobm->params[ctx->id("OSTANDARD")] = Property(iostd);
if (iobs)
iobs->params[ctx->id("OSTANDARD")] = Property(iostd);
iobm->params[ctx->id("OUSED")] = Property("0");
if (port_t != IdString())
iobm->params[ctx->id("TUSED")] = Property("0");
if (kind == IOSTD_PSEUDO_DIFF) {
iobs->params[ctx->id("OUSED")] = Property("0");
if (port_t != IdString())
iobs->params[ctx->id("TUSED")] = Property("0");
}
if (kind == IOSTD_SINGLE_DRIVE) {
if (ci->params.count(ctx->id("DRIVE")))
iobm->params[ctx->id("DRIVEATTRBOX")] = ci->params[ctx->id("DRIVE")];
else
iobm->params[ctx->id("DRIVEATTRBOX")] = Property("12");
if (ci->params.count(ctx->id("SLEW")))
iobm->params[ctx->id("SLEW")] = ci->params[ctx->id("SLEW")];
else
iobm->params[ctx->id("SLEW")] = Property("SLOW");
}
if (kind == IOSTD_DIFF) {
connect_ports(ctx, iobm, ctx->id("DIFFO_OUT"), iobs, ctx->id("DIFFO_IN"));
iobs->params[ctx->id("OUTMUX")] = Property("0");
}
}
if (ci->params.count(ctx->id("PULLTYPE")))
iobm->params[ctx->id("PULLTYPE")] = ci->params[ctx->id("PULLTYPE")];
if (ci->params.count(ctx->id("SUSPEND")))
iobm->params[ctx->id("SUSPEND")] = ci->params[ctx->id("SUSPEND")];
if (ci->params.count(ctx->id("PRE_EMPHASIS")))
iobm->params[ctx->id("PRE_EMPHASIS")] = ci->params[ctx->id("PRE_EMPHASIS")];
auto loc_attr = ci->attrs.find(ctx->id("LOC"));
if (loc_attr != ci->attrs.end()) {
std::string pin = loc_attr->second.as_string();
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",
ci->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str());
} else {
log_info("pin '%s' constrained to Bel '%s'.\n", ci->name.c_str(ctx),
ctx->getBelName(pinBel).c_str(ctx));
}
iobm->attrs[ctx->id("BEL")] = Property(ctx->getBelName(pinBel).str(ctx));
if (iobs) {
BelId opinBel = pinBel;
if ((pinBel.index & 1) && kind == IOSTD_DIFF) {
log_error("True differential IO pin '%s' constrained to pin '%s', which is not a master pin.\n",
ci->name.c_str(ctx), pin.c_str());
}
opinBel.index ^= 1;
iobs->attrs[ctx->id("BEL")] = Property(ctx->getBelName(opinBel).str(ctx));
}
} else {
if (iobs) {
// XXX
NPNR_ASSERT_FALSE("UNCONSTRAINED DIFF PAIR");
}
}
packed_cells.insert(ci->name);
}
flush_cells();
}
// Ensure ilogic/ologic cell for every IOB that needs one.
void pack_iologic()
{
@ -122,10 +468,10 @@ class LeuctraPacker
}
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")]));
BelId bel = ctx->getBelByName(ctx->id(ci->attrs[ctx->id("BEL")].as_string()));
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->attrs[ctx->id("BEL")] = Property(ctx->getBelName(child_bel).str(ctx));
child->constr_parent = nullptr;
child->constr_spec = -1;
@ -138,6 +484,23 @@ class LeuctraPacker
flush_cells();
}
// Convert FFs/latches to LEUCTRA_FFs.
void pack_bram()
{
log_info("Packing Block RAMs...\n");
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == ctx->id("RAMB16BWER")) {
fixup_ramb16(ctx, ci, new_cells, packed_cells);
} else if (ci->type == ctx->id("RAMB8BWER")) {
fixup_ramb8(ctx, ci, new_cells, packed_cells);
}
}
flush_cells();
}
// Convert FFs/latches to LEUCTRA_FFs.
void pack_ff()
{
@ -145,7 +508,18 @@ class LeuctraPacker
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_xilinx_ff(ctx, ci)) {
if (ci->type == ctx->id("FDRE") ||
ci->type == ctx->id("FDSE") ||
ci->type == ctx->id("FDCE") ||
ci->type == ctx->id("FDPE") ||
ci->type == ctx->id("FDRE_1") ||
ci->type == ctx->id("FDSE_1") ||
ci->type == ctx->id("FDCE_1") ||
ci->type == ctx->id("FDPE_1") ||
ci->type == ctx->id("LDCE") ||
ci->type == ctx->id("LDPE") ||
ci->type == ctx->id("LDCE_1") ||
ci->type == ctx->id("LDPE_1")) {
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);
@ -158,7 +532,289 @@ class LeuctraPacker
flush_cells();
}
// Convert FFs/latches to LEUCTRA_FFs.
// Convert RAMs to LEUCTRA_LCs.
void pack_ram()
{
log_info("Packing distributed RAM...\n");
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == ctx->id("RAM32X1D") || ci->type == ctx->id("RAM64X1D")) {
int sz = ci->type == ctx->id("RAM32X1D") ? 5 : 6;
CellInfo *lcs[2];
for (int i = 0; i < 2; i++) {
std::unique_ptr<CellInfo> lut_cell =
create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), ci->name.str(ctx) + "$lc" + std::to_string(i));
new_cells.push_back(std::move(lut_cell));
lcs[i] = new_cells.back().get();
lcs[i]->params[ctx->id("MODE")] = Property("RAM" + std::to_string(sz));
if (sz == 6)
lcs[i]->params[ctx->id("DIMUX")] = Property("XI");
lcs[i]->attrs[ctx->id("NEEDS_M")] = Property(true);
}
NetInfo *net;
bool net_inv;
if (get_invertible_port(ctx, ci, ctx->id("WCLK"), false, true, net, net_inv)) {
set_invertible_port(ctx, lcs[0], ctx->id("CLK"), net, net_inv, true, new_cells);
set_invertible_port(ctx, lcs[1], ctx->id("CLK"), net, net_inv, true, new_cells);
}
net = ci->ports[ctx->id("WE")].net;
disconnect_port(ctx, ci, ctx->id("WE"));
connect_port(ctx,net, lcs[0], ctx->id("WE"));
connect_port(ctx,net, lcs[1], ctx->id("WE"));
for (int i = 0; i < sz; i++) {
IdString pname = ctx->id("A" + std::to_string(i));
net = ci->ports[pname].net;
disconnect_port(ctx, ci, pname);
connect_port(ctx,net, lcs[0], ctx->id("WA" + std::to_string(i+1)));
connect_port(ctx,net, lcs[1], ctx->id("WA" + std::to_string(i+1)));
connect_port(ctx,net, lcs[1], ctx->id("RA" + std::to_string(i+1)));
pname = ctx->id("DPRA" + std::to_string(i));
net = ci->ports[pname].net;
disconnect_port(ctx, ci, pname);
connect_port(ctx,net, lcs[0], ctx->id("RA" + std::to_string(i+1)));
}
net = ci->ports[ctx->id("D")].net;
disconnect_port(ctx, ci, ctx->id("D"));
if (sz == 5) {
connect_port(ctx,net, lcs[0], ctx->id("DDI5"));
connect_port(ctx,net, lcs[1], ctx->id("DDI5"));
set_const_port(ctx, lcs[0], ctx->id("RA6"), true, new_cells);
set_const_port(ctx, lcs[1], ctx->id("RA6"), true, new_cells);
} else {
connect_port(ctx,net, lcs[0], ctx->id("XI"));
connect_port(ctx,net, lcs[1], ctx->id("XI"));
}
replace_port(ci, ctx->id("SPO"), lcs[1], ctx->id("O6"));
replace_port(ci, ctx->id("DPO"), lcs[0], ctx->id("O6"));
Property v = ci->params[ctx->id("INIT")];
Property nv(0, 64);
if (sz == 5) {
for (int i = 0; i < 64; i++)
nv.str[i] = v.str[i%32];
nv.update_intval();
} else {
nv = v;
}
lcs[0]->params[ctx->id("INIT")] = nv;
lcs[1]->params[ctx->id("INIT")] = nv;
lcs[1]->attrs[ctx->id("LOCMASK")] = Property(0x8, 4);
lcs[0]->constr_parent = lcs[1];
lcs[0]->constr_z = -3;
lcs[1]->constr_children.push_back(lcs[0]);
packed_cells.insert(ci->name);
} else if (ci->type == ctx->id("RAM128X1D")) {
CellInfo *lcs[4];
for (int i = 0; i < 4; i++) {
std::unique_ptr<CellInfo> lut_cell =
create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), ci->name.str(ctx) + "$lc" + std::to_string(i));
new_cells.push_back(std::move(lut_cell));
lcs[i] = new_cells.back().get();
lcs[i]->params[ctx->id("MODE")] = Property("RAM7");
lcs[i]->params[ctx->id("DIMUX")] = Property("DDI7");
lcs[i]->attrs[ctx->id("NEEDS_M")] = Property(true);
}
NetInfo *net;
bool net_inv;
if (get_invertible_port(ctx, ci, ctx->id("WCLK"), false, true, net, net_inv)) {
for (int i = 0; i < 4; i++)
set_invertible_port(ctx, lcs[i], ctx->id("CLK"), net, net_inv, true, new_cells);
}
net = ci->ports[ctx->id("WE")].net;
disconnect_port(ctx, ci, ctx->id("WE"));
for (int i = 0; i < 4; i++)
connect_port(ctx,net, lcs[i], ctx->id("WE"));
for (int i = 0; i < 7; i++) {
IdString pname = ctx->id("A[" + std::to_string(i) + "]");
net = ci->ports[pname].net;
disconnect_port(ctx, ci, pname);
for (int j = 0; j < 4; j++)
connect_port(ctx,net, lcs[j], ctx->id("WA" + std::to_string(i+1)));
if (i < 6) {
for (int j = 2; j < 4; j++)
connect_port(ctx,net, lcs[j], ctx->id("RA" + std::to_string(i+1)));
} else {
connect_port(ctx,net, lcs[2], ctx->id("XI"));
}
pname = ctx->id("DPRA[" + std::to_string(i) + "]");
net = ci->ports[pname].net;
disconnect_port(ctx, ci, pname);
if (i < 6) {
for (int j = 0; j < 2; j++)
connect_port(ctx,net, lcs[j], ctx->id("RA" + std::to_string(i+1)));
} else {
connect_port(ctx,net, lcs[0], ctx->id("XI"));
}
}
net = ci->ports[ctx->id("D")].net;
disconnect_port(ctx, ci, ctx->id("D"));
for (int i = 0; i < 4; i++)
connect_port(ctx,net, lcs[i], ctx->id("DDI7"));
replace_port(ci, ctx->id("SPO"), lcs[2], ctx->id("MO"));
replace_port(ci, ctx->id("DPO"), lcs[0], ctx->id("MO"));
connect_ports(ctx, lcs[3], ctx->id("O6"), lcs[2], ctx->id("DMI0"));
connect_ports(ctx, lcs[2], ctx->id("O6"), lcs[2], ctx->id("DMI1"));
connect_ports(ctx, lcs[1], ctx->id("O6"), lcs[0], ctx->id("DMI0"));
connect_ports(ctx, lcs[0], ctx->id("O6"), lcs[0], ctx->id("DMI1"));
Property v = ci->params[ctx->id("INIT")];
Property nv(0, 64);
for (int i = 0; i < 64; i++)
nv.str[i] = v.str[i];
nv.update_intval();
lcs[3]->params[ctx->id("INIT")] = nv;
lcs[1]->params[ctx->id("INIT")] = nv;
for (int i = 0; i < 64; i++)
nv.str[i] = v.str[i + 64];
nv.update_intval();
lcs[2]->params[ctx->id("INIT")] = nv;
lcs[0]->params[ctx->id("INIT")] = nv;
lcs[3]->attrs[ctx->id("LOCMASK")] = Property(0x8, 4);
for (int i = 0; i < 3; i++) {
lcs[i]->constr_parent = lcs[3];
lcs[i]->constr_z = (i - 3) * 3;
lcs[3]->constr_children.push_back(lcs[i]);
}
packed_cells.insert(ci->name);
} else if (ci->type == ctx->id("RAM32M")) {
CellInfo *lcs[4];
for (int i = 0; i < 4; i++) {
std::unique_ptr<CellInfo> lut_cell =
create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), ci->name.str(ctx) + "$lc" + std::to_string(i));
new_cells.push_back(std::move(lut_cell));
lcs[i] = new_cells.back().get();
lcs[i]->params[ctx->id("MODE")] = Property("RAM5");
lcs[i]->params[ctx->id("DIMUX")] = Property("XI");
lcs[i]->attrs[ctx->id("NEEDS_M")] = Property(true);
}
NetInfo *net;
bool net_inv;
if (get_invertible_port(ctx, ci, ctx->id("WCLK"), false, true, net, net_inv)) {
for (int i = 0; i < 4; i++)
set_invertible_port(ctx, lcs[i], ctx->id("CLK"), net, net_inv, true, new_cells);
}
net = ci->ports[ctx->id("WE")].net;
disconnect_port(ctx, ci, ctx->id("WE"));
for (int i = 0; i < 4; i++)
connect_port(ctx,net, lcs[i], ctx->id("WE"));
for (int i = 0; i < 4; i++) {
std::string l{"ABCD"[i]};
for (int j = 0; j < 5; j++) {
IdString pname = ctx->id("ADDR" + l + "[" + std::to_string(j) + "]");
net = ci->ports[pname].net;
disconnect_port(ctx, ci, pname);
if (i == 3) {
for (int k = 0; k < 4; j++)
connect_port(ctx,net, lcs[k], ctx->id("WA" + std::to_string(j+1)));
}
connect_port(ctx,net, lcs[i], ctx->id("RA" + std::to_string(j+1)));
}
set_const_port(ctx, lcs[i], ctx->id("RA6"), true, new_cells);
replace_port(ci, ctx->id("DI" + l + "[0]"), lcs[i], ctx->id("XI"));
replace_port(ci, ctx->id("DI" + l + "[1]"), lcs[i], ctx->id("DDI5"));
replace_port(ci, ctx->id("DO" + l + "[0]"), lcs[i], ctx->id("O5"));
replace_port(ci, ctx->id("DO" + l + "[1]"), lcs[i], ctx->id("O6"));
Property v = ci->params[ctx->id("INIT_" + l)];
Property nv(0, 64);
for (int j = 0; j < 32; j++)
for (int k = 0; k < 2; k++)
nv.str[j | k << 5] = v.str[j << 1 | k];
nv.update_intval();
lcs[i]->params[ctx->id("INIT")] = nv;
}
lcs[3]->attrs[ctx->id("LOCMASK")] = Property(0x8, 4);
for (int i = 0; i < 3; i++) {
lcs[i]->constr_parent = lcs[3];
lcs[i]->constr_z = (i - 3) * 3;
lcs[3]->constr_children.push_back(lcs[i]);
}
packed_cells.insert(ci->name);
}
}
flush_cells();
}
// Convert CARRY4s to LEUCTRA_LCs.
void pack_carry()
{
log_info("Packing CARRY4s...\n");
// CARRY4 -> next CARRY4 in the chain
std::unordered_map<CellInfo *, CellInfo *> chain;
// CARRY4s that are chain starts.
std::vector<CellInfo *> init;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == ctx->id("CARRY4")) {
NetInfo *net = ci->ports.at(ctx->id("CI")).net;
CellInfo *prev = nullptr;
if (net)
prev = net->driver.cell;
bool cval;
if (!prev || get_const_val(ctx, net, cval)) {
init.push_back(ci);
} else {
if (prev->type != ctx->id("CARRY4") || net->driver.port != ctx->id("CO[3]"))
log_error("CARRY4 %s has weird CI: %s (%s) %s", ci->name.c_str(ctx), prev->name.c_str(ctx), prev->type.c_str(ctx), net->driver.port.c_str(ctx));
if (chain.count(prev))
log_error("Split carry chain: %s %s %s", prev->name.c_str(ctx), ci->name.c_str(ctx), chain[prev]->name.c_str(ctx));
chain[prev] = ci;
}
}
}
for (auto cell : init) {
CellInfo *cur = cell;
CellInfo *link = nullptr;
while (cur) {
link = convert_carry4(ctx, cur, link, new_cells, packed_cells);
cur = chain[cur];
}
}
flush_cells();
}
// Convert MUXF8s to LEUCTRA_LCs.
void pack_muxf8()
{
log_info("Packing MUXF8s...\n");
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == ctx->id("MUXF8")) {
NetInfo *net = ci->ports.at(ctx->id("O")).net;
convert_muxf8(ctx, net, ci->name.str(ctx) + "$lc", new_cells, packed_cells);
}
}
flush_cells();
}
// Convert MUXF7s to LEUCTRA_LCs.
void pack_muxf7()
{
log_info("Packing MUXF7s...\n");
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == ctx->id("MUXF7")) {
NetInfo *net = ci->ports.at(ctx->id("O")).net;
convert_muxf7(ctx, net, ci->name.str(ctx) + "$lc", new_cells, packed_cells);
}
}
flush_cells();
}
// Convert LUTs to LEUCTRA_LCs.
void pack_lut()
{
log_info("Packing LUTs...\n");
@ -166,12 +822,26 @@ class LeuctraPacker
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));
NetInfo *net = ci->ports.at(ctx->id("O")).net;
convert_lut(ctx, net, ci->name.str(ctx) + "$lc", new_cells, packed_cells);
}
}
packed_cells.insert(ci->name);
flush_cells();
}
// Convert misc cell types.
void pack_misc()
{
log_info("Converting misc cell types...\n");
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == ctx->id("BUFG")) {
ci->type = ctx->id("BUFGMUX");
rename_port(ctx, ci, ctx->id("I"), ctx->id("I0"));
set_const_port(ctx, ci, ctx->id("S"), true, new_cells);
ci->params[ctx->id("SINV")] = Property("S_B");
}
}
@ -200,20 +870,22 @@ class LeuctraPacker
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";
gnd_cell->params[ctx->id("INIT")] = Property(0, 64);
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();
gnd_cell->attrs[ctx->id("CONST")] = Property(false);
std::unique_ptr<CellInfo> vcc_cell = create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), "$PACKER_VCC");
vcc_cell->params[ctx->id("INIT")] = "1111111111111111111111111111111111111111111111111111111111111111";
vcc_cell->params[ctx->id("INIT")] = Property(-1, 64);
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();
vcc_cell->attrs[ctx->id("CONST")] = Property(true);
std::vector<IdString> dead_nets;
@ -253,10 +925,20 @@ class LeuctraPacker
public:
void pack()
{
pack_iob();
//insert_iob();
convert_iob();
pack_iologic();
pack_bram();
pack_ff();
pack_ram();
// pack_srl();
pack_carry();
pack_muxf8();
pack_muxf7();
// clean_inv();
pack_lut();
// pack_lc_ff();
pack_misc();
pack_constants();
}
@ -274,6 +956,7 @@ bool Arch::pack()
try {
log_break();
LeuctraPacker(ctx).pack();
ctx->settings[ctx->id("pack")] = 1;
log_info("Checksum: 0x%08x\n", ctx->checksum());
// XXX
//assignArchInfo();

View File

@ -1,47 +0,0 @@
/*
* 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 "project.h"
#include <boost/filesystem/convenience.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <fstream>
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path)
{
root.put("project.arch.package", ctx->archArgs().package);
root.put("project.arch.speed", ctx->archArgs().speed);
}
std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
{
ArchArgs chipArgs;
chipArgs.device = root.get<std::string>("project.arch.type");
chipArgs.package = root.get<std::string>("project.arch.package");
chipArgs.speed = root.get<std::string>("project.arch.speed");
return std::unique_ptr<Context>(new Context(chipArgs));
}
void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {}
NEXTPNR_NAMESPACE_END

View File

@ -32,16 +32,21 @@ void write_textcfg(const Context *ctx, std::ostream &out)
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 &param : cell.second->params) {
out << "OPT " << param.first.str(ctx) << " " << param.second << 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;
out << "FROM " << net.second->driver.cell->name.str(ctx) << " " << net.second->driver.port.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;
}

View File

@ -65,7 +65,7 @@ bool Arch::applyUCF(std::string filename, std::istream &in)
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(),
log_warning(" ignoring unsupported UCF command '%s' (on line %d)\n", command.c_str(),
lineno);
} else if (verb == "NET") {
if (words.size() < 2)
@ -82,12 +82,15 @@ bool Arch::applyUCF(std::string filename, std::istream &in)
pos += 2;
auto fnd_cell = cells.find(id(target));
if (fnd_cell != cells.end()) {
fnd_cell->second->attrs[id(attr)] = value;
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->attrs[id("PULLTYPE")] = attr;
fnd_cell->second->params[id("PULLTYPE")] = attr;
}
} else if (attr == "PERIOD") {
if (pos + 2 > words.size() || words.at(pos) != "=")