aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bba/README.md46
-rw-r--r--bba/main.cc89
-rw-r--r--common/nextpnr.h28
-rw-r--r--common/router1.cc4
-rw-r--r--ecp5/arch.h6
-rw-r--r--ecp5/bitstream.cc2
-rw-r--r--generic/arch.cc13
-rw-r--r--generic/arch.h12
-rw-r--r--gui/base.qrc4
-rw-r--r--gui/basewindow.cc29
-rw-r--r--gui/basewindow.h4
-rw-r--r--gui/designwidget.cc119
-rw-r--r--gui/designwidget.h3
-rw-r--r--gui/fpgaviewwidget.cc538
-rw-r--r--gui/fpgaviewwidget.h236
-rw-r--r--gui/ice40/mainwindow.cc2
-rw-r--r--gui/lineshader.cc236
-rw-r--r--gui/lineshader.h209
-rw-r--r--gui/resources/shape_handles.pngbin0 -> 538 bytes
-rw-r--r--gui/resources/shape_square.pngbin0 -> 353 bytes
-rw-r--r--gui/resources/zoom_in.pngbin0 -> 725 bytes
-rw-r--r--gui/resources/zoom_out.pngbin0 -> 708 bytes
-rw-r--r--ice40/arch.cc267
-rw-r--r--ice40/arch.h4
-rw-r--r--ice40/chipdb.py3
-rw-r--r--ice40/gfx.cc15
-rw-r--r--ice40/main.cc4
27 files changed, 1087 insertions, 786 deletions
diff --git a/bba/README.md b/bba/README.md
index 7e64b582..166ff7ff 100644
--- a/bba/README.md
+++ b/bba/README.md
@@ -12,59 +12,63 @@ independent.
Valid commands for the input are as follows.
-pre <string>
-------------
+pre \<string\>
+--------------
When a C file is generated as output, all the "pre" strings will be included
before the binary blob.
-post <string>
--------------
+post \<string\>
+---------------
When a C file is generated as output, all the "post" strings will be included
after the binary blob.
-push <name>
------------
+push \<name\>
+-------------
All following commands up until the matching "pop" will be writen to stream
-<name>. Everything written to the same stream will end up in a continous
-region of the output.
+\<name\>. Everything written to the same stream will end up in a continous
+region of the output. The statements `pop`, `label`, `ref`, `u8`, `u16`,
+`u32`, and `str` are only valid within such a block. The name used in the
+first push statement also determines the name of the variable in the generated
+C output (when C is selected as output file format).
pop
---
End of a push..pop block.
-label <name> [<comment>]
-------------------------
+label \<name\> \[\<comment\>\]
+------------------------------
Add a label for the current position.
-ref <name> [<comment>]
-----------------------
+ref \<name\> \[\<comment\>\]
+----------------------------
Add a 32-bit reference to the specified label. The reference will be a byte
offset relative to the memory location of the reference itself.
-u8 <value> [<comment>]
-----------------------
+u8 \<value\> \[\<comment\>\]
+----------------------------
Add a 8-bit value to the binary blob.
-u16 <value> [<comment>]
------------------------
+u16 \<value\> \[\<comment\>\]
+-----------------------------
Add a 16-bit value to the binary blob. Note that the input must be structured
in a way that ensures that all u16 are aligned to 2-byte addresses.
-u32 <value> [<comment>]
-----------------------
+u32 \<value\> \[\<comment\>\]
+-----------------------------
Add a 32-bit value to the binary blob. Note that the input must be structured
in a way that ensures that all u32 are aligned to 4-byte addresses.
-str <string>
-------------
+str "\<string\>" \[\<comment\>\]
+--------------------------------
-Add a reference to a zero-terminated copy of the specified string.
+Add a reference to a zero-terminated copy of that string. Any character may be
+used to quote the string, but the most common choices are `"` and `|`.
diff --git a/bba/main.cc b/bba/main.cc
index 9f7da8d2..263cf39e 100644
--- a/bba/main.cc
+++ b/bba/main.cc
@@ -52,6 +52,7 @@ std::map<std::string, int> streamIndex;
std::vector<int> streamStack;
std::vector<int> labels;
+std::vector<std::string> labelNames;
std::map<std::string, int> labelIndex;
std::vector<std::string> preText, postText;
@@ -67,6 +68,7 @@ const char *skipWhitespace(const char *p)
int main(int argc, char **argv)
{
+ bool debug = false;
bool verbose = false;
bool bigEndian = false;
bool writeC = false;
@@ -76,6 +78,7 @@ int main(int argc, char **argv)
po::positional_options_description pos;
po::options_description options("Allowed options");
options.add_options()("v", "verbose output");
+ options.add_options()("d", "debug output");
options.add_options()("b", "big endian");
options.add_options()("c", "write c strings");
options.add_options()("files", po::value<std::vector<std::string>>(), "file parameters");
@@ -94,6 +97,8 @@ int main(int argc, char **argv)
}
if (vm.count("v"))
verbose = true;
+ if (vm.count("d"))
+ debug = true;
if (vm.count("b"))
bigEndian = true;
if (vm.count("c"))
@@ -152,11 +157,13 @@ int main(int argc, char **argv)
Stream &s = streams.at(streamStack.back());
if (labelIndex.count(label) == 0) {
labelIndex[label] = labels.size();
+ if (debug)
+ labelNames.push_back(label);
labels.push_back(-1);
}
s.tokenTypes.push_back(cmd == "label" ? TOK_LABEL : TOK_REF);
s.tokenValues.push_back(labelIndex.at(label));
- if (verbose)
+ if (debug)
s.tokenComments.push_back(comment);
continue;
}
@@ -167,28 +174,41 @@ int main(int argc, char **argv)
Stream &s = streams.at(streamStack.back());
s.tokenTypes.push_back(cmd == "u8" ? TOK_U8 : cmd == "u16" ? TOK_U16 : TOK_U32);
s.tokenValues.push_back(atoll(value));
- if (verbose)
+ if (debug)
s.tokenComments.push_back(comment);
continue;
}
if (cmd == "str") {
const char *value = skipWhitespace(strtok(nullptr, "\r\n"));
+ char terminator[2] = {*value, 0};
+ assert(terminator[0] != 0);
+ value = strtok((char *)value + 1, terminator);
+ const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
std::string label = std::string("str:") + value;
Stream &s = streams.at(streamStack.back());
if (labelIndex.count(label) == 0) {
labelIndex[label] = labels.size();
+ if (debug)
+ labelNames.push_back(label);
labels.push_back(-1);
}
s.tokenTypes.push_back(TOK_REF);
s.tokenValues.push_back(labelIndex.at(label));
- if (verbose)
- s.tokenComments.push_back(value);
+ if (debug)
+ s.tokenComments.push_back(comment);
stringStream.tokenTypes.push_back(TOK_LABEL);
stringStream.tokenValues.push_back(labelIndex.at(label));
+ stringStream.tokenComments.push_back("");
while (1) {
stringStream.tokenTypes.push_back(TOK_U8);
stringStream.tokenValues.push_back(*value);
+ if (debug) {
+ char char_comment[4] = {'\'', *value, '\'', 0};
+ if (*value < 32 || *value >= 127)
+ char_comment[0] = 0;
+ stringStream.tokenComments.push_back(char_comment);
+ }
if (*value == 0)
break;
value++;
@@ -208,8 +228,10 @@ int main(int argc, char **argv)
assert(!streams.empty());
assert(streamStack.empty());
streams.push_back(Stream());
+ streams.back().name = "strings";
streams.back().tokenTypes.swap(stringStream.tokenTypes);
streams.back().tokenValues.swap(stringStream.tokenValues);
+ streams.back().tokenComments.swap(stringStream.tokenComments);
int cursor = 0;
for (auto &s : streams) {
@@ -247,6 +269,9 @@ int main(int argc, char **argv)
cursor = 0;
for (auto &s : streams) {
+ if (debug)
+ printf("-- %s --\n", s.name.c_str());
+
for (int i = 0; i < int(s.tokenTypes.size()); i++) {
uint32_t value = s.tokenValues[i];
int numBytes = 0;
@@ -307,6 +332,51 @@ int main(int argc, char **argv)
}
cursor += numBytes;
}
+
+ if (debug) {
+ printf("%08x ", cursor - numBytes);
+ for (int k = cursor - numBytes; k < cursor; k++)
+ printf("%02x ", data[k]);
+ for (int k = numBytes; k < 4; k++)
+ printf(" ");
+
+ unsigned long long v = s.tokenValues[i];
+
+ switch (s.tokenTypes[i]) {
+ case TOK_LABEL:
+ if (s.tokenComments[i].empty())
+ printf("label %s\n", labelNames[v].c_str());
+ else
+ printf("label %-24s %s\n", labelNames[v].c_str(), s.tokenComments[i].c_str());
+ break;
+ case TOK_REF:
+ if (s.tokenComments[i].empty())
+ printf("ref %s\n", labelNames[v].c_str());
+ else
+ printf("ref %-26s %s\n", labelNames[v].c_str(), s.tokenComments[i].c_str());
+ break;
+ case TOK_U8:
+ if (s.tokenComments[i].empty())
+ printf("u8 %llu\n", v);
+ else
+ printf("u8 %-27llu %s\n", v, s.tokenComments[i].c_str());
+ break;
+ case TOK_U16:
+ if (s.tokenComments[i].empty())
+ printf("u16 %-26llu\n", v);
+ else
+ printf("u16 %-26llu %s\n", v, s.tokenComments[i].c_str());
+ break;
+ case TOK_U32:
+ if (s.tokenComments[i].empty())
+ printf("u32 %-26llu\n", v);
+ else
+ printf("u32 %-26llu %s\n", v, s.tokenComments[i].c_str());
+ break;
+ default:
+ assert(0);
+ }
+ }
}
}
@@ -319,7 +389,8 @@ int main(int argc, char **argv)
fprintf(fileOut, "const char %s[%d] =\n\"", streams[0].name.c_str(), int(data.size()) + 1);
cursor = 1;
- for (auto d : data) {
+ for (int i = 0; i < int(data.size()); i++) {
+ auto d = data[i];
if (cursor > 70) {
fputc('\"', fileOut);
fputc('\n', fileOut);
@@ -329,9 +400,11 @@ int main(int argc, char **argv)
fputc('\"', fileOut);
cursor = 1;
}
- if (d < 32 || d >= 128) {
- fprintf(fileOut, "\\%03o", int(d));
- cursor += 4;
+ if (d < 32 || d >= 127) {
+ if (i + 1 < int(data.size()) && (data[i + 1] < '0' || '9' < data[i + 1]))
+ cursor += fprintf(fileOut, "\\%o", int(d));
+ else
+ cursor += fprintf(fileOut, "\\%03o", int(d));
} else if (d == '\"' || d == '\'' || d == '\\') {
fputc('\\', fileOut);
fputc(d, fileOut);
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 4d9cf5f7..c4c05ae0 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -145,21 +145,25 @@ struct GraphicElement
{
enum type_t
{
- G_NONE,
- G_LINE,
- G_ARROW,
- G_BOX,
- G_CIRCLE,
- G_LABEL
- } type = G_NONE;
+ TYPE_NONE,
+ TYPE_LINE,
+ TYPE_ARROW,
+ TYPE_BOX,
+ TYPE_CIRCLE,
+ TYPE_LABEL,
+
+ TYPE_MAX
+ } type = TYPE_NONE;
enum style_t
{
- G_FRAME,
- G_HIDDEN,
- G_INACTIVE,
- G_ACTIVE,
- } style = G_FRAME;
+ STYLE_FRAME, // Static "frame". Contrast between STYLE_INACTIVE and STYLE_ACTIVE
+ STYLE_HIDDEN, // Only display when object is selected or highlighted
+ STYLE_INACTIVE, // Render using low-contrast color
+ STYLE_ACTIVE, // Render using high-contast color
+
+ STYLE_MAX
+ } style = STYLE_FRAME;
float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
std::string text;
diff --git a/common/router1.cc b/common/router1.cc
index dae8d8cb..2ae54245 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -813,15 +813,15 @@ bool router1(Context *ctx)
log_info("Checksum: 0x%08x\n", ctx->checksum());
#ifndef NDEBUG
ctx->check();
- ctx->unlock();
#endif
compute_fmax(ctx, true /* print_fmax */, true /* print_path */);
+ ctx->unlock();
return true;
} catch (log_execution_error_exception) {
#ifndef NDEBUG
ctx->check();
- ctx->unlock();
#endif
+ ctx->unlock();
return false;
}
}
diff --git a/ecp5/arch.h b/ecp5/arch.h
index b5f3d817..b6aac9cf 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -538,6 +538,8 @@ struct Arch : BaseCtx
return id(name.str());
}
+ IdString getWireType(WireId wire) const { return IdString(); }
+
uint32_t getWireChecksum(WireId wire) const { return wire.index; }
void bindWire(WireId wire, IdString net, PlaceStrength strength)
@@ -616,6 +618,8 @@ struct Arch : BaseCtx
PipId getPipByName(IdString name) const;
IdString getPipName(PipId pip) const;
+ IdString getPipType(PipId pip) const { return IdString(); }
+
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
void bindPip(PipId pip, IdString net, PlaceStrength strength)
@@ -748,7 +752,7 @@ struct Arch : BaseCtx
return chip_info->tiletype_names[locInfo(pip)->pip_data[pip.index].tile_type].get();
}
- int8_t getPipType(PipId pip) const { return locInfo(pip)->pip_data[pip.index].pip_type; }
+ int8_t getPipClass(PipId pip) const { return locInfo(pip)->pip_data[pip.index].pip_type; }
BelId getPackagePinBel(const std::string &pin) const;
std::string getBelPackagePin(BelId bel) const;
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index f87b7038..df9b12d5 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -174,7 +174,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
// Add all set, configurable pips to the config
for (auto pip : ctx->getPips()) {
if (ctx->getBoundPipNet(pip) != IdString()) {
- if (ctx->getPipType(pip) == 0) { // ignore fixed pips
+ if (ctx->getPipClass(pip) == 0) { // ignore fixed pips
std::string tile = empty_chip.get_tile_by_position_and_type(pip.location.y, pip.location.x,
ctx->getPipTiletype(pip));
std::string source = get_trellis_wirename(ctx, pip.location, ctx->getPipSrcWire(pip));
diff --git a/generic/arch.cc b/generic/arch.cc
index d7401356..5c9864ab 100644
--- a/generic/arch.cc
+++ b/generic/arch.cc
@@ -24,22 +24,24 @@
NEXTPNR_NAMESPACE_BEGIN
-void Arch::addWire(IdString name, int x, int y)
+void Arch::addWire(IdString name, IdString type, int x, int y)
{
NPNR_ASSERT(wires.count(name) == 0);
WireInfo &wi = wires[name];
wi.name = name;
+ wi.type = type;
wi.x = x;
wi.y = y;
wire_ids.push_back(name);
}
-void Arch::addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay)
+void Arch::addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay)
{
NPNR_ASSERT(pips.count(name) == 0);
PipInfo &pi = pips[name];
pi.name = name;
+ pi.type = type;
pi.srcWire = srcWire;
pi.dstWire = dstWire;
pi.delay = delay;
@@ -49,11 +51,12 @@ void Arch::addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo d
pip_ids.push_back(name);
}
-void Arch::addAlias(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay)
+void Arch::addAlias(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay)
{
NPNR_ASSERT(pips.count(name) == 0);
PipInfo &pi = pips[name];
pi.name = name;
+ pi.type = type;
pi.srcWire = srcWire;
pi.dstWire = dstWire;
pi.delay = delay;
@@ -266,6 +269,8 @@ WireId Arch::getWireByName(IdString name) const
IdString Arch::getWireName(WireId wire) const { return wire; }
+IdString Arch::getWireType(WireId wire) const { return wires.at(wire).type; }
+
uint32_t Arch::getWireChecksum(WireId wire) const
{
// FIXME
@@ -316,6 +321,8 @@ PipId Arch::getPipByName(IdString name) const
IdString Arch::getPipName(PipId pip) const { return pip; }
+IdString Arch::getPipType(PipId pip) const { return pips.at(pip).type; }
+
uint32_t Arch::getPipChecksum(PipId wire) const
{
// FIXME
diff --git a/generic/arch.h b/generic/arch.h
index 97ed1ac2..01a90ee1 100644
--- a/generic/arch.h
+++ b/generic/arch.h
@@ -31,7 +31,7 @@ struct WireInfo;
struct PipInfo
{
- IdString name, bound_net;
+ IdString name, type, bound_net;
WireId srcWire, dstWire;
DelayInfo delay;
DecalXY decalxy;
@@ -39,7 +39,7 @@ struct PipInfo
struct WireInfo
{
- IdString name, bound_net;
+ IdString name, type, bound_net;
std::vector<PipId> downhill, uphill, aliases;
BelPin uphill_bel_pin;
std::vector<BelPin> downhill_bel_pins;
@@ -96,9 +96,9 @@ struct Arch : BaseCtx
float grid_distance_to_delay;
- void addWire(IdString name, int x, int y);
- void addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay);
- void addAlias(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay);
+ void addWire(IdString name, IdString type, int x, int y);
+ void addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay);
+ void addAlias(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay);
void addBel(IdString name, IdString type, Loc loc, bool gb);
void addBelInput(IdString bel, IdString name, IdString wire);
@@ -157,6 +157,7 @@ struct Arch : BaseCtx
WireId getWireByName(IdString name) const;
IdString getWireName(WireId wire) const;
+ IdString getWireType(WireId wire) const;
uint32_t getWireChecksum(WireId wire) const;
void bindWire(WireId wire, IdString net, PlaceStrength strength);
void unbindWire(WireId wire);
@@ -169,6 +170,7 @@ struct Arch : BaseCtx
PipId getPipByName(IdString name) const;
IdString getPipName(PipId pip) const;
+ IdString getPipType(PipId pip) const;
uint32_t getPipChecksum(PipId pip) const;
void bindPip(PipId pip, IdString net, PlaceStrength strength);
void unbindPip(PipId pip);
diff --git a/gui/base.qrc b/gui/base.qrc
index 1a848f54..7b3fa55c 100644
--- a/gui/base.qrc
+++ b/gui/base.qrc
@@ -10,5 +10,9 @@
<file>resources/resultset_next.png</file>
<file>resources/resultset_last.png</file>
<file>resources/cross.png</file>
+ <file>resources/zoom_in.png</file>
+ <file>resources/zoom_out.png</file>
+ <file>resources/shape_handles.png</file>
+ <file>resources/shape_square.png</file>
</qresource>
</RCC>
diff --git a/gui/basewindow.cc b/gui/basewindow.cc
index 78c2fe3a..e07200de 100644
--- a/gui/basewindow.cc
+++ b/gui/basewindow.cc
@@ -25,7 +25,6 @@
#include <QSplitter>
#include "designwidget.h"
#include "fpgaviewwidget.h"
-#include "jsonparse.h"
#include "log.h"
#include "mainwindow.h"
#include "pythontab.h"
@@ -76,7 +75,7 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent
centralTabWidget->setTabsClosable(true);
connect(centralTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
- FPGAViewWidget *fpgaView = new FPGAViewWidget();
+ fpgaView = new FPGAViewWidget();
centralTabWidget->addTab(fpgaView, "Graphics");
centralTabWidget->tabBar()->tabButton(0, QTabBar::RightSide)->resize(0, 0);
@@ -163,4 +162,30 @@ void BaseMainWindow::createMenusAndBars()
mainToolBar->addAction(actionSave);
}
+void BaseMainWindow::createGraphicsBar()
+{
+ QAction *actionZoomIn = new QAction("Zoom In", this);
+ actionZoomIn->setIcon(QIcon(":/icons/resources/zoom_in.png"));
+ connect(actionZoomIn, SIGNAL(triggered()), fpgaView, SLOT(zoomIn()));
+
+ QAction *actionZoomOut = new QAction("Zoom Out", this);
+ actionZoomOut->setIcon(QIcon(":/icons/resources/zoom_out.png"));
+ connect(actionZoomOut, SIGNAL(triggered()), fpgaView, SLOT(zoomOut()));
+
+ QAction *actionZoomSelected = new QAction("Zoom Selected", this);
+ actionZoomSelected->setIcon(QIcon(":/icons/resources/shape_handles.png"));
+ connect(actionZoomSelected, SIGNAL(triggered()), fpgaView, SLOT(zoomSelected()));
+
+ QAction *actionZoomOutbound = new QAction("Zoom Outbound", this);
+ actionZoomOutbound->setIcon(QIcon(":/icons/resources/shape_square.png"));
+ connect(actionZoomOutbound, SIGNAL(triggered()), fpgaView, SLOT(zoomOutbound()));
+
+ graphicsToolBar = new QToolBar();
+ addToolBar(Qt::TopToolBarArea, graphicsToolBar);
+ graphicsToolBar->addAction(actionZoomIn);
+ graphicsToolBar->addAction(actionZoomOut);
+ graphicsToolBar->addAction(actionZoomSelected);
+ graphicsToolBar->addAction(actionZoomOutbound);
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/gui/basewindow.h b/gui/basewindow.h
index 1184fa80..a25a2854 100644
--- a/gui/basewindow.h
+++ b/gui/basewindow.h
@@ -37,6 +37,7 @@ NEXTPNR_NAMESPACE_BEGIN
class PythonTab;
class DesignWidget;
+class FPGAViewWidget;
class BaseMainWindow : public QMainWindow
{
@@ -49,6 +50,7 @@ class BaseMainWindow : public QMainWindow
protected:
void createMenusAndBars();
+ void createGraphicsBar();
protected Q_SLOTS:
void writeInfo(std::string text);
@@ -70,12 +72,14 @@ class BaseMainWindow : public QMainWindow
QMenuBar *menuBar;
QToolBar *mainToolBar;
+ QToolBar *graphicsToolBar;
QStatusBar *statusBar;
QAction *actionNew;
QAction *actionOpen;
QAction *actionSave;
QProgressBar *progressBar;
DesignWidget *designview;
+ FPGAViewWidget *fpgaView;
};
NEXTPNR_NAMESPACE_END
diff --git a/gui/designwidget.cc b/gui/designwidget.cc
index c17990a5..43964edf 100644
--- a/gui/designwidget.cc
+++ b/gui/designwidget.cc
@@ -567,6 +567,7 @@ void DesignWidget::onItemSelectionChanged()
QtProperty *topItem = addTopLevelProperty("Wire");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
+ addProperty(topItem, QVariant::String, "Type", ctx->getWireType(wire).c_str(ctx));
addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire));
addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundWireNet(wire).c_str(ctx), ElementType::NET);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx),
@@ -618,6 +619,7 @@ void DesignWidget::onItemSelectionChanged()
QtProperty *topItem = addTopLevelProperty("Pip");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
+ addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx));
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundPipNet(pip).c_str(ctx), ElementType::NET);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingPipNet(pip).c_str(ctx),
@@ -775,52 +777,64 @@ std::vector<DecalXY> DesignWidget::getDecals(ElementType type, IdString value)
return decals;
}
-void DesignWidget::updateHighlightGroup(QTreeWidgetItem *item, int group)
+void DesignWidget::updateHighlightGroup(QList<QTreeWidgetItem *> items, int group)
{
- if (highlightSelected.contains(item)) {
- if (highlightSelected[item] == group) {
- highlightSelected.remove(item);
+ const bool shouldClear = items.size() == 1;
+ for (auto item : items) {
+ if (highlightSelected.contains(item)) {
+ if (shouldClear && highlightSelected[item] == group) {
+ highlightSelected.remove(item);
+ } else
+ highlightSelected[item] = group;
} else
- highlightSelected[item] = group;
- } else
- highlightSelected.insert(item, group);
-
- std::vector<DecalXY> decals;
+ highlightSelected.insert(item, group);
+ }
+ std::vector<DecalXY> decals[8];
for (auto it : highlightSelected.toStdMap()) {
- if (it.second == group) {
- ElementType type = static_cast<ElementTreeItem *>(it.first)->getType();
- IdString value = static_cast<IdStringTreeItem *>(it.first)->getData();
- std::vector<DecalXY> d = getDecals(type, value);
- std::move(d.begin(), d.end(), std::back_inserter(decals));
- }
+ ElementType type = static_cast<ElementTreeItem *>(it.first)->getType();
+ IdString value = static_cast<IdStringTreeItem *>(it.first)->getData();
+ std::vector<DecalXY> d = getDecals(type, value);
+ std::move(d.begin(), d.end(), std::back_inserter(decals[it.second]));
}
-
- Q_EMIT highlight(decals, group);
+ for (int i = 0; i < 8; i++)
+ Q_EMIT highlight(decals[i], i);
}
void DesignWidget::prepareMenuProperty(const QPoint &pos)
{
QTreeWidget *tree = propertyEditor->treeWidget();
-
- itemContextMenu = tree->itemAt(pos);
- if (itemContextMenu->parent() == nullptr)
- return;
-
- QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu);
- if (!browserItem)
- return;
- QtProperty *selectedProperty = browserItem->property();
- ElementType type = getElementTypeByName(selectedProperty->propertyId());
- if (type == ElementType::NONE)
- return;
- IdString value = ctx->id(selectedProperty->valueText().toStdString());
-
- QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx));
+ QList<QTreeWidgetItem *> items;
+ for (auto itemContextMenu : tree->selectedItems()) {
+ QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu);
+ if (!browserItem)
+ continue;
+ QtProperty *selectedProperty = browserItem->property();
+ ElementType type = getElementTypeByName(selectedProperty->propertyId());
+ if (type == ElementType::NONE)
+ continue;
+ IdString value = ctx->id(selectedProperty->valueText().toStdString());
+ items.append(nameToItem[getElementIndex(type)].value(value.c_str(ctx)));
+ }
+ int selectedIndex = -1;
+ if (items.size() == 1) {
+ QTreeWidgetItem *item = items.at(0);
+ if (highlightSelected.contains(item))
+ selectedIndex = highlightSelected[item];
+ }
QMenu menu(this);
QAction *selectAction = new QAction("&Select", this);
- connect(selectAction, &QAction::triggered, this, [this, type, value] { Q_EMIT selected(getDecals(type, value)); });
+ connect(selectAction, &QAction::triggered, this, [this, items] {
+ std::vector<DecalXY> decals;
+ for (auto clickItem : items) {
+ IdString value = static_cast<IdStringTreeItem *>(clickItem)->getData();
+ ElementType type = static_cast<ElementTreeItem *>(clickItem)->getType();
+ std::vector<DecalXY> d = getDecals(type, value);
+ std::move(d.begin(), d.end(), std::back_inserter(decals));
+ }
+ Q_EMIT selected(decals);
+ });
menu.addAction(selectAction);
QMenu *subMenu = menu.addMenu("Highlight");
@@ -833,27 +847,24 @@ void DesignWidget::prepareMenuProperty(const QPoint &pos)
action->setCheckable(true);
subMenu->addAction(action);
group->addAction(action);
- if (highlightSelected.contains(item) && highlightSelected[item] == i)
+ if (selectedIndex == i)
action->setChecked(true);
- connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); });
+ connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); });
}
menu.exec(tree->mapToGlobal(pos));
}
void DesignWidget::prepareMenuTree(const QPoint &pos)
{
- QTreeWidget *tree = treeWidget;
-
- itemContextMenu = tree->itemAt(pos);
-
- ElementType type = static_cast<ElementTreeItem *>(itemContextMenu)->getType();
- IdString value = static_cast<IdStringTreeItem *>(itemContextMenu)->getData();
-
- if (type == ElementType::NONE)
+ if (treeWidget->selectedItems().size() == 0)
return;
-
- QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx));
-
+ int selectedIndex = -1;
+ QList<QTreeWidgetItem *> items = treeWidget->selectedItems();
+ if (treeWidget->selectedItems().size() == 1) {
+ QTreeWidgetItem *item = treeWidget->selectedItems().at(0);
+ if (highlightSelected.contains(item))
+ selectedIndex = highlightSelected[item];
+ }
QMenu menu(this);
QMenu *subMenu = menu.addMenu("Highlight");
QActionGroup *group = new QActionGroup(this);
@@ -865,11 +876,11 @@ void DesignWidget::prepareMenuTree(const QPoint &pos)
action->setCheckable(true);
subMenu->addAction(action);
group->addAction(action);
- if (highlightSelected.contains(item) && highlightSelected[item] == i)
+ if (selectedIndex == i)
action->setChecked(true);
- connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); });
+ connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); });
}
- menu.exec(tree->mapToGlobal(pos));
+ menu.exec(treeWidget->mapToGlobal(pos));
}
void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column)
@@ -878,14 +889,8 @@ void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column)
ElementType type = getElementTypeByName(selectedProperty->propertyId());
QString value = selectedProperty->valueText();
int index = getElementIndex(type);
- switch (type) {
- case ElementType::NONE:
- return;
- default: {
- if (nameToItem[index].contains(value))
- treeWidget->setCurrentItem(nameToItem[index].value(value));
- } break;
- }
+ if (type != ElementType::NONE && nameToItem[index].contains(value))
+ treeWidget->setCurrentItem(nameToItem[index].value(value));
}
NEXTPNR_NAMESPACE_END
diff --git a/gui/designwidget.h b/gui/designwidget.h
index b5877f60..6d4b7fe1 100644
--- a/gui/designwidget.h
+++ b/gui/designwidget.h
@@ -60,7 +60,7 @@ class DesignWidget : public QWidget
void updateButtons();
void addToHistory(QTreeWidgetItem *item);
std::vector<DecalXY> getDecals(ElementType type, IdString value);
- void updateHighlightGroup(QTreeWidgetItem *item, int group);
+ void updateHighlightGroup(QList<QTreeWidgetItem *> item, int group);
Q_SIGNALS:
void info(std::string text);
void selected(std::vector<DecalXY> decal);
@@ -85,7 +85,6 @@ class DesignWidget : public QWidget
QtGroupPropertyManager *groupManager;
QtVariantEditorFactory *variantFactory;
QtTreePropertyBrowser *propertyEditor;
- QTreeWidgetItem *itemContextMenu;
QMap<QtProperty *, QString> propertyToId;
QMap<QString, QtProperty *> idToProperty;
diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc
index 15f37ce0..de73e27b 100644
--- a/gui/fpgaviewwidget.cc
+++ b/gui/fpgaviewwidget.cc
@@ -31,220 +31,11 @@
NEXTPNR_NAMESPACE_BEGIN
-void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur,
- const QVector2D *next) const
-{
- // buildPoint emits two vertices per line point, along with normals to move
- // them the right directio when rendering and miter to compensate for
- // bends.
-
- if (cur == nullptr) {
- // BUG
- return;
- }
-
- if (prev == nullptr && next == nullptr) {
- // BUG
- return;
- }
-
- // TODO(q3k): fast path for vertical/horizontal lines?
-
- // TODO(q3k): consider moving some of the linear algebra to the GPU,
- // they're better at this than poor old CPUs.
-
- // Build two unit vectors pointing in the direction of the two segments
- // defined by (prev, cur) and (cur, next)
- QVector2D dprev, dnext;
- if (prev == nullptr) {
- dnext = *next - *cur;
- dprev = dnext;
- } else if (next == nullptr) {
- dprev = *cur - *prev;
- dnext = dprev;
- } else {
- dprev = *cur - *prev;
- dnext = *next - *cur;
- }
- dprev.normalize();
- dnext.normalize();
-
- // Calculate tangent unit vector.
- QVector2D tangent(dprev + dnext);
- tangent.normalize();
-
- // Calculate normal to tangent - this is the line on which the vectors need
- // to be pushed to build a thickened line.
- const QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x());
-
- // Calculate normal to one of the lines.
- const QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x());
- // https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg
- // (the ^-1 is performed in the shader)
- const float miter = QVector2D::dotProduct(tangent_normal, dprev_normal);
-
- const float x = cur->x();
- const float y = cur->y();
- const float mx = tangent_normal.x();
- const float my = tangent_normal.y();
-
- // Push back 'left' vertex.
- building->vertices.push_back(Vertex2DPOD(x, y));
- building->normals.push_back(Vertex2DPOD(mx, my));
- building->miters.push_back(miter);
-
- // Push back 'right' vertex.
- building->vertices.push_back(Vertex2DPOD(x, y));
- building->normals.push_back(Vertex2DPOD(mx, my));
- building->miters.push_back(-miter);
-}
-
-void PolyLine::build(LineShaderData &target) const
-{
- if (points_.size() < 2) {
- return;
- }
- const QVector2D *first = &points_.front();
- const QVector2D *last = &points_.back();
-
- // Index number of vertices, used to build the index buffer.
- unsigned int startIndex = target.vertices.size();
- unsigned int index = startIndex;
-
- // For every point on the line, call buildPoint with (prev, point, next).
- // If we're building a closed line, prev/next wrap around. Otherwise
- // they are passed as nullptr and buildPoint interprets that accordinglu.
- const QVector2D *prev = nullptr;
-
- // Loop iterator used to ensure next is valid.
- unsigned int i = 0;
- for (const QVector2D &point : points_) {
- const QVector2D *next = nullptr;
- if (++i < points_.size()) {
- next = (&point + 1);
- }
-
- // If the line is closed, wrap around. Otherwise, pass nullptr.
- if (prev == nullptr && closed_) {
- buildPoint(&target, last, &point, next);
- } else if (next == nullptr && closed_) {
- buildPoint(&target, prev, &point, first);
- } else {
- buildPoint(&target, prev, &point, next);
- }
-
- // If we have a prev point relative to cur, build a pair of triangles
- // to render vertices into lines.
- if (prev != nullptr) {
- target.indices.push_back(index);
- target.indices.push_back(index + 1);
- target.indices.push_back(index + 2);
-
- target.indices.push_back(index + 2);
- target.indices.push_back(index + 1);
- target.indices.push_back(index + 3);
-
- index += 2;
- }
- prev = &point;
- }
-
- // If we're closed, build two more vertices that loop the line around.
- if (closed_) {
- target.indices.push_back(index);
- target.indices.push_back(index + 1);
- target.indices.push_back(startIndex);
-
- target.indices.push_back(startIndex);
- target.indices.push_back(index + 1);
- target.indices.push_back(startIndex + 1);
- }
-}
-
-bool LineShader::compile(void)
-{
- program_ = new QOpenGLShaderProgram(parent_);
- program_->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource_);
- program_->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource_);
- if (!program_->link()) {
- printf("could not link program: %s\n", program_->log().toStdString().c_str());
- return false;
- }
-
- if (!vao_.create())
- log_abort();
- vao_.bind();
-
- if (!buffers_.position.create())
- log_abort();
- if (!buffers_.normal.create())
- log_abort();
- if (!buffers_.miter.create())
- log_abort();
- if (!buffers_.index.create())
- log_abort();
-
- attributes_.position = program_->attributeLocation("position");
- attributes_.normal = program_->attributeLocation("normal");
- attributes_.miter = program_->attributeLocation("miter");
- uniforms_.thickness = program_->uniformLocation("thickness");
- uniforms_.projection = program_->uniformLocation("projection");
- uniforms_.color = program_->uniformLocation("color");
-
- vao_.release();
- return true;
-}
-
-void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection)
-{
- auto gl = QOpenGLContext::currentContext()->functions();
- if (line.vertices.size() == 0)
- return;
- vao_.bind();
- program_->bind();
-
- buffers_.position.bind();
- buffers_.position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size());
-
- buffers_.normal.bind();
- buffers_.normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size());
-
- buffers_.miter.bind();
- buffers_.miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size());
-
- buffers_.index.bind();
- buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size());
-
- program_->setUniformValue(uniforms_.projection, projection);
- program_->setUniformValue(uniforms_.thickness, thickness);
- program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF());
-
- buffers_.position.bind();
- program_->enableAttributeArray("position");
- gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
-
- buffers_.normal.bind();
- program_->enableAttributeArray("normal");
- gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
-
- buffers_.miter.bind();
- program_->enableAttributeArray("miter");
- gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0, (void *)0);
-
- buffers_.index.bind();
- gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT, (void *)0);
-
- program_->disableAttributeArray("miter");
- program_->disableAttributeArray("normal");
- program_->disableAttributeArray("position");
-
- program_->release();
- vao_.release();
-}
-
-FPGAViewWidget::FPGAViewWidget(QWidget *parent)
- : QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr), paintTimer_(this),
- rendererData_(new FPGAViewWidget::RendererData), rendererArgs_(new FPGAViewWidget::RendererArgs)
+FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
+ QOpenGLWidget(parent), ctx_(nullptr), paintTimer_(this),
+ lineShader_(this), zoom_(500.0f),
+ rendererData_(new FPGAViewWidget::RendererData),
+ rendererArgs_(new FPGAViewWidget::RendererArgs)
{
colors_.background = QColor("#000000");
colors_.grid = QColor("#333");
@@ -311,69 +102,49 @@ void FPGAViewWidget::initializeGL()
0.0);
}
-void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal)
+void FPGAViewWidget::drawGraphicElement(LineShaderData &out, const GraphicElement &el, float x, float y)
{
const float scale = 1.0;
- float offsetX = 0.0, offsetY = 0.0;
- for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
- offsetX = decal.x;
- offsetY = decal.y;
-
- if (el.type == GraphicElement::G_BOX) {
- auto line = PolyLine(true);
- line.point(offsetX + scale * el.x1, offsetY + scale * el.y1);
- line.point(offsetX + scale * el.x2, offsetY + scale * el.y1);
- line.point(offsetX + scale * el.x2, offsetY + scale * el.y2);
- line.point(offsetX + scale * el.x1, offsetY + scale * el.y2);
- line.build(out);
- }
+ if (el.type == GraphicElement::TYPE_BOX) {
+ auto line = PolyLine(true);
+ line.point(x + scale * el.x1, y + scale * el.y1);
+ line.point(x + scale * el.x2, y + scale * el.y1);
+ line.point(x + scale * el.x2, y + scale * el.y2);
+ line.point(x + scale * el.x1, y + scale * el.y2);
+ line.build(out);
+ }
- if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) {
- PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2)
- .build(out);
- }
+ if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) {
+ PolyLine(x + scale * el.x1, y + scale * el.y1, x + scale * el.x2, y + scale * el.y2)
+ .build(out);
}
}
-void FPGAViewWidget::drawDecal(LineShaderData out[], const DecalXY &decal)
+void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal)
{
- const float scale = 1.0;
- float offsetX = 0.0, offsetY = 0.0;
+ float offsetX = decal.x;
+ float offsetY = decal.y;
for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
- offsetX = decal.x;
- offsetY = decal.y;
-
- if (el.type == GraphicElement::G_BOX) {
- auto line = PolyLine(true);
- line.point(offsetX + scale * el.x1, offsetY + scale * el.y1);
- line.point(offsetX + scale * el.x2, offsetY + scale * el.y1);
- line.point(offsetX + scale * el.x2, offsetY + scale * el.y2);
- line.point(offsetX + scale * el.x1, offsetY + scale * el.y2);
- switch (el.style) {
- case GraphicElement::G_FRAME:
- case GraphicElement::G_INACTIVE:
- case GraphicElement::G_ACTIVE:
- line.build(out[el.style]);
- break;
- default:
- break;
- }
- }
+ drawGraphicElement(out, el, offsetX, offsetY);
+ }
+}
- if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) {
- auto line = PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2,
- offsetY + scale * el.y2);
- switch (el.style) {
- case GraphicElement::G_FRAME:
- case GraphicElement::G_INACTIVE:
- case GraphicElement::G_ACTIVE:
- line.build(out[el.style]);
- break;
- default:
- break;
- }
+void FPGAViewWidget::drawArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], const DecalXY &decal)
+{
+ float offsetX = decal.x;
+ float offsetY = decal.y;
+
+ for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
+ switch (el.style) {
+ case GraphicElement::STYLE_FRAME:
+ case GraphicElement::STYLE_INACTIVE:
+ case GraphicElement::STYLE_ACTIVE:
+ drawGraphicElement(out[el.style], el, offsetX, offsetY);
+ break;
+ default:
+ break;
}
}
}
@@ -403,24 +174,28 @@ void FPGAViewWidget::paintGL()
float thick1Px = mouseToWorldCoordinates(1, 0).x();
float thick11Px = mouseToWorldCoordinates(1.1, 0).x();
- // Draw grid.
+ // Render grid.
auto grid = LineShaderData();
for (float i = -100.0f; i < 100.0f; i += 1.0f) {
PolyLine(-100.0f, i, 100.0f, i).build(grid);
PolyLine(i, -100.0f, i, 100.0f).build(grid);
}
+ // Draw grid.
lineShader_.draw(grid, colors_.grid, thick1Px, matrix);
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);
+ // Render Arch graphics.
+ lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_FRAME], colors_.frame, thick11Px, matrix);
+ lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_HIDDEN], colors_.hidden, thick11Px, matrix);
+ lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_INACTIVE], colors_.inactive, thick11Px, matrix);
+ lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_ACTIVE], colors_.active, thick11Px, matrix);
+
+ // Draw highlighted items.
for (int i = 0; i < 8; i++)
- lineShader_.draw(rendererData_->highlighted[i], colors_.highlight[i], thick11Px, matrix);
+ lineShader_.draw(rendererData_->gfxHighlighted[i], colors_.highlight[i], thick11Px, matrix);
- lineShader_.draw(rendererData_->selected, colors_.selected, thick11Px, matrix);
+ lineShader_.draw(rendererData_->gfxSelected, colors_.selected, thick11Px, matrix);
rendererDataLock_.unlock();
}
@@ -431,126 +206,154 @@ 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.
+ // Data from Context needed to render all decals.
std::vector<DecalXY> belDecals;
std::vector<DecalXY> wireDecals;
std::vector<DecalXY> pipDecals;
std::vector<DecalXY> groupDecals;
- if (decalsChanged) {
- for (auto bel : ctx_->getBels()) {
- belDecals.push_back(ctx_->getBelDecal(bel));
+ bool decalsChanged = false;
+ {
+ // Take the UI/Normal mutex on the Context, copy over all we need as
+ // fast as we can.
+ std::lock_guard<std::mutex> lock_ui(ctx_->ui_mutex);
+ std::lock_guard<std::mutex> lock(ctx_->mutex);
+
+ // For now, collapse any decal changes into change of all decals.
+ // TODO(q3k): fix this
+ if (ctx_->allUiReload) {
+ ctx_->allUiReload = false;
+ decalsChanged = true;
}
- for (auto wire : ctx_->getWires()) {
- wireDecals.push_back(ctx_->getWireDecal(wire));
+ if (ctx_->frameUiReload) {
+ ctx_->frameUiReload = false;
+ decalsChanged = true;
}
- for (auto pip : ctx_->getPips()) {
- pipDecals.push_back(ctx_->getPipDecal(pip));
+ if (ctx_->belUiReload.size() > 0) {
+ ctx_->belUiReload.clear();
+ decalsChanged = true;
}
- for (auto group : ctx_->getGroups()) {
- groupDecals.push_back(ctx_->getGroupDecal(group));
+ 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.
+ if (decalsChanged) {
+ for (auto bel : ctx_->getBels()) {
+ 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();
+ // Arguments from the main UI thread on what we should render.
+ std::vector<DecalXY> selectedDecals;
+ std::vector<DecalXY> highlightedDecals[8];
+ bool highlightedOrSelectedChanged;
+ {
+ // Take the renderer arguments lock, copy over all we need.
+ QMutexLocker lock(&rendererArgsLock_);
+ selectedDecals = rendererArgs_->selectedDecals;
+ for (int i = 0; i < 8; i++)
+ highlightedDecals[i] = rendererArgs_->highlightedDecals[i];
+ highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged;
+ rendererArgs_->highlightedOrSelectedChanged = false;
+ }
+ // Render decals if necessary.
if (decalsChanged) {
auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);
// Draw Bels.
for (auto const &decal : belDecals) {
- drawDecal(data->decals, decal);
+ drawArchDecal(data->gfxByStyle, decal);
}
// Draw Wires.
for (auto const &decal : wireDecals) {
- drawDecal(data->decals, decal);
+ drawArchDecal(data->gfxByStyle, decal);
}
// Draw Pips.
for (auto const &decal : pipDecals) {
- drawDecal(data->decals, decal);
+ drawArchDecal(data->gfxByStyle, decal);
}
// Draw Groups.
for (auto const &decal : groupDecals) {
- drawDecal(data->decals, decal);
+ drawArchDecal(data->gfxByStyle, decal);
}
// Swap over.
- rendererDataLock_.lock();
- rendererData_ = std::move(data);
- rendererDataLock_.unlock();
+ {
+ QMutexLocker lock(&rendererDataLock_);
+
+ // If we're not re-rendering any highlights/selections, let's
+ // copy them over from teh current object.
+ if (!highlightedOrSelectedChanged) {
+ data->gfxSelected = rendererData_->gfxSelected;
+ for (int i = 0; i < 8; i++)
+ data->gfxHighlighted[i] = rendererData_->gfxHighlighted[i];
+ }
+
+ rendererData_ = std::move(data);
+ }
}
- rendererDataLock_.lock();
- if (decalsChanged || highlightedOrSelectedChanged) {
- rendererData_->selected.clear();
- for (auto &decal : selectedItems) {
- drawDecal(rendererData_->selected, decal);
+ if (highlightedOrSelectedChanged) {
+ QMutexLocker locker(&rendererDataLock_);
+
+ // Render selected.
+ rendererData_->gfxSelected.clear();
+ for (auto &decal : selectedDecals) {
+ drawDecal(rendererData_->gfxSelected, decal);
}
+
+ // Render highlighted.
for (int i = 0; i < 8; i++) {
- rendererData_->highlighted[i].clear();
- for (auto &decal : highlightedItems[i]) {
- drawDecal(rendererData_->highlighted[i], decal);
+ rendererData_->gfxHighlighted[i].clear();
+ for (auto &decal : highlightedDecals[i]) {
+ drawDecal(rendererData_->gfxHighlighted[i], decal);
}
}
}
- rendererDataLock_.unlock();
}
void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
{
- rendererArgsLock_.lock();
- rendererArgs_->selectedItems = decals;
- rendererArgs_->highlightedOrSelectedChanged = true;
- rendererArgsLock_.unlock();
+ {
+ QMutexLocker locker(&rendererArgsLock_);
+ rendererArgs_->selectedDecals = decals;
+ rendererArgs_->highlightedOrSelectedChanged = true;
+ }
pokeRenderer();
}
void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)
{
- rendererArgsLock_.lock();
- rendererArgs_->highlightedItems[group] = decals;
- rendererArgs_->highlightedOrSelectedChanged = true;
- rendererArgsLock_.unlock();
+ {
+ QMutexLocker locker(&rendererArgsLock_);
+ rendererArgs_->highlightedDecals[group] = decals;
+ rendererArgs_->highlightedOrSelectedChanged = true;
+ }
pokeRenderer();
}
void FPGAViewWidget::resizeGL(int width, int height) {}
-void FPGAViewWidget::mousePressEvent(QMouseEvent *event) { lastPos_ = event->pos(); }
+void FPGAViewWidget::mousePressEvent(QMouseEvent *event) { lastDragPos_ = event->pos(); }
// Invert the projection matrix to calculate screen/mouse to world/grid
// coordinates.
@@ -566,9 +369,9 @@ QVector4D FPGAViewWidget::mouseToWorldCoordinates(int x, int y)
void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
{
- const int dx = event->x() - lastPos_.x();
- const int dy = event->y() - lastPos_.y();
- lastPos_ = event->pos();
+ const int dx = event->x() - lastDragPos_.x();
+ const int dy = event->y() - lastDragPos_.y();
+ lastDragPos_ = event->pos();
auto world = mouseToWorldCoordinates(dx, dy);
viewMove_.translate(world.x(), -world.y());
@@ -580,21 +383,32 @@ void FPGAViewWidget::wheelEvent(QWheelEvent *event)
{
QPoint degree = event->angleDelta() / 8;
- if (!degree.isNull()) {
-
- if (zoom_ < zoomNear_) {
- zoom_ = zoomNear_;
- } else if (zoom_ < zoomLvl1_) {
- zoom_ -= degree.y() / 10.0;
- } else if (zoom_ < zoomLvl2_) {
- zoom_ -= degree.y() / 5.0;
- } else if (zoom_ < zoomFar_) {
- zoom_ -= degree.y();
- } else {
- zoom_ = zoomFar_;
- }
- update();
+ if (!degree.isNull())
+ zoom(degree.y());
+}
+
+void FPGAViewWidget::zoom(int level)
+{
+ if (zoom_ < zoomNear_) {
+ zoom_ = zoomNear_;
+ } else if (zoom_ < zoomLvl1_) {
+ zoom_ -= level / 10.0;
+ } else if (zoom_ < zoomLvl2_) {
+ zoom_ -= level / 5.0;
+ } else if (zoom_ < zoomFar_) {
+ zoom_ -= level;
+ } else {
+ zoom_ = zoomFar_;
}
+ update();
}
+void FPGAViewWidget::zoomIn() { zoom(10); }
+
+void FPGAViewWidget::zoomOut() { zoom(-10); }
+
+void FPGAViewWidget::zoomSelected() {}
+
+void FPGAViewWidget::zoomOutbound() {}
+
NEXTPNR_NAMESPACE_END
diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h
index b87c5d0a..260ebf05 100644
--- a/gui/fpgaviewwidget.h
+++ b/gui/fpgaviewwidget.h
@@ -33,183 +33,10 @@
#include <QWaitCondition>
#include "nextpnr.h"
+#include "lineshader.h"
NEXTPNR_NAMESPACE_BEGIN
-// Vertex2DPOD is a structure of X, Y coordinates that can be passed to OpenGL
-// directly.
-NPNR_PACKED_STRUCT(struct Vertex2DPOD {
- GLfloat x;
- GLfloat y;
-
- Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {}
-});
-
-// LineShaderData is a built set of vertices that can be rendered by the
-// LineShader.
-// Each LineShaderData can have its' own color and thickness.
-struct LineShaderData
-{
- std::vector<Vertex2DPOD> vertices;
- std::vector<Vertex2DPOD> normals;
- std::vector<GLfloat> miters;
- std::vector<GLuint> indices;
-
- LineShaderData(void) {}
-
- void clear(void)
- {
- vertices.clear();
- normals.clear();
- miters.clear();
- indices.clear();
- }
-};
-
-// PolyLine is a set of segments defined by points, that can be built to a
-// ShaderLine for GPU rendering.
-class PolyLine
-{
- private:
- std::vector<QVector2D> points_;
- bool closed_;
-
- void buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur, const QVector2D *next) const;
-
- public:
- // Create an empty PolyLine.
- PolyLine(bool closed = false) : closed_(closed) {}
-
- // Create a non-closed polyline consisting of one segment.
- PolyLine(float x0, float y0, float x1, float y1) : closed_(false)
- {
- point(x0, y0);
- point(x1, y1);
- }
-
- // Add a point to the PolyLine.
- void point(float x, float y) { points_.push_back(QVector2D(x, y)); }
-
- // Built PolyLine to shader data.
- void build(LineShaderData &target) const;
-
- // Set whether line is closed (ie. a loop).
- void setClosed(bool closed) { closed_ = closed; }
-};
-
-// LineShader is an OpenGL shader program that renders LineShaderData on the
-// GPU.
-// The LineShader expects two vertices per line point. It will push those
-// vertices along the given normal * miter. This is used to 'stretch' the line
-// to be as wide as the given thickness. The normal and miter are calculated
-// by the PolyLine build method in order to construct a constant thickness line
-// with miter edge joints.
-//
-// +------+------+
-//
-// |
-// PolyLine.build()
-// |
-// V
-//
-// ^ ^ ^
-// | | | <--- normal vectors (x2, pointing in the same
-// +/+----+/+----+/+ direction)
-//
-// |
-// vertex shader
-// |
-// V
-//
-// +------+------+ ^ by normal * miter * thickness/2
-// | | |
-// +------+------+ V by normal * miter * thickness/2
-//
-// (miter is flipped for every second vertex generated)
-class LineShader
-{
- private:
- QObject *parent_;
- QOpenGLShaderProgram *program_;
-
- // GL attribute locations.
- struct
- {
- // original position of line vertex
- GLuint position;
- // normal by which vertex should be translated
- GLuint normal;
- // scalar defining:
- // - how stretched the normal vector should be to
- // compensate for bends
- // - which way the normal should be applied (+1 for one vertex, -1
- // for the other)
- GLuint miter;
- } attributes_;
-
- // GL buffers
- struct
- {
- QOpenGLBuffer position;
- QOpenGLBuffer normal;
- QOpenGLBuffer miter;
- QOpenGLBuffer index;
- } buffers_;
-
- // GL uniform locations.
- struct
- {
- // combines m/v/p matrix to apply
- GLuint projection;
- // desired thickness of line
- GLuint thickness;
- // color of line
- GLuint color;
- } uniforms_;
-
- QOpenGLVertexArrayObject vao_;
-
- public:
- LineShader(QObject *parent) : parent_(parent), program_(nullptr)
- {
- buffers_.position = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
- buffers_.position.setUsagePattern(QOpenGLBuffer::StaticDraw);
-
- buffers_.normal = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
- buffers_.normal.setUsagePattern(QOpenGLBuffer::StaticDraw);
-
- buffers_.miter = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
- buffers_.miter.setUsagePattern(QOpenGLBuffer::StaticDraw);
-
- buffers_.index = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
- buffers_.index.setUsagePattern(QOpenGLBuffer::StaticDraw);
- }
-
- static constexpr const char *vertexShaderSource_ =
- "#version 110\n"
- "attribute highp vec2 position;\n"
- "attribute highp vec2 normal;\n"
- "attribute highp float miter;\n"
- "uniform highp float thickness;\n"
- "uniform highp mat4 projection;\n"
- "void main() {\n"
- " vec2 p = position.xy + vec2(normal * thickness/2.0 / miter);\n"
- " gl_Position = projection * vec4(p, 0.0, 1.0);\n"
- "}\n";
-
- static constexpr const char *fragmentShaderSource_ = "#version 110\n"
- "uniform lowp vec4 color;\n"
- "void main() {\n"
- " gl_FragColor = color;\n"
- "}\n";
-
- // Must be called on initialization.
- bool compile(void);
-
- // Render a LineShaderData with a given M/V/P transformation.
- void draw(const LineShaderData &data, const QColor &color, float thickness, const QMatrix4x4 &projection);
-};
-
class PeriodicRunner : public QThread
{
Q_OBJECT
@@ -267,53 +94,43 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
FPGAViewWidget(QWidget *parent = 0);
~FPGAViewWidget();
- QSize minimumSizeHint() const override;
- QSize sizeHint() const override;
-
- void setXTranslation(float t_x);
- void setYTranslation(float t_y);
- void setZoom(float t_z);
-
- void xRotationChanged(int angle);
- void yRotationChanged(int angle);
- void zRotationChanged(int angle);
-
protected:
+ // Qt callbacks.
void initializeGL() Q_DECL_OVERRIDE;
void paintGL() Q_DECL_OVERRIDE;
void resizeGL(int width, int height) Q_DECL_OVERRIDE;
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
- void drawDecal(LineShaderData &data, const DecalXY &decal);
- void drawDecal(LineShaderData out[], const DecalXY &decal);
+ QSize minimumSizeHint() const override;
+ QSize sizeHint() const override;
+
+
public Q_SLOTS:
void newContext(Context *ctx);
void onSelectedArchItem(std::vector<DecalXY> decals);
void onHighlightGroupChanged(std::vector<DecalXY> decals, int group);
void pokeRenderer(void);
+ void zoomIn();
+ void zoomOut();
+ void zoomSelected();
+ void zoomOutbound();
private:
- void renderLines(void);
-
- QPoint lastPos_;
- LineShader lineShader_;
- QMatrix4x4 viewMove_;
- float zoom_;
- QMatrix4x4 getProjection(void);
- QVector4D mouseToWorldCoordinates(int x, int y);
-
const float zoomNear_ = 1.0f; // do not zoom closer than this
const float zoomFar_ = 10000.0f; // do not zoom further than this
-
const float zoomLvl1_ = 100.0f;
const float zoomLvl2_ = 50.0f;
Context *ctx_;
QTimer paintTimer_;
-
std::unique_ptr<PeriodicRunner> renderRunner_;
+ QPoint lastDragPos_;
+ LineShader lineShader_;
+ QMatrix4x4 viewMove_;
+ float zoom_;
+
struct
{
QColor background;
@@ -328,22 +145,29 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
struct RendererData
{
- LineShaderData decals[4];
- LineShaderData selected;
- LineShaderData highlighted[8];
+ LineShaderData gfxByStyle[GraphicElement::STYLE_MAX];
+ LineShaderData gfxSelected;
+ LineShaderData gfxHighlighted[8];
};
+ std::unique_ptr<RendererData> rendererData_;
+ QMutex rendererDataLock_;
struct RendererArgs
{
- std::vector<DecalXY> selectedItems;
- std::vector<DecalXY> highlightedItems[8];
+ std::vector<DecalXY> selectedDecals;
+ std::vector<DecalXY> highlightedDecals[8];
bool highlightedOrSelectedChanged;
};
-
- std::unique_ptr<RendererData> rendererData_;
- QMutex rendererDataLock_;
std::unique_ptr<RendererArgs> rendererArgs_;
QMutex rendererArgsLock_;
+
+ void zoom(int level);
+ void renderLines(void);
+ void drawGraphicElement(LineShaderData &out, const GraphicElement &el, float x, float y);
+ void drawDecal(LineShaderData &out, const DecalXY &decal);
+ void drawArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], const DecalXY &decal);
+ QVector4D mouseToWorldCoordinates(int x, int y);
+ QMatrix4x4 getProjection(void);
};
NEXTPNR_NAMESPACE_END
diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc
index 810a98ae..f06971b6 100644
--- a/gui/ice40/mainwindow.cc
+++ b/gui/ice40/mainwindow.cc
@@ -159,6 +159,8 @@ void MainWindow::createMenu()
taskToolBar->addAction(actionPlay);
taskToolBar->addAction(actionPause);
taskToolBar->addAction(actionStop);
+
+ createGraphicsBar();
}
#if defined(_MSC_VER)
diff --git a/gui/lineshader.cc b/gui/lineshader.cc
new file mode 100644
index 00000000..94a7a010
--- /dev/null
+++ b/gui/lineshader.cc
@@ -0,0 +1,236 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * 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
+ * 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 "log.h"
+#include "lineshader.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur,
+ const QVector2D *next) const
+{
+ // buildPoint emits two vertices per line point, along with normals to move
+ // them the right directio when rendering and miter to compensate for
+ // bends.
+
+ if (cur == nullptr) {
+ // BUG
+ return;
+ }
+
+ if (prev == nullptr && next == nullptr) {
+ // BUG
+ return;
+ }
+
+ // TODO(q3k): fast path for vertical/horizontal lines?
+
+ // TODO(q3k): consider moving some of the linear algebra to the GPU,
+ // they're better at this than poor old CPUs.
+
+ // Build two unit vectors pointing in the direction of the two segments
+ // defined by (prev, cur) and (cur, next)
+ QVector2D dprev, dnext;
+ if (prev == nullptr) {
+ dnext = *next - *cur;
+ dprev = dnext;
+ } else if (next == nullptr) {
+ dprev = *cur - *prev;
+ dnext = dprev;
+ } else {
+ dprev = *cur - *prev;
+ dnext = *next - *cur;
+ }
+ dprev.normalize();
+ dnext.normalize();
+
+ // Calculate tangent unit vector.
+ QVector2D tangent(dprev + dnext);
+ tangent.normalize();
+
+ // Calculate normal to tangent - this is the line on which the vectors need
+ // to be pushed to build a thickened line.
+ const QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x());
+
+ // Calculate normal to one of the lines.
+ const QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x());
+ // https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg
+ // (the ^-1 is performed in the shader)
+ const float miter = QVector2D::dotProduct(tangent_normal, dprev_normal);
+
+ const float x = cur->x();
+ const float y = cur->y();
+ const float mx = tangent_normal.x();
+ const float my = tangent_normal.y();
+
+ // Push back 'left' vertex.
+ building->vertices.push_back(Vertex2DPOD(x, y));
+ building->normals.push_back(Vertex2DPOD(mx, my));
+ building->miters.push_back(miter);
+
+ // Push back 'right' vertex.
+ building->vertices.push_back(Vertex2DPOD(x, y));
+ building->normals.push_back(Vertex2DPOD(mx, my));
+ building->miters.push_back(-miter);
+}
+
+void PolyLine::build(LineShaderData &target) const
+{
+ if (points_.size() < 2) {
+ return;
+ }
+ const QVector2D *first = &points_.front();
+ const QVector2D *last = &points_.back();
+
+ // Index number of vertices, used to build the index buffer.
+ unsigned int startIndex = target.vertices.size();
+ unsigned int index = startIndex;
+
+ // For every point on the line, call buildPoint with (prev, point, next).
+ // If we're building a closed line, prev/next wrap around. Otherwise
+ // they are passed as nullptr and buildPoint interprets that accordinglu.
+ const QVector2D *prev = nullptr;
+
+ // Loop iterator used to ensure next is valid.
+ unsigned int i = 0;
+ for (const QVector2D &point : points_) {
+ const QVector2D *next = nullptr;
+ if (++i < points_.size()) {
+ next = (&point + 1);
+ }
+
+ // If the line is closed, wrap around. Otherwise, pass nullptr.
+ if (prev == nullptr && closed_) {
+ buildPoint(&target, last, &point, next);
+ } else if (next == nullptr && closed_) {
+ buildPoint(&target, prev, &point, first);
+ } else {
+ buildPoint(&target, prev, &point, next);
+ }
+
+ // If we have a prev point relative to cur, build a pair of triangles
+ // to render vertices into lines.
+ if (prev != nullptr) {
+ target.indices.push_back(index);
+ target.indices.push_back(index + 1);
+ target.indices.push_back(index + 2);
+
+ target.indices.push_back(index + 2);
+ target.indices.push_back(index + 1);
+ target.indices.push_back(index + 3);
+
+ index += 2;
+ }
+ prev = &point;
+ }
+
+ // If we're closed, build two more vertices that loop the line around.
+ if (closed_) {
+ target.indices.push_back(index);
+ target.indices.push_back(index + 1);
+ target.indices.push_back(startIndex);
+
+ target.indices.push_back(startIndex);
+ target.indices.push_back(index + 1);
+ target.indices.push_back(startIndex + 1);
+ }
+}
+
+bool LineShader::compile(void)
+{
+ program_ = new QOpenGLShaderProgram(parent_);
+ program_->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource_);
+ program_->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource_);
+ if (!program_->link()) {
+ printf("could not link program: %s\n", program_->log().toStdString().c_str());
+ return false;
+ }
+
+ if (!vao_.create())
+ log_abort();
+ vao_.bind();
+
+ if (!buffers_.position.create())
+ log_abort();
+ if (!buffers_.normal.create())
+ log_abort();
+ if (!buffers_.miter.create())
+ log_abort();
+ if (!buffers_.index.create())
+ log_abort();
+
+ attributes_.position = program_->attributeLocation("position");
+ attributes_.normal = program_->attributeLocation("normal");
+ attributes_.miter = program_->attributeLocation("miter");
+ uniforms_.thickness = program_->uniformLocation("thickness");
+ uniforms_.projection = program_->uniformLocation("projection");
+ uniforms_.color = program_->uniformLocation("color");
+
+ vao_.release();
+ return true;
+}
+
+void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection)
+{
+ auto gl = QOpenGLContext::currentContext()->functions();
+ if (line.vertices.size() == 0)
+ return;
+ vao_.bind();
+ program_->bind();
+
+ buffers_.position.bind();
+ buffers_.position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size());
+
+ buffers_.normal.bind();
+ buffers_.normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size());
+
+ buffers_.miter.bind();
+ buffers_.miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size());
+
+ buffers_.index.bind();
+ buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size());
+
+ program_->setUniformValue(uniforms_.projection, projection);
+ program_->setUniformValue(uniforms_.thickness, thickness);
+ program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF());
+
+ buffers_.position.bind();
+ program_->enableAttributeArray("position");
+ gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
+
+ buffers_.normal.bind();
+ program_->enableAttributeArray("normal");
+ gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
+
+ buffers_.miter.bind();
+ program_->enableAttributeArray("miter");
+ gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0, (void *)0);
+
+ buffers_.index.bind();
+ gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT, (void *)0);
+
+ program_->disableAttributeArray("miter");
+ program_->disableAttributeArray("normal");
+ program_->disableAttributeArray("position");
+
+ program_->release();
+ vao_.release();
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/gui/lineshader.h b/gui/lineshader.h
new file mode 100644
index 00000000..3f4c4057
--- /dev/null
+++ b/gui/lineshader.h
@@ -0,0 +1,209 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * 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
+ * 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.
+ *
+ */
+
+#ifndef LINESHADER_H
+#define LINESHADER_H
+
+#include <QOpenGLBuffer>
+#include <QOpenGLFunctions>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLWidget>
+
+#include "nextpnr.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// Vertex2DPOD is a structure of X, Y coordinates that can be passed to OpenGL
+// directly.
+NPNR_PACKED_STRUCT(struct Vertex2DPOD {
+ GLfloat x;
+ GLfloat y;
+
+ Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {}
+});
+
+// LineShaderData is a built set of vertices that can be rendered by the
+// LineShader.
+// Each LineShaderData can have its' own color and thickness.
+struct LineShaderData
+{
+ std::vector<Vertex2DPOD> vertices;
+ std::vector<Vertex2DPOD> normals;
+ std::vector<GLfloat> miters;
+ std::vector<GLuint> indices;
+
+ LineShaderData(void) {}
+
+ void clear(void)
+ {
+ vertices.clear();
+ normals.clear();
+ miters.clear();
+ indices.clear();
+ }
+};
+
+// PolyLine is a set of segments defined by points, that can be built to a
+// ShaderLine for GPU rendering.
+class PolyLine
+{
+ private:
+ std::vector<QVector2D> points_;
+ bool closed_;
+
+ void buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur, const QVector2D *next) const;
+
+ public:
+ // Create an empty PolyLine.
+ PolyLine(bool closed = false) : closed_(closed) {}
+
+ // Create a non-closed polyline consisting of one segment.
+ PolyLine(float x0, float y0, float x1, float y1) : closed_(false)
+ {
+ point(x0, y0);
+ point(x1, y1);
+ }
+
+ // Add a point to the PolyLine.
+ void point(float x, float y) { points_.push_back(QVector2D(x, y)); }
+
+ // Built PolyLine to shader data.
+ void build(LineShaderData &target) const;
+
+ // Set whether line is closed (ie. a loop).
+ void setClosed(bool closed) { closed_ = closed; }
+};
+
+// LineShader is an OpenGL shader program that renders LineShaderData on the
+// GPU.
+// The LineShader expects two vertices per line point. It will push those
+// vertices along the given normal * miter. This is used to 'stretch' the line
+// to be as wide as the given thickness. The normal and miter are calculated
+// by the PolyLine build method in order to construct a constant thickness line
+// with miter edge joints.
+//
+// +------+------+
+//
+// |
+// PolyLine.build()
+// |
+// V
+//
+// ^ ^ ^
+// | | | <--- normal vectors (x2, pointing in the same
+// +/+----+/+----+/+ direction)
+//
+// |
+// vertex shader
+// |
+// V
+//
+// +------+------+ ^ by normal * miter * thickness/2
+// | | |
+// +------+------+ V by normal * miter * thickness/2
+//
+// (miter is flipped for every second vertex generated)
+class LineShader
+{
+ private:
+ QObject *parent_;
+ QOpenGLShaderProgram *program_;
+
+ // GL attribute locations.
+ struct
+ {
+ // original position of line vertex
+ GLuint position;
+ // normal by which vertex should be translated
+ GLuint normal;
+ // scalar defining:
+ // - how stretched the normal vector should be to
+ // compensate for bends
+ // - which way the normal should be applied (+1 for one vertex, -1
+ // for the other)
+ GLuint miter;
+ } attributes_;
+
+ // GL buffers
+ struct
+ {
+ QOpenGLBuffer position;
+ QOpenGLBuffer normal;
+ QOpenGLBuffer miter;
+ QOpenGLBuffer index;
+ } buffers_;
+
+ // GL uniform locations.
+ struct
+ {
+ // combines m/v/p matrix to apply
+ GLuint projection;
+ // desired thickness of line
+ GLuint thickness;
+ // color of line
+ GLuint color;
+ } uniforms_;
+
+ QOpenGLVertexArrayObject vao_;
+
+ public:
+ LineShader(QObject *parent) : parent_(parent), program_(nullptr)
+ {
+ buffers_.position = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
+ buffers_.position.setUsagePattern(QOpenGLBuffer::StaticDraw);
+
+ buffers_.normal = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
+ buffers_.normal.setUsagePattern(QOpenGLBuffer::StaticDraw);
+
+ buffers_.miter = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
+ buffers_.miter.setUsagePattern(QOpenGLBuffer::StaticDraw);
+
+ buffers_.index = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
+ buffers_.index.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ }
+
+ static constexpr const char *vertexShaderSource_ =
+ "#version 110\n"
+ "attribute highp vec2 position;\n"
+ "attribute highp vec2 normal;\n"
+ "attribute highp float miter;\n"
+ "uniform highp float thickness;\n"
+ "uniform highp mat4 projection;\n"
+ "void main() {\n"
+ " vec2 p = position.xy + vec2(normal * thickness/2.0 / miter);\n"
+ " gl_Position = projection * vec4(p, 0.0, 1.0);\n"
+ "}\n";
+
+ static constexpr const char *fragmentShaderSource_ = "#version 110\n"
+ "uniform lowp vec4 color;\n"
+ "void main() {\n"
+ " gl_FragColor = color;\n"
+ "}\n";
+
+ // Must be called on initialization.
+ bool compile(void);
+
+ // Render a LineShaderData with a given M/V/P transformation.
+ void draw(const LineShaderData &data, const QColor &color, float thickness, const QMatrix4x4 &projection);
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif
diff --git a/gui/resources/shape_handles.png b/gui/resources/shape_handles.png
new file mode 100644
index 00000000..ce27fe3a
--- /dev/null
+++ b/gui/resources/shape_handles.png
Binary files differ
diff --git a/gui/resources/shape_square.png b/gui/resources/shape_square.png
new file mode 100644
index 00000000..33af0460
--- /dev/null
+++ b/gui/resources/shape_square.png
Binary files differ
diff --git a/gui/resources/zoom_in.png b/gui/resources/zoom_in.png
new file mode 100644
index 00000000..cdf0a52f
--- /dev/null
+++ b/gui/resources/zoom_in.png
Binary files differ
diff --git a/gui/resources/zoom_out.png b/gui/resources/zoom_out.png
new file mode 100644
index 00000000..07bf98a7
--- /dev/null
+++ b/gui/resources/zoom_out.png
Binary files differ
diff --git a/ice40/arch.cc b/ice40/arch.cc
index dedc59bc..3803f842 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -440,11 +440,103 @@ GroupId Arch::getGroupByName(IdString name) const
return GroupId();
}
-IdString Arch::getGroupName(GroupId group) const { return IdString(); }
+IdString Arch::getGroupName(GroupId group) const
+{
+ std::string suffix;
+
+ switch (group.type) {
+ case GroupId::TYPE_FRAME:
+ suffix = "tile";
+ break;
+ case GroupId::TYPE_MAIN_SW:
+ suffix = "main_sw";
+ break;
+ case GroupId::TYPE_LOCAL_SW:
+ suffix = "local_sw";
+ break;
+ case GroupId::TYPE_LC0_SW:
+ suffix = "lc0_sw";
+ break;
+ case GroupId::TYPE_LC1_SW:
+ suffix = "lc1_sw";
+ break;
+ case GroupId::TYPE_LC2_SW:
+ suffix = "lc2_sw";
+ break;
+ case GroupId::TYPE_LC3_SW:
+ suffix = "lc3_sw";
+ break;
+ case GroupId::TYPE_LC4_SW:
+ suffix = "lc4_sw";
+ break;
+ case GroupId::TYPE_LC5_SW:
+ suffix = "lc5_sw";
+ break;
+ case GroupId::TYPE_LC6_SW:
+ suffix = "lc6_sw";
+ break;
+ case GroupId::TYPE_LC7_SW:
+ suffix = "lc7_sw";
+ break;
+ default:
+ return IdString();
+ }
+
+ return id("X" + std::to_string(group.x) + "/Y" + std::to_string(group.y) + "/" + suffix);
+}
std::vector<GroupId> Arch::getGroups() const
{
std::vector<GroupId> ret;
+
+ for (int y = 0; y < chip_info->height; y++) {
+ for (int x = 0; x < chip_info->width; x++) {
+ TileType type = chip_info->tile_grid[y * chip_info->width + x];
+ if (type == TILE_NONE)
+ continue;
+
+ GroupId group;
+ group.type = GroupId::TYPE_FRAME;
+ group.x = x;
+ group.y = y;
+ // ret.push_back(group);
+
+ group.type = GroupId::TYPE_MAIN_SW;
+ ret.push_back(group);
+
+ group.type = GroupId::TYPE_LOCAL_SW;
+ ret.push_back(group);
+
+#if 0
+ if (type == TILE_LOGIC)
+ {
+ group.type = GroupId::TYPE_LC0_SW;
+ ret.push_back(group);
+
+ group.type = GroupId::TYPE_LC1_SW;
+ ret.push_back(group);
+
+ group.type = GroupId::TYPE_LC2_SW;
+ ret.push_back(group);
+
+ group.type = GroupId::TYPE_LC3_SW;
+ ret.push_back(group);
+
+ group.type = GroupId::TYPE_LC4_SW;
+ ret.push_back(group);
+
+ group.type = GroupId::TYPE_LC5_SW;
+ ret.push_back(group);
+
+ group.type = GroupId::TYPE_LC6_SW;
+ ret.push_back(group);
+
+ group.type = GroupId::TYPE_LC7_SW;
+ ret.push_back(group);
+ }
+#endif
+ }
+ }
return ret;
}
@@ -552,22 +644,70 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
std::vector<GraphicElement> ret;
if (decal.type == DecalId::TYPE_FRAME) {
- for (int x = 0; x <= chip_info->width; x++)
- for (int y = 0; y <= chip_info->height; y++) {
- GraphicElement el;
- el.type = GraphicElement::G_LINE;
- el.x1 = x - 0.05, el.x2 = x + 0.05, el.y1 = y, el.y2 = y, el.z = 0;
- ret.push_back(el);
- el.x1 = x, el.x2 = x, el.y1 = y - 0.05, el.y2 = y + 0.05, el.z = 0;
- ret.push_back(el);
- }
+ /* nothing */
+ }
+
+ if (decal.type == DecalId::TYPE_GROUP) {
+ int type = (decal.index >> 16) & 255;
+ int x = (decal.index >> 8) & 255;
+ int y = decal.index & 255;
+
+ if (type == GroupId::TYPE_FRAME) {
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_LINE;
+ el.style = GraphicElement::STYLE_FRAME;
+
+ el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.01, el.y2 = y + 0.01;
+ ret.push_back(el);
+ el.x1 = x + 0.01, el.x2 = x + 0.01, el.y1 = y + 0.01, el.y2 = y + 0.02;
+ ret.push_back(el);
+
+ el.x1 = x + 0.99, el.x2 = x + 0.98, el.y1 = y + 0.01, el.y2 = y + 0.01;
+ ret.push_back(el);
+ el.x1 = x + 0.99, el.x2 = x + 0.99, el.y1 = y + 0.01, el.y2 = y + 0.02;
+ ret.push_back(el);
+
+ el.x1 = x + 0.99, el.x2 = x + 0.98, el.y1 = y + 0.99, el.y2 = y + 0.99;
+ ret.push_back(el);
+ el.x1 = x + 0.99, el.x2 = x + 0.99, el.y1 = y + 0.99, el.y2 = y + 0.98;
+ ret.push_back(el);
+
+ el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.99, el.y2 = y + 0.99;
+ ret.push_back(el);
+ el.x1 = x + 0.01, el.x2 = x + 0.01, el.y1 = y + 0.99, el.y2 = y + 0.98;
+ ret.push_back(el);
+ }
+
+ if (type == GroupId::TYPE_MAIN_SW) {
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_BOX;
+ el.style = GraphicElement::STYLE_FRAME;
+
+ el.x1 = x + main_swbox_x1;
+ el.x2 = x + main_swbox_x2;
+ el.y1 = y + main_swbox_y1;
+ el.y2 = y + main_swbox_y2;
+ ret.push_back(el);
+ }
+
+ if (type == GroupId::TYPE_LOCAL_SW) {
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_BOX;
+ el.style = GraphicElement::STYLE_FRAME;
+
+ el.x1 = x + local_swbox_x1;
+ el.x2 = x + local_swbox_x2;
+ el.y1 = y + local_swbox_y1;
+ el.y2 = y + local_swbox_y2;
+ ret.push_back(el);
+ }
}
if (decal.type == DecalId::TYPE_WIRE) {
int n = chip_info->wire_data[decal.index].num_segments;
const WireSegmentPOD *p = chip_info->wire_data[decal.index].segments.get();
- GraphicElement::style_t style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
+ GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
for (int i = 0; i < n; i++)
gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style);
@@ -575,7 +715,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
if (decal.type == DecalId::TYPE_PIP) {
const PipInfoPOD &p = chip_info->pip_data[decal.index];
- GraphicElement::style_t style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_HIDDEN;
+ GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_HIDDEN;
gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style);
}
@@ -587,111 +727,40 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
if (bel_type == TYPE_ICESTORM_LC) {
GraphicElement el;
- el.type = GraphicElement::G_BOX;
- el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
+ el.type = GraphicElement::TYPE_BOX;
+ el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
(chip_info->bel_data[bel.index].z) * logic_cell_pitch;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 +
(chip_info->bel_data[bel.index].z) * logic_cell_pitch;
- el.z = 0;
ret.push_back(el);
-
- if (chip_info->bel_data[bel.index].z == 0) {
- int tx = chip_info->bel_data[bel.index].x;
- int ty = chip_info->bel_data[bel.index].y;
-
- // Main switchbox
- GraphicElement main_sw;
- main_sw.type = GraphicElement::G_BOX;
- main_sw.style = GraphicElement::G_FRAME;
- main_sw.x1 = tx + main_swbox_x1;
- main_sw.x2 = tx + main_swbox_x2;
- main_sw.y1 = ty + main_swbox_y1;
- main_sw.y2 = ty + main_swbox_y2;
- ret.push_back(main_sw);
-
- // Local tracks to LUT input switchbox
- GraphicElement local_sw;
- local_sw.type = GraphicElement::G_BOX;
- local_sw.style = GraphicElement::G_FRAME;
- local_sw.x1 = tx + local_swbox_x1;
- local_sw.x2 = tx + local_swbox_x2;
- local_sw.y1 = ty + local_swbox_y1;
- local_sw.y2 = ty + local_swbox_y2;
- local_sw.z = 0;
- ret.push_back(local_sw);
- }
}
if (bel_type == TYPE_SB_IO) {
- if (chip_info->bel_data[bel.index].x == 0 || chip_info->bel_data[bel.index].x == chip_info->width - 1) {
- GraphicElement el;
- el.type = GraphicElement::G_BOX;
- el.x1 = chip_info->bel_data[bel.index].x + 0.1;
- el.x2 = chip_info->bel_data[bel.index].x + 0.9;
- if (chip_info->bel_data[bel.index].z == 0) {
- el.y1 = chip_info->bel_data[bel.index].y + 0.10;
- el.y2 = chip_info->bel_data[bel.index].y + 0.45;
- } else {
- el.y1 = chip_info->bel_data[bel.index].y + 0.55;
- el.y2 = chip_info->bel_data[bel.index].y + 0.90;
- }
- el.z = 0;
- ret.push_back(el);
- } else {
- GraphicElement el;
- el.type = GraphicElement::G_BOX;
- if (chip_info->bel_data[bel.index].z == 0) {
- el.x1 = chip_info->bel_data[bel.index].x + 0.10;
- el.x2 = chip_info->bel_data[bel.index].x + 0.45;
- } else {
- el.x1 = chip_info->bel_data[bel.index].x + 0.55;
- el.x2 = chip_info->bel_data[bel.index].x + 0.90;
- }
- el.y1 = chip_info->bel_data[bel.index].y + 0.1;
- el.y2 = chip_info->bel_data[bel.index].y + 0.9;
- el.z = 0;
- ret.push_back(el);
- }
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_BOX;
+ el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
+ el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
+ el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
+ el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
+ (4 * chip_info->bel_data[bel.index].z) * logic_cell_pitch;
+ el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 +
+ (4 * chip_info->bel_data[bel.index].z + 3) * logic_cell_pitch;
+ ret.push_back(el);
}
if (bel_type == TYPE_ICESTORM_RAM) {
for (int i = 0; i < 2; i++) {
- int tx = chip_info->bel_data[bel.index].x;
- int ty = chip_info->bel_data[bel.index].y + i;
-
GraphicElement el;
- el.type = GraphicElement::G_BOX;
- el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE;
+ el.type = GraphicElement::TYPE_BOX;
+ el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
- el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1;
- el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + 7 * logic_cell_pitch;
- el.z = 0;
+ el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + i;
+ el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + i + 7 * logic_cell_pitch;
ret.push_back(el);
-
- // Main switchbox
- GraphicElement main_sw;
- main_sw.type = GraphicElement::G_BOX;
- main_sw.style = GraphicElement::G_FRAME;
- main_sw.x1 = tx + main_swbox_x1;
- main_sw.x2 = tx + main_swbox_x2;
- main_sw.y1 = ty + main_swbox_y1;
- main_sw.y2 = ty + main_swbox_y2;
- ret.push_back(main_sw);
-
- // Local tracks to LUT input switchbox
- GraphicElement local_sw;
- local_sw.type = GraphicElement::G_BOX;
- local_sw.style = GraphicElement::G_FRAME;
- local_sw.x1 = tx + local_swbox_x1;
- local_sw.x2 = tx + local_swbox_x2;
- local_sw.y1 = ty + local_swbox_y1;
- local_sw.y2 = ty + local_swbox_y2;
- local_sw.z = 0;
- ret.push_back(local_sw);
}
}
}
diff --git a/ice40/arch.h b/ice40/arch.h
index 123b408c..51cbe725 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -469,6 +469,8 @@ struct Arch : BaseCtx
return id(chip_info->wire_data[wire.index].name.get());
}
+ IdString getWireType(WireId wire) const { return IdString(); }
+
uint32_t getWireChecksum(WireId wire) const { return wire.index; }
void bindWire(WireId wire, IdString net, PlaceStrength strength)
@@ -611,6 +613,8 @@ struct Arch : BaseCtx
IdString getPipName(PipId pip) const;
+ IdString getPipType(PipId pip) const { return IdString(); }
+
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
WireId getPipSrcWire(PipId pip) const
diff --git a/ice40/chipdb.py b/ice40/chipdb.py
index b5fee359..b6af8fcf 100644
--- a/ice40/chipdb.py
+++ b/ice40/chipdb.py
@@ -675,7 +675,8 @@ class BinaryBlobAssembler:
print("ref %s %s" % (name, comment))
def s(self, s, comment):
- print("str %s" % s)
+ assert "|" not in s
+ print("str |%s| %s" % (s, comment))
def u8(self, v, comment):
if comment is None:
diff --git a/ice40/gfx.cc b/ice40/gfx.cc
index 1b01cbd8..0798862a 100644
--- a/ice40/gfx.cc
+++ b/ice40/gfx.cc
@@ -24,7 +24,7 @@ NEXTPNR_NAMESPACE_BEGIN
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)
{
GraphicElement el;
- el.type = GraphicElement::G_LINE;
+ el.type = GraphicElement::TYPE_LINE;
el.style = style;
// Horizontal Span-4 Wires
@@ -647,7 +647,7 @@ void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, fl
float ty = 0.5 * (y1 + y2);
GraphicElement el;
- el.type = GraphicElement::G_ARROW;
+ el.type = GraphicElement::TYPE_ARROW;
el.style = style;
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
@@ -701,6 +701,17 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src,
if (getWireXY_local(src, x1, y1) && getWireXY_local(dst, x2, y2))
pipGfx(g, x, y, x1, y1, x2, y2, local_swbox_x1, local_swbox_y1, local_swbox_x2, local_swbox_y2, style);
+
+ if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) {
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_ARROW;
+ el.style = style;
+ el.x1 = x + logic_cell_x1 + 0.005 * 3;
+ el.x2 = el.x1;
+ el.y1 = y + 0.01;
+ el.y2 = y + 0.02;
+ g.push_back(el);
+ }
}
NEXTPNR_NAMESPACE_END
diff --git a/ice40/main.cc b/ice40/main.cc
index 2560387b..865eea9e 100644
--- a/ice40/main.cc
+++ b/ice40/main.cc
@@ -52,13 +52,13 @@ void svg_dump_decal(const Context *ctx, const DecalXY &decal)
const std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\"";
for (auto &el : ctx->getDecalGraphics(decal.decal)) {
- if (el.type == GraphicElement::G_BOX) {
+ if (el.type == GraphicElement::TYPE_BOX) {
std::cout << "<rect x=\"" << (offset + scale * (decal.x + el.x1)) << "\" y=\""
<< (offset + scale * (decal.y + el.y1)) << "\" height=\"" << (scale * (el.y2 - el.y1))
<< "\" width=\"" << (scale * (el.x2 - el.x1)) << "\" " << style << "/>\n";
}
- if (el.type == GraphicElement::G_LINE) {
+ if (el.type == GraphicElement::TYPE_LINE) {
std::cout << "<line x1=\"" << (offset + scale * (decal.x + el.x1)) << "\" y1=\""
<< (offset + scale * (decal.y + el.y1)) << "\" x2=\"" << (offset + scale * (decal.x + el.x2))
<< "\" y2=\"" << (offset + scale * (decal.y + el.y2)) << "\" " << style << "/>\n";