aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Shah <dave@ds0.me>2020-10-22 19:25:17 +0100
committerDavid Shah <dave@ds0.me>2020-11-30 08:45:28 +0000
commite6c28877735444c3bf7927a771cafd9f35bdac72 (patch)
treec9f7fcf99d78ead31707413eed10d54e822d71de
parentc89d830e1689116955c9257dd2933ff49eceeaba (diff)
downloadnextpnr-e6c28877735444c3bf7927a771cafd9f35bdac72.tar.gz
nextpnr-e6c28877735444c3bf7927a771cafd9f35bdac72.tar.bz2
nextpnr-e6c28877735444c3bf7927a771cafd9f35bdac72.zip
nexus: Basic support for carries
Signed-off-by: David Shah <dave@ds0.me>
-rw-r--r--nexus/arch_place.cc2
-rw-r--r--nexus/constids.inc2
-rw-r--r--nexus/fasm.cc6
-rw-r--r--nexus/pack.cc96
4 files changed, 103 insertions, 3 deletions
diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc
index feec75ad..35b14caf 100644
--- a/nexus/arch_place.cc
+++ b/nexus/arch_place.cc
@@ -59,6 +59,8 @@ bool Arch::nexus_logic_tile_valid(LogicTileStatus &lts) const
// If LUT1 is carry then LUT0 must be carry too
if (lut1->lutInfo.is_carry && (lut0 == nullptr || !lut0->lutInfo.is_carry))
return false;
+ if (!lut1->lutInfo.is_carry && lut0 != nullptr && lut0->lutInfo.is_carry)
+ return false;
}
// Check for correct use of FF1 DI
if (ff1 != nullptr && ff1->ffInfo.di != nullptr && (lut1 == nullptr || ff1->ffInfo.di != lut1->lutInfo.f))
diff --git a/nexus/constids.inc b/nexus/constids.inc
index 0c01b577..8b1d2c89 100644
--- a/nexus/constids.inc
+++ b/nexus/constids.inc
@@ -205,3 +205,5 @@ X(CER)
X(RST)
X(WEAMUX)
+
+X(VCC_DRV)
diff --git a/nexus/fasm.cc b/nexus/fasm.cc
index bd710ae4..72efa5da 100644
--- a/nexus/fasm.cc
+++ b/nexus/fasm.cc
@@ -22,6 +22,8 @@
#include "nextpnr.h"
#include "util.h"
+#include <queue>
+
NEXTPNR_NAMESPACE_BEGIN
namespace {
struct NexusFasmWriter
@@ -291,12 +293,10 @@ struct NexusFasmWriter
push(stringf("SLICE%c", slice));
if (cell->params.count(id_INIT))
write_int_vector(stringf("K%d.INIT[15:0]", k), int_or_default(cell->params, id_INIT, 0), 16);
-#if 0
if (cell->lutInfo.is_carry) {
write_bit("MODE.CCU2");
- write_enum(cell, "INJECT", "NO");
+ write_enum(cell, "CCU2.INJECT", "NO");
}
-#endif
pop(2);
}
// Write config for an OXIDE_FF cell
diff --git a/nexus/pack.cc b/nexus/pack.cc
index ea3b4aca..8ba986b5 100644
--- a/nexus/pack.cc
+++ b/nexus/pack.cc
@@ -448,6 +448,14 @@ struct NexusPacker
// Pin is tied to a constant
// If there is a hard constant option; use it
if ((pin_style & int(req_mux)) == req_mux) {
+
+ if (cell->type == id_OXIDE_COMB) {
+ // Due to potentially overlapping routing, explicitly keep the one-driver
+ // until can correctly use the dedicated Vcc route
+ if (str_or_default(cell->params, id_MODE, "LOGIC") != "LOGIC")
+ continue;
+ }
+
disconnect_port(ctx, cell, port_name);
ctx->set_cell_pinmux(cell, port_name, req_mux);
} else if (port.second.net == nullptr) {
@@ -1120,6 +1128,93 @@ struct NexusPacker
}
}
+ void pack_carries()
+ {
+ // Find root carry cells
+ log_info("Packing carries...\n");
+ std::vector<CellInfo *> roots;
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (ci->type != id_CCU2)
+ continue;
+ if (get_net_or_empty(ci, id_CIN) != nullptr)
+ continue;
+ roots.push_back(ci);
+ }
+ for (CellInfo *root : roots) {
+ CellInfo *ci = root;
+ CellInfo *constr_base = nullptr;
+ int idx = 0;
+ do {
+ if (ci->type != id_CCU2)
+ log_error("Found non-carry cell '%s' in carry chain!\n", ctx->nameOf(ci));
+ // Split the carry into two COMB cells
+ std::vector<CellInfo *> combs;
+ for (int i = 0; i < 2; i++)
+ combs.push_back(
+ ctx->createCell(ctx->id(stringf("%s$ccu2_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB));
+ // Rewire LUT ports
+ for (int i = 0; i < 2; i++) {
+ combs[i]->params[id_MODE] = std::string("CCU2");
+ replace_port(ci, bus_flat("A", i), combs[i], id_A);
+ replace_port(ci, bus_flat("B", i), combs[i], id_B);
+ replace_port(ci, bus_flat("C", i), combs[i], id_C);
+ replace_port(ci, bus_flat("D", i), combs[i], id_D);
+ replace_port(ci, bus_flat("S", i), combs[i], id_F);
+ }
+
+ // External carry chain
+ replace_port(ci, id_CIN, combs[0], id_FCI);
+ replace_port(ci, id_COUT, combs[1], id_FCO);
+
+ // Copy parameters
+ if (ci->params.count(id_INJECT))
+ combs[0]->params[id_INJECT] = ci->params[id_INJECT];
+ combs[0]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT0, 16, 0);
+ combs[1]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT1, 16, 0);
+
+ // Internal carry net between the two split COMB cells
+ NetInfo *int_cy = ctx->createNet(ctx->id(stringf("%s$widefn_int_cy$", ctx->nameOf(ci))));
+ combs[0]->addOutput(id_FCO);
+ combs[1]->addInput(id_FCI);
+ connect_port(ctx, int_cy, combs[0], id_FCO);
+ connect_port(ctx, int_cy, combs[1], id_FCI);
+
+ // Relative constraints
+ for (int i = 0; i < 2; i++) {
+ int z = (idx % 8);
+ combs[i]->constr_z = ((z / 2) << 3) | (z % 2);
+ combs[i]->constr_abs_z = true;
+ if (constr_base == nullptr) {
+ // This is the very first cell in the chain
+ constr_base = combs[i];
+ } else {
+ combs[i]->constr_x = (idx / 8);
+ combs[i]->constr_y = 0;
+ combs[i]->constr_parent = constr_base;
+ constr_base->constr_children.push_back(combs[i]);
+ }
+
+ ++idx;
+ }
+
+ ctx->cells.erase(ci->name);
+
+ // Find next cell in chain, if it exists
+ NetInfo *fco = get_net_or_empty(combs[1], id_FCO);
+ ci = nullptr;
+ if (fco != nullptr) {
+ if (fco->users.size() > 1)
+ log_error("Carry cell '%s' has multiple fanout on FCO\n", ctx->nameOf(combs[1]));
+ else if (fco->users.size() == 1) {
+ NPNR_ASSERT(fco->users.at(0).port == id_CIN);
+ ci = fco->users.at(0).cell;
+ }
+ }
+ } while (ci != nullptr);
+ }
+ }
+
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
void operator()()
@@ -1128,6 +1223,7 @@ struct NexusPacker
convert_prims();
pack_bram();
pack_lutram();
+ pack_carries();
pack_widefn();
pack_ffs();
pack_constants();