Merge branch 'q3k/lock-2-electric-boogaloo' into 'master'
Basic locking and threading for Arch/GUI See merge request SymbioticEDA/nextpnr!10
This commit is contained in:
commit
9e6deed3b8
212
common/nextpnr.h
212
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,86 +274,11 @@ 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;
|
||||
|
||||
IdString id(const std::string &s) const { return IdString(this, s); }
|
||||
|
||||
IdString id(const char *s) const { return IdString(this, s); }
|
||||
|
||||
// --------------------------------------------------------------
|
||||
|
||||
std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets;
|
||||
std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells;
|
||||
|
||||
BaseCtx()
|
||||
{
|
||||
idstring_str_to_idx = new std::unordered_map<std::string, int>;
|
||||
idstring_idx_to_str = new std::vector<const std::string *>;
|
||||
IdString::initialize_add(this, "", 0);
|
||||
IdString::initialize_arch(this);
|
||||
}
|
||||
|
||||
~BaseCtx()
|
||||
{
|
||||
delete idstring_str_to_idx;
|
||||
delete idstring_idx_to_str;
|
||||
}
|
||||
|
||||
Context *getCtx() { return reinterpret_cast<Context *>(this); }
|
||||
|
||||
const Context *getCtx() const { return reinterpret_cast<const Context *>(this); }
|
||||
|
||||
// --------------------------------------------------------------
|
||||
|
||||
bool allUiReload = false;
|
||||
bool frameUiReload = false;
|
||||
std::unordered_set<BelId> belUiReload;
|
||||
std::unordered_set<WireId> wireUiReload;
|
||||
std::unordered_set<PipId> pipUiReload;
|
||||
std::unordered_set<GroupId> groupUiReload;
|
||||
|
||||
void refreshUi() { allUiReload = true; }
|
||||
|
||||
void refreshUiFrame() { frameUiReload = true; }
|
||||
|
||||
void refreshUiBel(BelId bel) { belUiReload.insert(bel); }
|
||||
|
||||
void refreshUiWire(WireId wire) { wireUiReload.insert(wire); }
|
||||
|
||||
void refreshUiPip(PipId pip) { pipUiReload.insert(pip); }
|
||||
|
||||
void refreshUiGroup(GroupId group) { groupUiReload.insert(group); }
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#include "arch.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct Context : Arch
|
||||
{
|
||||
bool verbose = false;
|
||||
bool debug = false;
|
||||
bool force = false;
|
||||
bool timing_driven = true;
|
||||
float target_freq = 12e6;
|
||||
|
||||
Context(ArchArgs args) : Arch(args) {}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// provided by router1.cc
|
||||
bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t &delay);
|
||||
|
||||
// --------------------------------------------------------------
|
||||
|
||||
uint64_t rngstate = 0x3141592653589793;
|
||||
DeterministicRNG() : rngstate(0x3141592653589793) {}
|
||||
|
||||
uint64_t rng64()
|
||||
{
|
||||
@ -408,6 +337,133 @@ struct Context : Arch
|
||||
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;
|
||||
|
||||
BaseCtx()
|
||||
{
|
||||
idstring_str_to_idx = new std::unordered_map<std::string, int>;
|
||||
idstring_idx_to_str = new std::vector<const std::string *>;
|
||||
IdString::initialize_add(this, "", 0);
|
||||
IdString::initialize_arch(this);
|
||||
}
|
||||
|
||||
~BaseCtx()
|
||||
{
|
||||
delete idstring_str_to_idx;
|
||||
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 = true;
|
||||
bool frameUiReload = false;
|
||||
std::unordered_set<BelId> belUiReload;
|
||||
std::unordered_set<WireId> wireUiReload;
|
||||
std::unordered_set<PipId> pipUiReload;
|
||||
std::unordered_set<GroupId> groupUiReload;
|
||||
|
||||
void refreshUi() { allUiReload = true; }
|
||||
|
||||
void refreshUiFrame() { frameUiReload = true; }
|
||||
|
||||
void refreshUiBel(BelId bel) { belUiReload.insert(bel); }
|
||||
|
||||
void refreshUiWire(WireId wire) { wireUiReload.insert(wire); }
|
||||
|
||||
void refreshUiPip(PipId pip) { pipUiReload.insert(pip); }
|
||||
|
||||
void refreshUiGroup(GroupId group) { groupUiReload.insert(group); }
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#include "arch.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct Context : Arch, DeterministicRNG
|
||||
{
|
||||
bool verbose = false;
|
||||
bool debug = false;
|
||||
bool force = false;
|
||||
bool timing_driven = true;
|
||||
float target_freq = 12e6;
|
||||
|
||||
Context(ArchArgs args) : Arch(args) {}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// provided by router1.cc
|
||||
bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t &delay);
|
||||
|
||||
// --------------------------------------------------------------
|
||||
|
||||
uint32_t checksum() const;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -241,26 +241,26 @@ void LineShader::draw(const LineShaderData &line, const QColor &color, float thi
|
||||
}
|
||||
|
||||
FPGAViewWidget::FPGAViewWidget(QWidget *parent)
|
||||
: QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr), selectedItemsChanged_(false)
|
||||
: QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr), paintTimer_(this),
|
||||
rendererData_(new FPGAViewWidget::RendererData), rendererArgs_(new FPGAViewWidget::RendererArgs)
|
||||
{
|
||||
backgroundColor_ = QColor("#000000");
|
||||
gridColor_ = QColor("#333");
|
||||
gFrameColor_ = QColor("#d0d0d0");
|
||||
gHiddenColor_ = QColor("#606060");
|
||||
gInactiveColor_ = QColor("#303030");
|
||||
gActiveColor_ = QColor("#f0f0f0");
|
||||
gSelectedColor_ = QColor("#ff6600");
|
||||
frameColor_ = QColor("#0066ba");
|
||||
highlightColors[0] = QColor("#6495ed");
|
||||
highlightColors[1] = QColor("#7fffd4");
|
||||
highlightColors[2] = QColor("#98fb98");
|
||||
highlightColors[3] = QColor("#ffd700");
|
||||
highlightColors[4] = QColor("#cd5c5c");
|
||||
highlightColors[5] = QColor("#fa8072");
|
||||
highlightColors[6] = QColor("#ff69b4");
|
||||
highlightColors[7] = QColor("#da70d6");
|
||||
for (int i = 0; i < 8; i++)
|
||||
highlightItemsChanged_[i] = false;
|
||||
colors_.background = QColor("#000000");
|
||||
colors_.grid = QColor("#333");
|
||||
colors_.frame = QColor("#d0d0d0");
|
||||
colors_.hidden = QColor("#606060");
|
||||
colors_.inactive = QColor("#303030");
|
||||
colors_.active = QColor("#f0f0f0");
|
||||
colors_.selected = QColor("#ff6600");
|
||||
colors_.highlight[0] = QColor("#6495ed");
|
||||
colors_.highlight[1] = QColor("#7fffd4");
|
||||
colors_.highlight[2] = QColor("#98fb98");
|
||||
colors_.highlight[3] = QColor("#ffd700");
|
||||
colors_.highlight[4] = QColor("#cd5c5c");
|
||||
colors_.highlight[5] = QColor("#fa8072");
|
||||
colors_.highlight[6] = QColor("#ff69b4");
|
||||
colors_.highlight[7] = QColor("#da70d6");
|
||||
|
||||
rendererArgs_->highlightedOrSelectedChanged = false;
|
||||
|
||||
auto fmt = format();
|
||||
fmt.setMajorVersion(3);
|
||||
@ -268,7 +268,6 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent)
|
||||
setFormat(fmt);
|
||||
|
||||
fmt = format();
|
||||
// printf("FPGAViewWidget running on OpenGL %d.%d\n", fmt.majorVersion(), fmt.minorVersion());
|
||||
if (fmt.majorVersion() < 3) {
|
||||
printf("Could not get OpenGL 3.0 context. Aborting.\n");
|
||||
log_abort();
|
||||
@ -276,6 +275,13 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent)
|
||||
if (fmt.minorVersion() < 1) {
|
||||
printf("Could not get OpenGL 3.1 context - trying anyway...\n ");
|
||||
}
|
||||
|
||||
connect(&paintTimer_, SIGNAL(timeout()), this, SLOT(update()));
|
||||
paintTimer_.start(std::chrono::duration<int, std::milli>(1000 / 20)); // paint GL 20 times per second
|
||||
|
||||
renderRunner_ = std::unique_ptr<PeriodicRunner>(new PeriodicRunner(this, [this] { renderLines(); }));
|
||||
renderRunner_->start();
|
||||
renderRunner_->startTimer(std::chrono::duration<int, std::milli>(1000 / 2)); // render line 2 times per second
|
||||
}
|
||||
|
||||
FPGAViewWidget::~FPGAViewWidget() {}
|
||||
@ -283,8 +289,7 @@ FPGAViewWidget::~FPGAViewWidget() {}
|
||||
void FPGAViewWidget::newContext(Context *ctx)
|
||||
{
|
||||
ctx_ = ctx;
|
||||
selectedItems_.clear();
|
||||
update();
|
||||
pokeRenderer();
|
||||
}
|
||||
|
||||
QSize FPGAViewWidget::minimumSizeHint() const { return QSize(640, 480); }
|
||||
@ -297,7 +302,8 @@ void FPGAViewWidget::initializeGL()
|
||||
log_error("Could not compile shader.\n");
|
||||
}
|
||||
initializeOpenGLFunctions();
|
||||
glClearColor(backgroundColor_.red() / 255, backgroundColor_.green() / 255, backgroundColor_.blue() / 255, 0.0);
|
||||
glClearColor(colors_.background.red() / 255, colors_.background.green() / 255, colors_.background.blue() / 255,
|
||||
0.0);
|
||||
}
|
||||
|
||||
void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal)
|
||||
@ -398,67 +404,143 @@ void FPGAViewWidget::paintGL()
|
||||
PolyLine(-100.0f, i, 100.0f, i).build(grid);
|
||||
PolyLine(i, -100.0f, i, 100.0f).build(grid);
|
||||
}
|
||||
lineShader_.draw(grid, gridColor_, thick1Px, matrix);
|
||||
lineShader_.draw(grid, colors_.grid, thick1Px, matrix);
|
||||
|
||||
LineShaderData shaders[4] = {LineShaderData(), LineShaderData(), LineShaderData(), LineShaderData()};
|
||||
rendererDataLock_.lock();
|
||||
lineShader_.draw(rendererData_->decals[0], colors_.frame, thick11Px, matrix);
|
||||
lineShader_.draw(rendererData_->decals[1], colors_.hidden, thick11Px, matrix);
|
||||
lineShader_.draw(rendererData_->decals[2], colors_.inactive, thick11Px, matrix);
|
||||
lineShader_.draw(rendererData_->decals[3], colors_.active, thick11Px, matrix);
|
||||
|
||||
if (ctx_) {
|
||||
// Draw Bels.
|
||||
for (int i = 0; i < 8; i++)
|
||||
lineShader_.draw(rendererData_->highlighted[i], colors_.highlight[i], thick11Px, matrix);
|
||||
|
||||
lineShader_.draw(rendererData_->selected, colors_.selected, thick11Px, matrix);
|
||||
rendererDataLock_.unlock();
|
||||
}
|
||||
|
||||
void FPGAViewWidget::pokeRenderer(void) { renderRunner_->poke(); }
|
||||
|
||||
void FPGAViewWidget::renderLines(void)
|
||||
{
|
||||
if (ctx_ == nullptr)
|
||||
return;
|
||||
|
||||
ctx_->lock_ui();
|
||||
|
||||
// For now, collapse any decal changes into change of all decals.
|
||||
// TODO(q3k): fix this
|
||||
bool decalsChanged = false;
|
||||
if (ctx_->allUiReload) {
|
||||
ctx_->allUiReload = false;
|
||||
decalsChanged = true;
|
||||
}
|
||||
if (ctx_->frameUiReload) {
|
||||
ctx_->frameUiReload = false;
|
||||
decalsChanged = true;
|
||||
}
|
||||
if (ctx_->belUiReload.size() > 0) {
|
||||
ctx_->belUiReload.clear();
|
||||
decalsChanged = true;
|
||||
}
|
||||
if (ctx_->wireUiReload.size() > 0) {
|
||||
ctx_->wireUiReload.clear();
|
||||
decalsChanged = true;
|
||||
}
|
||||
if (ctx_->pipUiReload.size() > 0) {
|
||||
ctx_->pipUiReload.clear();
|
||||
decalsChanged = true;
|
||||
}
|
||||
if (ctx_->groupUiReload.size() > 0) {
|
||||
ctx_->groupUiReload.clear();
|
||||
decalsChanged = true;
|
||||
}
|
||||
|
||||
// Local copy of decals, taken as fast as possible to not block the P&R.
|
||||
std::vector<DecalXY> belDecals;
|
||||
std::vector<DecalXY> wireDecals;
|
||||
std::vector<DecalXY> pipDecals;
|
||||
std::vector<DecalXY> groupDecals;
|
||||
if (decalsChanged) {
|
||||
for (auto bel : ctx_->getBels()) {
|
||||
drawDecal(shaders, ctx_->getBelDecal(bel));
|
||||
belDecals.push_back(ctx_->getBelDecal(bel));
|
||||
}
|
||||
for (auto wire : ctx_->getWires()) {
|
||||
wireDecals.push_back(ctx_->getWireDecal(wire));
|
||||
}
|
||||
for (auto pip : ctx_->getPips()) {
|
||||
pipDecals.push_back(ctx_->getPipDecal(pip));
|
||||
}
|
||||
for (auto group : ctx_->getGroups()) {
|
||||
groupDecals.push_back(ctx_->getGroupDecal(group));
|
||||
}
|
||||
}
|
||||
ctx_->unlock_ui();
|
||||
|
||||
rendererArgsLock_.lock();
|
||||
auto selectedItems = rendererArgs_->selectedItems;
|
||||
auto highlightedItems = rendererArgs_->highlightedItems;
|
||||
auto highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged;
|
||||
rendererArgs_->highlightedOrSelectedChanged = false;
|
||||
rendererArgsLock_.unlock();
|
||||
|
||||
if (decalsChanged) {
|
||||
auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);
|
||||
// Draw Bels.
|
||||
for (auto const &decal : belDecals) {
|
||||
drawDecal(data->decals, decal);
|
||||
}
|
||||
// Draw Wires.
|
||||
for (auto wire : ctx_->getWires()) {
|
||||
drawDecal(shaders, ctx_->getWireDecal(wire));
|
||||
for (auto const &decal : wireDecals) {
|
||||
drawDecal(data->decals, decal);
|
||||
}
|
||||
// Draw Pips.
|
||||
for (auto pip : ctx_->getPips()) {
|
||||
drawDecal(shaders, ctx_->getPipDecal(pip));
|
||||
for (auto const &decal : pipDecals) {
|
||||
drawDecal(data->decals, decal);
|
||||
}
|
||||
// Draw Groups.
|
||||
for (auto group : ctx_->getGroups()) {
|
||||
drawDecal(shaders, ctx_->getGroupDecal(group));
|
||||
for (auto const &decal : groupDecals) {
|
||||
drawDecal(data->decals, decal);
|
||||
}
|
||||
|
||||
if (selectedItemsChanged_) {
|
||||
selectedItemsChanged_ = false;
|
||||
selectedShader_.clear();
|
||||
for (auto decal : selectedItems_) {
|
||||
drawDecal(selectedShader_, decal);
|
||||
}
|
||||
// Swap over.
|
||||
rendererDataLock_.lock();
|
||||
rendererData_ = std::move(data);
|
||||
rendererDataLock_.unlock();
|
||||
}
|
||||
|
||||
rendererDataLock_.lock();
|
||||
if (decalsChanged || highlightedOrSelectedChanged) {
|
||||
rendererData_->selected.clear();
|
||||
for (auto &decal : selectedItems) {
|
||||
drawDecal(rendererData_->selected, decal);
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (highlightItemsChanged_[i]) {
|
||||
highlightItemsChanged_[i] = false;
|
||||
highlightShader_[i].clear();
|
||||
for (auto decal : highlightItems_[i]) {
|
||||
drawDecal(highlightShader_[i], decal);
|
||||
}
|
||||
rendererData_->highlighted[i].clear();
|
||||
for (auto &decal : highlightedItems[i]) {
|
||||
drawDecal(rendererData_->highlighted[i], decal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lineShader_.draw(shaders[0], gFrameColor_, thick11Px, matrix);
|
||||
lineShader_.draw(shaders[1], gHiddenColor_, thick11Px, matrix);
|
||||
lineShader_.draw(shaders[2], gInactiveColor_, thick11Px, matrix);
|
||||
lineShader_.draw(shaders[3], gActiveColor_, thick11Px, matrix);
|
||||
for (int i = 0; i < 8; i++)
|
||||
lineShader_.draw(highlightShader_[i], highlightColors[i], thick11Px, matrix);
|
||||
lineShader_.draw(selectedShader_, gSelectedColor_, thick11Px, matrix);
|
||||
rendererDataLock_.unlock();
|
||||
}
|
||||
|
||||
void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
|
||||
{
|
||||
selectedItems_ = decals;
|
||||
selectedItemsChanged_ = true;
|
||||
update();
|
||||
rendererArgsLock_.lock();
|
||||
rendererArgs_->selectedItems = decals;
|
||||
rendererArgs_->highlightedOrSelectedChanged = true;
|
||||
rendererArgsLock_.unlock();
|
||||
pokeRenderer();
|
||||
}
|
||||
|
||||
void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)
|
||||
{
|
||||
highlightItems_[group] = decals;
|
||||
highlightItemsChanged_[group] = true;
|
||||
update();
|
||||
rendererArgsLock_.lock();
|
||||
rendererArgs_->highlightedItems[group] = decals;
|
||||
rendererArgs_->highlightedOrSelectedChanged = true;
|
||||
rendererArgsLock_.unlock();
|
||||
pokeRenderer();
|
||||
}
|
||||
|
||||
void FPGAViewWidget::resizeGL(int width, int height) {}
|
||||
|
@ -21,12 +21,16 @@
|
||||
#define MAPGLWIDGET_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMutex>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QPainter>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
@ -206,17 +210,58 @@ class LineShader
|
||||
void draw(const LineShaderData &data, const QColor &color, float thickness, const QMatrix4x4 &projection);
|
||||
};
|
||||
|
||||
class PeriodicRunner : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QMutex mutex_;
|
||||
QWaitCondition condition_;
|
||||
bool abort_;
|
||||
std::function<void()> target_;
|
||||
QTimer timer_;
|
||||
|
||||
public:
|
||||
explicit PeriodicRunner(QObject *parent, std::function<void()> target)
|
||||
: QThread(parent), abort_(false), target_(target), timer_(this)
|
||||
{
|
||||
connect(&timer_, &QTimer::timeout, this, &PeriodicRunner::poke);
|
||||
}
|
||||
|
||||
void run(void) override
|
||||
{
|
||||
for (;;) {
|
||||
mutex_.lock();
|
||||
condition_.wait(&mutex_);
|
||||
|
||||
if (abort_) {
|
||||
mutex_.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
target_();
|
||||
|
||||
mutex_.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void startTimer(std::chrono::milliseconds value) { timer_.start(value); }
|
||||
|
||||
~PeriodicRunner()
|
||||
{
|
||||
mutex_.lock();
|
||||
abort_ = true;
|
||||
condition_.wakeOne();
|
||||
mutex_.unlock();
|
||||
|
||||
wait();
|
||||
}
|
||||
|
||||
void poke(void) { condition_.wakeOne(); }
|
||||
};
|
||||
|
||||
class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor backgroundColor MEMBER backgroundColor_ DESIGNABLE true)
|
||||
Q_PROPERTY(QColor gridColor MEMBER gridColor_ DESIGNABLE true)
|
||||
Q_PROPERTY(QColor gFrameColor MEMBER gFrameColor_ DESIGNABLE true)
|
||||
Q_PROPERTY(QColor gHiddenColor MEMBER gHiddenColor_ DESIGNABLE true)
|
||||
Q_PROPERTY(QColor gInactiveColor MEMBER gInactiveColor_ DESIGNABLE true)
|
||||
Q_PROPERTY(QColor gActiveColor MEMBER gActiveColor_ DESIGNABLE true)
|
||||
Q_PROPERTY(QColor gSelectedColor MEMBER gSelectedColor_ DESIGNABLE true)
|
||||
Q_PROPERTY(QColor frameColor MEMBER frameColor_ DESIGNABLE true)
|
||||
|
||||
public:
|
||||
FPGAViewWidget(QWidget *parent = 0);
|
||||
@ -246,8 +291,11 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
void newContext(Context *ctx);
|
||||
void onSelectedArchItem(std::vector<DecalXY> decals);
|
||||
void onHighlightGroupChanged(std::vector<DecalXY> decals, int group);
|
||||
void pokeRenderer(void);
|
||||
|
||||
private:
|
||||
void renderLines(void);
|
||||
|
||||
QPoint lastPos_;
|
||||
LineShader lineShader_;
|
||||
QMatrix4x4 viewMove_;
|
||||
@ -262,24 +310,40 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
const float zoomLvl2_ = 50.0f;
|
||||
|
||||
Context *ctx_;
|
||||
QTimer paintTimer_;
|
||||
|
||||
QColor backgroundColor_;
|
||||
QColor gridColor_;
|
||||
QColor gFrameColor_;
|
||||
QColor gHiddenColor_;
|
||||
QColor gInactiveColor_;
|
||||
QColor gActiveColor_;
|
||||
QColor gSelectedColor_;
|
||||
QColor frameColor_;
|
||||
std::unique_ptr<PeriodicRunner> renderRunner_;
|
||||
|
||||
LineShaderData selectedShader_;
|
||||
std::vector<DecalXY> selectedItems_;
|
||||
bool selectedItemsChanged_;
|
||||
struct
|
||||
{
|
||||
QColor background;
|
||||
QColor grid;
|
||||
QColor frame;
|
||||
QColor hidden;
|
||||
QColor inactive;
|
||||
QColor active;
|
||||
QColor selected;
|
||||
QColor highlight[8];
|
||||
} colors_;
|
||||
|
||||
LineShaderData highlightShader_[8];
|
||||
std::vector<DecalXY> highlightItems_[8];
|
||||
bool highlightItemsChanged_[8];
|
||||
QColor highlightColors[8];
|
||||
struct RendererData
|
||||
{
|
||||
LineShaderData decals[4];
|
||||
LineShaderData selected;
|
||||
LineShaderData highlighted[8];
|
||||
};
|
||||
|
||||
struct RendererArgs
|
||||
{
|
||||
std::vector<DecalXY> selectedItems;
|
||||
std::vector<DecalXY> highlightedItems[8];
|
||||
bool highlightedOrSelectedChanged;
|
||||
};
|
||||
|
||||
std::unique_ptr<RendererData> rendererData_;
|
||||
QMutex rendererDataLock_;
|
||||
std::unique_ptr<RendererArgs> rendererArgs_;
|
||||
QMutex rendererArgsLock_;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -19,13 +19,13 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "cells.h"
|
||||
#include "gfx.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "placer1.h"
|
||||
#include "router1.h"
|
||||
#include "util.h"
|
||||
#include "cells.h"
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@ -520,9 +520,9 @@ DecalXY Arch::getWireDecal(WireId wire) const
|
||||
DecalXY Arch::getPipDecal(PipId pip) const
|
||||
{
|
||||
DecalXY decalxy;
|
||||
// decalxy.decal.type = DecalId::TYPE_PIP;
|
||||
// decalxy.decal.index = pip.index;
|
||||
// decalxy.decal.active = pip_to_net.at(pip.index) != IdString();
|
||||
decalxy.decal.type = DecalId::TYPE_PIP;
|
||||
decalxy.decal.index = pip.index;
|
||||
decalxy.decal.active = pip_to_net.at(pip.index) != IdString();
|
||||
return decalxy;
|
||||
};
|
||||
|
||||
|
@ -390,6 +390,7 @@ struct Arch : BaseCtx
|
||||
bel_to_cell[bel.index] = cell;
|
||||
cells[cell]->bel = bel;
|
||||
cells[cell]->belStrength = strength;
|
||||
refreshUiBel(bel);
|
||||
}
|
||||
|
||||
void unbindBel(BelId bel)
|
||||
@ -399,6 +400,7 @@ struct Arch : BaseCtx
|
||||
cells[bel_to_cell[bel.index]]->bel = BelId();
|
||||
cells[bel_to_cell[bel.index]]->belStrength = STRENGTH_NONE;
|
||||
bel_to_cell[bel.index] = IdString();
|
||||
refreshUiBel(bel);
|
||||
}
|
||||
|
||||
bool checkBelAvail(BelId bel) const
|
||||
@ -509,6 +511,7 @@ struct Arch : BaseCtx
|
||||
wire_to_net[wire.index] = net;
|
||||
nets[net]->wires[wire].pip = PipId();
|
||||
nets[net]->wires[wire].strength = strength;
|
||||
refreshUiWire(wire);
|
||||
}
|
||||
|
||||
void unbindWire(WireId wire)
|
||||
@ -528,6 +531,7 @@ struct Arch : BaseCtx
|
||||
|
||||
net_wires.erase(it);
|
||||
wire_to_net[wire.index] = IdString();
|
||||
refreshUiWire(wire);
|
||||
}
|
||||
|
||||
bool checkWireAvail(WireId wire) const
|
||||
@ -581,6 +585,8 @@ struct Arch : BaseCtx
|
||||
wire_to_net[dst.index] = net;
|
||||
nets[net]->wires[dst].pip = pip;
|
||||
nets[net]->wires[dst].strength = strength;
|
||||
refreshUiPip(pip);
|
||||
refreshUiWire(dst);
|
||||
}
|
||||
|
||||
void unbindPip(PipId pip)
|
||||
@ -597,6 +603,8 @@ struct Arch : BaseCtx
|
||||
|
||||
pip_to_net[pip.index] = IdString();
|
||||
switches_locked[chip_info->pip_data[pip.index].switch_index] = IdString();
|
||||
refreshUiPip(pip);
|
||||
refreshUiWire(dst);
|
||||
}
|
||||
|
||||
bool checkPipAvail(PipId pip) const
|
||||
|
Loading…
Reference in New Issue
Block a user