timing: Add a few more cell types
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
6457b4ca7b
commit
8c1f25cf31
@ -435,10 +435,24 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; };
|
|||||||
|
|
||||||
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
|
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
|
||||||
{
|
{
|
||||||
|
auto lookup_port = [&](IdString p) {
|
||||||
|
auto fnd = cell->tmg_portmap.find(p);
|
||||||
|
return fnd == cell->tmg_portmap.end() ? p : fnd->second;
|
||||||
|
};
|
||||||
if (cell->type == id_OXIDE_COMB) {
|
if (cell->type == id_OXIDE_COMB) {
|
||||||
|
if (cell->lutInfo.is_carry) {
|
||||||
|
bool result = lookup_cell_delay(cell->tmg_index, lookup_port(fromPort), lookup_port(toPort), delay);
|
||||||
|
// Because CCU2 = 2x OXIDE_COMB
|
||||||
|
if (result && fromPort == id_FCI && toPort == id_FCO) {
|
||||||
|
delay.min_delay /= 2;
|
||||||
|
delay.max_delay /= 2;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
if (toPort == id_F)
|
if (toPort == id_F)
|
||||||
return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay);
|
return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,20 +460,44 @@ 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)
|
if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_FCI)
|
||||||
return TMG_COMB_INPUT;
|
return TMG_COMB_INPUT;
|
||||||
if (port == id_F) {
|
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_FCI) && disconnected(id_SEL))
|
||||||
return TMG_IGNORE;
|
return TMG_IGNORE;
|
||||||
else
|
else
|
||||||
return TMG_COMB_OUTPUT;
|
return TMG_COMB_OUTPUT;
|
||||||
}
|
}
|
||||||
|
} else if (cell->type == id_OXIDE_FF) {
|
||||||
|
if (port == id_CLK)
|
||||||
|
return TMG_CLOCK_INPUT;
|
||||||
|
else if (port == id_Q) {
|
||||||
|
clockInfoCount = 1;
|
||||||
|
return TMG_REGISTER_OUTPUT;
|
||||||
|
} else {
|
||||||
|
clockInfoCount = 1;
|
||||||
|
return TMG_REGISTER_INPUT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return TMG_IGNORE;
|
return TMG_IGNORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const { return {}; }
|
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
|
||||||
|
{
|
||||||
|
TimingClockingInfo info;
|
||||||
|
if (cell->type == id_OXIDE_FF) {
|
||||||
|
info.edge = (cell->ffInfo.ctrlset.clkmux == ID_INV) ? FALLING_EDGE : RISING_EDGE;
|
||||||
|
info.clock_port = id_CLK;
|
||||||
|
if (port == id_Q)
|
||||||
|
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 {
|
||||||
|
NPNR_ASSERT_FALSE("missing clocking info");
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
@ -750,6 +788,38 @@ void Arch::lookup_cell_setuphold(int type_idx, IdString from_port, IdString cloc
|
|||||||
[](const CellSetupHoldPOD &sh) { return std::make_pair(sh.sig_port, sh.clock_port); },
|
[](const CellSetupHoldPOD &sh) { return std::make_pair(sh.sig_port, sh.clock_port); },
|
||||||
std::make_pair(from_port.index, clock.index));
|
std::make_pair(from_port.index, clock.index));
|
||||||
NPNR_ASSERT(dly_idx != -1);
|
NPNR_ASSERT(dly_idx != -1);
|
||||||
|
setup.min_delay = ct.setup_holds[dly_idx].min_setup;
|
||||||
|
setup.max_delay = ct.setup_holds[dly_idx].max_setup;
|
||||||
|
hold.min_delay = ct.setup_holds[dly_idx].min_hold;
|
||||||
|
hold.max_delay = ct.setup_holds[dly_idx].max_hold;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arch::lookup_cell_setuphold_clock(int type_idx, IdString from_port, IdString &clock, DelayInfo &setup,
|
||||||
|
DelayInfo &hold) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(type_idx != -1);
|
||||||
|
const auto &ct = speed_grade->cell_types[type_idx];
|
||||||
|
int dly_idx = db_binary_search(
|
||||||
|
ct.setup_holds.get(), ct.num_setup_holds, [](const CellSetupHoldPOD &sh) { return sh.sig_port; },
|
||||||
|
from_port.index);
|
||||||
|
NPNR_ASSERT(dly_idx != -1);
|
||||||
|
clock = IdString(ct.setup_holds[dly_idx].clock_port);
|
||||||
|
setup.min_delay = ct.setup_holds[dly_idx].min_setup;
|
||||||
|
setup.max_delay = ct.setup_holds[dly_idx].max_setup;
|
||||||
|
hold.min_delay = ct.setup_holds[dly_idx].min_hold;
|
||||||
|
hold.max_delay = ct.setup_holds[dly_idx].max_hold;
|
||||||
|
}
|
||||||
|
void Arch::lookup_cell_clock_out(int type_idx, IdString to_port, IdString &clock, DelayInfo &delay) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(type_idx != -1);
|
||||||
|
const auto &ct = speed_grade->cell_types[type_idx];
|
||||||
|
int dly_idx = db_binary_search(
|
||||||
|
ct.prop_delays.get(), ct.num_prop_delays, [](const CellPropDelayPOD &pd) { return pd.to_port; },
|
||||||
|
to_port.index);
|
||||||
|
NPNR_ASSERT(dly_idx != -1);
|
||||||
|
clock = ct.prop_delays[dly_idx].from_port;
|
||||||
|
delay.min_delay = ct.prop_delays[dly_idx].min_delay;
|
||||||
|
delay.max_delay = ct.prop_delays[dly_idx].max_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
@ -1547,10 +1547,19 @@ struct Arch : BaseCtx
|
|||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
// Cell timing lookup helpers
|
// Cell timing lookup helpers
|
||||||
|
|
||||||
|
// Given cell type and variant, get the index inside the speed grade timing data
|
||||||
int get_cell_timing_idx(IdString cell_type, IdString cell_variant = IdString()) const;
|
int get_cell_timing_idx(IdString cell_type, IdString cell_variant = IdString()) const;
|
||||||
|
// Return true and set delay if a comb path exists in a given cell timing index
|
||||||
bool lookup_cell_delay(int type_idx, IdString from_port, IdString to_port, DelayInfo &delay) const;
|
bool lookup_cell_delay(int type_idx, IdString from_port, IdString to_port, DelayInfo &delay) const;
|
||||||
|
// Get setup and hold time for a given cell timing index and signal/clock pair
|
||||||
void lookup_cell_setuphold(int type_idx, IdString from_port, IdString clock, DelayInfo &setup,
|
void lookup_cell_setuphold(int type_idx, IdString from_port, IdString clock, DelayInfo &setup,
|
||||||
DelayInfo &hold) const;
|
DelayInfo &hold) const;
|
||||||
|
// Get setup and hold time and associated clock for a given cell timing index and signal
|
||||||
|
void lookup_cell_setuphold_clock(int type_idx, IdString from_port, IdString &clock, DelayInfo &setup,
|
||||||
|
DelayInfo &hold) const;
|
||||||
|
// Similar to lookup_cell_delay but only needs the 'to' signal, intended for clk->out delays
|
||||||
|
void lookup_cell_clock_out(int type_idx, IdString to_port, IdString &clock, DelayInfo &delay) const;
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
// List of IO constraints, used by PDC parser
|
// List of IO constraints, used by PDC parser
|
||||||
|
@ -188,6 +188,8 @@ struct ArchCellInfo
|
|||||||
};
|
};
|
||||||
|
|
||||||
int tmg_index = -1;
|
int tmg_index = -1;
|
||||||
|
// Map from cell/bel ports to logical timing ports
|
||||||
|
std::unordered_map<IdString, IdString> tmg_portmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -90,6 +90,7 @@ X(CIN)
|
|||||||
X(COUT)
|
X(COUT)
|
||||||
X(S0)
|
X(S0)
|
||||||
X(S1)
|
X(S1)
|
||||||
|
X(F0)
|
||||||
|
|
||||||
X(CLKMUX)
|
X(CLKMUX)
|
||||||
X(CEMUX)
|
X(CEMUX)
|
||||||
|
@ -1264,7 +1264,14 @@ 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, id_LUT4);
|
cell->tmg_index = get_cell_timing_idx(id_OXIDE_COMB, cell->lutInfo.is_carry ? id_CCU2 : id_LUT4);
|
||||||
|
if (cell->lutInfo.is_carry) {
|
||||||
|
cell->tmg_portmap[id_A] = id_A0;
|
||||||
|
cell->tmg_portmap[id_B] = id_B0;
|
||||||
|
cell->tmg_portmap[id_C] = id_C0;
|
||||||
|
cell->tmg_portmap[id_D] = id_D0;
|
||||||
|
cell->tmg_portmap[id_F] = id_F0;
|
||||||
|
}
|
||||||
} 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";
|
||||||
cell->ffInfo.ctrlset.regddr_en = is_enabled(cell, id_REGDDR);
|
cell->ffInfo.ctrlset.regddr_en = is_enabled(cell, id_REGDDR);
|
||||||
@ -1277,6 +1284,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 = 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);
|
||||||
|
cell->tmg_index = get_cell_timing_idx(id_OXIDE_FF, id("PPP:SYNC"));
|
||||||
} else if (cell->type == id_RAMW) {
|
} else if (cell->type == id_RAMW) {
|
||||||
cell->ffInfo.ctrlset.async = true;
|
cell->ffInfo.ctrlset.async = true;
|
||||||
cell->ffInfo.ctrlset.regddr_en = false;
|
cell->ffInfo.ctrlset.regddr_en = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user