aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Shah <dave@ds0.me>2020-10-19 13:31:21 +0100
committerDavid Shah <dave@ds0.me>2020-11-30 08:45:27 +0000
commitcbf99d5e5390d8439722e0172067b687be5ac060 (patch)
tree89f2a8095bf7b0c9f578e8d3caceb6201a98b513
parentdfd6b6e39e56a2c2b10b051b9b54926e120f319e (diff)
downloadnextpnr-cbf99d5e5390d8439722e0172067b687be5ac060.tar.gz
nextpnr-cbf99d5e5390d8439722e0172067b687be5ac060.tar.bz2
nextpnr-cbf99d5e5390d8439722e0172067b687be5ac060.zip
nexus: LUTRAM support
Signed-off-by: David Shah <dave@ds0.me>
-rw-r--r--common/design_utils.cc7
-rw-r--r--nexus/arch_place.cc10
-rw-r--r--nexus/constids.inc5
-rw-r--r--nexus/fasm.cc13
-rw-r--r--nexus/pack.cc119
-rw-r--r--nexus/pins.cc8
6 files changed, 157 insertions, 5 deletions
diff --git a/common/design_utils.cc b/common/design_utils.cc
index dd866758..9478afb2 100644
--- a/common/design_utils.cc
+++ b/common/design_utils.cc
@@ -30,6 +30,13 @@ void replace_port(CellInfo *old_cell, IdString old_name, CellInfo *rep_cell, IdS
if (!old_cell->ports.count(old_name))
return;
PortInfo &old = old_cell->ports.at(old_name);
+
+ // Create port on the replacement cell if it doesn't already exist
+ if (!rep_cell->ports.count(rep_name)) {
+ rep_cell->ports[rep_name].name = rep_name;
+ rep_cell->ports[rep_name].type = old.type;
+ }
+
PortInfo &rep = rep_cell->ports.at(rep_name);
NPNR_ASSERT(old.type == rep.type);
diff --git a/nexus/arch_place.cc b/nexus/arch_place.cc
index 7e50de29..feec75ad 100644
--- a/nexus/arch_place.cc
+++ b/nexus/arch_place.cc
@@ -33,6 +33,16 @@ bool Arch::nexus_logic_tile_valid(LogicTileStatus &lts) const
CellInfo *lut1 = lts.cells[(s << 3) | BEL_LUT1];
CellInfo *ff0 = lts.cells[(s << 3) | BEL_FF0];
CellInfo *ff1 = lts.cells[(s << 3) | BEL_FF1];
+
+ if (s == 2) {
+ CellInfo *ramw = lts.cells[(s << 3) | BEL_RAMW];
+ // Nothing else in SLICEC can be used if the RAMW is used
+ if (ramw != nullptr) {
+ if (lut0 != nullptr || lut1 != nullptr || ff0 != nullptr || ff1 != nullptr)
+ return false;
+ }
+ }
+
if (lut0 != nullptr) {
// Check for overuse of M signal
if (lut0->lutInfo.mux2_used && ff0 != nullptr && ff0->ffInfo.m != nullptr)
diff --git a/nexus/constids.inc b/nexus/constids.inc
index c89c1ec8..625931f2 100644
--- a/nexus/constids.inc
+++ b/nexus/constids.inc
@@ -23,7 +23,7 @@ X(WAD0)
X(WAD1)
X(WAD2)
X(WAD3)
-X(WD)
+X(WDI)
X(WCK)
X(WRE)
@@ -167,3 +167,6 @@ X(ACC54_CORE)
X(DCC)
X(CLKI)
X(CLKO)
+
+X(DPR16X4)
+X(INITVAL)
diff --git a/nexus/fasm.cc b/nexus/fasm.cc
index 66747461..031cd9b3 100644
--- a/nexus/fasm.cc
+++ b/nexus/fasm.cc
@@ -298,6 +298,17 @@ struct NexusFasmWriter
pop(2);
}
+ // Write out config for an OXIDE_RAMW cell
+ void write_ramw(const CellInfo *cell)
+ {
+ BelId bel = cell->bel;
+ push_tile(bel.tile, id_PLC);
+ push("SLICEC");
+ write_bit("MODE.RAMW");
+ write_cell_muxes(cell);
+ pop(2);
+ }
+
std::unordered_set<BelId> used_io;
// Write config for an SEIO33_CORE cell
@@ -442,6 +453,8 @@ struct NexusFasmWriter
write_comb(ci);
else if (ci->type == id_OXIDE_FF)
write_ff(ci);
+ else if (ci->type == id_RAMW)
+ write_ramw(ci);
else if (ci->type == id_SEIO33_CORE)
write_io33(ci);
else if (ci->type == id_SEIO18_CORE)
diff --git a/nexus/pack.cc b/nexus/pack.cc
index 33e0a11f..7dbef99b 100644
--- a/nexus/pack.cc
+++ b/nexus/pack.cc
@@ -878,11 +878,124 @@ struct NexusPacker
}
}
+ // Get a bus port name
+ IdString bus(const std::string &base, int i) { return ctx->id(stringf("%s[%d]", base.c_str(), i)); }
+
+ IdString bus_flat(const std::string &base, int i) { return ctx->id(stringf("%s%d", base.c_str(), i)); }
+
+ // Pack a LUTRAM into COMB and RAMW cells
+ void pack_lutram()
+ {
+ // Do this so we don't have an iterate-and-modfiy situation
+ std::vector<CellInfo *> lutrams;
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (ci->type != id_DPR16X4)
+ continue;
+ lutrams.push_back(ci);
+ }
+
+ // Port permutation vectors
+ IdString ramw_wdo[4] = {id_D1, id_C1, id_A1, id_B1};
+ IdString ramw_wado[4] = {id_D0, id_B0, id_C0, id_A0};
+ IdString comb0_rad[4] = {id_D, id_B, id_C, id_A};
+ IdString comb1_rad[4] = {id_C, id_B, id_D, id_A};
+
+ for (CellInfo *ci : lutrams) {
+ // Create constituent cells
+ CellInfo *ramw = ctx->createCell(ctx->id(stringf("%s$lutram_ramw$", ctx->nameOf(ci))), id_RAMW);
+ std::vector<CellInfo *> combs;
+ for (int i = 0; i < 4; i++)
+ combs.push_back(
+ ctx->createCell(ctx->id(stringf("%s$lutram_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB));
+ // Rewiring - external WCK and WRE
+ replace_port(ci, id_WCK, ramw, id_CLK);
+ replace_port(ci, id_WRE, ramw, id_LSR);
+
+ // Internal WCK and WRE signals
+ ramw->addOutput(id_WCKO);
+ ramw->addOutput(id_WREO);
+ NetInfo *int_wck = ctx->createNet(ctx->id(stringf("%s$lutram_wck$", ctx->nameOf(ci))));
+ NetInfo *int_wre = ctx->createNet(ctx->id(stringf("%s$lutram_wre$", ctx->nameOf(ci))));
+ connect_port(ctx, int_wck, ramw, id_WCKO);
+ connect_port(ctx, int_wre, ramw, id_WREO);
+
+ uint64_t initval = ctx->parse_lattice_param(ci, id_INITVAL, 64, 0).as_int64();
+
+ // Rewiring - buses
+ for (int i = 0; i < 4; i++) {
+ // Write address - external
+ replace_port(ci, bus("WAD", i), ramw, ramw_wado[i]);
+ // Write data - external
+ replace_port(ci, bus("DI", i), ramw, ramw_wdo[i]);
+ // Read data
+ replace_port(ci, bus("DO", i), combs[i], id_F);
+ // Read address
+ NetInfo *rad = get_net_or_empty(ci, bus("RAD", i));
+ if (rad != nullptr) {
+ for (int j = 0; j < 4; j++) {
+ IdString port = (j % 2) ? comb1_rad[i] : comb0_rad[i];
+ combs[j]->addInput(port);
+ connect_port(ctx, rad, combs[j], port);
+ }
+ disconnect_port(ctx, ci, bus("RAD", i));
+ }
+ // Write address - internal
+ NetInfo *int_wad = ctx->createNet(ctx->id(stringf("%s$lutram_wad[%d]$", ctx->nameOf(ci), i)));
+ ramw->addOutput(bus_flat("WADO", i));
+ connect_port(ctx, int_wad, ramw, bus_flat("WADO", i));
+ for (int j = 0; j < 4; j++) {
+ combs[j]->addInput(bus_flat("WAD", i));
+ connect_port(ctx, int_wad, combs[j], bus_flat("WAD", i));
+ }
+ // Write data - internal
+ NetInfo *int_wd = ctx->createNet(ctx->id(stringf("%s$lutram_wd[%d]$", ctx->nameOf(ci), i)));
+ ramw->addOutput(bus_flat("WDO", i));
+ connect_port(ctx, int_wd, ramw, bus_flat("WDO", i));
+ combs[i]->addInput(id_WDI);
+ connect_port(ctx, int_wd, combs[i], id_WDI);
+ // Write clock and enable - internal
+ combs[i]->addInput(id_WCK);
+ combs[i]->addInput(id_WRE);
+ connect_port(ctx, int_wck, combs[i], id_WCK);
+ connect_port(ctx, int_wre, combs[i], id_WRE);
+ // Remap init val
+ uint64_t split_init = 0;
+ for (int j = 0; j < 16; j++)
+ if (initval & (1ULL << (4 * j + i)))
+ split_init |= (1 << j);
+ combs[i]->params[id_INIT] = Property(split_init, 16);
+ }
+
+ // Setup relative constraints
+ combs[0]->constr_z = 0;
+ combs[0]->constr_abs_z = true;
+ for (int i = 1; i < 4; i++) {
+ combs[i]->constr_x = 0;
+ combs[i]->constr_y = 0;
+ combs[i]->constr_z = ((i / 2) << 3) | (i % 2);
+ combs[i]->constr_abs_z = true;
+ combs[i]->constr_parent = combs[0];
+ combs[0]->constr_children.push_back(combs[i]);
+ }
+
+ ramw->constr_x = 0;
+ ramw->constr_y = 0;
+ ramw->constr_z = (2 << 3) | Arch::BEL_RAMW;
+ ramw->constr_abs_z = true;
+ ramw->constr_parent = combs[0];
+ combs[0]->constr_children.push_back(ramw);
+ // Remove now-packed cell
+ ctx->cells.erase(ci->name);
+ }
+ }
+
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
void operator()()
{
pack_io();
+ pack_lutram();
pack_ffs();
pack_constants();
pack_luts();
@@ -929,13 +1042,13 @@ void Arch::assignCellInfo(CellInfo *cell)
cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR);
cell->ffInfo.di = get_net_or_empty(cell, id_DI);
cell->ffInfo.m = get_net_or_empty(cell, id_M);
- } else if (cell->type == ID_RAMW) {
- cell->ffInfo.ctrlset.async = false;
+ } else if (cell->type == id_RAMW) {
+ cell->ffInfo.ctrlset.async = true;
cell->ffInfo.ctrlset.regddr_en = false;
cell->ffInfo.ctrlset.gsr_en = false;
cell->ffInfo.ctrlset.clkmux = id(str_or_default(cell->params, id_CLKMUX, "CLK")).index;
cell->ffInfo.ctrlset.cemux = ID_CE;
- cell->ffInfo.ctrlset.lsrmux = id(str_or_default(cell->params, id_LSRMUX, "LSR")).index;
+ cell->ffInfo.ctrlset.lsrmux = ID_INV;
cell->ffInfo.ctrlset.clk = get_net_or_empty(cell, id_CLK);
cell->ffInfo.ctrlset.ce = nullptr;
cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR);
diff --git a/nexus/pins.cc b/nexus/pins.cc
index f9ddd5f5..1fa62b28 100644
--- a/nexus/pins.cc
+++ b/nexus/pins.cc
@@ -31,11 +31,12 @@ static const std::unordered_map<IdString, Arch::CellPinsData> base_cell_pin_data
{id_WRE, PINSTYLE_DEDI},
{id_FCI, PINSTYLE_DEDI},
+ {id_F1, PINSTYLE_DEDI},
{id_WAD0, PINSTYLE_DEDI},
{id_WAD1, PINSTYLE_DEDI},
{id_WAD2, PINSTYLE_DEDI},
{id_WAD3, PINSTYLE_DEDI},
- {id_WD, PINSTYLE_DEDI},
+ {id_WDI, PINSTYLE_DEDI},
{{}, PINSTYLE_PU},
}},
@@ -46,6 +47,11 @@ static const std::unordered_map<IdString, Arch::CellPinsData> base_cell_pin_data
{id_CE, PINSTYLE_CE},
{{}, PINSTYLE_DEDI},
}},
+ {id_RAMW,
+ {
+ {id_CLK, PINSTYLE_CLK},
+ {{}, PINSTYLE_DEDI},
+ }},
{id_SEIO18_CORE,
{
{id_T, PINSTYLE_T},