Merge branch 'redist_slack' into 'redist_slack'

Redist slack

See merge request eddiehung/nextpnr!10
This commit is contained in:
Eddie Hung 2018-07-27 05:54:18 +00:00
commit 56d551d407
71 changed files with 7372 additions and 1745 deletions

2
.gitignore vendored
View File

@ -25,3 +25,5 @@ build/
/Testing/* /Testing/*
CTestTestfile.cmake CTestTestfile.cmake
install_manifest.txt install_manifest.txt
/bbasm
/ImportExecutables.cmake

View File

@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
class QtPropertyPrivate class QtPropertyPrivate
{ {
public: public:
QtPropertyPrivate(QtAbstractPropertyManager *manager) : m_enabled(true), m_modified(false), m_manager(manager) {} QtPropertyPrivate(QtAbstractPropertyManager *manager) : m_enabled(true), m_selectable(true), m_modified(false), m_manager(manager) {}
QtProperty *q_ptr; QtProperty *q_ptr;
QSet<QtProperty *> m_parentItems; QSet<QtProperty *> m_parentItems;
@ -66,6 +66,7 @@ public:
QString m_name; QString m_name;
QString m_id; QString m_id;
bool m_enabled; bool m_enabled;
bool m_selectable;
bool m_modified; bool m_modified;
QtAbstractPropertyManager * const m_manager; QtAbstractPropertyManager * const m_manager;
@ -260,6 +261,11 @@ bool QtProperty::isEnabled() const
return d_ptr->m_enabled; return d_ptr->m_enabled;
} }
bool QtProperty::isSelectable() const
{
return d_ptr->m_selectable;
}
/*! /*!
Returns whether the property is modified. Returns whether the property is modified.
@ -409,6 +415,15 @@ void QtProperty::setEnabled(bool enable)
propertyChanged(); propertyChanged();
} }
void QtProperty::setSelectable(bool selectable)
{
if (d_ptr->m_selectable == selectable)
return;
d_ptr->m_selectable = selectable;
propertyChanged();
}
/*! /*!
Sets the property's modified state according to the passed \a modified value. Sets the property's modified state according to the passed \a modified value.

View File

@ -83,6 +83,7 @@ public:
QString propertyName() const; QString propertyName() const;
QString propertyId() const; QString propertyId() const;
bool isEnabled() const; bool isEnabled() const;
bool isSelectable() const;
bool isModified() const; bool isModified() const;
bool hasValue() const; bool hasValue() const;
@ -97,6 +98,7 @@ public:
void setPropertyName(const QString &text); void setPropertyName(const QString &text);
void setPropertyId(const QString &text); void setPropertyId(const QString &text);
void setEnabled(bool enable); void setEnabled(bool enable);
void setSelectable(bool selectable);
void setModified(bool modified); void setModified(bool modified);
bool isSubProperty()const; bool isSubProperty()const;

View File

@ -651,6 +651,11 @@ void QtTreePropertyBrowserPrivate::updateItem(QTreeWidgetItem *item)
else else
disableItem(item); disableItem(item);
} }
if (property->isSelectable()) {
item->setFlags(item->flags() | Qt::ItemIsSelectable);
} else {
item->setFlags(item->flags() & ~Qt::ItemIsSelectable);
}
m_treeWidget->viewport()->update(); m_treeWidget->viewport()->update();
} }

View File

@ -1339,6 +1339,7 @@ void addPropertyRecusively(QtVariantPropertyManager * manager,
newProp->setWhatsThis(prop->whatsThis()); newProp->setWhatsThis(prop->whatsThis());
newProp->setModified(prop->isModified()); newProp->setModified(prop->isModified());
newProp->setEnabled(prop->isEnabled()); newProp->setEnabled(prop->isEnabled());
newProp->setSelectable(prop->isSelectable());
newProp->setValue(prop->value()); newProp->setValue(prop->value());
foreach(QtProperty * subProp, prop->subProperties()) foreach(QtProperty * subProp, prop->subProperties())

View File

@ -167,6 +167,8 @@ if(MINGW)
add_definitions("-Wa,-mbig-obj") add_definitions("-Wa,-mbig-obj")
endif(MINGW) endif(MINGW)
include(bba/bba.cmake)
foreach (family ${ARCH}) foreach (family ${ARCH})
message(STATUS "Configuring architecture : ${family}") message(STATUS "Configuring architecture : ${family}")
string(TOUPPER ${family} ufamily) string(TOUPPER ${family} ufamily)

74
bba/README.md Normal file
View File

@ -0,0 +1,74 @@
Binary Blob Assembler (bba)
===========================
This tools read a text file describing binary data, and write that binary data
file. The usual flow is that the input to bbasm is generated by a python
script, and the output is linked or loaded into a C program, using (packed)
structs to interpret the binary data.
All references (pointers) are encoded als 32 bit byte offset relative to the
location of the pointer. This way the resulting binary blob is position
independent.
Valid commands for the input are as follows.
pre \<string\>
--------------
When a C file is generated as output, all the "pre" strings will be included
before the binary blob.
post \<string\>
---------------
When a C file is generated as output, all the "post" strings will be included
after the binary blob.
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. 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\>\]
------------------------------
Add a label for the current position.
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\>\]
----------------------------
Add a 8-bit value to the binary blob.
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\>\]
-----------------------------
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\>" \[\<comment\>\]
--------------------------------
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 `|`.

13
bba/bba.cmake Normal file
View File

@ -0,0 +1,13 @@
IF(CMAKE_CROSSCOMPILING)
SET(IMPORT_EXECUTABLES "IMPORTFILE-NOTFOUND" CACHE FILEPATH "Point it to the export file from a native build")
INCLUDE(${IMPORT_EXECUTABLES})
ENDIF(CMAKE_CROSSCOMPILING)
IF(NOT CMAKE_CROSSCOMPILING)
ADD_EXECUTABLE(bbasm bba/main.cc)
target_link_libraries(bbasm LINK_PUBLIC ${Boost_PROGRAM_OPTIONS_LIBRARY})
ENDIF(NOT CMAKE_CROSSCOMPILING)
IF(NOT CMAKE_CROSSCOMPILING)
EXPORT(TARGETS bbasm FILE ${CMAKE_BINARY_DIR}/ImportExecutables.cmake )
ENDIF(NOT CMAKE_CROSSCOMPILING)

427
bba/main.cc Normal file
View File

@ -0,0 +1,427 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 Miodrag Milanovic <miodrag@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 <assert.h>
#include <boost/program_options.hpp>
#include <iostream>
#include <map>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
enum TokenType : int8_t
{
TOK_LABEL,
TOK_REF,
TOK_U8,
TOK_U16,
TOK_U32
};
struct Stream
{
std::string name;
std::vector<TokenType> tokenTypes;
std::vector<uint32_t> tokenValues;
std::vector<std::string> tokenComments;
};
Stream stringStream;
std::vector<Stream> streams;
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;
const char *skipWhitespace(const char *p)
{
if (p == nullptr)
return "";
while (*p == ' ' || *p == '\t')
p++;
return p;
}
int main(int argc, char **argv)
{
bool debug = false;
bool verbose = false;
bool bigEndian = false;
bool writeC = false;
char buffer[512];
namespace po = boost::program_options;
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");
pos.add("files", -1);
po::variables_map vm;
try {
po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).positional(pos).run();
po::store(parsed, vm);
po::notify(vm);
} catch (std::exception &e) {
std::cout << e.what() << "\n";
return 1;
}
if (vm.count("v"))
verbose = true;
if (vm.count("d"))
debug = true;
if (vm.count("b"))
bigEndian = true;
if (vm.count("c"))
writeC = true;
if (vm.count("files") == 0) {
printf("File parameters are mandatory\n");
exit(-1);
}
std::vector<std::string> files = vm["files"].as<std::vector<std::string>>();
if (files.size() != 2) {
printf("Input and output parameters must be set\n");
exit(-1);
}
FILE *fileIn = fopen(files.at(0).c_str(), "rt");
assert(fileIn != nullptr);
FILE *fileOut = fopen(files.at(1).c_str(), writeC ? "wt" : "wb");
assert(fileOut != nullptr);
while (fgets(buffer, 512, fileIn) != nullptr) {
std::string cmd = strtok(buffer, " \t\r\n");
if (cmd == "pre") {
const char *p = skipWhitespace(strtok(nullptr, "\r\n"));
preText.push_back(p);
continue;
}
if (cmd == "post") {
const char *p = skipWhitespace(strtok(nullptr, "\r\n"));
postText.push_back(p);
continue;
}
if (cmd == "push") {
const char *p = strtok(nullptr, " \t\r\n");
if (streamIndex.count(p) == 0) {
streamIndex[p] = streams.size();
streams.resize(streams.size() + 1);
streams.back().name = p;
}
streamStack.push_back(streamIndex.at(p));
continue;
}
if (cmd == "pop") {
streamStack.pop_back();
continue;
}
if (cmd == "label" || cmd == "ref") {
const char *label = strtok(nullptr, " \t\r\n");
const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
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 (debug)
s.tokenComments.push_back(comment);
continue;
}
if (cmd == "u8" || cmd == "u16" || cmd == "u32") {
const char *value = strtok(nullptr, " \t\r\n");
const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
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 (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 (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++;
}
continue;
}
assert(0);
}
if (verbose) {
printf("Constructed %d streams:\n", int(streams.size()));
for (auto &s : streams)
printf(" stream '%s' with %d tokens\n", s.name.c_str(), int(s.tokenTypes.size()));
}
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) {
for (int i = 0; i < int(s.tokenTypes.size()); i++) {
switch (s.tokenTypes[i]) {
case TOK_LABEL:
labels[s.tokenValues[i]] = cursor;
break;
case TOK_REF:
cursor += 4;
break;
case TOK_U8:
cursor += 1;
break;
case TOK_U16:
assert(cursor % 2 == 0);
cursor += 2;
break;
case TOK_U32:
assert(cursor % 4 == 0);
cursor += 4;
break;
default:
assert(0);
}
}
}
if (verbose) {
printf("resolved positions for %d labels.\n", int(labels.size()));
printf("total data (including strings): %.2f MB\n", double(cursor) / (1024 * 1024));
}
std::vector<uint8_t> data(cursor);
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;
switch (s.tokenTypes[i]) {
case TOK_LABEL:
break;
case TOK_REF:
value = labels[value] - cursor;
numBytes = 4;
break;
case TOK_U8:
numBytes = 1;
break;
case TOK_U16:
numBytes = 2;
break;
case TOK_U32:
numBytes = 4;
break;
default:
assert(0);
}
if (bigEndian) {
switch (numBytes) {
case 4:
data[cursor++] = value >> 24;
data[cursor++] = value >> 16;
/* fall-through */
case 2:
data[cursor++] = value >> 8;
/* fall-through */
case 1:
data[cursor++] = value;
/* fall-through */
case 0:
break;
default:
assert(0);
}
} else {
switch (numBytes) {
case 4:
data[cursor + 3] = value >> 24;
data[cursor + 2] = value >> 16;
/* fall-through */
case 2:
data[cursor + 1] = value >> 8;
/* fall-through */
case 1:
data[cursor] = value;
/* fall-through */
case 0:
break;
default:
assert(0);
}
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);
}
}
}
}
assert(cursor == int(data.size()));
if (writeC) {
for (auto &s : preText)
fprintf(fileOut, "%s\n", s.c_str());
fprintf(fileOut, "const char %s[%d] =\n\"", streams[0].name.c_str(), int(data.size()) + 1);
cursor = 1;
for (int i = 0; i < int(data.size()); i++) {
auto d = data[i];
if (cursor > 70) {
fputc('\"', fileOut);
fputc('\n', fileOut);
cursor = 0;
}
if (cursor == 0) {
fputc('\"', fileOut);
cursor = 1;
}
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);
cursor += 2;
} else {
fputc(d, fileOut);
cursor++;
}
}
fprintf(fileOut, "\";\n");
for (auto &s : postText)
fprintf(fileOut, "%s\n", s.c_str());
} else {
fwrite(data.data(), int(data.size()), 1, fileOut);
}
return 0;
}

View File

@ -17,8 +17,8 @@
* *
*/ */
#include "nextpnr.h"
#include "log.h" #include "log.h"
#include "nextpnr.h"
#if 0 #if 0
#define dbg(...) log(__VA_ARGS__) #define dbg(...) log(__VA_ARGS__)
@ -84,8 +84,7 @@ void archcheck_locs(const Context *ctx)
log_info("Checking all locations..\n"); log_info("Checking all locations..\n");
for (int x = 0; x < ctx->getGridDimX(); x++) for (int x = 0; x < ctx->getGridDimX(); x++)
for (int y = 0; y < ctx->getGridDimY(); y++) for (int y = 0; y < ctx->getGridDimY(); y++) {
{
dbg("> %d %d\n", x, y); dbg("> %d %d\n", x, y);
std::unordered_set<int> usedz; std::unordered_set<int> usedz;

View File

@ -23,10 +23,10 @@
#include <condition_variable> #include <condition_variable>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <pthread.h>
#include <stdexcept> #include <stdexcept>
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <thread>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@ -79,19 +79,19 @@ class assertion_failure : public std::runtime_error
}; };
NPNR_NORETURN NPNR_NORETURN
inline bool assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line) inline void assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
{ {
throw assertion_failure(message, expr_str, filename, line); throw assertion_failure(message, expr_str, filename, line);
} }
NPNR_NORETURN NPNR_NORETURN
inline bool assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line) inline void assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
{ {
throw assertion_failure(message, expr_str, filename, line); throw assertion_failure(message, expr_str, filename, line);
} }
#define NPNR_ASSERT(cond) ((void)((cond) || (assert_fail_impl(#cond, #cond, __FILE__, __LINE__)))) #define NPNR_ASSERT(cond) (!(cond) ? assert_fail_impl(#cond, #cond, __FILE__, __LINE__) : (void)true)
#define NPNR_ASSERT_MSG(cond, msg) ((void)((cond) || (assert_fail_impl(msg, #cond, __FILE__, __LINE__)))) #define NPNR_ASSERT_MSG(cond, msg) (!(cond) ? assert_fail_impl(msg, #cond, __FILE__, __LINE__) : (void)true)
#define NPNR_ASSERT_FALSE(msg) (assert_fail_impl(msg, "false", __FILE__, __LINE__)) #define NPNR_ASSERT_FALSE(msg) (assert_fail_impl(msg, "false", __FILE__, __LINE__))
#define NPNR_ASSERT_FALSE_STR(msg) (assert_fail_impl_str(msg, "false", __FILE__, __LINE__)) #define NPNR_ASSERT_FALSE_STR(msg) (assert_fail_impl_str(msg, "false", __FILE__, __LINE__))
@ -145,20 +145,25 @@ struct GraphicElement
{ {
enum type_t enum type_t
{ {
G_NONE, TYPE_NONE,
G_LINE, TYPE_LINE,
G_BOX, TYPE_ARROW,
G_CIRCLE, TYPE_BOX,
G_LABEL TYPE_CIRCLE,
} type = G_NONE; TYPE_LABEL,
TYPE_MAX
} type = TYPE_NONE;
enum style_t enum style_t
{ {
G_FRAME, STYLE_FRAME, // Static "frame". Contrast between STYLE_INACTIVE and STYLE_ACTIVE
G_HIDDEN, STYLE_HIDDEN, // Only display when object is selected or highlighted
G_INACTIVE, STYLE_INACTIVE, // Render using low-contrast color
G_ACTIVE, STYLE_ACTIVE, // Render using high-contast color
} style = G_FRAME;
STYLE_MAX
} style = STYLE_FRAME;
float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0; float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
std::string text; std::string text;
@ -272,6 +277,16 @@ struct CellInfo : ArchCellInfo
// cell_port -> bel_pin // cell_port -> bel_pin
std::unordered_map<IdString, IdString> pins; std::unordered_map<IdString, IdString> pins;
// placement constraints
CellInfo *constr_parent;
std::vector<CellInfo *> constr_children;
const int UNCONSTR = INT_MIN;
int constr_x = UNCONSTR; // this.x - parent.x
int constr_y = UNCONSTR; // this.y - parent.y
int constr_z = UNCONSTR; // this.z - parent.z
bool constr_abs_z = false; // parent.z := 0
// parent.[xyz] := 0 when (constr_parent == nullptr)
}; };
struct DeterministicRNG struct DeterministicRNG
@ -343,7 +358,7 @@ struct BaseCtx
{ {
// Lock to perform mutating actions on the Context. // Lock to perform mutating actions on the Context.
std::mutex mutex; std::mutex mutex;
pthread_t mutex_owner; std::thread::id mutex_owner;
// Lock to be taken by UI when wanting to access context - the yield() // Lock to be taken by UI when wanting to access context - the yield()
// method will lock/unlock it when its' released the main mutex to make // method will lock/unlock it when its' released the main mutex to make
@ -376,12 +391,12 @@ struct BaseCtx
void lock(void) void lock(void)
{ {
mutex.lock(); mutex.lock();
mutex_owner = pthread_self(); mutex_owner = std::this_thread::get_id();
} }
void unlock(void) void unlock(void)
{ {
NPNR_ASSERT(pthread_equal(pthread_self(), mutex_owner) != 0); NPNR_ASSERT(std::this_thread::get_id() == mutex_owner);
mutex.unlock(); mutex.unlock();
} }
@ -455,6 +470,7 @@ struct Context : Arch, DeterministicRNG
bool force = false; bool force = false;
bool timing_driven = true; bool timing_driven = true;
float target_freq = 12e6; float target_freq = 12e6;
bool user_freq = false;
Context(ArchArgs args) : Arch(args) {} Context(ArchArgs args) : Arch(args) {}

View File

@ -28,19 +28,20 @@ NEXTPNR_NAMESPACE_BEGIN
wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns) wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns)
{ {
wirelen_t wirelength = 0; wirelen_t wirelength = 0;
int driver_x, driver_y; Loc driver_loc;
bool driver_gb; bool driver_gb;
CellInfo *driver_cell = net->driver.cell; CellInfo *driver_cell = net->driver.cell;
if (!driver_cell) if (!driver_cell)
return 0; return 0;
if (driver_cell->bel == BelId()) if (driver_cell->bel == BelId())
return 0; return 0;
ctx->estimatePosition(driver_cell->bel, driver_x, driver_y, driver_gb); driver_gb = ctx->getBelGlobalBuf(driver_cell->bel);
driver_loc = ctx->getBelLocation(driver_cell->bel);
WireId drv_wire = ctx->getBelPinWire(driver_cell->bel, ctx->portPinFromId(net->driver.port)); WireId drv_wire = ctx->getBelPinWire(driver_cell->bel, ctx->portPinFromId(net->driver.port));
if (driver_gb) if (driver_gb)
return 0; return 0;
float worst_slack = 1000; float worst_slack = 1000;
int xmin = driver_x, xmax = driver_x, ymin = driver_y, ymax = driver_y; int xmin = driver_loc.x, xmax = driver_loc.x, ymin = driver_loc.y, ymax = driver_loc.y;
for (auto load : net->users) { for (auto load : net->users) {
if (load.cell == nullptr) if (load.cell == nullptr)
continue; continue;
@ -56,15 +57,14 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
worst_slack = std::min(slack, worst_slack); worst_slack = std::min(slack, worst_slack);
} }
int load_x, load_y; if (ctx->getBelGlobalBuf(load_cell->bel))
bool load_gb;
ctx->estimatePosition(load_cell->bel, load_x, load_y, load_gb);
if (load_gb)
continue; continue;
xmin = std::min(xmin, load_x); Loc load_loc = ctx->getBelLocation(load_cell->bel);
ymin = std::min(ymin, load_y);
xmax = std::max(xmax, load_x); xmin = std::min(xmin, load_loc.x);
ymax = std::max(ymax, load_y); ymin = std::min(ymin, load_loc.y);
xmax = std::max(xmax, load_loc.x);
ymax = std::max(ymax, load_loc.y);
} }
if (ctx->timing_driven && type == MetricType::COST) { if (ctx->timing_driven && type == MetricType::COST) {
wirelength = wirelen_t((((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-worst_slack / 5))))); wirelength = wirelen_t((((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-worst_slack / 5)))));

View File

@ -50,9 +50,7 @@ class SAPlacer
{ {
int num_bel_types = 0; int num_bel_types = 0;
for (auto bel : ctx->getBels()) { for (auto bel : ctx->getBels()) {
int x, y; Loc loc = ctx->getBelLocation(bel);
bool gb;
ctx->estimatePosition(bel, x, y, gb);
BelType type = ctx->getBelType(bel); BelType type = ctx->getBelType(bel);
int type_idx; int type_idx;
if (bel_types.find(type) == bel_types.end()) { if (bel_types.find(type) == bel_types.end()) {
@ -63,13 +61,13 @@ class SAPlacer
} }
if (int(fast_bels.size()) < type_idx + 1) if (int(fast_bels.size()) < type_idx + 1)
fast_bels.resize(type_idx + 1); fast_bels.resize(type_idx + 1);
if (int(fast_bels.at(type_idx).size()) < (x + 1)) if (int(fast_bels.at(type_idx).size()) < (loc.x + 1))
fast_bels.at(type_idx).resize(x + 1); fast_bels.at(type_idx).resize(loc.x + 1);
if (int(fast_bels.at(type_idx).at(x).size()) < (y + 1)) if (int(fast_bels.at(type_idx).at(loc.x).size()) < (loc.y + 1))
fast_bels.at(type_idx).at(x).resize(y + 1); fast_bels.at(type_idx).at(loc.x).resize(loc.y + 1);
max_x = std::max(max_x, x); max_x = std::max(max_x, loc.x);
max_y = std::max(max_y, y); max_y = std::max(max_y, loc.y);
fast_bels.at(type_idx).at(x).at(y).push_back(bel); fast_bels.at(type_idx).at(loc.x).at(loc.y).push_back(bel);
} }
diameter = std::max(max_x, max_y) + 1; diameter = std::max(max_x, max_y) + 1;
} }
@ -96,7 +94,13 @@ class SAPlacer
BelType bel_type = ctx->getBelType(bel); BelType bel_type = ctx->getBelType(bel);
if (bel_type != ctx->belTypeFromId(cell->type)) { if (bel_type != ctx->belTypeFromId(cell->type)) {
log_error("Bel \'%s\' of type \'%s\' does not match cell " log_error("Bel \'%s\' of type \'%s\' does not match cell "
"\'%s\' of type \'%s\'", "\'%s\' of type \'%s\'\n",
loc_name.c_str(), ctx->belTypeToId(bel_type).c_str(ctx), cell->name.c_str(ctx),
cell->type.c_str(ctx));
}
if (!ctx->isValidBelForCell(cell, bel)) {
log_error("Bel \'%s\' of type \'%s\' is not valid for cell "
"\'%s\' of type \'%s\'\n",
loc_name.c_str(), ctx->belTypeToId(bel_type).c_str(ctx), cell->name.c_str(ctx), loc_name.c_str(), ctx->belTypeToId(bel_type).c_str(ctx), cell->name.c_str(ctx),
cell->type.c_str(ctx)); cell->type.c_str(ctx));
} }
@ -235,8 +239,7 @@ class SAPlacer
diameter *= post_legalise_dia_scale; diameter *= post_legalise_dia_scale;
ctx->shuffle(autoplaced); ctx->shuffle(autoplaced);
assign_budget(ctx); assign_budget(ctx);
} } else {
else {
update_budget(ctx); update_budget(ctx);
} }
@ -272,6 +275,7 @@ class SAPlacer
} }
} }
} }
compute_fmax(ctx, true /* print_fmax */);
ctx->unlock(); ctx->unlock();
return true; return true;
} }
@ -387,8 +391,6 @@ class SAPlacer
// SA acceptance criterea // SA acceptance criterea
if (delta < 0 || (temp > 1e-6 && (ctx->rng() / float(0x3fffffff)) <= std::exp(-delta / temp))) { if (delta < 0 || (temp > 1e-6 && (ctx->rng() / float(0x3fffffff)) <= std::exp(-delta / temp))) {
n_accept++; n_accept++;
//if (delta < 2)
// improved = true;
} else { } else {
if (other != IdString()) if (other != IdString())
ctx->unbindBel(oldBel); ctx->unbindBel(oldBel);
@ -413,12 +415,10 @@ class SAPlacer
BelId random_bel_for_cell(CellInfo *cell) BelId random_bel_for_cell(CellInfo *cell)
{ {
BelType targetType = ctx->belTypeFromId(cell->type); BelType targetType = ctx->belTypeFromId(cell->type);
int x, y; Loc curr_loc = ctx->getBelLocation(cell->bel);
bool gb;
ctx->estimatePosition(cell->bel, x, y, gb);
while (true) { while (true) {
int nx = ctx->rng(2 * diameter + 1) + std::max(x - diameter, 0); int nx = ctx->rng(2 * diameter + 1) + std::max(curr_loc.x - diameter, 0);
int ny = ctx->rng(2 * diameter + 1) + std::max(y - diameter, 0); int ny = ctx->rng(2 * diameter + 1) + std::max(curr_loc.y - diameter, 0);
int beltype_idx = bel_types.at(targetType); int beltype_idx = bel_types.at(targetType);
if (nx >= int(fast_bels.at(beltype_idx).size())) if (nx >= int(fast_bels.at(beltype_idx).size()))
continue; continue;

View File

@ -613,38 +613,10 @@ bool router1(Context *ctx)
std::unordered_set<IdString> normalRouteNets, ripupQueue; std::unordered_set<IdString> normalRouteNets, ripupQueue;
if (iterCnt == 1) { if (ctx->verbose || iterCnt == 1)
if (ctx->verbose) log_info("routing queue contains %d jobs.\n", int(jobQueue.size()));
log_info("routing queue contains %d jobs.\n", int(jobQueue.size()));
} else { update_budget(ctx);
static auto actual_delay = [](Context *ctx, WireId src, WireId dst) {
delay_t total_delay = 0;
WireId last = dst;
auto net_name = ctx->getBoundWireNet(src);
if (net_name != IdString()) {
auto net = ctx->nets.at(net_name).get();
while (last != src) {
total_delay += ctx->getWireDelay(last).maxDelay();
auto pip = net->wires.at(last).pip;
NPNR_ASSERT(ctx->getBoundPipNet(pip) == net_name);
total_delay += ctx->getPipDelay(pip).maxDelay();
last = ctx->getPipSrcWire(pip);
if (ctx->getBoundWireNet(last) != net_name) {
log_warning("Wire %s bound to %s not %s!\n", ctx->getWireName(last).c_str(ctx), ctx->getBoundWireNet(last).c_str(ctx), net_name.c_str(ctx));
break;
}
NPNR_ASSERT(ctx->getBoundWireNet(last) == net_name);
}
NPNR_ASSERT(last != WireId());
}
if (last != src)
total_delay += ctx->estimateDelay(src, last);
else
total_delay += ctx->getWireDelay(last).maxDelay();
return total_delay;
};
update_budget(ctx, actual_delay);
}
bool printNets = ctx->verbose && (jobQueue.size() < 10); bool printNets = ctx->verbose && (jobQueue.size() < 10);
@ -841,14 +813,15 @@ bool router1(Context *ctx)
log_info("Checksum: 0x%08x\n", ctx->checksum()); log_info("Checksum: 0x%08x\n", ctx->checksum());
#ifndef NDEBUG #ifndef NDEBUG
ctx->check(); ctx->check();
ctx->unlock();
#endif #endif
compute_fmax(ctx, true /* print_fmax */, true /* print_path */);
ctx->unlock();
return true; return true;
} catch (log_execution_error_exception) { } catch (log_execution_error_exception) {
#ifndef NDEBUG #ifndef NDEBUG
ctx->check(); ctx->check();
ctx->unlock();
#endif #endif
ctx->unlock();
return false; return false;
} }
} }

View File

@ -26,16 +26,26 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack); typedef std::unordered_map<const PortInfo *, delay_t> UpdateMap;
typedef std::list<const PortRef *> PortRefList;
static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack, UpdateMap *updates,
delay_t &min_slack, PortRefList *current_path, PortRefList *crit_path);
// Follow a path, returning budget to annotate // Follow a path, returning budget to annotate
static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, delay_t slack) static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, delay_t slack, UpdateMap *updates,
delay_t &min_slack, PortRefList *current_path, PortRefList *crit_path)
{ {
delay_t value; delay_t value;
if (ctx->getPortClock(user.cell, user.port) != IdString()) { if (ctx->getPortClock(user.cell, user.port) != IdString()) {
// At the end of a timing path (arguably, should check setup time // At the end of a timing path (arguably, should check setup time
// here too) // here too)
value = slack / path_length; value = slack / path_length;
if (slack < min_slack) {
min_slack = slack;
if (crit_path)
*crit_path = *current_path;
}
} else { } else {
// Default to the path ending here, if no further paths found // Default to the path ending here, if no further paths found
value = slack / path_length; value = slack / path_length;
@ -48,7 +58,8 @@ static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, de
if (is_path) { if (is_path) {
NetInfo *net = port.second.net; NetInfo *net = port.second.net;
if (net) { if (net) {
delay_t path_budget = follow_net(ctx, net, path_length, slack - comb_delay); delay_t path_budget = follow_net(ctx, net, path_length, slack - comb_delay, updates, min_slack,
current_path, crit_path);
value = std::min(value, path_budget); value = std::min(value, path_budget);
} }
} }
@ -56,21 +67,67 @@ static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, de
} }
} }
if (value < user.budget) { if (updates) {
user.budget = value; auto ret = updates->emplace(&user.cell->ports.at(user.port), value);
if (!ret.second)
ret.first->second = std::min(value, ret.first->second);
} }
return value; return value;
} }
static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack) static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack, UpdateMap *updates,
delay_t &min_slack, PortRefList *current_path, PortRefList *crit_path)
{ {
delay_t net_budget = slack / (path_length + 1); delay_t net_budget = slack / (path_length + 1);
for (auto &usr : net->users) { for (unsigned i = 0; i < net->users.size(); ++i) {
net_budget = std::min(net_budget, follow_user_port(ctx, usr, path_length + 1, slack)); auto &usr = net->users[i];
if (crit_path)
current_path->push_back(&usr);
// If budget override is less than existing budget, then do not increment path length
int pl = path_length + 1;
auto budget = ctx->getBudgetOverride(net, i, net_budget);
if (budget < net_budget) {
net_budget = budget;
pl = std::max(1, path_length);
}
net_budget = std::min(net_budget,
follow_user_port(ctx, usr, pl, slack - ctx->getNetinfoRouteDelay(net, i),
updates, min_slack, current_path, crit_path));
if (crit_path)
current_path->pop_back();
} }
return net_budget; return net_budget;
} }
static delay_t compute_min_slack(Context *ctx, UpdateMap *updates, PortRefList *crit_path)
{
delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
delay_t min_slack = default_slack;
PortRefList current_path;
// Go through all clocked drivers and distribute the available path
// slack evenly into the budget of every sink on the path ---
// record this value into the UpdateMap
for (auto &cell : ctx->cells) {
for (auto port : cell.second->ports) {
if (port.second.type == PORT_OUT) {
IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
if (clock_domain != IdString()) {
delay_t slack = default_slack; // TODO: clock constraints
delay_t clkToQ;
if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ))
slack -= clkToQ;
if (port.second.net)
follow_net(ctx, port.second.net, 0, slack, updates, min_slack, &current_path, crit_path);
}
}
}
}
return min_slack;
}
void assign_budget(Context *ctx) void assign_budget(Context *ctx)
{ {
log_break(); log_break();
@ -82,24 +139,35 @@ void assign_budget(Context *ctx)
usr.budget = default_slack; usr.budget = default_slack;
} }
} }
// Go through all clocked drivers and set up paths
for (auto &cell : ctx->cells) { UpdateMap updates;
for (auto port : cell.second->ports) { delay_t min_slack = compute_min_slack(ctx, &updates, nullptr);
if (port.second.type == PORT_OUT) {
IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first); // If user has not specified a frequency, adjust the target frequency dynamically
if (clock_domain != IdString()) { // TODO(eddieh): Tune these factors
delay_t slack = delay_t(1.0e12 / ctx->target_freq); // TODO: clock constraints if (!ctx->user_freq) {
if (port.second.net) if (min_slack < 0)
follow_net(ctx, port.second.net, 0, slack); ctx->target_freq = 1e12 / (default_slack - 0.95 * min_slack);
} else
} ctx->target_freq = 1e12 / (default_slack - 1.2 * min_slack);
} if (ctx->verbose)
log_info("minimum slack for this assign = %d, target Fmax for next update = %.2f MHz\n", min_slack,
ctx->target_freq / 1e6);
} }
// Post-allocation check // Update the budgets
for (auto &net : ctx->nets) { for (auto &net : ctx->nets) {
for (auto user : net.second->users) { for (size_t i = 0; i < net.second->users.size(); ++i) {
if (user.budget < 0) auto &user = net.second->users[i];
auto pi = &user.cell->ports.at(user.port);
auto it = updates.find(pi);
if (it == updates.end())
continue;
auto budget = ctx->getNetinfoRouteDelay(net.second.get(), i) - it->second;
user.budget = ctx->getBudgetOverride(net.second.get(), i, budget);
// Post-update check
if (ctx->user_freq && user.budget < 0)
log_warning("port %s.%s, connected to net '%s', has negative " log_warning("port %s.%s, connected to net '%s', has negative "
"timing budget of %fns\n", "timing budget of %fns\n",
user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx), user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
@ -115,112 +183,25 @@ void assign_budget(Context *ctx)
log_info("Checksum: 0x%08x\n", ctx->checksum()); log_info("Checksum: 0x%08x\n", ctx->checksum());
} }
typedef std::unordered_map<const PortInfo*, delay_t> updates_t; void update_budget(Context *ctx)
typedef std::unordered_map<const PortInfo*, delay_t> delays_t;
static delay_t follow_net_update(Context *ctx, NetInfo *net, int path_length, delay_t slack, const delays_t& delays, updates_t& updates);
// Follow a path, returning budget to annotate
static delay_t follow_user_port_update(Context *ctx, PortRef &user, int path_length, delay_t slack, const delays_t& delays, updates_t& updates)
{ {
delay_t value; UpdateMap updates;
if (ctx->getPortClock(user.cell, user.port) != IdString()) { delay_t min_slack = compute_min_slack(ctx, &updates, nullptr);
// At the end of a timing path (arguably, should check setup time
// here too)
value = slack / path_length;
} else {
// Default to the path ending here, if no further paths found
value = slack / path_length;
// Follow outputs of the user
for (auto& port : user.cell->ports) {
if (port.second.type == PORT_OUT) {
delay_t comb_delay;
// Look up delay through this path
bool is_path = ctx->getCellDelay(user.cell, user.port, port.first, comb_delay);
if (is_path) {
NetInfo *net = port.second.net;
if (net) {
delay_t path_budget = follow_net_update(ctx, net, path_length, slack - comb_delay, delays, updates);
value = std::min(value, path_budget);
}
}
}
}
}
auto ret = updates.emplace(&user.cell->ports.at(user.port), value);
if (!ret.second && value < ret.first->second) {
ret.first->second = value;
}
return value;
}
static delay_t follow_net_update(Context *ctx, NetInfo *net, int path_length, delay_t slack, const delays_t& delays,updates_t& updates)
{
delay_t net_budget = slack / (path_length + 1);
for (auto& usr : net->users) {
net_budget = std::min(net_budget, follow_user_port_update(ctx, usr, path_length + 1, slack - get_or_default(delays, &usr.cell->ports.at(usr.port), 0.), delays, updates));
}
return net_budget;
}
void update_budget(Context *ctx, std::function<delay_t(Context*,WireId,WireId)> delay_fn)
{
delays_t delays;
updates_t updates;
// Compute the delay for every pin on every net
for (auto &n : ctx->nets) {
auto net = n.second.get();
int driver_x, driver_y;
bool driver_gb;
CellInfo *driver_cell = net->driver.cell;
if (!driver_cell)
continue;
if (driver_cell->bel == BelId())
continue;
ctx->estimatePosition(driver_cell->bel, driver_x, driver_y, driver_gb);
WireId drv_wire = ctx->getWireBelPin(driver_cell->bel, ctx->portPinFromId(net->driver.port));
if (driver_gb)
continue;
for (auto& load : net->users) {
if (load.cell == nullptr)
continue;
CellInfo *load_cell = load.cell;
if (load_cell->bel == BelId())
continue;
WireId user_wire = ctx->getWireBelPin(load_cell->bel, ctx->portPinFromId(load.port));
delay_t raw_wl = delay_fn(ctx, drv_wire, user_wire);
delays.emplace(&load_cell->ports.at(load.port), raw_wl);
}
}
// Go through all clocked drivers and distribute the available path slack evenly into every budget
for (auto &cell : ctx->cells) {
for (auto& port : cell.second->ports) {
if (port.second.type == PORT_OUT) {
IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
if (clock_domain != IdString()) {
if (port.second.net)
follow_net_update(ctx, port.second.net, 0, delay_t(1.0e12 / ctx->target_freq) - get_or_default(delays, &port.second, 0.), delays, updates);
}
}
}
}
// Update the budgets // Update the budgets
for (auto &net : ctx->nets) { for (auto &net : ctx->nets) {
for (auto& user : net.second->users) { for (size_t i = 0; i < net.second->users.size(); ++i) {
auto &user = net.second->users[i];
auto pi = &user.cell->ports.at(user.port); auto pi = &user.cell->ports.at(user.port);
auto budget = ctx->getNetinfoRouteDelay(net.second.get(), i);
auto it = updates.find(pi); auto it = updates.find(pi);
if (it == updates.end()) continue; if (it != updates.end())
auto budget = delays.at(pi) + it->second; budget -= it->second;
user.budget = ctx->getBudgetOverride(net.second->driver, budget); user.budget = ctx->getBudgetOverride(net.second.get(), i, budget);
// Post-update check // Post-update check
if (ctx->verbose) { if (ctx->verbose) {
if (user.budget < 0) if (ctx->user_freq && user.budget < 0)
log_warning("port %s.%s, connected to net '%s', has negative " log_warning("port %s.%s, connected to net '%s', has negative "
"timing budget of %fns\n", "timing budget of %fns\n",
user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx), user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
@ -233,6 +214,59 @@ void update_budget(Context *ctx, std::function<delay_t(Context*,WireId,WireId)>
} }
} }
} }
// If user has not specified a frequency, adjust the frequency dynamically:
if (!ctx->user_freq) {
delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
ctx->target_freq = 1e12 / (default_slack - min_slack);
if (ctx->verbose)
log_info("minimum slack for this update = %d, target Fmax for next update = %.2f MHz\n", min_slack,
ctx->target_freq / 1e6);
}
}
void compute_fmax(Context *ctx, bool print_fmax, bool print_path)
{
delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
PortRefList crit_path;
delay_t min_slack = compute_min_slack(ctx, nullptr, &crit_path);
if (print_path) {
delay_t total = 0;
log_break();
log_info("Critical path report:\n");
log_info("curr total\n");
auto &front = crit_path.front();
auto &front_port = front->cell->ports.at(front->port);
auto &front_driver = front_port.net->driver;
auto last_port = ctx->getPortClock(front_driver.cell, front_driver.port);
for (auto sink : crit_path) {
auto sink_cell = sink->cell;
auto &port = sink_cell->ports.at(sink->port);
auto net = port.net;
unsigned i = 0;
for (auto &usr : net->users)
if (&usr == sink) break;
else ++i;
auto &driver = net->driver;
auto driver_cell = driver.cell;
delay_t comb_delay;
ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
total += comb_delay;
log_info("%4d %4d Source %s.%s\n", comb_delay, total, driver_cell->name.c_str(ctx),
driver.port.c_str(ctx));
delay_t net_delay = ctx->getNetinfoRouteDelay(net, i);
total += net_delay;
auto driver_loc = ctx->getBelLocation(driver_cell->bel);
auto sink_loc = ctx->getBelLocation(sink_cell->bel);
log_info("%4d %4d Net %s budget %d (%d,%d) -> (%d,%d)\n", net_delay, total, net->name.c_str(ctx),
sink->budget, driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y);
log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx));
last_port = sink->port;
}
log_break();
}
if (print_fmax)
log_info("estimated Fmax = %.2f MHz\n", 1e6 / (default_slack - min_slack));
} }
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -27,7 +27,10 @@ NEXTPNR_NAMESPACE_BEGIN
// Assign "budget" values for all user ports in the design // Assign "budget" values for all user ports in the design
void assign_budget(Context *ctx); void assign_budget(Context *ctx);
void update_budget(Context *ctx, std::function<delay_t(Context*,WireId,WireId)> delay_fn=&Context::estimateDelay); // Evenly redistribute the total path slack amongst all sinks on each path
void update_budget(Context *ctx);
void compute_fmax(Context *ctx, bool print_fmax = false, bool print_path = false);
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -192,17 +192,20 @@ BelId Arch::getBelByName(IdString name) const
return ret; return ret;
} }
BelRange Arch::getBelsAtSameTile(BelId bel) const BelRange Arch::getBelsByTile(int x, int y) const
{ {
BelRange br; BelRange br;
NPNR_ASSERT(bel != BelId());
br.b.cursor_tile = bel.location.y * chip_info->width + bel.location.x; br.b.cursor_tile = y * chip_info->width + x;
br.e.cursor_tile = bel.location.y * chip_info->width + bel.location.x; br.e.cursor_tile = y * chip_info->width + x;
br.b.cursor_index = 0; br.b.cursor_index = 0;
br.e.cursor_index = locInfo(bel)->num_bels - 1; br.e.cursor_index = chip_info->locations[chip_info->location_type[br.b.cursor_tile]].num_bels - 1;
br.b.chip = chip_info; br.b.chip = chip_info;
br.e.chip = chip_info; br.e.chip = chip_info;
++br.e; if (br.e.cursor_index == -1)
++br.e.cursor_index;
else
++br.e;
return br; return br;
} }
@ -278,6 +281,7 @@ PipId Arch::getPipByName(IdString name) const
Location loc; Location loc;
std::string basename; std::string basename;
std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this));
ret.location = loc;
const LocationTypePOD *loci = locInfo(ret); const LocationTypePOD *loci = locInfo(ret);
for (int i = 0; i < loci->num_pips; i++) { for (int i = 0; i < loci->num_pips; i++) {
PipId curr; PipId curr;
@ -285,6 +289,8 @@ PipId Arch::getPipByName(IdString name) const
curr.index = i; curr.index = i;
pip_by_name[getPipName(curr)] = curr; pip_by_name[getPipName(curr)] = curr;
} }
if (pip_by_name.find(name) == pip_by_name.end())
NPNR_ASSERT_FALSE_STR("no pip named " + name.str(this));
return pip_by_name[name]; return pip_by_name[name];
} }
@ -322,13 +328,52 @@ BelId Arch::getPackagePinBel(const std::string &pin) const
std::string Arch::getBelPackagePin(BelId bel) const std::string Arch::getBelPackagePin(BelId bel) const
{ {
for (int i = 0; i < package_info->num_pins; i++) { for (int i = 0; i < package_info->num_pins; i++) {
if (package_info->pin_data[i].abs_loc == bel.location && package_info->pin_data[i].bel_index == bel.index) { if (Location(package_info->pin_data[i].abs_loc) == bel.location &&
package_info->pin_data[i].bel_index == bel.index) {
return package_info->pin_data[i].name.get(); return package_info->pin_data[i].name.get();
} }
} }
return ""; return "";
} }
int Arch::getPioBelBank(BelId bel) const
{
for (int i = 0; i < chip_info->num_pios; i++) {
if (Location(chip_info->pio_info[i].abs_loc) == bel.location && chip_info->pio_info[i].bel_index == bel.index) {
return chip_info->pio_info[i].bank;
}
}
NPNR_ASSERT_FALSE("failed to find PIO");
}
std::string Arch::getPioFunctionName(BelId bel) const
{
for (int i = 0; i < chip_info->num_pios; i++) {
if (Location(chip_info->pio_info[i].abs_loc) == bel.location && chip_info->pio_info[i].bel_index == bel.index) {
const char *func = chip_info->pio_info[i].function_name.get();
if (func == nullptr)
return "";
else
return func;
}
}
NPNR_ASSERT_FALSE("failed to find PIO");
}
BelId Arch::getPioByFunctionName(const std::string &name) const
{
for (int i = 0; i < chip_info->num_pios; i++) {
const char *func = chip_info->pio_info[i].function_name.get();
if (func != nullptr && func == name) {
BelId bel;
bel.location = chip_info->pio_info[i].abs_loc;
bel.index = chip_info->pio_info[i].bel_index;
return bel;
}
}
return BelId();
}
std::vector<PortPin> Arch::getBelPins(BelId bel) const std::vector<PortPin> Arch::getBelPins(BelId bel) const
{ {
@ -361,45 +406,14 @@ BelId Arch::getBelByLocation(Loc loc) const
return BelId(); return BelId();
} }
BelRange Arch::getBelsByTile(int x, int y) const
{
BelRange br;
int num_bels = 0;
if (x < chip_info->width && y < chip_info->height) {
const LocationTypePOD &locI = chip_info->locations[chip_info->location_type[y * chip_info->width + x]];
num_bels = locI.num_bels;
}
br.b.cursor_tile = y * chip_info->width + x;
br.e.cursor_tile = y * chip_info->width + x;
br.b.cursor_index = 0;
br.e.cursor_index = num_bels - 1;
br.b.chip = chip_info;
br.e.chip = chip_info;
++br.e;
return br;
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
{
x = bel.location.x;
y = bel.location.y;
gb = false;
}
delay_t Arch::estimateDelay(WireId src, WireId dst) const delay_t Arch::estimateDelay(WireId src, WireId dst) const
{ {
return 200 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y)); return 200 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
} }
delay_t Arch::getBudgetOverride(const PortRef& pr, delay_t v) const delay_t Arch::getBudgetOverride(NetInfo *net_info, int user_idx, delay_t budget) const { return budget; }
{
return v;
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------

View File

@ -98,7 +98,7 @@ NPNR_PACKED_STRUCT(struct LocationTypePOD {
}); });
NPNR_PACKED_STRUCT(struct PIOInfoPOD { NPNR_PACKED_STRUCT(struct PIOInfoPOD {
Location abs_loc; LocationPOD abs_loc;
int32_t bel_index; int32_t bel_index;
RelPtr<char> function_name; RelPtr<char> function_name;
int16_t bank; int16_t bank;
@ -107,7 +107,7 @@ NPNR_PACKED_STRUCT(struct PIOInfoPOD {
NPNR_PACKED_STRUCT(struct PackagePinPOD { NPNR_PACKED_STRUCT(struct PackagePinPOD {
RelPtr<char> name; RelPtr<char> name;
Location abs_loc; LocationPOD abs_loc;
int32_t bel_index; int32_t bel_index;
}); });
@ -117,6 +117,26 @@ NPNR_PACKED_STRUCT(struct PackageInfoPOD {
RelPtr<PackagePinPOD> pin_data; RelPtr<PackagePinPOD> pin_data;
}); });
enum TapDirection : int8_t
{
TAP_DIR_LEFT = 0,
TAP_DIR_RIGHT = 1
};
enum GlobalQuadrant : int8_t
{
QUAD_UL = 0,
QUAD_UR = 1,
QUAD_LL = 2,
QUAD_LR = 3,
};
NPNR_PACKED_STRUCT(struct GlobalInfoPOD {
int16_t tap_col;
TapDirection tap_dir;
GlobalQuadrant quad;
});
NPNR_PACKED_STRUCT(struct ChipInfoPOD { NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height; int32_t width, height;
int32_t num_tiles; int32_t num_tiles;
@ -124,6 +144,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t num_packages, num_pios; int32_t num_packages, num_pios;
RelPtr<LocationTypePOD> locations; RelPtr<LocationTypePOD> locations;
RelPtr<int32_t> location_type; RelPtr<int32_t> location_type;
RelPtr<GlobalInfoPOD> location_glbinfo;
RelPtr<RelPtr<char>> tiletype_names; RelPtr<RelPtr<char>> tiletype_names;
RelPtr<PackageInfoPOD> package_info; RelPtr<PackageInfoPOD> package_info;
RelPtr<PIOInfoPOD> pio_info; RelPtr<PIOInfoPOD> pio_info;
@ -482,8 +503,6 @@ struct Arch : BaseCtx
return range; return range;
} }
BelRange getBelsAtSameTile(BelId bel) const;
BelType getBelType(BelId bel) const BelType getBelType(BelId bel) const
{ {
NPNR_ASSERT(bel != BelId()); NPNR_ASSERT(bel != BelId());
@ -519,6 +538,8 @@ struct Arch : BaseCtx
return id(name.str()); return id(name.str());
} }
IdString getWireType(WireId wire) const { return IdString(); }
uint32_t getWireChecksum(WireId wire) const { return wire.index; } uint32_t getWireChecksum(WireId wire) const { return wire.index; }
void bindWire(WireId wire, IdString net, PlaceStrength strength) void bindWire(WireId wire, IdString net, PlaceStrength strength)
@ -597,6 +618,8 @@ struct Arch : BaseCtx
PipId getPipByName(IdString name) const; PipId getPipByName(IdString name) const;
IdString getPipName(PipId pip) const; IdString getPipName(PipId pip) const;
IdString getPipType(PipId pip) const { return IdString(); }
uint32_t getPipChecksum(PipId pip) const { return pip.index; } uint32_t getPipChecksum(PipId pip) const { return pip.index; }
void bindPip(PipId pip, IdString net, PlaceStrength strength) void bindPip(PipId pip, IdString net, PlaceStrength strength)
@ -729,10 +752,14 @@ struct Arch : BaseCtx
return chip_info->tiletype_names[locInfo(pip)->pip_data[pip.index].tile_type].get(); 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; BelId getPackagePinBel(const std::string &pin) const;
std::string getBelPackagePin(BelId bel) const; std::string getBelPackagePin(BelId bel) const;
int getPioBelBank(BelId bel) const;
// For getting GCLK, PLL, Vref, etc, pins
std::string getPioFunctionName(BelId bel) const;
BelId getPioByFunctionName(const std::string &name) const;
PortType getBelPinType(BelId bel, PortPin pin) const; PortType getBelPinType(BelId bel, PortPin pin) const;
@ -748,13 +775,12 @@ struct Arch : BaseCtx
// ------------------------------------------------- // -------------------------------------------------
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const;
delay_t estimateDelay(WireId src, WireId dst) const; delay_t estimateDelay(WireId src, WireId dst) const;
delay_t getDelayEpsilon() const { return 20; } delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; } delay_t getRipupDelayPenalty() const { return 200; }
float getDelayNS(delay_t v) const { return v * 0.001; } float getDelayNS(delay_t v) const { return v * 0.001; }
uint32_t getDelayChecksum(delay_t v) const { return v; } uint32_t getDelayChecksum(delay_t v) const { return v; }
delay_t getBudgetOverride(const PortRef& pr, delay_t v) const; delay_t getBudgetOverride(NetInfo *net_info, int user_idx, delay_t budget) const;
// ------------------------------------------------- // -------------------------------------------------

View File

@ -66,7 +66,8 @@ bool Arch::isBelLocationValid(BelId bel) const
{ {
if (getBelType(bel) == TYPE_TRELLIS_SLICE) { if (getBelType(bel) == TYPE_TRELLIS_SLICE) {
std::vector<const CellInfo *> bel_cells; std::vector<const CellInfo *> bel_cells;
for (auto bel_other : getBelsAtSameTile(bel)) { Loc bel_loc = getBelLocation(bel);
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
IdString cell_other = getBoundBelCell(bel_other); IdString cell_other = getBoundBelCell(bel_other);
if (cell_other != IdString()) { if (cell_other != IdString()) {
const CellInfo *ci_other = cells.at(cell_other).get(); const CellInfo *ci_other = cells.at(cell_other).get();
@ -89,8 +90,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
NPNR_ASSERT(getBelType(bel) == TYPE_TRELLIS_SLICE); NPNR_ASSERT(getBelType(bel) == TYPE_TRELLIS_SLICE);
std::vector<const CellInfo *> bel_cells; std::vector<const CellInfo *> bel_cells;
Loc bel_loc = getBelLocation(bel);
for (auto bel_other : getBelsAtSameTile(bel)) { for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
IdString cell_other = getBoundBelCell(bel_other); IdString cell_other = getBoundBelCell(bel_other);
if (cell_other != IdString() && bel_other != bel) { if (cell_other != IdString() && bel_other != bel) {
const CellInfo *ci_other = cells.at(cell_other).get(); const CellInfo *ci_other = cells.at(cell_other).get();

View File

@ -2,7 +2,7 @@
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com> * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <dave@ds0.me> * Copyright (C) 2018 David Shah <david@symbioticeda.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -20,13 +20,132 @@
#ifndef NO_PYTHON #ifndef NO_PYTHON
#include "arch_pybindings.h"
#include "nextpnr.h" #include "nextpnr.h"
#include "pybindings.h" #include "pybindings.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
void arch_wrap_python() {} void arch_wrap_python()
{
using namespace PythonConversion;
class_<ArchArgs>("ArchArgs").def_readwrite("type", &ArchArgs::type);
class_<BelId>("BelId").def_readwrite("index", &BelId::index);
class_<WireId>("WireId").def_readwrite("index", &WireId::index);
class_<PipId>("PipId").def_readwrite("index", &PipId::index);
class_<BelPin>("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
enum_<PortPin>("PortPin")
#define X(t) .value("PIN_" #t, PIN_##t)
#include "portpins.inc"
;
#undef X
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
.def("checksum", &Context::checksum)
.def("pack", &Context::pack)
.def("place", &Context::place)
.def("route", &Context::route);
fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<BelType>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
fn_wrapper_1a<Context, decltype(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail");
fn_wrapper_1a<Context, decltype(&Context::getBelChecksum), &Context::getBelChecksum, pass_through<uint32_t>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum");
fn_wrapper_3a_v<Context, decltype(&Context::bindBel), &Context::bindBel, conv_from_str<BelId>,
conv_from_str<IdString>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel");
fn_wrapper_1a_v<Context, decltype(&Context::unbindBel), &Context::unbindBel, conv_from_str<BelId>>::def_wrap(
ctx_cls, "unbindBel");
fn_wrapper_1a<Context, decltype(&Context::getBoundBelCell), &Context::getBoundBelCell, conv_to_str<IdString>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell");
fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell,
conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
"getBels");
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
conv_from_str<BelId>, conv_from_str<PortPin>>::def_wrap(ctx_cls, "getBelPinWire");
fn_wrapper_1a<Context, decltype(&Context::getWireBelPins), &Context::getWireBelPins, wrap_context<BelPinRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireBelPins");
fn_wrapper_1a<Context, decltype(&Context::getWireChecksum), &Context::getWireChecksum, pass_through<uint32_t>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum");
fn_wrapper_3a_v<Context, decltype(&Context::bindWire), &Context::bindWire, conv_from_str<WireId>,
conv_from_str<IdString>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire");
fn_wrapper_1a_v<Context, decltype(&Context::unbindWire), &Context::unbindWire, conv_from_str<WireId>>::def_wrap(
ctx_cls, "unbindWire");
fn_wrapper_1a<Context, decltype(&Context::checkWireAvail), &Context::checkWireAvail, pass_through<bool>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail");
fn_wrapper_1a<Context, decltype(&Context::getBoundWireNet), &Context::getBoundWireNet, conv_to_str<IdString>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet");
fn_wrapper_1a<Context, decltype(&Context::getConflictingWireNet), &Context::getConflictingWireNet,
conv_to_str<IdString>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet");
fn_wrapper_0a<Context, decltype(&Context::getWires), &Context::getWires, wrap_context<WireRange>>::def_wrap(
ctx_cls, "getWires");
fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips, wrap_context<AllPipRange>>::def_wrap(
ctx_cls, "getPips");
fn_wrapper_1a<Context, decltype(&Context::getPipChecksum), &Context::getPipChecksum, pass_through<uint32_t>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum");
fn_wrapper_3a_v<Context, decltype(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>,
conv_from_str<IdString>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip");
fn_wrapper_1a_v<Context, decltype(&Context::unbindPip), &Context::unbindPip, conv_from_str<PipId>>::def_wrap(
ctx_cls, "unbindPip");
fn_wrapper_1a<Context, decltype(&Context::checkPipAvail), &Context::checkPipAvail, pass_through<bool>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail");
fn_wrapper_1a<Context, decltype(&Context::getBoundPipNet), &Context::getBoundPipNet, conv_to_str<IdString>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet");
fn_wrapper_1a<Context, decltype(&Context::getConflictingPipNet), &Context::getConflictingPipNet,
conv_to_str<IdString>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet");
fn_wrapper_1a<Context, decltype(&Context::getPipsDownhill), &Context::getPipsDownhill, wrap_context<PipRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsDownhill");
fn_wrapper_1a<Context, decltype(&Context::getPipsUphill), &Context::getPipsUphill, wrap_context<PipRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill");
fn_wrapper_1a<Context, decltype(&Context::getWireAliases), &Context::getWireAliases, wrap_context<PipRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireAliases");
fn_wrapper_1a<Context, decltype(&Context::getPipSrcWire), &Context::getPipSrcWire, conv_to_str<WireId>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire");
fn_wrapper_1a<Context, decltype(&Context::getPipDstWire), &Context::getPipDstWire, conv_to_str<WireId>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire");
fn_wrapper_1a<Context, decltype(&Context::getPipDelay), &Context::getPipDelay, pass_through<DelayInfo>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay");
fn_wrapper_1a<Context, decltype(&Context::getPackagePinBel), &Context::getPackagePinBel, conv_to_str<BelId>,
pass_through<std::string>>::def_wrap(ctx_cls, "getPackagePinBel");
fn_wrapper_1a<Context, decltype(&Context::getBelPackagePin), &Context::getBelPackagePin, pass_through<std::string>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelPackagePin");
fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap(
ctx_cls, "getChipName");
fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
"archId");
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
"cells");
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
"nets");
WRAP_RANGE(Bel, conv_to_str<BelId>);
WRAP_RANGE(Wire, conv_to_str<WireId>);
WRAP_RANGE(AllPip, conv_to_str<PipId>);
WRAP_RANGE(Pip, conv_to_str<PipId>);
WRAP_MAP_UPTR(CellMap, "IdCellMap");
WRAP_MAP_UPTR(NetMap, "IdNetMap");
}
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
#endif #endif // NO_PYTHON

View File

@ -30,6 +30,7 @@
#include <fstream> #include <fstream>
#include <streambuf> #include <streambuf>
#include "io.h"
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
@ -173,7 +174,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
// Add all set, configurable pips to the config // Add all set, configurable pips to the config
for (auto pip : ctx->getPips()) { for (auto pip : ctx->getPips()) {
if (ctx->getBoundPipNet(pip) != IdString()) { 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, std::string tile = empty_chip.get_tile_by_position_and_type(pip.location.y, pip.location.x,
ctx->getPipTiletype(pip)); ctx->getPipTiletype(pip));
std::string source = get_trellis_wirename(ctx, pip.location, ctx->getPipSrcWire(pip)); std::string source = get_trellis_wirename(ctx, pip.location, ctx->getPipSrcWire(pip));
@ -182,12 +183,47 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
} }
} }
} }
// Find bank voltages
std::unordered_map<int, IOVoltage> bankVcc;
std::unordered_map<int, bool> bankLvds;
// Set all bankref tiles to 3.3V (TODO) for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (ci->bel != BelId() && ci->type == ctx->id("TRELLIS_IO")) {
int bank = ctx->getPioBelBank(ci->bel);
std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT");
std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33");
if (dir != "INPUT") {
IOVoltage vcc = get_vccio(ioType_from_str(iotype));
if (bankVcc.find(bank) != bankVcc.end()) {
// TODO: strong and weak constraints
if (bankVcc[bank] != vcc) {
log_error("Error processing '%s': incompatible IO voltages %s and %s on bank %d.",
cell.first.c_str(ctx), iovoltage_to_str(bankVcc[bank]).c_str(),
iovoltage_to_str(vcc).c_str(), bank);
}
} else {
bankVcc[bank] = vcc;
}
}
if (iotype == "LVDS")
bankLvds[bank] = true;
}
}
// Set all bankref tiles to appropriate VccIO
for (const auto &tile : empty_chip.tiles) { for (const auto &tile : empty_chip.tiles) {
std::string type = tile.second->info.type; std::string type = tile.second->info.type;
if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") { if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") {
cc.tiles[tile.first].add_enum("BANK.VCCIO", "3V3"); int bank = std::stoi(type.substr(7));
if (bankVcc.find(bank) != bankVcc.end())
cc.tiles[tile.first].add_enum("BANK.VCCIO", iovoltage_to_str(bankVcc[bank]));
if (bankLvds[bank]) {
cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON");
cc.tiles[tile.first].add_enum("BANK.LVDSO", "ON");
}
} }
} }
@ -235,6 +271,20 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
std::string pic_tile = get_pic_tile(ctx, empty_chip, bel); std::string pic_tile = get_pic_tile(ctx, empty_chip, bel);
cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype); cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
cc.tiles[pic_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype); cc.tiles[pic_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype);
if (is_differential(ioType_from_str(iotype))) {
// Explicitly disable other pair
std::string other;
if (pio == "PIOA")
other = "PIOB";
else if (pio == "PIOC")
other = "PIOD";
else
log_error("cannot place differential IO at location %s\n", pio.c_str());
// cc.tiles[pio_tile].add_enum(other + ".BASE_TYPE", "_NONE_");
// cc.tiles[pic_tile].add_enum(other + ".BASE_TYPE", "_NONE_");
cc.tiles[pio_tile].add_enum(other + ".PULLMODE", "NONE");
cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", "NONE");
}
if (dir != "INPUT" && if (dir != "INPUT" &&
(ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr)) { (ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr)) {
// Tie tristate low if unconnected for outputs or bidir // Tie tristate low if unconnected for outputs or bidir
@ -247,7 +297,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get(); std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get();
cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0"); cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0");
} }
if (dir == "INPUT") { if (dir == "INPUT" && !is_differential(ioType_from_str(iotype))) {
cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", "ON"); cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", "ON");
} }
} else { } else {

View File

@ -24,11 +24,16 @@ if (MSVC)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resources/chipdb.rc PROPERTIES LANGUAGE RC) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resources/chipdb.rc PROPERTIES LANGUAGE RC)
foreach (dev ${devices}) foreach (dev ${devices})
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.bin) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.bin)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.bba)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc)
add_custom_command(OUTPUT ${DEV_CC_DB} add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${ENV_CMD} python3 ${DB_PY} -b -p ${DEV_PORTS_INC} ${dev} ${DEV_CC_DB} COMMAND ${ENV_CMD} python3 ${DB_PY} -p ${DEV_PORTS_INC} ${dev} > ${DEV_CC_BBA_DB}
DEPENDS ${DB_PY} DEPENDS ${DB_PY}
) )
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm ${DEV_CC_BBA_DB} ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)
target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB}) target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB})
set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE) set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE)
foreach (target ${family_targets}) foreach (target ${family_targets})
@ -39,11 +44,18 @@ else()
target_compile_options(ecp5_chipdb PRIVATE -g0 -O0 -w) target_compile_options(ecp5_chipdb PRIVATE -g0 -O0 -w)
foreach (dev ${devices}) foreach (dev ${devices})
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.cc) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.cc)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/chipdbs/chipdb-${dev}.bba)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/portpins.inc)
add_custom_command(OUTPUT ${DEV_CC_DB} add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${ENV_CMD} python3 ${DB_PY} -c -p ${DEV_PORTS_INC} ${dev} ${DEV_CC_DB} COMMAND ${ENV_CMD} python3 ${DB_PY} -p ${DEV_PORTS_INC} ${dev} > ${DEV_CC_BBA_DB}.new
COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
DEPENDS ${DB_PY} DEPENDS ${DB_PY}
) )
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm --c ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)
target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB}) target_sources(ecp5_chipdb PRIVATE ${DEV_CC_DB})
foreach (target ${family_targets}) foreach (target ${family_targets})
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ecp5_chipdb>) target_sources(${target} PRIVATE $<TARGET_OBJECTS:ecp5_chipdb>)

217
ecp5/io.cc Normal file
View File

@ -0,0 +1,217 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@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 "io.h"
NEXTPNR_NAMESPACE_BEGIN
std::string iovoltage_to_str(IOVoltage v)
{
switch (v) {
case IOVoltage::VCC_3V3:
return "3V3";
case IOVoltage::VCC_2V5:
return "2V5";
case IOVoltage::VCC_1V8:
return "1V8";
case IOVoltage::VCC_1V5:
return "1V5";
case IOVoltage::VCC_1V35:
return "1V35";
case IOVoltage::VCC_1V2:
return "1V2";
}
NPNR_ASSERT_FALSE("unknown IO voltage");
}
IOVoltage iovoltage_from_str(const std::string &name)
{
if (name == "3V3")
return IOVoltage::VCC_3V3;
if (name == "2V5")
return IOVoltage::VCC_2V5;
if (name == "1V8")
return IOVoltage::VCC_1V8;
if (name == "1V5")
return IOVoltage::VCC_1V5;
if (name == "1V35")
return IOVoltage::VCC_1V35;
if (name == "1V2")
return IOVoltage::VCC_1V2;
NPNR_ASSERT_FALSE("unknown IO voltage");
}
std::string iotype_to_str(IOType type)
{
if (type == IOType::TYPE_NONE)
return "NONE";
#define X(t) \
if (type == IOType::t) \
return #t;
#include "iotypes.inc"
#undef X
if (type == IOType::TYPE_UNKNOWN)
return "<unknown>";
NPNR_ASSERT_FALSE("unknown IO type");
}
IOType ioType_from_str(const std::string &name)
{
if (name == "NONE")
return IOType::TYPE_NONE;
#define X(t) \
if (name == #t) \
return IOType::t;
#include "iotypes.inc"
return IOType::TYPE_UNKNOWN;
}
IOVoltage get_vccio(IOType type)
{
switch (type) {
case IOType::LVTTL33:
case IOType::LVCMOS33:
case IOType::LVCMOS33D:
case IOType::LVPECL33:
case IOType::LVPECL33E:
return IOVoltage::VCC_3V3;
case IOType::LVCMOS25:
case IOType::LVCMOS25D:
case IOType::LVDS:
case IOType::SLVS:
case IOType::SUBLVDS:
case IOType::LVDS25E:
case IOType::MLVDS25:
case IOType::MLVDS25E:
case IOType::BLVDS25:
return IOVoltage::VCC_2V5;
case IOType::LVCMOS18:
case IOType::LVCMOS18D:
case IOType::SSTL18_I:
case IOType::SSTL18_II:
case IOType::SSTL18D_I:
case IOType::SSTL18D_II:
return IOVoltage::VCC_1V8;
case IOType::LVCMOS15:
case IOType::SSTL15_I:
case IOType::SSTL15_II:
case IOType::SSTL15D_I:
case IOType::SSTL15D_II:
return IOVoltage::VCC_1V5;
case IOType::SSTL135_I:
case IOType::SSTL135_II:
case IOType::SSTL135D_I:
case IOType::SSTL135D_II:
return IOVoltage::VCC_1V35;
case IOType::LVCMOS12:
case IOType::HSUL12:
case IOType::HSUL12D:
return IOVoltage::VCC_1V2;
default:
NPNR_ASSERT_FALSE("unknown IO type, unable to determine VccIO");
}
}
bool is_strong_vccio_constraint(IOType type, PortType dir, IOSide side)
{
if (dir == PORT_OUT || dir == PORT_INOUT)
return true;
switch (type) {
case IOType::TYPE_NONE:
case IOType::LVCMOS33D:
case IOType::LVPECL33:
case IOType::LVDS:
case IOType::MLVDS25:
case IOType::BLVDS25:
case IOType::SLVS:
case IOType::SUBLVDS:
case IOType::LVCMOS12:
case IOType::HSUL12:
case IOType::HSUL12D:
return false;
case IOType::LVCMOS33:
case IOType::LVTTL33:
case IOType::LVCMOS25:
return (side == IOSide::LEFT || side == IOSide::RIGHT);
default:
return true;
}
}
bool is_differential(IOType type)
{
switch (type) {
case IOType::LVCMOS33D:
case IOType::LVCMOS25D:
case IOType::LVPECL33:
case IOType::LVDS:
case IOType::MLVDS25:
case IOType::BLVDS25:
case IOType::SLVS:
case IOType::SUBLVDS:
case IOType::LVCMOS18D:
case IOType::SSTL18D_I:
case IOType::SSTL18D_II:
case IOType::SSTL15D_I:
case IOType::SSTL15D_II:
case IOType::SSTL135D_I:
case IOType::SSTL135D_II:
case IOType::HSUL12D:
return true;
default:
return false;
}
}
bool is_referenced(IOType type)
{
switch (type) {
case IOType::SSTL18_I:
case IOType::SSTL18_II:
case IOType::SSTL18D_I:
case IOType::SSTL18D_II:
case IOType::SSTL15_I:
case IOType::SSTL15_II:
case IOType::SSTL15D_I:
case IOType::SSTL15D_II:
case IOType::SSTL135_I:
case IOType::SSTL135_II:
case IOType::SSTL135D_I:
case IOType::SSTL135D_II:
case IOType::HSUL12:
case IOType::HSUL12D:
return true;
default:
return false;
}
}
bool valid_loc_for_io(IOType type, PortType dir, IOSide side, int z)
{
bool is_lr = side == IOSide::LEFT || side == IOSide::RIGHT;
if (is_referenced(type) && !is_lr)
return false;
if (is_differential(type) && (!is_lr || ((z % 2) == 1)))
return false;
if ((type == IOType::LVCMOS18D || type == IOType::LVDS) && (dir == PORT_OUT || PORT_INOUT) && z != 0)
return false;
return true;
}
NEXTPNR_NAMESPACE_END

70
ecp5/io.h Normal file
View File

@ -0,0 +1,70 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@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 IO_H
#define IO_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
enum class IOVoltage
{
VCC_3V3,
VCC_2V5,
VCC_1V8,
VCC_1V5,
VCC_1V35,
VCC_1V2
};
std::string iovoltage_to_str(IOVoltage v);
IOVoltage iovoltage_from_str(const std::string &name);
enum class IOType
{
TYPE_NONE,
#define X(t) t,
#include "iotypes.inc"
#undef X
TYPE_UNKNOWN,
};
enum class IOSide
{
LEFT,
RIGHT,
TOP,
BOTTOM,
};
std::string iotype_to_str(IOType type);
IOType ioType_from_str(const std::string &name);
// IO related functions
IOVoltage get_vccio(IOType type);
bool is_strong_vccio_constraint(IOType type, PortType dir, IOSide side);
bool is_differential(IOType type);
bool is_referenced(IOType type);
bool valid_loc_for_io(IOType type, PortType dir, IOSide side, int z);
NEXTPNR_NAMESPACE_END
#endif

37
ecp5/iotypes.inc Normal file
View File

@ -0,0 +1,37 @@
X(LVTTL33)
X(LVCMOS33)
X(LVCMOS25)
X(LVCMOS18)
X(LVCMOS15)
X(LVCMOS12)
X(SSTL18_I)
X(SSTL18_II)
X(SSTL15_I)
X(SSTL15_II)
X(SSTL135_I)
X(SSTL135_II)
X(HSUL12)
X(SSTL18D_I)
X(SSTL18D_II)
X(SSTL135D_I)
X(SSTL135D_II)
X(SSTL15D_I)
X(SSTL15D_II)
X(HSUL12D)
X(LVCMOS33D)
X(LVCMOS25D)
X(LVDS)
X(BLVDS25)
X(MLVDS25)
X(LVPECL33)
X(SLVS)
X(SUBLVDS)
X(LVCMOS18D)
X(LVDS25E)
X(BLVDS25E)
X(MLVDS25E)
X(LVPECL33E)

View File

@ -63,6 +63,7 @@ int main(int argc, char *argv[])
#ifndef NO_GUI #ifndef NO_GUI
options.add_options()("gui", "start gui"); options.add_options()("gui", "start gui");
#endif #endif
options.add_options()("test", "check architecture database integrity");
options.add_options()("25k", "set device type to LFE5U-25F"); options.add_options()("25k", "set device type to LFE5U-25F");
options.add_options()("45k", "set device type to LFE5U-45F"); options.add_options()("45k", "set device type to LFE5U-45F");
@ -99,18 +100,16 @@ int main(int argc, char *argv[])
} }
if (vm.count("help") || argc == 1) { if (vm.count("help") || argc == 1) {
std::cout << boost::filesystem::basename(argv[0]) std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git "
<< " -- Next Generation Place and Route (git " "sha1 " GIT_COMMIT_HASH_STR ")\n";
"sha1 " GIT_COMMIT_HASH_STR ")\n";
std::cout << "\n"; std::cout << "\n";
std::cout << options << "\n"; std::cout << options << "\n";
return argc != 1; return argc != 1;
} }
if (vm.count("version")) { if (vm.count("version")) {
std::cout << boost::filesystem::basename(argv[0]) std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git "
<< " -- Next Generation Place and Route (git " "sha1 " GIT_COMMIT_HASH_STR ")\n";
"sha1 " GIT_COMMIT_HASH_STR ")\n";
return 1; return 1;
} }
@ -148,6 +147,9 @@ int main(int argc, char *argv[])
if (vm.count("no-tmdriv")) if (vm.count("no-tmdriv"))
ctx->timing_driven = false; ctx->timing_driven = false;
if (vm.count("test"))
ctx->archcheck();
#ifndef NO_GUI #ifndef NO_GUI
if (vm.count("gui")) { if (vm.count("gui")) {
Application a(argc, argv); Application a(argc, argv);
@ -165,8 +167,12 @@ int main(int argc, char *argv[])
if (!ctx->pack() && !ctx->force) if (!ctx->pack() && !ctx->force)
log_error("Packing design failed.\n"); log_error("Packing design failed.\n");
if (vm.count("freq")) if (vm.count("freq")) {
ctx->target_freq = vm["freq"].as<double>() * 1e6; ctx->target_freq = vm["freq"].as<double>() * 1e6;
ctx->user_freq = true;
} else {
log_warning("Target frequency not specified. Will optimise for max frequency.\n");
}
assign_budget(ctx.get()); assign_budget(ctx.get());
ctx->check(); ctx->check();
print_utilisation(ctx.get()); print_utilisation(ctx.get());

View File

@ -10,11 +10,7 @@ type_at_location = dict()
tiletype_names = dict() tiletype_names = dict()
parser = argparse.ArgumentParser(description="import ECP5 routing and bels from Project Trellis") parser = argparse.ArgumentParser(description="import ECP5 routing and bels from Project Trellis")
group = parser.add_mutually_exclusive_group()
group.add_argument("-b", "--binary", action="store_true")
group.add_argument("-c", "--c_file", action="store_true")
parser.add_argument("device", type=str, help="target device") parser.add_argument("device", type=str, help="target device")
parser.add_argument("outfile", type=argparse.FileType('w'), help="output filename")
parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc") parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc")
args = parser.parse_args() args = parser.parse_args()
@ -36,284 +32,50 @@ portpins = dict()
class BinaryBlobAssembler: class BinaryBlobAssembler:
def __init__(self, cname, endianness, nodebug=False): def l(self, name, ltype = None, export = False):
assert endianness in ["le", "be"] if ltype is None:
self.cname = cname print("label %s" % (name,))
self.endianness = endianness else:
self.finalized = False print("label %s %s" % (name, ltype))
self.data = bytearray()
self.comments = dict()
self.labels = dict()
self.exports = set()
self.labels_byaddr = dict()
self.ltypes_byaddr = dict()
self.strings = dict()
self.refs = dict()
self.nodebug = nodebug
def l(self, name, ltype=None, export=False):
assert not self.finalized
assert name not in self.labels
assert len(self.data) not in self.labels_byaddr
self.labels[name] = len(self.data)
if ltype is not None:
self.ltypes_byaddr[len(self.data)] = ltype
self.labels_byaddr[len(self.data)] = name
if export:
assert ltype is not None
self.exports.add(len(self.data))
def r(self, name, comment): def r(self, name, comment):
assert not self.finalized if comment is None:
assert len(self.data) % 4 == 0 print("ref %s" % (name,))
assert len(self.data) not in self.refs else:
if self.nodebug: print("ref %s %s" % (name, comment))
comment = None
if name is not None:
self.refs[len(self.data)] = (name, comment)
self.data.append(0)
self.data.append(0)
self.data.append(0)
self.data.append(0)
if (name is None) and (comment is not None):
self.comments[len(self.data)] = comment + " (null reference)"
def s(self, s, comment): def s(self, s, comment):
assert not self.finalized print("str %s" % s)
if self.nodebug:
comment = None
if s not in self.strings:
index = len(self.strings)
self.strings[s] = index
else:
index = self.strings[s]
if comment is not None:
self.r("str%d" % index, '%s: "%s"' % (comment, s))
else:
self.r("str%d" % index, None)
def u8(self, v, comment): def u8(self, v, comment):
assert not self.finalized if comment is None:
if self.nodebug: print("u8 %d" % (v,))
comment = None else:
self.data.append(v) print("u8 %d %s" % (v, comment))
if comment is not None:
self.comments[len(self.data)] = comment
def u16(self, v, comment): def u16(self, v, comment):
assert not self.finalized if comment is None:
assert len(self.data) % 2 == 0 print("u16 %d" % (v,))
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
elif self.endianness == "be":
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
else: else:
assert 0 print("u16 %d %s" % (v, comment))
if comment is not None:
self.comments[len(self.data)] = comment
def s16(self, v, comment):
assert not self.finalized
assert len(self.data) % 2 == 0
if self.nodebug:
comment = None
c2val = (((-v) ^ 0xffff) + 1) if v < 0 else v
if self.endianness == "le":
self.data.append(c2val & 255)
self.data.append((c2val >> 8) & 255)
elif self.endianness == "be":
self.data.append((c2val >> 8) & 255)
self.data.append(c2val & 255)
else:
assert 0
if comment is not None:
self.comments[len(self.data)] = comment
def u32(self, v, comment): def u32(self, v, comment):
assert not self.finalized if comment is None:
assert len(self.data) % 4 == 0 print("u32 %d" % (v,))
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 24) & 255)
elif self.endianness == "be":
self.data.append((v >> 24) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
else: else:
assert 0 print("u32 %d %s" % (v, comment))
if comment is not None:
self.comments[len(self.data)] = comment
def finalize(self): def pre(self, s):
assert not self.finalized print("pre %s" % s)
for s, index in sorted(self.strings.items()):
self.l("str%d" % index, "char")
for c in s:
self.data.append(ord(c))
self.data.append(0)
self.finalized = True
cursor = 0
while cursor < len(self.data):
if cursor in self.refs:
v = self.labels[self.refs[cursor][0]] - cursor
if self.endianness == "le":
self.data[cursor + 0] = (v & 255)
self.data[cursor + 1] = ((v >> 8) & 255)
self.data[cursor + 2] = ((v >> 16) & 255)
self.data[cursor + 3] = ((v >> 24) & 255)
elif self.endianness == "be":
self.data[cursor + 0] = ((v >> 24) & 255)
self.data[cursor + 1] = ((v >> 16) & 255)
self.data[cursor + 2] = ((v >> 8) & 255)
self.data[cursor + 3] = (v & 255)
else:
assert 0
cursor += 4
else:
cursor += 1
def write_verbose_c(self, f, ctype="const unsigned char"): def post(self, s):
assert self.finalized print("post %s" % s)
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
cursor = 0
bytecnt = 0
while cursor < len(self.data):
if cursor in self.comments:
if bytecnt == 0:
print(" ", end="", file=f)
print(" // %s" % self.comments[cursor], file=f)
bytecnt = 0
if cursor in self.labels_byaddr:
if bytecnt != 0:
print(file=f)
if cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (
self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
else:
print(" // [%d] %s" % (cursor, self.labels_byaddr[cursor]), file=f)
bytecnt = 0
if cursor in self.refs:
if bytecnt != 0:
print(file=f)
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 0]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 1]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 2]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor + 3]), end="", file=f)
print(" // [%d] %s (reference to %s)" % (cursor, self.refs[cursor][1], self.refs[cursor][0]), file=f)
bytecnt = 0
cursor += 4
else:
if bytecnt == 0:
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor]), end=("" if bytecnt < 15 else "\n"), file=f)
bytecnt = (bytecnt + 1) & 15
cursor += 1
if bytecnt != 0:
print(file=f)
print("};", file=f)
def write_compact_c(self, f, ctype="const unsigned char"): def push(self, name):
assert self.finalized print("push %s" % name)
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
column = 0
for v in self.data:
if column == 0:
print(" ", end="", file=f)
column += 2
s = "%d," % v
print(s, end="", file=f)
column += len(s)
if column > 75:
print(file=f)
column = 0
if column != 0:
print(file=f)
for cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (
self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
print("};", file=f)
def write_uint64_c(self, f, ctype="const uint64_t"):
assert self.finalized
print("%s %s[%d] = {" % (ctype, self.cname, (len(self.data) + 7) // 8), file=f)
column = 0
for i in range((len(self.data) + 7) // 8):
v0 = self.data[8 * i + 0] if 8 * i + 0 < len(self.data) else 0
v1 = self.data[8 * i + 1] if 8 * i + 1 < len(self.data) else 0
v2 = self.data[8 * i + 2] if 8 * i + 2 < len(self.data) else 0
v3 = self.data[8 * i + 3] if 8 * i + 3 < len(self.data) else 0
v4 = self.data[8 * i + 4] if 8 * i + 4 < len(self.data) else 0
v5 = self.data[8 * i + 5] if 8 * i + 5 < len(self.data) else 0
v6 = self.data[8 * i + 6] if 8 * i + 6 < len(self.data) else 0
v7 = self.data[8 * i + 7] if 8 * i + 7 < len(self.data) else 0
if self.endianness == "le":
v = v0 << 0
v |= v1 << 8
v |= v2 << 16
v |= v3 << 24
v |= v4 << 32
v |= v5 << 40
v |= v6 << 48
v |= v7 << 56
elif self.endianness == "be":
v = v7 << 0
v |= v6 << 8
v |= v5 << 16
v |= v4 << 24
v |= v3 << 32
v |= v2 << 40
v |= v1 << 48
v |= v0 << 56
else:
assert 0
if column == 3:
print(" 0x%016x," % v, file=f)
column = 0
else:
if column == 0:
print(" ", end="", file=f)
print(" 0x%016x," % v, end="", file=f)
column += 1
if column != 0:
print("", file=f)
print("};", file=f)
def write_string_c(self, f, ctype="const char"):
assert self.finalized
assert self.data[len(self.data) - 1] == 0
print("%s %s[%d] =" % (ctype, self.cname, len(self.data)), file=f)
print(" \"", end="", file=f)
column = 0
for i in range(len(self.data) - 1):
if (self.data[i] < 32) or (self.data[i] > 126):
print("\\%03o" % self.data[i], end="", file=f)
column += 4
elif self.data[i] == ord('"') or self.data[i] == ord('\\'):
print("\\" + chr(self.data[i]), end="", file=f)
column += 2
else:
print(chr(self.data[i]), end="", file=f)
column += 1
if column > 70 and (i != len(self.data) - 2):
print("\"\n \"", end="", file=f)
column = 0
print("\";", file=f)
def write_binary(self, f):
assert self.finalized
assert self.data[len(self.data) - 1] == 0
f.buffer.write(self.data)
def pop(self):
print("pop")
bel_types = { bel_types = {
"NONE": 0, "NONE": 0,
@ -364,13 +126,25 @@ def process_pio_db(ddrg, device):
if bel_idx is not None: if bel_idx is not None:
pindata.append((loc, bel_idx, bank, pinfunc)) pindata.append((loc, bel_idx, bank, pinfunc))
global_data = {}
quadrants = ["UL", "UR", "LL", "LR"]
def process_loc_globals(chip):
for y in range(0, max_row+1):
for x in range(0, max_col+1):
quad = chip.global_data.get_quadrant(y, x)
tapdrv = chip.global_data.get_tap_driver(y, x)
global_data[x, y] = (quadrants.index(quad), int(tapdrv.dir), tapdrv.col)
def write_database(dev_name, ddrg, endianness): def write_database(dev_name, ddrg, endianness):
def write_loc(loc, sym_name): def write_loc(loc, sym_name):
bba.s16(loc.x, "%s.x" % sym_name) bba.u16(loc.x, "%s.x" % sym_name)
bba.s16(loc.y, "%s.y" % sym_name) bba.u16(loc.y, "%s.y" % sym_name)
bba = BinaryBlobAssembler("chipdb_blob_%s" % dev_name, endianness) bba = BinaryBlobAssembler()
bba.pre('#include "nextpnr.h"')
bba.pre('NEXTPNR_NAMESPACE_BEGIN')
bba.post('NEXTPNR_NAMESPACE_END')
bba.push("chipdb_blob_%s" % dev_name)
bba.r("chip_info", "chip_info") bba.r("chip_info", "chip_info")
loctypes = list([_.key() for _ in ddrg.locationTypes]) loctypes = list([_.key() for _ in ddrg.locationTypes])
@ -450,6 +224,14 @@ def write_database(dev_name, ddrg, endianness):
for y in range(0, max_row+1): for y in range(0, max_row+1):
for x in range(0, max_col+1): for x in range(0, max_col+1):
bba.u32(loctypes.index(ddrg.typeAtLocation[pytrellis.Location(x, y)]), "loctype") bba.u32(loctypes.index(ddrg.typeAtLocation[pytrellis.Location(x, y)]), "loctype")
bba.l("location_glbinfo", "GlobalInfoPOD")
for y in range(0, max_row+1):
for x in range(0, max_col+1):
bba.u16(global_data[x, y][2], "tap_col")
bba.u8(global_data[x, y][1], "tap_dir")
bba.u8(global_data[x, y][0], "quad")
for package, pkgdata in sorted(packages.items()): for package, pkgdata in sorted(packages.items()):
bba.l("package_data_%s" % package, "PackagePinPOD") bba.l("package_data_%s" % package, "PackagePinPOD")
for pin in pkgdata: for pin in pkgdata:
@ -491,11 +273,12 @@ def write_database(dev_name, ddrg, endianness):
bba.r("locations", "locations") bba.r("locations", "locations")
bba.r("location_types", "location_type") bba.r("location_types", "location_type")
bba.r("location_glbinfo", "location_glbinfo")
bba.r("tiletype_names", "tiletype_names") bba.r("tiletype_names", "tiletype_names")
bba.r("package_data", "package_info") bba.r("package_data", "package_info")
bba.r("pio_info", "pio_info") bba.r("pio_info", "pio_info")
bba.finalize() bba.pop()
return bba return bba
dev_names = {"25k": "LFE5U-25F", "45k": "LFE5U-45F", "85k": "LFE5U-85F"} dev_names = {"25k": "LFE5U-25F", "45k": "LFE5U-45F", "85k": "LFE5U-85F"}
@ -518,30 +301,18 @@ def main():
idx = len(portpins) + 1 idx = len(portpins) + 1
portpins[line[1]] = idx portpins[line[1]] = idx
print("Initialising chip...") # print("Initialising chip...")
chip = pytrellis.Chip(dev_names[args.device]) chip = pytrellis.Chip(dev_names[args.device])
print("Building routing graph...") # print("Building routing graph...")
ddrg = pytrellis.make_dedup_chipdb(chip) ddrg = pytrellis.make_dedup_chipdb(chip)
max_row = chip.get_max_row() max_row = chip.get_max_row()
max_col = chip.get_max_col() max_col = chip.get_max_col()
process_pio_db(ddrg, args.device) process_pio_db(ddrg, args.device)
print("{} unique location types".format(len(ddrg.locationTypes))) process_loc_globals(chip)
# print("{} unique location types".format(len(ddrg.locationTypes)))
bba = write_database(args.device, ddrg, "le") bba = write_database(args.device, ddrg, "le")
if args.c_file:
print('#include "nextpnr.h"', file=args.outfile)
print('NEXTPNR_NAMESPACE_BEGIN', file=args.outfile)
if args.binary:
bba.write_binary(args.outfile)
if args.c_file:
bba.write_string_c(args.outfile)
if args.c_file:
print('NEXTPNR_NAMESPACE_END', file=args.outfile)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -24,22 +24,24 @@
NEXTPNR_NAMESPACE_BEGIN 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); NPNR_ASSERT(wires.count(name) == 0);
WireInfo &wi = wires[name]; WireInfo &wi = wires[name];
wi.name = name; wi.name = name;
wi.type = type;
wi.x = x; wi.x = x;
wi.y = y; wi.y = y;
wire_ids.push_back(name); 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); NPNR_ASSERT(pips.count(name) == 0);
PipInfo &pi = pips[name]; PipInfo &pi = pips[name];
pi.name = name; pi.name = name;
pi.type = type;
pi.srcWire = srcWire; pi.srcWire = srcWire;
pi.dstWire = dstWire; pi.dstWire = dstWire;
pi.delay = delay; pi.delay = delay;
@ -49,11 +51,12 @@ void Arch::addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo d
pip_ids.push_back(name); 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); NPNR_ASSERT(pips.count(name) == 0);
PipInfo &pi = pips[name]; PipInfo &pi = pips[name];
pi.name = name; pi.name = name;
pi.type = type;
pi.srcWire = srcWire; pi.srcWire = srcWire;
pi.dstWire = dstWire; pi.dstWire = dstWire;
pi.delay = delay; pi.delay = delay;
@ -77,18 +80,18 @@ void Arch::addBel(IdString name, IdString type, Loc loc, bool gb)
bel_ids.push_back(name); bel_ids.push_back(name);
bel_by_loc[loc] = name; bel_by_loc[loc] = name;
if (bels_by_tile.size() <= loc.x) if (int(bels_by_tile.size()) <= loc.x)
bels_by_tile.resize(loc.x + 1); bels_by_tile.resize(loc.x + 1);
if (bels_by_tile[loc.x].size() <= loc.y) if (int(bels_by_tile[loc.x].size()) <= loc.y)
bels_by_tile[loc.x].resize(loc.y + 1); bels_by_tile[loc.x].resize(loc.y + 1);
bels_by_tile[loc.x][loc.y].push_back(name); bels_by_tile[loc.x][loc.y].push_back(name);
if (tileDimZ.size() <= loc.x) if (int(tileDimZ.size()) <= loc.x)
tileDimZ.resize(loc.x + 1); tileDimZ.resize(loc.x + 1);
if (tileDimZ[loc.x].size() <= loc.y) if (int(tileDimZ[loc.x].size()) <= loc.y)
tileDimZ[loc.x].resize(loc.y + 1); tileDimZ[loc.x].resize(loc.y + 1);
gridDimX = std::max(gridDimX, loc.x + 1); gridDimX = std::max(gridDimX, loc.x + 1);
@ -193,6 +196,24 @@ BelId Arch::getBelByName(IdString name) const
IdString Arch::getBelName(BelId bel) const { return bel; } IdString Arch::getBelName(BelId bel) const { return bel; }
Loc Arch::getBelLocation(BelId bel) const
{
auto &info = bels.at(bel);
return Loc(info.x, info.y, info.z);
}
BelId Arch::getBelByLocation(Loc loc) const
{
auto it = bel_by_loc.find(loc);
if (it != bel_by_loc.end())
return it->second;
return BelId();
}
const std::vector<BelId> &Arch::getBelsByTile(int x, int y) const { return bels_by_tile.at(x).at(y); }
bool Arch::getBelGlobalBuf(BelId bel) const { return bels.at(bel).gb; }
uint32_t Arch::getBelChecksum(BelId bel) const uint32_t Arch::getBelChecksum(BelId bel) const
{ {
// FIXME // FIXME
@ -234,6 +255,7 @@ std::vector<PortPin> Arch::getBelPins(BelId bel) const
std::vector<PortPin> ret; std::vector<PortPin> ret;
for (auto &it : bels.at(bel).pins) for (auto &it : bels.at(bel).pins)
ret.push_back(it.first); ret.push_back(it.first);
return ret;
} }
// --------------------------------------------------------------- // ---------------------------------------------------------------
@ -247,6 +269,8 @@ WireId Arch::getWireByName(IdString name) const
IdString Arch::getWireName(WireId wire) const { return wire; } 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 uint32_t Arch::getWireChecksum(WireId wire) const
{ {
// FIXME // FIXME
@ -297,6 +321,8 @@ PipId Arch::getPipByName(IdString name) const
IdString Arch::getPipName(PipId pip) const { return pip; } 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 uint32_t Arch::getPipChecksum(PipId wire) const
{ {
// FIXME // FIXME
@ -368,13 +394,6 @@ const std::vector<GroupId> &Arch::getGroupGroups(GroupId group) const { return g
// --------------------------------------------------------------- // ---------------------------------------------------------------
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
{
x = bels.at(bel).x;
y = bels.at(bel).y;
gb = bels.at(bel).gb;
}
delay_t Arch::estimateDelay(WireId src, WireId dst) const delay_t Arch::estimateDelay(WireId src, WireId dst) const
{ {
const WireInfo &s = wires.at(src); const WireInfo &s = wires.at(src);
@ -384,10 +403,7 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
return (dx + dy) * grid_distance_to_delay; return (dx + dy) * grid_distance_to_delay;
} }
delay_t Arch::getBudgetOverride(const PortRef& pr, delay_t v) const delay_t Arch::getBudgetOverride(NetInfo *net_info, int user_idx, delay_t budget) const { return budget; }
{
return v;
}
// --------------------------------------------------------------- // ---------------------------------------------------------------

View File

@ -31,7 +31,7 @@ struct WireInfo;
struct PipInfo struct PipInfo
{ {
IdString name, bound_net; IdString name, type, bound_net;
WireId srcWire, dstWire; WireId srcWire, dstWire;
DelayInfo delay; DelayInfo delay;
DecalXY decalxy; DecalXY decalxy;
@ -39,7 +39,7 @@ struct PipInfo
struct WireInfo struct WireInfo
{ {
IdString name, bound_net; IdString name, type, bound_net;
std::vector<PipId> downhill, uphill, aliases; std::vector<PipId> downhill, uphill, aliases;
BelPin uphill_bel_pin; BelPin uphill_bel_pin;
std::vector<BelPin> downhill_bel_pins; std::vector<BelPin> downhill_bel_pins;
@ -96,9 +96,9 @@ struct Arch : BaseCtx
float grid_distance_to_delay; float grid_distance_to_delay;
void addWire(IdString name, int x, int y); void addWire(IdString name, IdString type, int x, int y);
void addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay); void addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay);
void addAlias(IdString name, 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 addBel(IdString name, IdString type, Loc loc, bool gb);
void addBelInput(IdString bel, IdString name, IdString wire); void addBelInput(IdString bel, IdString name, IdString wire);
@ -141,7 +141,7 @@ struct Arch : BaseCtx
IdString getBelName(BelId bel) const; IdString getBelName(BelId bel) const;
Loc getBelLocation(BelId bel) const; Loc getBelLocation(BelId bel) const;
BelId getBelByLocation(Loc loc) const; BelId getBelByLocation(Loc loc) const;
std::vector<BelId> getBelsByTile(int x, int y) const; const std::vector<BelId> &getBelsByTile(int x, int y) const;
bool getBelGlobalBuf(BelId bel) const; bool getBelGlobalBuf(BelId bel) const;
uint32_t getBelChecksum(BelId bel) const; uint32_t getBelChecksum(BelId bel) const;
void bindBel(BelId bel, IdString cell, PlaceStrength strength); void bindBel(BelId bel, IdString cell, PlaceStrength strength);
@ -157,6 +157,7 @@ struct Arch : BaseCtx
WireId getWireByName(IdString name) const; WireId getWireByName(IdString name) const;
IdString getWireName(WireId wire) const; IdString getWireName(WireId wire) const;
IdString getWireType(WireId wire) const;
uint32_t getWireChecksum(WireId wire) const; uint32_t getWireChecksum(WireId wire) const;
void bindWire(WireId wire, IdString net, PlaceStrength strength); void bindWire(WireId wire, IdString net, PlaceStrength strength);
void unbindWire(WireId wire); void unbindWire(WireId wire);
@ -169,6 +170,7 @@ struct Arch : BaseCtx
PipId getPipByName(IdString name) const; PipId getPipByName(IdString name) const;
IdString getPipName(PipId pip) const; IdString getPipName(PipId pip) const;
IdString getPipType(PipId pip) const;
uint32_t getPipChecksum(PipId pip) const; uint32_t getPipChecksum(PipId pip) const;
void bindPip(PipId pip, IdString net, PlaceStrength strength); void bindPip(PipId pip, IdString net, PlaceStrength strength);
void unbindPip(PipId pip); void unbindPip(PipId pip);
@ -191,13 +193,12 @@ struct Arch : BaseCtx
const std::vector<PipId> &getGroupPips(GroupId group) const; const std::vector<PipId> &getGroupPips(GroupId group) const;
const std::vector<GroupId> &getGroupGroups(GroupId group) const; const std::vector<GroupId> &getGroupGroups(GroupId group) const;
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const;
delay_t estimateDelay(WireId src, WireId dst) const; delay_t estimateDelay(WireId src, WireId dst) const;
delay_t getDelayEpsilon() const { return 0.01; } delay_t getDelayEpsilon() const { return 0.01; }
delay_t getRipupDelayPenalty() const { return 1.0; } delay_t getRipupDelayPenalty() const { return 1.0; }
float getDelayNS(delay_t v) const { return v; } float getDelayNS(delay_t v) const { return v; }
uint32_t getDelayChecksum(delay_t v) const { return 0; } uint32_t getDelayChecksum(delay_t v) const { return 0; }
delay_t getBudgetOverride(const PortRef& pr, delay_t v) const; delay_t getBudgetOverride(NetInfo *net_info, int user_idx, delay_t budget) const;
bool pack() { return true; } bool pack() { return true; }
bool place(); bool place();

View File

@ -20,12 +20,22 @@
#ifndef NO_PYTHON #ifndef NO_PYTHON
#include "arch_pybindings.h"
#include "nextpnr.h" #include "nextpnr.h"
#include "pybindings.h" #include "pybindings.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
void arch_wrap_python() { class_<ArchArgs>("ArchArgs"); } void arch_wrap_python()
{
using namespace PythonConversion;
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
.def("checksum", &Context::checksum)
.def("pack", &Context::pack)
.def("place", &Context::place)
.def("route", &Context::route);
}
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -75,18 +75,16 @@ int main(int argc, char *argv[])
} }
if (vm.count("help") || argc == 1) { if (vm.count("help") || argc == 1) {
std::cout << boost::filesystem::basename(argv[0]) std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git "
<< " -- Next Generation Place and Route (git " "sha1 " GIT_COMMIT_HASH_STR ")\n";
"sha1 " GIT_COMMIT_HASH_STR ")\n";
std::cout << "\n"; std::cout << "\n";
std::cout << options << "\n"; std::cout << options << "\n";
return argc != 1; return argc != 1;
} }
if (vm.count("version")) { if (vm.count("version")) {
std::cout << boost::filesystem::basename(argv[0]) std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git "
<< " -- Next Generation Place and Route (git " "sha1 " GIT_COMMIT_HASH_STR ")\n";
"sha1 " GIT_COMMIT_HASH_STR ")\n";
return 1; return 1;
} }

View File

@ -10,5 +10,9 @@
<file>resources/resultset_next.png</file> <file>resources/resultset_next.png</file>
<file>resources/resultset_last.png</file> <file>resources/resultset_last.png</file>
<file>resources/cross.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> </qresource>
</RCC> </RCC>

View File

@ -25,7 +25,6 @@
#include <QSplitter> #include <QSplitter>
#include "designwidget.h" #include "designwidget.h"
#include "fpgaviewwidget.h" #include "fpgaviewwidget.h"
#include "jsonparse.h"
#include "log.h" #include "log.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "pythontab.h" #include "pythontab.h"
@ -76,7 +75,7 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent
centralTabWidget->setTabsClosable(true); centralTabWidget->setTabsClosable(true);
connect(centralTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); connect(centralTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
FPGAViewWidget *fpgaView = new FPGAViewWidget(); fpgaView = new FPGAViewWidget();
centralTabWidget->addTab(fpgaView, "Graphics"); centralTabWidget->addTab(fpgaView, "Graphics");
centralTabWidget->tabBar()->tabButton(0, QTabBar::RightSide)->resize(0, 0); centralTabWidget->tabBar()->tabButton(0, QTabBar::RightSide)->resize(0, 0);
@ -163,4 +162,30 @@ void BaseMainWindow::createMenusAndBars()
mainToolBar->addAction(actionSave); 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 NEXTPNR_NAMESPACE_END

View File

@ -37,6 +37,7 @@ NEXTPNR_NAMESPACE_BEGIN
class PythonTab; class PythonTab;
class DesignWidget; class DesignWidget;
class FPGAViewWidget;
class BaseMainWindow : public QMainWindow class BaseMainWindow : public QMainWindow
{ {
@ -49,6 +50,7 @@ class BaseMainWindow : public QMainWindow
protected: protected:
void createMenusAndBars(); void createMenusAndBars();
void createGraphicsBar();
protected Q_SLOTS: protected Q_SLOTS:
void writeInfo(std::string text); void writeInfo(std::string text);
@ -70,12 +72,14 @@ class BaseMainWindow : public QMainWindow
QMenuBar *menuBar; QMenuBar *menuBar;
QToolBar *mainToolBar; QToolBar *mainToolBar;
QToolBar *graphicsToolBar;
QStatusBar *statusBar; QStatusBar *statusBar;
QAction *actionNew; QAction *actionNew;
QAction *actionOpen; QAction *actionOpen;
QAction *actionSave; QAction *actionSave;
QProgressBar *progressBar; QProgressBar *progressBar;
DesignWidget *designview; DesignWidget *designview;
FPGAViewWidget *fpgaView;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -35,6 +35,7 @@ class ElementTreeItem : public QTreeWidgetItem
ElementTreeItem(ElementType t, QString str, QTreeWidgetItem *parent) ElementTreeItem(ElementType t, QString str, QTreeWidgetItem *parent)
: QTreeWidgetItem(parent, QStringList(str)), type(t) : QTreeWidgetItem(parent, QStringList(str)), type(t)
{ {
this->setFlags(this->flags() & ~Qt::ItemIsSelectable);
} }
virtual ~ElementTreeItem(){}; virtual ~ElementTreeItem(){};
@ -49,6 +50,7 @@ class IdStringTreeItem : public ElementTreeItem
public: public:
IdStringTreeItem(IdString d, ElementType t, QString str, QTreeWidgetItem *parent) : ElementTreeItem(t, str, parent) IdStringTreeItem(IdString d, ElementType t, QString str, QTreeWidgetItem *parent) : ElementTreeItem(t, str, parent)
{ {
this->setFlags(this->flags() | Qt::ItemIsSelectable);
this->data = d; this->data = d;
} }
virtual ~IdStringTreeItem(){}; virtual ~IdStringTreeItem(){};
@ -68,6 +70,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
treeWidget->setColumnCount(1); treeWidget->setColumnCount(1);
treeWidget->setHeaderLabel("Items"); treeWidget->setHeaderLabel("Items");
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
treeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
// Add property view // Add property view
variantManager = new QtVariantPropertyManager(this); variantManager = new QtVariantPropertyManager(this);
@ -79,6 +82,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
propertyEditor->setPropertiesWithoutValueMarked(true); propertyEditor->setPropertiesWithoutValueMarked(true);
propertyEditor->show(); propertyEditor->show();
propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu); propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu);
propertyEditor->treeWidget()->setSelectionMode(QAbstractItemView::ExtendedSelection);
QLineEdit *lineEdit = new QLineEdit(); QLineEdit *lineEdit = new QLineEdit();
lineEdit->setClearButtonEnabled(true); lineEdit->setClearButtonEnabled(true);
@ -229,6 +233,7 @@ void DesignWidget::addToHistory(QTreeWidgetItem *item)
void DesignWidget::newContext(Context *ctx) void DesignWidget::newContext(Context *ctx)
{ {
highlightSelected.clear();
treeWidget->clear(); treeWidget->clear();
// reset pointers since they are not valid after clear // reset pointers since they are not valid after clear
nets_root = nullptr; nets_root = nullptr;
@ -247,6 +252,7 @@ void DesignWidget::newContext(Context *ctx)
QTreeWidgetItem *bel_root = new QTreeWidgetItem(treeWidget); QTreeWidgetItem *bel_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> bel_items; QMap<QString, QTreeWidgetItem *> bel_items;
bel_root->setText(0, "Bels"); bel_root->setText(0, "Bels");
bel_root->setFlags(bel_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, bel_root); treeWidget->insertTopLevelItem(0, bel_root);
if (ctx) { if (ctx) {
for (auto bel : ctx->getBels()) { for (auto bel : ctx->getBels()) {
@ -279,6 +285,7 @@ void DesignWidget::newContext(Context *ctx)
QTreeWidgetItem *wire_root = new QTreeWidgetItem(treeWidget); QTreeWidgetItem *wire_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> wire_items; QMap<QString, QTreeWidgetItem *> wire_items;
wire_root->setText(0, "Wires"); wire_root->setText(0, "Wires");
wire_root->setFlags(wire_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, wire_root); treeWidget->insertTopLevelItem(0, wire_root);
if (ctx) { if (ctx) {
for (auto wire : ctx->getWires()) { for (auto wire : ctx->getWires()) {
@ -310,7 +317,9 @@ void DesignWidget::newContext(Context *ctx)
QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget); QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> pip_items; QMap<QString, QTreeWidgetItem *> pip_items;
pip_root->setText(0, "Pips"); pip_root->setText(0, "Pips");
pip_root->setFlags(pip_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, pip_root); treeWidget->insertTopLevelItem(0, pip_root);
#ifndef ARCH_ECP5
if (ctx) { if (ctx) {
for (auto pip : ctx->getPips()) { for (auto pip : ctx->getPips()) {
auto id = ctx->getPipName(pip); auto id = ctx->getPipName(pip);
@ -337,54 +346,88 @@ void DesignWidget::newContext(Context *ctx)
for (auto pip : nameToItem[2].toStdMap()) { for (auto pip : nameToItem[2].toStdMap()) {
pip_root->addChild(pip.second); pip_root->addChild(pip.second);
} }
#endif
nets_root = new QTreeWidgetItem(treeWidget);
nets_root->setText(0, "Nets");
nets_root->setFlags(nets_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, nets_root);
cells_root = new QTreeWidgetItem(treeWidget);
cells_root->setText(0, "Cells");
cells_root->setFlags(cells_root->flags() & ~Qt::ItemIsSelectable);
treeWidget->insertTopLevelItem(0, cells_root);
updateTree(); updateTree();
} }
void DesignWidget::updateTree() void DesignWidget::updateTree()
{ {
clearProperties(); if (!ctx)
delete nets_root; return;
delete cells_root;
nameToItem[3].clear();
nameToItem[4].clear();
clearProperties();
// treeWidget->setSortingEnabled(false);
// Remove nets not existing any more
QMap<QString, QTreeWidgetItem *>::iterator i = nameToItem[3].begin();
while (i != nameToItem[3].end()) {
QMap<QString, QTreeWidgetItem *>::iterator prev = i;
++i;
if (ctx->nets.find(ctx->id(prev.key().toStdString())) == ctx->nets.end()) {
if (treeWidget->currentItem() == prev.value())
treeWidget->setCurrentItem(nets_root);
if (highlightSelected.contains(prev.value()))
highlightSelected.remove(prev.value());
delete prev.value();
nameToItem[3].erase(prev);
}
}
// Add nets to tree // Add nets to tree
nets_root = new QTreeWidgetItem(treeWidget); for (auto &item : ctx->nets) {
nets_root->setText(0, "Nets"); auto id = item.first;
treeWidget->insertTopLevelItem(0, nets_root); QString name = QString(id.c_str(ctx));
if (ctx) { if (!nameToItem[3].contains(name)) {
for (auto &item : ctx->nets) {
auto id = item.first;
QString name = QString(id.c_str(ctx));
IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::NET, name, nullptr); IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::NET, name, nullptr);
nets_root->addChild(newItem);
nameToItem[3].insert(name, newItem); nameToItem[3].insert(name, newItem);
} }
} }
for (auto item : nameToItem[3].toStdMap()) {
nets_root->addChild(item.second);
}
// Remove cells not existing any more
i = nameToItem[4].begin();
while (i != nameToItem[4].end()) {
QMap<QString, QTreeWidgetItem *>::iterator prev = i;
++i;
if (ctx->cells.find(ctx->id(prev.key().toStdString())) == ctx->cells.end()) {
if (treeWidget->currentItem() == prev.value())
treeWidget->setCurrentItem(cells_root);
if (highlightSelected.contains(prev.value()))
highlightSelected.remove(prev.value());
delete prev.value();
nameToItem[4].erase(prev);
}
}
// Add cells to tree // Add cells to tree
cells_root = new QTreeWidgetItem(treeWidget); for (auto &item : ctx->cells) {
cells_root->setText(0, "Cells"); auto id = item.first;
treeWidget->insertTopLevelItem(0, cells_root); QString name = QString(id.c_str(ctx));
if (ctx) { if (!nameToItem[4].contains(name)) {
for (auto &item : ctx->cells) {
auto id = item.first;
QString name = QString(id.c_str(ctx));
IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::CELL, name, nullptr); IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::CELL, name, nullptr);
cells_root->addChild(newItem);
nameToItem[4].insert(name, newItem); nameToItem[4].insert(name, newItem);
} }
} }
for (auto item : nameToItem[4].toStdMap()) { // treeWidget->sortByColumn(0, Qt::AscendingOrder);
cells_root->addChild(item.second); // treeWidget->setSortingEnabled(true);
}
} }
QtProperty *DesignWidget::addTopLevelProperty(const QString &id) QtProperty *DesignWidget::addTopLevelProperty(const QString &id)
{ {
QtProperty *topItem = groupManager->addProperty(id); QtProperty *topItem = groupManager->addProperty(id);
propertyToId[topItem] = id; propertyToId[topItem] = id;
idToProperty[id] = topItem; idToProperty[id] = topItem;
topItem->setSelectable(false);
propertyEditor->addProperty(topItem); propertyEditor->addProperty(topItem);
return topItem; return topItem;
} }
@ -452,12 +495,14 @@ void DesignWidget::addProperty(QtProperty *topItem, int propertyType, const QStr
QtVariantProperty *item = readOnlyManager->addProperty(propertyType, name); QtVariantProperty *item = readOnlyManager->addProperty(propertyType, name);
item->setValue(value); item->setValue(value);
item->setPropertyId(getElementTypeName(type)); item->setPropertyId(getElementTypeName(type));
item->setSelectable(type != ElementType::NONE);
topItem->addSubProperty(item); topItem->addSubProperty(item);
} }
QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name) QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name)
{ {
QtProperty *item = groupManager->addProperty(name); QtProperty *item = groupManager->addProperty(name);
item->setSelectable(false);
topItem->addSubProperty(item); topItem->addSubProperty(item);
return item; return item;
} }
@ -467,6 +512,18 @@ void DesignWidget::onItemSelectionChanged()
if (treeWidget->selectedItems().size() == 0) if (treeWidget->selectedItems().size() == 0)
return; return;
if (treeWidget->selectedItems().size() > 1) {
std::vector<DecalXY> decals;
for (auto clickItem : treeWidget->selectedItems()) {
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);
return;
}
QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0); QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0);
if (!clickItem->parent()) if (!clickItem->parent())
@ -510,6 +567,7 @@ void DesignWidget::onItemSelectionChanged()
QtProperty *topItem = addTopLevelProperty("Wire"); QtProperty *topItem = addTopLevelProperty("Wire");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx)); 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::Bool, "Available", ctx->checkWireAvail(wire));
addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundWireNet(wire).c_str(ctx), ElementType::NET); 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), addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx),
@ -561,6 +619,7 @@ void DesignWidget::onItemSelectionChanged()
QtProperty *topItem = addTopLevelProperty("Pip"); QtProperty *topItem = addTopLevelProperty("Pip");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx)); 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::Bool, "Available", ctx->checkPipAvail(pip));
addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundPipNet(pip).c_str(ctx), ElementType::NET); 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), addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingPipNet(pip).c_str(ctx),
@ -718,52 +777,64 @@ std::vector<DecalXY> DesignWidget::getDecals(ElementType type, IdString value)
return decals; return decals;
} }
void DesignWidget::updateHighlightGroup(QTreeWidgetItem *item, int group) void DesignWidget::updateHighlightGroup(QList<QTreeWidgetItem *> items, int group)
{ {
if (highlightSelected.contains(item)) { const bool shouldClear = items.size() == 1;
if (highlightSelected[item] == group) { for (auto item : items) {
highlightSelected.remove(item); if (highlightSelected.contains(item)) {
if (shouldClear && highlightSelected[item] == group) {
highlightSelected.remove(item);
} else
highlightSelected[item] = group;
} else } else
highlightSelected[item] = group; highlightSelected.insert(item, group);
} else }
highlightSelected.insert(item, group); std::vector<DecalXY> decals[8];
std::vector<DecalXY> decals;
for (auto it : highlightSelected.toStdMap()) { for (auto it : highlightSelected.toStdMap()) {
if (it.second == group) { ElementType type = static_cast<ElementTreeItem *>(it.first)->getType();
ElementType type = static_cast<ElementTreeItem *>(it.first)->getType(); IdString value = static_cast<IdStringTreeItem *>(it.first)->getData();
IdString value = static_cast<IdStringTreeItem *>(it.first)->getData(); std::vector<DecalXY> d = getDecals(type, value);
std::vector<DecalXY> d = getDecals(type, value); std::move(d.begin(), d.end(), std::back_inserter(decals[it.second]));
std::move(d.begin(), d.end(), std::back_inserter(decals));
}
} }
for (int i = 0; i < 8; i++)
Q_EMIT highlight(decals, group); Q_EMIT highlight(decals[i], i);
} }
void DesignWidget::prepareMenuProperty(const QPoint &pos) void DesignWidget::prepareMenuProperty(const QPoint &pos)
{ {
QTreeWidget *tree = propertyEditor->treeWidget(); QTreeWidget *tree = propertyEditor->treeWidget();
QList<QTreeWidgetItem *> items;
itemContextMenu = tree->itemAt(pos); for (auto itemContextMenu : tree->selectedItems()) {
if (itemContextMenu->parent() == nullptr) QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu);
return; if (!browserItem)
continue;
QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu); QtProperty *selectedProperty = browserItem->property();
if (!browserItem) ElementType type = getElementTypeByName(selectedProperty->propertyId());
return; if (type == ElementType::NONE)
QtProperty *selectedProperty = browserItem->property(); continue;
ElementType type = getElementTypeByName(selectedProperty->propertyId()); IdString value = ctx->id(selectedProperty->valueText().toStdString());
if (type == ElementType::NONE) items.append(nameToItem[getElementIndex(type)].value(value.c_str(ctx)));
return; }
IdString value = ctx->id(selectedProperty->valueText().toStdString()); int selectedIndex = -1;
if (items.size() == 1) {
QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx)); QTreeWidgetItem *item = items.at(0);
if (highlightSelected.contains(item))
selectedIndex = highlightSelected[item];
}
QMenu menu(this); QMenu menu(this);
QAction *selectAction = new QAction("&Select", 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); menu.addAction(selectAction);
QMenu *subMenu = menu.addMenu("Highlight"); QMenu *subMenu = menu.addMenu("Highlight");
@ -776,27 +847,24 @@ void DesignWidget::prepareMenuProperty(const QPoint &pos)
action->setCheckable(true); action->setCheckable(true);
subMenu->addAction(action); subMenu->addAction(action);
group->addAction(action); group->addAction(action);
if (highlightSelected.contains(item) && highlightSelected[item] == i) if (selectedIndex == i)
action->setChecked(true); 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(tree->mapToGlobal(pos));
} }
void DesignWidget::prepareMenuTree(const QPoint &pos) void DesignWidget::prepareMenuTree(const QPoint &pos)
{ {
QTreeWidget *tree = treeWidget; if (treeWidget->selectedItems().size() == 0)
itemContextMenu = tree->itemAt(pos);
ElementType type = static_cast<ElementTreeItem *>(itemContextMenu)->getType();
IdString value = static_cast<IdStringTreeItem *>(itemContextMenu)->getData();
if (type == ElementType::NONE)
return; return;
int selectedIndex = -1;
QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx)); 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 menu(this);
QMenu *subMenu = menu.addMenu("Highlight"); QMenu *subMenu = menu.addMenu("Highlight");
QActionGroup *group = new QActionGroup(this); QActionGroup *group = new QActionGroup(this);
@ -808,11 +876,11 @@ void DesignWidget::prepareMenuTree(const QPoint &pos)
action->setCheckable(true); action->setCheckable(true);
subMenu->addAction(action); subMenu->addAction(action);
group->addAction(action); group->addAction(action);
if (highlightSelected.contains(item) && highlightSelected[item] == i) if (selectedIndex == i)
action->setChecked(true); 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) void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column)
@ -821,14 +889,8 @@ void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column)
ElementType type = getElementTypeByName(selectedProperty->propertyId()); ElementType type = getElementTypeByName(selectedProperty->propertyId());
QString value = selectedProperty->valueText(); QString value = selectedProperty->valueText();
int index = getElementIndex(type); int index = getElementIndex(type);
switch (type) { if (type != ElementType::NONE && nameToItem[index].contains(value))
case ElementType::NONE: treeWidget->setCurrentItem(nameToItem[index].value(value));
return;
default: {
if (nameToItem[index].contains(value))
treeWidget->setCurrentItem(nameToItem[index].value(value));
} break;
}
} }
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -60,7 +60,7 @@ class DesignWidget : public QWidget
void updateButtons(); void updateButtons();
void addToHistory(QTreeWidgetItem *item); void addToHistory(QTreeWidgetItem *item);
std::vector<DecalXY> getDecals(ElementType type, IdString value); std::vector<DecalXY> getDecals(ElementType type, IdString value);
void updateHighlightGroup(QTreeWidgetItem *item, int group); void updateHighlightGroup(QList<QTreeWidgetItem *> item, int group);
Q_SIGNALS: Q_SIGNALS:
void info(std::string text); void info(std::string text);
void selected(std::vector<DecalXY> decal); void selected(std::vector<DecalXY> decal);
@ -85,7 +85,6 @@ class DesignWidget : public QWidget
QtGroupPropertyManager *groupManager; QtGroupPropertyManager *groupManager;
QtVariantEditorFactory *variantFactory; QtVariantEditorFactory *variantFactory;
QtTreePropertyBrowser *propertyEditor; QtTreePropertyBrowser *propertyEditor;
QTreeWidgetItem *itemContextMenu;
QMap<QtProperty *, QString> propertyToId; QMap<QtProperty *, QString> propertyToId;
QMap<QString, QtProperty *> idToProperty; QMap<QString, QtProperty *> idToProperty;

View File

@ -31,222 +31,15 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur, FPGAViewWidget::FPGAViewWidget(QWidget *parent) :
const QVector2D *next) const QOpenGLWidget(parent), ctx_(nullptr), paintTimer_(this),
{ lineShader_(this), zoom_(500.0f),
// buildPoint emits two vertices per line point, along with normals to move rendererData_(new FPGAViewWidget::RendererData),
// them the right directio when rendering and miter to compensate for rendererArgs_(new FPGAViewWidget::RendererArgs)
// 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();
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)
{ {
colors_.background = QColor("#000000"); colors_.background = QColor("#000000");
colors_.grid = QColor("#333"); colors_.grid = QColor("#333");
colors_.frame = QColor("#d0d0d0"); colors_.frame = QColor("#808080");
colors_.hidden = QColor("#606060"); colors_.hidden = QColor("#606060");
colors_.inactive = QColor("#303030"); colors_.inactive = QColor("#303030");
colors_.active = QColor("#f0f0f0"); colors_.active = QColor("#f0f0f0");
@ -289,6 +82,9 @@ FPGAViewWidget::~FPGAViewWidget() {}
void FPGAViewWidget::newContext(Context *ctx) void FPGAViewWidget::newContext(Context *ctx)
{ {
ctx_ = ctx; ctx_ = ctx;
onSelectedArchItem(std::vector<DecalXY>());
for (int i = 0; i < 8; i++)
onHighlightGroupChanged(std::vector<DecalXY>(), i);
pokeRenderer(); pokeRenderer();
} }
@ -306,69 +102,49 @@ void FPGAViewWidget::initializeGL()
0.0); 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; const float scale = 1.0;
float offsetX = 0.0, offsetY = 0.0;
for (auto &el : ctx_->getDecalGraphics(decal.decal)) { if (el.type == GraphicElement::TYPE_BOX) {
offsetX = decal.x; auto line = PolyLine(true);
offsetY = decal.y; 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_BOX) { if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) {
auto line = PolyLine(true); PolyLine(x + scale * el.x1, y + scale * el.y1, x + scale * el.x2, y + scale * el.y2)
line.point(offsetX + scale * el.x1, offsetY + scale * el.y1); .build(out);
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::G_LINE) {
PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + 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 = decal.x;
float offsetX = 0.0, offsetY = 0.0; float offsetY = decal.y;
for (auto &el : ctx_->getDecalGraphics(decal.decal)) { for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
offsetX = decal.x; drawGraphicElement(out, el, offsetX, offsetY);
offsetY = decal.y; }
}
if (el.type == GraphicElement::G_BOX) { void FPGAViewWidget::drawArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], const DecalXY &decal)
auto line = PolyLine(true); {
line.point(offsetX + scale * el.x1, offsetY + scale * el.y1); float offsetX = decal.x;
line.point(offsetX + scale * el.x2, offsetY + scale * el.y1); float offsetY = decal.y;
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;
}
}
if (el.type == GraphicElement::G_LINE) { for (auto &el : ctx_->getDecalGraphics(decal.decal)) {
auto line = PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, switch (el.style) {
offsetY + scale * el.y2); case GraphicElement::STYLE_FRAME:
switch (el.style) { case GraphicElement::STYLE_INACTIVE:
case GraphicElement::G_FRAME: case GraphicElement::STYLE_ACTIVE:
case GraphicElement::G_INACTIVE: drawGraphicElement(out[el.style], el, offsetX, offsetY);
case GraphicElement::G_ACTIVE: break;
line.build(out[el.style]); default:
break; break;
default:
break;
}
} }
} }
} }
@ -398,24 +174,28 @@ void FPGAViewWidget::paintGL()
float thick1Px = mouseToWorldCoordinates(1, 0).x(); float thick1Px = mouseToWorldCoordinates(1, 0).x();
float thick11Px = mouseToWorldCoordinates(1.1, 0).x(); float thick11Px = mouseToWorldCoordinates(1.1, 0).x();
// Draw grid. // Render grid.
auto grid = LineShaderData(); auto grid = LineShaderData();
for (float i = -100.0f; i < 100.0f; i += 1.0f) { for (float i = -100.0f; i < 100.0f; i += 1.0f) {
PolyLine(-100.0f, i, 100.0f, i).build(grid); PolyLine(-100.0f, i, 100.0f, i).build(grid);
PolyLine(i, -100.0f, i, 100.0f).build(grid); PolyLine(i, -100.0f, i, 100.0f).build(grid);
} }
// Draw grid.
lineShader_.draw(grid, colors_.grid, thick1Px, matrix); lineShader_.draw(grid, colors_.grid, thick1Px, matrix);
rendererDataLock_.lock(); 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++) 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(); rendererDataLock_.unlock();
} }
@ -426,126 +206,154 @@ void FPGAViewWidget::renderLines(void)
if (ctx_ == nullptr) if (ctx_ == nullptr)
return; return;
ctx_->lock_ui(); // Data from Context needed to render all decals.
// For now, collapse any decal changes into change of all decals.
// TODO(q3k): fix this
bool decalsChanged = false;
if (ctx_->allUiReload) {
ctx_->allUiReload = false;
decalsChanged = true;
}
if (ctx_->frameUiReload) {
ctx_->frameUiReload = false;
decalsChanged = true;
}
if (ctx_->belUiReload.size() > 0) {
ctx_->belUiReload.clear();
decalsChanged = true;
}
if (ctx_->wireUiReload.size() > 0) {
ctx_->wireUiReload.clear();
decalsChanged = true;
}
if (ctx_->pipUiReload.size() > 0) {
ctx_->pipUiReload.clear();
decalsChanged = true;
}
if (ctx_->groupUiReload.size() > 0) {
ctx_->groupUiReload.clear();
decalsChanged = true;
}
// Local copy of decals, taken as fast as possible to not block the P&R.
std::vector<DecalXY> belDecals; std::vector<DecalXY> belDecals;
std::vector<DecalXY> wireDecals; std::vector<DecalXY> wireDecals;
std::vector<DecalXY> pipDecals; std::vector<DecalXY> pipDecals;
std::vector<DecalXY> groupDecals; std::vector<DecalXY> groupDecals;
if (decalsChanged) { bool decalsChanged = false;
for (auto bel : ctx_->getBels()) { {
belDecals.push_back(ctx_->getBelDecal(bel)); // 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()) { if (ctx_->frameUiReload) {
wireDecals.push_back(ctx_->getWireDecal(wire)); ctx_->frameUiReload = false;
decalsChanged = true;
} }
for (auto pip : ctx_->getPips()) { if (ctx_->belUiReload.size() > 0) {
pipDecals.push_back(ctx_->getPipDecal(pip)); ctx_->belUiReload.clear();
decalsChanged = true;
} }
for (auto group : ctx_->getGroups()) { if (ctx_->wireUiReload.size() > 0) {
groupDecals.push_back(ctx_->getGroupDecal(group)); 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(); // Arguments from the main UI thread on what we should render.
auto selectedItems = rendererArgs_->selectedItems; std::vector<DecalXY> selectedDecals;
auto highlightedItems = rendererArgs_->highlightedItems; std::vector<DecalXY> highlightedDecals[8];
auto highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged; bool highlightedOrSelectedChanged;
rendererArgs_->highlightedOrSelectedChanged = false; {
rendererArgsLock_.unlock(); // 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) { if (decalsChanged) {
auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData); auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);
// Draw Bels. // Draw Bels.
for (auto const &decal : belDecals) { for (auto const &decal : belDecals) {
drawDecal(data->decals, decal); drawArchDecal(data->gfxByStyle, decal);
} }
// Draw Wires. // Draw Wires.
for (auto const &decal : wireDecals) { for (auto const &decal : wireDecals) {
drawDecal(data->decals, decal); drawArchDecal(data->gfxByStyle, decal);
} }
// Draw Pips. // Draw Pips.
for (auto const &decal : pipDecals) { for (auto const &decal : pipDecals) {
drawDecal(data->decals, decal); drawArchDecal(data->gfxByStyle, decal);
} }
// Draw Groups. // Draw Groups.
for (auto const &decal : groupDecals) { for (auto const &decal : groupDecals) {
drawDecal(data->decals, decal); drawArchDecal(data->gfxByStyle, decal);
} }
// Swap over. // Swap over.
rendererDataLock_.lock(); {
rendererData_ = std::move(data); QMutexLocker lock(&rendererDataLock_);
rendererDataLock_.unlock();
// 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 (highlightedOrSelectedChanged) {
if (decalsChanged || highlightedOrSelectedChanged) { QMutexLocker locker(&rendererDataLock_);
rendererData_->selected.clear();
for (auto &decal : selectedItems) { // Render selected.
drawDecal(rendererData_->selected, decal); rendererData_->gfxSelected.clear();
for (auto &decal : selectedDecals) {
drawDecal(rendererData_->gfxSelected, decal);
} }
// Render highlighted.
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
rendererData_->highlighted[i].clear(); rendererData_->gfxHighlighted[i].clear();
for (auto &decal : highlightedItems[i]) { for (auto &decal : highlightedDecals[i]) {
drawDecal(rendererData_->highlighted[i], decal); drawDecal(rendererData_->gfxHighlighted[i], decal);
} }
} }
} }
rendererDataLock_.unlock();
} }
void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals) void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)
{ {
rendererArgsLock_.lock(); {
rendererArgs_->selectedItems = decals; QMutexLocker locker(&rendererArgsLock_);
rendererArgs_->highlightedOrSelectedChanged = true; rendererArgs_->selectedDecals = decals;
rendererArgsLock_.unlock(); rendererArgs_->highlightedOrSelectedChanged = true;
}
pokeRenderer(); pokeRenderer();
} }
void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group) void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)
{ {
rendererArgsLock_.lock(); {
rendererArgs_->highlightedItems[group] = decals; QMutexLocker locker(&rendererArgsLock_);
rendererArgs_->highlightedOrSelectedChanged = true; rendererArgs_->highlightedDecals[group] = decals;
rendererArgsLock_.unlock(); rendererArgs_->highlightedOrSelectedChanged = true;
}
pokeRenderer(); pokeRenderer();
} }
void FPGAViewWidget::resizeGL(int width, int height) {} 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 // Invert the projection matrix to calculate screen/mouse to world/grid
// coordinates. // coordinates.
@ -561,9 +369,9 @@ QVector4D FPGAViewWidget::mouseToWorldCoordinates(int x, int y)
void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event) void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
{ {
const int dx = event->x() - lastPos_.x(); const int dx = event->x() - lastDragPos_.x();
const int dy = event->y() - lastPos_.y(); const int dy = event->y() - lastDragPos_.y();
lastPos_ = event->pos(); lastDragPos_ = event->pos();
auto world = mouseToWorldCoordinates(dx, dy); auto world = mouseToWorldCoordinates(dx, dy);
viewMove_.translate(world.x(), -world.y()); viewMove_.translate(world.x(), -world.y());
@ -575,21 +383,32 @@ void FPGAViewWidget::wheelEvent(QWheelEvent *event)
{ {
QPoint degree = event->angleDelta() / 8; QPoint degree = event->angleDelta() / 8;
if (!degree.isNull()) { if (!degree.isNull())
zoom(degree.y());
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();
}
} }
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 NEXTPNR_NAMESPACE_END

View File

@ -33,183 +33,10 @@
#include <QWaitCondition> #include <QWaitCondition>
#include "nextpnr.h" #include "nextpnr.h"
#include "lineshader.h"
NEXTPNR_NAMESPACE_BEGIN 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 class PeriodicRunner : public QThread
{ {
Q_OBJECT Q_OBJECT
@ -267,53 +94,43 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
FPGAViewWidget(QWidget *parent = 0); FPGAViewWidget(QWidget *parent = 0);
~FPGAViewWidget(); ~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: protected:
// Qt callbacks.
void initializeGL() Q_DECL_OVERRIDE; void initializeGL() Q_DECL_OVERRIDE;
void paintGL() Q_DECL_OVERRIDE; void paintGL() Q_DECL_OVERRIDE;
void resizeGL(int width, int height) Q_DECL_OVERRIDE; void resizeGL(int width, int height) Q_DECL_OVERRIDE;
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
void drawDecal(LineShaderData &data, const DecalXY &decal); QSize minimumSizeHint() const override;
void drawDecal(LineShaderData out[], const DecalXY &decal); QSize sizeHint() const override;
public Q_SLOTS: public Q_SLOTS:
void newContext(Context *ctx); void newContext(Context *ctx);
void onSelectedArchItem(std::vector<DecalXY> decals); void onSelectedArchItem(std::vector<DecalXY> decals);
void onHighlightGroupChanged(std::vector<DecalXY> decals, int group); void onHighlightGroupChanged(std::vector<DecalXY> decals, int group);
void pokeRenderer(void); void pokeRenderer(void);
void zoomIn();
void zoomOut();
void zoomSelected();
void zoomOutbound();
private: 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 zoomNear_ = 1.0f; // do not zoom closer than this
const float zoomFar_ = 10000.0f; // do not zoom further than this const float zoomFar_ = 10000.0f; // do not zoom further than this
const float zoomLvl1_ = 100.0f; const float zoomLvl1_ = 100.0f;
const float zoomLvl2_ = 50.0f; const float zoomLvl2_ = 50.0f;
Context *ctx_; Context *ctx_;
QTimer paintTimer_; QTimer paintTimer_;
std::unique_ptr<PeriodicRunner> renderRunner_; std::unique_ptr<PeriodicRunner> renderRunner_;
QPoint lastDragPos_;
LineShader lineShader_;
QMatrix4x4 viewMove_;
float zoom_;
struct struct
{ {
QColor background; QColor background;
@ -328,22 +145,29 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions
struct RendererData struct RendererData
{ {
LineShaderData decals[4]; LineShaderData gfxByStyle[GraphicElement::STYLE_MAX];
LineShaderData selected; LineShaderData gfxSelected;
LineShaderData highlighted[8]; LineShaderData gfxHighlighted[8];
}; };
std::unique_ptr<RendererData> rendererData_;
QMutex rendererDataLock_;
struct RendererArgs struct RendererArgs
{ {
std::vector<DecalXY> selectedItems; std::vector<DecalXY> selectedDecals;
std::vector<DecalXY> highlightedItems[8]; std::vector<DecalXY> highlightedDecals[8];
bool highlightedOrSelectedChanged; bool highlightedOrSelectedChanged;
}; };
std::unique_ptr<RendererData> rendererData_;
QMutex rendererDataLock_;
std::unique_ptr<RendererArgs> rendererArgs_; std::unique_ptr<RendererArgs> rendererArgs_;
QMutex rendererArgsLock_; 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 NEXTPNR_NAMESPACE_END

View File

@ -159,6 +159,8 @@ void MainWindow::createMenu()
taskToolBar->addAction(actionPlay); taskToolBar->addAction(actionPlay);
taskToolBar->addAction(actionPause); taskToolBar->addAction(actionPause);
taskToolBar->addAction(actionStop); taskToolBar->addAction(actionStop);
createGraphicsBar();
} }
#if defined(_MSC_VER) #if defined(_MSC_VER)

View File

@ -109,7 +109,6 @@ void Worker::budget(double freq)
Q_EMIT taskStarted(); Q_EMIT taskStarted();
try { try {
ctx->target_freq = freq; ctx->target_freq = freq;
assign_budget(ctx);
Q_EMIT budget_finish(true); Q_EMIT budget_finish(true);
} catch (WorkerInterruptionRequested) { } catch (WorkerInterruptionRequested) {
Q_EMIT taskCanceled(); Q_EMIT taskCanceled();
@ -121,6 +120,8 @@ void Worker::place(bool timing_driven)
Q_EMIT taskStarted(); Q_EMIT taskStarted();
try { try {
ctx->timing_driven = timing_driven; ctx->timing_driven = timing_driven;
log_info("Assigned budget %0.2f MHz", ctx->target_freq / 1e6);
assign_budget(ctx);
Q_EMIT place_finished(ctx->place()); Q_EMIT place_finished(ctx->place());
} catch (WorkerInterruptionRequested) { } catch (WorkerInterruptionRequested) {
Q_EMIT taskCanceled(); Q_EMIT taskCanceled();

236
gui/lineshader.cc Normal file
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

View File

@ -2,6 +2,7 @@
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com> * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -187,6 +188,13 @@ Arch::Arch(ArchArgs args) : args(args)
id_i3 = id("I3"); id_i3 = id("I3");
id_dff_en = id("DFF_ENABLE"); id_dff_en = id("DFF_ENABLE");
id_neg_clk = id("NEG_CLK"); id_neg_clk = id("NEG_CLK");
id_cin = id("CIN");
id_cout = id("COUT");
id_o = id("O");
id_lo = id("LO");
id_icestorm_ram = id("ICESTORM_RAM");
id_rclk = id("RCLK");
id_wclk = id("WCLK");
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -291,23 +299,6 @@ BelRange Arch::getBelsByTile(int x, int y) const
return br; return br;
} }
BelRange Arch::getBelsAtSameTile(BelId bel) const
{
BelRange br;
NPNR_ASSERT(bel != BelId());
int x = chip_info->bel_data[bel.index].x;
int y = chip_info->bel_data[bel.index].y;
int start = bel.index, end = bel.index;
while (start >= 0 && chip_info->bel_data[start].x == x && chip_info->bel_data[start].y == y)
start--;
start++;
br.b.cursor = start;
while (end < chip_info->num_bels && chip_info->bel_data[end].x == x && chip_info->bel_data[end].y == y)
end++;
br.e.cursor = end;
return br;
}
PortType Arch::getBelPinType(BelId bel, PortPin pin) const PortType Arch::getBelPinType(BelId bel, PortPin pin) const
{ {
NPNR_ASSERT(bel != BelId()); NPNR_ASSERT(bel != BelId());
@ -331,11 +322,12 @@ WireId Arch::getBelPinWire(BelId bel, PortPin pin) const
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires; int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get(); const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
for (int i = 0; i < num_bel_wires; i++) for (int i = 0; i < num_bel_wires; i++) {
if (bel_wires[i].port == pin) { if (bel_wires[i].port == pin) {
ret.index = bel_wires[i].wire_index; ret.index = bel_wires[i].wire_index;
break; break;
} }
}
return ret; return ret;
} }
@ -448,11 +440,103 @@ GroupId Arch::getGroupByName(IdString name) const
return GroupId(); 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> Arch::getGroups() const
{ {
std::vector<GroupId> ret; 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; return ret;
} }
@ -482,14 +566,6 @@ std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
{
NPNR_ASSERT(bel != BelId());
x = chip_info->bel_data[bel.index].x;
y = chip_info->bel_data[bel.index].y;
gb = chip_info->bel_data[bel.index].type == TYPE_SB_GB;
}
delay_t Arch::estimateDelay(WireId src, WireId dst) const delay_t Arch::estimateDelay(WireId src, WireId dst) const
{ {
NPNR_ASSERT(src != WireId()); NPNR_ASSERT(src != WireId());
@ -511,10 +587,18 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
return xscale * abs(xd) + yscale * abs(yd) + offset; return xscale * abs(xd) + yscale * abs(yd) + offset;
} }
delay_t Arch::getBudgetOverride(const PortRef& pr, delay_t v) const delay_t Arch::getBudgetOverride(NetInfo *net_info, int user_idx, delay_t budget) const
{ {
if (pr.port == id("COUT")) return 0; const auto& driver = net_info->driver;
return v; if (driver.port == id_cout) {
const auto& sink = net_info->users[user_idx];
auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel);
if (driver_loc.y == sink_loc.y)
return 0;
return 250;
}
return budget;
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -574,22 +658,70 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
std::vector<GraphicElement> ret; std::vector<GraphicElement> ret;
if (decal.type == DecalId::TYPE_FRAME) { if (decal.type == DecalId::TYPE_FRAME) {
for (int x = 0; x <= chip_info->width; x++) /* nothing */
for (int y = 0; y <= chip_info->height; y++) { }
GraphicElement el;
el.type = GraphicElement::G_LINE; if (decal.type == DecalId::TYPE_GROUP) {
el.x1 = x - 0.05, el.x2 = x + 0.05, el.y1 = y, el.y2 = y, el.z = 0; int type = (decal.index >> 16) & 255;
ret.push_back(el); int x = (decal.index >> 8) & 255;
el.x1 = x, el.x2 = x, el.y1 = y - 0.05, el.y2 = y + 0.05, el.z = 0; int y = decal.index & 255;
ret.push_back(el);
} 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) { if (decal.type == DecalId::TYPE_WIRE) {
int n = chip_info->wire_data[decal.index].num_segments; int n = chip_info->wire_data[decal.index].num_segments;
const WireSegmentPOD *p = chip_info->wire_data[decal.index].segments.get(); 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++) for (int i = 0; i < n; i++)
gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style); gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style);
@ -597,7 +729,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
if (decal.type == DecalId::TYPE_PIP) { if (decal.type == DecalId::TYPE_PIP) {
const PipInfoPOD &p = chip_info->pip_data[decal.index]; 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); gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style);
} }
@ -609,111 +741,40 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
if (bel_type == TYPE_ICESTORM_LC) { if (bel_type == TYPE_ICESTORM_LC) {
GraphicElement el; GraphicElement el;
el.type = GraphicElement::G_BOX; el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE; el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1; 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.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
(chip_info->bel_data[bel.index].z) * logic_cell_pitch; (chip_info->bel_data[bel.index].z) * logic_cell_pitch;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 +
(chip_info->bel_data[bel.index].z) * logic_cell_pitch; (chip_info->bel_data[bel.index].z) * logic_cell_pitch;
el.z = 0;
ret.push_back(el); 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 (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;
GraphicElement el; el.type = GraphicElement::TYPE_BOX;
el.type = GraphicElement::G_BOX; el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + 0.1; el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
el.x2 = chip_info->bel_data[bel.index].x + 0.9; el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
if (chip_info->bel_data[bel.index].z == 0) { el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
el.y1 = chip_info->bel_data[bel.index].y + 0.10; (4 * chip_info->bel_data[bel.index].z) * logic_cell_pitch;
el.y2 = chip_info->bel_data[bel.index].y + 0.45; el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 +
} else { (4 * chip_info->bel_data[bel.index].z + 3) * logic_cell_pitch;
el.y1 = chip_info->bel_data[bel.index].y + 0.55; ret.push_back(el);
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);
}
} }
if (bel_type == TYPE_ICESTORM_RAM) { if (bel_type == TYPE_ICESTORM_RAM) {
for (int i = 0; i < 2; i++) { 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; GraphicElement el;
el.type = GraphicElement::G_BOX; el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE; el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1; 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.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1; 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 + 7 * logic_cell_pitch; el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + i + 7 * logic_cell_pitch;
el.z = 0;
ret.push_back(el); 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);
} }
} }
} }
@ -725,20 +786,28 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, delay_t &delay) const
{ {
if (cell->type == id("ICESTORM_LC")) { if (cell->type == id_icestorm_lc) {
if ((fromPort == id("I0") || fromPort == id("I1") || fromPort == id("I2") || fromPort == id("I3")) && if ((fromPort == id_i0 || fromPort == id_i1 || fromPort == id_i2 || fromPort == id_i3) &&
(toPort == id("O") || toPort == id("LO"))) { (toPort == id_o || toPort == id_lo)) {
delay = 450; delay = 450;
return true; return true;
} else if (fromPort == id("CIN") && toPort == id("COUT")) { } else if (fromPort == id_cin && toPort == id_cout) {
delay = 120; delay = 120;
return true; return true;
} else if (fromPort == id("I1") && toPort == id("COUT")) { } else if (fromPort == id_i1 && toPort == id_cout) {
delay = 260; delay = 260;
return true; return true;
} else if (fromPort == id("I2") && toPort == id("COUT")) { } else if (fromPort == id_i2 && toPort == id_cout) {
delay = 230; delay = 230;
return true; return true;
} else if (fromPort == id_clk && toPort == id_o) {
delay = 540;
return true;
}
} else if (cell->type == id_icestorm_ram) {
if (fromPort == id_rclk) {
delay = 2140;
return true;
} }
} }
return false; return false;
@ -746,9 +815,14 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
IdString Arch::getPortClock(const CellInfo *cell, IdString port) const IdString Arch::getPortClock(const CellInfo *cell, IdString port) const
{ {
if (cell->type == id("ICESTORM_LC") && bool_or_default(cell->params, id("DFF_ENABLE"))) { if (cell->type == id_icestorm_lc && cell->lcInfo.dffEnable) {
if (port != id("LO") && port != id("CIN") && port != id("COUT")) if (port != id_lo && port != id_cin && port != id_cout)
return id("CLK"); return id_clk;
} else if (cell->type == id_icestorm_ram) {
if (port.str(this)[0] == 'R')
return id_rclk;
else
return id_wclk;
} }
return IdString(); return IdString();
} }
@ -757,6 +831,8 @@ bool Arch::isClockPort(const CellInfo *cell, IdString port) const
{ {
if (cell->type == id("ICESTORM_LC") && port == id("CLK")) if (cell->type == id("ICESTORM_LC") && port == id("CLK"))
return true; return true;
if (cell->type == id("ICESTORM_RAM") && (port == id("RCLK") || (port == id("WCLK"))))
return true;
return false; return false;
} }

View File

@ -83,10 +83,6 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD {
int32_t num_uphill, num_downhill; int32_t num_uphill, num_downhill;
RelPtr<int32_t> pips_uphill, pips_downhill; RelPtr<int32_t> pips_uphill, pips_downhill;
int32_t num_bels_downhill;
BelPortPOD bel_uphill;
RelPtr<BelPortPOD> bels_downhill;
int32_t num_bel_pins; int32_t num_bel_pins;
RelPtr<BelPortPOD> bel_pins; RelPtr<BelPortPOD> bel_pins;
@ -453,8 +449,6 @@ struct Arch : BaseCtx
bool getBelGlobalBuf(BelId bel) const { return chip_info->bel_data[bel.index].type == TYPE_SB_GB; } bool getBelGlobalBuf(BelId bel) const { return chip_info->bel_data[bel.index].type == TYPE_SB_GB; }
BelRange getBelsAtSameTile(BelId bel) const NPNR_DEPRECATED;
BelType getBelType(BelId bel) const BelType getBelType(BelId bel) const
{ {
NPNR_ASSERT(bel != BelId()); NPNR_ASSERT(bel != BelId());
@ -475,6 +469,8 @@ struct Arch : BaseCtx
return id(chip_info->wire_data[wire.index].name.get()); 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; } uint32_t getWireChecksum(WireId wire) const { return wire.index; }
void bindWire(WireId wire, IdString net, PlaceStrength strength) void bindWire(WireId wire, IdString net, PlaceStrength strength)
@ -617,6 +613,8 @@ struct Arch : BaseCtx
IdString getPipName(PipId pip) const; IdString getPipName(PipId pip) const;
IdString getPipType(PipId pip) const { return IdString(); }
uint32_t getPipChecksum(PipId pip) const { return pip.index; } uint32_t getPipChecksum(PipId pip) const { return pip.index; }
WireId getPipSrcWire(PipId pip) const WireId getPipSrcWire(PipId pip) const
@ -685,13 +683,12 @@ struct Arch : BaseCtx
// ------------------------------------------------- // -------------------------------------------------
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const NPNR_DEPRECATED;
delay_t estimateDelay(WireId src, WireId dst) const; delay_t estimateDelay(WireId src, WireId dst) const;
delay_t getDelayEpsilon() const { return 20; } delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; } delay_t getRipupDelayPenalty() const { return 200; }
float getDelayNS(delay_t v) const { return v * 0.001; } float getDelayNS(delay_t v) const { return v * 0.001; }
uint32_t getDelayChecksum(delay_t v) const { return v; } uint32_t getDelayChecksum(delay_t v) const { return v; }
delay_t getBudgetOverride(const PortRef& pr, delay_t v) const; delay_t getBudgetOverride(NetInfo *net_info, int user_idx, delay_t budget) const;
// ------------------------------------------------- // -------------------------------------------------
@ -747,6 +744,21 @@ struct Arch : BaseCtx
IdString id_cen, id_clk, id_sr; IdString id_cen, id_clk, id_sr;
IdString id_i0, id_i1, id_i2, id_i3; IdString id_i0, id_i1, id_i2, id_i3;
IdString id_dff_en, id_neg_clk; IdString id_dff_en, id_neg_clk;
IdString id_cin, id_cout;
IdString id_o, id_lo;
IdString id_icestorm_ram, id_rclk, id_wclk;
// -------------------------------------------------
BelPin getIOBSharingPLLPin(BelId pll, PortPin pll_pin) const
{
auto wire = getBelPinWire(pll, pll_pin);
for (auto src_bel : getWireBelPins(wire)) {
if (getBelType(src_bel.bel) == TYPE_SB_IO && src_bel.pin == PIN_D_IN_0) {
return src_bel;
}
}
NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}");
}
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -3,6 +3,7 @@
* *
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com> * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com> * Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -71,7 +72,8 @@ bool Arch::isBelLocationValid(BelId bel) const
{ {
if (getBelType(bel) == TYPE_ICESTORM_LC) { if (getBelType(bel) == TYPE_ICESTORM_LC) {
std::vector<const CellInfo *> bel_cells; std::vector<const CellInfo *> bel_cells;
for (auto bel_other : getBelsAtSameTile(bel)) { Loc bel_loc = getBelLocation(bel);
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
IdString cell_other = getBoundBelCell(bel_other); IdString cell_other = getBoundBelCell(bel_other);
if (cell_other != IdString()) { if (cell_other != IdString()) {
const CellInfo *ci_other = cells.at(cell_other).get(); const CellInfo *ci_other = cells.at(cell_other).get();
@ -94,8 +96,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
NPNR_ASSERT(getBelType(bel) == TYPE_ICESTORM_LC); NPNR_ASSERT(getBelType(bel) == TYPE_ICESTORM_LC);
std::vector<const CellInfo *> bel_cells; std::vector<const CellInfo *> bel_cells;
Loc bel_loc = getBelLocation(bel);
for (auto bel_other : getBelsAtSameTile(bel)) { for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
IdString cell_other = getBoundBelCell(bel_other); IdString cell_other = getBoundBelCell(bel_other);
if (cell_other != IdString() && bel_other != bel) { if (cell_other != IdString() && bel_other != bel) {
const CellInfo *ci_other = cells.at(cell_other).get(); const CellInfo *ci_other = cells.at(cell_other).get();
@ -106,6 +108,36 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
bel_cells.push_back(cell); bel_cells.push_back(cell);
return logicCellsCompatible(bel_cells); return logicCellsCompatible(bel_cells);
} else if (cell->type == id_sb_io) { } else if (cell->type == id_sb_io) {
// Do not allow placement of input SB_IOs on blocks where there a PLL is outputting to.
// Find shared PLL by looking for driving bel siblings from D_IN_0
// that are a PLL clock output.
auto wire = getBelPinWire(bel, PIN_D_IN_0);
PortPin pll_bel_pin;
BelId pll_bel;
for (auto pin : getWireBelPins(wire)) {
if (pin.pin == PIN_PLLOUT_A || pin.pin == PIN_PLLOUT_B) {
pll_bel = pin.bel;
pll_bel_pin = pin.pin;
break;
}
}
// Is there a PLL that shares this IO buffer?
if (pll_bel.index != -1) {
auto pll_cell = getBoundBelCell(pll_bel);
// Is a PLL placed in this PLL bel?
if (pll_cell != IdString()) {
// Is the shared port driving a net?
auto pi = cells.at(pll_cell)->ports[portPinToId(pll_bel_pin)];
if (pi.net != nullptr) {
// Are we perhaps a PAD INPUT Bel that can be placed here?
if (cells.at(pll_cell)->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this)) {
return true;
}
return false;
}
}
}
return getBelPackagePin(bel) != ""; return getBelPackagePin(bel) != "";
} else if (cell->type == id_sb_gb) { } else if (cell->type == id_sb_gb) {
NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr); NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr);

View File

@ -79,8 +79,6 @@ void arch_wrap_python()
conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell"); conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls, fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
"getBels"); "getBels");
fn_wrapper_1a<Context, decltype(&Context::getBelsAtSameTile), &Context::getBelsAtSameTile, wrap_context<BelRange>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelsAtSameTile");
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>, fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
conv_from_str<BelId>, conv_from_str<PortPin>>::def_wrap(ctx_cls, "getBelPinWire"); conv_from_str<BelId>, conv_from_str<PortPin>>::def_wrap(ctx_cls, "getBelPinWire");

6
ice40/benchmark/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
hx8kdemo.log
hx8kdemo.blif
hx8kdemo.json
hx8kdemo_[an][0-9].asc
hx8kdemo_[an][0-9].log
report_[an][0-9].txt

27
ice40/benchmark/Makefile Normal file
View File

@ -0,0 +1,27 @@
reports::
define mkreport
reports:: report_a$1.txt report_n$1.txt
report_a$1.txt: hx8kdemo_a$1.asc
icetime -m -r report_a$1.txt -d hx8k hx8kdemo_a$1.asc
report_n$1.txt: hx8kdemo_n$1.asc
icetime -m -r report_n$1.txt -d hx8k hx8kdemo_n$1.asc
hx8kdemo_a$1.asc: hx8kdemo.blif
arachne-pnr -d 8k -p hx8kdemo.pcf -o hx8kdemo_a$1.asc -s 1$1 hx8kdemo.blif > hx8kdemo_a$1.log 2>&1
hx8kdemo_n$1.asc: hx8kdemo.json
../../nextpnr-ice40 --asc hx8kdemo_n$1.asc --json hx8kdemo.json --pcf hx8kdemo.pcf --hx8k --seed 1$1 > hx8kdemo_n$1.log 2>&1
endef
$(foreach i,0 1 2 3 4 5 6 7 8 9,$(eval $(call mkreport,$(i))))
hx8kdemo.blif: hx8kdemo.json
hx8kdemo.json: hx8kdemo.v spimemio.v simpleuart.v picosoc.v picorv32.v
yosys -ql hx8kdemo.log -p 'synth_ice40 -top hx8kdemo -blif hx8kdemo.blif -json hx8kdemo.json' $^
clean:
rm -f hx8kdemo.log hx8kdemo.blif hx8kdemo.json
rm -f hx8kdemo_[an][0-9].asc hx8kdemo_[an][0-9].log report_[an][0-9].txt

View File

@ -0,0 +1,40 @@
# Pinout for the iCE40-HX8K Breakout Board
set_io clk J3
set_io flash_csb R12
set_io flash_clk R11
set_io flash_io0 P12
set_io flash_io1 P11
# for QSPI mode the flash chip on the iCE40-HX8K Breakout Board
# must be replaced with one that supports QSPI and the IO2 and IO3
# pins must be soldered to T9 and P8 (center on J3)
set_io flash_io2 T9
set_io flash_io3 P8
set_io ser_tx B12
set_io ser_rx B10
# left on J3
set_io debug_ser_tx T1
set_io debug_ser_rx R3
# right on J3
set_io debug_flash_csb T15
set_io debug_flash_clk R16
set_io debug_flash_io0 N12
set_io debug_flash_io1 P13
set_io debug_flash_io2 T13
set_io debug_flash_io3 T14
set_io leds[7] B5 # D9
set_io leds[6] B4 # D8
set_io leds[5] A2 # D7
set_io leds[4] A1 # D6
set_io leds[3] C5 # D5
set_io leds[2] C4 # D4
set_io leds[1] B3 # D3
set_io leds[0] C3 # D2

139
ice40/benchmark/hx8kdemo.v Normal file
View File

@ -0,0 +1,139 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* 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.
*
*/
module hx8kdemo (
input clk,
output ser_tx,
input ser_rx,
output [7:0] leds,
output flash_csb,
output flash_clk,
inout flash_io0,
inout flash_io1,
inout flash_io2,
inout flash_io3,
output debug_ser_tx,
output debug_ser_rx,
output debug_flash_csb,
output debug_flash_clk,
output debug_flash_io0,
output debug_flash_io1,
output debug_flash_io2,
output debug_flash_io3
);
reg [5:0] reset_cnt = 0;
wire resetn = &reset_cnt;
always @(posedge clk) begin
reset_cnt <= reset_cnt + !resetn;
end
wire flash_io0_oe, flash_io0_do, flash_io0_di;
wire flash_io1_oe, flash_io1_do, flash_io1_di;
wire flash_io2_oe, flash_io2_do, flash_io2_di;
wire flash_io3_oe, flash_io3_do, flash_io3_di;
SB_IO #(
.PIN_TYPE(6'b 1010_01),
.PULLUP(1'b 0)
) flash_io_buf [3:0] (
.PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}),
.OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}),
.D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}),
.D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di})
);
wire iomem_valid;
reg iomem_ready;
wire [3:0] iomem_wstrb;
wire [31:0] iomem_addr;
wire [31:0] iomem_wdata;
reg [31:0] iomem_rdata;
reg [31:0] gpio;
assign leds = gpio;
always @(posedge clk) begin
if (!resetn) begin
gpio <= 0;
end else begin
iomem_ready <= 0;
if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 03) begin
iomem_ready <= 1;
iomem_rdata <= gpio;
if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0];
if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8];
if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16];
if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24];
end
end
end
picosoc soc (
.clk (clk ),
.resetn (resetn ),
.ser_tx (ser_tx ),
.ser_rx (ser_rx ),
.flash_csb (flash_csb ),
.flash_clk (flash_clk ),
.flash_io0_oe (flash_io0_oe),
.flash_io1_oe (flash_io1_oe),
.flash_io2_oe (flash_io2_oe),
.flash_io3_oe (flash_io3_oe),
.flash_io0_do (flash_io0_do),
.flash_io1_do (flash_io1_do),
.flash_io2_do (flash_io2_do),
.flash_io3_do (flash_io3_do),
.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di),
.irq_5 (1'b0 ),
.irq_6 (1'b0 ),
.irq_7 (1'b0 ),
.iomem_valid (iomem_valid ),
.iomem_ready (iomem_ready ),
.iomem_wstrb (iomem_wstrb ),
.iomem_addr (iomem_addr ),
.iomem_wdata (iomem_wdata ),
.iomem_rdata (iomem_rdata )
);
assign debug_ser_tx = ser_tx;
assign debug_ser_rx = ser_rx;
assign debug_flash_csb = flash_csb;
assign debug_flash_clk = flash_clk;
assign debug_flash_io0 = flash_io0_di;
assign debug_flash_io1 = flash_io1_di;
assign debug_flash_io2 = flash_io2_di;
assign debug_flash_io3 = flash_io3_di;
endmodule

2977
ice40/benchmark/picorv32.v Normal file

File diff suppressed because it is too large Load Diff

241
ice40/benchmark/picosoc.v Normal file
View File

@ -0,0 +1,241 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* 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 PICORV32_REGS
`ifdef PICORV32_V
`error "picosoc.v must be read before picorv32.v!"
`endif
`define PICORV32_REGS picosoc_regs
`endif
module picosoc (
input clk,
input resetn,
output iomem_valid,
input iomem_ready,
output [ 3:0] iomem_wstrb,
output [31:0] iomem_addr,
output [31:0] iomem_wdata,
input [31:0] iomem_rdata,
input irq_5,
input irq_6,
input irq_7,
output ser_tx,
input ser_rx,
output flash_csb,
output flash_clk,
output flash_io0_oe,
output flash_io1_oe,
output flash_io2_oe,
output flash_io3_oe,
output flash_io0_do,
output flash_io1_do,
output flash_io2_do,
output flash_io3_do,
input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di
);
parameter integer MEM_WORDS = 256;
parameter [31:0] STACKADDR = (4*MEM_WORDS); // end of memory
parameter [31:0] PROGADDR_RESET = 32'h 0010_0000; // 1 MB into flash
reg [31:0] irq;
wire irq_stall = 0;
wire irq_uart = 0;
always @* begin
irq = 0;
irq[3] = irq_stall;
irq[4] = irq_uart;
irq[5] = irq_5;
irq[6] = irq_6;
irq[7] = irq_7;
end
wire mem_valid;
wire mem_instr;
wire mem_ready;
wire [31:0] mem_addr;
wire [31:0] mem_wdata;
wire [3:0] mem_wstrb;
wire [31:0] mem_rdata;
wire spimem_ready;
wire [31:0] spimem_rdata;
reg ram_ready;
wire [31:0] ram_rdata;
assign iomem_valid = mem_valid && (mem_addr[31:24] > 8'h 01);
assign iomem_wstrb = mem_wstrb;
assign iomem_addr = mem_addr;
assign iomem_wdata = mem_wdata;
wire spimemio_cfgreg_sel = mem_valid && (mem_addr == 32'h 0200_0000);
wire [31:0] spimemio_cfgreg_do;
wire simpleuart_reg_div_sel = mem_valid && (mem_addr == 32'h 0200_0004);
wire [31:0] simpleuart_reg_div_do;
wire simpleuart_reg_dat_sel = mem_valid && (mem_addr == 32'h 0200_0008);
wire [31:0] simpleuart_reg_dat_do;
wire simpleuart_reg_dat_wait;
assign mem_ready = (iomem_valid && iomem_ready) || spimem_ready || ram_ready || spimemio_cfgreg_sel ||
simpleuart_reg_div_sel || (simpleuart_reg_dat_sel && !simpleuart_reg_dat_wait);
assign mem_rdata = (iomem_valid && iomem_ready) ? iomem_rdata : spimem_ready ? spimem_rdata : ram_ready ? ram_rdata :
spimemio_cfgreg_sel ? spimemio_cfgreg_do : simpleuart_reg_div_sel ? simpleuart_reg_div_do :
simpleuart_reg_dat_sel ? simpleuart_reg_dat_do : 32'h 0000_0000;
picorv32 #(
.STACKADDR(STACKADDR),
.PROGADDR_RESET(PROGADDR_RESET),
.PROGADDR_IRQ(32'h 0000_0000),
.BARREL_SHIFTER(1),
.COMPRESSED_ISA(1),
.ENABLE_MUL(1),
.ENABLE_DIV(1),
.ENABLE_IRQ(1),
.ENABLE_IRQ_QREGS(0)
) cpu (
.clk (clk ),
.resetn (resetn ),
.mem_valid (mem_valid ),
.mem_instr (mem_instr ),
.mem_ready (mem_ready ),
.mem_addr (mem_addr ),
.mem_wdata (mem_wdata ),
.mem_wstrb (mem_wstrb ),
.mem_rdata (mem_rdata ),
.irq (irq )
);
spimemio spimemio (
.clk (clk),
.resetn (resetn),
.valid (mem_valid && mem_addr >= 4*MEM_WORDS && mem_addr < 32'h 0200_0000),
.ready (spimem_ready),
.addr (mem_addr[23:0]),
.rdata (spimem_rdata),
.flash_csb (flash_csb ),
.flash_clk (flash_clk ),
.flash_io0_oe (flash_io0_oe),
.flash_io1_oe (flash_io1_oe),
.flash_io2_oe (flash_io2_oe),
.flash_io3_oe (flash_io3_oe),
.flash_io0_do (flash_io0_do),
.flash_io1_do (flash_io1_do),
.flash_io2_do (flash_io2_do),
.flash_io3_do (flash_io3_do),
.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di),
.cfgreg_we(spimemio_cfgreg_sel ? mem_wstrb : 4'b 0000),
.cfgreg_di(mem_wdata),
.cfgreg_do(spimemio_cfgreg_do)
);
simpleuart simpleuart (
.clk (clk ),
.resetn (resetn ),
.ser_tx (ser_tx ),
.ser_rx (ser_rx ),
.reg_div_we (simpleuart_reg_div_sel ? mem_wstrb : 4'b 0000),
.reg_div_di (mem_wdata),
.reg_div_do (simpleuart_reg_div_do),
.reg_dat_we (simpleuart_reg_dat_sel ? mem_wstrb[0] : 1'b 0),
.reg_dat_re (simpleuart_reg_dat_sel && !mem_wstrb),
.reg_dat_di (mem_wdata),
.reg_dat_do (simpleuart_reg_dat_do),
.reg_dat_wait(simpleuart_reg_dat_wait)
);
always @(posedge clk)
ram_ready <= mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS;
picosoc_mem #(.WORDS(MEM_WORDS)) memory (
.clk(clk),
.wen((mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS) ? mem_wstrb : 4'b0),
.addr(mem_addr[23:2]),
.wdata(mem_wdata),
.rdata(ram_rdata)
);
endmodule
// Implementation note:
// Replace the following two modules with wrappers for your SRAM cells.
module picosoc_regs (
input clk, wen,
input [5:0] waddr,
input [5:0] raddr1,
input [5:0] raddr2,
input [31:0] wdata,
output [31:0] rdata1,
output [31:0] rdata2
);
reg [31:0] regs [0:31];
always @(posedge clk)
if (wen) regs[waddr[4:0]] <= wdata;
assign rdata1 = regs[raddr1[4:0]];
assign rdata2 = regs[raddr2[4:0]];
endmodule
module picosoc_mem #(
parameter integer WORDS = 256
) (
input clk,
input [3:0] wen,
input [21:0] addr,
input [31:0] wdata,
output reg [31:0] rdata
);
reg [31:0] mem [0:WORDS-1];
always @(posedge clk) begin
rdata <= mem[addr];
if (wen[0]) mem[addr][ 7: 0] <= wdata[ 7: 0];
if (wen[1]) mem[addr][15: 8] <= wdata[15: 8];
if (wen[2]) mem[addr][23:16] <= wdata[23:16];
if (wen[3]) mem[addr][31:24] <= wdata[31:24];
end
endmodule

View File

@ -0,0 +1,69 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import subprocess\n",
"\n",
"gitrev = subprocess.getoutput(\"git rev-parse --short HEAD\")\n",
"\n",
"data_a = 1 + np.zeros(10)\n",
"data_n = 1 + np.zeros(10)\n",
"\n",
"for i in range(10):\n",
" try:\n",
" with open(\"report_a%d.txt\" % i, \"r\") as f:\n",
" for line in f:\n",
" if line.startswith(\"Total path delay:\"):\n",
" data_a[i] = float(line.split()[3])\n",
" except:\n",
" pass\n",
" try:\n",
" with open(\"report_n%d.txt\" % i, \"r\") as f:\n",
" for line in f:\n",
" if line.startswith(\"Total path delay:\"):\n",
" data_n[i] = float(line.split()[3])\n",
" except:\n",
" pass\n",
"\n",
"plt.figure(figsize=(9,3))\n",
"plt.title(\"nextpnr -- ice40/benchmark/ -- %s\" % gitrev)\n",
"plt.bar(np.arange(10), data_a, color='blue')\n",
"plt.bar(15+np.arange(10), data_n, color='red')\n",
"plt.ylabel('Longest path (ns)')\n",
"plt.xticks([5, 20], [\"arachne-pnr\", \"nextpnr\"])\n",
"plt.xlim(-2, 27)\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

View File

@ -0,0 +1,137 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* 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.
*
*/
module simpleuart (
input clk,
input resetn,
output ser_tx,
input ser_rx,
input [3:0] reg_div_we,
input [31:0] reg_div_di,
output [31:0] reg_div_do,
input reg_dat_we,
input reg_dat_re,
input [31:0] reg_dat_di,
output [31:0] reg_dat_do,
output reg_dat_wait
);
reg [31:0] cfg_divider;
reg [3:0] recv_state;
reg [31:0] recv_divcnt;
reg [7:0] recv_pattern;
reg [7:0] recv_buf_data;
reg recv_buf_valid;
reg [9:0] send_pattern;
reg [3:0] send_bitcnt;
reg [31:0] send_divcnt;
reg send_dummy;
assign reg_div_do = cfg_divider;
assign reg_dat_wait = reg_dat_we && (send_bitcnt || send_dummy);
assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0;
always @(posedge clk) begin
if (!resetn) begin
cfg_divider <= 1;
end else begin
if (reg_div_we[0]) cfg_divider[ 7: 0] <= reg_div_di[ 7: 0];
if (reg_div_we[1]) cfg_divider[15: 8] <= reg_div_di[15: 8];
if (reg_div_we[2]) cfg_divider[23:16] <= reg_div_di[23:16];
if (reg_div_we[3]) cfg_divider[31:24] <= reg_div_di[31:24];
end
end
always @(posedge clk) begin
if (!resetn) begin
recv_state <= 0;
recv_divcnt <= 0;
recv_pattern <= 0;
recv_buf_data <= 0;
recv_buf_valid <= 0;
end else begin
recv_divcnt <= recv_divcnt + 1;
if (reg_dat_re)
recv_buf_valid <= 0;
case (recv_state)
0: begin
if (!ser_rx)
recv_state <= 1;
recv_divcnt <= 0;
end
1: begin
if (2*recv_divcnt > cfg_divider) begin
recv_state <= 2;
recv_divcnt <= 0;
end
end
10: begin
if (recv_divcnt > cfg_divider) begin
recv_buf_data <= recv_pattern;
recv_buf_valid <= 1;
recv_state <= 0;
end
end
default: begin
if (recv_divcnt > cfg_divider) begin
recv_pattern <= {ser_rx, recv_pattern[7:1]};
recv_state <= recv_state + 1;
recv_divcnt <= 0;
end
end
endcase
end
end
assign ser_tx = send_pattern[0];
always @(posedge clk) begin
if (reg_div_we)
send_dummy <= 1;
send_divcnt <= send_divcnt + 1;
if (!resetn) begin
send_pattern <= ~0;
send_bitcnt <= 0;
send_divcnt <= 0;
send_dummy <= 1;
end else begin
if (send_dummy && !send_bitcnt) begin
send_pattern <= ~0;
send_bitcnt <= 15;
send_divcnt <= 0;
send_dummy <= 0;
end else
if (reg_dat_we && !send_bitcnt) begin
send_pattern <= {1'b1, reg_dat_di[7:0], 1'b0};
send_bitcnt <= 10;
send_divcnt <= 0;
end else
if (send_divcnt > cfg_divider && send_bitcnt) begin
send_pattern <= {1'b1, send_pattern[9:1]};
send_bitcnt <= send_bitcnt - 1;
send_divcnt <= 0;
end
end
end
endmodule

579
ice40/benchmark/spimemio.v Normal file
View File

@ -0,0 +1,579 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* 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.
*
*/
module spimemio (
input clk, resetn,
input valid,
output ready,
input [23:0] addr,
output reg [31:0] rdata,
output flash_csb,
output flash_clk,
output flash_io0_oe,
output flash_io1_oe,
output flash_io2_oe,
output flash_io3_oe,
output flash_io0_do,
output flash_io1_do,
output flash_io2_do,
output flash_io3_do,
input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di,
input [3:0] cfgreg_we,
input [31:0] cfgreg_di,
output [31:0] cfgreg_do
);
reg xfer_resetn;
reg din_valid;
wire din_ready;
reg [7:0] din_data;
reg [3:0] din_tag;
reg din_cont;
reg din_qspi;
reg din_ddr;
reg din_rd;
wire dout_valid;
wire [7:0] dout_data;
wire [3:0] dout_tag;
reg [23:0] buffer;
reg [23:0] rd_addr;
reg rd_valid;
reg rd_wait;
reg rd_inc;
assign ready = valid && (addr == rd_addr) && rd_valid;
wire jump = valid && !ready && (addr != rd_addr+4) && rd_valid;
reg softreset;
reg config_en; // cfgreg[31]
reg config_ddr; // cfgreg[22]
reg config_qspi; // cfgreg[21]
reg config_cont; // cfgreg[20]
reg [3:0] config_dummy; // cfgreg[19:16]
reg [3:0] config_oe; // cfgreg[11:8]
reg config_csb; // cfgreg[5]
reg config_clk; // cfgref[4]
reg [3:0] config_do; // cfgreg[3:0]
assign cfgreg_do[31] = config_en;
assign cfgreg_do[30:23] = 0;
assign cfgreg_do[22] = config_ddr;
assign cfgreg_do[21] = config_qspi;
assign cfgreg_do[20] = config_cont;
assign cfgreg_do[19:16] = config_dummy;
assign cfgreg_do[15:12] = 0;
assign cfgreg_do[11:8] = {flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe};
assign cfgreg_do[7:6] = 0;
assign cfgreg_do[5] = flash_csb;
assign cfgreg_do[4] = flash_clk;
assign cfgreg_do[3:0] = {flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di};
always @(posedge clk) begin
softreset <= !config_en || cfgreg_we;
if (!resetn) begin
softreset <= 1;
config_en <= 1;
config_csb <= 0;
config_clk <= 0;
config_oe <= 0;
config_do <= 0;
config_ddr <= 0;
config_qspi <= 0;
config_cont <= 0;
config_dummy <= 8;
end else begin
if (cfgreg_we[0]) begin
config_csb <= cfgreg_di[5];
config_clk <= cfgreg_di[4];
config_do <= cfgreg_di[3:0];
end
if (cfgreg_we[1]) begin
config_oe <= cfgreg_di[11:8];
end
if (cfgreg_we[2]) begin
config_ddr <= cfgreg_di[22];
config_qspi <= cfgreg_di[21];
config_cont <= cfgreg_di[20];
config_dummy <= cfgreg_di[19:16];
end
if (cfgreg_we[3]) begin
config_en <= cfgreg_di[31];
end
end
end
wire xfer_csb;
wire xfer_clk;
wire xfer_io0_oe;
wire xfer_io1_oe;
wire xfer_io2_oe;
wire xfer_io3_oe;
wire xfer_io0_do;
wire xfer_io1_do;
wire xfer_io2_do;
wire xfer_io3_do;
reg xfer_io0_90;
reg xfer_io1_90;
reg xfer_io2_90;
reg xfer_io3_90;
always @(negedge clk) begin
xfer_io0_90 <= xfer_io0_do;
xfer_io1_90 <= xfer_io1_do;
xfer_io2_90 <= xfer_io2_do;
xfer_io3_90 <= xfer_io3_do;
end
assign flash_csb = config_en ? xfer_csb : config_csb;
assign flash_clk = config_en ? xfer_clk : config_clk;
assign flash_io0_oe = config_en ? xfer_io0_oe : config_oe[0];
assign flash_io1_oe = config_en ? xfer_io1_oe : config_oe[1];
assign flash_io2_oe = config_en ? xfer_io2_oe : config_oe[2];
assign flash_io3_oe = config_en ? xfer_io3_oe : config_oe[3];
assign flash_io0_do = config_en ? (config_ddr ? xfer_io0_90 : xfer_io0_do) : config_do[0];
assign flash_io1_do = config_en ? (config_ddr ? xfer_io1_90 : xfer_io1_do) : config_do[1];
assign flash_io2_do = config_en ? (config_ddr ? xfer_io2_90 : xfer_io2_do) : config_do[2];
assign flash_io3_do = config_en ? (config_ddr ? xfer_io3_90 : xfer_io3_do) : config_do[3];
wire xfer_dspi = din_ddr && !din_qspi;
wire xfer_ddr = din_ddr && din_qspi;
spimemio_xfer xfer (
.clk (clk ),
.resetn (xfer_resetn ),
.din_valid (din_valid ),
.din_ready (din_ready ),
.din_data (din_data ),
.din_tag (din_tag ),
.din_cont (din_cont ),
.din_dspi (xfer_dspi ),
.din_qspi (din_qspi ),
.din_ddr (xfer_ddr ),
.din_rd (din_rd ),
.dout_valid (dout_valid ),
.dout_data (dout_data ),
.dout_tag (dout_tag ),
.flash_csb (xfer_csb ),
.flash_clk (xfer_clk ),
.flash_io0_oe (xfer_io0_oe ),
.flash_io1_oe (xfer_io1_oe ),
.flash_io2_oe (xfer_io2_oe ),
.flash_io3_oe (xfer_io3_oe ),
.flash_io0_do (xfer_io0_do ),
.flash_io1_do (xfer_io1_do ),
.flash_io2_do (xfer_io2_do ),
.flash_io3_do (xfer_io3_do ),
.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di)
);
reg [3:0] state;
always @(posedge clk) begin
xfer_resetn <= 1;
din_valid <= 0;
if (!resetn || softreset) begin
state <= 0;
xfer_resetn <= 0;
rd_valid <= 0;
din_tag <= 0;
din_cont <= 0;
din_qspi <= 0;
din_ddr <= 0;
din_rd <= 0;
end else begin
if (dout_valid && dout_tag == 1) buffer[ 7: 0] <= dout_data;
if (dout_valid && dout_tag == 2) buffer[15: 8] <= dout_data;
if (dout_valid && dout_tag == 3) buffer[23:16] <= dout_data;
if (dout_valid && dout_tag == 4) begin
rdata <= {dout_data, buffer};
rd_addr <= rd_inc ? rd_addr + 4 : addr;
rd_valid <= 1;
rd_wait <= rd_inc;
rd_inc <= 1;
end
if (valid)
rd_wait <= 0;
case (state)
0: begin
din_valid <= 1;
din_data <= 8'h ff;
din_tag <= 0;
if (din_ready) begin
din_valid <= 0;
state <= 1;
end
end
1: begin
if (dout_valid) begin
xfer_resetn <= 0;
state <= 2;
end
end
2: begin
din_valid <= 1;
din_data <= 8'h ab;
din_tag <= 0;
if (din_ready) begin
din_valid <= 0;
state <= 3;
end
end
3: begin
if (dout_valid) begin
xfer_resetn <= 0;
state <= 4;
end
end
4: begin
rd_inc <= 0;
din_valid <= 1;
din_tag <= 0;
case ({config_ddr, config_qspi})
2'b11: din_data <= 8'h ED;
2'b01: din_data <= 8'h EB;
2'b10: din_data <= 8'h BB;
2'b00: din_data <= 8'h 03;
endcase
if (din_ready) begin
din_valid <= 0;
state <= 5;
end
end
5: begin
if (valid && !ready) begin
din_valid <= 1;
din_tag <= 0;
din_data <= addr[23:16];
din_qspi <= config_qspi;
din_ddr <= config_ddr;
if (din_ready) begin
din_valid <= 0;
state <= 6;
end
end
end
6: begin
din_valid <= 1;
din_tag <= 0;
din_data <= addr[15:8];
if (din_ready) begin
din_valid <= 0;
state <= 7;
end
end
7: begin
din_valid <= 1;
din_tag <= 0;
din_data <= addr[7:0];
if (din_ready) begin
din_valid <= 0;
din_data <= 0;
state <= config_qspi || config_ddr ? 8 : 9;
end
end
8: begin
din_valid <= 1;
din_tag <= 0;
din_data <= config_cont ? 8'h A5 : 8'h FF;
if (din_ready) begin
din_rd <= 1;
din_data <= config_dummy;
din_valid <= 0;
state <= 9;
end
end
9: begin
din_valid <= 1;
din_tag <= 1;
if (din_ready) begin
din_valid <= 0;
state <= 10;
end
end
10: begin
din_valid <= 1;
din_data <= 8'h 00;
din_tag <= 2;
if (din_ready) begin
din_valid <= 0;
state <= 11;
end
end
11: begin
din_valid <= 1;
din_tag <= 3;
if (din_ready) begin
din_valid <= 0;
state <= 12;
end
end
12: begin
if (!rd_wait || valid) begin
din_valid <= 1;
din_tag <= 4;
if (din_ready) begin
din_valid <= 0;
state <= 9;
end
end
end
endcase
if (jump) begin
rd_inc <= 0;
rd_valid <= 0;
xfer_resetn <= 0;
if (config_cont) begin
state <= 5;
end else begin
state <= 4;
din_qspi <= 0;
din_ddr <= 0;
end
din_rd <= 0;
end
end
end
endmodule
module spimemio_xfer (
input clk, resetn,
input din_valid,
output din_ready,
input [7:0] din_data,
input [3:0] din_tag,
input din_cont,
input din_dspi,
input din_qspi,
input din_ddr,
input din_rd,
output dout_valid,
output [7:0] dout_data,
output [3:0] dout_tag,
output reg flash_csb,
output reg flash_clk,
output reg flash_io0_oe,
output reg flash_io1_oe,
output reg flash_io2_oe,
output reg flash_io3_oe,
output reg flash_io0_do,
output reg flash_io1_do,
output reg flash_io2_do,
output reg flash_io3_do,
input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di
);
reg [7:0] obuffer;
reg [7:0] ibuffer;
reg [3:0] count;
reg [3:0] dummy_count;
reg xfer_cont;
reg xfer_dspi;
reg xfer_qspi;
reg xfer_ddr;
reg xfer_ddr_q;
reg xfer_rd;
reg [3:0] xfer_tag;
reg [3:0] xfer_tag_q;
reg [7:0] next_obuffer;
reg [7:0] next_ibuffer;
reg [3:0] next_count;
reg fetch;
reg next_fetch;
reg last_fetch;
always @(posedge clk) begin
xfer_ddr_q <= xfer_ddr;
xfer_tag_q <= xfer_tag;
end
assign din_ready = din_valid && resetn && next_fetch;
assign dout_valid = (xfer_ddr_q ? fetch && !last_fetch : next_fetch && !fetch) && resetn;
assign dout_data = ibuffer;
assign dout_tag = xfer_tag_q;
always @* begin
flash_io0_oe = 0;
flash_io1_oe = 0;
flash_io2_oe = 0;
flash_io3_oe = 0;
flash_io0_do = 0;
flash_io1_do = 0;
flash_io2_do = 0;
flash_io3_do = 0;
next_obuffer = obuffer;
next_ibuffer = ibuffer;
next_count = count;
next_fetch = 0;
if (dummy_count == 0) begin
casez ({xfer_ddr, xfer_qspi, xfer_dspi})
3'b 000: begin
flash_io0_oe = 1;
flash_io0_do = obuffer[7];
if (flash_clk) begin
next_obuffer = {obuffer[6:0], 1'b 0};
next_count = count - |count;
end else begin
next_ibuffer = {ibuffer[6:0], flash_io1_di};
end
next_fetch = (next_count == 0);
end
3'b 01?: begin
flash_io0_oe = !xfer_rd;
flash_io1_oe = !xfer_rd;
flash_io2_oe = !xfer_rd;
flash_io3_oe = !xfer_rd;
flash_io0_do = obuffer[4];
flash_io1_do = obuffer[5];
flash_io2_do = obuffer[6];
flash_io3_do = obuffer[7];
if (flash_clk) begin
next_obuffer = {obuffer[3:0], 4'b 0000};
next_count = count - {|count, 2'b00};
end else begin
next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di};
end
next_fetch = (next_count == 0);
end
3'b 11?: begin
flash_io0_oe = !xfer_rd;
flash_io1_oe = !xfer_rd;
flash_io2_oe = !xfer_rd;
flash_io3_oe = !xfer_rd;
flash_io0_do = obuffer[4];
flash_io1_do = obuffer[5];
flash_io2_do = obuffer[6];
flash_io3_do = obuffer[7];
next_obuffer = {obuffer[3:0], 4'b 0000};
next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di};
next_count = count - {|count, 2'b00};
next_fetch = (next_count == 0);
end
3'b ??1: begin
flash_io0_oe = !xfer_rd;
flash_io1_oe = !xfer_rd;
flash_io0_do = obuffer[6];
flash_io1_do = obuffer[7];
if (flash_clk) begin
next_obuffer = {obuffer[5:0], 2'b 00};
next_count = count - {|count, 1'b0};
end else begin
next_ibuffer = {ibuffer[5:0], flash_io1_di, flash_io0_di};
end
next_fetch = (next_count == 0);
end
endcase
end
end
always @(posedge clk) begin
if (!resetn) begin
fetch <= 1;
last_fetch <= 1;
flash_csb <= 1;
flash_clk <= 0;
count <= 0;
dummy_count <= 0;
xfer_tag <= 0;
xfer_cont <= 0;
xfer_dspi <= 0;
xfer_qspi <= 0;
xfer_ddr <= 0;
xfer_rd <= 0;
end else begin
fetch <= next_fetch;
last_fetch <= xfer_ddr ? fetch : 1;
if (dummy_count) begin
flash_clk <= !flash_clk && !flash_csb;
dummy_count <= dummy_count - flash_clk;
end else
if (count) begin
flash_clk <= !flash_clk && !flash_csb;
obuffer <= next_obuffer;
ibuffer <= next_ibuffer;
count <= next_count;
end
if (din_valid && din_ready) begin
flash_csb <= 0;
flash_clk <= 0;
count <= 8;
dummy_count <= din_rd ? din_data : 0;
obuffer <= din_data;
xfer_tag <= din_tag;
xfer_cont <= din_cont;
xfer_dspi <= din_dspi;
xfer_qspi <= din_qspi;
xfer_ddr <= din_ddr;
xfer_rd <= din_rd;
end
end
end
endmodule

View File

@ -3,6 +3,7 @@
* *
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com> * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com> * Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -74,14 +75,26 @@ void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cf
for (int i = 0; i < cfg.num_bits; i++) { for (int i = 0; i < cfg.num_bits; i++) {
int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col); int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
if (cbit && !value) if (cbit && !value)
log_error("clearing already set config bit %s", name.c_str()); log_error("clearing already set config bit %s\n", name.c_str());
cbit = value; cbit = value;
} }
} else { } else {
int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col); int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
cbit = value; cbit = value;
if (cbit && !value) if (cbit && !value)
log_error("clearing already set config bit %s[%d]", name.c_str(), index); log_error("clearing already set config bit %s[%d]\n", name.c_str(), index);
}
}
// Set an IE_{EN,REN} logical bit in a tile config. Logical means enabled.
// On {HX,LP}1K devices these bits are active low, so we need to invert them.
void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg,
const std::string &name, bool value)
{
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
set_config(ti, tile_cfg, name, !value);
} else {
set_config(ti, tile_cfg, name, value);
} }
} }
@ -117,7 +130,7 @@ static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t; typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t;
static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name, static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name,
bool value) bool value, std::string prefix)
{ {
const ChipInfoPOD *chip = ctx->chip_info; const ChipInfoPOD *chip = ctx->chip_info;
@ -125,7 +138,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi
const auto &cbit = cell_cbits.entries[i]; const auto &cbit = cell_cbits.entries[i];
if (cbit.entry_name.get() == name) { if (cbit.entry_name.get() == name) {
const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)]; const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)];
set_config(ti, config.at(cbit.y).at(cbit.x), std::string("IpConfig.") + cbit.cbit_name.get(), value); set_config(ti, config.at(cbit.y).at(cbit.x), prefix + cbit.cbit_name.get(), value);
return; return;
} }
} }
@ -133,7 +146,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi
} }
void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell, void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
const std::vector<std::pair<std::string, int>> &params, bool string_style) const std::vector<std::pair<std::string, int>> &params, bool string_style, std::string prefix)
{ {
const ChipInfoPOD *chip = ctx->chip_info; const ChipInfoPOD *chip = ctx->chip_info;
const auto &bc = get_ec_config(chip, cell->bel); const auto &bc = get_ec_config(chip, cell->bel);
@ -163,10 +176,10 @@ void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *ce
value.resize(p.second); value.resize(p.second);
if (p.second == 1) { if (p.second == 1) {
set_ec_cbit(config, ctx, bc, p.first, value.at(0)); set_ec_cbit(config, ctx, bc, p.first, value.at(0), prefix);
} else { } else {
for (int i = 0; i < p.second; i++) { for (int i = 0; i < p.second; i++) {
set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i)); set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix);
} }
} }
} }
@ -258,8 +271,13 @@ void write_asc(const Context *ctx, std::ostream &out)
} }
} }
} }
std::unordered_set<Loc> sb_io_used_by_pll;
std::unordered_set<Loc> sb_io_used_by_io;
// Set logic cell config // Set logic cell config
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
BelId bel = cell.second.get()->bel; BelId bel = cell.second.get()->bel;
if (bel == BelId()) { if (bel == BelId()) {
std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl; std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
@ -304,6 +322,7 @@ void write_asc(const Context *ctx, std::ostream &out)
} else if (cell.second->type == ctx->id("SB_IO")) { } else if (cell.second->type == ctx->id("SB_IO")) {
const BelInfoPOD &beli = ci.bel_data[bel.index]; const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z; int x = beli.x, y = beli.y, z = beli.z;
sb_io_used_by_io.insert(Loc(x, y, z));
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE")); unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER")); bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
@ -405,7 +424,70 @@ void write_asc(const Context *ctx, std::ostream &out)
{"MODE_8x8", 1}, {"MODE_8x8", 1},
{"A_SIGNED", 1}, {"A_SIGNED", 1},
{"B_SIGNED", 1}}; {"B_SIGNED", 1}};
configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false); configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig."));
} else if (cell.second->type == ctx->id("ICESTORM_PLL")) {
const std::vector<std::pair<std::string, int>> pll_params = {{"DELAY_ADJMODE_FB", 1},
{"DELAY_ADJMODE_REL", 1},
{"DIVF", 7},
{"DIVQ", 3},
{"DIVR", 4},
{"FDA_FEEDBACK", 4},
{"FDA_RELATIVE", 4},
{"FEEDBACK_PATH", 3},
{"FILTER_RANGE", 3},
{"PLLOUT_SELECT_A", 2},
{"PLLOUT_SELECT_B", 2},
{"PLLTYPE", 3},
{"SHIFTREG_DIV_MODE", 1},
{"TEST_MODE", 1}};
configure_extra_cell(config, ctx, cell.second.get(), pll_params, false, std::string("PLL."));
// Configure the SB_IOs that the clock outputs are going through.
for (auto &port : cell.second->ports) {
// If this port is not a PLLOUT port, ignore it.
if (port.second.name != ctx->id("PLLOUT_A") && port.second.name != ctx->id("PLLOUT_B"))
continue;
// If the output is not driving any net, ignore it.
if (port.second.net == nullptr)
continue;
// Get IO Bel that this PLL port goes through by finding sibling
// Bel driving the same wire via PIN_D_IN_0.
auto wire = ctx->getBelPinWire(cell.second->bel, ctx->portPinFromId(port.second.name));
BelId io_bel;
for (auto pin : ctx->getWireBelPins(wire)) {
if (pin.pin == PIN_D_IN_0) {
io_bel = pin.bel;
break;
}
}
NPNR_ASSERT(io_bel.index != -1);
auto io_bel_loc = ctx->getBelLocation(io_bel);
// Check that this SB_IO is either unused or just used as an output.
if (sb_io_used_by_io.count(io_bel_loc)) {
log_error("SB_IO '%s' already in use, cannot route PLL through\n", ctx->getBelName(bel).c_str(ctx));
}
sb_io_used_by_pll.insert(io_bel_loc);
// Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html)
auto ieren = get_ieren(bi, io_bel_loc.x, io_bel_loc.y, io_bel_loc.z);
int iex, iey, iez;
std::tie(iex, iey, iez) = ieren;
NPNR_ASSERT(iez != -1);
// Write config.
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
// Enable input buffer and disable pull-up resistor in block
// (this is used by the PLL).
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
// PINTYPE[0] passes the PLL through to the fabric.
set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x),
"IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true);
}
} else { } else {
NPNR_ASSERT(false); NPNR_ASSERT(false);
} }
@ -416,14 +498,16 @@ void write_asc(const Context *ctx, std::ostream &out)
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
const BelInfoPOD &beli = ci.bel_data[bel.index]; const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z; int x = beli.x, y = beli.y, z = beli.z;
if (sb_io_used_by_pll.count(Loc(x, y, z))) {
continue;
}
auto ieren = get_ieren(bi, x, y, z); auto ieren = get_ieren(bi, x, y, z);
int iex, iey, iez; int iex, iey, iez;
std::tie(iex, iey, iez) = ieren; std::tie(iex, iey, iez) = ieren;
if (iez != -1) { if (iez != -1) {
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) { set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true); set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
}
} }
} else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) { } else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) {
const BelInfoPOD &beli = ci.bel_data[bel.index]; const BelInfoPOD &beli = ci.bel_data[bel.index];
@ -482,9 +566,8 @@ void write_asc(const Context *ctx, std::ostream &out)
set_config(ti, config.at(y).at(x), set_config(ti, config.at(y).at(x),
"Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true); "Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
else else
set_config(ti, config.at(y).at(x), set_config(ti, config.at(y).at(x), "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) +
"Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" + "_LC0" + std::to_string(lc_idx) + "_inmux02_5",
std::to_string(lc_idx) + "_inmux02_5",
true); true);
} }
} }

View File

@ -3,6 +3,7 @@
* *
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com> * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com> * Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -207,6 +208,40 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT); add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT);
add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT); add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT);
} else if (type == ctx->id("ICESTORM_PLL")) {
new_cell->params[ctx->id("DELAY_ADJMODE_FB")] = "0";
new_cell->params[ctx->id("DELAY_ADJMODE_REL")] = "0";
new_cell->params[ctx->id("DIVF")] = "0";
new_cell->params[ctx->id("DIVQ")] = "0";
new_cell->params[ctx->id("DIVR")] = "0";
new_cell->params[ctx->id("FDA_FEEDBACK")] = "0";
new_cell->params[ctx->id("FDA_RELATIVE")] = "0";
new_cell->params[ctx->id("FEEDBACK_PATH")] = "0";
new_cell->params[ctx->id("FILTER_RANGE")] = "0";
new_cell->params[ctx->id("PLLOUT_SELECT_A")] = "0";
new_cell->params[ctx->id("PLLOUT_SELECT_B")] = "0";
new_cell->params[ctx->id("PLLTYPE")] = "0";
new_cell->params[ctx->id("SHIFTREG_DIVMODE")] = "0";
new_cell->params[ctx->id("TEST_MODE")] = "0";
add_port(ctx, new_cell.get(), "BYPASS", PORT_IN);
add_port(ctx, new_cell.get(), "DYNAMICDELAY", PORT_IN);
add_port(ctx, new_cell.get(), "EXTFEEDBACK", PORT_IN);
add_port(ctx, new_cell.get(), "LATCHINPUTVALUE", PORT_IN);
add_port(ctx, new_cell.get(), "REFERENCECLK", PORT_IN);
add_port(ctx, new_cell.get(), "RESETB", PORT_IN);
add_port(ctx, new_cell.get(), "SCLK", PORT_IN);
add_port(ctx, new_cell.get(), "SDI", PORT_IN);
add_port(ctx, new_cell.get(), "SDI", PORT_OUT);
add_port(ctx, new_cell.get(), "LOCK", PORT_OUT);
add_port(ctx, new_cell.get(), "PLLOUT_A", PORT_OUT);
add_port(ctx, new_cell.get(), "PLLOUT_B", PORT_OUT);
} else { } else {
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx)); log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
} }
@ -312,6 +347,21 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
} }
} }
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell)
{
if (cell->type == ctx->id("SB_PLL40_PAD"))
return 2;
if (cell->type == ctx->id("SB_PLL40_2_PAD"))
return 4;
if (cell->type == ctx->id("SB_PLL40_2F_PAD"))
return 5;
if (cell->type == ctx->id("SB_PLL40_CORE"))
return 3;
if (cell->type == ctx->id("SB_PLL40_2F_CORE"))
return 7;
NPNR_ASSERT(0);
}
bool is_clock_port(const BaseCtx *ctx, const PortRef &port) bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
{ {
if (port.cell == nullptr) if (port.cell == nullptr)

View File

@ -71,6 +71,21 @@ inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell-
inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); } inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); }
inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
cell->type == ctx->id("SB_PLL40_2F_PAD") || cell->type == ctx->id("SB_PLL40_CORE") ||
cell->type == ctx->id("SB_PLL40_2F_CORE");
}
inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
cell->type == ctx->id("SB_PLL40_2F_PAD");
}
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports // Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
// as needed. Set no_dff if a DFF is not being used, so that the output // as needed. Set no_dff if a DFF is not being used, so that the output
// can be reconnected // can be reconnected

View File

@ -6,17 +6,11 @@ import textwrap
import argparse import argparse
parser = argparse.ArgumentParser(description="convert ICE40 chip database") parser = argparse.ArgumentParser(description="convert ICE40 chip database")
group = parser.add_mutually_exclusive_group()
group.add_argument("-b", "--binary", action="store_true")
group.add_argument("-c", "--c_file", action="store_true")
parser.add_argument("filename", type=str, help="chipdb input filename") parser.add_argument("filename", type=str, help="chipdb input filename")
parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc") parser.add_argument("-p", "--portspins", type=str, help="path to portpins.inc")
parser.add_argument("-g", "--gfxh", type=str, help="path to gfx.h") parser.add_argument("-g", "--gfxh", type=str, help="path to gfx.h")
args = parser.parse_args() args = parser.parse_args()
endianness = "le"
nodebug = True
dev_name = None dev_name = None
dev_width = None dev_width = None
dev_height = None dev_height = None
@ -41,8 +35,6 @@ extra_cells = dict()
extra_cell_config = dict() extra_cell_config = dict()
packages = list() packages = list()
wire_uphill_belport = dict()
wire_downhill_belports = dict()
wire_belports = dict() wire_belports = dict()
wire_names = dict() wire_names = dict()
@ -184,6 +176,8 @@ def wire_type(name):
wt = "LOCAL" wt = "LOCAL"
elif name in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"): elif name in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"):
wt = "LOCAL" wt = "LOCAL"
elif name in ("PLLOUT_A", "PLLOUT_B"):
wt = "LOCAL"
if wt is None: if wt is None:
print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr) print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr)
@ -451,17 +445,12 @@ for i in range(8):
add_wire(0, 0, "padin_%d" % i) add_wire(0, 0, "padin_%d" % i)
def add_bel_input(bel, wire, port): def add_bel_input(bel, wire, port):
if wire not in wire_downhill_belports:
wire_downhill_belports[wire] = set()
wire_downhill_belports[wire].add((bel, port))
if wire not in wire_belports: if wire not in wire_belports:
wire_belports[wire] = set() wire_belports[wire] = set()
wire_belports[wire].add((bel, port)) wire_belports[wire].add((bel, port))
bel_wires[bel].append((wire, port, 0)) bel_wires[bel].append((wire, port, 0))
def add_bel_output(bel, wire, port): def add_bel_output(bel, wire, port):
assert wire not in wire_uphill_belport
wire_uphill_belport[wire] = (bel, port)
if wire not in wire_belports: if wire not in wire_belports:
wire_belports[wire] = set() wire_belports[wire] = set()
wire_belports[wire].add((bel, port)) wire_belports[wire].add((bel, port))
@ -591,6 +580,9 @@ def is_ec_output(ec_entry):
if "glb_netwk_" in wirename: return True if "glb_netwk_" in wirename: return True
return False return False
def is_ec_pll_clock_output(ec, ec_entry):
return ec[0] == 'PLL' and ec_entry[0] in ('PLLOUT_A', 'PLLOUT_B')
def add_bel_ec(ec): def add_bel_ec(ec):
ectype, x, y, z = ec ectype, x, y, z = ec
bel = len(bel_name) bel = len(bel_name)
@ -605,6 +597,10 @@ def add_bel_ec(ec):
add_bel_output(bel, wire_names[entry[1]], entry[0]) add_bel_output(bel, wire_names[entry[1]], entry[0])
else: else:
add_bel_input(bel, wire_names[entry[1]], entry[0]) add_bel_input(bel, wire_names[entry[1]], entry[0])
elif is_ec_pll_clock_output(ec, entry):
x, y, z = entry[1]
z = 'io_{}/D_IN_0'.format(z)
add_bel_output(bel, wire_names[(x, y, z)], entry[0])
else: else:
extra_cell_config[bel].append(entry) extra_cell_config[bel].append(entry)
@ -662,270 +658,61 @@ for tile_xy, tile_type in sorted(tiles.items()):
add_bel_ec(ec) add_bel_ec(ec)
for ec in sorted(extra_cells.keys()): for ec in sorted(extra_cells.keys()):
if ec[1] == 0 and ec[2] == 0: if ec[1] in (0, dev_width - 1) and ec[2] in (0, dev_height - 1):
add_bel_ec(ec) add_bel_ec(ec)
class BinaryBlobAssembler: class BinaryBlobAssembler:
def __init__(self, cname, endianness, nodebug = False):
assert endianness in ["le", "be"]
self.cname = cname
self.endianness = endianness
self.finalized = False
self.data = bytearray()
self.comments = dict()
self.labels = dict()
self.exports = set()
self.labels_byaddr = dict()
self.ltypes_byaddr = dict()
self.strings = dict()
self.refs = dict()
self.nodebug = nodebug
def l(self, name, ltype = None, export = False): def l(self, name, ltype = None, export = False):
assert not self.finalized if ltype is None:
assert name not in self.labels print("label %s" % (name,))
assert len(self.data) not in self.labels_byaddr else:
self.labels[name] = len(self.data) print("label %s %s" % (name, ltype))
if ltype is not None:
self.ltypes_byaddr[len(self.data)] = ltype
self.labels_byaddr[len(self.data)] = name
if export:
assert ltype is not None
self.exports.add(len(self.data))
def r(self, name, comment): def r(self, name, comment):
assert not self.finalized if comment is None:
assert len(self.data) % 4 == 0 print("ref %s" % (name,))
assert len(self.data) not in self.refs else:
if self.nodebug: print("ref %s %s" % (name, comment))
comment = None
if name is not None:
self.refs[len(self.data)] = (name, comment)
self.data.append(0)
self.data.append(0)
self.data.append(0)
self.data.append(0)
if (name is None) and (comment is not None):
self.comments[len(self.data)] = comment + " (null reference)"
def s(self, s, comment): def s(self, s, comment):
assert not self.finalized assert "|" not in s
if self.nodebug: print("str |%s| %s" % (s, comment))
comment = None
if s not in self.strings:
index = len(self.strings)
self.strings[s] = index
else:
index = self.strings[s]
if comment is not None:
self.r("str%d" % index, '%s: "%s"' % (comment, s))
else:
self.r("str%d" % index, None)
def u8(self, v, comment): def u8(self, v, comment):
assert not self.finalized if comment is None:
if self.nodebug: print("u8 %d" % (v,))
comment = None else:
self.data.append(v) print("u8 %d %s" % (v, comment))
if comment is not None:
self.comments[len(self.data)] = comment
def u16(self, v, comment): def u16(self, v, comment):
assert not self.finalized if comment is None:
assert len(self.data) % 2 == 0 print("u16 %d" % (v,))
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
elif self.endianness == "be":
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
else: else:
assert 0 print("u16 %d %s" % (v, comment))
if comment is not None:
self.comments[len(self.data)] = comment
def u32(self, v, comment): def u32(self, v, comment):
assert not self.finalized if comment is None:
assert len(self.data) % 4 == 0 print("u32 %d" % (v,))
if self.nodebug:
comment = None
if self.endianness == "le":
self.data.append(v & 255)
self.data.append((v >> 8) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 24) & 255)
elif self.endianness == "be":
self.data.append((v >> 24) & 255)
self.data.append((v >> 16) & 255)
self.data.append((v >> 8) & 255)
self.data.append(v & 255)
else: else:
assert 0 print("u32 %d %s" % (v, comment))
if comment is not None:
self.comments[len(self.data)] = comment
def finalize(self): def pre(self, s):
assert not self.finalized print("pre %s" % s)
for s, index in sorted(self.strings.items()):
self.l("str%d" % index, "char")
for c in s:
self.data.append(ord(c))
self.data.append(0)
self.finalized = True
cursor = 0
while cursor < len(self.data):
if cursor in self.refs:
v = self.labels[self.refs[cursor][0]] - cursor
if self.endianness == "le":
self.data[cursor+0] = (v & 255)
self.data[cursor+1] = ((v >> 8) & 255)
self.data[cursor+2] = ((v >> 16) & 255)
self.data[cursor+3] = ((v >> 24) & 255)
elif self.endianness == "be":
self.data[cursor+0] = ((v >> 24) & 255)
self.data[cursor+1] = ((v >> 16) & 255)
self.data[cursor+2] = ((v >> 8) & 255)
self.data[cursor+3] = (v & 255)
else:
assert 0
cursor += 4
else:
cursor += 1
def write_verbose_c(self, f, ctype = "const unsigned char"): def post(self, s):
assert self.finalized print("post %s" % s)
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
cursor = 0
bytecnt = 0
while cursor < len(self.data):
if cursor in self.comments:
if bytecnt == 0:
print(" ", end="", file=f)
print(" // %s" % self.comments[cursor], file=f)
bytecnt = 0
if cursor in self.labels_byaddr:
if bytecnt != 0:
print(file=f)
if cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
else:
print(" // [%d] %s" % (cursor, self.labels_byaddr[cursor]), file=f)
bytecnt = 0
if cursor in self.refs:
if bytecnt != 0:
print(file=f)
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor+0]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor+1]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor+2]), end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor+3]), end="", file=f)
print(" // [%d] %s (reference to %s)" % (cursor, self.refs[cursor][1], self.refs[cursor][0]), file=f)
bytecnt = 0
cursor += 4
else:
if bytecnt == 0:
print(" ", end="", file=f)
print(" %-4s" % ("%d," % self.data[cursor]), end=("" if bytecnt < 15 else "\n"), file=f)
bytecnt = (bytecnt + 1) & 15
cursor += 1
if bytecnt != 0:
print(file=f)
print("};", file=f)
def write_compact_c(self, f, ctype = "const unsigned char"): def push(self, name):
assert self.finalized print("push %s" % name)
print("%s %s[%d] = {" % (ctype, self.cname, len(self.data)), file=f)
column = 0
for v in self.data:
if column == 0:
print(" ", end="", file=f)
column += 2
s = "%d," % v
print(s, end="", file=f)
column += len(s)
if column > 75:
print(file=f)
column = 0
if column != 0:
print(file=f)
for cursor in self.exports:
print("#define %s ((%s*)(%s+%d))" % (self.labels_byaddr[cursor], self.ltypes_byaddr[cursor], self.cname, cursor), file=f)
print("};", file=f)
def write_uint64_c(self, f, ctype = "const uint64_t"): def pop(self):
assert self.finalized print("pop")
print("%s %s[%d] = {" % (ctype, self.cname, (len(self.data)+7) // 8), file=f)
column = 0
for i in range((len(self.data)+7) // 8):
v0 = self.data[8*i+0] if 8*i+0 < len(self.data) else 0
v1 = self.data[8*i+1] if 8*i+1 < len(self.data) else 0
v2 = self.data[8*i+2] if 8*i+2 < len(self.data) else 0
v3 = self.data[8*i+3] if 8*i+3 < len(self.data) else 0
v4 = self.data[8*i+4] if 8*i+4 < len(self.data) else 0
v5 = self.data[8*i+5] if 8*i+5 < len(self.data) else 0
v6 = self.data[8*i+6] if 8*i+6 < len(self.data) else 0
v7 = self.data[8*i+7] if 8*i+7 < len(self.data) else 0
if self.endianness == "le":
v = v0 << 0
v |= v1 << 8
v |= v2 << 16
v |= v3 << 24
v |= v4 << 32
v |= v5 << 40
v |= v6 << 48
v |= v7 << 56
elif self.endianness == "be":
v = v7 << 0
v |= v6 << 8
v |= v5 << 16
v |= v4 << 24
v |= v3 << 32
v |= v2 << 40
v |= v1 << 48
v |= v0 << 56
else:
assert 0
if column == 3:
print(" 0x%016x," % v, file=f)
column = 0
else:
if column == 0:
print(" ", end="", file=f)
print(" 0x%016x," % v, end="", file=f)
column += 1
if column != 0:
print("", file=f)
print("};", file=f)
def write_string_c(self, f, ctype = "const char"): bba = BinaryBlobAssembler()
assert self.finalized bba.pre('#include "nextpnr.h"')
assert self.data[len(self.data)-1] == 0 bba.pre('NEXTPNR_NAMESPACE_BEGIN')
print("%s %s[%d] =" % (ctype, self.cname, len(self.data)), file=f) bba.post('NEXTPNR_NAMESPACE_END')
print(" \"", end="", file=f) bba.push("chipdb_blob_%s" % dev_name)
column = 0
for i in range(len(self.data)-1):
if (self.data[i] < 32) or (self.data[i] > 126):
print("\\%03o" % self.data[i], end="", file=f)
column += 4
elif self.data[i] == ord('"') or self.data[i] == ord('\\'):
print("\\" + chr(self.data[i]), end="", file=f)
column += 2
else:
print(chr(self.data[i]), end="", file=f)
column += 1
if column > 70 and (i != len(self.data)-2):
print("\"\n \"", end="", file=f)
column = 0
print("\";", file=f)
def write_binary(self, f):
assert self.finalized
assert self.data[len(self.data)-1] == 0
f.buffer.write(self.data)
bba = BinaryBlobAssembler("chipdb_blob_%s" % dev_name, endianness)
bba.r("chip_info_%s" % dev_name, "chip_info") bba.r("chip_info_%s" % dev_name, "chip_info")
index = 0 index = 0
@ -1001,15 +788,6 @@ for wire in range(num_wires):
num_downhill = 0 num_downhill = 0
list_downhill = None list_downhill = None
if wire in wire_downhill_belports:
num_bels_downhill = len(wire_downhill_belports[wire])
bba.l("wire%d_downbels" % wire, "BelPortPOD")
for belport in sorted(wire_downhill_belports[wire]):
bba.u32(belport[0], "bel_index")
bba.u32(portpins[belport[1]], "port")
else:
num_bels_downhill = 0
if wire in wire_belports: if wire in wire_belports:
num_bel_pins = len(wire_belports[wire]) num_bel_pins = len(wire_belports[wire])
bba.l("wire%d_bels" % wire, "BelPortPOD") bba.l("wire%d_bels" % wire, "BelPortPOD")
@ -1028,19 +806,9 @@ for wire in range(num_wires):
info["num_downhill"] = num_downhill info["num_downhill"] = num_downhill
info["list_downhill"] = list_downhill info["list_downhill"] = list_downhill
info["num_bels_downhill"] = num_bels_downhill
info["list_bels_downhill"] = ("wire%d_downbels" % wire) if num_bels_downhill > 0 else None
info["num_bel_pins"] = num_bel_pins info["num_bel_pins"] = num_bel_pins
info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None
if wire in wire_uphill_belport:
info["uphill_bel"] = wire_uphill_belport[wire][0]
info["uphill_pin"] = portpins[wire_uphill_belport[wire][1]]
else:
info["uphill_bel"] = -1
info["uphill_pin"] = 0
avg_x, avg_y = 0, 0 avg_x, avg_y = 0, 0
if wire in wire_xy: if wire in wire_xy:
for x, y in wire_xy[wire]: for x, y in wire_xy[wire]:
@ -1115,10 +883,6 @@ for wire, info in enumerate(wireinfo):
bba.u32(info["num_downhill"], "num_downhill") bba.u32(info["num_downhill"], "num_downhill")
bba.r(info["list_uphill"], "pips_uphill") bba.r(info["list_uphill"], "pips_uphill")
bba.r(info["list_downhill"], "pips_downhill") bba.r(info["list_downhill"], "pips_downhill")
bba.u32(info["num_bels_downhill"], "num_bels_downhill")
bba.u32(info["uphill_bel"], "bel_uphill.bel_index")
bba.u32(info["uphill_pin"], "bel_uphill.port")
bba.r(info["list_bels_downhill"], "bels_downhill")
bba.u32(info["num_bel_pins"], "num_bel_pins") bba.u32(info["num_bel_pins"], "num_bel_pins")
bba.r(info["list_bel_pins"], "bel_pins") bba.r(info["list_bel_pins"], "bel_pins")
bba.u32(len(wire_segments[wire]), "num_segments") bba.u32(len(wire_segments[wire]), "num_segments")
@ -1264,23 +1028,4 @@ bba.r("bits_info_%s" % dev_name, "bits_info")
bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config") bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config")
bba.r("package_info_%s" % dev_name, "packages_data") bba.r("package_info_%s" % dev_name, "packages_data")
bba.finalize() bba.pop()
if args.c_file:
print('#include "nextpnr.h"')
print('NEXTPNR_NAMESPACE_BEGIN')
if args.binary:
bba.write_binary(sys.stdout)
if args.c_file:
bba.write_string_c(sys.stdout)
# bba.write_uint64_c(sys.stdout)
# bba.write_compact_c(sys.stdout, "uint8_t")
# bba.write_verbose_c(sys.stdout, "uint8_t")
if args.c_file:
print('NEXTPNR_NAMESPACE_END')

View File

@ -19,13 +19,18 @@ if (MSVC)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ice40/resources/chipdb.rc PROPERTIES LANGUAGE RC) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ice40/resources/chipdb.rc PROPERTIES LANGUAGE RC)
foreach (dev ${devices}) foreach (dev ${devices})
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt) set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h) set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_DB} add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB} COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY} DEPENDS ${DEV_TXT_DB} ${DB_PY}
) )
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm ${DEV_CC_BBA_DB} ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB}) target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE) set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE)
foreach (target ${family_targets}) foreach (target ${family_targets})
@ -36,14 +41,20 @@ else()
target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w) target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w)
foreach (dev ${devices}) foreach (dev ${devices})
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt) set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h) set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_DB} add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB}.new COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB} COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY} DEPENDS ${DEV_TXT_DB} ${DB_PY}
) )
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm --c ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB}) target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
foreach (target ${family_targets}) foreach (target ${family_targets})
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ice40_chipdb>) target_sources(${target} PRIVATE $<TARGET_OBJECTS:ice40_chipdb>)

View File

@ -24,7 +24,7 @@ NEXTPNR_NAMESPACE_BEGIN
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style) void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)
{ {
GraphicElement el; GraphicElement el;
el.type = GraphicElement::G_LINE; el.type = GraphicElement::TYPE_LINE;
el.style = style; el.style = style;
// Horizontal Span-4 Wires // 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); float ty = 0.5 * (y1 + y2);
GraphicElement el; GraphicElement el;
el.type = GraphicElement::G_LINE; el.type = GraphicElement::TYPE_ARROW;
el.style = style; el.style = style;
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) { 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)) 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); 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 NEXTPNR_NAMESPACE_END

View File

@ -464,7 +464,11 @@ enum GfxTileWireId
TILE_WIRE_SP12_H_R_23, TILE_WIRE_SP12_H_R_23,
TILE_WIRE_SP12_H_L_22, TILE_WIRE_SP12_H_L_22,
TILE_WIRE_SP12_H_L_23 TILE_WIRE_SP12_H_L_23,
TILE_WIRE_PLLIN,
TILE_WIRE_PLLOUT_A,
TILE_WIRE_PLLOUT_B
}; };
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style); void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);

View File

@ -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\""; const std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\"";
for (auto &el : ctx->getDecalGraphics(decal.decal)) { 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=\"" std::cout << "<rect x=\"" << (offset + scale * (decal.x + el.x1)) << "\" y=\""
<< (offset + scale * (decal.y + el.y1)) << "\" height=\"" << (scale * (el.y2 - el.y1)) << (offset + scale * (decal.y + el.y1)) << "\" height=\"" << (scale * (el.y2 - el.y1))
<< "\" width=\"" << (scale * (el.x2 - el.x1)) << "\" " << style << "/>\n"; << "\" 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=\"" std::cout << "<line x1=\"" << (offset + scale * (decal.x + el.x1)) << "\" y1=\""
<< (offset + scale * (decal.y + el.y1)) << "\" x2=\"" << (offset + scale * (decal.x + el.x2)) << (offset + scale * (decal.y + el.y1)) << "\" x2=\"" << (offset + scale * (decal.x + el.x2))
<< "\" y2=\"" << (offset + scale * (decal.y + el.y2)) << "\" " << style << "/>\n"; << "\" y2=\"" << (offset + scale * (decal.y + el.y2)) << "\" " << style << "/>\n";
@ -144,18 +144,16 @@ int main(int argc, char *argv[])
#endif #endif
if (vm.count("help") || argc == 1) { if (vm.count("help") || argc == 1) {
help: help:
std::cout << boost::filesystem::basename(argv[0]) std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git "
<< " -- Next Generation Place and Route (git " "sha1 " GIT_COMMIT_HASH_STR ")\n";
"sha1 " GIT_COMMIT_HASH_STR ")\n";
std::cout << "\n"; std::cout << "\n";
std::cout << options << "\n"; std::cout << options << "\n";
return argc != 1; return argc != 1;
} }
if (vm.count("version")) { if (vm.count("version")) {
std::cout << boost::filesystem::basename(argv[0]) std::cout << boost::filesystem::basename(argv[0]) << " -- Next Generation Place and Route (git "
<< " -- Next Generation Place and Route (git " "sha1 " GIT_COMMIT_HASH_STR ")\n";
"sha1 " GIT_COMMIT_HASH_STR ")\n";
return 1; return 1;
} }
@ -365,8 +363,12 @@ int main(int argc, char *argv[])
} }
} }
if (vm.count("freq")) if (vm.count("freq")) {
ctx->target_freq = vm["freq"].as<double>() * 1e6; ctx->target_freq = vm["freq"].as<double>() * 1e6;
ctx->user_freq = true;
} else {
log_warning("Target frequency not specified. Will optimise for max frequency.\n");
}
ctx->timing_driven = true; ctx->timing_driven = true;
if (vm.count("no-tmdriv")) if (vm.count("no-tmdriv"))

View File

@ -3,6 +3,7 @@
* *
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com> * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com> * Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -311,6 +312,10 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne
(user.port != ctx->id("CLK") && (user.port != ctx->id("CLK") &&
((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) { ((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) {
uc->ports[user.port].net = nullptr; uc->ports[user.port].net = nullptr;
} else if (is_ram(ctx, uc) && !constval && user.port != ctx->id("RCLK") && user.port != ctx->id("RCLKN") &&
user.port != ctx->id("WCLK") && user.port != ctx->id("WCLKN") && user.port != ctx->id("RCLKE") &&
user.port != ctx->id("WCLKE")) {
uc->ports[user.port].net = nullptr;
} else { } else {
uc->ports[user.port].net = constnet; uc->ports[user.port].net = constnet;
constnet->users.push_back(user); constnet->users.push_back(user);
@ -536,6 +541,56 @@ static void promote_globals(Context *ctx)
} }
} }
// spliceLUT adds a pass-through LUT LC between the given cell's output port
// and either all users or only non_LUT users.
static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
{
auto port = ci->ports[portId];
NPNR_ASSERT(port.net != nullptr);
// Create pass-through LUT.
std::unique_ptr<CellInfo> pt = create_ice_cell(ctx, ctx->id("ICESTORM_LC"),
ci->name.str(ctx) + "$nextpnr_" + portId.str(ctx) + "_lut_through");
pt->params[ctx->id("LUT_INIT")] = "65280"; // output is always I3
// Create LUT output net.
std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_" + portId.str(ctx) + "_lut_through_net");
out_net->driver.cell = pt.get();
out_net->driver.port = ctx->id("O");
pt->ports.at(ctx->id("O")).net = out_net.get();
// New users of the original cell's port
std::vector<PortRef> new_users;
for (const auto &user : port.net->users) {
if (onlyNonLUTs && user.cell->type == ctx->id("ICESTORM_LC")) {
new_users.push_back(user);
continue;
}
// Rewrite pointer into net in user.
user.cell->ports[user.port].net = out_net.get();
// Add user to net.
PortRef pr;
pr.cell = user.cell;
pr.port = user.port;
out_net->users.push_back(pr);
}
// Add LUT to new users.
PortRef pr;
pr.cell = pt.get();
pr.port = ctx->id("I3");
new_users.push_back(pr);
pt->ports.at(ctx->id("I3")).net = port.net;
// Replace users of the original net.
port.net->users = new_users;
ctx->nets[out_net->name] = std::move(out_net);
return pt;
}
// Pack special functions // Pack special functions
static void pack_special(Context *ctx) static void pack_special(Context *ctx)
{ {
@ -590,6 +645,184 @@ static void pack_special(Context *ctx)
} }
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
} }
new_cells.push_back(std::move(packed));
} else if (is_sb_pll40(ctx, ci)) {
bool is_pad = is_sb_pll40_pad(ctx, ci);
bool is_core = !is_pad;
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL");
packed->attrs[ctx->id("TYPE")] = ci->type.str(ctx);
packed_cells.insert(ci->name);
for (auto attr : ci->attrs)
packed->attrs[attr.first] = attr.second;
for (auto param : ci->params)
packed->params[param.first] = param.second;
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
packed->params[ctx->id("FEEDBACK_PATH")] =
feedback_path == "DELAY" ? "0" : feedback_path == "SIMPLE"
? "1"
: feedback_path == "PHASE_AND_DELAY"
? "2"
: feedback_path == "EXTERNAL" ? "6"
: feedback_path;
packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci));
NetInfo *pad_packagepin_net = nullptr;
for (auto port : ci->ports) {
PortInfo &pi = port.second;
std::string newname = pi.name.str(ctx);
size_t bpos = newname.find('[');
if (bpos != std::string::npos) {
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
}
if (pi.name == ctx->id("PLLOUTCOREA"))
newname = "PLLOUT_A";
if (pi.name == ctx->id("PLLOUTCOREB"))
newname = "PLLOUT_B";
if (pi.name == ctx->id("PLLOUTCORE"))
newname = "PLLOUT_A";
if (pi.name == ctx->id("PACKAGEPIN")) {
if (!is_pad) {
log_error(" PLL '%s' has a PACKAGEPIN but is not a PAD PLL", ci->name.c_str(ctx));
} else {
// We drop this port and instead place the PLL adequately below.
pad_packagepin_net = port.second.net;
NPNR_ASSERT(pad_packagepin_net != nullptr);
continue;
}
}
if (pi.name == ctx->id("REFERENCECLK")) {
if (!is_core)
log_error(" PLL '%s' has a REFERENCECLK but is not a CORE PLL", ci->name.c_str(ctx));
}
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
// If PLL is not constrained already, do that - we need this
// information to then constrain the LOCK LUT.
BelId pll_bel;
bool constrained = false;
if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) {
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != TYPE_ICESTORM_PLL)
continue;
// A PAD PLL must have its' PACKAGEPIN on the SB_IO that's shared
// with PLLOUT_A.
if (is_pad) {
auto pll_sb_io_belpin = ctx->getIOBSharingPLLPin(bel, PIN_PLLOUT_A);
NPNR_ASSERT(pad_packagepin_net != nullptr);
auto pll_packagepin_driver = pad_packagepin_net->driver;
NPNR_ASSERT(pll_packagepin_driver.cell != nullptr);
if (pll_packagepin_driver.cell->type != ctx->id("SB_IO")) {
log_error(" PLL '%s' has a PACKAGEPIN driven by "
"an %s, should be directly connected to an input SB_IO\n",
ci->name.c_str(ctx), pll_packagepin_driver.cell->type.c_str(ctx));
}
auto packagepin_cell = pll_packagepin_driver.cell;
auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL"));
if (packagepin_bel_name == packagepin_cell->attrs.end()) {
log_error(" PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
packagepin_cell->name.c_str(ctx));
}
auto packagepin_bel = ctx->getBelByName(ctx->id(packagepin_bel_name->second));
if (pll_sb_io_belpin.bel != packagepin_bel) {
log_error(" PLL '%s' PACKAGEPIN is connected to pin %s, can only be pin %s\n",
ci->name.c_str(ctx), ctx->getBelPackagePin(packagepin_bel).c_str(),
ctx->getBelPackagePin(pll_sb_io_belpin.bel).c_str());
}
if (pad_packagepin_net->users.size() != 1) {
log_error(" PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx),
pad_packagepin_net->name.c_str(ctx));
}
// Set an attribute about this PLL's PAD SB_IO.
packed->attrs[ctx->id("BEL_PAD_INPUT")] = packagepin_bel_name->second;
// Remove the connection from the SB_IO to the PLL.
packagepin_cell->ports.erase(pll_packagepin_driver.port);
}
log_info(" constrained '%s' to %s\n", packed->name.c_str(ctx), ctx->getBelName(bel).c_str(ctx));
packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
pll_bel = bel;
constrained = true;
}
if (!constrained) {
log_error(" could not constrain '%s' to any PLL Bel\n", packed->name.c_str(ctx));
}
}
// Delete the original PACKAGEPIN net if needed.
if (pad_packagepin_net != nullptr) {
for (auto user : pad_packagepin_net->users) {
user.cell->ports.erase(user.port);
}
ctx->nets.erase(pad_packagepin_net->name);
pad_packagepin_net = nullptr;
}
// The LOCK signal on iCE40 PLLs goes through the neigh_op_bnl_1 wire.
// In practice, this means the LOCK signal can only directly reach LUT
// inputs.
// If we have a net connected to LOCK, make sure it only drives LUTs.
auto port = packed->ports[ctx->id("LOCK")];
if (port.net != nullptr) {
bool found_lut = false;
bool all_luts = true;
unsigned int lut_count = 0;
for (const auto &user : port.net->users) {
NPNR_ASSERT(user.cell != nullptr);
if (user.cell->type == ctx->id("ICESTORM_LC")) {
found_lut = true;
lut_count++;
} else {
all_luts = false;
}
}
if (found_lut && all_luts) {
// Every user is a LUT, carry on now.
} else if (found_lut && !all_luts && lut_count < 8) {
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
new_cells.push_back(std::move(pt));
} else {
// Strategy: create a pass-through LUT, move every user behind it.
log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx));
auto pt = spliceLUT(ctx, packed.get(), port.name, false);
new_cells.push_back(std::move(pt));
}
// Find wire that will be driven by this port.
const auto pll_out_wire = ctx->getBelPinWire(pll_bel, ctx->portPinFromId(port.name));
NPNR_ASSERT(pll_out_wire.index != -1);
// Now, constrain all LUTs on the output of the signal to be at
// the correct Bel relative to the PLL Bel.
int x = ctx->chip_info->wire_data[pll_out_wire.index].x;
int y = ctx->chip_info->wire_data[pll_out_wire.index].y;
int z = 0;
for (const auto &user : port.net->users) {
NPNR_ASSERT(user.cell != nullptr);
NPNR_ASSERT(user.cell->type == ctx->id("ICESTORM_LC"));
// TODO(q3k): handle when the Bel might be already the
// target of another constraint.
NPNR_ASSERT(z < 8);
auto target_bel = ctx->getBelByLocation(Loc(x, y, z++));
auto target_bel_name = ctx->getBelName(target_bel).str(ctx);
user.cell->attrs[ctx->id("BEL")] = target_bel_name;
log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str());
}
}
new_cells.push_back(std::move(packed)); new_cells.push_back(std::move(packed));
} }
} }

42
ice40/picorv32_benchmark.py Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
import os, sys, threading
from os import path
import subprocess
import re
num_runs = 8
if not path.exists("picorv32.json"):
subprocess.run(["wget", "https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v"], check=True)
subprocess.run(["yosys", "-q", "-p", "synth_ice40 -json picorv32.json -top top", "picorv32.v", "picorv32_top.v"], check=True)
fmax = {}
if not path.exists("picorv32_work"):
os.mkdir("picorv32_work")
threads = []
for i in range(num_runs):
def runner(run):
ascfile = "picorv32_work/picorv32_s{}.asc".format(run)
if path.exists(ascfile):
os.remove(ascfile)
result = subprocess.run(["../nextpnr-ice40", "--hx8k", "--seed", str(run), "--json", "picorv32.json", "--asc", ascfile, "--freq", "70"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
if result.returncode != 0:
print("Run {} failed!".format(run))
else:
icetime_res = subprocess.check_output(["icetime", "-d", "hx8k", ascfile])
fmax_m = re.search(r'\(([0-9.]+) MHz\)', icetime_res.decode('utf-8'))
fmax[run] = float(fmax_m.group(1))
threads.append(threading.Thread(target=runner, args=[i+1]))
for t in threads: t.start()
for t in threads: t.join()
fmax_min = min(fmax.values())
fmax_max = max(fmax.values())
fmax_avg = sum(fmax.values()) / len(fmax)
print("{}/{} runs passed".format(len(fmax), num_runs))
print("icetime: min = {} MHz, avg = {} MHz, max = {} MHz".format(fmax_min, fmax_avg, fmax_max))

View File

@ -75,11 +75,9 @@ static void get_chain_midpoint(const Context *ctx, const CellChain &chain, float
for (auto cell : chain.cells) { for (auto cell : chain.cells) {
if (cell->bel == BelId()) if (cell->bel == BelId())
continue; continue;
int bel_x, bel_y; Loc bel_loc = ctx->getBelLocation(cell->bel);
bool bel_gb; total_x += bel_loc.x;
ctx->estimatePosition(cell->bel, bel_x, bel_y, bel_gb); total_y += bel_loc.y;
total_x += bel_x;
total_y += bel_y;
N++; N++;
} }
NPNR_ASSERT(N > 0); NPNR_ASSERT(N > 0);
@ -92,21 +90,20 @@ static int get_cell_evilness(const Context *ctx, const CellInfo *cell)
// This returns how "evil" a logic cell is, and thus how likely it is to be ripped up // This returns how "evil" a logic cell is, and thus how likely it is to be ripped up
// during logic tile legalisation // during logic tile legalisation
int score = 0; int score = 0;
if (get_net_or_empty(cell, ctx->id("I0"))) if (get_net_or_empty(cell, ctx->id_i0))
++score; ++score;
if (get_net_or_empty(cell, ctx->id("I1"))) if (get_net_or_empty(cell, ctx->id_i1))
++score; ++score;
if (get_net_or_empty(cell, ctx->id("I2"))) if (get_net_or_empty(cell, ctx->id_i2))
++score; ++score;
if (get_net_or_empty(cell, ctx->id("I3"))) if (get_net_or_empty(cell, ctx->id_i3))
++score; ++score;
if (bool_or_default(cell->params, ctx->id("DFF_ENABLE"))) { if (cell->lcInfo.dffEnable) {
const NetInfo *cen = get_net_or_empty(cell, ctx->id("CEN")), *sr = get_net_or_empty(cell, ctx->id("SR")); if (cell->lcInfo.cen)
if (cen)
score += 10; score += 10;
if (sr) if (cell->lcInfo.sr)
score += 10; score += 10;
if (bool_or_default(cell->params, ctx->id("NEG_CLK"))) if (cell->lcInfo.negClk)
score += 5; score += 5;
} }
return score; return score;

View File

@ -118,6 +118,8 @@ X(DYNAMICDELAY_5)
X(DYNAMICDELAY_6) X(DYNAMICDELAY_6)
X(DYNAMICDELAY_7) X(DYNAMICDELAY_7)
X(LOCK) X(LOCK)
X(PLLOUT_A)
X(PLLOUT_B)
X(BYPASS) X(BYPASS)
X(RESETB) X(RESETB)
X(LATCHINPUTVALUE) X(LATCHINPUTVALUE)