diff options
| author | gatecat <gatecat@ds0.me> | 2021-02-26 11:25:07 +0000 | 
|---|---|---|
| committer | gatecat <gatecat@ds0.me> | 2021-03-04 10:29:36 +0000 | 
| commit | d0772ce1e384609835ef3d918752038d42a8ce34 (patch) | |
| tree | 28a3226437e3fca91fedf735f57e77cffbf6f22e | |
| parent | fac6a6c068a7672e59796b542afe9a904b6dc04b (diff) | |
| download | nextpnr-d0772ce1e384609835ef3d918752038d42a8ce34.tar.gz nextpnr-d0772ce1e384609835ef3d918752038d42a8ce34.tar.bz2 nextpnr-d0772ce1e384609835ef3d918752038d42a8ce34.zip  | |
timing: Import cell delays to our own structures
Signed-off-by: gatecat <gatecat@ds0.me>
| -rw-r--r-- | common/timing.cc | 98 | ||||
| -rw-r--r-- | common/timing.h | 25 | 
2 files changed, 123 insertions, 0 deletions
diff --git a/common/timing.cc b/common/timing.cc index a61c0beb..785f9e7b 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -30,6 +30,100 @@  NEXTPNR_NAMESPACE_BEGIN +void TimingAnalyser::setup() +{ +    init_ports(); +    get_cell_delays(); +} + +void TimingAnalyser::init_ports() +{ +    // Per cell port structures +    for (auto cell : sorted(ctx->cells)) { +        CellInfo *ci = cell.second; +        for (auto port : sorted_ref(ci->ports)) { +            auto &data = ports[CellPortKey(ci->name, port.first)]; +            data.cell_port = CellPortKey(ci->name, port.first); +        } +    } +    // Cell port to net port mapping +    for (auto net : sorted(ctx->nets)) { +        NetInfo *ni = net.second; +        if (ni->driver.cell != nullptr) +            ports[CellPortKey(ni->driver)].net_port = NetPortKey(ni->name); +        for (size_t i = 0; i < ni->users.size(); i++) +            ports[CellPortKey(ni->users.at(i))].net_port = NetPortKey(ni->name, i); +    } +} + +void TimingAnalyser::get_cell_delays() +{ +    for (auto &port : ports) { +        CellInfo *ci = cell_info(port.first); +        auto &pi = port_info(port.first); +        auto &pd = port.second; + +        IdString name = port.first.port; +        // Ignore dangling ports altogether for timing purposes +        if (pd.net_port.net == IdString()) +            continue; +        pd.cell_arcs.clear(); +        int clkInfoCount = 0; +        TimingPortClass cls = ctx->getPortTimingClass(ci, name, clkInfoCount); +        if (cls == TMG_STARTPOINT || cls == TMG_ENDPOINT || cls == TMG_CLOCK_INPUT || cls == TMG_GEN_CLOCK || +            cls == TMG_IGNORE) +            continue; +        if (pi.type == PORT_IN) { +            // Input ports might have setup/hold relationships +            if (cls == TMG_REGISTER_INPUT) { +                for (int i = 0; i < clkInfoCount; i++) { +                    auto info = ctx->getPortClockingInfo(ci, name, i); +                    pd.cell_arcs.emplace_back(CellArc::SETUP, info.clock_port, DelayQuad(info.setup, info.setup), +                                              info.edge); +                    pd.cell_arcs.emplace_back(CellArc::HOLD, info.clock_port, DelayQuad(info.hold, info.hold), +                                              info.edge); +                } +            } +            // Combinational delays through cell +            for (auto &other_port : ci->ports) { +                auto &op = other_port.second; +                // ignore dangling ports and non-outputs +                if (op.net == nullptr || op.type != PORT_OUT) +                    continue; +                DelayQuad delay; +                bool is_path = ctx->getCellDelay(ci, name, other_port.first, delay); +                if (is_path) +                    pd.cell_arcs.emplace_back(CellArc::COMBINATIONAL, other_port.first, delay); +            } +        } else if (pi.type == PORT_OUT) { +            // Output ports might have clk-to-q relationships +            if (cls == TMG_REGISTER_OUTPUT) { +                for (int i = 0; i < clkInfoCount; i++) { +                    auto info = ctx->getPortClockingInfo(ci, name, i); +                    pd.cell_arcs.emplace_back(CellArc::CLK_TO_Q, info.clock_port, info.clockToQ, info.edge); +                } +            } +            // Combinational delays through cell +            for (auto &other_port : ci->ports) { +                auto &op = other_port.second; +                // ignore dangling ports and non-inputs +                if (op.net == nullptr || op.type != PORT_IN) +                    continue; +                DelayQuad delay; +                bool is_path = ctx->getCellDelay(ci, other_port.first, name, delay); +                if (is_path) +                    pd.cell_arcs.emplace_back(CellArc::COMBINATIONAL, other_port.first, delay); +            } +        } +    } +} + +CellInfo *TimingAnalyser::cell_info(const CellPortKey &key) { return ctx->cells.at(key.cell).get(); } + +PortInfo &TimingAnalyser::port_info(const CellPortKey &key) { return ctx->cells.at(key.cell)->ports.at(key.port); } + +/** LEGACY CODE BEGIN **/ +  namespace {  struct ClockEvent  { @@ -1005,6 +1099,10 @@ void get_criticalities(Context *ctx, NetCriticalityMap *net_crit)      net_crit->clear();      Timing timing(ctx, true, true, &crit_paths, nullptr, net_crit);      timing.walk_paths(); + +    // Test the new timing analyser, too +    TimingAnalyser sta_v2(ctx); +    sta_v2.setup();  }  NEXTPNR_NAMESPACE_END diff --git a/common/timing.h b/common/timing.h index 9b9b99e1..9c15911d 100644 --- a/common/timing.h +++ b/common/timing.h @@ -26,6 +26,14 @@ NEXTPNR_NAMESPACE_BEGIN  struct CellPortKey  { +    CellPortKey(){}; +    CellPortKey(IdString cell, IdString port) : cell(cell), port(port){}; +    explicit CellPortKey(const PortRef &pr) +    { +        NPNR_ASSERT(pr.cell != nullptr); +        cell = pr.cell->name; +        port = pr.port; +    }      IdString cell, port;      struct Hash      { @@ -43,6 +51,10 @@ struct NetPortKey  {      IdString net;      size_t idx; +    NetPortKey(){}; +    explicit NetPortKey(IdString net) : net(net), idx(DRIVER_IDX){};        // driver +    explicit NetPortKey(IdString net, size_t user) : net(net), idx(user){}; // user +      static const size_t DRIVER_IDX = std::numeric_limits<size_t>::max();      inline bool is_driver() const { return (idx == DRIVER_IDX); } @@ -87,8 +99,11 @@ struct TimingAnalyser  {    public:      TimingAnalyser(Context *ctx) : ctx(ctx){}; +    void setup();    private: +    void init_ports(); +    void get_cell_delays();      // To avoid storing the domain tag structure (which could get large when considering more complex constrained tag      // cases), assign each domain an ID and use that instead      typedef int domain_id_t; @@ -113,6 +128,7 @@ struct TimingAnalyser      // A cell timing arc, used to cache cell timings and reduce the number of potentially-expensive Arch API calls      struct CellArc      { +          enum ArcType          {              COMBINATIONAL, @@ -120,10 +136,16 @@ struct TimingAnalyser              HOLD,              CLK_TO_Q          } type; +          IdString other_port;          DelayQuad value;          // Clock polarity, not used for combinational arcs          ClockEdge edge; + +        CellArc(ArcType type, IdString other_port, DelayQuad value) +                : type(type), other_port(other_port), value(value), edge(RISING_EDGE){}; +        CellArc(ArcType type, IdString other_port, DelayQuad value, ClockEdge edge) +                : type(type), other_port(other_port), value(value), edge(edge){};      };      // Timing data for every cell port @@ -139,6 +161,9 @@ struct TimingAnalyser          DelayPair route_delay;      }; +    CellInfo *cell_info(const CellPortKey &key); +    PortInfo &port_info(const CellPortKey &key); +      std::unordered_map<CellPortKey, PerPort, CellPortKey::Hash> ports;      std::unordered_map<ClockDomainKey, domain_id_t, ClockDomainKey::Hash> domain_to_id;      std::vector<ClockDomainKey> id_to_domain;  | 
