aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt12
-rw-r--r--README.md2
-rw-r--r--common/command.cc36
-rw-r--r--common/command.h1
-rw-r--r--common/log.cc123
-rw-r--r--common/log.h20
-rw-r--r--common/placer1.cc28
-rw-r--r--common/placer1.h1
-rw-r--r--common/pybindings.cc26
-rw-r--r--common/pycontainers.h59
-rw-r--r--common/router1.cc12
-rw-r--r--common/timing.cc30
-rw-r--r--common/timing.h3
-rw-r--r--docs/constraints.md2
-rw-r--r--ecp5/arch_pybindings.h26
-rw-r--r--gui/basewindow.cc1
-rw-r--r--ice40/arch.cc26
-rw-r--r--ice40/arch.h20
-rw-r--r--ice40/arch_place.cc43
-rw-r--r--ice40/arch_pybindings.h26
-rw-r--r--ice40/archdefs.h5
-rw-r--r--ice40/bitstream.cc2119
-rw-r--r--ice40/cells.cc22
-rw-r--r--ice40/cells.h19
-rw-r--r--ice40/chipdb.py107
-rw-r--r--ice40/constids.inc2
-rw-r--r--ice40/pack.cc221
-rw-r--r--ice40/pcf.cc12
-rw-r--r--json/jsonparse.cc5
29 files changed, 1693 insertions, 1316 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0b02e73d..24ec76b2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -121,14 +121,14 @@ if (BUILD_PYTHON)
set(version ${PYTHONLIBS_VERSION_STRING})
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})
while (NOT "${version}" STREQUAL "" AND NOT Boost_PYTHON_FOUND)
STRING(REGEX REPLACE "([0-9.]+).[0-9]+" "\\1" 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})
STRING(REGEX MATCHALL "([0-9.]+).[0-9]+" has_more_version ${version})
@@ -138,21 +138,21 @@ if (BUILD_PYTHON)
endwhile ()
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).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
endif ()
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).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
endif ()
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).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
@@ -160,7 +160,7 @@ if (BUILD_PYTHON)
if (NOT Boost_PYTHON_FOUND)
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).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
diff --git a/README.md b/README.md
index 010acd8b..2ea73636 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ of the selected architecture:
- Qt5 or later (`qt5-default` for Ubuntu 16.04)
- 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)
-- 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
- 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`
diff --git a/common/command.cc b/common/command.cc
index c5c777e8..6cc9fbe7 100644
--- a/common/command.cc
+++ b/common/command.cc
@@ -40,7 +40,7 @@
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()
{
@@ -64,14 +64,14 @@ bool CommandHandler::parseOptions()
bool CommandHandler::executeBeforeContext()
{
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";
- std::cout << options << "\n";
+ std::cerr << options << "\n";
return argc != 1;
}
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";
return true;
}
@@ -84,7 +84,9 @@ po::options_description CommandHandler::getGeneralOptions()
po::options_description general("General options");
general.add_options()("help,h", "show help");
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()("force,f", "keep running after errors");
#ifndef NO_GUI
@@ -102,6 +104,7 @@ po::options_description CommandHandler::getGeneralOptions()
#endif
general.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
general.add_options()("seed", po::value<int>(), "seed value for random number generator");
+ general.add_options()("randomize-seed,r", "randomize seed value for random number generator");
general.add_options()("slack_redist_iter", po::value<int>(), "number of iterations between slack redistribution");
general.add_options()("cstrweight", po::value<float>(), "placer weighting for relative constraint satisfaction");
general.add_options()("pack-only", "pack design only without placement or routing");
@@ -127,7 +130,17 @@ void CommandHandler::setupContext(Context *ctx)
}
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")) {
@@ -138,6 +151,15 @@ void CommandHandler::setupContext(Context *ctx)
ctx->rngseed(vm["seed"].as<int>());
}
+ if (vm.count("randomize-seed")) {
+ srand(time(NULL));
+ int r;
+ do {
+ r = rand();
+ } while (r == 0);
+ ctx->rngseed(r);
+ }
+
if (vm.count("slack_redist_iter")) {
ctx->slack_redist_iter = vm["slack_redist_iter"].as<int>();
if (vm.count("freq") && vm["freq"].as<double>() == 0) {
@@ -248,7 +270,7 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
deinit_python();
#endif
- return 0;
+ return had_nonfatal_error ? 1 : 0;
}
void CommandHandler::conflicting_options(const boost::program_options::variables_map &vm, const char *opt1,
diff --git a/common/command.h b/common/command.h
index 12f710f6..30e0e58b 100644
--- a/common/command.h
+++ b/common/command.h
@@ -66,6 +66,7 @@ class CommandHandler
int argc;
char **argv;
ProjectHandler project;
+ std::ofstream logfile;
};
NEXTPNR_NAMESPACE_END
diff --git a/common/log.cc b/common/log.cc
index 6b2d6065..82e09fec 100644
--- a/common/log.cc
+++ b/common/log.cc
@@ -32,19 +32,14 @@ NEXTPNR_NAMESPACE_BEGIN
NPNR_NORETURN void logv_error(const char *format, va_list ap) NPNR_ATTRIBUTE(noreturn);
-std::vector<FILE *> log_files;
-std::vector<std::ostream *> log_streams;
-FILE *log_errfile = NULL;
+std::vector<std::pair<std::ostream *, LogLevel>> log_streams;
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;
void (*log_error_atexit)() = NULL;
-// static bool next_print_log = false;
static int log_newline_count = 0;
+bool had_nonfatal_error = false;
std::string stringf(const char *fmt, ...)
{
@@ -88,7 +83,7 @@ std::string vstringf(const char *fmt, va_list ap)
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
@@ -108,90 +103,50 @@ void logv(const char *format, va_list ap)
else
log_newline_count = str.size() - nnl_pos - 1;
- for (auto f : log_files)
- fputs(str.c_str(), f);
-
for (auto f : log_streams)
- *f << str;
+ if (f.second <= level)
+ *f.first << str;
if (log_write_function)
log_write_function(str);
}
-void logv_info(const char *format, va_list ap)
+void log_with_level(LogLevel level, const char *format, ...)
{
- std::string message = vstringf(format, ap);
-
- log_always("Info: %s", message.c_str());
- log_flush();
+ va_list ap;
+ va_start(ap, format);
+ logv(format, ap, level);
+ va_end(ap);
}
-void logv_warning(const char *format, va_list ap)
+void logv_prefixed(const char *prefix, const char *format, va_list ap, LogLevel level)
{
std::string message = vstringf(format, ap);
- log_always("Warning: %s", message.c_str());
+ log_with_level(level, "%s%s", prefix, 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, ...)
{
va_list ap;
va_start(ap, format);
- logv(format, ap);
+ logv(format, ap, LogLevel::ALWAYS);
va_end(ap);
}
void log(const char *format, ...)
{
- if (log_quiet_warnings)
- return;
va_list ap;
va_start(ap, format);
- logv(format, ap);
+ logv(format, ap, LogLevel::LOG);
va_end(ap);
}
void log_info(const char *format, ...)
{
- if (log_quiet_warnings)
- return;
va_list ap;
va_start(ap, format);
- logv_info(format, ap);
+ logv_prefixed("Info: ", format, ap, LogLevel::INFO);
va_end(ap);
}
@@ -199,15 +154,7 @@ void log_warning(const char *format, ...)
{
va_list ap;
va_start(ap, format);
- logv_warning(format, ap);
- va_end(ap);
-}
-
-void log_warning_noprefix(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- logv_warning_noprefix(format, ap);
+ logv_prefixed("Warning: ", format, ap, LogLevel::WARNING);
va_end(ap);
}
@@ -215,41 +162,35 @@ void log_error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
- logv_error(format, ap);
-}
-
-void log_cmd_error(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
+ logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR);
- if (log_cmd_error_throw) {
- log_last_error = vstringf(format, ap);
- log_always("ERROR: %s", log_last_error.c_str());
- log_flush();
- throw log_cmd_error_exception();
- }
+ if (log_error_atexit)
+ log_error_atexit();
- logv_error(format, ap);
+ throw log_execution_error_exception();
}
void log_break()
{
- if (log_quiet_warnings)
- return;
if (log_newline_count < 2)
- log_always("\n");
+ log("\n");
if (log_newline_count < 2)
- log_always("\n");
+ log("\n");
}
-void log_flush()
+void log_nonfatal_error(const char *format, ...)
{
- for (auto f : log_files)
- fflush(f);
+ va_list ap;
+ va_start(ap, format);
+ logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR);
+ va_end(ap);
+ had_nonfatal_error = true;
+}
+void log_flush()
+{
for (auto f : log_streams)
- f->flush();
+ f.first->flush();
}
NEXTPNR_NAMESPACE_END
diff --git a/common/log.h b/common/log.h
index b5fddf53..f1727ac6 100644
--- a/common/log.h
+++ b/common/log.h
@@ -42,14 +42,21 @@ struct log_execution_error_exception
{
};
-extern std::vector<FILE *> log_files;
-extern std::vector<std::ostream *> log_streams;
-extern FILE *log_errfile;
+enum class LogLevel
+{
+ LOG,
+ INFO,
+ WARNING,
+ ERROR,
+ ALWAYS
+};
+
+extern std::vector<std::pair<std::ostream *, LogLevel>> log_streams;
extern log_write_type log_write_function;
-extern bool log_quiet_warnings;
extern std::string log_last_error;
extern void (*log_error_atexit)();
+extern bool had_nonfatal_error;
std::string stringf(const char *fmt, ...);
std::string vstringf(const char *fmt, va_list ap);
@@ -59,10 +66,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_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_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_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_flush();
@@ -75,7 +80,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__)
#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
diff --git a/common/placer1.cc b/common/placer1.cc
index 0fd9a227..0db7ce00 100644
--- a/common/placer1.cc
+++ b/common/placer1.cc
@@ -51,15 +51,20 @@ class SAPlacer
{
int num_bel_types = 0;
for (auto bel : ctx->getBels()) {
- Loc loc = ctx->getBelLocation(bel);
IdString type = ctx->getBelType(bel);
- int type_idx;
if (bel_types.find(type) == bel_types.end()) {
- type_idx = num_bel_types++;
- bel_types[type] = type_idx;
+ bel_types[type] = std::tuple<int, int>(num_bel_types++, 1);
} else {
- type_idx = bel_types.at(type);
+ std::get<1>(bel_types.at(type))++;
}
+ }
+ for (auto bel : ctx->getBels()) {
+ Loc loc = ctx->getBelLocation(bel);
+ IdString type = ctx->getBelType(bel);
+ int type_idx = std::get<0>(bel_types.at(type));
+ int type_cnt = std::get<1>(bel_types.at(type));
+ if (type_cnt < cfg.minBelsForGridPick)
+ loc.x = loc.y = 0;
if (int(fast_bels.size()) < type_idx + 1)
fast_bels.resize(type_idx + 1);
if (int(fast_bels.at(type_idx).size()) < (loc.x + 1))
@@ -463,7 +468,10 @@ class SAPlacer
while (true) {
int nx = ctx->rng(2 * diameter + 1) + std::max(curr_loc.x - 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, beltype_cnt;
+ std::tie(beltype_idx, beltype_cnt) = bel_types.at(targetType);
+ if (beltype_cnt < cfg.minBelsForGridPick)
+ nx = ny = 0;
if (nx >= int(fast_bels.at(beltype_idx).size()))
continue;
if (ny >= int(fast_bels.at(beltype_idx).at(nx).size()))
@@ -485,7 +493,7 @@ class SAPlacer
bool improved = false;
int n_move, n_accept;
int diameter = 35, max_x = 1, max_y = 1;
- std::unordered_map<IdString, int> bel_types;
+ std::unordered_map<IdString, std::tuple<int, int>> bel_types;
std::vector<std::vector<std::vector<std::vector<BelId>>>> fast_bels;
std::unordered_set<BelId> locked_bels;
bool require_legal = true;
@@ -503,7 +511,11 @@ class SAPlacer
std::vector<decltype(NetInfo::udata)> old_udata;
};
-Placer1Cfg::Placer1Cfg(Context *ctx) : Settings(ctx) { constraintWeight = get<float>("placer1/constraintWeight", 10); }
+Placer1Cfg::Placer1Cfg(Context *ctx) : Settings(ctx)
+{
+ constraintWeight = get<float>("placer1/constraintWeight", 10);
+ minBelsForGridPick = get<int>("placer1/minBelsForGridPick", 64);
+}
bool placer1(Context *ctx, Placer1Cfg cfg)
{
diff --git a/common/placer1.h b/common/placer1.h
index 55db1fa5..7305f4b1 100644
--- a/common/placer1.h
+++ b/common/placer1.h
@@ -28,6 +28,7 @@ struct Placer1Cfg : public Settings
{
Placer1Cfg(Context *ctx);
float constraintWeight;
+ int minBelsForGridPick;
};
extern bool placer1(Context *ctx, Placer1Cfg cfg);
diff --git a/common/pybindings.cc b/common/pybindings.cc
index 061dfc47..6cae889d 100644
--- a/common/pybindings.cc
+++ b/common/pybindings.cc
@@ -68,6 +68,19 @@ void translate_assertfail(const assertion_failure &e)
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)
{
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>,
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;
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");
readwrite_wrapper<NetInfo &, decltype(&NetInfo::driver), &NetInfo::driver, wrap_context<PortRef &>,
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");
readonly_wrapper<NetInfo &, decltype(&NetInfo::wires), &NetInfo::wires, wrap_context<WireMap &>>::def_wrap(ni_cls,
"wires");
@@ -141,12 +154,21 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
readwrite_wrapper<PortRef &, decltype(&PortRef::budget), &PortRef::budget, pass_through<delay_t>,
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("load_design", load_design_shim, return_value_policy<manage_new_object>());
WRAP_MAP(AttrMap, pass_through<std::string>, "AttrMap");
WRAP_MAP(PortMap, wrap_context<PortInfo &>, "PortMap");
WRAP_MAP(PinMap, conv_to_str<IdString>, "PinMap");
+ WRAP_MAP(WireMap, wrap_context<PipMap &>, "WireMap");
+
+ WRAP_VECTOR(PortRefVector, wrap_context<PortRef &>);
arch_wrap_python();
}
diff --git a/common/pycontainers.h b/common/pycontainers.h
index 094706f7..70f69c51 100644
--- a/common/pycontainers.h
+++ b/common/pycontainers.h
@@ -119,9 +119,66 @@ struct range_wrapper
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
.second) or as a Python iterable and indexable object
- */
+*/
template <typename T1, typename T2> struct pair_wrapper
{
typedef std::pair<T1, T2> T;
diff --git a/common/router1.cc b/common/router1.cc
index 198461bc..cbc0df90 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -777,6 +777,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
router.arcs_without_ripup - last_arcs_without_ripup, int(router.arc_queue.size()));
last_arcs_with_ripup = router.arcs_with_ripup;
last_arcs_without_ripup = router.arcs_without_ripup;
+ ctx->yield();
#ifndef NDEBUG
router.check();
#endif
@@ -802,6 +803,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
router.arcs_with_ripup - last_arcs_with_ripup, router.arcs_without_ripup - last_arcs_without_ripup,
int(router.arc_queue.size()));
log_info("Routing complete.\n");
+ ctx->yield();
#ifndef NDEBUG
router.check();
@@ -810,7 +812,8 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
#endif
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();
return true;
@@ -868,7 +871,12 @@ bool Context::checkRoutedDesign() const
}
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 (ctx->debug)
diff --git a/common/timing.cc b/common/timing.cc
index 002ccda9..242d56d9 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -485,11 +485,11 @@ void assign_budget(Context *ctx, bool quiet)
for (auto &user : net.second->users) {
// Post-update check
if (!ctx->auto_freq && user.budget < 0)
- log_warning("port %s.%s, connected to net '%s', has negative "
- "timing budget of %fns\n",
- user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
- ctx->getDelayNS(user.budget));
- else if (ctx->verbose)
+ log_info("port %s.%s, connected to net '%s', has negative "
+ "timing budget of %fns\n",
+ user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
+ ctx->getDelayNS(user.budget));
+ else if (ctx->debug)
log_info("port %s.%s, connected to net '%s', has "
"timing budget of %fns\n",
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());
}
-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) {
std::string value;
@@ -705,15 +705,17 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
for (auto &clock : clock_reports) {
const auto &clock_name = clock.first.str(ctx);
const int width = max_width - clock_name.size();
- if (ctx->nets.at(clock.first)->clkconstr) {
- float target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay());
+ float target = ctx->target_freq / 1e6;
+ 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, "",
- clock_name.c_str(), clock_fmax[clock.first],
- (target < clock_fmax[clock.first]) ? "PASS" : "FAIL", target);
- } else {
- log_info("Max frequency for clock %*s'%s': %.02f MHz\n", width, "", clock_name.c_str(),
- clock_fmax[clock.first]);
- }
+ clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);
+ else
+ log_nonfatal_error("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
+ clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);
}
for (auto &eclock : empty_clocks) {
if (eclock != ctx->id("$async$"))
diff --git a/common/timing.h b/common/timing.h
index 1fd76310..42f928dc 100644
--- a/common/timing.h
+++ b/common/timing.h
@@ -29,7 +29,8 @@ void assign_budget(Context *ctx, bool quiet = false);
// Perform timing analysis and print out the fmax, and optionally the
// 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
diff --git a/docs/constraints.md b/docs/constraints.md
index 263df7b6..c12204da 100644
--- a/docs/constraints.md
+++ b/docs/constraints.md
@@ -2,7 +2,7 @@
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.
The iCE40 architecture supports PCF constraints thus:
diff --git a/ecp5/arch_pybindings.h b/ecp5/arch_pybindings.h
index f18b5ff1..9bd7bcdf 100644
--- a/ecp5/arch_pybindings.h
+++ b/ecp5/arch_pybindings.h
@@ -44,14 +44,36 @@ template <> struct string_converter<WireId>
{
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>
{
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
diff --git a/gui/basewindow.cc b/gui/basewindow.cc
index 92812285..49c2d8d5 100644
--- a/gui/basewindow.cc
+++ b/gui/basewindow.cc
@@ -44,7 +44,6 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, ArchArgs args,
initBasenameResource();
qRegisterMetaType<std::string>();
- log_files.clear();
log_streams.clear();
setObjectName("BaseMainWindow");
diff --git a/ice40/arch.cc b/ice40/arch.cc
index 2a9e167b..02e5515b 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -284,6 +284,25 @@ std::vector<IdString> Arch::getBelPins(BelId bel) const
return ret;
}
+bool Arch::isBelLocked(BelId bel) const
+{
+ const BelConfigPOD *bel_config = nullptr;
+ for (int i = 0; i < chip_info->num_belcfgs; i++) {
+ if (chip_info->bel_config[i].bel_index == bel.index) {
+ bel_config = &chip_info->bel_config[i];
+ break;
+ }
+ }
+ NPNR_ASSERT(bel_config != nullptr);
+ for (int i = 0; i < bel_config->num_entries; i++) {
+ if (strcmp("LOCKED", bel_config->entries[i].cbit_name.get()))
+ continue;
+ if ("LOCKED_" + archArgs().package == bel_config->entries[i].entry_name.get())
+ return true;
+ }
+ return false;
+}
+
// -----------------------------------------------------------------------
WireId Arch::getWireByName(IdString name) const
@@ -927,6 +946,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
return TMG_COMB_INPUT;
} else if (cell->type == id_SB_WARMBOOT) {
return TMG_ENDPOINT;
+ } else if (cell->type == id_SB_RGBA_DRV) {
+ if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2)
+ return TMG_IGNORE;
+ return TMG_ENDPOINT;
}
log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
}
@@ -1025,6 +1048,9 @@ void Arch::assignCellInfo(CellInfo *cell)
cell->lcInfo.inputCount++;
} else if (cell->type == id_SB_IO) {
cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT";
+ cell->ioInfo.global = bool_or_default(cell->attrs, this->id("GLOBAL"));
+ } else if (cell->type == id_SB_GB) {
+ cell->gbInfo.forPadIn = bool_or_default(cell->attrs, this->id("FOR_PAD_IN"));
}
}
diff --git a/ice40/arch.h b/ice40/arch.h
index 836dc46e..e8c597c9 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -212,11 +212,26 @@ NPNR_PACKED_STRUCT(struct CellTimingPOD {
RelPtr<CellPathDelayPOD> path_delays;
});
+NPNR_PACKED_STRUCT(struct GlobalNetworkInfoPOD {
+ uint8_t gb_x;
+ uint8_t gb_y;
+
+ uint8_t pi_gb_x;
+ uint8_t pi_gb_y;
+ uint8_t pi_gb_pio;
+
+ uint8_t pi_eb_bank;
+ uint16_t pi_eb_x;
+ uint16_t pi_eb_y;
+
+ uint16_t pad;
+});
+
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_bels, num_wires, num_pips;
int32_t num_switches, num_belcfgs, num_packages;
- int32_t num_timing_cells;
+ int32_t num_timing_cells, num_global_networks;
RelPtr<BelInfoPOD> bel_data;
RelPtr<WireInfoPOD> wire_data;
RelPtr<PipInfoPOD> pip_data;
@@ -225,6 +240,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD {
RelPtr<BelConfigPOD> bel_config;
RelPtr<PackageInfoPOD> packages_data;
RelPtr<CellTimingPOD> cell_timing;
+ RelPtr<GlobalNetworkInfoPOD> global_network_info;
RelPtr<RelPtr<char>> tile_wire_names;
});
@@ -510,6 +526,8 @@ struct Arch : BaseCtx
PortType getBelPinType(BelId bel, IdString pin) const;
std::vector<IdString> getBelPins(BelId bel) const;
+ bool isBelLocked(BelId bel) const;
+
// -------------------------------------------------
WireId getWireByName(IdString name) const;
diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc
index c97b9c26..41f9b640 100644
--- a/ice40/arch_place.cc
+++ b/ice40/arch_place.cc
@@ -114,31 +114,30 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
// Find shared PLL by looking for driving bel siblings from D_IN_0
// that are a PLL clock output.
auto wire = getBelPinWire(bel, id_D_IN_0);
- IdString pll_bel_pin;
- BelId pll_bel;
for (auto pin : getWireBelPins(wire)) {
if (pin.pin == id_PLLOUT_A || pin.pin == id_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 != nullptr) {
- // Is the shared port driving a net?
- auto pi = pll_cell->ports[pll_bel_pin];
- if (pi.net != nullptr) {
- // Are we perhaps a PAD INPUT Bel that can be placed here?
- if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this)) {
- return true;
- }
- return false;
- }
+ // Is there a PLL there ?
+ auto pll_cell = getBoundBelCell(pin.bel);
+ if (pll_cell == nullptr)
+ break;
+
+ // Is that port actually used ?
+ if ((pin.pin == id_PLLOUT_B) && !is_sb_pll40_dual(this, pll_cell))
+ break;
+
+ // Is that SB_IO used at an input ?
+ if ((cell->ports[id_D_IN_0].net == nullptr) && (cell->ports[id_D_IN_1].net == nullptr))
+ break;
+
+ // Are we perhaps a PAD INPUT Bel that can be placed here?
+ if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this))
+ return true;
+
+ // Conflict
+ return false;
}
}
+
Loc ioLoc = getBelLocation(bel);
Loc compLoc = ioLoc;
compLoc.z = 1 - compLoc.z;
@@ -162,6 +161,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
return getBelPackagePin(bel) != "";
} else if (cell->type == id_SB_GB) {
+ if (cell->gbInfo.forPadIn)
+ return true;
NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr);
const NetInfo *net = cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net;
IdString glb_net = getWireName(getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT));
diff --git a/ice40/arch_pybindings.h b/ice40/arch_pybindings.h
index 070c2396..eaf3ac97 100644
--- a/ice40/arch_pybindings.h
+++ b/ice40/arch_pybindings.h
@@ -45,14 +45,36 @@ template <> struct string_converter<WireId>
{
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>
{
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
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index b9614c07..2bffe667 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -150,8 +150,13 @@ struct ArchCellInfo
struct
{
bool lvds;
+ bool global;
// TODO: clk packing checks...
} ioInfo;
+ struct
+ {
+ bool forPadIn;
+ } gbInfo;
};
};
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index 4efb1091..ecb26753 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -1,1043 +1,1076 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Clifford Wolf <clifford@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
- * 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 "bitstream.h"
-#include <cctype>
-#include <vector>
-#include "cells.h"
-#include "log.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-inline TileType tile_at(const Context *ctx, int x, int y)
-{
- return ctx->chip_info->tile_grid[y * ctx->chip_info->width + x];
-}
-
-const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &name)
-{
- for (int i = 0; i < tile.num_config_entries; i++) {
- if (std::string(tile.entries[i].name.get()) == name) {
- return tile.entries[i];
- }
- }
- NPNR_ASSERT_FALSE_STR("unable to find config bit " + name);
-}
-
-std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t x, int8_t y, int8_t z)
-{
- for (int i = 0; i < bi.num_ierens; i++) {
- auto ie = bi.ierens[i];
- if (ie.iox == x && ie.ioy == y && ie.ioz == z) {
- return std::make_tuple(ie.ierx, ie.iery, ie.ierz);
- }
- }
- // No pin at this location
- return std::make_tuple(-1, -1, -1);
-};
-
-bool get_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name,
- int index = -1)
-{
- const ConfigEntryPOD &cfg = find_config(ti, name);
- if (index == -1) {
- for (int i = 0; i < cfg.num_bits; i++) {
- return tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
- }
- } else {
- return tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
- }
- return false;
-}
-
-void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name, bool value,
- int index = -1)
-{
- const ConfigEntryPOD &cfg = find_config(ti, name);
- if (index == -1) {
- for (int i = 0; i < cfg.num_bits; i++) {
- int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
- if (cbit && !value)
- log_error("clearing already set config bit %s\n", name.c_str());
- cbit = value;
- }
- } else {
- int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
- cbit = value;
- if (cbit && !value)
- 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);
- }
-}
-
-int get_param_or_def(const CellInfo *cell, const IdString param, int defval = 0)
-{
- auto found = cell->params.find(param);
- if (found != cell->params.end())
- return std::stoi(found->second);
- else
- return defval;
-}
-
-std::string get_param_str_or_def(const CellInfo *cell, const IdString param, std::string defval = "")
-{
- auto found = cell->params.find(param);
- if (found != cell->params.end())
- return found->second;
- else
- return defval;
-}
-
-char get_hexdigit(int i) { return std::string("0123456789ABCDEF").at(i); }
-
-static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
-{
- for (int i = 0; i < chip->num_belcfgs; i++) {
- if (chip->bel_config[i].bel_index == bel.index)
- return chip->bel_config[i];
- }
- NPNR_ASSERT_FALSE("failed to find bel config");
-}
-
-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,
- bool value, std::string prefix)
-{
- const ChipInfoPOD *chip = ctx->chip_info;
-
- for (int i = 0; i < cell_cbits.num_entries; i++) {
- const auto &cbit = cell_cbits.entries[i];
- if (cbit.entry_name.get() == name) {
- 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), prefix + cbit.cbit_name.get(), value);
- return;
- }
- }
- NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name);
-}
-
-void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
- const std::vector<std::pair<std::string, int>> &params, bool string_style, std::string prefix)
-{
- const ChipInfoPOD *chip = ctx->chip_info;
- const auto &bc = get_ec_config(chip, cell->bel);
- for (auto p : params) {
- std::vector<bool> value;
- if (string_style) {
- // Lattice's weird string style params, not sure if
- // prefixes other than 0b should be supported, only 0b features in docs
- std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
- assert(raw.substr(0, 2) == "0b");
- raw = raw.substr(2);
- value.resize(raw.length());
- for (int i = 0; i < (int)raw.length(); i++) {
- if (raw[i] == '1') {
- value[(raw.length() - 1) - i] = 1;
- } else {
- assert(raw[i] == '0');
- value[(raw.length() - 1) - i] = 0;
- }
- }
- } else {
- int ival = get_param_or_def(cell, ctx->id(p.first), 0);
-
- for (int i = 0; i < p.second; i++)
- value.push_back((ival >> i) & 0x1);
- }
-
- value.resize(p.second);
- if (p.second == 1) {
- set_ec_cbit(config, ctx, bc, p.first, value.at(0), prefix);
- } else {
- for (int i = 0; i < p.second; i++) {
- set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix);
- }
- }
- }
-}
-
-std::string tagTileType(TileType &tile)
-{
- if (tile == TILE_NONE)
- return "";
- switch (tile) {
- case TILE_LOGIC:
- return ".logic_tile";
- break;
- case TILE_IO:
- return ".io_tile";
- break;
- case TILE_RAMB:
- return ".ramb_tile";
- break;
- case TILE_RAMT:
- return ".ramt_tile";
- break;
- case TILE_DSP0:
- return ".dsp0_tile";
- break;
- case TILE_DSP1:
- return ".dsp1_tile";
- break;
- case TILE_DSP2:
- return ".dsp2_tile";
- break;
- case TILE_DSP3:
- return ".dsp3_tile";
- break;
- case TILE_IPCON:
- return ".ipcon_tile";
- break;
- default:
- NPNR_ASSERT(false);
- }
-}
-
-static BelPin get_one_bel_pin(const Context *ctx, WireId wire)
-{
- auto pins = ctx->getWireBelPins(wire);
- NPNR_ASSERT(pins.begin() != pins.end());
- return *pins.begin();
-}
-
-// Permute LUT init value given map (LUT input -> ext input)
-unsigned permute_lut(unsigned orig_init, const std::unordered_map<int, int> &input_permute)
-{
- unsigned new_init = 0;
-
- for (int i = 0; i < 16; i++) {
- int permute_address = 0;
- for (int j = 0; j < 4; j++) {
- if ((i >> j) & 0x1)
- permute_address |= (1 << input_permute.at(j));
- }
- if ((orig_init >> i) & 0x1) {
- new_init |= (1 << permute_address);
- }
- }
-
- return new_init;
-}
-
-void write_asc(const Context *ctx, std::ostream &out)
-{
-
- static const std::vector<int> lut_perm = {
- 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
- };
-
- // [y][x][row][col]
- const ChipInfoPOD &ci = *ctx->chip_info;
- const BitstreamInfoPOD &bi = *ci.bits_info;
- chipconfig_t config;
- config.resize(ci.height);
- for (int y = 0; y < ci.height; y++) {
- config.at(y).resize(ci.width);
- for (int x = 0; x < ci.width; x++) {
- TileType tile = tile_at(ctx, x, y);
- int rows = bi.tiles_nonrouting[tile].rows;
- int cols = bi.tiles_nonrouting[tile].cols;
- config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
- }
- }
- out << ".comment from next-pnr" << std::endl;
-
- switch (ctx->args.type) {
- case ArchArgs::LP384:
- out << ".device 384" << std::endl;
- break;
- case ArchArgs::HX1K:
- case ArchArgs::LP1K:
- out << ".device 1k" << std::endl;
- break;
- case ArchArgs::HX8K:
- case ArchArgs::LP8K:
- out << ".device 8k" << std::endl;
- break;
- case ArchArgs::UP5K:
- out << ".device 5k" << std::endl;
- break;
- default:
- NPNR_ASSERT_FALSE("unsupported device type\n");
- }
- // Set pips
- for (auto pip : ctx->getPips()) {
- if (ctx->pip_to_net[pip.index] != nullptr) {
- const PipInfoPOD &pi = ci.pip_data[pip.index];
- const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
- int sw_bel_idx = swi.bel;
- if (sw_bel_idx >= 0) {
- const BelInfoPOD &beli = ci.bel_data[sw_bel_idx];
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
- BelId sw_bel;
- sw_bel.index = sw_bel_idx;
- NPNR_ASSERT(ctx->getBelType(sw_bel) == id_ICESTORM_LC);
-
- if (ci.wire_data[ctx->getPipDstWire(pip).index].type == WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT)
- continue; // Permutation pips
- BelPin output = get_one_bel_pin(ctx, ctx->getPipDstWire(pip));
- NPNR_ASSERT(output.bel == sw_bel && output.pin == id_O);
- unsigned lut_init;
-
- WireId permWire;
- for (auto permPip : ctx->getPipsUphill(ctx->getPipSrcWire(pip))) {
- if (ctx->getBoundPipNet(permPip) != nullptr) {
- permWire = ctx->getPipSrcWire(permPip);
- }
- }
- NPNR_ASSERT(permWire != WireId());
- std::string dName = ci.wire_data[permWire.index].name.get();
-
- switch (dName.back()) {
- case '0':
- lut_init = 2;
- break;
- case '1':
- lut_init = 4;
- break;
- case '2':
- lut_init = 16;
- break;
- case '3':
- lut_init = 256;
- break;
- default:
- NPNR_ASSERT_FALSE("bad feedthru LUT input");
- }
- std::vector<bool> lc(20, false);
- for (int i = 0; i < 16; i++) {
- if ((lut_init >> i) & 0x1)
- lc.at(lut_perm.at(i)) = true;
- }
-
- for (int i = 0; i < 20; i++)
- set_config(ti, config.at(beli.y).at(beli.x), "LC_" + std::to_string(beli.z), lc.at(i), i);
- } else {
- for (int i = 0; i < swi.num_bits; i++) {
- bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
- int8_t &cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
- if (bool(cbit) != 0)
- NPNR_ASSERT(false);
- cbit = val;
- }
- }
- }
- }
-
- std::unordered_set<Loc> sb_io_used_by_pll;
- std::unordered_set<Loc> sb_io_used_by_io;
-
- // Set logic cell config
- for (auto &cell : ctx->cells) {
-
- BelId bel = cell.second.get()->bel;
- if (bel == BelId()) {
- std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
- continue;
- }
- if (cell.second->type == ctx->id("ICESTORM_LC")) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y, z = beli.z;
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
- unsigned lut_init = get_param_or_def(cell.second.get(), ctx->id("LUT_INIT"));
- bool neg_clk = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK"));
- bool dff_enable = get_param_or_def(cell.second.get(), ctx->id("DFF_ENABLE"));
- bool async_sr = get_param_or_def(cell.second.get(), ctx->id("ASYNC_SR"));
- bool set_noreset = get_param_or_def(cell.second.get(), ctx->id("SET_NORESET"));
- bool carry_enable = get_param_or_def(cell.second.get(), ctx->id("CARRY_ENABLE"));
- std::vector<bool> lc(20, false);
-
- // Discover permutation
- std::unordered_map<int, int> input_perm;
- std::set<int> unused;
- for (int i = 0; i < 4; i++)
- unused.insert(i);
- for (int i = 0; i < 4; i++) {
- WireId lut_wire = ctx->getBelPinWire(bel, IdString(ID_I0 + i));
- for (auto pip : ctx->getPipsUphill(lut_wire)) {
- if (ctx->getBoundPipNet(pip) != nullptr) {
- std::string name = ci.wire_data[ctx->getPipSrcWire(pip).index].name.get();
- switch (name.back()) {
- case '0':
- input_perm[i] = 0;
- unused.erase(0);
- break;
- case '1':
- input_perm[i] = 1;
- unused.erase(1);
- break;
- case '2':
- input_perm[i] = 2;
- unused.erase(2);
- break;
- case '3':
- input_perm[i] = 3;
- unused.erase(3);
- break;
- default:
- NPNR_ASSERT_FALSE("failed to determine LUT permutation");
- }
- break;
- }
- }
- }
- for (int i = 0; i < 4; i++) {
- if (!input_perm.count(i)) {
- NPNR_ASSERT(!unused.empty());
- input_perm[i] = *(unused.begin());
- unused.erase(input_perm[i]);
- }
- }
- lut_init = permute_lut(lut_init, input_perm);
- for (int i = 0; i < 16; i++) {
- if ((lut_init >> i) & 0x1)
- lc.at(lut_perm.at(i)) = true;
- }
- lc.at(8) = carry_enable;
- lc.at(9) = dff_enable;
- lc.at(18) = set_noreset;
- lc.at(19) = async_sr;
-
- for (int i = 0; i < 20; i++)
- set_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), lc.at(i), i);
- if (dff_enable)
- set_config(ti, config.at(y).at(x), "NegClk", neg_clk);
-
- bool carry_const = get_param_or_def(cell.second.get(), ctx->id("CIN_CONST"));
- bool carry_set = get_param_or_def(cell.second.get(), ctx->id("CIN_SET"));
- if (carry_const) {
- if (!ctx->force)
- NPNR_ASSERT(z == 0);
- set_config(ti, config.at(y).at(x), "CarryInSet", carry_set);
- }
- } else if (cell.second->type == ctx->id("SB_IO")) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- 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];
- 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 pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
- bool lvds = get_param_str_or_def(cell.second.get(), ctx->id("IO_STANDARD")) == "SB_LVDS_INPUT";
-
- for (int i = 0; i < 6; i++) {
- bool val = (pin_type >> i) & 0x01;
- set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
- }
- set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
- auto ieren = get_ieren(bi, x, y, z);
- int iex, iey, iez;
- std::tie(iex, iey, iez) = ieren;
- NPNR_ASSERT(iez != -1);
-
- bool input_en;
- if (lvds) {
- input_en = false;
- pullup = false;
- } else {
- if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
- (ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
- input_en = true;
- } else {
- input_en = false;
- }
- }
-
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
- set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
- } else {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), input_en);
- set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
- }
-
- if (ctx->args.type == ArchArgs::UP5K) {
- if (iez == 0) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
- } else if (iez == 1) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
- }
- }
-
- if (lvds) {
- NPNR_ASSERT(z == 0);
- set_config(ti, config.at(y).at(x), "IoCtrl.LVDS", true);
- // Set comp IO config
- auto comp_ieren = get_ieren(bi, x, y, 1);
- int ciex, ciey, ciez;
- std::tie(ciex, ciey, ciez) = comp_ieren;
-
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), !input_en);
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
- } else {
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), input_en);
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
- }
-
- if (ctx->args.type == ArchArgs::UP5K) {
- if (ciez == 0) {
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_39", !pullup);
- } else if (iez == 1) {
- set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_35", !pullup);
- }
- }
- }
- } else if (cell.second->type == ctx->id("SB_GB")) {
- // no cell config bits
- } else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y;
- const TileInfoPOD &ti_ramt = bi.tiles_nonrouting[TILE_RAMT];
- const TileInfoPOD &ti_ramb = bi.tiles_nonrouting[TILE_RAMB];
- if (!(ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
- set_config(ti_ramb, config.at(y).at(x), "RamConfig.PowerUp", true);
- }
- bool negclk_r = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_R"));
- bool negclk_w = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_W"));
- int write_mode = get_param_or_def(cell.second.get(), ctx->id("WRITE_MODE"));
- int read_mode = get_param_or_def(cell.second.get(), ctx->id("READ_MODE"));
- set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w);
- set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r);
-
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_0", write_mode & 0x1);
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2);
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1);
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2);
- } else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) {
- // No config needed
- } else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y, z = beli.z;
- NPNR_ASSERT(ctx->args.type == ArchArgs::UP5K);
- if (x == 0 && y == 0) {
- const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
- if (z == 1) {
- set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_0", true);
- } else if (z == 2) {
- set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_1", true);
- } else {
- NPNR_ASSERT(false);
- }
- } else if (x == 25 && y == 0) {
- const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
- if (z == 3) {
- set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_0", true);
- } else if (z == 4) {
- set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_1", true);
- } else {
- NPNR_ASSERT(false);
- }
- }
- } else if (cell.second->type == ctx->id("ICESTORM_DSP")) {
- const std::vector<std::pair<std::string, int>> mac16_params = {{"C_REG", 1},
- {"A_REG", 1},
- {"B_REG", 1},
- {"D_REG", 1},
- {"TOP_8x8_MULT_REG", 1},
- {"BOT_8x8_MULT_REG", 1},
- {"PIPELINE_16x16_MULT_REG1", 1},
- {"PIPELINE_16x16_MULT_REG2", 1},
- {"TOPOUTPUT_SELECT", 2},
- {"TOPADDSUB_LOWERINPUT", 2},
- {"TOPADDSUB_UPPERINPUT", 1},
- {"TOPADDSUB_CARRYSELECT", 2},
- {"BOTOUTPUT_SELECT", 2},
- {"BOTADDSUB_LOWERINPUT", 2},
- {"BOTADDSUB_UPPERINPUT", 1},
- {"BOTADDSUB_CARRYSELECT", 2},
- {"MODE_8x8", 1},
- {"A_SIGNED", 1},
- {"B_SIGNED", 1}};
- configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig."));
- } else if (cell.second->type == ctx->id("ICESTORM_HFOSC")) {
- const std::vector<std::pair<std::string, int>> hfosc_params = {{"CLKHF_DIV", 2}, {"TRIM_EN", 1}};
- configure_extra_cell(config, ctx, cell.second.get(), hfosc_params, true, 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, port.second.name);
- BelId io_bel;
- for (auto pin : ctx->getWireBelPins(wire)) {
- if (pin.pin == id_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 {
- NPNR_ASSERT(false);
- }
- }
- // Set config bits in unused IO and RAM
- for (auto bel : ctx->getBels()) {
- if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_SB_IO) {
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- 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);
- int iex, iey, iez;
- std::tie(iex, iey, iez) = ieren;
- if (iez != -1) {
- // IO is not actually unused if part of an LVDS pair
- if (z == 1) {
- BelId lvds0 = ctx->getBelByLocation(Loc{x, y, 0});
- const CellInfo *lvds0cell = ctx->getBoundBelCell(lvds0);
- if (lvds0cell != nullptr && lvds0cell->ioInfo.lvds)
- continue;
- }
- 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);
- }
- } else if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_ICESTORM_RAM) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y;
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_RAMB];
- if ((ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
- set_config(ti, config.at(y).at(x), "RamConfig.PowerUp", true);
- }
- }
- }
-
- // Set other config bits
- for (int y = 0; y < ci.height; y++) {
- for (int x = 0; x < ci.width; x++) {
- TileType tile = tile_at(ctx, x, y);
- const TileInfoPOD &ti = bi.tiles_nonrouting[tile];
-
- // set all ColBufCtrl bits (FIXME)
- bool setColBufCtrl = true;
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- if (tile == TILE_RAMB || tile == TILE_RAMT) {
- setColBufCtrl = (y == 3 || y == 5 || y == 11 || y == 13);
- } else {
- setColBufCtrl = (y == 4 || y == 5 || y == 12 || y == 13);
- }
- } else if (ctx->args.type == ArchArgs::LP8K || ctx->args.type == ArchArgs::HX8K) {
- setColBufCtrl = (y == 8 || y == 9 || y == 24 || y == 25);
- } else if (ctx->args.type == ArchArgs::UP5K) {
- setColBufCtrl = (y == 4 || y == 5 || y == 14 || y == 15 || y == 26 || y == 27);
- } else if (ctx->args.type == ArchArgs::LP384) {
- setColBufCtrl = false;
- }
- if (setColBufCtrl) {
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_0", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_1", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_2", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_3", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_4", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_5", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_6", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_7", true);
- }
-
- // Weird UltraPlus bits
- if (tile == TILE_DSP0 || tile == TILE_DSP1 || tile == TILE_DSP2 || tile == TILE_DSP3 ||
- tile == TILE_IPCON) {
- if (ctx->args.type == ArchArgs::UP5K && x == 25 && y == 14) {
- // Mystery bits not set in this one tile
- } else {
- for (int lc_idx = 0; lc_idx < 8; lc_idx++) {
- static const std::vector<int> ip_dsp_lut_perm = {
- 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
- };
- for (int i = 0; i < 16; i++)
- set_config(ti, config.at(y).at(x), "LC_" + std::to_string(lc_idx), ((i % 8) >= 4),
- ip_dsp_lut_perm.at(i));
- if (tile == TILE_IPCON)
- set_config(ti, config.at(y).at(x),
- "Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
- else
- set_config(ti, config.at(y).at(x),
- "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
- std::to_string(lc_idx) + "_inmux02_5",
- true);
- }
- }
- }
- }
- }
-
- // Write config out
- for (int y = 0; y < ci.height; y++) {
- for (int x = 0; x < ci.width; x++) {
- TileType tile = tile_at(ctx, x, y);
- if (tile == TILE_NONE)
- continue;
- out << tagTileType(tile);
- out << " " << x << " " << y << std::endl;
- for (auto row : config.at(y).at(x)) {
- for (auto col : row) {
- if (col == 1)
- out << "1";
- else
- out << "0";
- }
- out << std::endl;
- }
- out << std::endl;
- }
- }
-
- // Write RAM init data
- for (auto &cell : ctx->cells) {
- if (cell.second->bel != BelId()) {
- if (cell.second->type == ctx->id("ICESTORM_RAM")) {
- const BelInfoPOD &beli = ci.bel_data[cell.second->bel.index];
- int x = beli.x, y = beli.y;
- out << ".ram_data " << x << " " << y << std::endl;
- for (int w = 0; w < 16; w++) {
- std::vector<bool> bits(256);
- std::string init =
- get_param_str_or_def(cell.second.get(), ctx->id(std::string("INIT_") + get_hexdigit(w)));
- for (size_t i = 0; i < init.size(); i++) {
- bool val = (init.at((init.size() - 1) - i) == '1');
- bits.at(i) = val;
- }
- for (int i = bits.size() - 4; i >= 0; i -= 4) {
- int c = bits.at(i) + (bits.at(i + 1) << 1) + (bits.at(i + 2) << 2) + (bits.at(i + 3) << 3);
- out << char(std::tolower(get_hexdigit(c)));
- }
- out << std::endl;
- }
- out << std::endl;
- }
- }
- }
-
- // Write symbols
- // const bool write_symbols = 1;
- for (auto wire : ctx->getWires()) {
- NetInfo *net = ctx->getBoundWireNet(wire);
- if (net != nullptr)
- out << ".sym " << wire.index << " " << net->name.str(ctx) << std::endl;
- }
-}
-
-void read_config(Context *ctx, std::istream &in, chipconfig_t &config)
-{
- constexpr size_t line_buf_size = 65536;
- char buffer[line_buf_size];
- int tile_x = -1, tile_y = -1, line_nr = -1;
-
- while (1) {
- in.getline(buffer, line_buf_size);
- if (buffer[0] == '.') {
- line_nr = -1;
- const char *tok = strtok(buffer, " \t\r\n");
-
- if (!strcmp(tok, ".device")) {
- std::string config_device = strtok(nullptr, " \t\r\n");
- std::string expected;
- switch (ctx->args.type) {
- case ArchArgs::LP384:
- expected = "384";
- break;
- case ArchArgs::HX1K:
- case ArchArgs::LP1K:
- expected = "1k";
- break;
- case ArchArgs::HX8K:
- case ArchArgs::LP8K:
- expected = "8k";
- break;
- case ArchArgs::UP5K:
- expected = "5k";
- break;
- default:
- log_error("unsupported device type\n");
- }
- if (expected != config_device)
- log_error("device type does not match\n");
- } else if (!strcmp(tok, ".io_tile") || !strcmp(tok, ".logic_tile") || !strcmp(tok, ".ramb_tile") ||
- !strcmp(tok, ".ramt_tile") || !strcmp(tok, ".ipcon_tile") || !strcmp(tok, ".dsp0_tile") ||
- !strcmp(tok, ".dsp1_tile") || !strcmp(tok, ".dsp2_tile") || !strcmp(tok, ".dsp3_tile")) {
- line_nr = 0;
- tile_x = atoi(strtok(nullptr, " \t\r\n"));
- tile_y = atoi(strtok(nullptr, " \t\r\n"));
-
- TileType tile = tile_at(ctx, tile_x, tile_y);
- if (tok != tagTileType(tile))
- log_error("Wrong tile type for specified position\n");
-
- } else if (!strcmp(tok, ".extra_bit")) {
- /*
- int b = atoi(strtok(nullptr, " \t\r\n"));
- int x = atoi(strtok(nullptr, " \t\r\n"));
- int y = atoi(strtok(nullptr, " \t\r\n"));
- std::tuple<int, int, int> key(b, x, y);
- extra_bits.insert(key);
- */
- } else if (!strcmp(tok, ".sym")) {
- int wireIndex = atoi(strtok(nullptr, " \t\r\n"));
- const char *name = strtok(nullptr, " \t\r\n");
-
- IdString netName = ctx->id(name);
-
- if (ctx->nets.find(netName) == ctx->nets.end()) {
- std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
- created_net->name = netName;
- ctx->nets[netName] = std::move(created_net);
- }
-
- WireId wire;
- wire.index = wireIndex;
- ctx->bindWire(wire, ctx->nets.at(netName).get(), STRENGTH_WEAK);
- }
- } else if (line_nr >= 0 && strlen(buffer) > 0) {
- if (line_nr > int(config.at(tile_y).at(tile_x).size() - 1))
- log_error("Invalid data in input asc file");
- for (int i = 0; buffer[i] == '0' || buffer[i] == '1'; i++)
- config.at(tile_y).at(tile_x).at(line_nr).at(i) = (buffer[i] == '1') ? 1 : 0;
- line_nr++;
- }
- if (in.eof())
- break;
- }
-}
-
-bool read_asc(Context *ctx, std::istream &in)
-{
- try {
- // [y][x][row][col]
- const ChipInfoPOD &ci = *ctx->chip_info;
- const BitstreamInfoPOD &bi = *ci.bits_info;
- chipconfig_t config;
- config.resize(ci.height);
- for (int y = 0; y < ci.height; y++) {
- config.at(y).resize(ci.width);
- for (int x = 0; x < ci.width; x++) {
- TileType tile = tile_at(ctx, x, y);
- int rows = bi.tiles_nonrouting[tile].rows;
- int cols = bi.tiles_nonrouting[tile].cols;
- config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
- }
- }
- read_config(ctx, in, config);
-
- // Set pips
- for (auto pip : ctx->getPips()) {
- const PipInfoPOD &pi = ci.pip_data[pip.index];
- const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
- bool isUsed = true;
- for (int i = 0; i < swi.num_bits; i++) {
- bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
- int8_t cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
- isUsed &= !(bool(cbit) ^ val);
- }
- if (isUsed) {
- NetInfo *net = ctx->wire_to_net[pi.dst];
- if (net != nullptr) {
- WireId wire;
- wire.index = pi.dst;
- ctx->unbindWire(wire);
- ctx->bindPip(pip, net, STRENGTH_WEAK);
- }
- }
- }
- for (auto bel : ctx->getBels()) {
- if (ctx->getBelType(bel) == id_ICESTORM_LC) {
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y, z = beli.z;
- std::vector<bool> lc(20, false);
- bool isUsed = false;
- for (int i = 0; i < 20; i++) {
- lc.at(i) = get_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), i);
- isUsed |= lc.at(i);
- }
- bool neg_clk = get_config(ti, config.at(y).at(x), "NegClk");
- isUsed |= neg_clk;
- bool carry_set = get_config(ti, config.at(y).at(x), "CarryInSet");
- isUsed |= carry_set;
-
- if (isUsed) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets and assign values of properties
- }
- }
- if (ctx->getBelType(bel) == id_SB_IO) {
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y, z = beli.z;
- bool isUsed = false;
- for (int i = 0; i < 6; i++) {
- isUsed |= get_config(ti, config.at(y).at(x),
- "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i));
- }
- bool neg_trigger = get_config(ti, config.at(y).at(x), "NegClk");
- isUsed |= neg_trigger;
-
- if (isUsed) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets and assign values of properties
- }
- }
- }
- // Add cells that are without change in initial state of configuration
- for (auto &net : ctx->nets) {
- for (auto w : net.second->wires) {
- if (w.second.pip == PipId()) {
- WireId wire = w.first;
- for (auto belpin : ctx->getWireBelPins(wire)) {
-
- if (ctx->checkBelAvail(belpin.bel)) {
- if (ctx->getBelType(belpin.bel) == id_ICESTORM_LC) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- if (ctx->getBelType(belpin.bel) == id_SB_IO) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- if (ctx->getBelType(belpin.bel) == id_SB_GB) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_GB"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- if (ctx->getBelType(belpin.bel) == id_SB_WARMBOOT) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_WARMBOOT"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- if (ctx->getBelType(belpin.bel) == id_ICESTORM_LFOSC) {
- std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"));
- IdString name = created->name;
- ctx->cells[name] = std::move(created);
- ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
- // TODO: Add port mapping to nets
- }
- }
- }
- }
- }
- }
- for (auto &cell : ctx->cells) {
- if (cell.second->bel != BelId()) {
- for (auto &port : cell.second->ports) {
- IdString pin = port.first;
- WireId wire = ctx->getBelPinWire(cell.second->bel, pin);
- if (wire != WireId()) {
- NetInfo *net = ctx->getBoundWireNet(wire);
- if (net != nullptr) {
- port.second.net = net;
- PortRef ref;
- ref.cell = cell.second.get();
- ref.port = port.second.name;
-
- if (port.second.type == PORT_OUT)
- net->driver = ref;
- else
- net->users.push_back(ref);
- }
- }
- }
- }
- }
- return true;
- } catch (log_execution_error_exception) {
- return false;
- }
-}
-NEXTPNR_NAMESPACE_END
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@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
+ * 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 "bitstream.h"
+#include <cctype>
+#include <vector>
+#include "cells.h"
+#include "log.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+inline TileType tile_at(const Context *ctx, int x, int y)
+{
+ return ctx->chip_info->tile_grid[y * ctx->chip_info->width + x];
+}
+
+const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &name)
+{
+ for (int i = 0; i < tile.num_config_entries; i++) {
+ if (std::string(tile.entries[i].name.get()) == name) {
+ return tile.entries[i];
+ }
+ }
+ NPNR_ASSERT_FALSE_STR("unable to find config bit " + name);
+}
+
+std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t x, int8_t y, int8_t z)
+{
+ for (int i = 0; i < bi.num_ierens; i++) {
+ auto ie = bi.ierens[i];
+ if (ie.iox == x && ie.ioy == y && ie.ioz == z) {
+ return std::make_tuple(ie.ierx, ie.iery, ie.ierz);
+ }
+ }
+ // No pin at this location
+ return std::make_tuple(-1, -1, -1);
+};
+
+bool get_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name,
+ int index = -1)
+{
+ const ConfigEntryPOD &cfg = find_config(ti, name);
+ if (index == -1) {
+ for (int i = 0; i < cfg.num_bits; i++) {
+ return tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
+ }
+ } else {
+ return tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
+ }
+ return false;
+}
+
+void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name, bool value,
+ int index = -1)
+{
+ const ConfigEntryPOD &cfg = find_config(ti, name);
+ if (index == -1) {
+ for (int i = 0; i < cfg.num_bits; i++) {
+ int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
+ if (cbit && !value)
+ log_error("clearing already set config bit %s\n", name.c_str());
+ cbit = value;
+ }
+ } else {
+ int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
+ cbit = value;
+ if (cbit && !value)
+ 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);
+ }
+}
+
+int get_param_or_def(const CellInfo *cell, const IdString param, int defval = 0)
+{
+ auto found = cell->params.find(param);
+ if (found != cell->params.end())
+ return std::stoi(found->second);
+ else
+ return defval;
+}
+
+std::string get_param_str_or_def(const CellInfo *cell, const IdString param, std::string defval = "")
+{
+ auto found = cell->params.find(param);
+ if (found != cell->params.end())
+ return found->second;
+ else
+ return defval;
+}
+
+char get_hexdigit(int i) { return std::string("0123456789ABCDEF").at(i); }
+
+static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
+{
+ for (int i = 0; i < chip->num_belcfgs; i++) {
+ if (chip->bel_config[i].bel_index == bel.index)
+ return chip->bel_config[i];
+ }
+ NPNR_ASSERT_FALSE("failed to find bel config");
+}
+
+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,
+ bool value, std::string prefix)
+{
+ const ChipInfoPOD *chip = ctx->chip_info;
+
+ for (int i = 0; i < cell_cbits.num_entries; i++) {
+ const auto &cbit = cell_cbits.entries[i];
+ if (cbit.entry_name.get() == name) {
+ 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), prefix + cbit.cbit_name.get(), value);
+ return;
+ }
+ }
+ NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name);
+}
+
+void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
+ const std::vector<std::pair<std::string, int>> &params, bool string_style, std::string prefix)
+{
+ const ChipInfoPOD *chip = ctx->chip_info;
+ const auto &bc = get_ec_config(chip, cell->bel);
+ for (auto p : params) {
+ std::vector<bool> value;
+ if (string_style) {
+ // Lattice's weird string style params, not sure if
+ // prefixes other than 0b should be supported, only 0b features in docs
+ std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
+ assert(raw.substr(0, 2) == "0b");
+ raw = raw.substr(2);
+ value.resize(raw.length());
+ for (int i = 0; i < (int)raw.length(); i++) {
+ if (raw[i] == '1') {
+ value[(raw.length() - 1) - i] = 1;
+ } else {
+ assert(raw[i] == '0');
+ value[(raw.length() - 1) - i] = 0;
+ }
+ }
+ } else {
+ int ival = get_param_or_def(cell, ctx->id(p.first), 0);
+
+ for (int i = 0; i < p.second; i++)
+ value.push_back((ival >> i) & 0x1);
+ }
+
+ value.resize(p.second);
+ if (p.second == 1) {
+ set_ec_cbit(config, ctx, bc, p.first, value.at(0), prefix);
+ } else {
+ for (int i = 0; i < p.second; i++) {
+ set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix);
+ }
+ }
+ }
+}
+
+std::string tagTileType(TileType &tile)
+{
+ if (tile == TILE_NONE)
+ return "";
+ switch (tile) {
+ case TILE_LOGIC:
+ return ".logic_tile";
+ break;
+ case TILE_IO:
+ return ".io_tile";
+ break;
+ case TILE_RAMB:
+ return ".ramb_tile";
+ break;
+ case TILE_RAMT:
+ return ".ramt_tile";
+ break;
+ case TILE_DSP0:
+ return ".dsp0_tile";
+ break;
+ case TILE_DSP1:
+ return ".dsp1_tile";
+ break;
+ case TILE_DSP2:
+ return ".dsp2_tile";
+ break;
+ case TILE_DSP3:
+ return ".dsp3_tile";
+ break;
+ case TILE_IPCON:
+ return ".ipcon_tile";
+ break;
+ default:
+ NPNR_ASSERT(false);
+ }
+}
+
+static BelPin get_one_bel_pin(const Context *ctx, WireId wire)
+{
+ auto pins = ctx->getWireBelPins(wire);
+ NPNR_ASSERT(pins.begin() != pins.end());
+ return *pins.begin();
+}
+
+// Permute LUT init value given map (LUT input -> ext input)
+unsigned permute_lut(unsigned orig_init, const std::unordered_map<int, int> &input_permute)
+{
+ unsigned new_init = 0;
+
+ for (int i = 0; i < 16; i++) {
+ int permute_address = 0;
+ for (int j = 0; j < 4; j++) {
+ if ((i >> j) & 0x1)
+ permute_address |= (1 << input_permute.at(j));
+ }
+ if ((orig_init >> i) & 0x1) {
+ new_init |= (1 << permute_address);
+ }
+ }
+
+ return new_init;
+}
+
+void write_asc(const Context *ctx, std::ostream &out)
+{
+
+ static const std::vector<int> lut_perm = {
+ 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
+ };
+
+ // [y][x][row][col]
+ const ChipInfoPOD &ci = *ctx->chip_info;
+ const BitstreamInfoPOD &bi = *ci.bits_info;
+ chipconfig_t config;
+ config.resize(ci.height);
+ for (int y = 0; y < ci.height; y++) {
+ config.at(y).resize(ci.width);
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ int rows = bi.tiles_nonrouting[tile].rows;
+ int cols = bi.tiles_nonrouting[tile].cols;
+ config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
+ }
+ }
+
+ std::vector<std::tuple<int, int, int>> extra_bits;
+
+ out << ".comment from next-pnr" << std::endl;
+
+ switch (ctx->args.type) {
+ case ArchArgs::LP384:
+ out << ".device 384" << std::endl;
+ break;
+ case ArchArgs::HX1K:
+ case ArchArgs::LP1K:
+ out << ".device 1k" << std::endl;
+ break;
+ case ArchArgs::HX8K:
+ case ArchArgs::LP8K:
+ out << ".device 8k" << std::endl;
+ break;
+ case ArchArgs::UP5K:
+ out << ".device 5k" << std::endl;
+ break;
+ default:
+ NPNR_ASSERT_FALSE("unsupported device type\n");
+ }
+ // Set pips
+ for (auto pip : ctx->getPips()) {
+ if (ctx->pip_to_net[pip.index] != nullptr) {
+ const PipInfoPOD &pi = ci.pip_data[pip.index];
+ const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
+ int sw_bel_idx = swi.bel;
+ if (sw_bel_idx >= 0) {
+ const BelInfoPOD &beli = ci.bel_data[sw_bel_idx];
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
+ BelId sw_bel;
+ sw_bel.index = sw_bel_idx;
+ NPNR_ASSERT(ctx->getBelType(sw_bel) == id_ICESTORM_LC);
+
+ if (ci.wire_data[ctx->getPipDstWire(pip).index].type == WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT)
+ continue; // Permutation pips
+ BelPin output = get_one_bel_pin(ctx, ctx->getPipDstWire(pip));
+ NPNR_ASSERT(output.bel == sw_bel && output.pin == id_O);
+ unsigned lut_init;
+
+ WireId permWire;
+ for (auto permPip : ctx->getPipsUphill(ctx->getPipSrcWire(pip))) {
+ if (ctx->getBoundPipNet(permPip) != nullptr) {
+ permWire = ctx->getPipSrcWire(permPip);
+ }
+ }
+ NPNR_ASSERT(permWire != WireId());
+ std::string dName = ci.wire_data[permWire.index].name.get();
+
+ switch (dName.back()) {
+ case '0':
+ lut_init = 2;
+ break;
+ case '1':
+ lut_init = 4;
+ break;
+ case '2':
+ lut_init = 16;
+ break;
+ case '3':
+ lut_init = 256;
+ break;
+ default:
+ NPNR_ASSERT_FALSE("bad feedthru LUT input");
+ }
+ std::vector<bool> lc(20, false);
+ for (int i = 0; i < 16; i++) {
+ if ((lut_init >> i) & 0x1)
+ lc.at(lut_perm.at(i)) = true;
+ }
+
+ for (int i = 0; i < 20; i++)
+ set_config(ti, config.at(beli.y).at(beli.x), "LC_" + std::to_string(beli.z), lc.at(i), i);
+ } else {
+ for (int i = 0; i < swi.num_bits; i++) {
+ bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
+ int8_t &cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
+ if (bool(cbit) != 0)
+ NPNR_ASSERT(false);
+ cbit = val;
+ }
+ }
+ }
+ }
+
+ // Scan for PLL and collects the affected SB_IOs
+ std::unordered_set<Loc> sb_io_used_by_pll_out;
+ std::unordered_set<Loc> sb_io_used_by_pll_pad;
+
+ for (auto &cell : ctx->cells) {
+ if (cell.second->type != ctx->id("ICESTORM_PLL"))
+ continue;
+
+ // Collect all locations matching an PLL output port
+ // note: It doesn't matter if the port is connected or not, or if fabric/global
+ // is used. As long as it's a PLL type for which the port exists, the SB_IO
+ // is not available and must be configured for PLL mode
+ const std::vector<IdString> ports = {id_PLLOUT_A, id_PLLOUT_B};
+ for (auto &port : ports) {
+ // If the output is not enabled in this mode, ignore it
+ if (port == id_PLLOUT_B && !is_sb_pll40_dual(ctx, cell.second.get()))
+ 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, port);
+ BelId io_bel;
+ for (auto pin : ctx->getWireBelPins(wire)) {
+ if (pin.pin == id_D_IN_0) {
+ io_bel = pin.bel;
+ break;
+ }
+ }
+ NPNR_ASSERT(io_bel.index != -1);
+ auto io_bel_loc = ctx->getBelLocation(io_bel);
+
+ // Mark this SB_IO as being used by a PLL output path
+ sb_io_used_by_pll_out.insert(io_bel_loc);
+
+ // If this is a PAD PLL, and this is the 'PLLOUT_A' port, then the same SB_IO is also PAD
+ if (port == id_PLLOUT_A && is_sb_pll40_pad(ctx, cell.second.get()))
+ sb_io_used_by_pll_pad.insert(io_bel_loc);
+ }
+ }
+
+ // Set logic cell config
+ for (auto &cell : ctx->cells) {
+
+ BelId bel = cell.second.get()->bel;
+ if (bel == BelId()) {
+ std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
+ continue;
+ }
+ if (cell.second->type == ctx->id("ICESTORM_LC")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
+ unsigned lut_init = get_param_or_def(cell.second.get(), ctx->id("LUT_INIT"));
+ bool neg_clk = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK"));
+ bool dff_enable = get_param_or_def(cell.second.get(), ctx->id("DFF_ENABLE"));
+ bool async_sr = get_param_or_def(cell.second.get(), ctx->id("ASYNC_SR"));
+ bool set_noreset = get_param_or_def(cell.second.get(), ctx->id("SET_NORESET"));
+ bool carry_enable = get_param_or_def(cell.second.get(), ctx->id("CARRY_ENABLE"));
+ std::vector<bool> lc(20, false);
+
+ // Discover permutation
+ std::unordered_map<int, int> input_perm;
+ std::set<int> unused;
+ for (int i = 0; i < 4; i++)
+ unused.insert(i);
+ for (int i = 0; i < 4; i++) {
+ WireId lut_wire = ctx->getBelPinWire(bel, IdString(ID_I0 + i));
+ for (auto pip : ctx->getPipsUphill(lut_wire)) {
+ if (ctx->getBoundPipNet(pip) != nullptr) {
+ std::string name = ci.wire_data[ctx->getPipSrcWire(pip).index].name.get();
+ switch (name.back()) {
+ case '0':
+ input_perm[i] = 0;
+ unused.erase(0);
+ break;
+ case '1':
+ input_perm[i] = 1;
+ unused.erase(1);
+ break;
+ case '2':
+ input_perm[i] = 2;
+ unused.erase(2);
+ break;
+ case '3':
+ input_perm[i] = 3;
+ unused.erase(3);
+ break;
+ default:
+ NPNR_ASSERT_FALSE("failed to determine LUT permutation");
+ }
+ break;
+ }
+ }
+ }
+ for (int i = 0; i < 4; i++) {
+ if (!input_perm.count(i)) {
+ NPNR_ASSERT(!unused.empty());
+ input_perm[i] = *(unused.begin());
+ unused.erase(input_perm[i]);
+ }
+ }
+ lut_init = permute_lut(lut_init, input_perm);
+ for (int i = 0; i < 16; i++) {
+ if ((lut_init >> i) & 0x1)
+ lc.at(lut_perm.at(i)) = true;
+ }
+ lc.at(8) = carry_enable;
+ lc.at(9) = dff_enable;
+ lc.at(18) = set_noreset;
+ lc.at(19) = async_sr;
+
+ for (int i = 0; i < 20; i++)
+ set_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), lc.at(i), i);
+ if (dff_enable)
+ set_config(ti, config.at(y).at(x), "NegClk", neg_clk);
+
+ bool carry_const = get_param_or_def(cell.second.get(), ctx->id("CIN_CONST"));
+ bool carry_set = get_param_or_def(cell.second.get(), ctx->id("CIN_SET"));
+ if (carry_const) {
+ if (!ctx->force)
+ NPNR_ASSERT(z == 0);
+ set_config(ti, config.at(y).at(x), "CarryInSet", carry_set);
+ }
+ } else if (cell.second->type == ctx->id("SB_IO")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
+ 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 pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
+ bool lvds = get_param_str_or_def(cell.second.get(), ctx->id("IO_STANDARD")) == "SB_LVDS_INPUT";
+ bool used_by_pll_out = sb_io_used_by_pll_out.count(Loc(x, y, z)) > 0;
+ bool used_by_pll_pad = sb_io_used_by_pll_pad.count(Loc(x, y, z)) > 0;
+
+ for (int i = used_by_pll_out ? 2 : 0; i < 6; i++) {
+ bool val = (pin_type >> i) & 0x01;
+ set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
+ }
+ set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
+ auto ieren = get_ieren(bi, x, y, z);
+ int iex, iey, iez;
+ std::tie(iex, iey, iez) = ieren;
+ NPNR_ASSERT(iez != -1);
+
+ bool input_en;
+ if (lvds) {
+ input_en = false;
+ pullup = false;
+ } else {
+ if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
+ (ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
+ input_en = true;
+ } else {
+ input_en = false;
+ }
+ }
+
+ input_en = (input_en & !used_by_pll_out) | used_by_pll_pad;
+ input_en |= cell.second->ioInfo.global;
+
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
+ } else {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), input_en);
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
+ }
+
+ if (ctx->args.type == ArchArgs::UP5K) {
+ if (iez == 0) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
+ } else if (iez == 1) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
+ }
+ }
+
+ if (lvds) {
+ NPNR_ASSERT(z == 0);
+ set_config(ti, config.at(y).at(x), "IoCtrl.LVDS", true);
+ // Set comp IO config
+ auto comp_ieren = get_ieren(bi, x, y, 1);
+ int ciex, ciey, ciez;
+ std::tie(ciex, ciey, ciez) = comp_ieren;
+
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), !input_en);
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
+ } else {
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), input_en);
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
+ }
+
+ if (ctx->args.type == ArchArgs::UP5K) {
+ if (ciez == 0) {
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_39", !pullup);
+ } else if (iez == 1) {
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_35", !pullup);
+ }
+ }
+ }
+ } else if (cell.second->type == ctx->id("SB_GB")) {
+ if (cell.second->gbInfo.forPadIn) {
+ Loc gb_loc = ctx->getBelLocation(bel);
+ for (int i = 0; i < ci.num_global_networks; i++) {
+ if ((gb_loc.x == ci.global_network_info[i].gb_x) && (gb_loc.y == ci.global_network_info[i].gb_y)) {
+ extra_bits.push_back(std::make_tuple(ci.global_network_info[i].pi_eb_bank,
+ ci.global_network_info[i].pi_eb_x,
+ ci.global_network_info[i].pi_eb_y));
+ }
+ }
+ }
+ } else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y;
+ const TileInfoPOD &ti_ramt = bi.tiles_nonrouting[TILE_RAMT];
+ const TileInfoPOD &ti_ramb = bi.tiles_nonrouting[TILE_RAMB];
+ if (!(ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
+ set_config(ti_ramb, config.at(y).at(x), "RamConfig.PowerUp", true);
+ }
+ bool negclk_r = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_R"));
+ bool negclk_w = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_W"));
+ int write_mode = get_param_or_def(cell.second.get(), ctx->id("WRITE_MODE"));
+ int read_mode = get_param_or_def(cell.second.get(), ctx->id("READ_MODE"));
+ set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w);
+ set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r);
+
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_0", write_mode & 0x1);
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2);
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1);
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2);
+ } else if (cell.second->type == ctx->id("SB_RGBA_DRV")) {
+ const std::vector<std::pair<std::string, int>> rgba_params = {
+ {"CURRENT_MODE", 1}, {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}};
+ configure_extra_cell(config, ctx, cell.second.get(), rgba_params, true, std::string("IpConfig."));
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGBA_DRV_EN", true, "IpConfig.");
+ } else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) {
+ // No config needed
+ } else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ NPNR_ASSERT(ctx->args.type == ArchArgs::UP5K);
+ if (x == 0 && y == 0) {
+ const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
+ if (z == 1) {
+ set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_0", true);
+ } else if (z == 2) {
+ set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_1", true);
+ } else {
+ NPNR_ASSERT(false);
+ }
+ } else if (x == 25 && y == 0) {
+ const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
+ if (z == 3) {
+ set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_0", true);
+ } else if (z == 4) {
+ set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_1", true);
+ } else {
+ NPNR_ASSERT(false);
+ }
+ }
+ } else if (cell.second->type == ctx->id("ICESTORM_DSP")) {
+ const std::vector<std::pair<std::string, int>> mac16_params = {{"C_REG", 1},
+ {"A_REG", 1},
+ {"B_REG", 1},
+ {"D_REG", 1},
+ {"TOP_8x8_MULT_REG", 1},
+ {"BOT_8x8_MULT_REG", 1},
+ {"PIPELINE_16x16_MULT_REG1", 1},
+ {"PIPELINE_16x16_MULT_REG2", 1},
+ {"TOPOUTPUT_SELECT", 2},
+ {"TOPADDSUB_LOWERINPUT", 2},
+ {"TOPADDSUB_UPPERINPUT", 1},
+ {"TOPADDSUB_CARRYSELECT", 2},
+ {"BOTOUTPUT_SELECT", 2},
+ {"BOTADDSUB_LOWERINPUT", 2},
+ {"BOTADDSUB_UPPERINPUT", 1},
+ {"BOTADDSUB_CARRYSELECT", 2},
+ {"MODE_8x8", 1},
+ {"A_SIGNED", 1},
+ {"B_SIGNED", 1}};
+ configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig."));
+ } else if (cell.second->type == ctx->id("ICESTORM_HFOSC")) {
+ const std::vector<std::pair<std::string, int>> hfosc_params = {{"CLKHF_DIV", 2}, {"TRIM_EN", 1}};
+ configure_extra_cell(config, ctx, cell.second.get(), hfosc_params, true, 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 &io_bel_loc : sb_io_used_by_pll_out) {
+ // Write config.
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
+
+ // PINTYPE[1:0] == "01" 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_1", false);
+ 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 {
+ NPNR_ASSERT(false);
+ }
+ }
+ // Set config bits in unused IO and RAM
+ for (auto bel : ctx->getBels()) {
+ if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_SB_IO) {
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ if (sb_io_used_by_pll_out.count(Loc(x, y, z))) {
+ continue;
+ }
+
+ auto ieren = get_ieren(bi, x, y, z);
+ int iex, iey, iez;
+ std::tie(iex, iey, iez) = ieren;
+ if (iez != -1) {
+ // IO is not actually unused if part of an LVDS pair
+ if (z == 1) {
+ BelId lvds0 = ctx->getBelByLocation(Loc{x, y, 0});
+ const CellInfo *lvds0cell = ctx->getBoundBelCell(lvds0);
+ if (lvds0cell != nullptr && lvds0cell->ioInfo.lvds)
+ continue;
+ }
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
+ } else {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + 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] == nullptr && ctx->getBelType(bel) == id_ICESTORM_RAM) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y;
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_RAMB];
+ if ((ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
+ set_config(ti, config.at(y).at(x), "RamConfig.PowerUp", true);
+ }
+ }
+ }
+
+ // Set other config bits
+ for (int y = 0; y < ci.height; y++) {
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ const TileInfoPOD &ti = bi.tiles_nonrouting[tile];
+
+ // set all ColBufCtrl bits (FIXME)
+ bool setColBufCtrl = true;
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ if (tile == TILE_RAMB || tile == TILE_RAMT) {
+ setColBufCtrl = (y == 3 || y == 5 || y == 11 || y == 13);
+ } else {
+ setColBufCtrl = (y == 4 || y == 5 || y == 12 || y == 13);
+ }
+ } else if (ctx->args.type == ArchArgs::LP8K || ctx->args.type == ArchArgs::HX8K) {
+ setColBufCtrl = (y == 8 || y == 9 || y == 24 || y == 25);
+ } else if (ctx->args.type == ArchArgs::UP5K) {
+ setColBufCtrl = (y == 4 || y == 5 || y == 14 || y == 15 || y == 26 || y == 27);
+ } else if (ctx->args.type == ArchArgs::LP384) {
+ setColBufCtrl = false;
+ }
+ if (setColBufCtrl) {
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_0", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_1", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_2", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_3", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_4", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_5", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_6", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_7", true);
+ }
+
+ // Weird UltraPlus bits
+ if (tile == TILE_DSP0 || tile == TILE_DSP1 || tile == TILE_DSP2 || tile == TILE_DSP3 ||
+ tile == TILE_IPCON) {
+ if (ctx->args.type == ArchArgs::UP5K && x == 25 && y == 14) {
+ // Mystery bits not set in this one tile
+ } else {
+ for (int lc_idx = 0; lc_idx < 8; lc_idx++) {
+ static const std::vector<int> ip_dsp_lut_perm = {
+ 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
+ };
+ for (int i = 0; i < 16; i++)
+ set_config(ti, config.at(y).at(x), "LC_" + std::to_string(lc_idx), ((i % 8) >= 4),
+ ip_dsp_lut_perm.at(i));
+ if (tile == TILE_IPCON)
+ set_config(ti, config.at(y).at(x),
+ "Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
+ else
+ set_config(ti, config.at(y).at(x),
+ "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
+ std::to_string(lc_idx) + "_inmux02_5",
+ true);
+ }
+ }
+ }
+ }
+ }
+
+ // Write config out
+ for (int y = 0; y < ci.height; y++) {
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ if (tile == TILE_NONE)
+ continue;
+ out << tagTileType(tile);
+ out << " " << x << " " << y << std::endl;
+ for (auto row : config.at(y).at(x)) {
+ for (auto col : row) {
+ if (col == 1)
+ out << "1";
+ else
+ out << "0";
+ }
+ out << std::endl;
+ }
+ out << std::endl;
+ }
+ }
+
+ // Write RAM init data
+ for (auto &cell : ctx->cells) {
+ if (cell.second->bel != BelId()) {
+ if (cell.second->type == ctx->id("ICESTORM_RAM")) {
+ const BelInfoPOD &beli = ci.bel_data[cell.second->bel.index];
+ int x = beli.x, y = beli.y;
+ out << ".ram_data " << x << " " << y << std::endl;
+ for (int w = 0; w < 16; w++) {
+ std::vector<bool> bits(256);
+ std::string init =
+ get_param_str_or_def(cell.second.get(), ctx->id(std::string("INIT_") + get_hexdigit(w)));
+ for (size_t i = 0; i < init.size(); i++) {
+ bool val = (init.at((init.size() - 1) - i) == '1');
+ bits.at(i) = val;
+ }
+ for (int i = bits.size() - 4; i >= 0; i -= 4) {
+ int c = bits.at(i) + (bits.at(i + 1) << 1) + (bits.at(i + 2) << 2) + (bits.at(i + 3) << 3);
+ out << char(std::tolower(get_hexdigit(c)));
+ }
+ out << std::endl;
+ }
+ out << std::endl;
+ }
+ }
+ }
+
+ // Write extra-bits
+ for (auto eb : extra_bits)
+ out << ".extra_bit " << std::get<0>(eb) << " " << std::get<1>(eb) << " " << std::get<2>(eb) << std::endl;
+
+ // Write symbols
+ // const bool write_symbols = 1;
+ for (auto wire : ctx->getWires()) {
+ NetInfo *net = ctx->getBoundWireNet(wire);
+ if (net != nullptr)
+ out << ".sym " << wire.index << " " << net->name.str(ctx) << std::endl;
+ }
+}
+
+void read_config(Context *ctx, std::istream &in, chipconfig_t &config)
+{
+ constexpr size_t line_buf_size = 65536;
+ char buffer[line_buf_size];
+ int tile_x = -1, tile_y = -1, line_nr = -1;
+
+ while (1) {
+ in.getline(buffer, line_buf_size);
+ if (buffer[0] == '.') {
+ line_nr = -1;
+ const char *tok = strtok(buffer, " \t\r\n");
+
+ if (!strcmp(tok, ".device")) {
+ std::string config_device = strtok(nullptr, " \t\r\n");
+ std::string expected;
+ switch (ctx->args.type) {
+ case ArchArgs::LP384:
+ expected = "384";
+ break;
+ case ArchArgs::HX1K:
+ case ArchArgs::LP1K:
+ expected = "1k";
+ break;
+ case ArchArgs::HX8K:
+ case ArchArgs::LP8K:
+ expected = "8k";
+ break;
+ case ArchArgs::UP5K:
+ expected = "5k";
+ break;
+ default:
+ log_error("unsupported device type\n");
+ }
+ if (expected != config_device)
+ log_error("device type does not match\n");
+ } else if (!strcmp(tok, ".io_tile") || !strcmp(tok, ".logic_tile") || !strcmp(tok, ".ramb_tile") ||
+ !strcmp(tok, ".ramt_tile") || !strcmp(tok, ".ipcon_tile") || !strcmp(tok, ".dsp0_tile") ||
+ !strcmp(tok, ".dsp1_tile") || !strcmp(tok, ".dsp2_tile") || !strcmp(tok, ".dsp3_tile")) {
+ line_nr = 0;
+ tile_x = atoi(strtok(nullptr, " \t\r\n"));
+ tile_y = atoi(strtok(nullptr, " \t\r\n"));
+
+ TileType tile = tile_at(ctx, tile_x, tile_y);
+ if (tok != tagTileType(tile))
+ log_error("Wrong tile type for specified position\n");
+
+ } else if (!strcmp(tok, ".extra_bit")) {
+ /*
+ int b = atoi(strtok(nullptr, " \t\r\n"));
+ int x = atoi(strtok(nullptr, " \t\r\n"));
+ int y = atoi(strtok(nullptr, " \t\r\n"));
+ std::tuple<int, int, int> key(b, x, y);
+ extra_bits.insert(key);
+ */
+ } else if (!strcmp(tok, ".sym")) {
+ int wireIndex = atoi(strtok(nullptr, " \t\r\n"));
+ const char *name = strtok(nullptr, " \t\r\n");
+
+ IdString netName = ctx->id(name);
+
+ if (ctx->nets.find(netName) == ctx->nets.end()) {
+ std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
+ created_net->name = netName;
+ ctx->nets[netName] = std::move(created_net);
+ }
+
+ WireId wire;
+ wire.index = wireIndex;
+ ctx->bindWire(wire, ctx->nets.at(netName).get(), STRENGTH_WEAK);
+ }
+ } else if (line_nr >= 0 && strlen(buffer) > 0) {
+ if (line_nr > int(config.at(tile_y).at(tile_x).size() - 1))
+ log_error("Invalid data in input asc file");
+ for (int i = 0; buffer[i] == '0' || buffer[i] == '1'; i++)
+ config.at(tile_y).at(tile_x).at(line_nr).at(i) = (buffer[i] == '1') ? 1 : 0;
+ line_nr++;
+ }
+ if (in.eof())
+ break;
+ }
+}
+
+bool read_asc(Context *ctx, std::istream &in)
+{
+ try {
+ // [y][x][row][col]
+ const ChipInfoPOD &ci = *ctx->chip_info;
+ const BitstreamInfoPOD &bi = *ci.bits_info;
+ chipconfig_t config;
+ config.resize(ci.height);
+ for (int y = 0; y < ci.height; y++) {
+ config.at(y).resize(ci.width);
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ int rows = bi.tiles_nonrouting[tile].rows;
+ int cols = bi.tiles_nonrouting[tile].cols;
+ config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
+ }
+ }
+ read_config(ctx, in, config);
+
+ // Set pips
+ for (auto pip : ctx->getPips()) {
+ const PipInfoPOD &pi = ci.pip_data[pip.index];
+ const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
+ bool isUsed = true;
+ for (int i = 0; i < swi.num_bits; i++) {
+ bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
+ int8_t cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
+ isUsed &= !(bool(cbit) ^ val);
+ }
+ if (isUsed) {
+ NetInfo *net = ctx->wire_to_net[pi.dst];
+ if (net != nullptr) {
+ WireId wire;
+ wire.index = pi.dst;
+ ctx->unbindWire(wire);
+ ctx->bindPip(pip, net, STRENGTH_WEAK);
+ }
+ }
+ }
+ for (auto bel : ctx->getBels()) {
+ if (ctx->getBelType(bel) == id_ICESTORM_LC) {
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ std::vector<bool> lc(20, false);
+ bool isUsed = false;
+ for (int i = 0; i < 20; i++) {
+ lc.at(i) = get_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), i);
+ isUsed |= lc.at(i);
+ }
+ bool neg_clk = get_config(ti, config.at(y).at(x), "NegClk");
+ isUsed |= neg_clk;
+ bool carry_set = get_config(ti, config.at(y).at(x), "CarryInSet");
+ isUsed |= carry_set;
+
+ if (isUsed) {
+ std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
+ IdString name = created->name;
+ ctx->cells[name] = std::move(created);
+ ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK);
+ // TODO: Add port mapping to nets and assign values of properties
+ }
+ }
+ if (ctx->getBelType(bel) == id_SB_IO) {
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ bool isUsed = false;
+ for (int i = 0; i < 6; i++) {
+ isUsed |= get_config(ti, config.at(y).at(x),
+ "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i));
+ }
+ bool neg_trigger = get_config(ti, config.at(y).at(x), "NegClk");
+ isUsed |= neg_trigger;
+
+ if (isUsed) {
+ std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
+ IdString name = created->name;
+ ctx->cells[name] = std::move(created);
+ ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK);
+ // TODO: Add port mapping to nets and assign values of properties
+ }
+ }
+ }
+ // Add cells that are without change in initial state of configuration
+ for (auto &net : ctx->nets) {
+ for (auto w : net.second->wires) {
+ if (w.second.pip == PipId()) {
+ WireId wire = w.first;
+ for (auto belpin : ctx->getWireBelPins(wire)) {
+
+ if (ctx->checkBelAvail(belpin.bel)) {
+ if (ctx->getBelType(belpin.bel) == id_ICESTORM_LC) {
+ std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
+ IdString name = created->name;
+ ctx->cells[name] = std::move(created);
+ ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
+ // TODO: Add port mapping to nets
+ }
+ if (ctx->getBelType(belpin.bel) == id_SB_IO) {
+ std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
+ IdString name = created->name;
+ ctx->cells[name] = std::move(created);
+ ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
+ // TODO: Add port mapping to nets
+ }
+ if (ctx->getBelType(belpin.bel) == id_SB_GB) {
+ std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_GB"));
+ IdString name = created->name;
+ ctx->cells[name] = std::move(created);
+ ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
+ // TODO: Add port mapping to nets
+ }
+ if (ctx->getBelType(belpin.bel) == id_SB_WARMBOOT) {
+ std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_WARMBOOT"));
+ IdString name = created->name;
+ ctx->cells[name] = std::move(created);
+ ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
+ // TODO: Add port mapping to nets
+ }
+ if (ctx->getBelType(belpin.bel) == id_ICESTORM_LFOSC) {
+ std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"));
+ IdString name = created->name;
+ ctx->cells[name] = std::move(created);
+ ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
+ // TODO: Add port mapping to nets
+ }
+ }
+ }
+ }
+ }
+ }
+ for (auto &cell : ctx->cells) {
+ if (cell.second->bel != BelId()) {
+ for (auto &port : cell.second->ports) {
+ IdString pin = port.first;
+ WireId wire = ctx->getBelPinWire(cell.second->bel, pin);
+ if (wire != WireId()) {
+ NetInfo *net = ctx->getBoundWireNet(wire);
+ if (net != nullptr) {
+ port.second.net = net;
+ PortRef ref;
+ ref.cell = cell.second.get();
+ ref.port = port.second.name;
+
+ if (port.second.type == PORT_OUT)
+ net->driver = ref;
+ else
+ net->users.push_back(ref);
+ }
+ }
+ }
+ }
+ }
+ return true;
+ } catch (log_execution_error_exception) {
+ return false;
+ }
+}
+NEXTPNR_NAMESPACE_END
diff --git a/ice40/cells.cc b/ice40/cells.cc
index fbb77b0c..dbb75c2c 100644
--- a/ice40/cells.cc
+++ b/ice40/cells.cc
@@ -219,7 +219,7 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
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("FEEDBACK_PATH")] = "1";
new_cell->params[ctx->id("FILTER_RANGE")] = "0";
new_cell->params[ctx->id("PLLOUT_SELECT_A")] = "0";
@@ -244,8 +244,22 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
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);
- add_port(ctx, new_cell.get(), "PLLOUTGLOBALA", PORT_OUT);
- add_port(ctx, new_cell.get(), "PLLOUTGLOBALB", PORT_OUT);
+ add_port(ctx, new_cell.get(), "PLLOUT_A_GLOBAL", PORT_OUT);
+ add_port(ctx, new_cell.get(), "PLLOUT_B_GLOBAL", PORT_OUT);
+ } else if (type == ctx->id("SB_RGBA_DRV")) {
+ new_cell->params[ctx->id("CURRENT_MODE")] = "0b0";
+ new_cell->params[ctx->id("RGB0_CURRENT")] = "0b000000";
+ new_cell->params[ctx->id("RGB1_CURRENT")] = "0b000000";
+ new_cell->params[ctx->id("RGB2_CURRENT")] = "0b000000";
+
+ add_port(ctx, new_cell.get(), "CURREN", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGBLEDEN", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB0PWM", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB1PWM", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB2PWM", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB0", PORT_OUT);
+ add_port(ctx, new_cell.get(), "RGB1", PORT_OUT);
+ add_port(ctx, new_cell.get(), "RGB2", PORT_OUT);
} else {
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
}
@@ -362,7 +376,7 @@ uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell)
if (cell->type == ctx->id("SB_PLL40_2_PAD"))
return 4;
if (cell->type == ctx->id("SB_PLL40_2F_PAD"))
- return 5;
+ return 6;
if (cell->type == ctx->id("SB_PLL40_CORE"))
return 3;
if (cell->type == ctx->id("SB_PLL40_2F_CORE"))
diff --git a/ice40/cells.h b/ice40/cells.h
index 054388ac..1fbd9073 100644
--- a/ice40/cells.h
+++ b/ice40/cells.h
@@ -53,6 +53,9 @@ inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type
// Return true if a cell is a SB_IO
inline bool is_sb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_IO"); }
+// Return true if a cell is a SB_GB_IO
+inline bool is_sb_gb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_GB_IO"); }
+
// Return true if a cell is a global buffer
inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_GB"); }
@@ -71,6 +74,8 @@ 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_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGBA_DRV"); }
+
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") ||
@@ -81,7 +86,19 @@ inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
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");
+ cell->type == ctx->id("SB_PLL40_2F_PAD") ||
+ (cell->type == ctx->id("ICESTORM_PLL") &&
+ (cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_PAD" || cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2_PAD" ||
+ cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2F_PAD"));
+}
+
+inline bool is_sb_pll40_dual(const BaseCtx *ctx, const CellInfo *cell)
+{
+ return cell->type == ctx->id("SB_PLL40_2_PAD") || cell->type == ctx->id("SB_PLL40_2F_PAD") ||
+ cell->type == ctx->id("SB_PLL40_2F_CORE") ||
+ (cell->type == ctx->id("ICESTORM_PLL") && (cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2_PAD" ||
+ cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2F_PAD" ||
+ cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2F_CORE"));
}
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
diff --git a/ice40/chipdb.py b/ice40/chipdb.py
index 5b2f3e57..96231b26 100644
--- a/ice40/chipdb.py
+++ b/ice40/chipdb.py
@@ -36,6 +36,7 @@ ierens = list()
extra_cells = dict()
extra_cell_config = dict()
packages = list()
+glbinfo = dict([(i, {}) for i in range(8)])
wire_belports = dict()
@@ -640,6 +641,18 @@ with open(args.filename, "r") as f:
extra_cells[mode[1]] = []
continue
+ if line[0] == ".gbufin":
+ mode = ("gbufin",)
+ continue
+
+ if line[0] == ".gbufpin":
+ mode = ("gbufpin",)
+ continue
+
+ if line[0] == ".extra_bits":
+ mode = ("extra_bits",)
+ continue
+
if (line[0][0] == ".") or (mode is None):
mode = None
continue
@@ -692,11 +705,33 @@ with open(args.filename, "r") as f:
if mode[0] == "extra_cell":
if line[0] == "LOCKED":
- extra_cells[mode[1]].append((("LOCKED_" + line[1]), (0, 0, "LOCKED")))
+ for pkg in line[1:]:
+ extra_cells[mode[1]].append((("LOCKED_" + pkg), (0, 0, "LOCKED")))
else:
extra_cells[mode[1]].append((line[0], (int(line[1]), int(line[2]), line[3])))
continue
+ if mode[0] == "gbufin":
+ idx = int(line[2])
+ glbinfo[idx]['gb_x'] = int(line[0])
+ glbinfo[idx]['gb_y'] = int(line[1])
+ continue
+
+ if mode[0] == "gbufpin":
+ idx = int(line[3])
+ glbinfo[idx]['pi_gb_x'] = int(line[0])
+ glbinfo[idx]['pi_gb_y'] = int(line[1])
+ glbinfo[idx]['pi_gb_pio'] = int(line[2])
+ continue
+
+ if mode[0] == "extra_bits":
+ if line[0].startswith('padin_glb_netwk.'):
+ idx = int(line[0].split('.')[1])
+ glbinfo[idx]['pi_eb_bank'] = int(line[1])
+ glbinfo[idx]['pi_eb_x'] = int(line[2])
+ glbinfo[idx]['pi_eb_y'] = int(line[3])
+ continue
+
def add_wire(x, y, name):
global num_wires
wire_idx = num_wires
@@ -828,6 +863,10 @@ def add_bel_io(x, y, z):
add_bel_input(bel, wire_dout_1, "D_OUT_1")
add_bel_input(bel, wire_out_en, "OUTPUT_ENABLE")
+ for gidx, ginfo in glbinfo.items():
+ if (ginfo['pi_gb_x'], ginfo['pi_gb_y'], ginfo['pi_gb_pio']) == (x,y,z):
+ add_bel_output(bel, wire_names[(x, y, "glb_netwk_%d" % gidx)], "GLOBAL_BUFFER_OUTPUT")
+
def add_bel_ram(x, y):
bel = len(bel_name)
bel_name.append("X%d/Y%d/ram" % (x, y))
@@ -885,6 +924,18 @@ def is_ec_output(ec_entry):
def is_ec_pll_clock_output(ec, ec_entry):
return ec[0] == 'PLL' and ec_entry[0] in ('PLLOUT_A', 'PLLOUT_B')
+def add_pll_clock_output(bel, ec, entry):
+ # Fabric output
+ io_x, io_y, io_z = entry[1]
+ io_zs = 'io_{}/D_IN_0'.format(io_z)
+ io_z = int(io_z)
+ add_bel_output(bel, wire_names[(io_x, io_y, io_zs)], entry[0])
+
+ # Global output
+ for gidx, ginfo in glbinfo.items():
+ if (ginfo['pi_gb_x'], ginfo['pi_gb_y'], ginfo['pi_gb_pio']) == (io_x, io_y, io_z):
+ add_bel_output(bel, wire_names[(io_x, io_y, "glb_netwk_%d" % gidx)], entry[0] + '_GLOBAL')
+
def add_bel_ec(ec):
ectype, x, y, z = ec
bel = len(bel_name)
@@ -894,15 +945,13 @@ def add_bel_ec(ec):
bel_pos.append((x, y, z))
bel_wires.append(list())
for entry in extra_cells[ec]:
- if is_ec_wire(entry) and "glb_netwk_" not in entry[1][2]: # TODO: osc glb output conflicts with GB
+ if is_ec_wire(entry):
if is_ec_output(entry):
add_bel_output(bel, wire_names[entry[1]], entry[0])
else:
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])
+ add_pll_clock_output(bel, ec, entry)
else:
extra_cell_config[bel].append(entry)
@@ -973,42 +1022,8 @@ for tile_xy, tile_type in sorted(tiles.items()):
for i in range(2):
add_bel_io(tile_xy[0], tile_xy[1], i)
- if dev_name == "1k":
- add_bel_gb(tile_xy, 7, 0, 0)
- add_bel_gb(tile_xy, 7, 17, 1)
- add_bel_gb(tile_xy, 13, 9, 2)
- add_bel_gb(tile_xy, 0, 9, 3)
- add_bel_gb(tile_xy, 6, 17, 4)
- add_bel_gb(tile_xy, 6, 0, 5)
- add_bel_gb(tile_xy, 0, 8, 6)
- add_bel_gb(tile_xy, 13, 8, 7)
- elif dev_name == "5k":
- add_bel_gb(tile_xy, 13, 0, 0)
- add_bel_gb(tile_xy, 13, 31, 1)
- add_bel_gb(tile_xy, 19, 31, 2)
- add_bel_gb(tile_xy, 6, 31, 3)
- add_bel_gb(tile_xy, 12, 31, 4)
- add_bel_gb(tile_xy, 12, 0, 5)
- add_bel_gb(tile_xy, 6, 0, 6)
- add_bel_gb(tile_xy, 19, 0, 7)
- elif dev_name == "8k":
- add_bel_gb(tile_xy, 33, 16, 7)
- add_bel_gb(tile_xy, 0, 16, 6)
- add_bel_gb(tile_xy, 17, 33, 1)
- add_bel_gb(tile_xy, 17, 0, 0)
- add_bel_gb(tile_xy, 0, 17, 3)
- add_bel_gb(tile_xy, 33, 17, 2)
- add_bel_gb(tile_xy, 16, 0, 5)
- add_bel_gb(tile_xy, 16, 33, 4)
- elif dev_name == "384":
- add_bel_gb(tile_xy, 7, 4, 7)
- add_bel_gb(tile_xy, 0, 4, 6)
- add_bel_gb(tile_xy, 4, 9, 1)
- add_bel_gb(tile_xy, 4, 0, 0)
- add_bel_gb(tile_xy, 0, 5, 3)
- add_bel_gb(tile_xy, 7, 5, 2)
- add_bel_gb(tile_xy, 3, 0, 5)
- add_bel_gb(tile_xy, 3, 9, 4)
+ for gidx, ginfo in glbinfo.items():
+ add_bel_gb(tile_xy, ginfo['gb_x'], ginfo['gb_y'], gidx)
if tile_type == "ramb":
add_bel_ram(tile_xy[0], tile_xy[1])
@@ -1423,6 +1438,14 @@ for cell, timings in sorted(cell_timings.items()):
bba.u32(len(timings), "num_paths")
bba.r("cell_paths_%d" % beltype, "path_delays")
+bba.l("global_network_info_%s" % dev_name, "GlobalNetworkInfoPOD")
+for i in range(len(glbinfo)):
+ for k in ['gb_x', 'gb_y', 'pi_gb_x', 'pi_gb_y', 'pi_gb_pio', 'pi_eb_bank']:
+ bba.u8(glbinfo[i][k], k)
+ for k in ['pi_eb_x', 'pi_eb_y']:
+ bba.u16(glbinfo[i][k], k)
+ bba.u16(0, "padding")
+
bba.l("chip_info_%s" % dev_name)
bba.u32(dev_width, "dev_width")
bba.u32(dev_height, "dev_height")
@@ -1433,6 +1456,7 @@ bba.u32(len(switchinfo), "num_switches")
bba.u32(len(extra_cell_config), "num_belcfgs")
bba.u32(len(packageinfo), "num_packages")
bba.u32(len(cell_timings), "num_timing_cells")
+bba.u32(len(glbinfo), "num_global_networks")
bba.r("bel_data_%s" % dev_name, "bel_data")
bba.r("wire_data_%s" % dev_name, "wire_data")
bba.r("pip_data_%s" % dev_name, "pip_data")
@@ -1441,6 +1465,7 @@ 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("package_info_%s" % dev_name, "packages_data")
bba.r("cell_timings_%s" % dev_name, "cell_timing")
+bba.r("global_network_info_%s" % dev_name, "global_network_info")
bba.r("tile_wire_names", "tile_wire_names")
bba.pop()
diff --git a/ice40/constids.inc b/ice40/constids.inc
index dad08e59..e1c4992e 100644
--- a/ice40/constids.inc
+++ b/ice40/constids.inc
@@ -121,6 +121,8 @@ X(DYNAMICDELAY_7)
X(LOCK)
X(PLLOUT_A)
X(PLLOUT_B)
+X(PLLOUT_A_GLOBAL)
+X(PLLOUT_B_GLOBAL)
X(BYPASS)
X(RESETB)
X(LATCHINPUTVALUE)
diff --git a/ice40/pack.cc b/ice40/pack.cc
index 7a27d505..682baadd 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -381,12 +381,44 @@ static void pack_constants(Context *ctx)
}
}
+static std::unique_ptr<CellInfo> create_padin_gbuf(Context *ctx, CellInfo *cell, IdString port_name,
+ std::string gbuf_name)
+{
+ // Find the matching SB_GB BEL connected to the same global network
+ BelId gb_bel;
+ BelId bel = ctx->getBelByName(ctx->id(cell->attrs[ctx->id("BEL")]));
+ auto wire = ctx->getBelPinWire(bel, port_name);
+ for (auto src_bel : ctx->getWireBelPins(wire)) {
+ if (ctx->getBelType(src_bel.bel) == id_SB_GB && src_bel.pin == id_GLOBAL_BUFFER_OUTPUT) {
+ gb_bel = src_bel.bel;
+ break;
+ }
+ }
+
+ NPNR_ASSERT(gb_bel != BelId());
+
+ // Create a SB_GB Cell and lock it there
+ std::unique_ptr<CellInfo> gb = create_ice_cell(ctx, ctx->id("SB_GB"), gbuf_name);
+ gb->attrs[ctx->id("FOR_PAD_IN")] = "1";
+ gb->attrs[ctx->id("BEL")] = ctx->getBelName(gb_bel).str(ctx);
+
+ // Reconnect the net to that port for easier identification it's a global net
+ replace_port(cell, port_name, gb.get(), id_GLOBAL_BUFFER_OUTPUT);
+
+ return gb;
+}
+
static bool is_nextpnr_iob(Context *ctx, CellInfo *cell)
{
return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||
cell->type == ctx->id("$nextpnr_iobuf");
}
+static bool is_ice_iob(const Context *ctx, const CellInfo *cell)
+{
+ return is_sb_io(ctx, cell) || is_sb_gb_io(ctx, cell);
+}
+
// Pack IO buffers
static void pack_io(Context *ctx)
{
@@ -399,12 +431,15 @@ static void pack_io(Context *ctx)
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_nextpnr_iob(ctx, ci)) {
- CellInfo *sb = nullptr;
+ CellInfo *sb = nullptr, *rgb = nullptr;
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
- sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
+ sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci);
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
- sb = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
+ NetInfo *net = ci->ports.at(ctx->id("I")).net;
+ sb = net_only_drives(ctx, net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci);
+ if (net && net->driver.cell && is_sb_rgba_drv(ctx, net->driver.cell))
+ rgb = net->driver.cell;
}
if (sb != nullptr) {
// Trivial case, SB_IO used. Just destroy the net and the
@@ -415,8 +450,8 @@ static void pack_io(Context *ctx)
if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) &&
net->users.size() > 1) ||
(ci->type == ctx->id("$nextpnr_obuf") && (net->users.size() > 2 || net->driver.cell != nullptr)))
- log_error("PACKAGE_PIN of SB_IO '%s' connected to more than a single top level IO.\n",
- sb->name.c_str(ctx));
+ log_error("PACKAGE_PIN of %s '%s' connected to more than a single top level IO.\n",
+ sb->type.c_str(ctx), sb->name.c_str(ctx));
if (net != nullptr) {
delete_nets.insert(net->name);
@@ -428,6 +463,11 @@ static void pack_io(Context *ctx)
delete_nets.insert(net2->name);
}
}
+ } else if (rgb != nullptr) {
+ log_info("%s use by SB_RGBA_DRV %s, not creating SB_IO\n", ci->name.c_str(ctx), rgb->name.c_str(ctx));
+ disconnect_port(ctx, ci, ctx->id("I"));
+ packed_cells.insert(ci->name);
+ continue;
} else {
// Create a SB_IO buffer
std::unique_ptr<CellInfo> ice_cell =
@@ -438,6 +478,24 @@ static void pack_io(Context *ctx)
}
packed_cells.insert(ci->name);
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin()));
+ } else if (is_sb_io(ctx, ci) || is_sb_gb_io(ctx, ci)) {
+ NetInfo *net = ci->ports.at(ctx->id("PACKAGE_PIN")).net;
+ if ((net != nullptr) && (net->users.size() > 1))
+ log_error("PACKAGE_PIN of %s '%s' connected to more than a single top level IO.\n", ci->type.c_str(ctx),
+ ci->name.c_str(ctx));
+ }
+ }
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (is_sb_gb_io(ctx, ci)) {
+ // If something is connecto the GLOBAL OUTPUT, create the fake 'matching' SB_GB
+ std::unique_ptr<CellInfo> gb =
+ create_padin_gbuf(ctx, ci, id_GLOBAL_BUFFER_OUTPUT, "$gbuf_" + ci->name.str(ctx) + "_io");
+ new_cells.push_back(std::move(gb));
+
+ // Make it a normal SB_IO with global marker
+ ci->type = ctx->id("SB_IO");
+ ci->attrs[ctx->id("GLOBAL")] = "1";
}
}
for (auto pcell : packed_cells) {
@@ -456,8 +514,8 @@ static bool is_logic_port(BaseCtx *ctx, const PortRef &port)
{
if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port))
return false;
- return !is_sb_io(ctx, port.cell) && !is_sb_pll40(ctx, port.cell) && !is_sb_pll40_pad(ctx, port.cell) &&
- port.cell->type != ctx->id("SB_GB");
+ return !is_sb_io(ctx, port.cell) && !is_sb_gb_io(ctx, port.cell) && !is_gbuf(ctx, port.cell) &&
+ !is_sb_pll40(ctx, port.cell);
}
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
@@ -645,6 +703,22 @@ static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString
return pt;
}
+// Force placement for cells that are unique anyway
+static BelId cell_place_unique(Context *ctx, CellInfo *ci)
+{
+ for (auto bel : ctx->getBels()) {
+ if (ctx->getBelType(bel) != ci->type)
+ continue;
+ if (ctx->isBelLocked(bel))
+ continue;
+ IdString bel_name = ctx->getBelName(bel);
+ ci->attrs[ctx->id("BEL")] = bel_name.str(ctx);
+ log_info(" constrained %s '%s' to %s\n", ci->type.c_str(ctx), ci->name.c_str(ctx), bel_name.c_str(ctx));
+ return bel;
+ }
+ log_error("Unable to place cell '%s' of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx));
+}
+
// Pack special functions
static void pack_special(Context *ctx)
{
@@ -659,25 +733,33 @@ static void pack_special(Context *ctx)
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"), ci->name.str(ctx) + "_OSC");
packed_cells.insert(ci->name);
+ cell_place_unique(ctx, packed.get());
replace_port(ci, ctx->id("CLKLFEN"), packed.get(), ctx->id("CLKLFEN"));
replace_port(ci, ctx->id("CLKLFPU"), packed.get(), ctx->id("CLKLFPU"));
- if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME
+ if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) {
replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF_FABRIC"));
} else {
replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF"));
+ std::unique_ptr<CellInfo> gb =
+ create_padin_gbuf(ctx, packed.get(), ctx->id("CLKLF"), "$gbuf_" + ci->name.str(ctx) + "_lfosc");
+ new_cells.push_back(std::move(gb));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_hfosc(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_HFOSC"), ci->name.str(ctx) + "_OSC");
packed_cells.insert(ci->name);
+ cell_place_unique(ctx, packed.get());
packed->params[ctx->id("CLKHF_DIV")] = str_or_default(ci->params, ctx->id("CLKHF_DIV"), "0b00");
replace_port(ci, ctx->id("CLKHFEN"), packed.get(), ctx->id("CLKHFEN"));
replace_port(ci, ctx->id("CLKHFPU"), packed.get(), ctx->id("CLKHFPU"));
- if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME
+ if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) {
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF_FABRIC"));
} else {
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF"));
+ std::unique_ptr<CellInfo> gb =
+ create_padin_gbuf(ctx, packed.get(), ctx->id("CLKHF"), "$gbuf_" + ci->name.str(ctx) + "_hfosc");
+ new_cells.push_back(std::move(gb));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_spram(ctx, ci)) {
@@ -713,6 +795,29 @@ static void pack_special(Context *ctx)
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_rgba_drv(ctx, ci)) {
+ /* Force placement (no choices anyway) */
+ cell_place_unique(ctx, ci);
+
+ /* Disconnect all external ports and check there is no users (they should have been
+ * dealth with during IO packing */
+ for (auto port : ci->ports) {
+ PortInfo &pi = port.second;
+ NetInfo *net = pi.net;
+
+ if (net == nullptr)
+ continue;
+ if ((pi.name != ctx->id("RGB0")) && (pi.name != ctx->id("RGB1")) && (pi.name != ctx->id("RGB2")))
+ continue;
+
+ if (net->users.size() > 0)
+ log_error("SB_RGBA_DRV port connected to more than just package pin !\n");
+
+ ctx->nets.erase(net->name);
+ }
+ ci->ports.erase(ctx->id("RGB0"));
+ ci->ports.erase(ctx->id("RGB1"));
+ ci->ports.erase(ctx->id("RGB2"));
} else if (is_sb_pll40(ctx, ci)) {
bool is_pad = is_sb_pll40_pad(ctx, ci);
bool is_core = !is_pad;
@@ -727,6 +832,24 @@ static void pack_special(Context *ctx)
for (auto param : ci->params)
packed->params[param.first] = param.second;
+ const std::map<IdString, IdString> pos_map_name = {
+ {ctx->id("PLLOUT_SELECT"), ctx->id("PLLOUT_SELECT_A")},
+ {ctx->id("PLLOUT_SELECT_PORTA"), ctx->id("PLLOUT_SELECT_A")},
+ {ctx->id("PLLOUT_SELECT_PORTB"), ctx->id("PLLOUT_SELECT_B")},
+ };
+ const std::map<std::string, int> pos_map_val = {
+ {"GENCLK", 0},
+ {"GENCLK_HALF", 1},
+ {"SHIFTREG_90deg", 2},
+ {"SHIFTREG_0deg", 3},
+ };
+ for (auto param : ci->params)
+ if (pos_map_name.find(param.first) != pos_map_name.end()) {
+ if (pos_map_val.find(param.second) == pos_map_val.end())
+ log_error("Invalid PLL output selection '%s'\n", param.second.c_str());
+ packed->params[pos_map_name.at(param.first)] = std::to_string(pos_map_val.at(param.second));
+ }
+
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
packed->params[ctx->id("FEEDBACK_PATH")] =
feedback_path == "DELAY"
@@ -739,32 +862,6 @@ static void pack_special(Context *ctx)
NetInfo *pad_packagepin_net = nullptr;
- int pllout_a_used = 0;
- int pllout_b_used = 0;
- for (auto port : ci->ports) {
- PortInfo &pi = port.second;
- if (pi.name == ctx->id("PLLOUTCOREA"))
- pllout_a_used++;
- if (pi.name == ctx->id("PLLOUTCOREB"))
- pllout_b_used++;
- if (pi.name == ctx->id("PLLOUTCORE"))
- pllout_a_used++;
- if (pi.name == ctx->id("PLLOUTGLOBALA"))
- pllout_a_used++;
- if (pi.name == ctx->id("PLLOUTGLOBALB"))
- pllout_b_used++;
- if (pi.name == ctx->id("PLLOUTGLOBAL"))
- pllout_a_used++;
- }
-
- if (pllout_a_used > 1)
- log_error("PLL '%s' is using multiple ports mapping to PLLOUT_A output of the PLL\n",
- ci->name.c_str(ctx));
-
- if (pllout_b_used > 1)
- log_error("PLL '%s' is using multiple ports mapping to PLLOUT_B output of the PLL\n",
- ci->name.c_str(ctx));
-
for (auto port : ci->ports) {
PortInfo &pi = port.second;
std::string newname = pi.name.str(ctx);
@@ -772,24 +869,15 @@ static void pack_special(Context *ctx)
if (bpos != std::string::npos) {
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
}
- if (pi.name == ctx->id("PLLOUTCOREA"))
+
+ if (pi.name == ctx->id("PLLOUTCOREA") || pi.name == ctx->id("PLLOUTCORE"))
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("PLLOUTGLOBALA"))
- newname = "PLLOUT_A";
+ if (pi.name == ctx->id("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBAL"))
+ newname = "PLLOUT_A_GLOBAL";
if (pi.name == ctx->id("PLLOUTGLOBALB"))
- newname = "PLLOUT_B";
- if (pi.name == ctx->id("PLLOUTGLOBAL"))
- newname = "PLLOUT_A";
-
- if (pi.name == ctx->id("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBALB") ||
- pi.name == ctx->id("PLLOUTGLOBAL"))
- log_warning("PLL '%s' is using port %s but implementation does not actually "
- "use the global clock output of the PLL\n",
- ci->name.c_str(ctx), pi.name.str(ctx).c_str());
+ newname = "PLLOUT_B_GLOBAL";
if (pi.name == ctx->id("PACKAGEPIN")) {
if (!is_pad) {
@@ -833,6 +921,8 @@ static void pack_special(Context *ctx)
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ICESTORM_PLL)
continue;
+ if (ctx->isBelLocked(bel))
+ continue;
// A PAD PLL must have its' PACKAGEPIN on the SB_IO that's shared
// with PLLOUT_A.
@@ -874,11 +964,22 @@ static void pack_special(Context *ctx)
packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
pll_bel = bel;
constrained = true;
+ break;
}
if (!constrained) {
log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n",
packed->name.c_str(ctx));
}
+ } else {
+ pll_bel = ctx->getBelByName(ctx->id(packed->attrs[ctx->id("BEL")]));
+ if (ctx->getBelType(pll_bel) != id_ICESTORM_PLL)
+ log_error("PLL '%s' is constrained to BEL %s which isn't a ICESTORM_PLL BEL\n",
+ packed->name.c_str(ctx), ctx->getBelName(pll_bel).c_str(ctx));
+ if (ctx->isBelLocked(pll_bel))
+ log_error("PLL '%s' is constrained to locked BEL %s\n", packed->name.c_str(ctx),
+ ctx->getBelName(pll_bel).c_str(ctx));
+ log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx),
+ ctx->getBelName(pll_bel).c_str(ctx));
}
// Delete the original PACKAGEPIN net if needed.
@@ -886,6 +987,8 @@ static void pack_special(Context *ctx)
for (auto user : pad_packagepin_net->users) {
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);
pad_packagepin_net = nullptr;
}
@@ -947,6 +1050,24 @@ static void pack_special(Context *ctx)
}
}
+ // Handle the global buffer connections
+ for (auto port : packed->ports) {
+ PortInfo &pi = port.second;
+ bool is_b_port;
+
+ if (pi.name == ctx->id("PLLOUT_A_GLOBAL"))
+ is_b_port = false;
+ else if (pi.name == ctx->id("PLLOUT_B_GLOBAL"))
+ is_b_port = true;
+ else
+ continue;
+
+ std::unique_ptr<CellInfo> gb =
+ create_padin_gbuf(ctx, packed.get(), pi.name,
+ "$gbuf_" + ci->name.str(ctx) + "_pllout_" + (is_b_port ? "b" : "a"));
+ new_cells.push_back(std::move(gb));
+ }
+
new_cells.push_back(std::move(packed));
}
}
@@ -966,13 +1087,13 @@ bool Arch::pack()
try {
log_break();
pack_constants(ctx);
- promote_globals(ctx);
pack_io(ctx);
pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx);
pack_carries(ctx);
pack_ram(ctx);
pack_special(ctx);
+ promote_globals(ctx);
ctx->assignArchInfo();
constrain_chains(ctx);
ctx->assignArchInfo();
diff --git a/ice40/pcf.cc b/ice40/pcf.cc
index af5b3e17..ce453af9 100644
--- a/ice40/pcf.cc
+++ b/ice40/pcf.cc
@@ -33,7 +33,9 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
if (!in)
log_error("failed to open PCF file\n");
std::string line;
+ int lineno = 0;
while (std::getline(in, line)) {
+ lineno++;
size_t cstart = line.find("#");
if (cstart != std::string::npos)
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;
while (args_end < words.size() && words.at(args_end).at(0) == '-')
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 pin = words.at(args_end + 1);
auto fnd_cell = ctx->cells.find(ctx->id(cell));
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 {
BelId pin_bel = ctx->getPackagePinBel(pin);
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);
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
fnd_cell->second->attrs[ctx->id("BEL")].c_str());
}
} 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);
diff --git a/json/jsonparse.cc b/json/jsonparse.cc
index e7f39a19..bddbee0b 100644
--- a/json/jsonparse.cc
+++ b/json/jsonparse.cc
@@ -472,12 +472,7 @@ void json_import_ports(Context *ctx, const string &modname, const std::vector<Id
vcc_net(ctx, net.get());
} else if (wire_node->data_string.compare(string("x")) == 0) {
-
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
log_error(" Unknown fixed type wire node "
"value, \'%s\'\n",