diff --git a/.github/workflows/interchange_ci.yml b/.github/workflows/interchange_ci.yml.disable similarity index 100% rename from .github/workflows/interchange_ci.yml rename to .github/workflows/interchange_ci.yml.disable diff --git a/README.md b/README.md index 14128b87..36ff9a8b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc index 593feb33..1621eda5 100644 --- a/ecp5/arch_pybindings.cc +++ b/ecp5/arch_pybindings.cc @@ -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, "HierarchyMap"); + + m.def("write_bitstream", &write_bitstream); } NEXTPNR_NAMESPACE_END diff --git a/gowin/arch.cc b/gowin/arch.cc index 65f15204..db1430f9 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -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 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(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(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; } diff --git a/gowin/arch.h b/gowin/arch.h index c13cdf09..cd20f28a 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -459,6 +459,9 @@ struct Arch : BaseArch 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 // 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 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. }; } diff --git a/gowin/cells.cc b/gowin/cells.cc index 0dc0ce06..d83b07c8 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -77,6 +77,9 @@ std::unique_ptr 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)); } diff --git a/gowin/constids.inc b/gowin/constids.inc index 0c788ecd..8916f093 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -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) diff --git a/gowin/pack.cc b/gowin/pack.cc index 0ba71705..fbd2092f 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -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); diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index 3b024d81..67ddf777 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -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++)