nexus: LUTRAM support

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2020-10-19 13:31:21 +01:00
parent dfd6b6e39e
commit cbf99d5e53
6 changed files with 157 additions and 5 deletions

View File

@ -30,6 +30,13 @@ void replace_port(CellInfo *old_cell, IdString old_name, CellInfo *rep_cell, IdS
if (!old_cell->ports.count(old_name)) if (!old_cell->ports.count(old_name))
return; return;
PortInfo &old = old_cell->ports.at(old_name); 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); PortInfo &rep = rep_cell->ports.at(rep_name);
NPNR_ASSERT(old.type == rep.type); NPNR_ASSERT(old.type == rep.type);

View File

@ -33,6 +33,16 @@ bool Arch::nexus_logic_tile_valid(LogicTileStatus &lts) const
CellInfo *lut1 = lts.cells[(s << 3) | BEL_LUT1]; CellInfo *lut1 = lts.cells[(s << 3) | BEL_LUT1];
CellInfo *ff0 = lts.cells[(s << 3) | BEL_FF0]; CellInfo *ff0 = lts.cells[(s << 3) | BEL_FF0];
CellInfo *ff1 = lts.cells[(s << 3) | BEL_FF1]; 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) { if (lut0 != nullptr) {
// Check for overuse of M signal // Check for overuse of M signal
if (lut0->lutInfo.mux2_used && ff0 != nullptr && ff0->ffInfo.m != nullptr) if (lut0->lutInfo.mux2_used && ff0 != nullptr && ff0->ffInfo.m != nullptr)

View File

@ -23,7 +23,7 @@ X(WAD0)
X(WAD1) X(WAD1)
X(WAD2) X(WAD2)
X(WAD3) X(WAD3)
X(WD) X(WDI)
X(WCK) X(WCK)
X(WRE) X(WRE)
@ -167,3 +167,6 @@ X(ACC54_CORE)
X(DCC) X(DCC)
X(CLKI) X(CLKI)
X(CLKO) X(CLKO)
X(DPR16X4)
X(INITVAL)

View File

@ -298,6 +298,17 @@ struct NexusFasmWriter
pop(2); 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; std::unordered_set<BelId> used_io;
// Write config for an SEIO33_CORE cell // Write config for an SEIO33_CORE cell
@ -442,6 +453,8 @@ struct NexusFasmWriter
write_comb(ci); write_comb(ci);
else if (ci->type == id_OXIDE_FF) else if (ci->type == id_OXIDE_FF)
write_ff(ci); write_ff(ci);
else if (ci->type == id_RAMW)
write_ramw(ci);
else if (ci->type == id_SEIO33_CORE) else if (ci->type == id_SEIO33_CORE)
write_io33(ci); write_io33(ci);
else if (ci->type == id_SEIO18_CORE) else if (ci->type == id_SEIO18_CORE)

View File

@ -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) {} explicit NexusPacker(Context *ctx) : ctx(ctx) {}
void operator()() void operator()()
{ {
pack_io(); pack_io();
pack_lutram();
pack_ffs(); pack_ffs();
pack_constants(); pack_constants();
pack_luts(); pack_luts();
@ -929,13 +1042,13 @@ void Arch::assignCellInfo(CellInfo *cell)
cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR);
cell->ffInfo.di = get_net_or_empty(cell, id_DI); cell->ffInfo.di = get_net_or_empty(cell, id_DI);
cell->ffInfo.m = get_net_or_empty(cell, id_M); cell->ffInfo.m = get_net_or_empty(cell, id_M);
} else if (cell->type == ID_RAMW) { } else if (cell->type == id_RAMW) {
cell->ffInfo.ctrlset.async = false; cell->ffInfo.ctrlset.async = true;
cell->ffInfo.ctrlset.regddr_en = false; cell->ffInfo.ctrlset.regddr_en = false;
cell->ffInfo.ctrlset.gsr_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.clkmux = id(str_or_default(cell->params, id_CLKMUX, "CLK")).index;
cell->ffInfo.ctrlset.cemux = ID_CE; 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.clk = get_net_or_empty(cell, id_CLK);
cell->ffInfo.ctrlset.ce = nullptr; cell->ffInfo.ctrlset.ce = nullptr;
cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR);

View File

@ -31,11 +31,12 @@ static const std::unordered_map<IdString, Arch::CellPinsData> base_cell_pin_data
{id_WRE, PINSTYLE_DEDI}, {id_WRE, PINSTYLE_DEDI},
{id_FCI, PINSTYLE_DEDI}, {id_FCI, PINSTYLE_DEDI},
{id_F1, PINSTYLE_DEDI},
{id_WAD0, PINSTYLE_DEDI}, {id_WAD0, PINSTYLE_DEDI},
{id_WAD1, PINSTYLE_DEDI}, {id_WAD1, PINSTYLE_DEDI},
{id_WAD2, PINSTYLE_DEDI}, {id_WAD2, PINSTYLE_DEDI},
{id_WAD3, PINSTYLE_DEDI}, {id_WAD3, PINSTYLE_DEDI},
{id_WD, PINSTYLE_DEDI}, {id_WDI, PINSTYLE_DEDI},
{{}, PINSTYLE_PU}, {{}, PINSTYLE_PU},
}}, }},
@ -46,6 +47,11 @@ static const std::unordered_map<IdString, Arch::CellPinsData> base_cell_pin_data
{id_CE, PINSTYLE_CE}, {id_CE, PINSTYLE_CE},
{{}, PINSTYLE_DEDI}, {{}, PINSTYLE_DEDI},
}}, }},
{id_RAMW,
{
{id_CLK, PINSTYLE_CLK},
{{}, PINSTYLE_DEDI},
}},
{id_SEIO18_CORE, {id_SEIO18_CORE,
{ {
{id_T, PINSTYLE_T}, {id_T, PINSTYLE_T},