aboutsummaryrefslogtreecommitdiffstats
path: root/gowin
diff options
context:
space:
mode:
authorYRabbit <rabbit@yrabbit.cyou>2022-05-27 22:44:21 +1000
committerYRabbit <rabbit@yrabbit.cyou>2022-05-27 22:44:21 +1000
commitdb696af2fe53ab4dc341a09303bb6cd371d76519 (patch)
tree04c7ffcc0bdda09aec357f7d739e598b07252ad6 /gowin
parent4ecbf6c6e941699413e0d0bba660fc9dc18efcfe (diff)
downloadnextpnr-db696af2fe53ab4dc341a09303bb6cd371d76519.tar.gz
nextpnr-db696af2fe53ab4dc341a09303bb6cd371d76519.tar.bz2
nextpnr-db696af2fe53ab4dc341a09303bb6cd371d76519.zip
gowin: Add support for long wires
Gowin chips have a highly sophisticated system of long wires that are wired to each cell and allow the clock or logic to spread quickly. This commit implements some of the capabilities of the long wire system for quadrants, leaving out the fine-tuning of them for each column. To make use of the long wire system, the specified wire is cut at the driver and a special cell is placed between the driver and the rest of the wire. * VCC and GND can not use long wires because they are in every cell and there is no point in using a net * Long wire numbers can be specified manually or assigned automatically. * The route from the driver to the port of the new cell can be quite long, this will have to be solved somehow. * It might make sense to add a mechanism for automatically finding candidates for long wires. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
Diffstat (limited to 'gowin')
-rw-r--r--gowin/arch.cc206
-rw-r--r--gowin/arch.h10
-rw-r--r--gowin/cells.cc3
-rw-r--r--gowin/constids.inc80
-rw-r--r--gowin/pack.cc8
5 files changed, 299 insertions, 8 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc
index 4fc2cd43..a991879e 100644
--- a/gowin/arch.cc
+++ b/gowin/arch.cc
@@ -19,6 +19,7 @@
*/
#include <boost/algorithm/string.hpp>
+#include <cells.h>
#include <iostream>
#include <math.h>
#include <regex>
@@ -175,6 +176,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);
@@ -628,6 +768,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 +809,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 +855,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 +896,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 +1193,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);
@@ -1675,6 +1872,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 14181d79..8bbbd514 100644
--- a/gowin/arch.h
+++ b/gowin/arch.h
@@ -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;
};
@@ -487,7 +493,9 @@ enum
iologic_0_z = 20, // 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 c3b21782..6010164a 100644
--- a/gowin/cells.cc
+++ b/gowin/cells.cc
@@ -69,6 +69,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));
}
diff --git a/gowin/constids.inc b/gowin/constids.inc
index 3691c506..99c791f8 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)
@@ -812,6 +887,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 4adfec1a..83820142 100644
--- a/gowin/pack.cc
+++ b/gowin/pack.cc
@@ -947,9 +947,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);