aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorClifford Wolf <cliffordvienna@gmail.com>2018-07-21 19:45:24 +0000
committerClifford Wolf <cliffordvienna@gmail.com>2018-07-21 19:45:24 +0000
commit9e6deed3b839a2df92644f71bc6cbc1be4ea0480 (patch)
treee6b16c9c91f0c1521d609e238e94356e42cbdb4e /common
parent30e2f0e1e8cfdb24abe6c3a8013691497c706975 (diff)
parent6588aafdb8038625e8ce0cc265a2851cbb16b1c9 (diff)
downloadnextpnr-9e6deed3b839a2df92644f71bc6cbc1be4ea0480.tar.gz
nextpnr-9e6deed3b839a2df92644f71bc6cbc1be4ea0480.tar.bz2
nextpnr-9e6deed3b839a2df92644f71bc6cbc1be4ea0480.zip
Merge branch 'q3k/lock-2-electric-boogaloo' into 'master'
Basic locking and threading for Arch/GUI See merge request SymbioticEDA/nextpnr!10
Diffstat (limited to 'common')
-rw-r--r--common/nextpnr.h194
-rw-r--r--common/placer1.cc15
-rw-r--r--common/router1.cc17
3 files changed, 154 insertions, 72 deletions
diff --git a/common/nextpnr.h b/common/nextpnr.h
index c8915c19..d89eb15b 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>
@@ -270,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;
@@ -300,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;
@@ -332,7 +448,7 @@ NEXTPNR_NAMESPACE_END
NEXTPNR_NAMESPACE_BEGIN
-struct Context : Arch
+struct Context : Arch, DeterministicRNG
{
bool verbose = false;
bool debug = false;
@@ -349,66 +465,6 @@ struct Context : Arch
// --------------------------------------------------------------
- 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;
diff --git a/common/placer1.cc b/common/placer1.cc
index 74a11040..025c7c15 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;
double avg_metric = curr_metric;
@@ -169,6 +175,7 @@ class SAPlacer
try_swap_position(cell, try_bel);
}
}
+
// Heuristic to improve placement on the 8k
if (improved)
n_no_progress = 0;
@@ -178,6 +185,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;
}
@@ -232,8 +240,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)) {
@@ -251,6 +263,7 @@ class SAPlacer
}
}
}
+ ctx->unlock();
return true;
}
@@ -436,7 +449,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 1ea50448..a171374f 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -695,6 +695,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;
@@ -766,15 +767,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 "
@@ -828,8 +833,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))
@@ -857,6 +864,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);
@@ -888,11 +897,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;
}