nexus: Add LUTRAM and WIDEFN9 timing support

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2020-11-11 11:31:14 +00:00
parent 8c1f25cf31
commit 9b89a82573
3 changed files with 33 additions and 4 deletions

View File

@ -449,7 +449,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
} }
return result; return result;
} else { } else {
if (toPort == id_F) if (toPort == id_F || toPort == id_OFX)
return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay); return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay);
} }
} }
@ -460,11 +460,12 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
{ {
auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; }; auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; };
if (cell->type == id_OXIDE_COMB) { if (cell->type == id_OXIDE_COMB) {
if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_FCI) if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_SEL || port == id_F1 ||
port == id_FCI || port == id_WDI)
return TMG_COMB_INPUT; return TMG_COMB_INPUT;
if (port == id_F || port == id_OFX || port == id_FCO) { if (port == id_F || port == id_OFX || port == id_FCO) {
if (disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) && if (disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) &&
disconnected(id_FCI) && disconnected(id_SEL)) disconnected(id_FCI) && disconnected(id_SEL) && disconnected(id_WDI))
return TMG_IGNORE; return TMG_IGNORE;
else else
return TMG_COMB_OUTPUT; return TMG_COMB_OUTPUT;
@ -479,6 +480,17 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
clockInfoCount = 1; clockInfoCount = 1;
return TMG_REGISTER_INPUT; return TMG_REGISTER_INPUT;
} }
} else if (cell->type == id_RAMW) {
if (port == id_CLK)
return TMG_CLOCK_INPUT;
else if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3) {
clockInfoCount = 1;
return TMG_REGISTER_OUTPUT;
} else if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 ||
port == id_D0 || port == id_D1) {
clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
} }
return TMG_IGNORE; return TMG_IGNORE;
} }
@ -493,6 +505,13 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, port, info.clockToQ)); NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, port, info.clockToQ));
else else
lookup_cell_setuphold(cell->tmg_index, port, id_CLK, info.setup, info.hold); lookup_cell_setuphold(cell->tmg_index, port, id_CLK, info.setup, info.hold);
} else if (cell->type == id_RAMW) {
info.edge = (cell->ffInfo.ctrlset.clkmux == ID_INV) ? FALLING_EDGE : RISING_EDGE;
info.clock_port = id_CLK;
if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3)
NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, port, info.clockToQ));
else
lookup_cell_setuphold(cell->tmg_index, port, id_CLK, info.setup, info.hold);
} else { } else {
NPNR_ASSERT_FALSE("missing clocking info"); NPNR_ASSERT_FALSE("missing clocking info");
} }

View File

@ -171,6 +171,7 @@ X(CLKO)
X(DPR16X4) X(DPR16X4)
X(INITVAL) X(INITVAL)
X(DPRAM)
X(DP16K) X(DP16K)
X(PDP16K) X(PDP16K)

View File

@ -990,6 +990,8 @@ struct NexusPacker
if (initval & (1ULL << (4 * j + i))) if (initval & (1ULL << (4 * j + i)))
split_init |= (1 << j); split_init |= (1 << j);
combs[i]->params[id_INIT] = Property(split_init, 16); combs[i]->params[id_INIT] = Property(split_init, 16);
combs[i]->params[id_MODE] = std::string("DPRAM");
} }
// Setup relative constraints // Setup relative constraints
@ -1264,13 +1266,19 @@ void Arch::assignCellInfo(CellInfo *cell)
cell->lutInfo.mux2_used = port_used(cell, id_OFX); cell->lutInfo.mux2_used = port_used(cell, id_OFX);
cell->lutInfo.f = get_net_or_empty(cell, id_F); cell->lutInfo.f = get_net_or_empty(cell, id_F);
cell->lutInfo.ofx = get_net_or_empty(cell, id_OFX); cell->lutInfo.ofx = get_net_or_empty(cell, id_OFX);
cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, cell->lutInfo.is_carry ? id_CCU2 : id_LUT4);
if (cell->lutInfo.is_carry) { if (cell->lutInfo.is_carry) {
cell->tmg_portmap[id_A] = id_A0; cell->tmg_portmap[id_A] = id_A0;
cell->tmg_portmap[id_B] = id_B0; cell->tmg_portmap[id_B] = id_B0;
cell->tmg_portmap[id_C] = id_C0; cell->tmg_portmap[id_C] = id_C0;
cell->tmg_portmap[id_D] = id_D0; cell->tmg_portmap[id_D] = id_D0;
cell->tmg_portmap[id_F] = id_F0; cell->tmg_portmap[id_F] = id_F0;
cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_CCU2);
} else if (cell->lutInfo.ofx != nullptr) {
cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_WIDEFN9);
} else if (cell->lutInfo.is_memory) {
cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_DPRAM);
} else {
cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, id_LUT4);
} }
} else if (cell->type == id_OXIDE_FF) { } else if (cell->type == id_OXIDE_FF) {
cell->ffInfo.ctrlset.async = str_or_default(cell->params, id_SRMODE, "LSR_OVER_CE") == "ASYNC"; cell->ffInfo.ctrlset.async = str_or_default(cell->params, id_SRMODE, "LSR_OVER_CE") == "ASYNC";
@ -1297,6 +1305,7 @@ 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 = nullptr; cell->ffInfo.di = nullptr;
cell->ffInfo.m = nullptr; cell->ffInfo.m = nullptr;
cell->tmg_index = get_cell_timing_idx(id_RAMW);
} }
} }