diff options
Diffstat (limited to 'common')
| -rw-r--r-- | common/archcheck.cc | 146 | ||||
| -rw-r--r-- | common/log.h | 6 | ||||
| -rw-r--r-- | common/nextpnr.cc | 61 | ||||
| -rw-r--r-- | common/nextpnr.h | 202 | ||||
| -rw-r--r-- | common/place_common.cc | 4 | ||||
| -rw-r--r-- | common/placer1.cc | 14 | ||||
| -rw-r--r-- | common/router1.cc | 312 | ||||
| -rw-r--r-- | common/timing.cc | 4 | ||||
| -rw-r--r-- | common/timing.h | 2 | 
9 files changed, 493 insertions, 258 deletions
diff --git a/common/archcheck.cc b/common/archcheck.cc new file mode 100644 index 00000000..5c4ef26c --- /dev/null +++ b/common/archcheck.cc @@ -0,0 +1,146 @@ +/* + *  nextpnr -- Next Generation Place and Route + * + *  Copyright (C) 2018  Clifford Wolf <clifford@symbioticeda.com> + * + *  Permission to use, copy, modify, and/or distribute this software for any + *  purpose with or without fee is hereby granted, provided that the above + *  copyright notice and this permission notice appear in all copies. + * + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "nextpnr.h" +#include "log.h" + +#if 0 +#define dbg(...) log(__VA_ARGS__) +#else +#define dbg(...) +#endif + +USING_NEXTPNR_NAMESPACE + +namespace { + +void archcheck_names(const Context *ctx) +{ +    log_info("Checking entity names.\n"); + +    log_info("Checking bel names..\n"); +    for (BelId bel : ctx->getBels()) { +        IdString name = ctx->getBelName(bel); +        BelId bel2 = ctx->getBelByName(name); +        log_assert(bel == bel2); +    } + +    log_info("Checking wire names..\n"); +    for (WireId wire : ctx->getWires()) { +        IdString name = ctx->getWireName(wire); +        WireId wire2 = ctx->getWireByName(name); +        log_assert(wire == wire2); +    } + +    log_info("Checking pip names..\n"); +    for (PipId pip : ctx->getPips()) { +        IdString name = ctx->getPipName(pip); +        PipId pip2 = ctx->getPipByName(name); +        log_assert(pip == pip2); +    } + +    log_break(); +} + +void archcheck_locs(const Context *ctx) +{ +    log_info("Checking location data.\n"); + +    log_info("Checking all bels..\n"); +    for (BelId bel : ctx->getBels()) { +        log_assert(bel != BelId()); +        dbg("> %s\n", ctx->getBelName(bel).c_str(ctx)); + +        Loc loc = ctx->getBelLocation(bel); +        dbg("   ... %d %d %d\n", loc.x, loc.y, loc.z); + +        log_assert(0 <= loc.x); +        log_assert(0 <= loc.y); +        log_assert(0 <= loc.z); +        log_assert(loc.x < ctx->getGridDimX()); +        log_assert(loc.y < ctx->getGridDimY()); +        log_assert(loc.z < ctx->getTileDimZ(loc.x, loc.y)); + +        BelId bel2 = ctx->getBelByLocation(loc); +        dbg("   ... %s\n", ctx->getBelName(bel2).c_str(ctx)); +        log_assert(bel == bel2); +    } + +    log_info("Checking all locations..\n"); +    for (int x = 0; x < ctx->getGridDimX(); x++) +        for (int y = 0; y < ctx->getGridDimY(); y++) +        { +            dbg("> %d %d\n", x, y); +            std::unordered_set<int> usedz; + +            for (int z = 0; z < ctx->getTileDimZ(x, y); z++) { +                BelId bel = ctx->getBelByLocation(Loc(x, y, z)); +                if (bel == BelId()) +                    continue; +                Loc loc = ctx->getBelLocation(bel); +                dbg("   + %d %s\n", z, ctx->getBelName(bel).c_str(ctx)); +                log_assert(x == loc.x); +                log_assert(y == loc.y); +                log_assert(z == loc.z); +                usedz.insert(z); +            } + +            for (BelId bel : ctx->getBelsByTile(x, y)) { +                Loc loc = ctx->getBelLocation(bel); +                dbg("   - %d %s\n", loc.z, ctx->getBelName(bel).c_str(ctx)); +                log_assert(x == loc.x); +                log_assert(y == loc.y); +                log_assert(usedz.count(loc.z)); +                usedz.erase(loc.z); +            } + +            log_assert(usedz.empty()); +        } + +    log_break(); +} + +void archcheck_conn(const Context *ctx) +{ +#if 0 +    log_info("Checking connectivity data.\n"); + +    log_info("Checking all wires..\n"); +    for (WireId wire : ctx->getWires()) +    { +        ... +    } +#endif +} + +} // namespace + +NEXTPNR_NAMESPACE_BEGIN + +void Context::archcheck() const +{ +    log_info("Running architecture database integrity check.\n"); +    log_break(); + +    archcheck_names(this); +    archcheck_locs(this); +    archcheck_conn(this); +} + +NEXTPNR_NAMESPACE_END diff --git a/common/log.h b/common/log.h index c3607e39..35450311 100644 --- a/common/log.h +++ b/common/log.h @@ -69,17 +69,13 @@ NPNR_NORETURN void log_cmd_error(const char *format, ...) NPNR_ATTRIBUTE(format(  void log_break();  void log_flush(); -#ifndef NDEBUG  static inline void log_assert_worker(bool cond, const char *expr, const char *file, int line)  {      if (!cond)          log_error("Assert `%s' failed in %s:%d.\n", expr, file, line);  }  #define log_assert(_assert_expr_)                                                                                      \ -    YOSYS_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__) -#else -#define log_assert(_assert_expr_) -#endif +    NEXTPNR_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__)  #define log_abort() log_error("Abort in %s:%d.\n", __FILE__, __LINE__)  #define log_ping() log("-- %s:%d %s --\n", __FILE__, __LINE__, __PRETTY_FUNCTION__) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index 3861e5fe..2c50c9a1 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -51,6 +51,67 @@ void IdString::initialize_add(const BaseCtx *ctx, const char *s, int idx)      ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);  } +WireId Context::getNetinfoSourceWire(NetInfo *net_info) const +{ +    if (net_info->driver.cell == nullptr) +        return WireId(); + +    auto src_bel = net_info->driver.cell->bel; + +    if (src_bel == BelId()) +        return WireId(); + +    IdString driver_port = net_info->driver.port; + +    auto driver_port_it = net_info->driver.cell->pins.find(driver_port); +    if (driver_port_it != net_info->driver.cell->pins.end()) +        driver_port = driver_port_it->second; + +    return getBelPinWire(src_bel, portPinFromId(driver_port)); +} + +WireId Context::getNetinfoSinkWire(NetInfo *net_info, int user_idx) const +{ +    auto &user_info = net_info->users[user_idx]; +    auto dst_bel = user_info.cell->bel; + +    if (dst_bel == BelId()) +        return WireId(); + +    IdString user_port = user_info.port; + +    auto user_port_it = user_info.cell->pins.find(user_port); + +    if (user_port_it != user_info.cell->pins.end()) +        user_port = user_port_it->second; + +    return getBelPinWire(dst_bel, portPinFromId(user_port)); +} + +delay_t Context::getNetinfoRouteDelay(NetInfo *net_info, int user_idx) const +{ +    WireId src_wire = getNetinfoSourceWire(net_info); +    WireId cursor = getNetinfoSinkWire(net_info, user_idx); +    delay_t delay = 0; + +    while (cursor != WireId() && cursor != src_wire) { +        auto it = net_info->wires.find(cursor); +        if (it == net_info->wires.end()) +            break; +        PipId pip = it->second.pip; +        delay += getPipDelay(pip).maxDelay(); +        delay += getWireDelay(cursor).maxDelay(); +        cursor = getPipSrcWire(pip); +    } + +    if (cursor == src_wire) +        delay += getWireDelay(src_wire).maxDelay(); +    else +        delay += estimateDelay(src_wire, cursor); + +    return delay; +} +  static uint32_t xorshift32(uint32_t x)  {      x ^= x << 13; diff --git a/common/nextpnr.h b/common/nextpnr.h index 40fd3d13..021772fe 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -2,6 +2,7 @@   *  nextpnr -- Next Generation Place and Route   *   *  Copyright (C) 2018  Clifford Wolf <clifford@symbioticeda.com> + *  Copyright (C) 2018  Serge Bazanski <q3k@symbioticeda.com>   *   *  Permission to use, copy, modify, and/or distribute this software for any   *  purpose with or without fee is hereby granted, provided that the above @@ -19,7 +20,10 @@  #include <algorithm>  #include <assert.h> +#include <condition_variable>  #include <memory> +#include <mutex> +#include <pthread.h>  #include <stdexcept>  #include <stdint.h>  #include <string> @@ -164,6 +168,9 @@ struct Loc  {      int x = -1, y = -1, z = -1; +    Loc() {} +    Loc(int x, int y, int z) : x(x), y(y), z(z) {} +      bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); }      bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z == other.z); }  }; @@ -267,19 +274,87 @@ struct CellInfo : ArchCellInfo      std::unordered_map<IdString, IdString> pins;  }; -struct BaseCtx +struct DeterministicRNG  { -    // -------------------------------------------------------------- +    uint64_t rngstate; -    mutable std::unordered_map<std::string, int> *idstring_str_to_idx; -    mutable std::vector<const std::string *> *idstring_idx_to_str; +    DeterministicRNG() : rngstate(0x3141592653589793) {} -    IdString id(const std::string &s) const { return IdString(this, s); } +    uint64_t rng64() +    { +        // xorshift64star +        // https://arxiv.org/abs/1402.6246 -    IdString id(const char *s) const { return IdString(this, s); } +        uint64_t retval = rngstate * 0x2545F4914F6CDD1D; -    // -------------------------------------------------------------- +        rngstate ^= rngstate >> 12; +        rngstate ^= rngstate << 25; +        rngstate ^= rngstate >> 27; + +        return retval; +    } +    int rng() { return rng64() & 0x3fffffff; } + +    int rng(int n) +    { +        assert(n > 0); + +        // round up to power of 2 +        int m = n - 1; +        m |= (m >> 1); +        m |= (m >> 2); +        m |= (m >> 4); +        m |= (m >> 8); +        m |= (m >> 16); +        m += 1; + +        while (1) { +            int x = rng64() & (m - 1); +            if (x < n) +                return x; +        } +    } + +    void rngseed(uint64_t seed) +    { +        rngstate = seed ? seed : 0x3141592653589793; +        for (int i = 0; i < 5; i++) +            rng64(); +    } + +    template <typename T> void shuffle(std::vector<T> &a) +    { +        for (size_t i = 0; i != a.size(); i++) { +            size_t j = i + rng(a.size() - i); +            if (j > i) +                std::swap(a[i], a[j]); +        } +    } + +    template <typename T> void sorted_shuffle(std::vector<T> &a) +    { +        std::sort(a.begin(), a.end()); +        shuffle(a); +    } +}; + +struct BaseCtx +{ +    // Lock to perform mutating actions on the Context. +    std::mutex mutex; +    pthread_t mutex_owner; + +    // Lock to be taken by UI when wanting to access context - the yield() +    // method will lock/unlock it when its' released the main mutex to make +    // sure the UI is not starved. +    std::mutex ui_mutex; + +    // ID String database. +    mutable std::unordered_map<std::string, int> *idstring_str_to_idx; +    mutable std::vector<const std::string *> *idstring_idx_to_str; + +    // Placed nets and cells.      std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets;      std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells; @@ -297,13 +372,57 @@ struct BaseCtx          delete idstring_idx_to_str;      } +    // Must be called before performing any mutating changes on the Ctx/Arch. +    void lock(void) +    { +        mutex.lock(); +        mutex_owner = pthread_self(); +    } + +    void unlock(void) +    { +        NPNR_ASSERT(pthread_equal(pthread_self(), mutex_owner) != 0); +        mutex.unlock(); +    } + +    // Must be called by the UI before rendering data. This lock will be +    // prioritized when processing code calls yield(). +    void lock_ui(void) +    { +        ui_mutex.lock(); +        mutex.lock(); +    } + +    void unlock_ui(void) +    { +        mutex.unlock(); +        ui_mutex.unlock(); +    } + +    // Yield to UI by unlocking the main mutex, flashing the UI mutex and +    // relocking the main mutex. Call this when you're performing a +    // long-standing action while holding a lock to let the UI show +    // visualization updates. +    // Must be called with the main lock taken. +    void yield(void) +    { +        unlock(); +        ui_mutex.lock(); +        ui_mutex.unlock(); +        lock(); +    } + +    IdString id(const std::string &s) const { return IdString(this, s); } + +    IdString id(const char *s) const { return IdString(this, s); } +      Context *getCtx() { return reinterpret_cast<Context *>(this); }      const Context *getCtx() const { return reinterpret_cast<const Context *>(this); }      // -------------------------------------------------------------- -    bool allUiReload = false; +    bool allUiReload = true;      bool frameUiReload = false;      std::unordered_set<BelId> belUiReload;      std::unordered_set<WireId> wireUiReload; @@ -329,7 +448,7 @@ NEXTPNR_NAMESPACE_END  NEXTPNR_NAMESPACE_BEGIN -struct Context : Arch +struct Context : Arch, DeterministicRNG  {      bool verbose = false;      bool debug = false; @@ -341,74 +460,19 @@ struct Context : Arch      // -------------------------------------------------------------- +    WireId getNetinfoSourceWire(NetInfo *net_info) const; +    WireId getNetinfoSinkWire(NetInfo *net_info, int user_idx) const; +    delay_t getNetinfoRouteDelay(NetInfo *net_info, int user_idx) const; +      // provided by router1.cc      bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t &delay);      // -------------------------------------------------------------- -    uint64_t rngstate = 0x3141592653589793; - -    uint64_t rng64() -    { -        // xorshift64star -        // https://arxiv.org/abs/1402.6246 - -        uint64_t retval = rngstate * 0x2545F4914F6CDD1D; - -        rngstate ^= rngstate >> 12; -        rngstate ^= rngstate << 25; -        rngstate ^= rngstate >> 27; - -        return retval; -    } - -    int rng() { return rng64() & 0x3fffffff; } - -    int rng(int n) -    { -        assert(n > 0); - -        // round up to power of 2 -        int m = n - 1; -        m |= (m >> 1); -        m |= (m >> 2); -        m |= (m >> 4); -        m |= (m >> 8); -        m |= (m >> 16); -        m += 1; - -        while (1) { -            int x = rng64() & (m - 1); -            if (x < n) -                return x; -        } -    } - -    void rngseed(uint64_t seed) -    { -        rngstate = seed ? seed : 0x3141592653589793; -        for (int i = 0; i < 5; i++) -            rng64(); -    } - -    template <typename T> void shuffle(std::vector<T> &a) -    { -        for (size_t i = 0; i != a.size(); i++) { -            size_t j = i + rng(a.size() - i); -            if (j > i) -                std::swap(a[i], a[j]); -        } -    } - -    template <typename T> void sorted_shuffle(std::vector<T> &a) -    { -        std::sort(a.begin(), a.end()); -        shuffle(a); -    } -      uint32_t checksum() const;      void check() const; +    void archcheck() const;  };  NEXTPNR_NAMESPACE_END diff --git a/common/place_common.cc b/common/place_common.cc index 370eff23..95b7b2aa 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -36,7 +36,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type      if (driver_cell->bel == BelId())          return 0;      ctx->estimatePosition(driver_cell->bel, driver_x, driver_y, driver_gb); -    WireId drv_wire = ctx->getWireBelPin(driver_cell->bel, ctx->portPinFromId(net->driver.port)); +    WireId drv_wire = ctx->getBelPinWire(driver_cell->bel, ctx->portPinFromId(net->driver.port));      if (driver_gb)          return 0;      float worst_slack = 1000; @@ -48,7 +48,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type          if (load_cell->bel == BelId())              continue;          if (ctx->timing_driven && type == MetricType::COST) { -            WireId user_wire = ctx->getWireBelPin(load_cell->bel, ctx->portPinFromId(load.port)); +            WireId user_wire = ctx->getBelPinWire(load_cell->bel, ctx->portPinFromId(load.port));              delay_t raw_wl = ctx->estimateDelay(drv_wire, user_wire);              float slack = ctx->getDelayNS(load.budget) - ctx->getDelayNS(raw_wl);              if (slack < 0) diff --git a/common/placer1.cc b/common/placer1.cc index be20c072..461fc4e8 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -80,6 +80,7 @@ class SAPlacer          size_t placed_cells = 0;          // Initial constraints placer +        ctx->lock();          for (auto &cell_entry : ctx->cells) {              CellInfo *cell = cell_entry.second.get();              auto loc = cell->attrs.find(ctx->id("BEL")); @@ -118,16 +119,19 @@ class SAPlacer          }          std::sort(autoplaced.begin(), autoplaced.end(), [](CellInfo *a, CellInfo *b) { return a->name < b->name; });          ctx->shuffle(autoplaced); +        ctx->unlock();          // Place cells randomly initially          log_info("Creating initial placement for remaining %d cells.\n", int(autoplaced.size()));          for (auto cell : autoplaced) { +            ctx->lock();              place_initial(cell);              placed_cells++;              if ((placed_cells - constr_placed_cells) % 500 == 0)                  log_info("  initial placement placed %d/%d cells\n", int(placed_cells - constr_placed_cells),                           int(autoplaced.size())); +            ctx->unlock();          }          if ((placed_cells - constr_placed_cells) % 500 != 0)              log_info("  initial placement placed %d/%d cells\n", int(placed_cells - constr_placed_cells), @@ -136,6 +140,7 @@ class SAPlacer          log_info("Running simulated annealing placer.\n");          // Calculate metric after initial placement +        ctx->lock();          curr_metric = 0;          curr_tns = 0;          for (auto &net : ctx->nets) { @@ -143,6 +148,7 @@ class SAPlacer              metrics[net.first] = wl;              curr_metric += wl;          } +        ctx->unlock();          int n_no_progress = 0;          wirelen_t min_metric = curr_metric; @@ -185,6 +191,7 @@ class SAPlacer              if (temp <= 1e-3 && n_no_progress >= 5) {                  if (iter % 5 != 0)                      log_info("  at iteration #%d: temp = %f, cost = %f\n", iter, temp, double(curr_metric)); +                ctx->unlock();                  break;              } @@ -242,8 +249,12 @@ class SAPlacer                  metrics[net.first] = wl;                  curr_metric += wl;              } + +            // Let the UI show visualization updates. +            ctx->yield();          }          // Final post-pacement validitiy check +        ctx->lock();          for (auto bel : ctx->getBels()) {              IdString cell = ctx->getBoundBelCell(bel);              if (!ctx->isBelLocationValid(bel)) { @@ -261,6 +272,7 @@ class SAPlacer                  }              }          } +        ctx->unlock();          return true;      } @@ -446,7 +458,9 @@ bool placer1(Context *ctx)          placer.place();          log_info("Checksum: 0x%08x\n", ctx->checksum());  #ifndef NDEBUG +        ctx->lock();          ctx->check(); +        ctx->unlock();  #endif          return true;      } catch (log_execution_error_exception) { diff --git a/common/router1.cc b/common/router1.cc index 86fb1a44..431770da 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -22,6 +22,7 @@  #include "log.h"  #include "router1.h" +#include "timing.h"  namespace { @@ -251,7 +252,8 @@ struct Router          }      } -    Router(Context *ctx, RipupScoreboard &scores, IdString net_name, int user_idx = -1, bool reroute = false, bool ripup = false, delay_t ripup_penalty = 0) +    Router(Context *ctx, RipupScoreboard &scores, IdString net_name, int user_idx = -1, bool reroute = false, +           bool ripup = false, delay_t ripup_penalty = 0)              : ctx(ctx), scores(scores), net_name(net_name), ripup(ripup), ripup_penalty(ripup_penalty)      {          auto net_info = ctx->nets.at(net_name).get(); @@ -262,42 +264,26 @@ struct Router          if (ctx->debug)              log("  Source: %s.%s.\n", net_info->driver.cell->name.c_str(ctx), net_info->driver.port.c_str(ctx)); -        auto src_bel = net_info->driver.cell->bel; - -        if (src_bel == BelId()) -            log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx), -                      net_info->driver.cell->type.c_str(ctx)); - -        if (ctx->debug) -            log("    Source bel: %s\n", ctx->getBelName(src_bel).c_str(ctx)); - -        IdString driver_port = net_info->driver.port; - -        auto driver_port_it = net_info->driver.cell->pins.find(driver_port); -        if (driver_port_it != net_info->driver.cell->pins.end()) -            driver_port = driver_port_it->second; - -        auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port)); +        auto src_wire = ctx->getNetinfoSourceWire(net_info);          if (src_wire == WireId()) -            log_error("No wire found for port %s (pin %s) on source cell %s " -                      "(bel %s).\n", -                      net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx), -                      ctx->getBelName(src_bel).c_str(ctx)); +            log_error("No wire found for port %s on source cell %s.\n", net_info->driver.port.c_str(ctx), +                      net_info->driver.cell->name.c_str(ctx));          if (ctx->debug)              log("    Source wire: %s\n", ctx->getWireName(src_wire).c_str(ctx));          std::unordered_map<WireId, delay_t> src_wires; -        std::vector<PortRef> users_array; +        std::vector<int> users_array;          if (user_idx < 0) {              // route all users -            users_array = net_info->users; +            for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) +                users_array.push_back(user_idx);              ctx->shuffle(users_array);          } else {              // route only the selected user -            users_array.push_back(net_info->users[user_idx]); +            users_array.push_back(user_idx);          }          if (reroute) { @@ -311,32 +297,16 @@ struct Router                  ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);              src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay(); -            for (auto &user_it : net_info->users) { -                auto dst_bel = user_it.cell->bel; - -                if (dst_bel == BelId()) -                    log_error("Destination cell %s (%s) is not mapped to a bel.\n", user_it.cell->name.c_str(ctx), -                              user_it.cell->type.c_str(ctx)); - -                if (ctx->debug) -                    log("    Destination bel: %s\n", ctx->getBelName(dst_bel).c_str(ctx)); - -                IdString user_port = user_it.port; - -                auto user_port_it = user_it.cell->pins.find(user_port); - -                if (user_port_it != user_it.cell->pins.end()) -                    user_port = user_port_it->second; - -                auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port)); +            for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { +                auto dst_wire = ctx->getNetinfoSinkWire(net_info, user_idx);                  if (dst_wire == WireId()) -                    log_error("No wire found for port %s (pin %s) on destination " -                              "cell %s (bel %s).\n", -                              user_it.port.c_str(ctx), user_port.c_str(ctx), user_it.cell->name.c_str(ctx), -                              ctx->getBelName(dst_bel).c_str(ctx)); +                    log_error("No wire found for port %s on destination cell %s.\n", +                              net_info->users[user_idx].port.c_str(ctx), +                              net_info->users[user_idx].cell->name.c_str(ctx)); -                std::function<delay_t(WireId)> register_existing_path = [ctx, net_info, &src_wires, ®ister_existing_path](WireId wire) -> delay_t { +                std::function<delay_t(WireId)> register_existing_path = +                        [ctx, net_info, &src_wires, ®ister_existing_path](WireId wire) -> delay_t {                      auto it = src_wires.find(wire);                      if (it != src_wires.end())                          return it->second; @@ -345,7 +315,7 @@ struct Router                      delay_t delay = register_existing_path(ctx->getPipSrcWire(pip));                      delay += ctx->getPipDelay(pip).maxDelay();                      delay += ctx->getWireDelay(wire).maxDelay(); -                    delay -= 2*ctx->getDelayEpsilon(); +                    delay -= 2 * ctx->getDelayEpsilon();                      src_wires[wire] = delay;                      return delay; @@ -361,7 +331,7 @@ struct Router                  }                  register_existing_path(dst_wire); -        check_next_user_for_existing_path:; +            check_next_user_for_existing_path:;              }              std::vector<WireId> ripup_wires; @@ -371,39 +341,22 @@ struct Router              for (auto &it : ripup_wires) {                  if (ctx->debug) -                    log("  Unbind dangling wire for net %s: %s\n", -                        net_name.c_str(ctx), ctx->getWireName(it).c_str(ctx)); +                    log("  Unbind dangling wire for net %s: %s\n", net_name.c_str(ctx), +                        ctx->getWireName(it).c_str(ctx));                  ctx->unbindWire(it);              }          } -        for (auto &user_it : users_array) { -            if (ctx->debug) -                log("  Route to: %s.%s.\n", user_it.cell->name.c_str(ctx), user_it.port.c_str(ctx)); - -            auto dst_bel = user_it.cell->bel; - -            if (dst_bel == BelId()) -                log_error("Destination cell %s (%s) is not mapped to a bel.\n", user_it.cell->name.c_str(ctx), -                          user_it.cell->type.c_str(ctx)); - +        for (int user_idx : users_array) {              if (ctx->debug) -                log("    Destination bel: %s\n", ctx->getBelName(dst_bel).c_str(ctx)); +                log("  Route to: %s.%s.\n", net_info->users[user_idx].cell->name.c_str(ctx), +                    net_info->users[user_idx].port.c_str(ctx)); -            IdString user_port = user_it.port; - -            auto user_port_it = user_it.cell->pins.find(user_port); - -            if (user_port_it != user_it.cell->pins.end()) -                user_port = user_port_it->second; - -            auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port)); +            auto dst_wire = ctx->getNetinfoSinkWire(net_info, user_idx);              if (dst_wire == WireId()) -                log_error("No wire found for port %s (pin %s) on destination " -                          "cell %s (bel %s).\n", -                          user_it.port.c_str(ctx), user_port.c_str(ctx), user_it.cell->name.c_str(ctx), -                          ctx->getBelName(dst_bel).c_str(ctx)); +                log_error("No wire found for port %s on destination cell %s.\n", +                          net_info->users[user_idx].port.c_str(ctx), net_info->users[user_idx].cell->name.c_str(ctx));              if (ctx->debug) {                  log("    Destination wire: %s\n", ctx->getWireName(dst_wire).c_str(ctx)); @@ -499,35 +452,19 @@ struct RouteJob      };  }; - -void addFullNetRouteJob(Context *ctx, IdString net_name, -                     std::unordered_map<IdString, std::vector<bool>> &cache, -                     std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue) +void addFullNetRouteJob(Context *ctx, IdString net_name, std::unordered_map<IdString, std::vector<bool>> &cache, +                        std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue)  {      NetInfo *net_info = ctx->nets.at(net_name).get();      if (net_info->driver.cell == nullptr)          return; -    auto src_bel = net_info->driver.cell->bel; - -    if (src_bel == BelId()) -        log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx), -                  net_info->driver.cell->type.c_str(ctx)); - -    IdString driver_port = net_info->driver.port; - -    auto driver_port_it = net_info->driver.cell->pins.find(driver_port); -    if (driver_port_it != net_info->driver.cell->pins.end()) -        driver_port = driver_port_it->second; - -    auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port)); +    auto src_wire = ctx->getNetinfoSourceWire(net_info);      if (src_wire == WireId()) -        log_error("No wire found for port %s (pin %s) on source cell %s " -                  "(bel %s).\n", -                  net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx), -                  ctx->getBelName(src_bel).c_str(ctx)); +        log_error("No wire found for port %s on source cell %s.\n", net_info->driver.port.c_str(ctx), +                  net_info->driver.cell->name.c_str(ctx));      auto &net_cache = cache[net_name]; @@ -542,46 +479,30 @@ void addFullNetRouteJob(Context *ctx, IdString net_name,      bool got_slack = false; -    for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) -    { +    for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) {          if (net_cache[user_idx])              continue; -        auto &user_info = net_info->users[user_idx]; -        auto dst_bel = user_info.cell->bel; - -        if (dst_bel == BelId()) -            log_error("Destination cell %s (%s) is not mapped to a bel.\n", -                      user_info.cell->name.c_str(ctx), user_info.cell->type.c_str(ctx)); - -        IdString user_port = user_info.port; - -        auto user_port_it = user_info.cell->pins.find(user_port); - -        if (user_port_it != user_info.cell->pins.end()) -            user_port = user_port_it->second; - -        auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port)); +        auto dst_wire = ctx->getNetinfoSinkWire(net_info, user_idx);          if (dst_wire == WireId()) -            log_error("No wire found for port %s (pin %s) on destination " -                      "cell %s (bel %s).\n", -                      user_info.port.c_str(ctx), user_port.c_str(ctx), user_info.cell->name.c_str(ctx), -                      ctx->getBelName(dst_bel).c_str(ctx)); +            log_error("No wire found for port %s on destination cell %s.\n", net_info->users[user_idx].port.c_str(ctx), +                      net_info->users[user_idx].cell->name.c_str(ctx));          if (user_idx == 0) -            job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire); +            job.slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire);          else -            job.slack = std::min(job.slack, user_info.budget - ctx->estimateDelay(src_wire, dst_wire)); +            job.slack = std::min(job.slack, net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire));          WireId cursor = dst_wire;          while (src_wire != cursor) {              auto it = net_info->wires.find(cursor);              if (it == net_info->wires.end()) {                  if (!got_slack) -                    job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire); +                    job.slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire);                  else -                    job.slack = std::min(job.slack, user_info.budget - ctx->estimateDelay(src_wire, dst_wire)); +                    job.slack = std::min(job.slack, +                                         net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire));                  got_slack = true;                  break;              } @@ -596,8 +517,7 @@ void addFullNetRouteJob(Context *ctx, IdString net_name,          net_cache[user_idx] = true;  } -void addNetRouteJobs(Context *ctx, IdString net_name, -                     std::unordered_map<IdString, std::vector<bool>> &cache, +void addNetRouteJobs(Context *ctx, IdString net_name, std::unordered_map<IdString, std::vector<bool>> &cache,                       std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue)  {      NetInfo *net_info = ctx->nets.at(net_name).get(); @@ -605,73 +525,35 @@ void addNetRouteJobs(Context *ctx, IdString net_name,      if (net_info->driver.cell == nullptr)          return; -    auto src_bel = net_info->driver.cell->bel; - -    if (src_bel == BelId()) -        log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx), -                  net_info->driver.cell->type.c_str(ctx)); - -    IdString driver_port = net_info->driver.port; - -    auto driver_port_it = net_info->driver.cell->pins.find(driver_port); -    if (driver_port_it != net_info->driver.cell->pins.end()) -        driver_port = driver_port_it->second; - -    auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port)); +    auto src_wire = ctx->getNetinfoSourceWire(net_info);      if (src_wire == WireId()) -        log_error("No wire found for port %s (pin %s) on source cell %s " -                  "(bel %s).\n", -                  net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx), -                  ctx->getBelName(src_bel).c_str(ctx)); +        log_error("No wire found for port %s on source cell %s.\n", net_info->driver.port.c_str(ctx), +                  net_info->driver.cell->name.c_str(ctx));      auto &net_cache = cache[net_name];      if (net_cache.empty())          net_cache.resize(net_info->users.size()); -    for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) -    { +    for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) {          if (net_cache[user_idx])              continue; -        auto &user_info = net_info->users[user_idx]; -        auto dst_bel = user_info.cell->bel; - -        if (dst_bel == BelId()) -            log_error("Destination cell %s (%s) is not mapped to a bel.\n", -                      user_info.cell->name.c_str(ctx), user_info.cell->type.c_str(ctx)); - -        IdString user_port = user_info.port; - -        auto user_port_it = user_info.cell->pins.find(user_port); - -        if (user_port_it != user_info.cell->pins.end()) -            user_port = user_port_it->second; - -        auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port)); +        auto dst_wire = ctx->getNetinfoSinkWire(net_info, user_idx);          if (dst_wire == WireId()) -            log_error("No wire found for port %s (pin %s) on destination " -                      "cell %s (bel %s).\n", -                      user_info.port.c_str(ctx), user_port.c_str(ctx), user_info.cell->name.c_str(ctx), -                      ctx->getBelName(dst_bel).c_str(ctx)); +            log_error("No wire found for port %s on destination cell %s.\n", net_info->users[user_idx].port.c_str(ctx), +                      net_info->users[user_idx].cell->name.c_str(ctx));          WireId cursor = dst_wire;          while (src_wire != cursor) {              auto it = net_info->wires.find(cursor);              if (it == net_info->wires.end()) { -                if (ctx->debug) -                    log("Adding job [%s %d]: %s %s (%s) -> %s %s (%s)\n", -                        net_name.c_str(ctx), user_idx, -                        ctx->getBelName(src_bel).c_str(ctx), driver_port.c_str(ctx), -                        ctx->getWireName(src_wire).c_str(ctx), -                        ctx->getBelName(dst_bel).c_str(ctx), user_port.c_str(ctx), -                        ctx->getWireName(dst_wire).c_str(ctx));                  RouteJob job;                  job.net = net_name;                  job.user_idx = user_idx; -                job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire); +                job.slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire);                  job.randtag = ctx->rng();                  queue.push(job);                  net_cache[user_idx] = true; @@ -696,6 +578,7 @@ bool router1(Context *ctx)          log_break();          log_info("Routing..\n"); +        ctx->lock();          std::unordered_map<IdString, std::vector<bool>> jobCache;          std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> jobQueue; @@ -730,13 +613,43 @@ bool router1(Context *ctx)              std::unordered_set<IdString> normalRouteNets, ripupQueue; -            if (ctx->verbose || iterCnt == 1) -                log_info("routing queue contains %d jobs.\n", int(jobQueue.size())); +            if (iterCnt == 1) { +                if (ctx->verbose) +                    log_info("routing queue contains %d jobs.\n", int(jobQueue.size())); +            } else { +                static auto actual_delay = [](Context *ctx, WireId src, WireId dst) { +                    delay_t total_delay = 0; +                    WireId last = dst; +                    auto net_name = ctx->getBoundWireNet(src); +                    if (net_name != IdString()) { +                        auto net = ctx->nets.at(net_name).get(); +                        while (last != src) { +                            total_delay += ctx->getWireDelay(last).maxDelay(); +                            auto pip = net->wires.at(last).pip; +                            NPNR_ASSERT(ctx->getBoundPipNet(pip) == net_name); +                            total_delay += ctx->getPipDelay(pip).maxDelay(); +                            last = ctx->getPipSrcWire(pip); +                            if (ctx->getBoundWireNet(last) != net_name) { +                                log_warning("Wire %s bound to %s not %s!\n", ctx->getWireName(last).c_str(ctx), ctx->getBoundWireNet(last).c_str(ctx), net_name.c_str(ctx)); +                                break; +                            } +                            NPNR_ASSERT(ctx->getBoundWireNet(last) == net_name); +                        } +                        NPNR_ASSERT(last != WireId()); +                    } +                    if (last != src) +                        total_delay += ctx->estimateDelay(src, last); +                    else +                        total_delay += ctx->getWireDelay(last).maxDelay(); +                    return total_delay; +                }; +                update_budget(ctx, actual_delay); +            }              bool printNets = ctx->verbose && (jobQueue.size() < 10);              while (!jobQueue.empty()) { -                if(ctx->debug) +                if (ctx->debug)                      log("Next job slack: %f\n", double(jobQueue.top().slack));                  auto net_name = jobQueue.top().net; @@ -745,8 +658,8 @@ bool router1(Context *ctx)                  if (printNets) {                      if (user_idx < 0) -                        log_info("  routing all %d users of net %s\n", -                                 int(ctx->nets.at(net_name)->users.size()), net_name.c_str(ctx)); +                        log_info("  routing all %d users of net %s\n", int(ctx->nets.at(net_name)->users.size()), +                                 net_name.c_str(ctx));                      else                          log_info("  routing user %d of net %s\n", user_idx, net_name.c_str(ctx));                  } @@ -767,15 +680,19 @@ bool router1(Context *ctx)                      normalRouteNets.insert(net_name);                  } -                if ((ctx->verbose || iterCnt == 1) && !printNets && (jobCnt % 100 == 0)) +                if ((ctx->verbose || iterCnt == 1) && !printNets && (jobCnt % 100 == 0)) {                      log_info("  processed %d jobs. (%d routed, %d failed)\n", jobCnt, jobCnt - failedCnt, failedCnt); +                    ctx->yield(); +                }              }              NPNR_ASSERT(jobQueue.empty());              jobCache.clear(); -            if ((ctx->verbose || iterCnt == 1) && (jobCnt % 100 != 0)) +            if ((ctx->verbose || iterCnt == 1) && (jobCnt % 100 != 0)) {                  log_info("  processed %d jobs. (%d routed, %d failed)\n", jobCnt, jobCnt - failedCnt, failedCnt); +                ctx->yield(); +            }              if (ctx->verbose)                  log_info("  visited %d PIPs (%.2f%% revisits, %.2f%% overtime " @@ -829,8 +746,10 @@ bool router1(Context *ctx)                      ripCnt += router.rippedNets.size(); -                    if ((ctx->verbose || iterCnt == 1) && !printNets && (netCnt % 100 == 0)) +                    if ((ctx->verbose || iterCnt == 1) && !printNets && (netCnt % 100 == 0)) {                          log_info("  routed %d nets, ripped %d nets.\n", netCnt, ripCnt); +                        ctx->yield(); +                    }                  }                  if ((ctx->verbose || iterCnt == 1) && (netCnt % 100 != 0)) @@ -848,9 +767,8 @@ bool router1(Context *ctx)              }              if (!ctx->verbose) -                log_info("iteration %d: routed %d nets without ripup, routed %d nets with ripup.\n", -                         iterCnt, int(normalRouteNets.size()), int(ripupQueue.size())); - +                log_info("iteration %d: routed %d nets without ripup, routed %d nets with ripup.\n", iterCnt, +                         int(normalRouteNets.size()), int(ripupQueue.size()));              totalVisitCnt += visitCnt;              totalRevisitCnt += revisitCnt; @@ -858,6 +776,8 @@ bool router1(Context *ctx)              if (iterCnt == 8 || iterCnt == 16 || iterCnt == 32 || iterCnt == 64 || iterCnt == 128)                  ripup_penalty += ctx->getRipupDelayPenalty(); + +            ctx->yield();          }          log_info("routing complete after %d iterations.\n", iterCnt); @@ -867,6 +787,38 @@ bool router1(Context *ctx)                   totalVisitCnt, (100.0 * totalRevisitCnt) / totalVisitCnt,                   (100.0 * totalOvertimeRevisitCnt) / totalVisitCnt); +        { +            float tns = 0; +            int tns_net_count = 0; +            int tns_arc_count = 0; +            for (auto &net_it : ctx->nets) { +                bool got_negative_slack = false; +                NetInfo *net_info = ctx->nets.at(net_it.first).get(); +                for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { +                    delay_t arc_delay = ctx->getNetinfoRouteDelay(net_info, user_idx); +                    delay_t arc_budget = net_info->users[user_idx].budget; +                    delay_t arc_slack = arc_budget - arc_delay; +                    if (arc_slack < 0) { +                        if (!got_negative_slack) { +                            if (ctx->verbose) +                                log_info("net %s has negative slack arcs:\n", net_info->name.c_str(ctx)); +                            tns_net_count++; +                        } +                        if (ctx->verbose) +                            log_info("  arc %s -> %s has %f ns slack (delay %f, budget %f)\n", +                                     ctx->getWireName(ctx->getNetinfoSourceWire(net_info)).c_str(ctx), +                                     ctx->getWireName(ctx->getNetinfoSinkWire(net_info, user_idx)).c_str(ctx), +                                     ctx->getDelayNS(arc_slack), ctx->getDelayNS(arc_delay), +                                     ctx->getDelayNS(arc_budget)); +                        tns += ctx->getDelayNS(arc_slack); +                        tns_arc_count++; +                    } +                } +            } +            log_info("final tns with respect to arc budgets: %f ns (%d nets, %d arcs)\n", tns, tns_net_count, +                     tns_arc_count); +        } +          NPNR_ASSERT(jobQueue.empty());          jobCache.clear(); @@ -889,11 +841,13 @@ bool router1(Context *ctx)          log_info("Checksum: 0x%08x\n", ctx->checksum());  #ifndef NDEBUG          ctx->check(); +        ctx->unlock();  #endif          return true;      } catch (log_execution_error_exception) {  #ifndef NDEBUG          ctx->check(); +        ctx->unlock();  #endif          return false;      } diff --git a/common/timing.cc b/common/timing.cc index 9723550b..0e84dded 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -164,7 +164,7 @@ static delay_t follow_net_update(Context *ctx, NetInfo *net, int path_length, de      return net_budget;  } -void update_budget(Context *ctx) +void update_budget(Context *ctx, std::function<delay_t(Context*,WireId,WireId)> delay_fn)  {      delays_t delays;      updates_t updates; @@ -191,7 +191,7 @@ void update_budget(Context *ctx)              if (load_cell->bel == BelId())                  continue;              WireId user_wire = ctx->getWireBelPin(load_cell->bel, ctx->portPinFromId(load.port)); -            delay_t raw_wl = ctx->estimateDelay(drv_wire, user_wire); +            delay_t raw_wl = delay_fn(ctx, drv_wire, user_wire);              delays.emplace(&load_cell->ports.at(load.port), raw_wl);          }      } diff --git a/common/timing.h b/common/timing.h index b5574392..8c098963 100644 --- a/common/timing.h +++ b/common/timing.h @@ -27,7 +27,7 @@ NEXTPNR_NAMESPACE_BEGIN  // Assign "budget" values for all user ports in the design  void assign_budget(Context *ctx); -void update_budget(Context *ctx); +void update_budget(Context *ctx, std::function<delay_t(Context*,WireId,WireId)> delay_fn=&Context::estimateDelay);  NEXTPNR_NAMESPACE_END  | 
