This commit is contained in:
Eddie Hung 2018-11-27 09:45:35 -08:00
commit 664c48f5e4
34 changed files with 862 additions and 313 deletions

View File

@ -121,14 +121,14 @@ if (BUILD_PYTHON)
set(version ${PYTHONLIBS_VERSION_STRING}) set(version ${PYTHONLIBS_VERSION_STRING})
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version}) STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
find_package(Boost COMPONENTS "python-py${boost_py_version}" ${boost_libs}) find_package(Boost QUIET COMPONENTS "python-py${boost_py_version}" ${boost_libs})
set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND}) set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND})
while (NOT "${version}" STREQUAL "" AND NOT Boost_PYTHON_FOUND) while (NOT "${version}" STREQUAL "" AND NOT Boost_PYTHON_FOUND)
STRING(REGEX REPLACE "([0-9.]+).[0-9]+" "\\1" version ${version}) STRING(REGEX REPLACE "([0-9.]+).[0-9]+" "\\1" version ${version})
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version}) STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
find_package(Boost COMPONENTS "python-py${boost_py_version}" ${boost_libs}) find_package(Boost QUIET COMPONENTS "python-py${boost_py_version}" ${boost_libs})
set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND}) set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND})
STRING(REGEX MATCHALL "([0-9.]+).[0-9]+" has_more_version ${version}) STRING(REGEX MATCHALL "([0-9.]+).[0-9]+" has_more_version ${version})
@ -138,21 +138,21 @@ if (BUILD_PYTHON)
endwhile () endwhile ()
if (NOT Boost_PYTHON_FOUND) if (NOT Boost_PYTHON_FOUND)
find_package(Boost COMPONENTS python3 ${boost_libs}) find_package(Boost QUIET COMPONENTS python3 ${boost_libs})
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" ) if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE) set(Boost_PYTHON_FOUND TRUE)
endif () endif ()
endif () endif ()
if (NOT Boost_PYTHON_FOUND) if (NOT Boost_PYTHON_FOUND)
find_package(Boost COMPONENTS python36 ${boost_libs}) find_package(Boost QUIET COMPONENTS python36 ${boost_libs})
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" ) if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE) set(Boost_PYTHON_FOUND TRUE)
endif () endif ()
endif () endif ()
if (NOT Boost_PYTHON_FOUND) if (NOT Boost_PYTHON_FOUND)
find_package(Boost COMPONENTS python37 ${boost_libs}) find_package(Boost QUIET COMPONENTS python37 ${boost_libs})
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" ) if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE) set(Boost_PYTHON_FOUND TRUE)
endif () endif ()
@ -160,7 +160,7 @@ if (BUILD_PYTHON)
if (NOT Boost_PYTHON_FOUND) if (NOT Boost_PYTHON_FOUND)
STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING}) STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING})
find_package(Boost COMPONENTS python-${gentoo_version} ${boost_libs}) find_package(Boost QUIET COMPONENTS python-${gentoo_version} ${boost_libs})
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" ) if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE) set(Boost_PYTHON_FOUND TRUE)
endif () endif ()

View File

@ -35,7 +35,7 @@ of the selected architecture:
- Qt5 or later (`qt5-default` for Ubuntu 16.04) - Qt5 or later (`qt5-default` for Ubuntu 16.04)
- Python 3.5 or later, including development libraries (`python3-dev` for Ubuntu) - Python 3.5 or later, including development libraries (`python3-dev` for Ubuntu)
- on Windows make sure to install same version as supported by [vcpkg](https://github.com/Microsoft/vcpkg/blob/master/ports/python3/CONTROL) - on Windows make sure to install same version as supported by [vcpkg](https://github.com/Microsoft/vcpkg/blob/master/ports/python3/CONTROL)
- Boost libraries (`libboost-dev` or `libboost-all-dev` for Ubuntu) - Boost libraries (`libboost-dev libboost-filesystem-dev libboost-thread-dev libboost-program-options-dev libboost-python-dev libboost-dev` or `libboost-all-dev` for Ubuntu)
- Latest git Yosys is required to synthesise the demo design - Latest git Yosys is required to synthesise the demo design
- For building on Windows with MSVC, usage of vcpkg is advised for dependency installation. - For building on Windows with MSVC, usage of vcpkg is advised for dependency installation.
- For 32 bit builds: `vcpkg install boost-filesystem boost-program-options boost-thread boost-python qt5-base` - For 32 bit builds: `vcpkg install boost-filesystem boost-program-options boost-thread boost-python qt5-base`
@ -49,7 +49,8 @@ Getting started
### nextpnr-ice40 ### nextpnr-ice40
To build the iCE40 version of nextpnr, install [icestorm](http://www.clifford.at/icestorm/) with chipdbs installed in `/usr/local/share/icebox`. To build the iCE40 version of nextpnr, install [icestorm](http://www.clifford.at/icestorm/) with chipdbs installed in `/usr/local/share/icebox`
(or another location, which should be passed as -DICEBOX_ROOT=/path/to/icebox to CMake).
Then build and install `nextpnr-ice40` using the following commands: Then build and install `nextpnr-ice40` using the following commands:
``` ```
@ -96,8 +97,6 @@ sudo make install
- More examples of the ECP5 flow for a range of boards can be found in the [Project Trellis Examples](https://github.com/SymbiFlow/prjtrellis/tree/master/examples). - More examples of the ECP5 flow for a range of boards can be found in the [Project Trellis Examples](https://github.com/SymbiFlow/prjtrellis/tree/master/examples).
- Currently the ECP5 flow supports LUTs, flipflops and IO. IO must be instantiated using `TRELLIS_IO` primitives and constraints specified
using `LOC` and `IO_TYPE` attributes on those instances, as is used in the examples.
### nextpnr-generic ### nextpnr-generic
@ -116,7 +115,7 @@ Additional notes for building nextpnr
Use cmake `-D` options to specify which version of nextpnr you want to build. Use cmake `-D` options to specify which version of nextpnr you want to build.
Use `-DARCH=...` to set the architecture. It is semicolon separated list. Use `-DARCH=...` to set the architecture. It is a semicolon separated list.
Use `cmake . -DARCH=all` to build all supported architectures. Use `cmake . -DARCH=all` to build all supported architectures.
The following runs a debug build of the iCE40 architecture without GUI The following runs a debug build of the iCE40 architecture without GUI
@ -134,6 +133,9 @@ cmake -DARCH=ice40 -DBUILD_PYTHON=OFF -DBUILD_GUI=OFF -DSTATIC_BUILD=ON .
make -j$(nproc) make -j$(nproc)
``` ```
You can change the location where nextpnr will be installed (this will usually default to `/usr/local`) by using
`-DCMAKE_INSTALL_PREFIX=/install/prefix`.
Notes for developers Notes for developers
-------------------- --------------------

View File

@ -36,11 +36,12 @@
#include "jsonparse.h" #include "jsonparse.h"
#include "log.h" #include "log.h"
#include "timing.h" #include "timing.h"
#include "util.h"
#include "version.h" #include "version.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
CommandHandler::CommandHandler(int argc, char **argv) : argc(argc), argv(argv) { log_files.push_back(stdout); } CommandHandler::CommandHandler(int argc, char **argv) : argc(argc), argv(argv) { log_streams.clear(); }
bool CommandHandler::parseOptions() bool CommandHandler::parseOptions()
{ {
@ -64,14 +65,14 @@ bool CommandHandler::parseOptions()
bool CommandHandler::executeBeforeContext() bool CommandHandler::executeBeforeContext()
{ {
if (vm.count("help") || argc == 1) { if (vm.count("help") || argc == 1) {
std::cout << boost::filesystem::basename(argv[0]) std::cerr << boost::filesystem::basename(argv[0])
<< " -- Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")\n"; << " -- Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")\n";
std::cout << options << "\n"; std::cerr << options << "\n";
return argc != 1; return argc != 1;
} }
if (vm.count("version")) { if (vm.count("version")) {
std::cout << boost::filesystem::basename(argv[0]) std::cerr << boost::filesystem::basename(argv[0])
<< " -- Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")\n"; << " -- Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")\n";
return true; return true;
} }
@ -84,7 +85,9 @@ po::options_description CommandHandler::getGeneralOptions()
po::options_description general("General options"); po::options_description general("General options");
general.add_options()("help,h", "show help"); general.add_options()("help,h", "show help");
general.add_options()("verbose,v", "verbose output"); general.add_options()("verbose,v", "verbose output");
general.add_options()("quiet,q", "quiet mode, only errors displayed"); general.add_options()("quiet,q", "quiet mode, only errors and warnings displayed");
general.add_options()("log,l", po::value<std::string>(),
"log file, all log messages are written to this file regardless of -q");
general.add_options()("debug", "debug output"); general.add_options()("debug", "debug output");
general.add_options()("force,f", "keep running after errors"); general.add_options()("force,f", "keep running after errors");
#ifndef NO_GUI #ifndef NO_GUI
@ -128,7 +131,17 @@ void CommandHandler::setupContext(Context *ctx)
} }
if (vm.count("quiet")) { if (vm.count("quiet")) {
log_quiet_warnings = true; log_streams.push_back(std::make_pair(&std::cerr, LogLevel::WARNING));
} else {
log_streams.push_back(std::make_pair(&std::cerr, LogLevel::LOG));
}
if (vm.count("log")) {
std::string logfilename = vm["log"].as<std::string>();
logfile = std::ofstream(logfilename);
if (!logfile)
log_error("Failed to open log file '%s' for writing.\n", logfilename.c_str());
log_streams.push_back(std::make_pair(&logfile, LogLevel::LOG));
} }
if (vm.count("force")) { if (vm.count("force")) {
@ -144,7 +157,7 @@ void CommandHandler::setupContext(Context *ctx)
int r; int r;
do { do {
r = rand(); r = rand();
} while(r == 0); } while (r == 0);
ctx->rngseed(r); ctx->rngseed(r);
} }
@ -258,7 +271,7 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
deinit_python(); deinit_python();
#endif #endif
return 0; return had_nonfatal_error ? 1 : 0;
} }
void CommandHandler::conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, void CommandHandler::conflicting_options(const boost::program_options::variables_map &vm, const char *opt1,
@ -270,6 +283,15 @@ void CommandHandler::conflicting_options(const boost::program_options::variables
} }
} }
void CommandHandler::printFooter()
{
int warning_count = get_or_default(message_count_by_level, LogLevel::WARNING, 0),
error_count = get_or_default(message_count_by_level, LogLevel::ERROR, 0);
if (warning_count > 0 || error_count > 0)
log_always("%d warning%s, %d error%s\n", warning_count, warning_count == 1 ? "" : "s", error_count,
error_count == 1 ? "" : "s");
}
int CommandHandler::exec() int CommandHandler::exec()
{ {
try { try {
@ -288,8 +310,11 @@ int CommandHandler::exec()
settings = std::unique_ptr<Settings>(new Settings(ctx.get())); settings = std::unique_ptr<Settings>(new Settings(ctx.get()));
setupContext(ctx.get()); setupContext(ctx.get());
setupArchContext(ctx.get()); setupArchContext(ctx.get());
return executeMain(std::move(ctx)); int rc = executeMain(std::move(ctx));
printFooter();
return rc;
} catch (log_execution_error_exception) { } catch (log_execution_error_exception) {
printFooter();
return -1; return -1;
} }
} }

View File

@ -54,6 +54,7 @@ class CommandHandler
int executeMain(std::unique_ptr<Context> ctx); int executeMain(std::unique_ptr<Context> ctx);
po::options_description getGeneralOptions(); po::options_description getGeneralOptions();
void run_script_hook(const std::string &name); void run_script_hook(const std::string &name);
void printFooter();
protected: protected:
po::variables_map vm; po::variables_map vm;
@ -66,6 +67,7 @@ class CommandHandler
int argc; int argc;
char **argv; char **argv;
ProjectHandler project; ProjectHandler project;
std::ofstream logfile;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -32,19 +32,15 @@ NEXTPNR_NAMESPACE_BEGIN
NPNR_NORETURN void logv_error(const char *format, va_list ap) NPNR_ATTRIBUTE(noreturn); NPNR_NORETURN void logv_error(const char *format, va_list ap) NPNR_ATTRIBUTE(noreturn);
std::vector<FILE *> log_files; std::vector<std::pair<std::ostream *, LogLevel>> log_streams;
std::vector<std::ostream *> log_streams;
FILE *log_errfile = NULL;
log_write_type log_write_function = nullptr; log_write_type log_write_function = nullptr;
bool log_error_stderr = false;
bool log_cmd_error_throw = false;
bool log_quiet_warnings = false;
std::string log_last_error; std::string log_last_error;
void (*log_error_atexit)() = NULL; void (*log_error_atexit)() = NULL;
// static bool next_print_log = false; std::unordered_map<LogLevel, int> message_count_by_level;
static int log_newline_count = 0; static int log_newline_count = 0;
bool had_nonfatal_error = false;
std::string stringf(const char *fmt, ...) std::string stringf(const char *fmt, ...)
{ {
@ -88,7 +84,7 @@ std::string vstringf(const char *fmt, va_list ap)
return string; return string;
} }
void logv(const char *format, va_list ap) void logv(const char *format, va_list ap, LogLevel level = LogLevel::LOG)
{ {
// //
// Trim newlines from the beginning // Trim newlines from the beginning
@ -108,90 +104,51 @@ void logv(const char *format, va_list ap)
else else
log_newline_count = str.size() - nnl_pos - 1; log_newline_count = str.size() - nnl_pos - 1;
for (auto f : log_files)
fputs(str.c_str(), f);
for (auto f : log_streams) for (auto f : log_streams)
*f << str; if (f.second <= level)
*f.first << str;
if (log_write_function) if (log_write_function)
log_write_function(str); log_write_function(str);
} }
void logv_info(const char *format, va_list ap) void log_with_level(LogLevel level, const char *format, ...)
{
message_count_by_level[level]++;
va_list ap;
va_start(ap, format);
logv(format, ap, level);
va_end(ap);
}
void logv_prefixed(const char *prefix, const char *format, va_list ap, LogLevel level)
{ {
std::string message = vstringf(format, ap); std::string message = vstringf(format, ap);
log_always("Info: %s", message.c_str()); log_with_level(level, "%s%s", prefix, message.c_str());
log_flush(); log_flush();
} }
void logv_warning(const char *format, va_list ap)
{
std::string message = vstringf(format, ap);
log_always("Warning: %s", message.c_str());
log_flush();
}
void logv_warning_noprefix(const char *format, va_list ap)
{
std::string message = vstringf(format, ap);
log_always("%s", message.c_str());
}
void logv_error(const char *format, va_list ap)
{
#ifdef EMSCRIPTEN
auto backup_log_files = log_files;
#endif
if (log_errfile != NULL)
log_files.push_back(log_errfile);
if (log_error_stderr)
for (auto &f : log_files)
if (f == stdout)
f = stderr;
log_last_error = vstringf(format, ap);
log_always("ERROR: %s", log_last_error.c_str());
log_flush();
if (log_error_atexit)
log_error_atexit();
#ifdef EMSCRIPTEN
log_files = backup_log_files;
#endif
throw log_execution_error_exception();
}
void log_always(const char *format, ...) void log_always(const char *format, ...)
{ {
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
logv(format, ap); logv(format, ap, LogLevel::ALWAYS);
va_end(ap); va_end(ap);
} }
void log(const char *format, ...) void log(const char *format, ...)
{ {
if (log_quiet_warnings)
return;
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
logv(format, ap); logv(format, ap, LogLevel::LOG);
va_end(ap); va_end(ap);
} }
void log_info(const char *format, ...) void log_info(const char *format, ...)
{ {
if (log_quiet_warnings)
return;
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
logv_info(format, ap); logv_prefixed("Info: ", format, ap, LogLevel::INFO);
va_end(ap); va_end(ap);
} }
@ -199,15 +156,7 @@ void log_warning(const char *format, ...)
{ {
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
logv_warning(format, ap); logv_prefixed("Warning: ", format, ap, LogLevel::WARNING);
va_end(ap);
}
void log_warning_noprefix(const char *format, ...)
{
va_list ap;
va_start(ap, format);
logv_warning_noprefix(format, ap);
va_end(ap); va_end(ap);
} }
@ -215,41 +164,35 @@ void log_error(const char *format, ...)
{ {
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
logv_error(format, ap); logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR);
}
void log_cmd_error(const char *format, ...) if (log_error_atexit)
{ log_error_atexit();
va_list ap;
va_start(ap, format);
if (log_cmd_error_throw) { throw log_execution_error_exception();
log_last_error = vstringf(format, ap);
log_always("ERROR: %s", log_last_error.c_str());
log_flush();
throw log_cmd_error_exception();
}
logv_error(format, ap);
} }
void log_break() void log_break()
{ {
if (log_quiet_warnings)
return;
if (log_newline_count < 2) if (log_newline_count < 2)
log_always("\n"); log("\n");
if (log_newline_count < 2) if (log_newline_count < 2)
log_always("\n"); log("\n");
}
void log_nonfatal_error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR);
va_end(ap);
had_nonfatal_error = true;
} }
void log_flush() void log_flush()
{ {
for (auto f : log_files)
fflush(f);
for (auto f : log_streams) for (auto f : log_streams)
f->flush(); f.first->flush();
} }
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -26,8 +26,8 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <string> #include <string>
#include <unordered_map>
#include <vector> #include <vector>
#include "nextpnr.h" #include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -42,14 +42,22 @@ struct log_execution_error_exception
{ {
}; };
extern std::vector<FILE *> log_files; enum class LogLevel
extern std::vector<std::ostream *> log_streams; {
extern FILE *log_errfile; LOG,
INFO,
WARNING,
ERROR,
ALWAYS
};
extern std::vector<std::pair<std::ostream *, LogLevel>> log_streams;
extern log_write_type log_write_function; extern log_write_type log_write_function;
extern bool log_quiet_warnings;
extern std::string log_last_error; extern std::string log_last_error;
extern void (*log_error_atexit)(); extern void (*log_error_atexit)();
extern bool had_nonfatal_error;
extern std::unordered_map<LogLevel, int> message_count_by_level;
std::string stringf(const char *fmt, ...); std::string stringf(const char *fmt, ...);
std::string vstringf(const char *fmt, va_list ap); std::string vstringf(const char *fmt, va_list ap);
@ -59,10 +67,8 @@ void log(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_always(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2)); void log_always(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_info(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2)); void log_info(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_warning(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2)); void log_warning(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_warning_noprefix(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
NPNR_NORETURN void log_error(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2), noreturn); NPNR_NORETURN void log_error(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2), noreturn);
NPNR_NORETURN void log_cmd_error(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2), noreturn); void log_nonfatal_error(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_break(); void log_break();
void log_flush(); void log_flush();
@ -75,7 +81,6 @@ static inline void log_assert_worker(bool cond, const char *expr, const char *fi
NEXTPNR_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__) NEXTPNR_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__)
#define log_abort() log_error("Abort in %s:%d.\n", __FILE__, __LINE__) #define log_abort() log_error("Abort in %s:%d.\n", __FILE__, __LINE__)
#define log_ping() log("-- %s:%d %s --\n", __FILE__, __LINE__, __PRETTY_FUNCTION__)
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -409,12 +409,16 @@ void Context::check() const
void BaseCtx::addClock(IdString net, float freq) void BaseCtx::addClock(IdString net, float freq)
{ {
log_info("constraining clock net '%s' to %.02f MHz\n", net.c_str(this), freq);
std::unique_ptr<ClockConstraint> cc(new ClockConstraint()); std::unique_ptr<ClockConstraint> cc(new ClockConstraint());
cc->period = getCtx()->getDelayFromNS(1000 / freq); cc->period = getCtx()->getDelayFromNS(1000 / freq);
cc->high = getCtx()->getDelayFromNS(500 / freq); cc->high = getCtx()->getDelayFromNS(500 / freq);
cc->low = getCtx()->getDelayFromNS(500 / freq); cc->low = getCtx()->getDelayFromNS(500 / freq);
if (!nets.count(net)) {
log_warning("net '%s' does not exist in design, ignoring clock constraint\n", net.c_str(this));
} else {
nets.at(net)->clkconstr = std::move(cc); nets.at(net)->clkconstr = std::move(cc);
log_info("constraining clock net '%s' to %.02f MHz\n", net.c_str(this), freq);
}
} }
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -337,9 +337,10 @@ class SAPlacer
} }
} else { } else {
uint64_t score = ctx->rng64(); uint64_t score = ctx->rng64();
if (score <= best_ripup_score) { CellInfo *bound_cell = ctx->getBoundBelCell(bel);
if (score <= best_ripup_score && bound_cell->belStrength < STRENGTH_STRONG) {
best_ripup_score = score; best_ripup_score = score;
ripup_target = ctx->getBoundBelCell(bel); ripup_target = bound_cell;
ripup_bel = bel; ripup_bel = bel;
} }
} }

View File

@ -68,6 +68,19 @@ void translate_assertfail(const assertion_failure &e)
PyErr_SetString(PyExc_AssertionError, e.what()); PyErr_SetString(PyExc_AssertionError, e.what());
} }
namespace PythonConversion {
template <> struct string_converter<PortRef &>
{
inline PortRef from_str(Context *ctx, std::string name) { NPNR_ASSERT_FALSE("PortRef from_str not implemented"); }
inline std::string to_str(Context *ctx, const PortRef &pr)
{
return pr.cell->name.str(ctx) + "." + pr.port.str(ctx);
}
};
} // namespace PythonConversion
BOOST_PYTHON_MODULE(MODULE_NAME) BOOST_PYTHON_MODULE(MODULE_NAME)
{ {
register_exception_translator<assertion_failure>(&translate_assertfail); register_exception_translator<assertion_failure>(&translate_assertfail);
@ -120,7 +133,7 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
readwrite_wrapper<PortInfo &, decltype(&PortInfo::type), &PortInfo::type, pass_through<PortType>, readwrite_wrapper<PortInfo &, decltype(&PortInfo::type), &PortInfo::type, pass_through<PortType>,
pass_through<PortType>>::def_wrap(pi_cls, "type"); pass_through<PortType>>::def_wrap(pi_cls, "type");
typedef std::vector<PortRef> PortVector; typedef std::vector<PortRef> PortRefVector;
typedef std::unordered_map<WireId, PipMap> WireMap; typedef std::unordered_map<WireId, PipMap> WireMap;
auto ni_cls = class_<ContextualWrapper<NetInfo &>>("NetInfo", no_init); auto ni_cls = class_<ContextualWrapper<NetInfo &>>("NetInfo", no_init);
@ -128,7 +141,7 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
conv_from_str<IdString>>::def_wrap(ni_cls, "name"); conv_from_str<IdString>>::def_wrap(ni_cls, "name");
readwrite_wrapper<NetInfo &, decltype(&NetInfo::driver), &NetInfo::driver, wrap_context<PortRef &>, readwrite_wrapper<NetInfo &, decltype(&NetInfo::driver), &NetInfo::driver, wrap_context<PortRef &>,
unwrap_context<PortRef &>>::def_wrap(ni_cls, "driver"); unwrap_context<PortRef &>>::def_wrap(ni_cls, "driver");
readonly_wrapper<NetInfo &, decltype(&NetInfo::users), &NetInfo::users, wrap_context<PortVector &>>::def_wrap( readonly_wrapper<NetInfo &, decltype(&NetInfo::users), &NetInfo::users, wrap_context<PortRefVector &>>::def_wrap(
ni_cls, "users"); ni_cls, "users");
readonly_wrapper<NetInfo &, decltype(&NetInfo::wires), &NetInfo::wires, wrap_context<WireMap &>>::def_wrap(ni_cls, readonly_wrapper<NetInfo &, decltype(&NetInfo::wires), &NetInfo::wires, wrap_context<WireMap &>>::def_wrap(ni_cls,
"wires"); "wires");
@ -141,12 +154,21 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
readwrite_wrapper<PortRef &, decltype(&PortRef::budget), &PortRef::budget, pass_through<delay_t>, readwrite_wrapper<PortRef &, decltype(&PortRef::budget), &PortRef::budget, pass_through<delay_t>,
pass_through<delay_t>>::def_wrap(pr_cls, "budget"); pass_through<delay_t>>::def_wrap(pr_cls, "budget");
auto pm_cls = class_<ContextualWrapper<PipMap &>>("PipMap", no_init);
readwrite_wrapper<PipMap &, decltype(&PipMap::pip), &PipMap::pip, conv_to_str<PipId>,
conv_from_str<PipId>>::def_wrap(pm_cls, "pip");
readwrite_wrapper<PipMap &, decltype(&PipMap::strength), &PipMap::strength, pass_through<PlaceStrength>,
pass_through<PlaceStrength>>::def_wrap(pm_cls, "strength");
def("parse_json", parse_json_shim); def("parse_json", parse_json_shim);
def("load_design", load_design_shim, return_value_policy<manage_new_object>()); def("load_design", load_design_shim, return_value_policy<manage_new_object>());
WRAP_MAP(AttrMap, pass_through<std::string>, "AttrMap"); WRAP_MAP(AttrMap, pass_through<std::string>, "AttrMap");
WRAP_MAP(PortMap, wrap_context<PortInfo &>, "PortMap"); WRAP_MAP(PortMap, wrap_context<PortInfo &>, "PortMap");
WRAP_MAP(PinMap, conv_to_str<IdString>, "PinMap"); WRAP_MAP(PinMap, conv_to_str<IdString>, "PinMap");
WRAP_MAP(WireMap, wrap_context<PipMap &>, "WireMap");
WRAP_VECTOR(PortRefVector, wrap_context<PortRef &>);
arch_wrap_python(); arch_wrap_python();
} }

View File

@ -118,10 +118,67 @@ struct range_wrapper
#define WRAP_RANGE(t, conv) \ #define WRAP_RANGE(t, conv) \
range_wrapper<t##Range, return_value_policy<return_by_value>, conv>().wrap(#t "Range", #t "Iterator") range_wrapper<t##Range, return_value_policy<return_by_value>, conv>().wrap(#t "Range", #t "Iterator")
/*
A wrapper for a vector or similar structure. With support for conversion
*/
template <typename T, typename P = return_value_policy<return_by_value>,
typename value_conv = PythonConversion::pass_through<T>>
struct vector_wrapper
{
typedef decltype(std::declval<T>().begin()) iterator_t;
typedef decltype(*(std::declval<iterator_t>())) value_t;
typedef typename PythonConversion::ContextualWrapper<T &> wrapped_vector;
typedef typename PythonConversion::ContextualWrapper<std::pair<iterator_t, iterator_t>> wrapped_pair;
using return_t = typename value_conv::ret_type;
static wrapped_pair iter(wrapped_vector &range)
{
return wrapped_pair(range.ctx, std::make_pair(range.base.begin(), range.base.end()));
}
static std::string repr(wrapped_vector &range)
{
PythonConversion::string_converter<value_t> conv;
bool first = true;
std::stringstream ss;
ss << "[";
for (const auto &item : range.base) {
if (!first)
ss << ", ";
ss << "'" << conv.to_str(range.ctx, item) << "'";
first = false;
}
ss << "]";
return ss.str();
}
static int len(wrapped_vector &range) { return range.base.size(); }
static return_t getitem(wrapped_vector &range, int i)
{
return value_conv()(range.ctx, boost::ref(range.base.at(i)));
}
static void wrap(const char *range_name, const char *iter_name)
{
class_<wrapped_vector>(range_name, no_init)
.def("__iter__", iter)
.def("__repr__", repr)
.def("__len__", len)
.def("__getitem__", getitem);
iterator_wrapper<iterator_t, P, value_conv>().wrap(iter_name);
}
typedef iterator_wrapper<iterator_t, P, value_conv> iter_wrap;
};
#define WRAP_VECTOR(t, conv) vector_wrapper<t, return_value_policy<return_by_value>, conv>().wrap(#t, #t "Iterator")
/* /*
Wrapper for a pair, allows accessing either using C++-style members (.first and Wrapper for a pair, allows accessing either using C++-style members (.first and
.second) or as a Python iterable and indexable object .second) or as a Python iterable and indexable object
*/ */
template <typename T1, typename T2> struct pair_wrapper template <typename T1, typename T2> struct pair_wrapper
{ {
typedef std::pair<T1, T2> T; typedef std::pair<T1, T2> T;

View File

@ -812,7 +812,8 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
#endif #endif
log_info("Checksum: 0x%08x\n", ctx->checksum()); log_info("Checksum: 0x%08x\n", ctx->checksum());
timing_analysis(ctx, true /* slack_histogram */, true /* print_fmax */, true /* print_path */); timing_analysis(ctx, true /* slack_histogram */, true /* print_fmax */, true /* print_path */,
true /* warn_on_failure */);
ctx->unlock(); ctx->unlock();
return true; return true;
@ -870,7 +871,12 @@ bool Context::checkRoutedDesign() const
} }
auto src_wire = ctx->getNetinfoSourceWire(net_info); auto src_wire = ctx->getNetinfoSourceWire(net_info);
log_assert(src_wire != WireId()); if (src_wire == WireId()) {
log_assert(net_info->driver.cell == nullptr);
if (ctx->debug)
log(" undriven and unrouted\n");
continue;
}
if (net_info->wires.count(src_wire) == 0) { if (net_info->wires.count(src_wire) == 0) {
if (ctx->debug) if (ctx->debug)

View File

@ -485,11 +485,11 @@ void assign_budget(Context *ctx, bool quiet)
for (auto &user : net.second->users) { for (auto &user : net.second->users) {
// Post-update check // Post-update check
if (!ctx->auto_freq && user.budget < 0) if (!ctx->auto_freq && user.budget < 0)
log_warning("port %s.%s, connected to net '%s', has negative " log_info("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),
ctx->getDelayNS(user.budget)); ctx->getDelayNS(user.budget));
else if (ctx->verbose) else if (ctx->debug)
log_info("port %s.%s, connected to net '%s', has " log_info("port %s.%s, connected to net '%s', has "
"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),
@ -513,7 +513,7 @@ void assign_budget(Context *ctx, bool quiet)
log_info("Checksum: 0x%08x\n", ctx->checksum()); log_info("Checksum: 0x%08x\n", ctx->checksum());
} }
void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path) void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path, bool warn_on_failure)
{ {
auto format_event = [ctx](const ClockEvent &e, int field_width = 0) { auto format_event = [ctx](const ClockEvent &e, int field_width = 0) {
std::string value; std::string value;
@ -593,7 +593,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
if (print_path) { if (print_path) {
auto print_path_report = [ctx](ClockPair &clocks, PortRefVector &crit_path) { auto print_path_report = [ctx](ClockPair &clocks, PortRefVector &crit_path) {
delay_t total = 0; delay_t total = 0, logic_total = 0, route_total = 0;
auto &front = crit_path.front(); auto &front = crit_path.front();
auto &front_port = front->cell->ports.at(front->port); auto &front_port = front->cell->ports.at(front->port);
auto &front_driver = front_port.net->driver; auto &front_driver = front_port.net->driver;
@ -608,6 +608,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
if (clknet != nullptr && clknet->name == clocks.start.clock && if (clknet != nullptr && clknet->name == clocks.start.clock &&
clockInfo.edge == clocks.start.edge) { clockInfo.edge == clocks.start.edge) {
last_port = clockInfo.clock_port; last_port = clockInfo.clock_port;
total += clockInfo.clockToQ.maxDelay();
logic_total += clockInfo.clockToQ.maxDelay();
break;
} }
} }
} }
@ -627,10 +630,12 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay); ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
} }
total += comb_delay.maxDelay(); total += comb_delay.maxDelay();
logic_total += comb_delay.maxDelay();
log_info("%4.1f %4.1f Source %s.%s\n", ctx->getDelayNS(comb_delay.maxDelay()), ctx->getDelayNS(total), log_info("%4.1f %4.1f Source %s.%s\n", ctx->getDelayNS(comb_delay.maxDelay()), ctx->getDelayNS(total),
driver_cell->name.c_str(ctx), driver.port.c_str(ctx)); driver_cell->name.c_str(ctx), driver.port.c_str(ctx));
auto net_delay = ctx->getNetinfoRouteDelay(net, *sink); auto net_delay = ctx->getNetinfoRouteDelay(net, *sink);
total += net_delay; total += net_delay;
route_total += net_delay;
auto driver_loc = ctx->getBelLocation(driver_cell->bel); auto driver_loc = ctx->getBelLocation(driver_cell->bel);
auto sink_loc = ctx->getBelLocation(sink_cell->bel); auto sink_loc = ctx->getBelLocation(sink_cell->bel);
log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(net_delay), log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(net_delay),
@ -658,6 +663,17 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
} }
last_port = sink->port; last_port = sink->port;
} }
int clockCount = 0;
auto sinkClass = ctx->getPortTimingClass(crit_path.back()->cell, crit_path.back()->port, clockCount);
if (sinkClass == TMG_REGISTER_INPUT && clockCount > 0) {
auto sinkClockInfo = ctx->getPortClockingInfo(crit_path.back()->cell, crit_path.back()->port, 0);
delay_t setup = sinkClockInfo.setup.maxDelay();
total += setup;
logic_total += setup;
log_info("%4.1f %4.1f Setup %s.%s\n", ctx->getDelayNS(setup), ctx->getDelayNS(total),
crit_path.back()->cell->name.c_str(ctx), crit_path.back()->port.c_str(ctx));
}
log_info("%.1f ns logic, %.1f ns routing\n", ctx->getDelayNS(logic_total), ctx->getDelayNS(route_total));
}; };
for (auto &clock : clock_reports) { for (auto &clock : clock_reports) {
@ -689,15 +705,17 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
for (auto &clock : clock_reports) { for (auto &clock : clock_reports) {
const auto &clock_name = clock.first.str(ctx); const auto &clock_name = clock.first.str(ctx);
const int width = max_width - clock_name.size(); const int width = max_width - clock_name.size();
if (ctx->nets.at(clock.first)->clkconstr) { float target = ctx->target_freq / 1e6;
float target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay()); if (ctx->nets.at(clock.first)->clkconstr)
target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay());
bool passed = target < clock_fmax[clock.first];
if (!warn_on_failure || passed)
log_info("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "", log_info("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
clock_name.c_str(), clock_fmax[clock.first], clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);
(target < clock_fmax[clock.first]) ? "PASS" : "FAIL", target); else
} else { log_nonfatal_error("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
log_info("Max frequency for clock %*s'%s': %.02f MHz\n", width, "", clock_name.c_str(), clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);
clock_fmax[clock.first]);
}
} }
for (auto &eclock : empty_clocks) { for (auto &eclock : empty_clocks) {
if (eclock != ctx->id("$async$")) if (eclock != ctx->id("$async$"))

View File

@ -29,7 +29,8 @@ void assign_budget(Context *ctx, bool quiet = false);
// Perform timing analysis and print out the fmax, and optionally the // Perform timing analysis and print out the fmax, and optionally the
// critical path // critical path
void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_fmax = true, bool print_path = false); void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_fmax = true, bool print_path = false,
bool warn_on_failure = false);
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -2,7 +2,7 @@
There are three types of constraints available for end users of nextpnr. There are three types of constraints available for end users of nextpnr.
## Architecture-specific IO Cconstraints ## Architecture-specific IO Constraints
Architectures may provide support for their native (or any other) IO constraint format. Architectures may provide support for their native (or any other) IO constraint format.
The iCE40 architecture supports PCF constraints thus: The iCE40 architecture supports PCF constraints thus:

View File

@ -96,7 +96,7 @@ Arch::Arch(ArchArgs args) : args(args)
break; break;
} }
} }
speed_grade = &(chip_info->speed_grades[args.speed]);
if (!package_info) if (!package_info)
log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str()); log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str());
@ -400,7 +400,7 @@ BelId Arch::getBelByLocation(Loc loc) const
delay_t Arch::estimateDelay(WireId src, WireId dst) const delay_t Arch::estimateDelay(WireId src, WireId dst) const
{ {
return 170 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y)); return (240 - 20 * args.speed) * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
} }
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
@ -409,7 +409,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
auto driver_loc = getBelLocation(driver.cell->bel); auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel); auto sink_loc = getBelLocation(sink.cell->bel);
return 170 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y)); return (240 - 20 * args.speed) * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y));
} }
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
@ -422,7 +422,34 @@ bool Arch::route()
{ {
route_ecp5_globals(getCtx()); route_ecp5_globals(getCtx());
assign_budget(getCtx(), true); assign_budget(getCtx(), true);
return router1(getCtx(), Router1Cfg(getCtx()));
bool result = router1(getCtx(), Router1Cfg(getCtx()));
#if 0
std::vector<std::pair<WireId, int>> fanout_vector;
std::copy(wire_fanout.begin(), wire_fanout.end(), std::back_inserter(fanout_vector));
std::sort(fanout_vector.begin(), fanout_vector.end(), [](const std::pair<WireId, int> &a, const std::pair<WireId, int> &b) {
return a.second > b.second;
});
for (size_t i = 0; i < std::min(size_t(20), fanout_vector.size()); i++)
log_info(" fanout %s = %d\n", getWireName(fanout_vector[i].first).c_str(this), fanout_vector[i].second);
log_break();
PipId slowest_pip;
delay_t slowest_pipdelay = 0;
for (auto pip : pip_to_net) {
if (pip.second) {
delay_t dly = getPipDelay(pip.first).maxDelay();
if (dly > slowest_pipdelay) {
slowest_pip = pip.first;
slowest_pipdelay = dly;
}
}
}
log_info(" slowest pip %s = %.02f ns\n", getPipName(slowest_pip).c_str(this), getDelayNS(slowest_pipdelay));
log_info(" fanout %d\n", wire_fanout[getPipSrcWire(slowest_pip)]);
log_info(" base %d adder %d\n", speed_grade->pip_classes[locInfo(slowest_pip)->pip_data[slowest_pip.index].timing_class].max_base_delay,
speed_grade->pip_classes[locInfo(slowest_pip)->pip_data[slowest_pip.index].timing_class].max_fanout_adder);
#endif
return result;
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -482,94 +509,75 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; };
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
bool Arch::getDelayFromTimingDatabase(IdString tctype, IdString from, IdString to, DelayInfo &delay) const
{
for (int i = 0; i < speed_grade->num_cell_timings; i++) {
const auto &tc = speed_grade->cell_timings[i];
if (tc.cell_type == tctype.index) {
for (int j = 0; j < tc.num_prop_delays; j++) {
const auto &dly = tc.prop_delays[j];
if (dly.from_port == from.index && dly.to_port == to.index) {
delay.max_delay = dly.max_delay;
delay.min_delay = dly.min_delay;
return true;
}
}
return false;
}
}
NPNR_ASSERT_FALSE("failed to find timing cell in db");
}
void Arch::getSetupHoldFromTimingDatabase(IdString tctype, IdString clock, IdString port, DelayInfo &setup,
DelayInfo &hold) const
{
for (int i = 0; i < speed_grade->num_cell_timings; i++) {
const auto &tc = speed_grade->cell_timings[i];
if (tc.cell_type == tctype.index) {
for (int j = 0; j < tc.num_setup_holds; j++) {
const auto &sh = tc.setup_holds[j];
if (sh.clock_port == clock.index && sh.sig_port == port.index) {
setup.max_delay = sh.max_setup;
setup.min_delay = sh.min_setup;
hold.max_delay = sh.max_hold;
hold.min_delay = sh.min_hold;
return;
}
}
}
}
NPNR_ASSERT_FALSE("failed to find timing cell in db");
}
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{ {
// Data for -8 grade // Data for -8 grade
if (cell->type == id_TRELLIS_SLICE) { if (cell->type == id_TRELLIS_SLICE) {
bool has_carry = str_or_default(cell->params, id("MODE"), "LOGIC") == "CCU2"; bool has_carry = str_or_default(cell->params, id("MODE"), "LOGIC") == "CCU2";
if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0) { if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0 || fromPort == id_A1 ||
if (toPort == id_F0) { fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1 || fromPort == id_M0 || fromPort == id_M1 ||
delay.delay = 180; fromPort == id_FXA || fromPort == id_FXB || fromPort == id_FCI) {
return true; return getDelayFromTimingDatabase(has_carry ? id_SCCU2C : id_SLOGICB, fromPort, toPort, delay);
} else if (has_carry && toPort == id_F1) {
delay.delay = 500;
return true;
} else if (has_carry && toPort == id_FCO) {
delay.delay = 355;
return true;
} else if (toPort == id_OFX0) {
delay.delay = 306;
return true;
}
} }
if (fromPort == id_A1 || fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1) {
if (toPort == id_F1) {
delay.delay = 180;
return true;
} else if (has_carry && toPort == id_FCO) {
delay.delay = 355;
return true;
} else if (toPort == id_OFX0) {
delay.delay = 306;
return true;
}
}
if (has_carry && fromPort == id_FCI) {
if (toPort == id_F0) {
delay.delay = 328;
return true;
} else if (toPort == id_F1) {
delay.delay = 349;
return true;
} else if (toPort == id_FCO) {
delay.delay = 56;
return true;
}
}
if (fromPort == id_CLK && (toPort == id_Q0 || toPort == id_Q1)) {
delay.delay = 395;
return true;
}
if (fromPort == id_M0 && toPort == id_OFX0) {
delay.delay = 193;
return true;
}
#if 0 // FIXME
if (fromPort == id_WCK && (toPort == id_F0 || toPort == id_F1)) {
delay.delay = 717;
return true;
}
#endif
if ((fromPort == id_A0 && toPort == id_WADO3) || (fromPort == id_A1 && toPort == id_WDO1) || if ((fromPort == id_A0 && toPort == id_WADO3) || (fromPort == id_A1 && toPort == id_WDO1) ||
(fromPort == id_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) || (fromPort == id_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) ||
(fromPort == id_C0 && toPort == id_WADO2) || (fromPort == id_C1 && toPort == id_WDO0) || (fromPort == id_C0 && toPort == id_WADO2) || (fromPort == id_C1 && toPort == id_WDO0) ||
(fromPort == id_D0 && toPort == id_WADO0) || (fromPort == id_D1 && toPort == id_WDO2)) { (fromPort == id_D0 && toPort == id_WADO0) || (fromPort == id_D1 && toPort == id_WDO2)) {
delay.delay = 0; delay.min_delay = 0;
delay.max_delay = 0;
return true; return true;
} }
return false; return false;
} else if (cell->type == id_DCCA) { } else if (cell->type == id_DCCA) {
if (fromPort == id_CLKI && toPort == id_CLKO) { if (fromPort == id_CLKI && toPort == id_CLKO) {
delay.delay = 0; delay.min_delay = 0;
delay.max_delay = 0;
return true; return true;
} }
return false; return false;
} else if (cell->type == id_DP16KD) { } else if (cell->type == id_DP16KD) {
if (fromPort == id_CLKA) {
if (toPort.str(this).substr(0, 3) == "DOA") {
delay.delay = 4260;
return true;
}
} else if (fromPort == id_CLKB) {
if (toPort.str(this).substr(0, 3) == "DOB") {
delay.delay = 4280;
return true;
}
}
return false; return false;
} else { } else {
return false; return false;
@ -669,9 +677,9 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
{ {
TimingClockingInfo info; TimingClockingInfo info;
info.setup.delay = 0; info.setup = getDelayFromNS(0);
info.hold.delay = 0; info.hold = getDelayFromNS(0);
info.clockToQ.delay = 0; info.clockToQ = getDelayFromNS(0);
if (cell->type == id_TRELLIS_SLICE) { if (cell->type == id_TRELLIS_SLICE) {
int sd0 = int_or_default(cell->params, id("REG0_SD"), 0), sd1 = int_or_default(cell->params, id("REG1_SD"), 0); int sd0 = int_or_default(cell->params, id("REG0_SD"), 0), sd1 = int_or_default(cell->params, id("REG1_SD"), 0);
@ -679,18 +687,18 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
port == id_WAD3 || port == id_WRE) { port == id_WAD3 || port == id_WRE) {
info.edge = RISING_EDGE; info.edge = RISING_EDGE;
info.clock_port = id_WCK; info.clock_port = id_WCK;
info.setup.delay = 100; getSetupHoldFromTimingDatabase(id_SDPRAME, id_WCK, port, info.setup, info.hold);
info.hold.delay = 0;
} else if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 1 && port == id_M0) || } else if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 1 && port == id_M0) ||
(sd1 == 1 && port == id_M1)) { (sd1 == 1 && port == id_M1)) {
info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE; info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE;
info.clock_port = id_CLK; info.clock_port = id_CLK;
info.setup.delay = 100; getSetupHoldFromTimingDatabase(id_SLOGICB, id_CLK, port, info.setup, info.hold);
info.hold.delay = 0;
} else { } else {
info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE; info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE;
info.clock_port = id_CLK; info.clock_port = id_CLK;
info.clockToQ.delay = 395; bool is_path = getDelayFromTimingDatabase(id_SLOGICB, id_CLK, port, info.clockToQ);
NPNR_ASSERT(is_path);
} }
} else if (cell->type == id_DP16KD) { } else if (cell->type == id_DP16KD) {
std::string port_name = port.str(this); std::string port_name = port.str(this);
@ -711,10 +719,12 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
? FALLING_EDGE ? FALLING_EDGE
: RISING_EDGE; : RISING_EDGE;
if (cell->ports.at(port).type == PORT_OUT) { if (cell->ports.at(port).type == PORT_OUT) {
info.clockToQ.delay = 4280; bool is_path = getDelayFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, info.clock_port, port,
info.clockToQ);
NPNR_ASSERT(is_path);
} else { } else {
info.setup.delay = 100; getSetupHoldFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, info.clock_port, port, info.setup,
info.hold.delay = 0; info.hold);
} }
} else if (cell->type == id_DCUA) { } else if (cell->type == id_DCUA) {
std::string prefix = port.str(this).substr(0, 9); std::string prefix = port.str(this).substr(0, 9);
@ -728,10 +738,10 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
else if (prefix == "CH1_FF_RX") else if (prefix == "CH1_FF_RX")
info.clock_port = id_CH1_FF_RXI_CLK; info.clock_port = id_CH1_FF_RXI_CLK;
if (cell->ports.at(port).type == PORT_OUT) { if (cell->ports.at(port).type == PORT_OUT) {
info.clockToQ.delay = 660; info.clockToQ = getDelayFromNS(0.7);
} else { } else {
info.setup.delay = 1000; info.setup = getDelayFromNS(1);
info.hold.delay = 0; info.hold = getDelayFromNS(0);
} }
} }
return info; return info;

View File

@ -71,7 +71,7 @@ NPNR_PACKED_STRUCT(struct BelPortPOD {
NPNR_PACKED_STRUCT(struct PipInfoPOD { NPNR_PACKED_STRUCT(struct PipInfoPOD {
LocationPOD rel_src_loc, rel_dst_loc; LocationPOD rel_src_loc, rel_dst_loc;
int32_t src_idx, dst_idx; int32_t src_idx, dst_idx;
int32_t delay; int32_t timing_class;
int16_t tile_type; int16_t tile_type;
int8_t pip_type; int8_t pip_type;
int8_t padding_0; int8_t padding_0;
@ -151,6 +151,44 @@ NPNR_PACKED_STRUCT(struct GlobalInfoPOD {
int16_t spine_col; int16_t spine_col;
}); });
NPNR_PACKED_STRUCT(struct CellPropDelayPOD {
int32_t from_port;
int32_t to_port;
int32_t min_delay;
int32_t max_delay;
});
NPNR_PACKED_STRUCT(struct CellSetupHoldPOD {
int32_t sig_port;
int32_t clock_port;
int32_t min_setup;
int32_t max_setup;
int32_t min_hold;
int32_t max_hold;
});
NPNR_PACKED_STRUCT(struct CellTimingPOD {
int32_t cell_type;
int32_t num_prop_delays;
int32_t num_setup_holds;
RelPtr<CellPropDelayPOD> prop_delays;
RelPtr<CellSetupHoldPOD> setup_holds;
});
NPNR_PACKED_STRUCT(struct PipDelayPOD {
int32_t min_base_delay;
int32_t max_base_delay;
int32_t min_fanout_adder;
int32_t max_fanout_adder;
});
NPNR_PACKED_STRUCT(struct SpeedGradePOD {
int32_t num_cell_timings;
int32_t num_pip_classes;
RelPtr<CellTimingPOD> cell_timings;
RelPtr<PipDelayPOD> pip_classes;
});
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;
@ -163,6 +201,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD {
RelPtr<PackageInfoPOD> package_info; RelPtr<PackageInfoPOD> package_info;
RelPtr<PIOInfoPOD> pio_info; RelPtr<PIOInfoPOD> pio_info;
RelPtr<TileInfoPOD> tile_info; RelPtr<TileInfoPOD> tile_info;
RelPtr<SpeedGradePOD> speed_grades;
}); });
#if defined(_MSC_VER) #if defined(_MSC_VER)
@ -400,13 +439,20 @@ struct ArchArgs
LFE5UM5G_85F, LFE5UM5G_85F,
} type = NONE; } type = NONE;
std::string package; std::string package;
int speed = 6; enum SpeedGrade
{
SPEED_6 = 0,
SPEED_7,
SPEED_8,
SPEED_8_5G,
} speed = SPEED_6;
}; };
struct Arch : BaseCtx struct Arch : BaseCtx
{ {
const ChipInfoPOD *chip_info; const ChipInfoPOD *chip_info;
const PackageInfoPOD *package_info; const PackageInfoPOD *package_info;
const SpeedGradePOD *speed_grade;
mutable std::unordered_map<IdString, BelId> bel_by_name; mutable std::unordered_map<IdString, BelId> bel_by_name;
mutable std::unordered_map<IdString, WireId> wire_by_name; mutable std::unordered_map<IdString, WireId> wire_by_name;
@ -415,6 +461,7 @@ struct Arch : BaseCtx
std::vector<CellInfo *> bel_to_cell; std::vector<CellInfo *> bel_to_cell;
std::unordered_map<WireId, NetInfo *> wire_to_net; std::unordered_map<WireId, NetInfo *> wire_to_net;
std::unordered_map<PipId, NetInfo *> pip_to_net; std::unordered_map<PipId, NetInfo *> pip_to_net;
std::unordered_map<WireId, int> wire_fanout;
ArchArgs args; ArchArgs args;
Arch(ArchArgs args); Arch(ArchArgs args);
@ -597,6 +644,7 @@ struct Arch : BaseCtx
auto pip = it->second.pip; auto pip = it->second.pip;
if (pip != PipId()) { if (pip != PipId()) {
wire_fanout[getPipSrcWire(pip)]--;
pip_to_net[pip] = nullptr; pip_to_net[pip] = nullptr;
} }
@ -633,7 +681,8 @@ struct Arch : BaseCtx
DelayInfo getWireDelay(WireId wire) const DelayInfo getWireDelay(WireId wire) const
{ {
DelayInfo delay; DelayInfo delay;
delay.delay = 0; delay.min_delay = 0;
delay.max_delay = 0;
return delay; return delay;
} }
@ -686,6 +735,7 @@ struct Arch : BaseCtx
NPNR_ASSERT(pip_to_net[pip] == nullptr); NPNR_ASSERT(pip_to_net[pip] == nullptr);
pip_to_net[pip] = net; pip_to_net[pip] = net;
wire_fanout[getPipSrcWire(pip)]++;
WireId dst; WireId dst;
dst.index = locInfo(pip)->pip_data[pip.index].dst_idx; dst.index = locInfo(pip)->pip_data[pip.index].dst_idx;
@ -700,6 +750,7 @@ struct Arch : BaseCtx
{ {
NPNR_ASSERT(pip != PipId()); NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip] != nullptr); NPNR_ASSERT(pip_to_net[pip] != nullptr);
wire_fanout[getPipSrcWire(pip)]--;
WireId dst; WireId dst;
dst.index = locInfo(pip)->pip_data[pip.index].dst_idx; dst.index = locInfo(pip)->pip_data[pip.index].dst_idx;
@ -772,7 +823,17 @@ struct Arch : BaseCtx
{ {
DelayInfo delay; DelayInfo delay;
NPNR_ASSERT(pip != PipId()); NPNR_ASSERT(pip != PipId());
delay.delay = locInfo(pip)->pip_data[pip.index].delay; int fanout = 0;
auto fnd_fanout = wire_fanout.find(getPipSrcWire(pip));
if (fnd_fanout != wire_fanout.end())
fanout = fnd_fanout->second;
NPNR_ASSERT(locInfo(pip)->pip_data[pip.index].timing_class < speed_grade->num_pip_classes);
delay.min_delay =
speed_grade->pip_classes[locInfo(pip)->pip_data[pip.index].timing_class].min_base_delay +
fanout * speed_grade->pip_classes[locInfo(pip)->pip_data[pip.index].timing_class].min_fanout_adder;
delay.max_delay =
speed_grade->pip_classes[locInfo(pip)->pip_data[pip.index].timing_class].max_base_delay +
fanout * speed_grade->pip_classes[locInfo(pip)->pip_data[pip.index].timing_class].max_fanout_adder;
return delay; return delay;
} }
@ -862,7 +923,8 @@ struct Arch : BaseCtx
DelayInfo getDelayFromNS(float ns) const DelayInfo getDelayFromNS(float ns) const
{ {
DelayInfo del; DelayInfo del;
del.delay = delay_t(ns * 1000); del.min_delay = delay_t(ns * 1000);
del.max_delay = delay_t(ns * 1000);
return del; return del;
} }
uint32_t getDelayChecksum(delay_t v) const { return v; } uint32_t getDelayChecksum(delay_t v) const { return v; }
@ -895,6 +957,10 @@ struct Arch : BaseCtx
// Return true if a port is a net // Return true if a port is a net
bool isGlobalNet(const NetInfo *net) const; bool isGlobalNet(const NetInfo *net) const;
bool getDelayFromTimingDatabase(IdString tctype, IdString from, IdString to, DelayInfo &delay) const;
void getSetupHoldFromTimingDatabase(IdString tctype, IdString clock, IdString port, DelayInfo &setup,
DelayInfo &hold) const;
// ------------------------------------------------- // -------------------------------------------------
// Placement validity checks // Placement validity checks
bool isValidBelForCell(CellInfo *cell, BelId bel) const; bool isValidBelForCell(CellInfo *cell, BelId bel) const;

View File

@ -75,6 +75,8 @@ bool Arch::isBelLocationValid(BelId bel) const
bel_cells.push_back(cell_other); bel_cells.push_back(cell_other);
} }
} }
if (getBoundBelCell(bel) != nullptr && getBoundBelCell(bel)->sliceInfo.has_l6mux && ((bel_loc.z % 2) == 1))
return false;
return slicesCompatible(bel_cells); return slicesCompatible(bel_cells);
} else { } else {
CellInfo *cell = getBoundBelCell(bel); CellInfo *cell = getBoundBelCell(bel);
@ -92,6 +94,10 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
std::vector<const CellInfo *> bel_cells; std::vector<const CellInfo *> bel_cells;
Loc bel_loc = getBelLocation(bel); Loc bel_loc = getBelLocation(bel);
if (cell->sliceInfo.has_l6mux && ((bel_loc.z % 2) == 1))
return false;
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) { for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
CellInfo *cell_other = getBoundBelCell(bel_other); CellInfo *cell_other = getBoundBelCell(bel_other);
if (cell_other != nullptr && bel_other != bel) { if (cell_other != nullptr && bel_other != bel) {

View File

@ -44,14 +44,36 @@ template <> struct string_converter<WireId>
{ {
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); } WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); } std::string to_str(Context *ctx, WireId id)
{
if (id == WireId())
throw bad_wrap();
return ctx->getWireName(id).str(ctx);
}
};
template <> struct string_converter<const WireId>
{
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id)
{
if (id == WireId())
throw bad_wrap();
return ctx->getWireName(id).str(ctx);
}
}; };
template <> struct string_converter<PipId> template <> struct string_converter<PipId>
{ {
PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); } PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
std::string to_str(Context *ctx, PipId id) { return ctx->getPipName(id).str(ctx); } std::string to_str(Context *ctx, PipId id)
{
if (id == PipId())
throw bad_wrap();
return ctx->getPipName(id).str(ctx);
}
}; };
} // namespace PythonConversion } // namespace PythonConversion

View File

@ -30,21 +30,22 @@ typedef int delay_t;
struct DelayInfo struct DelayInfo
{ {
delay_t delay = 0; delay_t min_delay = 0, max_delay = 0;
delay_t minRaiseDelay() const { return delay; } delay_t minRaiseDelay() const { return min_delay; }
delay_t maxRaiseDelay() const { return delay; } delay_t maxRaiseDelay() const { return max_delay; }
delay_t minFallDelay() const { return delay; } delay_t minFallDelay() const { return min_delay; }
delay_t maxFallDelay() const { return delay; } delay_t maxFallDelay() const { return max_delay; }
delay_t minDelay() const { return delay; } delay_t minDelay() const { return min_delay; }
delay_t maxDelay() const { return delay; } delay_t maxDelay() const { return max_delay; }
DelayInfo operator+(const DelayInfo &other) const DelayInfo operator+(const DelayInfo &other) const
{ {
DelayInfo ret; DelayInfo ret;
ret.delay = this->delay + other.delay; ret.min_delay = this->min_delay + other.min_delay;
ret.max_delay = this->max_delay + other.max_delay;
return ret; return ret;
} }
}; };
@ -157,6 +158,7 @@ struct ArchCellInfo
struct struct
{ {
bool using_dff; bool using_dff;
bool has_l6mux;
IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode; IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode;
} sliceInfo; } sliceInfo;
}; };

View File

@ -407,8 +407,8 @@ std::vector<std::string> get_pll_tiles(Context *ctx, BelId bel)
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x, "PLL0_LR")); tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x, "PLL0_LR"));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x - 1, pll1_lr)); tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x - 1, pll1_lr));
} else if (name == "EHXPLL_UR") { } else if (name == "EHXPLL_UR") {
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x - 1, "PLL0_UR")); tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "PLL0_UR"));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x - 1, "PLL1_UR")); tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x + 1, "PLL1_UR"));
} else { } else {
NPNR_ASSERT_FALSE_STR("bad PLL loc " + name); NPNR_ASSERT_FALSE_STR("bad PLL loc " + name);
} }

View File

@ -36,7 +36,7 @@ inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type
inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("CCU2C"); } inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("CCU2C"); }
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_LC"); } inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_SLICE"); }
inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_IO"); } inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_IO"); }

View File

@ -811,7 +811,6 @@ X(INTLOCK)
X(REFCLK) X(REFCLK)
X(CLKINTFB) X(CLKINTFB)
X(EXTREFB) X(EXTREFB)
X(REFCLKP) X(REFCLKP)
X(REFCLKN) X(REFCLKN)
@ -1117,3 +1116,29 @@ X(SEL1)
X(SEL0) X(SEL0)
X(CDIV1) X(CDIV1)
X(CDIVX) X(CDIVX)
X(DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG)
X(DP16KD_REGMODE_A_NOREG_REGMODE_B_OUTREG)
X(DP16KD_REGMODE_A_OUTREG_REGMODE_B_NOREG)
X(DP16KD_REGMODE_A_OUTREG_REGMODE_B_OUTREG)
X(DP16KD_WRITEMODE_A_NORMAL_WRITEMODE_B_NORMAL)
X(DP16KD_WRITEMODE_A_NORMAL_WRITEMODE_B_READBEFOREWRITE)
X(DP16KD_WRITEMODE_A_NORMAL_WRITEMODE_B_WRITETHROUGH)
X(PIO_IOTYPE_LVCMOS12)
X(PIO_IOTYPE_LVCMOS15)
X(PIO_IOTYPE_LVCMOS18)
X(PIO_IOTYPE_LVCMOS25)
X(PIO_IOTYPE_LVCMOS33)
X(PIO_IOTYPE_LVDS)
X(PIO_IOTYPE_SSTL15_I)
X(PIO_IOTYPE_SSTL15_II)
X(PIO_IOTYPE_SSTL18_I)
X(PIO_IOTYPE_SSTL18_II)
X(SCCU2C)
X(SDPRAME)
X(SLOGICB)
X(SRAMWB)
X(PAD)
X(PADDI)
X(PADDO)
X(PADDT)

View File

@ -18,11 +18,13 @@ file(MAKE_DIRECTORY ecp5/chipdbs/)
add_library(ecp5_chipdb OBJECT ecp5/chipdbs/) add_library(ecp5_chipdb OBJECT ecp5/chipdbs/)
target_compile_definitions(ecp5_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family}) target_compile_definitions(ecp5_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family})
target_include_directories(ecp5_chipdb PRIVATE ${family}/) target_include_directories(ecp5_chipdb PRIVATE ${family}/)
if (WIN32) if (WIN32)
set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=\"${TRELLIS_ROOT}/libtrellis\;${TRELLIS_ROOT}/util/common\"") set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=\"${TRELLIS_ROOT}/libtrellis\;${TRELLIS_ROOT}/util/common\;${TRELLIS_ROOT}/timing/util\"")
else() else()
set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=${TRELLIS_ROOT}/libtrellis:${TRELLIS_ROOT}/util/common") set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=${TRELLIS_ROOT}/libtrellis:${TRELLIS_ROOT}/util/common:${TRELLIS_ROOT}/timing/util")
endif() endif()
if (MSVC) if (MSVC)
target_sources(ecp5_chipdb PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resource/embed.cc) target_sources(ecp5_chipdb PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resource/embed.cc)
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)

View File

@ -59,6 +59,8 @@ po::options_description ECP5CommandHandler::getArchOptions()
specific.add_options()("um5g-85k", "set device type to LFE5UM5G-85F"); specific.add_options()("um5g-85k", "set device type to LFE5UM5G-85F");
specific.add_options()("package", po::value<std::string>(), "select device package (defaults to CABGA381)"); specific.add_options()("package", po::value<std::string>(), "select device package (defaults to CABGA381)");
specific.add_options()("speed", po::value<int>(), "select device speedgrade (6, 7 or 8)");
specific.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format"); specific.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format");
specific.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write"); specific.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write");
@ -111,7 +113,31 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext()
chipArgs.package = vm["package"].as<std::string>(); chipArgs.package = vm["package"].as<std::string>();
else else
chipArgs.package = "CABGA381"; chipArgs.package = "CABGA381";
chipArgs.speed = 6; if (chipArgs.type == ArchArgs::LFE5UM5G_25F || chipArgs.type == ArchArgs::LFE5UM5G_45F ||
chipArgs.type == ArchArgs::LFE5UM5G_85F) {
if (vm.count("speed") && vm["speed"].as<int>() != 8)
log_error("Only speed grade 8 is available for 5G parts\n");
chipArgs.speed = ArchArgs::SPEED_8_5G;
} else {
if (vm.count("speed")) {
int speed = vm["speed"].as<int>();
switch (speed) {
case 6:
chipArgs.speed = ArchArgs::SPEED_6;
break;
case 7:
chipArgs.speed = ArchArgs::SPEED_7;
break;
case 8:
chipArgs.speed = ArchArgs::SPEED_8;
break;
default:
log_error("Unsupported speed grade '%d'\n", speed);
}
} else {
chipArgs.speed = ArchArgs::SPEED_6;
}
}
return std::unique_ptr<Context>(new Context(chipArgs)); return std::unique_ptr<Context>(new Context(chipArgs));
} }

View File

@ -357,9 +357,9 @@ class Ecp5Packer
} }
// Pass to pack LUT5s into a newly created slice // Pass to pack LUT5s into a newly created slice
void pack_lut5s() void pack_lut5xs()
{ {
log_info("Packing LUT5s...\n"); log_info("Packing LUT5-7s...\n");
for (auto cell : sorted(ctx->cells)) { for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second; CellInfo *ci = cell.second;
if (is_pfumx(ctx, ci)) { if (is_pfumx(ctx, ci)) {
@ -377,6 +377,8 @@ class Ecp5Packer
log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
if (lut1 == nullptr) if (lut1 == nullptr)
log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
if (ctx->verbose)
log_info(" mux '%s' forms part of a LUT5\n", cell.first.c_str(ctx));
replace_port(lut0, ctx->id("A"), packed.get(), ctx->id("A0")); replace_port(lut0, ctx->id("A"), packed.get(), ctx->id("A0"));
replace_port(lut0, ctx->id("B"), packed.get(), ctx->id("B0")); replace_port(lut0, ctx->id("B"), packed.get(), ctx->id("B0"));
replace_port(lut0, ctx->id("C"), packed.get(), ctx->id("C0")); replace_port(lut0, ctx->id("C"), packed.get(), ctx->id("C0"));
@ -412,8 +414,157 @@ class Ecp5Packer
} }
} }
flush_cells(); flush_cells();
// Pack LUT6s
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_l6mux(ctx, ci)) {
NetInfo *ofx0_0 = ci->ports.at(ctx->id("D0")).net;
if (ofx0_0 == nullptr)
log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx));
NetInfo *ofx0_1 = ci->ports.at(ctx->id("D1")).net;
if (ofx0_1 == nullptr)
log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx));
CellInfo *slice0 = net_driven_by(ctx, ofx0_0, is_lc, ctx->id("OFX0"));
CellInfo *slice1 = net_driven_by(ctx, ofx0_1, is_lc, ctx->id("OFX0"));
if (slice0 == nullptr) {
if (!net_driven_by(ctx, ofx0_0, is_l6mux, ctx->id("Z")) &&
!net_driven_by(ctx, ofx0_0, is_lc, ctx->id("OFX1")))
log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX0 but not a LUT7 mux "
"('%s.%s')\n",
ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx),
ofx0_0->driver.port.c_str(ctx));
continue;
}
if (slice1 == nullptr) {
if (!net_driven_by(ctx, ofx0_1, is_l6mux, ctx->id("Z")) &&
!net_driven_by(ctx, ofx0_1, is_lc, ctx->id("OFX1")))
log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX0 but not a LUT7 mux "
"('%s.%s')\n",
ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx),
ofx0_0->driver.port.c_str(ctx));
continue;
}
if (ctx->verbose)
log_info(" mux '%s' forms part of a LUT6\n", cell.first.c_str(ctx));
replace_port(ci, ctx->id("D0"), slice1, id_FXA);
replace_port(ci, ctx->id("D1"), slice1, id_FXB);
replace_port(ci, ctx->id("SD"), slice1, id_M1);
replace_port(ci, ctx->id("Z"), slice1, id_OFX1);
slice0->constr_z = 1;
slice0->constr_x = 0;
slice0->constr_y = 0;
slice0->constr_parent = slice1;
slice1->constr_z = 0;
slice1->constr_abs_z = true;
slice1->constr_children.push_back(slice0);
if (lutffPairs.find(ci->name) != lutffPairs.end()) {
CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get();
ff_to_slice(ctx, ff, slice1, 1, true);
packed_cells.insert(ff->name);
sliceUsage[slice1->name].ff1_used = true;
lutffPairs.erase(ci->name);
fflutPairs.erase(ff->name);
} }
packed_cells.insert(ci->name);
}
}
flush_cells();
// Pack LUT7s
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_l6mux(ctx, ci)) {
NetInfo *ofx1_0 = ci->ports.at(ctx->id("D0")).net;
if (ofx1_0 == nullptr)
log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx));
NetInfo *ofx1_1 = ci->ports.at(ctx->id("D1")).net;
if (ofx1_1 == nullptr)
log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx));
CellInfo *slice1 = net_driven_by(ctx, ofx1_0, is_lc, ctx->id("OFX1"));
CellInfo *slice3 = net_driven_by(ctx, ofx1_1, is_lc, ctx->id("OFX1"));
if (slice1 == nullptr)
log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX ('%s.%s')\n",
ci->name.c_str(ctx), ofx1_0->driver.cell->name.c_str(ctx),
ofx1_0->driver.port.c_str(ctx));
if (slice3 == nullptr)
log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX ('%s.%s')\n",
ci->name.c_str(ctx), ofx1_1->driver.cell->name.c_str(ctx),
ofx1_1->driver.port.c_str(ctx));
NetInfo *fxa_0 = slice1->ports.at(id_FXA).net;
if (fxa_0 == nullptr)
log_error("SLICE '%s' has disconnected port 'FXA'\n", slice1->name.c_str(ctx));
NetInfo *fxa_1 = slice3->ports.at(id_FXA).net;
if (fxa_1 == nullptr)
log_error("SLICE '%s' has disconnected port 'FXA'\n", slice3->name.c_str(ctx));
CellInfo *slice0 = net_driven_by(ctx, fxa_0, is_lc, ctx->id("OFX0"));
CellInfo *slice2 = net_driven_by(ctx, fxa_1, is_lc, ctx->id("OFX0"));
if (slice0 == nullptr)
log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n",
slice1->name.c_str(ctx), fxa_0->driver.cell->name.c_str(ctx),
fxa_0->driver.port.c_str(ctx));
if (slice2 == nullptr)
log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n",
slice3->name.c_str(ctx), fxa_1->driver.cell->name.c_str(ctx),
fxa_1->driver.port.c_str(ctx));
replace_port(ci, ctx->id("D0"), slice2, id_FXA);
replace_port(ci, ctx->id("D1"), slice2, id_FXB);
replace_port(ci, ctx->id("SD"), slice2, id_M1);
replace_port(ci, ctx->id("Z"), slice2, id_OFX1);
for (auto slice : {slice0, slice1, slice2, slice3}) {
slice->constr_children.clear();
slice->constr_abs_z = false;
slice->constr_x = slice->UNCONSTR;
slice->constr_y = slice->UNCONSTR;
slice->constr_z = slice->UNCONSTR;
slice->constr_parent = nullptr;
}
slice3->constr_children.clear();
slice3->constr_abs_z = true;
slice3->constr_z = 0;
slice2->constr_children.clear();
slice2->constr_abs_z = true;
slice2->constr_z = 1;
slice2->constr_x = 0;
slice2->constr_y = 0;
slice2->constr_parent = slice3;
slice3->constr_children.push_back(slice2);
slice1->constr_children.clear();
slice1->constr_abs_z = true;
slice1->constr_z = 2;
slice1->constr_x = 0;
slice1->constr_y = 0;
slice1->constr_parent = slice3;
slice3->constr_children.push_back(slice1);
slice0->constr_children.clear();
slice0->constr_abs_z = true;
slice0->constr_z = 3;
slice0->constr_x = 0;
slice0->constr_y = 0;
slice0->constr_parent = slice3;
slice3->constr_children.push_back(slice0);
if (lutffPairs.find(ci->name) != lutffPairs.end()) {
CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get();
ff_to_slice(ctx, ff, slice2, 1, true);
packed_cells.insert(ff->name);
sliceUsage[slice2->name].ff1_used = true;
lutffPairs.erase(ci->name);
fflutPairs.erase(ff->name);
}
packed_cells.insert(ci->name);
}
}
flush_cells();
}
// Create a feed in to the carry chain // Create a feed in to the carry chain
CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in) CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in)
{ {
@ -1183,7 +1334,7 @@ class Ecp5Packer
pack_dram(); pack_dram();
pack_carries(); pack_carries();
find_lutff_pairs(); find_lutff_pairs();
pack_lut5s(); pack_lut5xs();
pair_luts(); pair_luts();
pack_lut_pairs(); pack_lut_pairs();
pack_remaining_luts(); pack_remaining_luts();
@ -1252,6 +1403,10 @@ void Arch::assignArchInfo()
ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK")); ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK"));
ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR")); ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR"));
ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE"));
ci->sliceInfo.has_l6mux = false;
if (ci->ports.count(id_FXA) && ci->ports[id_FXA].net != nullptr &&
ci->ports[id_FXA].net->driver.port == id_OFX0)
ci->sliceInfo.has_l6mux = true;
} }
} }
} }

View File

@ -45,7 +45,7 @@ std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
chipArgs.type = ArchArgs::LFE5U_85F; chipArgs.type = ArchArgs::LFE5U_85F;
} }
chipArgs.package = root.get<std::string>("project.arch.package"); chipArgs.package = root.get<std::string>("project.arch.package");
chipArgs.speed = root.get<int>("project.arch.speed"); chipArgs.speed = ArchArgs::SpeedGrade(root.get<int>("project.arch.speed"));
return std::unique_ptr<Context>(new Context(chipArgs)); return std::unique_ptr<Context>(new Context(chipArgs));
} }

View File

@ -3,6 +3,8 @@ import pytrellis
import database import database
import argparse import argparse
import json import json
import pip_classes
import timing_dbs
from os import path from os import path
location_types = dict() location_types = dict()
@ -135,34 +137,81 @@ def process_loc_globals(chip):
spine = (-1, -1) spine = (-1, -1)
global_data[x, y] = (quadrants.index(quad), int(tapdrv.dir), tapdrv.col, spine) global_data[x, y] = (quadrants.index(quad), int(tapdrv.dir), tapdrv.col, spine)
def get_wire_type(name):
if "H00" in name or "V00" in name:
return "X0"
if "H01" in name or "V01" in name:
return "X1"
if "H02" in name or "V02" in name:
return "X2"
if "H06" in name or "V06" in name:
return "X6"
if "_SLICE" in name or "_EBR" in name:
return "SLICE"
return "LOCAL"
def get_pip_delay(wire_from, wire_to): speed_grade_names = ["6", "7", "8", "8_5G"]
# ECP5 timings WIP!!! speed_grade_cells = {}
type_from = get_wire_type(wire_from) speed_grade_pips = {}
type_to = get_wire_type(wire_to)
if type_from == "X2" and type_to == "X2": pip_class_to_idx = {"default": 0}
return 170
if type_from == "SLICE" or type_to == "SLICE": timing_port_xform = {
return 205 "RAD0": "D0",
if type_from in ("LOCAL", "X0") and type_to in ("X1", "X2", "X6"): "RAD1": "B0",
return 90 "RAD2": "C0",
if type_from == "X6" or type_to == "X6": "RAD3": "A0",
return 200 }
if type_from in ("X1", "X2", "X6") and type_to in ("LOCAL", "X0"):
return 90
return 100 def process_timing_data():
for grade in speed_grade_names:
with open(timing_dbs.cells_db_path("ECP5", grade)) as f:
cell_data = json.load(f)
cells = []
for cell, cdata in sorted(cell_data.items()):
celltype = constids[cell.replace(":", "_").replace("=", "_").replace(",", "_")]
delays = []
setupholds = []
for entry in cdata:
if entry["type"] == "Width":
continue
elif entry["type"] == "IOPath":
from_pin = entry["from_pin"][1] if type(entry["from_pin"]) is list else entry["from_pin"]
if from_pin in timing_port_xform:
from_pin = timing_port_xform[from_pin]
to_pin = entry["to_pin"]
if to_pin in timing_port_xform:
to_pin = timing_port_xform[to_pin]
min_delay = min(entry["rising"][0], entry["falling"][0])
max_delay = min(entry["rising"][2], entry["falling"][2])
delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay))
elif entry["type"] == "SetupHold":
pin = constids[entry["pin"]]
clock = constids[entry["clock"][1]]
min_setup = entry["setup"][0]
max_setup = entry["setup"][2]
min_hold = entry["hold"][0]
max_hold = entry["hold"][2]
setupholds.append((pin, clock, min_setup, max_setup, min_hold, max_hold))
else:
assert False, entry["type"]
cells.append((celltype, delays, setupholds))
pip_class_delays = []
for i in range(len(pip_class_to_idx)):
pip_class_delays.append((50, 50, 0, 0))
with open(timing_dbs.interconnect_db_path("ECP5", grade)) as f:
interconn_data = json.load(f)
for pipclass, pipdata in sorted(interconn_data.items()):
min_delay = pipdata["delay"][0] * 1.1
max_delay = pipdata["delay"][2] * 1.1
min_fanout = pipdata["fanout"][0]
max_fanout = pipdata["fanout"][2]
if grade == "6":
pip_class_to_idx[pipclass] = len(pip_class_delays)
pip_class_delays.append((min_delay, max_delay, min_fanout, max_fanout))
else:
if pipclass in pip_class_to_idx:
pip_class_delays[pip_class_to_idx[pipclass]] = (min_delay, max_delay, min_fanout, max_fanout)
speed_grade_cells[grade] = cells
speed_grade_pips[grade] = pip_class_delays
def get_pip_class(wire_from, wire_to):
class_name = pip_classes.get_pip_class(wire_from, wire_to)
if class_name is None or class_name not in pip_class_to_idx:
class_name = "default"
return pip_class_to_idx[class_name]
@ -181,7 +230,7 @@ def write_database(dev_name, chip, ddrg, endianness):
loc = loc_with_type[arc_loctype] loc = loc_with_type[arc_loctype]
lt = ddrg.typeAtLocation[pytrellis.Location(loc[0] + rel.x, loc[1] + rel.y)] lt = ddrg.typeAtLocation[pytrellis.Location(loc[0] + rel.x, loc[1] + rel.y)]
wire = ddrg.locationTypes[lt].wires[idx] wire = ddrg.locationTypes[lt].wires[idx]
return ddrg.to_str(wire.name) return "R{}C{}_{}".format(loc[1] + rel.y, loc[0] + rel.x, ddrg.to_str(wire.name))
bba = BinaryBlobAssembler() bba = BinaryBlobAssembler()
bba.pre('#include "nextpnr.h"') bba.pre('#include "nextpnr.h"')
@ -202,7 +251,7 @@ def write_database(dev_name, chip, ddrg, endianness):
bba.u32(arc.sinkWire.id, "dst_idx") bba.u32(arc.sinkWire.id, "dst_idx")
src_name = get_wire_name(idx, arc.srcWire.rel, arc.srcWire.id) src_name = get_wire_name(idx, arc.srcWire.rel, arc.srcWire.id)
snk_name = get_wire_name(idx, arc.sinkWire.rel, arc.sinkWire.id) snk_name = get_wire_name(idx, arc.sinkWire.rel, arc.sinkWire.id)
bba.u32(get_pip_delay(src_name, snk_name), "delay") # TODO:delay bba.u32(get_pip_class(src_name, snk_name), "timing_class")
bba.u16(get_tiletype_index(ddrg.to_str(arc.tiletype)), "tile_type") bba.u16(get_tiletype_index(ddrg.to_str(arc.tiletype)), "tile_type")
cls = arc.cls cls = arc.cls
if cls == 1 and "PCS" in snk_name or "DCU" in snk_name or "DCU" in src_name: if cls == 1 and "PCS" in snk_name or "DCU" in snk_name or "DCU" in src_name:
@ -321,11 +370,53 @@ def write_database(dev_name, chip, ddrg, endianness):
bba.u16(bank, "bank") bba.u16(bank, "bank")
bba.u16(0, "padding") bba.u16(0, "padding")
bba.l("tiletype_names", "RelPtr<char>") bba.l("tiletype_names", "RelPtr<char>")
for tt, idx in sorted(tiletype_names.items(), key=lambda x: x[1]): for tt, idx in sorted(tiletype_names.items(), key=lambda x: x[1]):
bba.s(tt, "name") bba.s(tt, "name")
for grade in speed_grade_names:
for cell in speed_grade_cells[grade]:
celltype, delays, setupholds = cell
if len(delays) > 0:
bba.l("cell_%d_delays_%s" % (celltype, grade))
for delay in delays:
from_pin, to_pin, min_delay, max_delay = delay
bba.u32(from_pin, "from_pin")
bba.u32(to_pin, "to_pin")
bba.u32(min_delay, "min_delay")
bba.u32(max_delay, "max_delay")
if len(setupholds) > 0:
bba.l("cell_%d_setupholds_%s" % (celltype, grade))
for sh in setupholds:
pin, clock, min_setup, max_setup, min_hold, max_hold = sh
bba.u32(pin, "sig_port")
bba.u32(clock, "clock_port")
bba.u32(min_setup, "min_setup")
bba.u32(max_setup, "max_setup")
bba.u32(min_hold, "min_hold")
bba.u32(max_hold, "max_hold")
bba.l("cell_timing_data_%s" % grade)
for cell in speed_grade_cells[grade]:
celltype, delays, setupholds = cell
bba.u32(celltype, "cell_type")
bba.u32(len(delays), "num_delays")
bba.u32(len(setupholds), "num_setup_hold")
bba.r("cell_%d_delays_%s" % (celltype, grade) if len(delays) > 0 else None, "delays")
bba.r("cell_%d_setupholds_%s" % (celltype, grade) if len(delays) > 0 else None, "setupholds")
bba.l("pip_timing_data_%s" % grade)
for pipclass in speed_grade_pips[grade]:
min_delay, max_delay, min_fanout, max_fanout = pipclass
bba.u32(min_delay, "min_delay")
bba.u32(max_delay, "max_delay")
bba.u32(min_fanout, "min_fanout")
bba.u32(max_fanout, "max_fanout")
bba.l("speed_grade_data")
for grade in speed_grade_names:
bba.u32(len(speed_grade_cells[grade]), "num_cell_timings")
bba.u32(len(speed_grade_pips[grade]), "num_pip_classes")
bba.r("cell_timing_data_%s" % grade, "cell_timings")
bba.r("pip_timing_data_%s" % grade, "pip_classes")
bba.l("chip_info") bba.l("chip_info")
bba.u32(max_col + 1, "width") bba.u32(max_col + 1, "width")
bba.u32(max_row + 1, "height") bba.u32(max_row + 1, "height")
@ -341,6 +432,7 @@ def write_database(dev_name, chip, ddrg, endianness):
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.r("tiles_info", "tile_info") bba.r("tiles_info", "tile_info")
bba.r("speed_grade_data", "speed_grades")
bba.pop() bba.pop()
return bba return bba
@ -375,6 +467,7 @@ def main():
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_timing_data()
process_pio_db(ddrg, args.device) process_pio_db(ddrg, args.device)
process_loc_globals(chip) process_loc_globals(chip)
# print("{} unique location types".format(len(ddrg.locationTypes))) # print("{} unique location types".format(len(ddrg.locationTypes)))

View File

@ -44,7 +44,6 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, ArchArgs args,
initBasenameResource(); initBasenameResource();
qRegisterMetaType<std::string>(); qRegisterMetaType<std::string>();
log_files.clear();
log_streams.clear(); log_streams.clear();
setObjectName("BaseMainWindow"); setObjectName("BaseMainWindow");

View File

@ -884,6 +884,13 @@ struct Arch : BaseCtx
} }
NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}"); NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}");
} }
int getDrivenGlobalNetwork(BelId bel) const
{
NPNR_ASSERT(getBelType(bel) == id_SB_GB);
IdString glb_net = getWireName(getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT));
return std::stoi(std::string("") + glb_net.str(this).back());
}
}; };
void ice40DelayFuzzerMain(Context *ctx); void ice40DelayFuzzerMain(Context *ctx);

View File

@ -165,8 +165,7 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
return true; return true;
NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr); NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr);
const NetInfo *net = cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net; const NetInfo *net = cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net;
IdString glb_net = getWireName(getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT)); int glb_id = getDrivenGlobalNetwork(bel);
int glb_id = std::stoi(std::string("") + glb_net.str(this).back());
if (net->is_reset && net->is_enable) if (net->is_reset && net->is_enable)
return false; return false;
else if (net->is_reset) else if (net->is_reset)

View File

@ -45,14 +45,36 @@ template <> struct string_converter<WireId>
{ {
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); } WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); } std::string to_str(Context *ctx, WireId id)
{
if (id == WireId())
throw bad_wrap();
return ctx->getWireName(id).str(ctx);
}
};
template <> struct string_converter<const WireId>
{
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id)
{
if (id == WireId())
throw bad_wrap();
return ctx->getWireName(id).str(ctx);
}
}; };
template <> struct string_converter<PipId> template <> struct string_converter<PipId>
{ {
PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); } PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
std::string to_str(Context *ctx, PipId id) { return ctx->getPipName(id).str(ctx); } std::string to_str(Context *ctx, PipId id)
{
if (id == PipId())
throw bad_wrap();
return ctx->getPipName(id).str(ctx);
}
}; };
} // namespace PythonConversion } // namespace PythonConversion

View File

@ -587,10 +587,36 @@ static void promote_globals(Context *ctx)
} }
} }
int prom_globals = 0, prom_resets = 0, prom_cens = 0, prom_logics = 0; int prom_globals = 0, prom_resets = 0, prom_cens = 0, prom_logics = 0;
int gbs_available = 8; int gbs_available = 8, resets_available = 4, cens_available = 4;
for (auto &cell : ctx->cells) for (auto &cell : ctx->cells)
if (is_gbuf(ctx, cell.second.get())) if (is_gbuf(ctx, cell.second.get())) {
/* One less buffer available */
--gbs_available; --gbs_available;
/* And possibly limits what we can promote */
if (cell.second->attrs.find(ctx->id("BEL")) != cell.second->attrs.end()) {
/* If the SB_GB is locked, doesn't matter what it drives */
BelId bel = ctx->getBelByName(ctx->id(cell.second->attrs[ctx->id("BEL")]));
int glb_id = ctx->getDrivenGlobalNetwork(bel);
if ((glb_id % 2) == 0)
resets_available--;
else if ((glb_id % 2) == 1)
cens_available--;
} else {
/* If it's free to move around, then look at what it drives */
NetInfo *ni = cell.second->ports[id_GLOBAL_BUFFER_OUTPUT].net;
for (auto user : ni->users) {
if (is_reset_port(ctx, user)) {
resets_available--;
break;
} else if (is_enable_port(ctx, user)) {
cens_available--;
break;
}
}
}
}
while (prom_globals < gbs_available) { while (prom_globals < gbs_available) {
auto global_clock = std::max_element(clock_count.begin(), clock_count.end(), auto global_clock = std::max_element(clock_count.begin(), clock_count.end(),
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) { [](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
@ -610,8 +636,8 @@ static void promote_globals(Context *ctx)
return a.second < b.second; return a.second < b.second;
}); });
if (global_clock->second == 0 && prom_logics < 4 && global_logic->second > logic_fanout_thresh && if (global_clock->second == 0 && prom_logics < 4 && global_logic->second > logic_fanout_thresh &&
(global_logic->second > global_cen->second || prom_cens >= 4) && (global_logic->second > global_cen->second || prom_cens >= cens_available) &&
(global_logic->second > global_reset->second || prom_resets >= 4)) { (global_logic->second > global_reset->second || prom_resets >= resets_available)) {
NetInfo *logicnet = ctx->nets[global_logic->first].get(); NetInfo *logicnet = ctx->nets[global_logic->first].get();
insert_global(ctx, logicnet, false, false, true); insert_global(ctx, logicnet, false, false, true);
++prom_globals; ++prom_globals;
@ -620,7 +646,7 @@ static void promote_globals(Context *ctx)
reset_count.erase(logicnet->name); reset_count.erase(logicnet->name);
cen_count.erase(logicnet->name); cen_count.erase(logicnet->name);
logic_count.erase(logicnet->name); logic_count.erase(logicnet->name);
} else if (global_reset->second > global_clock->second && prom_resets < 4) { } else if (global_reset->second > global_clock->second && prom_resets < resets_available) {
NetInfo *rstnet = ctx->nets[global_reset->first].get(); NetInfo *rstnet = ctx->nets[global_reset->first].get();
insert_global(ctx, rstnet, true, false, false); insert_global(ctx, rstnet, true, false, false);
++prom_globals; ++prom_globals;
@ -629,7 +655,7 @@ static void promote_globals(Context *ctx)
reset_count.erase(rstnet->name); reset_count.erase(rstnet->name);
cen_count.erase(rstnet->name); cen_count.erase(rstnet->name);
logic_count.erase(rstnet->name); logic_count.erase(rstnet->name);
} else if (global_cen->second > global_clock->second && prom_cens < 4 && } else if (global_cen->second > global_clock->second && prom_cens < cens_available &&
global_cen->second > enable_fanout_thresh) { global_cen->second > enable_fanout_thresh) {
NetInfo *cennet = ctx->nets[global_cen->first].get(); NetInfo *cennet = ctx->nets[global_cen->first].get();
insert_global(ctx, cennet, false, true, false); insert_global(ctx, cennet, false, true, false);
@ -874,7 +900,7 @@ static void pack_special(Context *ctx)
newname = "PLLOUT_A"; newname = "PLLOUT_A";
if (pi.name == ctx->id("PLLOUTCOREB")) if (pi.name == ctx->id("PLLOUTCOREB"))
newname = "PLLOUT_B"; newname = "PLLOUT_B";
if (pi.name == ctx->id("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBALA")) if (pi.name == ctx->id("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBAL"))
newname = "PLLOUT_A_GLOBAL"; newname = "PLLOUT_A_GLOBAL";
if (pi.name == ctx->id("PLLOUTGLOBALB")) if (pi.name == ctx->id("PLLOUTGLOBALB"))
newname = "PLLOUT_B_GLOBAL"; newname = "PLLOUT_B_GLOBAL";
@ -987,6 +1013,8 @@ static void pack_special(Context *ctx)
for (auto user : pad_packagepin_net->users) { for (auto user : pad_packagepin_net->users) {
user.cell->ports.erase(user.port); user.cell->ports.erase(user.port);
} }
if (pad_packagepin_net->driver.cell != nullptr)
pad_packagepin_net->driver.cell->ports.erase(pad_packagepin_net->driver.port);
ctx->nets.erase(pad_packagepin_net->name); ctx->nets.erase(pad_packagepin_net->name);
pad_packagepin_net = nullptr; pad_packagepin_net = nullptr;
} }

View File

@ -33,7 +33,9 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
if (!in) if (!in)
log_error("failed to open PCF file\n"); log_error("failed to open PCF file\n");
std::string line; std::string line;
int lineno = 0;
while (std::getline(in, line)) { while (std::getline(in, line)) {
lineno++;
size_t cstart = line.find("#"); size_t cstart = line.find("#");
if (cstart != std::string::npos) if (cstart != std::string::npos)
line = line.substr(0, cstart); line = line.substr(0, cstart);
@ -49,21 +51,25 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
size_t args_end = 1; size_t args_end = 1;
while (args_end < words.size() && words.at(args_end).at(0) == '-') while (args_end < words.size() && words.at(args_end).at(0) == '-')
args_end++; args_end++;
if (args_end >= words.size() - 1)
log_error("expected PCF syntax 'set_io cell pin' (on line %d)\n", lineno);
std::string cell = words.at(args_end); std::string cell = words.at(args_end);
std::string pin = words.at(args_end + 1); std::string pin = words.at(args_end + 1);
auto fnd_cell = ctx->cells.find(ctx->id(cell)); auto fnd_cell = ctx->cells.find(ctx->id(cell));
if (fnd_cell == ctx->cells.end()) { if (fnd_cell == ctx->cells.end()) {
log_warning("unmatched pcf constraint %s\n", cell.c_str()); log_warning("unmatched constraint '%s' (on line %d)\n", cell.c_str(), lineno);
} else { } else {
BelId pin_bel = ctx->getPackagePinBel(pin); BelId pin_bel = ctx->getPackagePinBel(pin);
if (pin_bel == BelId()) if (pin_bel == BelId())
log_error("package does not have a pin named %s\n", pin.c_str()); log_error("package does not have a pin named '%s' (on line %d)\n", pin.c_str(), lineno);
if (fnd_cell->second->attrs.count(ctx->id("BEL")))
log_error("duplicate pin constraint on '%s' (on line %d)\n", cell.c_str(), lineno);
fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx); fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx);
log_info("constrained '%s' to bel '%s'\n", cell.c_str(), log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
fnd_cell->second->attrs[ctx->id("BEL")].c_str()); fnd_cell->second->attrs[ctx->id("BEL")].c_str());
} }
} else { } else {
log_error("unsupported pcf command '%s'\n", cmd.c_str()); log_error("unsupported PCF command '%s' (on line %d)\n", cmd.c_str(), lineno);
} }
} }
ctx->settings.emplace(ctx->id("input/pcf"), filename); ctx->settings.emplace(ctx->id("input/pcf"), filename);

View File

@ -472,12 +472,7 @@ void json_import_ports(Context *ctx, const string &modname, const std::vector<Id
vcc_net(ctx, net.get()); vcc_net(ctx, net.get());
} else if (wire_node->data_string.compare(string("x")) == 0) { } else if (wire_node->data_string.compare(string("x")) == 0) {
ground_net(ctx, net.get()); ground_net(ctx, net.get());
log_info(" Floating wire node value, "
"'%s' on '%s'/'%s', converted to zero driver\n",
this_port.name.c_str(ctx), modname.c_str(), obj_name.c_str());
} else } else
log_error(" Unknown fixed type wire node " log_error(" Unknown fixed type wire node "
"value, \'%s\'\n", "value, \'%s\'\n",