nexus: LUTRAM support
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
dfd6b6e39e
commit
cbf99d5e53
@ -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);
|
||||
|
||||
|
@ -33,6 +33,16 @@ bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) 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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
119
nexus/pack.cc
119
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);
|
||||
|
@ -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},
|
||||
|
Loading…
Reference in New Issue
Block a user