Merge branch 'master' into shadowram

This commit is contained in:
Pepijn de Vos 2022-07-02 13:29:44 +02:00
commit 0641ff47d9
9 changed files with 324 additions and 25 deletions

View File

@ -145,7 +145,8 @@ An example of how to use the generic flow is in [generic/examples](generic/examp
The nextpnr GUI is not built by default, to reduce the number of dependencies for a standard headless build. To enable it, add `-DBUILD_GUI=ON` to the CMake command line and ensure that Qt5 and OpenGL are available:
- On Ubuntu, install `qt5-default`
- On Ubuntu 22.04 LTS, install `qtcreator qtbase5-dev qt5-qmake`
- On other Ubuntu versions, install `qt5-default`
- For MSVC vcpkg, install `qt5-base` (32-bit) or `qt5-base:x64-windows` (64-bit)
- For Homebrew, install `qt5` and add qt5 in path: `echo 'export PATH="/usr/local/opt/qt/bin:$PATH"' >> ~/.bash_profile`
` - this change is effective in next terminal session, so please re-open terminal window before building

View File

@ -21,6 +21,7 @@
#ifndef NO_PYTHON
#include "arch_pybindings.h"
#include "bitstream.h"
#include "nextpnr.h"
#include "pybindings.h"
@ -69,6 +70,8 @@ void arch_wrap_python(py::module &m)
WRAP_MAP_UPTR(m, CellMap, "IdCellMap");
WRAP_MAP_UPTR(m, NetMap, "IdNetMap");
WRAP_MAP(m, HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
m.def("write_bitstream", &write_bitstream);
}
NEXTPNR_NAMESPACE_END

View File

@ -19,6 +19,7 @@
*/
#include <boost/algorithm/string.hpp>
#include <cells.h>
#include <iostream>
#include <math.h>
#include <regex>
@ -33,6 +34,8 @@
NEXTPNR_NAMESPACE_BEGIN
const PairPOD *pairLookup(const PairPOD *list, const size_t len, const int dest);
// GUI
void Arch::fixClockSpineDecals(void)
{
@ -175,6 +178,145 @@ DecalXY Arch::getWireDecal(WireId wire) const
return wires.at(wire).decalxy_active;
}
bool Arch::allocate_longwire(NetInfo *ni, int lw_idx)
{
NPNR_ASSERT(ni != nullptr);
if (ni->driver.cell == nullptr) {
return false;
}
if (ni->name == id("$PACKER_VCC_NET") || ni->name == id("$PACKER_GND_NET")) {
return false;
}
// So far only for OBUF
switch (ni->driver.cell->type.index) {
case ID_ODDR: /* fall-through*/
case ID_ODDRC: /* fall-through*/
case ID_IOBUF: /* fall-through*/
case ID_TBUF:
return false;
case ID_OBUF:
if (getCtx()->debug) {
log_info("Long wire for IO %s\n", nameOf(ni));
}
ni = ni->driver.cell->ports.at(id_I).net;
return allocate_longwire(ni, lw_idx);
break;
default:
break;
}
if (getCtx()->debug) {
log_info("Requested index:%d\n", lw_idx);
}
if (avail_longwires == 0 || (lw_idx != -1 && (avail_longwires & (1 << lw_idx)) == 0)) {
return false;
}
int longwire = lw_idx;
if (lw_idx == -1) {
for (longwire = 7; longwire >= 0; --longwire) {
if (avail_longwires & (1 << longwire)) {
break;
}
}
}
avail_longwires &= ~(1 << longwire);
// BUFS cell
CellInfo *bufs;
char buf[40];
snprintf(buf, sizeof(buf), "$PACKER_BUFS%d", longwire);
std::unique_ptr<CellInfo> new_cell = create_generic_cell(getCtx(), id_BUFS, buf);
bufs = new_cell.get();
cells[bufs->name] = std::move(new_cell);
if (lw_idx != -1) {
bufs->cluster = bufs->name;
bufs->constr_z = lw_idx + BelZ::bufs_0_z;
bufs->constr_abs_z = true;
bufs->constr_children.clear();
}
// old driver -> bufs LW input net
snprintf(buf, sizeof(buf), "$PACKER_BUFS_%c", longwire + 'A');
auto net = std::make_unique<NetInfo>(id(buf));
NetInfo *bufs_net = net.get();
nets[net->name] = std::move(net);
// split the net
CellInfo *driver_cell = ni->driver.cell;
IdString driver_port = ni->driver.port;
driver_cell->disconnectPort(driver_port);
bufs->connectPort(id_O, ni);
bufs->connectPort(id_I, bufs_net);
driver_cell->connectPort(driver_port, bufs_net);
if (getCtx()->debug) {
log_info("Long wire %d was allocated\n", longwire);
}
return true;
}
void Arch::fix_longwire_bels()
{
// After routing, it is clear which wires and in which bus SS00 and SS40 are used and
// in which quadrant they are routed. Here we write it in the attributes.
for (auto &cell : cells) {
CellInfo *ci = cell.second.get();
if (ci->type != id_BUFS) {
continue;
}
const NetInfo *ni = ci->getPort(id_O);
if (ni == nullptr) {
continue;
}
// bus wire is one of the wires
// value does not matter, but the L/R parameter itself
for (auto &wire : ni->wires) {
WireId w = wires[wire.first].type;
switch (w.hash()) {
case ID_LWSPINETL0:
case ID_LWSPINETL1:
case ID_LWSPINETL2:
case ID_LWSPINETL3:
case ID_LWSPINETL4:
case ID_LWSPINETL5:
case ID_LWSPINETL6:
case ID_LWSPINETL7:
case ID_LWSPINEBL0:
case ID_LWSPINEBL1:
case ID_LWSPINEBL2:
case ID_LWSPINEBL3:
case ID_LWSPINEBL4:
case ID_LWSPINEBL5:
case ID_LWSPINEBL6:
case ID_LWSPINEBL7:
ci->setParam(id("L"), Property(w.str(this)));
break;
case ID_LWSPINETR0:
case ID_LWSPINETR1:
case ID_LWSPINETR2:
case ID_LWSPINETR3:
case ID_LWSPINETR4:
case ID_LWSPINETR5:
case ID_LWSPINETR6:
case ID_LWSPINETR7:
case ID_LWSPINEBR0:
case ID_LWSPINEBR1:
case ID_LWSPINEBR2:
case ID_LWSPINEBR3:
case ID_LWSPINEBR4:
case ID_LWSPINEBR5:
case ID_LWSPINEBR6:
case ID_LWSPINEBR7:
ci->setParam(id("R"), Property(w.str(this)));
break;
default:
break;
}
}
}
}
WireInfo &Arch::wire_info(IdString wire)
{
auto w = wires.find(wire);
@ -487,7 +629,15 @@ IdString Arch::wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString
}
snprintf(buf, 32, "%c%d0", direction, num);
wire = id(buf);
snprintf(buf, 32, "R%dC%d_%c%d", row + 1, col + 1, direction, num);
// local aliases
const TilePOD *tile = db->grid[row * db->cols + col].get();
auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, wire.index);
if (local_alias != nullptr) {
wire = IdString(local_alias->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, wire.c_str(this));
} else {
snprintf(buf, 32, "R%dC%d_%c%d", row + 1, col + 1, direction, num);
}
return id(buf);
}
@ -628,6 +778,28 @@ DelayQuad Arch::getWireTypeDelay(IdString wire)
case ID_W830:
len = id_X8;
break;
case ID_LT02:
case ID_LT13:
glbsrc = id_SPINE_TAP_SCLK_0;
break;
case ID_LT01:
case ID_LT04:
glbsrc = id_SPINE_TAP_SCLK_1;
break;
case ID_LBO0:
case ID_LBO1:
glbsrc = id_TAP_BRANCH_SCLK;
break;
case ID_LB01:
case ID_LB11:
case ID_LB21:
case ID_LB31:
case ID_LB41:
case ID_LB51:
case ID_LB61:
case ID_LB71:
glbsrc = id_BRANCH_SCLK;
break;
case ID_GT00:
case ID_GT10:
glbsrc = id_SPINE_TAP_PCLK;
@ -647,7 +819,9 @@ DelayQuad Arch::getWireTypeDelay(IdString wire)
glbsrc = id_BRANCH_PCLK;
break;
default:
if (wire.str(this).rfind("SPINE", 0) == 0) {
if (wire.str(this).rfind("LWSPINE", 0) == 0) {
glbsrc = IdString(ID_CENT_SPINE_SCLK);
} else if (wire.str(this).rfind("SPINE", 0) == 0) {
glbsrc = IdString(ID_CENT_SPINE_PCLK);
} else if (wire.str(this).rfind("UNK", 0) == 0) {
glbsrc = IdString(ID_PIO_CENT_PCLK);
@ -691,7 +865,7 @@ void Arch::read_cst(std::istream &in)
std::regex port_attrre = std::regex("([^ =;]+=[^ =;]+) *([^;]*;)");
std::regex iobelre = std::regex("IO([TRBL])([0-9]+)\\[?([A-Z])\\]?");
std::regex inslocre = std::regex("INS_LOC +\"([^\"]+)\" +R([0-9]+)C([0-9]+)\\[([0-9])\\]\\[([AB])\\] *;.*");
std::regex clockre = std::regex("CLOCK_LOC +\"([^\"]+)\" +BUF([GS])[^;]*;");
std::regex clockre = std::regex("CLOCK_LOC +\"([^\"]+)\" +BUF([GS])(\\[([0-7])\\])?[^;]*;.*");
std::smatch match, match_attr, match_pinloc;
std::string line, pinline;
enum
@ -732,15 +906,23 @@ void Arch::read_cst(std::istream &in)
continue;
}
switch (cst_type) {
case clock: { // CLOCK name BUFG|S
case clock: { // CLOCK name BUFG|S=#
std::string which_clock = match[2];
std::string lw = match[4];
int lw_idx = -1;
if (lw.length() > 0) {
lw_idx = atoi(lw.c_str());
log_info("lw_idx:%d\n", lw_idx);
}
if (which_clock.at(0) == 'S') {
auto ni = nets.find(net);
if (ni == nets.end()) {
log_info("Net %s not found\n", net.c_str(this));
continue;
}
log_info("Long wires are not implemented. The %s network will use normal routing.\n", net.c_str(this));
if (!allocate_longwire(ni->second.get(), lw_idx)) {
log_info("Can't use the long wires. The %s network will use normal routing.\n", net.c_str(this));
}
} else {
log_info("BUFG isn't supported\n");
continue;
@ -1021,6 +1203,31 @@ Arch::Arch(ArchArgs args) : args(args)
bool dff = true;
bool oddrc = false;
switch (static_cast<ConstIds>(bel->type_id)) {
case ID_BUFS7:
z++; /* fall-through*/
case ID_BUFS6:
z++; /* fall-through*/
case ID_BUFS5:
z++; /* fall-through*/
case ID_BUFS4:
z++; /* fall-through*/
case ID_BUFS3:
z++; /* fall-through*/
case ID_BUFS2:
z++; /* fall-through*/
case ID_BUFS1:
z++; /* fall-through*/
case ID_BUFS0:
snprintf(buf, 32, "R%dC%d_BUFS%d", row + 1, col + 1, z);
belname = id(buf);
addBel(belname, id_BUFS, Loc(col, row, BelZ::bufs_0_z + z), false);
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_I)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_I, id(buf));
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_O)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelOutput(belname, id_O, id(buf));
break;
case ID_GSR0:
snprintf(buf, 32, "R%dC%d_GSR0", row + 1, col + 1);
belname = id(buf);
@ -1324,12 +1531,6 @@ Arch::Arch(ArchArgs args) : args(args)
snprintf(buf, 32, "R%dC%d_%s_%s", row + 1, col + 1, srcid.c_str(this), destid.c_str(this));
IdString pipname = id(buf);
DelayQuad delay = getWireTypeDelay(destid);
// local alias
auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, srcid.index);
if (local_alias != nullptr) {
srcid = IdString(local_alias->src_id);
gsrcname = wireToGlobal(srcrow, srccol, db, srcid);
}
// global alias
srcid = IdString(pip.src_id);
GlobalAliasPOD alias;
@ -1705,6 +1906,7 @@ bool Arch::route()
}
getCtx()->settings[id_route] = 1;
archInfoToAttributes();
fix_longwire_bels();
return result;
}

View File

@ -459,6 +459,9 @@ struct Arch : BaseArch<ArchRanges>
void assignArchInfo() override;
bool cellsCompatible(const CellInfo **cells, int count) const;
bool haveBelType(int x, int y, IdString bel_type);
bool allocate_longwire(NetInfo *ni, int lw_idx = -1);
void fix_longwire_bels();
// chip db version
unsigned int const chipdb_version = 1;
@ -475,6 +478,9 @@ struct Arch : BaseArch<ArchRanges>
// XXX GW1NR-9 iobuf quirk
bool gw1n9_quirk = false;
// 8 Long wires
uint8_t avail_longwires = 0xff;
// Permissible combinations of modes in a single slice
std::map<const IdString, IdString> dff_comp_mode;
};
@ -488,7 +494,9 @@ enum
lutram_0_z = 30, // start Z for the IOLOGIC bels
vcc_0_z = 277, // virtual VCC bel Z
gnd_0_z = 278, // virtual VSS bel Z
osc_z = 280 // Z for the oscillator bels
osc_z = 280, // Z for the oscillator bels
bufs_0_z = 281, // Z for long wire buffer bel
free_z = 289 // Must be the last, one can use z starting from this value, adjust accordingly.
};
}

View File

@ -77,6 +77,9 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
new_cell->addOutput(id_G);
} else if (type == id_VCC) {
new_cell->addOutput(id_V);
} else if (type == id_BUFS) {
new_cell->addInput(id_I);
new_cell->addOutput(id_O);
} else {
log_error("unable to create generic cell of type %s\n", type.c_str(ctx));
}

View File

@ -679,6 +679,81 @@ X(IOBHS)
X(IOBIS)
X(IOBJS)
// long wires
X(BUFS)
X(BUFS0)
X(BUFS1)
X(BUFS2)
X(BUFS3)
X(BUFS4)
X(BUFS5)
X(BUFS6)
X(BUFS7)
X(LWT0)
X(LWB0)
X(LWT1)
X(LWB1)
X(LWT2)
X(LWB2)
X(LWT3)
X(LWB3)
X(LWT4)
X(LWB4)
X(LWT5)
X(LWB5)
X(LWT6)
X(LWB6)
X(LWT7)
X(LWB7)
X(LWSPINETL0)
X(LWSPINETL1)
X(LWSPINETL2)
X(LWSPINETL3)
X(LWSPINETL4)
X(LWSPINETL5)
X(LWSPINETL6)
X(LWSPINETL7)
X(LWSPINETR0)
X(LWSPINETR1)
X(LWSPINETR2)
X(LWSPINETR3)
X(LWSPINETR4)
X(LWSPINETR5)
X(LWSPINETR6)
X(LWSPINETR7)
X(LWSPINEBL0)
X(LWSPINEBL1)
X(LWSPINEBL2)
X(LWSPINEBL3)
X(LWSPINEBL4)
X(LWSPINEBL5)
X(LWSPINEBL6)
X(LWSPINEBL7)
X(LWSPINEBR0)
X(LWSPINEBR1)
X(LWSPINEBR2)
X(LWSPINEBR3)
X(LWSPINEBR4)
X(LWSPINEBR5)
X(LWSPINEBR6)
X(LWSPINEBR7)
X(LWI0)
X(LWI1)
X(LWI2)
X(LWI3)
X(LWI4)
X(LWI5)
X(LWI6)
X(LWI7)
X(LWO0)
X(LWO1)
X(LWO2)
X(LWO3)
X(LWO4)
X(LWO5)
X(LWO6)
X(LWO7)
// IOLOGIC
X(TX)
X(XXX_VSS)
@ -826,6 +901,11 @@ X(CENT_SPINE_PCLK)
X(SPINE_TAP_PCLK)
X(TAP_BRANCH_PCLK)
X(BRANCH_PCLK)
X(CENT_SPINE_SCLK)
X(SPINE_TAP_SCLK_0)
X(SPINE_TAP_SCLK_1)
X(TAP_BRANCH_SCLK)
X(BRANCH_SCLK)
X(clksetpos)
X(clkholdpos)
X(clk_qpos)

View File

@ -1033,9 +1033,11 @@ static void pack_io(Context *ctx)
if (constr_bel != ci->attrs.end()) {
constr_bel_name = constr_bel->second.as_string();
}
constr_bel = iob->attrs.find(id_BEL);
if (constr_bel != iob->attrs.end()) {
constr_bel_name = constr_bel->second.as_string();
if (iob != nullptr) {
constr_bel = iob->attrs.find(id_BEL);
if (constr_bel != iob->attrs.end()) {
constr_bel_name = constr_bel->second.as_string();
}
}
if (!constr_bel_name.empty()) {
BelId constr_bel = ctx->getBelByNameStr(constr_bel_name);

View File

@ -95,7 +95,7 @@ bool Arch::isBelLocationValid(BelId bel) const
}
return logic_cells_compatible(bel_cells.data(), num_cells);
} else {
CellInfo *cell = getBoundBelCell(bel);
const CellInfo *cell = getBoundBelCell(bel);
if (cell == nullptr)
return true;
else if (cell->type == id_SB_IO) {
@ -107,7 +107,7 @@ bool Arch::isBelLocationValid(BelId bel) const
for (auto pin : getWireBelPins(wire)) {
if (pin.pin == id_PLLOUT_A || pin.pin == id_PLLOUT_B) {
// Is there a PLL there ?
auto pll_cell = getBoundBelCell(pin.bel);
const CellInfo *pll_cell = getBoundBelCell(pin.bel);
if (pll_cell == nullptr)
break;
@ -116,11 +116,11 @@ bool Arch::isBelLocationValid(BelId bel) const
break;
// Is that SB_IO used at an input ?
if ((cell->ports[id_D_IN_0].net == nullptr) && (cell->ports[id_D_IN_1].net == nullptr))
if ((cell->getPort(id_D_IN_0) == nullptr) && (cell->getPort(id_D_IN_1) == nullptr))
break;
// Are we perhaps a PAD INPUT Bel that can be placed here?
if (pll_cell->attrs[id_BEL_PAD_INPUT] == getBelName(bel).str(getCtx()))
if (str_or_default(pll_cell->attrs, id_BEL_PAD_INPUT, "") == getBelName(bel).str(getCtx()))
return true;
// Conflict
@ -144,7 +144,7 @@ bool Arch::isBelLocationValid(BelId bel) const
} else {
// Check LVDS IO is not placed at complement location
BelId compBel = getBelByLocation(compLoc);
CellInfo *compCell = getBoundBelCell(compBel);
const CellInfo *compCell = getBoundBelCell(compBel);
if (compCell && compCell->ioInfo.lvds)
return false;
@ -161,10 +161,10 @@ bool Arch::isBelLocationValid(BelId bel) const
_io_pintype_need_clk_en(cell->ioInfo.pintype),
_io_pintype_need_clk_en(compCell->ioInfo.pintype),
};
NetInfo *nets[] = {
cell->ports[id_INPUT_CLK].net, compCell->ports[id_INPUT_CLK].net,
cell->ports[id_OUTPUT_CLK].net, compCell->ports[id_OUTPUT_CLK].net,
cell->ports[id_CLOCK_ENABLE].net, compCell->ports[id_CLOCK_ENABLE].net,
const NetInfo *nets[] = {
cell->getPort(id_INPUT_CLK), compCell->getPort(id_INPUT_CLK),
cell->getPort(id_OUTPUT_CLK), compCell->getPort(id_OUTPUT_CLK),
cell->getPort(id_CLOCK_ENABLE), compCell->getPort(id_CLOCK_ENABLE),
};
for (int i = 0; i < 6; i++)