aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ecp5/arch.h3
-rw-r--r--ecp5/lpf.cc103
-rw-r--r--ecp5/main.cc16
3 files changed, 122 insertions, 0 deletions
diff --git a/ecp5/arch.h b/ecp5/arch.h
index 35c8df19..0a5de822 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -917,6 +917,9 @@ struct Arch : BaseCtx
GlobalInfoPOD globalInfoAtLoc(Location loc);
+ // Apply LPF constraints to the context
+ bool applyLPF(std::string filename, std::istream &in);
+
IdString id_trellis_slice;
IdString id_clk, id_lsr;
IdString id_clkmux, id_lsrmux;
diff --git a/ecp5/lpf.cc b/ecp5/lpf.cc
new file mode 100644
index 00000000..c8e61414
--- /dev/null
+++ b/ecp5/lpf.cc
@@ -0,0 +1,103 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <sstream>
+#include "log.h"
+#include "log.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+bool Arch::applyLPF(std::string filename, std::istream &in)
+{
+ auto isempty = [] (const std::string &str) {return std::all_of(str.begin(), str.end(), [](char c){return isblank(c);});};
+ auto strip_quotes = [] (const std::string &str) {if (str.at(0) == '"') {
+ NPNR_ASSERT(str.back() == '"');
+ return str.substr(1, str.size() - 2);
+ } else {
+ return str;
+ }};
+
+ try {
+ if (!in)
+ log_error("failed to open LPF file\n");
+ std::string line;
+ std::string linebuf;
+ while (std::getline(in, line)) {
+ size_t cstart = line.find('#');
+ if (cstart != std::string::npos)
+ line = line.substr(0, cstart);
+ if (isempty(line))
+ continue;
+ linebuf += line;
+ // Look for a command up to a semicolon
+ size_t scpos = linebuf.find(';');
+ while (scpos != std::string::npos) {
+ std::string command = linebuf.substr(0, scpos);
+ // Split command into words
+ std::stringstream ss(line);
+ std::vector<std::string> words;
+ std::string tmp;
+ while (ss >> tmp)
+ words.push_back(tmp);
+ if (words.size() >= 0) {
+ std::string verb = words.at(0);
+ if(verb == "BLOCK" || verb == "SYSCONFIG" || verb == "FREQUENCY") {
+ log_warning(" ignoring unsupported LPF command '%s'\n", command.c_str());
+ } else if (verb == "LOCATE") {
+ NPNR_ASSERT(words.at(1) == "COMP");
+ std::string cell = strip_quotes(words.at(2));
+ NPNR_ASSERT(words.at(1) == "SITE");
+ auto fnd_cell = cells.find(id(cell));
+ if (fnd_cell == cells.end()) {
+ log_warning("unmatched LPF 'LOCATE COMP' '%s'\n", cell.c_str());
+ } else {
+ fnd_cell->second->attrs[id("LOC")] = strip_quotes(words.at(3));
+ }
+ } else if (verb == "IOBUF") {
+ NPNR_ASSERT(words.at(1) == "PORT");
+ std::string cell = strip_quotes(words.at(2));
+ auto fnd_cell = cells.find(id(cell));
+ if (fnd_cell == cells.end()) {
+ log_warning("unmatched LPF 'IOBUF PORT' '%s'\n", cell.c_str());
+ } else {
+ for (size_t i = 3; i < words.size(); i++) {
+ std::string setting = words.at(i);
+ size_t eqpos = setting.find('=');
+ NPNR_ASSERT(eqpos != std::string::npos);
+ std::string key = setting.substr(0, eqpos), value = setting.substr(eqpos + 1);
+ fnd_cell->second->attrs[id(key)] = value;
+ }
+ }
+ }
+ }
+
+ linebuf = linebuf.substr(scpos+1);
+ scpos = linebuf.find(';');
+ }
+ }
+ if (!isempty(linebuf))
+ log_error("unexpected end of LPF file\n");
+ settings.emplace(id("input/lpf"), filename);
+ return true;
+ } catch (log_execution_error_exception) {
+ return false;
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/ecp5/main.cc b/ecp5/main.cc
index e71b0983..c444f96f 100644
--- a/ecp5/main.cc
+++ b/ecp5/main.cc
@@ -35,6 +35,7 @@ class ECP5CommandHandler : public CommandHandler
virtual ~ECP5CommandHandler(){};
std::unique_ptr<Context> createContext() override;
void setupArchContext(Context *ctx) override{};
+ void customAfterLoad(Context *ctx) override;
void validate() override;
void customBitstream(Context *ctx) override;
@@ -56,9 +57,13 @@ po::options_description ECP5CommandHandler::getArchOptions()
specific.add_options()("um5g-25k", "set device type to LFE5UM5G-25F");
specific.add_options()("um5g-45k", "set device type to LFE5UM5G-45F");
specific.add_options()("um5g-85k", "set device type to LFE5UM5G-85F");
+
specific.add_options()("package", po::value<std::string>(), "select device package (defaults to CABGA381)");
specific.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format");
specific.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write");
+
+ specific.add_options()("lpf", po::value<std::vector<std::string>>(), "LPF pin constraint file(s)");
+
return specific;
}
void ECP5CommandHandler::validate()
@@ -111,6 +116,17 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext()
return std::unique_ptr<Context>(new Context(chipArgs));
}
+void ECP5CommandHandler::customAfterLoad(Context *ctx)
+{
+ if (vm.count("lpf")) {
+ std::vector<std::string> files = vm["lpf"].as<std::vector<std::string>>();
+ for (const auto &filename : files) {
+ std::ifstream in(filename);
+ ctx->applyLPF(filename, in);
+ }
+ }
+}
+
int main(int argc, char *argv[])
{
ECP5CommandHandler handler(argc, argv);