From 8c1f25cf31644c95e4ba0a1c2a2b212abd622167 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 10 Nov 2020 11:01:30 +0000 Subject: [PATCH] timing: Add a few more cell types Signed-off-by: David Shah --- nexus/arch.cc | 82 ++++++++++++++++++++++++++++++++++++++++++---- nexus/arch.h | 9 +++++ nexus/archdefs.h | 2 ++ nexus/constids.inc | 1 + nexus/pack.cc | 10 +++++- 5 files changed, 97 insertions(+), 7 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 78e10e16..b6181011 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -435,9 +435,23 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; 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 (toPort == id_F) - return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay); + 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) + return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay); + } } 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; }; 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; - 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) && - disconnected(id_FCI)) + disconnected(id_FCI) && disconnected(id_SEL)) return TMG_IGNORE; else 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; } -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); }, std::make_pair(from_port.index, clock.index)); 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; } // ----------------------------------------------------------------------- diff --git a/nexus/arch.h b/nexus/arch.h index cbe39316..9b75a09e 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1547,10 +1547,19 @@ struct Arch : BaseCtx // ------------------------------------------------- // 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; + // 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; + // 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, 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 diff --git a/nexus/archdefs.h b/nexus/archdefs.h index 8925c5f3..adc1342c 100644 --- a/nexus/archdefs.h +++ b/nexus/archdefs.h @@ -188,6 +188,8 @@ struct ArchCellInfo }; int tmg_index = -1; + // Map from cell/bel ports to logical timing ports + std::unordered_map tmg_portmap; }; NEXTPNR_NAMESPACE_END diff --git a/nexus/constids.inc b/nexus/constids.inc index 2ac2349c..b4d53265 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -90,6 +90,7 @@ X(CIN) X(COUT) X(S0) X(S1) +X(F0) X(CLKMUX) X(CEMUX) diff --git a/nexus/pack.cc b/nexus/pack.cc index 5dcfd9e2..481dcbf6 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1264,7 +1264,14 @@ void Arch::assignCellInfo(CellInfo *cell) cell->lutInfo.mux2_used = port_used(cell, id_OFX); cell->lutInfo.f = get_net_or_empty(cell, id_F); 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) { 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); @@ -1277,6 +1284,7 @@ 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); + cell->tmg_index = get_cell_timing_idx(id_OXIDE_FF, id("PPP:SYNC")); } else if (cell->type == id_RAMW) { cell->ffInfo.ctrlset.async = true; cell->ffInfo.ctrlset.regddr_en = false;