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());
|
port.net->users.end());
|
||||||
if (port.net->driver.cell == cell && port.net->driver.port == port_name)
|
if (port.net->driver.cell == cell && port.net->driver.port == port_name)
|
||||||
port.net->driver.cell = nullptr;
|
port.net->driver.cell = nullptr;
|
||||||
|
port.net = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,7 +497,8 @@ struct Router1
|
|||||||
}
|
}
|
||||||
qw.randtag = ctx->rng();
|
qw.randtag = ctx->rng();
|
||||||
|
|
||||||
queue.push(qw);
|
if (src_wire != dst_wire)
|
||||||
|
queue.push(qw);
|
||||||
visited[qw.wire] = qw;
|
visited[qw.wire] = qw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,12 +175,23 @@ struct Timing
|
|||||||
|
|
||||||
// Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and
|
// Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and
|
||||||
// the current output port, increment fanin counter
|
// the current output port, increment fanin counter
|
||||||
|
int found = 0;
|
||||||
for (auto i : input_ports) {
|
for (auto i : input_ports) {
|
||||||
DelayInfo comb_delay;
|
DelayInfo comb_delay;
|
||||||
bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay);
|
bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay);
|
||||||
if (is_path)
|
if (is_path) {
|
||||||
port_fanin[o]++;
|
port_fanin[o]++;
|
||||||
|
found++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!found && portClass == TMG_COMB_OUTPUT) {
|
||||||
|
// log_warning("fake root %s\n", o->net->name.c_str(ctx));
|
||||||
|
topographical_order.emplace_back(o->net);
|
||||||
|
TimingData td;
|
||||||
|
td.false_startpoint = true;
|
||||||
|
td.max_arrival = 0;
|
||||||
|
net_data[o->net][ClockEvent{async_clock, RISING_EDGE}] = td;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,14 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QInputDialog>
|
||||||
|
|
||||||
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
|
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
|
MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
|
||||||
: BaseMainWindow(std::move(context), args, parent)
|
: BaseMainWindow(std::move(context), handler, parent)
|
||||||
{
|
{
|
||||||
initMainResource();
|
initMainResource();
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ void MainWindow::createMenu() {
|
|||||||
actionLoadUCF = new QAction("Open UCF", this);
|
actionLoadUCF = new QAction("Open UCF", this);
|
||||||
actionLoadUCF->setIcon(QIcon(":/icons/resources/open_ucf.png"));
|
actionLoadUCF->setIcon(QIcon(":/icons/resources/open_ucf.png"));
|
||||||
actionLoadUCF->setStatusTip("Open UCF file");
|
actionLoadUCF->setStatusTip("Open UCF file");
|
||||||
actionLoadUCF->setEnabled(false);
|
actionLoadUCF->setEnabled(true);
|
||||||
connect(actionLoadUCF, &QAction::triggered, this, &MainWindow::open_ucf);
|
connect(actionLoadUCF, &QAction::triggered, this, &MainWindow::open_ucf);
|
||||||
|
|
||||||
// Add actions in menus
|
// Add actions in menus
|
||||||
@ -64,7 +65,14 @@ void MainWindow::createMenu() {
|
|||||||
menuDesign->addAction(actionLoadUCF);
|
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()
|
void MainWindow::open_ucf()
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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();
|
virtual ~MainWindow();
|
||||||
|
|
||||||
public:
|
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
|
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
||||||
{
|
{
|
||||||
// TODO
|
// XXX
|
||||||
return 13;
|
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
|
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
|
||||||
{
|
{
|
||||||
// TODO
|
// XXX
|
||||||
return 13;
|
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; }
|
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"));
|
cfg.ioBufTypes.insert(id("IOB"));
|
||||||
if (!placer_heap(getCtx(), cfg))
|
if (!placer_heap(getCtx(), cfg))
|
||||||
return false;
|
return false;
|
||||||
|
getCtx()->settings[getCtx()->id("place")] = 1;
|
||||||
} else if (placer == "sa") {
|
} else if (placer == "sa") {
|
||||||
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
|
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
|
||||||
return false;
|
return false;
|
||||||
|
getCtx()->settings[getCtx()->id("place")] = 1;
|
||||||
} else {
|
} else {
|
||||||
log_error("Leuctra architecture does not support placer '%s'\n", placer.c_str());
|
log_error("Leuctra architecture does not support placer '%s'\n", placer.c_str());
|
||||||
}
|
}
|
||||||
return true;
|
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
|
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
|
||||||
{
|
{
|
||||||
// XXX
|
// XXX
|
||||||
delay.min_delay = 11;
|
delay.min_delay = 150;
|
||||||
delay.max_delay = 13;
|
delay.max_delay = 150;
|
||||||
return true;
|
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
|
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
|
// 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;
|
return TMG_IGNORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,10 +772,56 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
|||||||
info.setup = getDelayFromNS(0);
|
info.setup = getDelayFromNS(0);
|
||||||
info.hold = getDelayFromNS(0);
|
info.hold = getDelayFromNS(0);
|
||||||
info.clockToQ = 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;
|
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
|
#ifdef WITH_HEAP
|
||||||
const std::string Arch::defaultPlacer = "heap";
|
const std::string Arch::defaultPlacer = "heap";
|
||||||
#else
|
#else
|
||||||
|
@ -685,6 +685,10 @@ struct Arch : BaseCtx
|
|||||||
BelId getBelByLocation(Loc loc) const
|
BelId getBelByLocation(Loc loc) const
|
||||||
{
|
{
|
||||||
BelId res;
|
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.x = loc.x;
|
||||||
res.location.y = loc.y;
|
res.location.y = loc.y;
|
||||||
res.index = loc.z;
|
res.index = loc.z;
|
||||||
@ -789,6 +793,11 @@ struct Arch : BaseCtx
|
|||||||
|
|
||||||
std::vector<IdString> getBelPins(BelId bel) const;
|
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 {
|
BelId getRelatedBel(BelId bel, int relation) const {
|
||||||
auto &tile = getTile(bel.location);
|
auto &tile = getTile(bel.location);
|
||||||
auto &related = tile.bels[bel.index].related[relation];
|
auto &related = tile.bels[bel.index].related[relation];
|
||||||
@ -1050,8 +1059,8 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
// TODO: delays.
|
// TODO: delays.
|
||||||
DelayInfo del;
|
DelayInfo del;
|
||||||
del.min_delay = 11;
|
del.min_delay = 150;
|
||||||
del.max_delay = 13;
|
del.max_delay = 150;
|
||||||
return del;
|
return del;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1135,7 +1144,7 @@ struct Arch : BaseCtx
|
|||||||
delay_t estimateDelay(WireId src, WireId dst) const;
|
delay_t estimateDelay(WireId src, WireId dst) const;
|
||||||
delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const;
|
delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const;
|
||||||
delay_t getDelayEpsilon() const { return 20; }
|
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; }
|
float getDelayNS(delay_t v) const { return v * 0.001; }
|
||||||
DelayInfo getDelayFromNS(float ns) const
|
DelayInfo getDelayFromNS(float ns) const
|
||||||
{
|
{
|
||||||
@ -1176,22 +1185,43 @@ struct Arch : BaseCtx
|
|||||||
// Placement validity checks
|
// Placement validity checks
|
||||||
// TODO: validate bel subtype (SLICEM vs SLICEL, IOBM vs IOBS, ...).
|
// TODO: validate bel subtype (SLICEM vs SLICEL, IOBM vs IOBS, ...).
|
||||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const {
|
bool isValidBelForCell(CellInfo *cell, BelId bel) const {
|
||||||
if (cell->type == id("LEUCTRA_FF") && (0x924924ull & 1ull << bel.index))
|
if (cell->type == id("LEUCTRA_FF")) {
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
bool isBelLocationValid(BelId bel) const {
|
bool isBelLocationValid(BelId bel) const {
|
||||||
CellInfo *cell = getBoundBelCell(bel);
|
CellInfo *cell = getBoundBelCell(bel);
|
||||||
if (cell == nullptr)
|
if (cell && !isValidBelForCell(cell, bel))
|
||||||
return true;
|
return false;
|
||||||
else
|
return true;
|
||||||
return isValidBelForCell(cell, bel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply UCF constraints to the context
|
// Apply UCF constraints to the context
|
||||||
bool applyUCF(std::string filename, std::istream &in);
|
bool applyUCF(std::string filename, std::istream &in);
|
||||||
|
|
||||||
//void assignArchInfo();
|
void assignArchInfo();
|
||||||
static const std::string defaultPlacer;
|
static const std::string defaultPlacer;
|
||||||
static const std::vector<std::string> availablePlacers;
|
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 operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept
|
||||||
{
|
{
|
||||||
std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX Location>()(pip.location);
|
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;
|
return seed;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
780
leuctra/cells.cc
780
leuctra/cells.cc
@ -19,12 +19,20 @@
|
|||||||
|
|
||||||
#include "cells.h"
|
#include "cells.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
#include "design_utils.h"
|
#include "design_utils.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
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)
|
void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
|
||||||
{
|
{
|
||||||
IdString id = ctx->id(name);
|
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")) {
|
if (type == ctx->id("LEUCTRA_FF")) {
|
||||||
new_cell->params[ctx->id("MODE")] = "FFSYNC";
|
new_cell->params[ctx->id("MODE")] = Property("FF_SYNC");
|
||||||
new_cell->params[ctx->id("INIT")] = "0";
|
|
||||||
add_port(ctx, new_cell.get(), "D", PORT_IN);
|
add_port(ctx, new_cell.get(), "D", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "CLK", 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(), "CE", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "SR", PORT_IN);
|
add_port(ctx, new_cell.get(), "SR", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "Q", PORT_OUT);
|
add_port(ctx, new_cell.get(), "Q", PORT_OUT);
|
||||||
} else if (type == ctx->id("LEUCTRA_LC")) {
|
} 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(), "I1", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "I2", 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(), "I3", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "I4", 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(), "I5", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "I6", 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(), "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")) {
|
} 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(), "O", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "T", 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(), "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")) {
|
} else if (type == ctx->id("ILOGIC2")) {
|
||||||
add_port(ctx, new_cell.get(), "D", PORT_IN);
|
add_port(ctx, new_cell.get(), "D", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "FABRICOUT", PORT_OUT);
|
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(), "OQ", PORT_OUT);
|
||||||
add_port(ctx, new_cell.get(), "T1", PORT_IN);
|
add_port(ctx, new_cell.get(), "T1", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "TQ", PORT_OUT);
|
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 {
|
} else {
|
||||||
log_error("unable to create Leuctra cell of type %s", type.c_str(ctx));
|
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)
|
std::unordered_set<IdString> &todelete_cells)
|
||||||
{
|
{
|
||||||
if (nxio->type == ctx->id("$nextpnr_ibuf")) {
|
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"));
|
replace_port(nxio, ctx->id("O"), iob, ctx->id("I"));
|
||||||
} else if (nxio->type == ctx->id("$nextpnr_obuf")) {
|
} 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"));
|
replace_port(nxio, ctx->id("I"), iob, ctx->id("O"));
|
||||||
} else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
|
} else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
|
||||||
// N.B. tristate will be dealt with below
|
// 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("I"), iob, ctx->id("O"));
|
||||||
replace_port(nxio, ctx->id("O"), iob, ctx->id("I"));
|
replace_port(nxio, ctx->id("O"), iob, ctx->id("I"));
|
||||||
} else {
|
} 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
|
// 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");
|
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"));
|
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"));
|
connect_ports(ctx, inv_lut.get(), ctx->id("O"), iob, ctx->id("T"));
|
||||||
created_cells.push_back(std::move(inv_lut));
|
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)
|
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")) {
|
if (orig->type == ctx->id("FDRE")) {
|
||||||
ff->params[ctx->id("MODE")] = "FF_SYNC";
|
mode = Property("FF_SYNC");
|
||||||
ff->params[ctx->id("INIT")] = "0";
|
srval = false;
|
||||||
replace_port(orig, ctx->id("D"), ff, ctx->id("D"));
|
sr_pin = ctx->id("R");
|
||||||
replace_port(orig, ctx->id("C"), ff, ctx->id("CLK"));
|
is_latch = false;
|
||||||
replace_port(orig, ctx->id("CE"), ff, ctx->id("CE"));
|
clk_inv = false;
|
||||||
replace_port(orig, ctx->id("R"), ff, ctx->id("SR"));
|
} else if (orig->type == ctx->id("FDSE")) {
|
||||||
replace_port(orig, ctx->id("Q"), ff, ctx->id("Q"));
|
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 {
|
} 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)
|
std::unordered_set<IdString> &todelete_cells)
|
||||||
{
|
{
|
||||||
auto &orig_init = orig->params[ctx->id("INIT")];
|
LutData ld = get_lut(ctx, net);
|
||||||
std::string new_init = "0000000000000000000000000000000000000000000000000000000000000000";
|
Property new_init(0, 64);
|
||||||
int nbits = 0;
|
for (int i = 0; i < 64; i++) {
|
||||||
if (orig->type == ctx->id("LUT1")) {
|
new_init.str[i] = ld.init.str[i % (1 << ld.nbits)];
|
||||||
nbits = 2;
|
}
|
||||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
new_init.update_intval();
|
||||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
kill_lut(ctx, ld, todelete_cells);
|
||||||
} else if (orig->type == ctx->id("LUT2")) {
|
|
||||||
nbits = 4;
|
std::unique_ptr<CellInfo> lut_cell =
|
||||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), name);
|
||||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
created_cells.push_back(std::move(lut_cell));
|
||||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
CellInfo *lut = created_cells.back().get();
|
||||||
} else if (orig->type == ctx->id("LUT3")) {
|
lut->params[ctx->id("INIT")] = std::move(new_init);
|
||||||
nbits = 8;
|
|
||||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
if (ld.nbits >= 1)
|
||||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
connect_port(ctx, ld.nets[0], lut, ctx->id("I1"));
|
||||||
replace_port(orig, ctx->id("I2"), lut, ctx->id("I3"));
|
if (ld.nbits >= 2)
|
||||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
connect_port(ctx, ld.nets[1], lut, ctx->id("I2"));
|
||||||
} else if (orig->type == ctx->id("LUT4")) {
|
if (ld.nbits >= 3)
|
||||||
nbits = 16;
|
connect_port(ctx, ld.nets[2], lut, ctx->id("I3"));
|
||||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
if (ld.nbits >= 4)
|
||||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
connect_port(ctx, ld.nets[3], lut, ctx->id("I4"));
|
||||||
replace_port(orig, ctx->id("I2"), lut, ctx->id("I3"));
|
if (ld.nbits >= 5)
|
||||||
replace_port(orig, ctx->id("I3"), lut, ctx->id("I4"));
|
connect_port(ctx, ld.nets[4], lut, ctx->id("I5"));
|
||||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
if (ld.nbits >= 6)
|
||||||
} else if (orig->type == ctx->id("LUT5")) {
|
connect_port(ctx, ld.nets[5], lut, ctx->id("I6"));
|
||||||
nbits = 32;
|
|
||||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
if (ld.cell)
|
||||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
connect_port(ctx, net, lut, ctx->id("O6"));
|
||||||
replace_port(orig, ctx->id("I2"), lut, ctx->id("I3"));
|
|
||||||
replace_port(orig, ctx->id("I3"), lut, ctx->id("I4"));
|
return lut;
|
||||||
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")) {
|
std::pair<CellInfo *, CellInfo *> convert_muxf7(Context *ctx, NetInfo *net, std::string name, std::vector<std::unique_ptr<CellInfo>> &created_cells,
|
||||||
new_init = orig_init;
|
std::unordered_set<IdString> &todelete_cells)
|
||||||
replace_port(orig, ctx->id("I0"), lut, ctx->id("I1"));
|
{
|
||||||
replace_port(orig, ctx->id("I1"), lut, ctx->id("I2"));
|
CellInfo *drv = net->driver.cell;
|
||||||
replace_port(orig, ctx->id("I2"), lut, ctx->id("I3"));
|
if (drv && drv->type == ctx->id("MUXF7")) {
|
||||||
replace_port(orig, ctx->id("I3"), lut, ctx->id("I4"));
|
// Good.
|
||||||
replace_port(orig, ctx->id("I4"), lut, ctx->id("I5"));
|
NetInfo *net0 = drv->ports.at(ctx->id("I0")).net;
|
||||||
replace_port(orig, ctx->id("I5"), lut, ctx->id("I6"));
|
NetInfo *net1 = drv->ports.at(ctx->id("I1")).net;
|
||||||
replace_port(orig, ctx->id("O"), lut, ctx->id("O6"));
|
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 {
|
} 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++) {
|
void convert_muxf8(Context *ctx, NetInfo *net, std::string name, std::vector<std::unique_ptr<CellInfo>> &created_cells,
|
||||||
int obit = i % nbits;
|
std::unordered_set<IdString> &todelete_cells)
|
||||||
new_init[63-i] = '0' + (init >> obit & 1);
|
{
|
||||||
|
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)
|
void insert_ilogic_pass(Context *ctx, CellInfo *iob, CellInfo *ilogic)
|
||||||
{
|
{
|
||||||
replace_port(iob, ctx->id("I"), ilogic, ctx->id("FABRICOUT"));
|
replace_port(iob, ctx->id("I"), ilogic, ctx->id("FABRICOUT"));
|
||||||
connect_ports(ctx, iob, ctx->id("I"), ilogic, ctx->id("D"));
|
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;
|
ilogic->constr_parent = iob;
|
||||||
iob->constr_children.push_back(ilogic);
|
iob->constr_children.push_back(ilogic);
|
||||||
// XXX enum
|
// 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"));
|
replace_port(iob, ctx->id("O"), ologic, ctx->id("D1"));
|
||||||
connect_ports(ctx, ologic, ctx->id("OQ"), iob, ctx->id("O"));
|
connect_ports(ctx, ologic, ctx->id("OQ"), iob, ctx->id("O"));
|
||||||
NetInfo *net_t = iob->ports.at(ctx->id("T")).net;
|
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) {
|
if (net_t != nullptr) {
|
||||||
replace_port(iob, ctx->id("T"), ologic, ctx->id("T1"));
|
replace_port(iob, ctx->id("T"), ologic, ctx->id("T1"));
|
||||||
connect_ports(ctx, ologic, ctx->id("TQ"), iob, ctx->id("T"));
|
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;
|
ologic->constr_parent = iob;
|
||||||
iob->constr_children.push_back(ologic);
|
iob->constr_children.push_back(ologic);
|
||||||
@ -241,4 +649,228 @@ void insert_ologic_pass(Context *ctx, CellInfo *iob, CellInfo *ologic)
|
|||||||
ologic->constr_spec = 2;
|
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
|
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) {
|
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) {
|
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("LUT2")
|
||||||
|| cell->type == ctx->id("LUT3")
|
|| cell->type == ctx->id("LUT3")
|
||||||
|| cell->type == ctx->id("LUT4")
|
|| 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
|
// 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,
|
void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::vector<std::unique_ptr<CellInfo>> &created_cells,
|
||||||
std::unordered_set<IdString> &todelete_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);
|
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);
|
std::unordered_set<IdString> &todelete_cells);
|
||||||
|
|
||||||
void insert_ilogic_pass(Context *ctx, CellInfo *iob, CellInfo *ilogic);
|
void insert_ilogic_pass(Context *ctx, CellInfo *iob, CellInfo *ilogic);
|
||||||
void insert_ologic_pass(Context *ctx, CellInfo *iob, CellInfo *ologic);
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -34,7 +34,7 @@ class LeuctraCommandHandler : public CommandHandler
|
|||||||
public:
|
public:
|
||||||
LeuctraCommandHandler(int argc, char **argv);
|
LeuctraCommandHandler(int argc, char **argv);
|
||||||
virtual ~LeuctraCommandHandler(){};
|
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 setupArchContext(Context *ctx) override{};
|
||||||
void customAfterLoad(Context *ctx) override;
|
void customAfterLoad(Context *ctx) override;
|
||||||
void customBitstream(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"))
|
if (vm.count("device"))
|
||||||
chipArgs.device = vm["device"].as<std::string>();
|
chipArgs.device = vm["device"].as<std::string>();
|
||||||
else
|
else
|
||||||
@ -75,7 +76,13 @@ std::unique_ptr<Context> LeuctraCommandHandler::createContext()
|
|||||||
chipArgs.package = vm["package"].as<std::string>();
|
chipArgs.package = vm["package"].as<std::string>();
|
||||||
if (vm.count("speed"))
|
if (vm.count("speed"))
|
||||||
chipArgs.speed = vm["speed"].as<std::string>();
|
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)
|
void LeuctraCommandHandler::customAfterLoad(Context *ctx)
|
||||||
|
719
leuctra/pack.cc
719
leuctra/pack.cc
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
|
#include "design_utils.h"
|
||||||
#include "cells.h"
|
#include "cells.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -54,14 +55,52 @@ class LeuctraPacker
|
|||||||
new_cells.clear();
|
new_cells.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove nextpnr iob cells, insert Xilinx primitives instead.
|
CellInfo *fetch_nxio(CellInfo *cell, IdString port) {
|
||||||
void pack_iob()
|
PortInfo pi = cell->ports.at(port);
|
||||||
{
|
if (pi.net == nullptr)
|
||||||
log_info("Packing IOBs...\n");
|
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)) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
CellInfo *ci = cell.second;
|
CellInfo *ci = cell.second;
|
||||||
if (is_nextpnr_iob(ctx, ci)) {
|
if (is_nextpnr_iob(ctx, ci)) {
|
||||||
|
abort();
|
||||||
|
NPNR_ASSERT_FALSE("SURVIVED");
|
||||||
|
|
||||||
CellInfo *iob = nullptr;
|
CellInfo *iob = nullptr;
|
||||||
std::unique_ptr<CellInfo> io_cell =
|
std::unique_ptr<CellInfo> io_cell =
|
||||||
create_leuctra_cell(ctx, ctx->id("IOB"), ci->name.str(ctx) + "$iob");
|
create_leuctra_cell(ctx, ctx->id("IOB"), ci->name.str(ctx) + "$iob");
|
||||||
@ -73,10 +112,12 @@ class LeuctraPacker
|
|||||||
if (iob != nullptr) {
|
if (iob != nullptr) {
|
||||||
for (const auto &attr : ci->attrs)
|
for (const auto &attr : ci->attrs)
|
||||||
iob->attrs[attr.first] = attr.second;
|
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"));
|
auto loc_attr = iob->attrs.find(ctx->id("LOC"));
|
||||||
if (loc_attr != iob->attrs.end()) {
|
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);
|
BelId pinBel = ctx->getPackagePinBel(pin);
|
||||||
if (pinBel == BelId()) {
|
if (pinBel == BelId()) {
|
||||||
log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n",
|
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),
|
log_info("pin '%s' constrained to Bel '%s'.\n", iob->name.c_str(ctx),
|
||||||
ctx->getBelName(pinBel).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();
|
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.
|
// Ensure ilogic/ologic cell for every IOB that needs one.
|
||||||
void pack_iologic()
|
void pack_iologic()
|
||||||
{
|
{
|
||||||
@ -122,10 +468,10 @@ class LeuctraPacker
|
|||||||
}
|
}
|
||||||
auto bel_attr = ci->attrs.find(ctx->id("BEL"));
|
auto bel_attr = ci->attrs.find(ctx->id("BEL"));
|
||||||
if (bel_attr != ci->attrs.end()) {
|
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) {
|
for (auto &child : ci->constr_children) {
|
||||||
BelId child_bel = ctx->getRelatedBel(bel, child->constr_spec);
|
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_parent = nullptr;
|
||||||
child->constr_spec = -1;
|
child->constr_spec = -1;
|
||||||
|
|
||||||
@ -138,6 +484,23 @@ class LeuctraPacker
|
|||||||
flush_cells();
|
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.
|
// Convert FFs/latches to LEUCTRA_FFs.
|
||||||
void pack_ff()
|
void pack_ff()
|
||||||
{
|
{
|
||||||
@ -145,7 +508,18 @@ class LeuctraPacker
|
|||||||
|
|
||||||
for (auto cell : sorted(ctx->cells)) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
CellInfo *ci = cell.second;
|
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 =
|
std::unique_ptr<CellInfo> ff_cell =
|
||||||
create_leuctra_cell(ctx, ctx->id("LEUCTRA_FF"), ci->name.str(ctx) + "$ff");
|
create_leuctra_cell(ctx, ctx->id("LEUCTRA_FF"), ci->name.str(ctx) + "$ff");
|
||||||
convert_ff(ctx, ci, ff_cell.get(), new_cells, packed_cells);
|
convert_ff(ctx, ci, ff_cell.get(), new_cells, packed_cells);
|
||||||
@ -158,7 +532,289 @@ class LeuctraPacker
|
|||||||
flush_cells();
|
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()
|
void pack_lut()
|
||||||
{
|
{
|
||||||
log_info("Packing LUTs...\n");
|
log_info("Packing LUTs...\n");
|
||||||
@ -166,12 +822,26 @@ class LeuctraPacker
|
|||||||
for (auto cell : sorted(ctx->cells)) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
CellInfo *ci = cell.second;
|
CellInfo *ci = cell.second;
|
||||||
if (is_xilinx_lut(ctx, ci)) {
|
if (is_xilinx_lut(ctx, ci)) {
|
||||||
std::unique_ptr<CellInfo> lut_cell =
|
NetInfo *net = ci->ports.at(ctx->id("O")).net;
|
||||||
create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), ci->name.str(ctx) + "$lc");
|
convert_lut(ctx, net, ci->name.str(ctx) + "$lc", new_cells, packed_cells);
|
||||||
convert_lut(ctx, ci, lut_cell.get(), new_cells, packed_cells);
|
}
|
||||||
new_cells.push_back(std::move(lut_cell));
|
}
|
||||||
|
|
||||||
packed_cells.insert(ci->name);
|
flush_cells();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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");
|
log_info("Packing constants..\n");
|
||||||
|
|
||||||
std::unique_ptr<CellInfo> gnd_cell = create_leuctra_cell(ctx, ctx->id("LEUCTRA_LC"), "$PACKER_GND");
|
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);
|
std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||||
gnd_net->name = ctx->id("$PACKER_GND_NET");
|
gnd_net->name = ctx->id("$PACKER_GND_NET");
|
||||||
gnd_net->driver.cell = gnd_cell.get();
|
gnd_net->driver.cell = gnd_cell.get();
|
||||||
gnd_net->driver.port = ctx->id("O6");
|
gnd_net->driver.port = ctx->id("O6");
|
||||||
gnd_cell->ports.at(ctx->id("O6")).net = gnd_net.get();
|
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");
|
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);
|
std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||||
vcc_net->name = ctx->id("$PACKER_VCC_NET");
|
vcc_net->name = ctx->id("$PACKER_VCC_NET");
|
||||||
vcc_net->driver.cell = vcc_cell.get();
|
vcc_net->driver.cell = vcc_cell.get();
|
||||||
vcc_net->driver.port = ctx->id("O6");
|
vcc_net->driver.port = ctx->id("O6");
|
||||||
vcc_cell->ports.at(ctx->id("O6")).net = vcc_net.get();
|
vcc_cell->ports.at(ctx->id("O6")).net = vcc_net.get();
|
||||||
|
vcc_cell->attrs[ctx->id("CONST")] = Property(true);
|
||||||
|
|
||||||
std::vector<IdString> dead_nets;
|
std::vector<IdString> dead_nets;
|
||||||
|
|
||||||
@ -253,10 +925,20 @@ class LeuctraPacker
|
|||||||
public:
|
public:
|
||||||
void pack()
|
void pack()
|
||||||
{
|
{
|
||||||
pack_iob();
|
//insert_iob();
|
||||||
|
convert_iob();
|
||||||
pack_iologic();
|
pack_iologic();
|
||||||
|
pack_bram();
|
||||||
pack_ff();
|
pack_ff();
|
||||||
|
pack_ram();
|
||||||
|
// pack_srl();
|
||||||
|
pack_carry();
|
||||||
|
pack_muxf8();
|
||||||
|
pack_muxf7();
|
||||||
|
// clean_inv();
|
||||||
pack_lut();
|
pack_lut();
|
||||||
|
// pack_lc_ff();
|
||||||
|
pack_misc();
|
||||||
pack_constants();
|
pack_constants();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +956,7 @@ bool Arch::pack()
|
|||||||
try {
|
try {
|
||||||
log_break();
|
log_break();
|
||||||
LeuctraPacker(ctx).pack();
|
LeuctraPacker(ctx).pack();
|
||||||
|
ctx->settings[ctx->id("pack")] = 1;
|
||||||
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
||||||
// XXX
|
// XXX
|
||||||
//assignArchInfo();
|
//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 &bel = ctx->getTileTypeBel(belid);
|
||||||
auto name = IdString(bel.name_id);
|
auto name = IdString(bel.name_id);
|
||||||
out << "PRIM " << belid.location.x << " " << belid.location.y << " " << name.str(ctx) << " " << cell.second->name.str(ctx) << std::endl;
|
out << "PRIM " << belid.location.x << " " << belid.location.y << " " << name.str(ctx) << " " << cell.second->name.str(ctx) << std::endl;
|
||||||
for (auto &attr : cell.second->attrs) {
|
for (auto &attr : cell.second->params) {
|
||||||
out << "OPT " << attr.first.str(ctx) << " " << attr.second << std::endl;
|
if (attr.second.is_string)
|
||||||
}
|
out << "PARAMSTR " << attr.first.str(ctx) << " " << attr.second.as_string() << std::endl;
|
||||||
for (auto ¶m : cell.second->params) {
|
else {
|
||||||
out << "OPT " << param.first.str(ctx) << " " << param.second << std::endl;
|
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) {
|
for (auto &net : ctx->nets) {
|
||||||
out << "NET " << net.second->name.str(ctx) << std::endl;
|
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) {
|
for (auto &user : net.second->users) {
|
||||||
out << "TO " << user.cell->name.str(ctx) << " " << user.port.str(ctx) << std::endl;
|
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) {
|
if (words.size() >= 0) {
|
||||||
std::string verb = words.at(0);
|
std::string verb = words.at(0);
|
||||||
if (verb == "CONFIG") {
|
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);
|
lineno);
|
||||||
} else if (verb == "NET") {
|
} else if (verb == "NET") {
|
||||||
if (words.size() < 2)
|
if (words.size() < 2)
|
||||||
@ -82,12 +82,15 @@ bool Arch::applyUCF(std::string filename, std::istream &in)
|
|||||||
pos += 2;
|
pos += 2;
|
||||||
auto fnd_cell = cells.find(id(target));
|
auto fnd_cell = cells.find(id(target));
|
||||||
if (fnd_cell != cells.end()) {
|
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") {
|
} else if (attr == "PULLUP" || attr == "PULLDOWN" || attr == "KEEPER") {
|
||||||
auto fnd_cell = cells.find(id(target));
|
auto fnd_cell = cells.find(id(target));
|
||||||
if (fnd_cell != cells.end()) {
|
if (fnd_cell != cells.end()) {
|
||||||
fnd_cell->second->attrs[id("PULLTYPE")] = attr;
|
fnd_cell->second->params[id("PULLTYPE")] = attr;
|
||||||
}
|
}
|
||||||
} else if (attr == "PERIOD") {
|
} else if (attr == "PERIOD") {
|
||||||
if (pos + 2 > words.size() || words.at(pos) != "=")
|
if (pos + 2 > words.size() || words.at(pos) != "=")
|
||||||
|
Loading…
Reference in New Issue
Block a user