Merge remote-tracking branch 'origin/master' into redist_slack
This commit is contained in:
commit
d5c2332ebf
@ -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 `|`.
|
||||
|
89
bba/main.cc
89
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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
const bool shouldClear = items.size() == 1;
|
||||
for (auto item : items) {
|
||||
if (highlightSelected.contains(item)) {
|
||||
if (highlightSelected[item] == group) {
|
||||
if (shouldClear && highlightSelected[item] == group) {
|
||||
highlightSelected.remove(item);
|
||||
} else
|
||||
highlightSelected[item] = group;
|
||||
} else
|
||||
highlightSelected.insert(item, group);
|
||||
|
||||
std::vector<DecalXY> decals;
|
||||
}
|
||||
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));
|
||||
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;
|
||||
|
||||
QList<QTreeWidgetItem *> items;
|
||||
for (auto itemContextMenu : tree->selectedItems()) {
|
||||
QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu);
|
||||
if (!browserItem)
|
||||
return;
|
||||
continue;
|
||||
QtProperty *selectedProperty = browserItem->property();
|
||||
ElementType type = getElementTypeByName(selectedProperty->propertyId());
|
||||
if (type == ElementType::NONE)
|
||||
return;
|
||||
continue;
|
||||
IdString value = ctx->id(selectedProperty->valueText().toStdString());
|
||||
|
||||
QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx));
|
||||
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))
|
||||
if (type != ElementType::NONE && nameToItem[index].contains(value))
|
||||
treeWidget->setCurrentItem(nameToItem[index].value(value));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -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;
|
||||
|
@ -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,71 +102,51 @@ 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) {
|
||||
if (el.type == GraphicElement::TYPE_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.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)
|
||||
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;
|
||||
drawGraphicElement(out, el, offsetX, offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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::G_FRAME:
|
||||
case GraphicElement::G_INACTIVE:
|
||||
case GraphicElement::G_ACTIVE:
|
||||
line.build(out[el.style]);
|
||||
case GraphicElement::STYLE_FRAME:
|
||||
case GraphicElement::STYLE_INACTIVE:
|
||||
case GraphicElement::STYLE_ACTIVE:
|
||||
drawGraphicElement(out[el.style], el, offsetX, offsetY);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QMatrix4x4 FPGAViewWidget::getProjection(void)
|
||||
@ -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,11 +206,20 @@ void FPGAViewWidget::renderLines(void)
|
||||
if (ctx_ == nullptr)
|
||||
return;
|
||||
|
||||
ctx_->lock_ui();
|
||||
// Data from Context needed to render all decals.
|
||||
std::vector<DecalXY> belDecals;
|
||||
std::vector<DecalXY> wireDecals;
|
||||
std::vector<DecalXY> pipDecals;
|
||||
std::vector<DecalXY> groupDecals;
|
||||
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
|
||||
bool decalsChanged = false;
|
||||
if (ctx_->allUiReload) {
|
||||
ctx_->allUiReload = false;
|
||||
decalsChanged = true;
|
||||
@ -462,10 +246,6 @@ void FPGAViewWidget::renderLines(void)
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
belDecals.push_back(ctx_->getBelDecal(bel));
|
||||
@ -480,77 +260,100 @@ void FPGAViewWidget::renderLines(void)
|
||||
groupDecals.push_back(ctx_->getGroupDecal(group));
|
||||
}
|
||||
}
|
||||
ctx_->unlock_ui();
|
||||
}
|
||||
|
||||
rendererArgsLock_.lock();
|
||||
auto selectedItems = rendererArgs_->selectedItems;
|
||||
auto highlightedItems = rendererArgs_->highlightedItems;
|
||||
auto highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged;
|
||||
// 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;
|
||||
rendererArgsLock_.unlock();
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
rendererDataLock_.lock();
|
||||
if (decalsChanged || highlightedOrSelectedChanged) {
|
||||
rendererData_->selected.clear();
|
||||
for (auto &decal : selectedItems) {
|
||||
drawDecal(rendererData_->selected, decal);
|
||||
rendererData_ = std::move(data);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
{
|
||||
QMutexLocker locker(&rendererArgsLock_);
|
||||
rendererArgs_->selectedDecals = decals;
|
||||
rendererArgs_->highlightedOrSelectedChanged = true;
|
||||
rendererArgsLock_.unlock();
|
||||
}
|
||||
pokeRenderer();
|
||||
}
|
||||
|
||||
void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)
|
||||
{
|
||||
rendererArgsLock_.lock();
|
||||
rendererArgs_->highlightedItems[group] = decals;
|
||||
{
|
||||
QMutexLocker locker(&rendererArgsLock_);
|
||||
rendererArgs_->highlightedDecals[group] = decals;
|
||||
rendererArgs_->highlightedOrSelectedChanged = true;
|
||||
rendererArgsLock_.unlock();
|
||||
}
|
||||
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 (!degree.isNull())
|
||||
zoom(degree.y());
|
||||
}
|
||||
|
||||
void FPGAViewWidget::zoom(int level)
|
||||
{
|
||||
if (zoom_ < zoomNear_) {
|
||||
zoom_ = zoomNear_;
|
||||
} else if (zoom_ < zoomLvl1_) {
|
||||
zoom_ -= degree.y() / 10.0;
|
||||
zoom_ -= level / 10.0;
|
||||
} else if (zoom_ < zoomLvl2_) {
|
||||
zoom_ -= degree.y() / 5.0;
|
||||
zoom_ -= level / 5.0;
|
||||
} else if (zoom_ < zoomFar_) {
|
||||
zoom_ -= degree.y();
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -159,6 +159,8 @@ void MainWindow::createMenu()
|
||||
taskToolBar->addAction(actionPlay);
|
||||
taskToolBar->addAction(actionPause);
|
||||
taskToolBar->addAction(actionStop);
|
||||
|
||||
createGraphicsBar();
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
236
gui/lineshader.cc
Normal file
236
gui/lineshader.cc
Normal file
@ -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
|
209
gui/lineshader.h
Normal file
209
gui/lineshader.h
Normal file
@ -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
|
BIN
gui/resources/shape_handles.png
Normal file
BIN
gui/resources/shape_handles.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 538 B |
BIN
gui/resources/shape_square.png
Normal file
BIN
gui/resources/shape_square.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 353 B |
BIN
gui/resources/zoom_in.png
Normal file
BIN
gui/resources/zoom_in.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 725 B |
BIN
gui/resources/zoom_out.png
Normal file
BIN
gui/resources/zoom_out.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 708 B |
255
ice40/arch.cc
255
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,13 +644,61 @@ 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++) {
|
||||
/* 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::G_LINE;
|
||||
el.x1 = x - 0.05, el.x2 = x + 0.05, el.y1 = y, el.y2 = y, el.z = 0;
|
||||
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, el.x2 = x, el.y1 = y - 0.05, el.y2 = y + 0.05, el.z = 0;
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -567,7 +707,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
||||
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;
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
15
ice40/gfx.cc
15
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
|
||||
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user