aboutsummaryrefslogtreecommitdiffstats
path: root/mistral
diff options
context:
space:
mode:
Diffstat (limited to 'mistral')
-rw-r--r--mistral/arch.h4
-rw-r--r--mistral/constids.inc2
-rw-r--r--mistral/main.cc9
-rw-r--r--mistral/qsf.cc281
4 files changed, 295 insertions, 1 deletions
diff --git a/mistral/arch.h b/mistral/arch.h
index d4f6bd5e..b818f001 100644
--- a/mistral/arch.h
+++ b/mistral/arch.h
@@ -455,6 +455,10 @@ struct Arch : BaseArch<ArchRanges>
typedef std::unordered_map<IdString, CellPinStyle> CellPinsData; // pins.cc
static const std::unordered_map<IdString, CellPinsData> cell_pins_db; // pins.cc
CellPinStyle get_cell_pin_style(const CellInfo *cell, IdString port) const; // pins.cc
+
+ // List of IO constraints, used by QSF parser
+ std::unordered_map<IdString, std::unordered_map<IdString, Property>> io_attr;
+ void read_qsf(std::istream &in); // qsf.cc
};
NEXTPNR_NAMESPACE_END
diff --git a/mistral/constids.inc b/mistral/constids.inc
index c17d9f89..342ca353 100644
--- a/mistral/constids.inc
+++ b/mistral/constids.inc
@@ -72,3 +72,5 @@ X(WIRE)
X(GND)
X(VCC)
+
+X(LOC) \ No newline at end of file
diff --git a/mistral/main.cc b/mistral/main.cc
index 702b4b7e..d5816693 100644
--- a/mistral/main.cc
+++ b/mistral/main.cc
@@ -49,6 +49,7 @@ po::options_description MistralCommandHandler::getArchOptions()
po::options_description specific("Architecture specific options");
specific.add_options()("mistral", po::value<std::string>(), "path to mistral root");
specific.add_options()("device", po::value<std::string>(), "device name (e.g. 5CSEBA6U23I7)");
+ specific.add_options()("qsf", po::value<std::string>(), "path to QSF constraints file");
return specific;
}
@@ -74,7 +75,13 @@ std::unique_ptr<Context> MistralCommandHandler::createContext(std::unordered_map
void MistralCommandHandler::customAfterLoad(Context *ctx)
{
- // TODO: qsf parsing
+ if (vm.count("qsf")) {
+ std::string filename = vm["qsf"].as<std::string>();
+ std::ifstream in(filename);
+ if (!in)
+ log_error("Failed to open input QSF file %s.\n", filename.c_str());
+ ctx->read_qsf(in);
+ }
}
int main(int argc, char *argv[])
diff --git a/mistral/qsf.cc b/mistral/qsf.cc
new file mode 100644
index 00000000..3f201aa1
--- /dev/null
+++ b/mistral/qsf.cc
@@ -0,0 +1,281 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2021 gatecat <gatecat@ds0.me>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "log.h"
+#include "nextpnr.h"
+#include "util.h"
+
+#include <iterator>
+
+NEXTPNR_NAMESPACE_BEGIN
+
+namespace {
+
+struct QsfOption
+{
+ std::string name; // name, excluding the initial '-'
+ int arg_count; // number of arguments that follow the option
+ bool required; // error out if this option isn't passed
+};
+
+typedef std::unordered_map<std::string, std::vector<std::string>> option_map_t;
+
+struct QsfCommand
+{
+ std::string name; // name of the command
+ std::vector<QsfOption> options; // list of "-options"
+ int pos_arg_count; // number of positional arguments expected to follow the command, -1 for any
+ std::function<void(Context *ctx, const option_map_t &options, const std::vector<std::string> &pos_args)> func;
+};
+
+void set_location_assignment_cmd(Context *ctx, const option_map_t &options, const std::vector<std::string> &pos_args)
+{
+ ctx->io_attr[ctx->id(options.at("to").at(0))][id_LOC] = pos_args.at(0);
+}
+
+void set_instance_assignment_cmd(Context *ctx, const option_map_t &options, const std::vector<std::string> &pos_args)
+{
+ ctx->io_attr[ctx->id(options.at("to").at(0))][id_LOC] = pos_args.at(0);
+}
+
+void set_global_assignment_cmd(Context *ctx, const option_map_t &options, const std::vector<std::string> &pos_args)
+{
+ // TODO
+}
+
+static const std::vector<QsfCommand> commands = {
+ {"set_location_assignment", {{"to", 1, true}}, 1, set_location_assignment_cmd},
+ {"set_instance_assignment",
+ {{"to", 1, true}, {"name", 1, true}, {"section_id", 1, false}},
+ 1,
+ set_instance_assignment_cmd},
+ {"set_global_assignment",
+ {{"name", 1, true}, {"section_id", 1, false}, {"rise", 0, false}, {"fall", 0, false}},
+ 1,
+ set_global_assignment_cmd},
+};
+
+struct QsfParser
+{
+ std::string buf;
+ int pos = 0;
+ int lineno = 0;
+ Context *ctx;
+
+ QsfParser(const std::string &buf, Context *ctx) : buf(buf), ctx(ctx){};
+
+ inline bool eof() const { return pos == int(buf.size()); }
+
+ inline char peek() const { return buf.at(pos); }
+
+ inline char get()
+ {
+ char c = buf.at(pos++);
+ if (c == '\n')
+ ++lineno;
+ return c;
+ }
+
+ std::string get(int n)
+ {
+ std::string s = buf.substr(pos, n);
+ pos += n;
+ return s;
+ }
+
+ // If next char matches c, take it from the stream and return true
+ bool check_get(char c)
+ {
+ if (peek() == c) {
+ get();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // If next char matches any in chars, take it from the stream and return true
+ bool check_get_any(const std::string &chrs)
+ {
+ char c = peek();
+ if (chrs.find(c) != std::string::npos) {
+ get();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ inline void skip_blank(bool nl = false)
+ {
+ while (!eof() && check_get_any(nl ? " \t\n\r" : " \t"))
+ ;
+ }
+
+ // Return true if end of line (or file)
+ inline bool skip_check_eol()
+ {
+ skip_blank(false);
+ if (eof())
+ return true;
+ char c = peek();
+ // Comments count as end of line
+ if (c == '#') {
+ get();
+ while (!eof() && peek() != '\n' && peek() != '\r')
+ get();
+ return true;
+ }
+ if (c == ';') {
+ // Forced end of line
+ get();
+ return true;
+ }
+ return (c == '\n' || c == '\r');
+ }
+
+ // We need to distinguish between quoted and unquoted strings, the former don't count as options
+ struct StringVal
+ {
+ std::string str;
+ bool is_quoted = false;
+ };
+
+ inline StringVal get_str()
+ {
+ StringVal s;
+ skip_blank(false);
+ if (eof())
+ return {"", false};
+
+ bool in_quotes = false, in_braces = false, escaped = false;
+
+ char c = get();
+
+ if (c == '"') {
+ in_quotes = true;
+ s.is_quoted = true;
+ } else if (c == '{') {
+ in_braces = true;
+ s.is_quoted = true;
+ } else {
+ s.str += c;
+ }
+
+ while (!eof()) {
+ char c = peek();
+ if (!in_quotes && !in_braces && !escaped && (std::isblank(c) || c == '\n' || c == '\r')) {
+ break;
+ }
+ get();
+ if (escaped) {
+ s.str += c;
+ escaped = false;
+ } else if ((in_quotes && c == '"') || (in_braces && c == '}')) {
+ break;
+ } else if (c == '\\') {
+ escaped = true;
+ } else {
+ s.str += c;
+ }
+ }
+
+ return s;
+ }
+
+ std::vector<StringVal> get_arguments()
+ {
+ std::vector<StringVal> args;
+ while (!skip_check_eol()) {
+ args.push_back(get_str());
+ }
+ skip_blank(true);
+ return args;
+ }
+
+ void evaluate(const std::vector<StringVal> &args)
+ {
+ if (args.empty())
+ return;
+ auto cmd_name = args.at(0).str;
+ auto fnd_cmd =
+ std::find_if(commands.begin(), commands.end(), [&](const QsfCommand &c) { return c.name == cmd_name; });
+ if (fnd_cmd == commands.end()) {
+ log_warning("Ignoring unknown command '%s' (line %d)\n", cmd_name.c_str(), lineno);
+ return;
+ }
+ option_map_t opt;
+ std::vector<std::string> pos_args;
+ for (size_t i = 1; i < args.size(); i++) {
+ auto arg = args.at(i);
+ if (arg.str.at(0) == '-' && !arg.is_quoted) {
+ for (auto &opt_data : fnd_cmd->options) {
+ if (arg.str.compare(1, std::string::npos, opt_data.name) != 0)
+ continue;
+ opt[opt_data.name]; // create empty entry, even if 0 arguments
+ for (int j = 0; j < opt_data.arg_count; j++) {
+ ++i;
+ if (i >= args.size())
+ log_error("Unexpected end of argument list to option '%s' (line %d)\n", arg.str.c_str(),
+ lineno);
+ opt[opt_data.name].push_back(args.at(i).str);
+ }
+ goto done;
+ }
+ log_error("Unknown option '%s' to command '%s' (line %d)\n", arg.str.c_str(), cmd_name.c_str(), lineno);
+ done:;
+ } else {
+ // positional argument
+ pos_args.push_back(arg.str);
+ }
+ }
+ // Check positional argument count
+ if (int(pos_args.size()) != fnd_cmd->pos_arg_count && fnd_cmd->pos_arg_count != -1) {
+ log_error("Expected %d positional arguments to command '%s', got %d (line %d)\n", fnd_cmd->pos_arg_count,
+ cmd_name.c_str(), int(pos_args.size()), lineno);
+ }
+ // Check required options
+ for (auto &opt_data : fnd_cmd->options) {
+ if (opt_data.required && !opt.count(opt_data.name))
+ log_error("Missing required option '%s' to command '%s' (line %d)\n", opt_data.name.c_str(),
+ cmd_name.c_str(), lineno);
+ }
+ // Execute
+ fnd_cmd->func(ctx, opt, pos_args);
+ }
+
+ void operator()()
+ {
+ while (!eof()) {
+ skip_blank(true);
+ auto args = get_arguments();
+ evaluate(args);
+ }
+ }
+};
+
+}; // namespace
+
+void Arch::read_qsf(std::istream &in)
+{
+ std::string buf(std::istreambuf_iterator<char>(in), {});
+ QsfParser(buf, getCtx())();
+}
+
+NEXTPNR_NAMESPACE_END \ No newline at end of file