wip
This commit is contained in:
parent
20dfebb09f
commit
b4ee8fc639
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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:
|
||||
|
245
leuctra/arch.cc
245
leuctra/arch.cc
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
780
leuctra/cells.cc
780
leuctra/cells.cc
@ -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 ¶m : orig->params) {
|
||||
log_error("FF %s has leftover param %s = %s\n", orig->name.c_str(ctx), param.first.c_str(ctx), param.second.str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
719
leuctra/pack.cc
719
leuctra/pack.cc
@ -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 ¶m : 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 ¶m : 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();
|
||||
|
@ -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
|
||||
|
@ -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 ¶m : 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;
|
||||
}
|
||||
|
@ -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) != "=")
|
||||
|
Loading…
Reference in New Issue
Block a user