Merge pull request #353 from YosysHQ/generic-frontend

New hierarchy-capable generic frontend framework and json11 based JSON frontend
This commit is contained in:
David Shah 2019-12-27 11:19:45 +00:00 committed by GitHub
commit 4e0ca50db1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2185 additions and 1071 deletions

19
3rdparty/json11/LICENSE.txt vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2013 Dropbox, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

790
3rdparty/json11/json11.cpp vendored Normal file
View File

@ -0,0 +1,790 @@
/* Copyright (c) 2013 Dropbox, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "json11.hpp"
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <limits>
namespace json11 {
static const int max_depth = 200;
using std::string;
using std::vector;
using std::map;
using std::make_shared;
using std::initializer_list;
using std::move;
/* Helper for representing null - just a do-nothing struct, plus comparison
* operators so the helpers in JsonValue work. We can't use nullptr_t because
* it may not be orderable.
*/
struct NullStruct {
bool operator==(NullStruct) const { return true; }
bool operator<(NullStruct) const { return false; }
};
/* * * * * * * * * * * * * * * * * * * *
* Serialization
*/
static void dump(NullStruct, string &out) {
out += "null";
}
static void dump(double value, string &out) {
if (std::isfinite(value)) {
char buf[32];
snprintf(buf, sizeof buf, "%.17g", value);
out += buf;
} else {
out += "null";
}
}
static void dump(int value, string &out) {
char buf[32];
snprintf(buf, sizeof buf, "%d", value);
out += buf;
}
static void dump(bool value, string &out) {
out += value ? "true" : "false";
}
static void dump(const string &value, string &out) {
out += '"';
for (size_t i = 0; i < value.length(); i++) {
const char ch = value[i];
if (ch == '\\') {
out += "\\\\";
} else if (ch == '"') {
out += "\\\"";
} else if (ch == '\b') {
out += "\\b";
} else if (ch == '\f') {
out += "\\f";
} else if (ch == '\n') {
out += "\\n";
} else if (ch == '\r') {
out += "\\r";
} else if (ch == '\t') {
out += "\\t";
} else if (static_cast<uint8_t>(ch) <= 0x1f) {
char buf[8];
snprintf(buf, sizeof buf, "\\u%04x", ch);
out += buf;
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
&& static_cast<uint8_t>(value[i+2]) == 0xa8) {
out += "\\u2028";
i += 2;
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
&& static_cast<uint8_t>(value[i+2]) == 0xa9) {
out += "\\u2029";
i += 2;
} else {
out += ch;
}
}
out += '"';
}
static void dump(const Json::array &values, string &out) {
bool first = true;
out += "[";
for (const auto &value : values) {
if (!first)
out += ", ";
value.dump(out);
first = false;
}
out += "]";
}
static void dump(const Json::object &values, string &out) {
bool first = true;
out += "{";
for (const auto &kv : values) {
if (!first)
out += ", ";
dump(kv.first, out);
out += ": ";
kv.second.dump(out);
first = false;
}
out += "}";
}
void Json::dump(string &out) const {
m_ptr->dump(out);
}
/* * * * * * * * * * * * * * * * * * * *
* Value wrappers
*/
template <Json::Type tag, typename T>
class Value : public JsonValue {
protected:
// Constructors
explicit Value(const T &value) : m_value(value) {}
explicit Value(T &&value) : m_value(move(value)) {}
// Get type tag
Json::Type type() const override {
return tag;
}
// Comparisons
bool equals(const JsonValue * other) const override {
return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
}
bool less(const JsonValue * other) const override {
return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
}
const T m_value;
void dump(string &out) const override { json11::dump(m_value, out); }
};
class JsonDouble final : public Value<Json::NUMBER, double> {
double number_value() const override { return m_value; }
int int_value() const override { return static_cast<int>(m_value); }
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
public:
explicit JsonDouble(double value) : Value(value) {}
};
class JsonInt final : public Value<Json::NUMBER, int> {
double number_value() const override { return m_value; }
int int_value() const override { return m_value; }
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
public:
explicit JsonInt(int value) : Value(value) {}
};
class JsonBoolean final : public Value<Json::BOOL, bool> {
bool bool_value() const override { return m_value; }
public:
explicit JsonBoolean(bool value) : Value(value) {}
};
class JsonString final : public Value<Json::STRING, string> {
const string &string_value() const override { return m_value; }
public:
explicit JsonString(const string &value) : Value(value) {}
explicit JsonString(string &&value) : Value(move(value)) {}
};
class JsonArray final : public Value<Json::ARRAY, Json::array> {
const Json::array &array_items() const override { return m_value; }
const Json & operator[](size_t i) const override;
public:
explicit JsonArray(const Json::array &value) : Value(value) {}
explicit JsonArray(Json::array &&value) : Value(move(value)) {}
};
class JsonObject final : public Value<Json::OBJECT, Json::object> {
const Json::object &object_items() const override { return m_value; }
const Json & operator[](const string &key) const override;
public:
explicit JsonObject(const Json::object &value) : Value(value) {}
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
};
class JsonNull final : public Value<Json::NUL, NullStruct> {
public:
JsonNull() : Value({}) {}
};
/* * * * * * * * * * * * * * * * * * * *
* Static globals - static-init-safe
*/
struct Statics {
const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
const string empty_string;
const vector<Json> empty_vector;
const map<string, Json> empty_map;
Statics() {}
};
static const Statics & statics() {
static const Statics s {};
return s;
}
static const Json & static_null() {
// This has to be separate, not in Statics, because Json() accesses statics().null.
static const Json json_null;
return json_null;
}
/* * * * * * * * * * * * * * * * * * * *
* Constructors
*/
Json::Json() noexcept : m_ptr(statics().null) {}
Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
/* * * * * * * * * * * * * * * * * * * *
* Accessors
*/
Json::Type Json::type() const { return m_ptr->type(); }
double Json::number_value() const { return m_ptr->number_value(); }
int Json::int_value() const { return m_ptr->int_value(); }
bool Json::bool_value() const { return m_ptr->bool_value(); }
const string & Json::string_value() const { return m_ptr->string_value(); }
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
double JsonValue::number_value() const { return 0; }
int JsonValue::int_value() const { return 0; }
bool JsonValue::bool_value() const { return false; }
const string & JsonValue::string_value() const { return statics().empty_string; }
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
const Json & JsonObject::operator[] (const string &key) const {
auto iter = m_value.find(key);
return (iter == m_value.end()) ? static_null() : iter->second;
}
const Json & JsonArray::operator[] (size_t i) const {
if (i >= m_value.size()) return static_null();
else return m_value[i];
}
/* * * * * * * * * * * * * * * * * * * *
* Comparison
*/
bool Json::operator== (const Json &other) const {
if (m_ptr == other.m_ptr)
return true;
if (m_ptr->type() != other.m_ptr->type())
return false;
return m_ptr->equals(other.m_ptr.get());
}
bool Json::operator< (const Json &other) const {
if (m_ptr == other.m_ptr)
return false;
if (m_ptr->type() != other.m_ptr->type())
return m_ptr->type() < other.m_ptr->type();
return m_ptr->less(other.m_ptr.get());
}
/* * * * * * * * * * * * * * * * * * * *
* Parsing
*/
/* esc(c)
*
* Format char c suitable for printing in an error message.
*/
static inline string esc(char c) {
char buf[12];
if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
} else {
snprintf(buf, sizeof buf, "(%d)", c);
}
return string(buf);
}
static inline bool in_range(long x, long lower, long upper) {
return (x >= lower && x <= upper);
}
namespace {
/* JsonParser
*
* Object that tracks all state of an in-progress parse.
*/
struct JsonParser final {
/* State
*/
const string &str;
size_t i;
string &err;
bool failed;
const JsonParse strategy;
/* fail(msg, err_ret = Json())
*
* Mark this parse as failed.
*/
Json fail(string &&msg) {
return fail(move(msg), Json());
}
template <typename T>
T fail(string &&msg, const T err_ret) {
if (!failed)
err = std::move(msg);
failed = true;
return err_ret;
}
/* consume_whitespace()
*
* Advance until the current character is non-whitespace.
*/
void consume_whitespace() {
while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
i++;
}
/* consume_comment()
*
* Advance comments (c-style inline and multiline).
*/
bool consume_comment() {
bool comment_found = false;
if (str[i] == '/') {
i++;
if (i == str.size())
return fail("unexpected end of input after start of comment", false);
if (str[i] == '/') { // inline comment
i++;
// advance until next line, or end of input
while (i < str.size() && str[i] != '\n') {
i++;
}
comment_found = true;
}
else if (str[i] == '*') { // multiline comment
i++;
if (i > str.size()-2)
return fail("unexpected end of input inside multi-line comment", false);
// advance until closing tokens
while (!(str[i] == '*' && str[i+1] == '/')) {
i++;
if (i > str.size()-2)
return fail(
"unexpected end of input inside multi-line comment", false);
}
i += 2;
comment_found = true;
}
else
return fail("malformed comment", false);
}
return comment_found;
}
/* consume_garbage()
*
* Advance until the current character is non-whitespace and non-comment.
*/
void consume_garbage() {
consume_whitespace();
if(strategy == JsonParse::COMMENTS) {
bool comment_found = false;
do {
comment_found = consume_comment();
if (failed) return;
consume_whitespace();
}
while(comment_found);
}
}
/* get_next_token()
*
* Return the next non-whitespace character. If the end of the input is reached,
* flag an error and return 0.
*/
char get_next_token() {
consume_garbage();
if (failed) return static_cast<char>(0);
if (i == str.size())
return fail("unexpected end of input", static_cast<char>(0));
return str[i++];
}
/* encode_utf8(pt, out)
*
* Encode pt as UTF-8 and add it to out.
*/
void encode_utf8(long pt, string & out) {
if (pt < 0)
return;
if (pt < 0x80) {
out += static_cast<char>(pt);
} else if (pt < 0x800) {
out += static_cast<char>((pt >> 6) | 0xC0);
out += static_cast<char>((pt & 0x3F) | 0x80);
} else if (pt < 0x10000) {
out += static_cast<char>((pt >> 12) | 0xE0);
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
out += static_cast<char>((pt & 0x3F) | 0x80);
} else {
out += static_cast<char>((pt >> 18) | 0xF0);
out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
out += static_cast<char>((pt & 0x3F) | 0x80);
}
}
/* parse_string()
*
* Parse a string, starting at the current position.
*/
string parse_string() {
string out;
long last_escaped_codepoint = -1;
while (true) {
if (i == str.size())
return fail("unexpected end of input in string", "");
char ch = str[i++];
if (ch == '"') {
encode_utf8(last_escaped_codepoint, out);
return out;
}
if (in_range(ch, 0, 0x1f))
return fail("unescaped " + esc(ch) + " in string", "");
// The usual case: non-escaped characters
if (ch != '\\') {
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = -1;
out += ch;
continue;
}
// Handle escapes
if (i == str.size())
return fail("unexpected end of input in string", "");
ch = str[i++];
if (ch == 'u') {
// Extract 4-byte escape sequence
string esc = str.substr(i, 4);
// Explicitly check length of the substring. The following loop
// relies on std::string returning the terminating NUL when
// accessing str[length]. Checking here reduces brittleness.
if (esc.length() < 4) {
return fail("bad \\u escape: " + esc, "");
}
for (size_t j = 0; j < 4; j++) {
if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
&& !in_range(esc[j], '0', '9'))
return fail("bad \\u escape: " + esc, "");
}
long codepoint = strtol(esc.data(), nullptr, 16);
// JSON specifies that characters outside the BMP shall be encoded as a pair
// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
// whether we're in the middle of such a beast: the previous codepoint was an
// escaped lead (high) surrogate, and this is a trail (low) surrogate.
if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
&& in_range(codepoint, 0xDC00, 0xDFFF)) {
// Reassemble the two surrogate pairs into one astral-plane character, per
// the UTF-16 algorithm.
encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
| (codepoint - 0xDC00)) + 0x10000, out);
last_escaped_codepoint = -1;
} else {
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = codepoint;
}
i += 4;
continue;
}
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = -1;
if (ch == 'b') {
out += '\b';
} else if (ch == 'f') {
out += '\f';
} else if (ch == 'n') {
out += '\n';
} else if (ch == 'r') {
out += '\r';
} else if (ch == 't') {
out += '\t';
} else if (ch == '"' || ch == '\\' || ch == '/') {
out += ch;
} else {
return fail("invalid escape character " + esc(ch), "");
}
}
}
/* parse_number()
*
* Parse a double.
*/
Json parse_number() {
size_t start_pos = i;
if (str[i] == '-')
i++;
// Integer part
if (str[i] == '0') {
i++;
if (in_range(str[i], '0', '9'))
return fail("leading 0s not permitted in numbers");
} else if (in_range(str[i], '1', '9')) {
i++;
while (in_range(str[i], '0', '9'))
i++;
} else {
return fail("invalid " + esc(str[i]) + " in number");
}
if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
return std::atoi(str.c_str() + start_pos);
}
// Decimal part
if (str[i] == '.') {
i++;
if (!in_range(str[i], '0', '9'))
return fail("at least one digit required in fractional part");
while (in_range(str[i], '0', '9'))
i++;
}
// Exponent part
if (str[i] == 'e' || str[i] == 'E') {
i++;
if (str[i] == '+' || str[i] == '-')
i++;
if (!in_range(str[i], '0', '9'))
return fail("at least one digit required in exponent");
while (in_range(str[i], '0', '9'))
i++;
}
return std::strtod(str.c_str() + start_pos, nullptr);
}
/* expect(str, res)
*
* Expect that 'str' starts at the character that was just read. If it does, advance
* the input and return res. If not, flag an error.
*/
Json expect(const string &expected, Json res) {
assert(i != 0);
i--;
if (str.compare(i, expected.length(), expected) == 0) {
i += expected.length();
return res;
} else {
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
}
}
/* parse_json()
*
* Parse a JSON object.
*/
Json parse_json(int depth) {
if (depth > max_depth) {
return fail("exceeded maximum nesting depth");
}
char ch = get_next_token();
if (failed)
return Json();
if (ch == '-' || (ch >= '0' && ch <= '9')) {
i--;
return parse_number();
}
if (ch == 't')
return expect("true", true);
if (ch == 'f')
return expect("false", false);
if (ch == 'n')
return expect("null", Json());
if (ch == '"')
return parse_string();
if (ch == '{') {
map<string, Json> data;
ch = get_next_token();
if (ch == '}')
return data;
while (1) {
if (ch != '"')
return fail("expected '\"' in object, got " + esc(ch));
string key = parse_string();
if (failed)
return Json();
ch = get_next_token();
if (ch != ':')
return fail("expected ':' in object, got " + esc(ch));
data[std::move(key)] = parse_json(depth + 1);
if (failed)
return Json();
ch = get_next_token();
if (ch == '}')
break;
if (ch != ',')
return fail("expected ',' in object, got " + esc(ch));
ch = get_next_token();
}
return data;
}
if (ch == '[') {
vector<Json> data;
ch = get_next_token();
if (ch == ']')
return data;
while (1) {
i--;
data.push_back(parse_json(depth + 1));
if (failed)
return Json();
ch = get_next_token();
if (ch == ']')
break;
if (ch != ',')
return fail("expected ',' in list, got " + esc(ch));
ch = get_next_token();
(void)ch;
}
return data;
}
return fail("expected value, got " + esc(ch));
}
};
}//namespace {
Json Json::parse(const string &in, string &err, JsonParse strategy) {
JsonParser parser { in, 0, err, false, strategy };
Json result = parser.parse_json(0);
// Check for any trailing garbage
parser.consume_garbage();
if (parser.failed)
return Json();
if (parser.i != in.size())
return parser.fail("unexpected trailing " + esc(in[parser.i]));
return result;
}
// Documented in json11.hpp
vector<Json> Json::parse_multi(const string &in,
std::string::size_type &parser_stop_pos,
string &err,
JsonParse strategy) {
JsonParser parser { in, 0, err, false, strategy };
parser_stop_pos = 0;
vector<Json> json_vec;
while (parser.i != in.size() && !parser.failed) {
json_vec.push_back(parser.parse_json(0));
if (parser.failed)
break;
// Check for another object
parser.consume_garbage();
if (parser.failed)
break;
parser_stop_pos = parser.i;
}
return json_vec;
}
/* * * * * * * * * * * * * * * * * * * *
* Shape-checking
*/
bool Json::has_shape(const shape & types, string & err) const {
if (!is_object()) {
err = "expected JSON object, got " + dump();
return false;
}
const auto& obj_items = object_items();
for (auto & item : types) {
const auto it = obj_items.find(item.first);
if (it == obj_items.cend() || it->second.type() != item.second) {
err = "bad type for " + item.first + " in " + dump();
return false;
}
}
return true;
}
} // namespace json11

232
3rdparty/json11/json11.hpp vendored Normal file
View File

@ -0,0 +1,232 @@
/* json11
*
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
*
* The core object provided by the library is json11::Json. A Json object represents any JSON
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
* object (std::map).
*
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
* Json::parse (static) to parse a std::string as a Json object.
*
* Internally, the various types of Json object are represented by the JsonValue class
* hierarchy.
*
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
* so some JSON implementations distinguish between integers and floating-point numbers, while
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
* to JSON that will be *silently* changed by a round-trip through those implementations.
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
* provides integer helpers.
*
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
* will be exact for +/- 275 years.)
*/
/* Copyright (c) 2013 Dropbox, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <initializer_list>
#ifdef _MSC_VER
#if _MSC_VER <= 1800 // VS 2013
#ifndef noexcept
#define noexcept throw()
#endif
#ifndef snprintf
#define snprintf _snprintf_s
#endif
#endif
#endif
namespace json11 {
enum JsonParse {
STANDARD, COMMENTS
};
class JsonValue;
class Json final {
public:
// Types
enum Type {
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
};
// Array and object typedefs
typedef std::vector<Json> array;
typedef std::map<std::string, Json> object;
// Constructors for the various types of JSON value.
Json() noexcept; // NUL
Json(std::nullptr_t) noexcept; // NUL
Json(double value); // NUMBER
Json(int value); // NUMBER
Json(bool value); // BOOL
Json(const std::string &value); // STRING
Json(std::string &&value); // STRING
Json(const char * value); // STRING
Json(const array &values); // ARRAY
Json(array &&values); // ARRAY
Json(const object &values); // OBJECT
Json(object &&values); // OBJECT
// Implicit constructor: anything with a to_json() function.
template <class T, class = decltype(&T::to_json)>
Json(const T & t) : Json(t.to_json()) {}
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
template <class M, typename std::enable_if<
std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
int>::type = 0>
Json(const M & m) : Json(object(m.begin(), m.end())) {}
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
template <class V, typename std::enable_if<
std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
int>::type = 0>
Json(const V & v) : Json(array(v.begin(), v.end())) {}
// This prevents Json(some_pointer) from accidentally producing a bool. Use
// Json(bool(some_pointer)) if that behavior is desired.
Json(void *) = delete;
// Accessors
Type type() const;
bool is_null() const { return type() == NUL; }
bool is_number() const { return type() == NUMBER; }
bool is_bool() const { return type() == BOOL; }
bool is_string() const { return type() == STRING; }
bool is_array() const { return type() == ARRAY; }
bool is_object() const { return type() == OBJECT; }
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
// distinguish between integer and non-integer numbers - number_value() and int_value()
// can both be applied to a NUMBER-typed object.
double number_value() const;
int int_value() const;
// Return the enclosed value if this is a boolean, false otherwise.
bool bool_value() const;
// Return the enclosed string if this is a string, "" otherwise.
const std::string &string_value() const;
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
const array &array_items() const;
// Return the enclosed std::map if this is an object, or an empty map otherwise.
const object &object_items() const;
// Return a reference to arr[i] if this is an array, Json() otherwise.
const Json & operator[](size_t i) const;
// Return a reference to obj[key] if this is an object, Json() otherwise.
const Json & operator[](const std::string &key) const;
// Serialize.
void dump(std::string &out) const;
std::string dump() const {
std::string out;
dump(out);
return out;
}
// Parse. If parse fails, return Json() and assign an error message to err.
static Json parse(const std::string & in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD);
static Json parse(const char * in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD) {
if (in) {
return parse(std::string(in), err, strategy);
} else {
err = "null input";
return nullptr;
}
}
// Parse multiple objects, concatenated or separated by whitespace
static std::vector<Json> parse_multi(
const std::string & in,
std::string::size_type & parser_stop_pos,
std::string & err,
JsonParse strategy = JsonParse::STANDARD);
static inline std::vector<Json> parse_multi(
const std::string & in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD) {
std::string::size_type parser_stop_pos;
return parse_multi(in, parser_stop_pos, err, strategy);
}
bool operator== (const Json &rhs) const;
bool operator< (const Json &rhs) const;
bool operator!= (const Json &rhs) const { return !(*this == rhs); }
bool operator<= (const Json &rhs) const { return !(rhs < *this); }
bool operator> (const Json &rhs) const { return (rhs < *this); }
bool operator>= (const Json &rhs) const { return !(*this < rhs); }
/* has_shape(types, err)
*
* Return true if this is a JSON object and, for each item in types, has a field of
* the given type. If not, return false and set err to a descriptive message.
*/
typedef std::initializer_list<std::pair<std::string, Type>> shape;
bool has_shape(const shape & types, std::string & err) const;
private:
std::shared_ptr<JsonValue> m_ptr;
};
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
class JsonValue {
protected:
friend class Json;
friend class JsonInt;
friend class JsonDouble;
virtual Json::Type type() const = 0;
virtual bool equals(const JsonValue * other) const = 0;
virtual bool less(const JsonValue * other) const = 0;
virtual void dump(std::string &out) const = 0;
virtual double number_value() const;
virtual int int_value() const;
virtual bool bool_value() const;
virtual const std::string &string_value() const;
virtual const Json::array &array_items() const;
virtual const Json &operator[](size_t i) const;
virtual const Json::object &object_items() const;
virtual const Json &operator[](const std::string &key) const;
virtual ~JsonValue() {}
};
} // namespace json11

View File

@ -191,7 +191,7 @@ if (BUILD_PYTHON)
endif ()
endif()
include_directories(common/ json/ ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})
include_directories(common/ json/ frontend/ 3rdparty/json11/ ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})
if(BUILD_HEAP)
find_package (Eigen3 REQUIRED NO_MODULE)
@ -202,7 +202,10 @@ endif()
aux_source_directory(common/ COMMON_SRC_FILES)
aux_source_directory(json/ JSON_PARSER_FILES)
set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES})
aux_source_directory(3rdparty/json11 EXT_JSON11_FILES)
aux_source_directory(frontend/ FRONTEND_FILES)
set(COMMON_FILES ${COMMON_SRC_FILES} ${EXT_JSON11_FILES} ${JSON_PARSER_FILES} ${FRONTEND_FILES})
set(CMAKE_BUILD_TYPE Release)
if(MINGW)

View File

@ -5,6 +5,10 @@ readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_conte
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls, "nets");
readonly_wrapper<Context, decltype(&Context::net_aliases), &Context::net_aliases, wrap_context<AliasMap &>>::def_wrap(
ctx_cls, "net_aliases");
readonly_wrapper<Context, decltype(&Context::hierarchy), &Context::hierarchy, wrap_context<HierarchyMap &>>::def_wrap(
ctx_cls, "hierarchy");
readwrite_wrapper<Context, decltype(&Context::top_module), &Context::top_module, conv_to_str<IdString>,
conv_from_str<IdString>>::def_wrap(ctx_cls, "top_module");
fn_wrapper_1a<Context, decltype(&Context::getNetByAlias), &Context::getNetByAlias, deref_and_wrap<NetInfo>,
conv_from_str<IdString>>::def_wrap(ctx_cls, "getNetByAlias");

View File

@ -35,7 +35,7 @@
#include <iostream>
#include "command.h"
#include "design_utils.h"
#include "jsonparse.h"
#include "json_frontend.h"
#include "jsonwrite.h"
#include "log.h"
#include "timing.h"
@ -265,9 +265,8 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>();
std::ifstream f(filename);
if (!parse_json_file(f, filename, w.getContext()))
if (!parse_json(f, filename, w.getContext()))
log_error("Loading design failed.\n");
customAfterLoad(w.getContext());
w.notifyChangeContext();
w.updateActions();
@ -284,7 +283,7 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>();
std::ifstream f(filename);
if (!parse_json_file(f, filename, ctx.get()))
if (!parse_json(f, filename, ctx.get()))
log_error("Loading design failed.\n");
customAfterLoad(ctx.get());
@ -382,12 +381,6 @@ int CommandHandler::exec()
return 0;
std::unordered_map<std::string, Property> values;
if (vm.count("json")) {
std::string filename = vm["json"].as<std::string>();
std::ifstream f(filename);
if (!load_json_settings(f, filename, values))
log_error("Loading design failed.\n");
}
std::unique_ptr<Context> ctx = createContext(values);
setupContext(ctx.get());
setupArchContext(ctx.get());
@ -404,17 +397,12 @@ std::unique_ptr<Context> CommandHandler::load_json(std::string filename)
{
vm.clear();
std::unordered_map<std::string, Property> values;
{
std::ifstream f(filename);
if (!load_json_settings(f, filename, values))
log_error("Loading design failed.\n");
}
std::unique_ptr<Context> ctx = createContext(values);
setupContext(ctx.get());
setupArchContext(ctx.get());
{
std::ifstream f(filename);
if (!parse_json_file(f, filename, ctx.get()))
if (!parse_json(f, filename, ctx.get()))
log_error("Loading design failed.\n");
}
customAfterLoad(ctx.get());

View File

@ -21,6 +21,7 @@
#include <boost/algorithm/string.hpp>
#include "design_utils.h"
#include "log.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
@ -522,7 +523,16 @@ void BaseCtx::createRectangularRegion(IdString name, int x0, int y0, int x1, int
void BaseCtx::addBelToRegion(IdString name, BelId bel) { region[name]->bels.insert(bel); }
void BaseCtx::constrainCellToRegion(IdString cell, IdString region_name)
{
cells[cell]->region = region[region_name].get();
// Support hierarchical cells as well as leaf ones
if (hierarchy.count(cell)) {
auto &hc = hierarchy.at(cell);
for (auto &lc : hc.leaf_cells)
constrainCellToRegion(lc.second, region_name);
for (auto &hsc : hc.hier_cells)
constrainCellToRegion(hsc.second, region_name);
}
if (cells.count(cell))
cells.at(cell)->region = region[region_name].get();
}
DecalXY BaseCtx::constructDecalXY(DecalId decal, float x, float y)
{
@ -723,4 +733,76 @@ void BaseCtx::copyBelPorts(IdString cell, BelId bel)
}
}
namespace {
struct FixupHierarchyWorker
{
FixupHierarchyWorker(Context *ctx) : ctx(ctx){};
Context *ctx;
void run()
{
trim_hierarchy(ctx->top_module);
rebuild_hierarchy();
};
// Remove cells and nets that no longer exist in the netlist
std::vector<IdString> todelete_cells, todelete_nets;
void trim_hierarchy(IdString path)
{
auto &h = ctx->hierarchy.at(path);
todelete_cells.clear();
todelete_nets.clear();
for (auto &lc : h.leaf_cells) {
if (!ctx->cells.count(lc.second))
todelete_cells.push_back(lc.first);
}
for (auto &n : h.nets)
if (!ctx->nets.count(n.second))
todelete_nets.push_back(n.first);
for (auto tdc : todelete_cells) {
h.leaf_cells_by_gname.erase(h.leaf_cells.at(tdc));
h.leaf_cells.erase(tdc);
}
for (auto tdn : todelete_nets) {
h.nets_by_gname.erase(h.nets.at(tdn));
h.nets.erase(tdn);
}
for (auto &sc : h.hier_cells)
trim_hierarchy(sc.second);
}
IdString construct_local_name(HierarchicalCell &hc, IdString global_name, bool is_cell)
{
std::string gn = global_name.str(ctx);
auto dp = gn.find_last_of('.');
if (dp != std::string::npos)
gn = gn.substr(dp + 1);
IdString name = ctx->id(gn);
// Make sure name is unique
int adder = 0;
while (is_cell ? hc.leaf_cells.count(name) : hc.nets.count(name)) {
++adder;
name = ctx->id(gn + "$" + std::to_string(adder));
}
return name;
}
// Update hierarchy structure for nets and cells that have hiercell set
void rebuild_hierarchy()
{
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->hierpath == IdString())
ci->hierpath = ctx->top_module;
auto &hc = ctx->hierarchy.at(ci->hierpath);
if (hc.leaf_cells_by_gname.count(ci->name))
continue; // already known
IdString local_name = construct_local_name(hc, ci->name, true);
hc.leaf_cells_by_gname[ci->name] = local_name;
hc.leaf_cells[local_name] = ci->name;
}
}
};
} // namespace
void Context::fixupHierarchy() { FixupHierarchyWorker(this).run(); }
NEXTPNR_NAMESPACE_END

View File

@ -387,7 +387,7 @@ struct ClockConstraint;
struct NetInfo : ArchNetInfo
{
IdString name;
IdString name, hierpath;
int32_t udata = 0;
PortRef driver;
@ -397,6 +397,8 @@ struct NetInfo : ArchNetInfo
// wire -> uphill_pip
std::unordered_map<WireId, PipMap> wires;
std::vector<IdString> aliases; // entries in net_aliases that point to this net
std::unique_ptr<ClockConstraint> clkconstr;
TimingConstrObjectId tmg_id;
@ -421,7 +423,7 @@ struct PortInfo
struct CellInfo : ArchCellInfo
{
IdString name, type;
IdString name, type, hierpath;
int32_t udata;
std::unordered_map<IdString, PortInfo> ports;
@ -525,6 +527,31 @@ struct TimingConstraint
std::unordered_set<TimingConstrObjectId> to;
};
// Represents the contents of a non-leaf cell in a design
// with hierarchy
struct HierarchicalPort
{
IdString name;
PortType dir;
std::vector<IdString> nets;
int offset;
bool upto;
};
struct HierarchicalCell
{
IdString name, type, parent, fullpath;
// Name inside cell instance -> global name
std::unordered_map<IdString, IdString> leaf_cells, nets;
// Global name -> name inside cell instance
std::unordered_map<IdString, IdString> leaf_cells_by_gname, nets_by_gname;
// Cell port to net
std::unordered_map<IdString, HierarchicalPort> ports;
// Name inside cell instance -> global name
std::unordered_map<IdString, IdString> hier_cells;
};
inline bool operator==(const std::pair<const TimingConstrObjectId, TimingConstraint *> &a,
const std::pair<TimingConstrObjectId, TimingConstraint *> &b)
{
@ -618,6 +645,11 @@ struct BaseCtx
std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets;
std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells;
// Hierarchical (non-leaf) cells by full path
std::unordered_map<IdString, HierarchicalCell> hierarchy;
// This is the root of the above structure
IdString top_module;
// Aliases for nets, which may have more than one name due to assignments and hierarchy
std::unordered_map<IdString, IdString> net_aliases;
@ -806,6 +838,10 @@ struct Context : Arch, DeterministicRNG
bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr,
std::unordered_map<WireId, PipId> *route = nullptr, bool useEstimate = true);
// --------------------------------------------------------------
// call after changing hierpath or adding/removing nets and cells
void fixupHierarchy();
// --------------------------------------------------------------
// provided by sdf.cc

View File

@ -22,7 +22,7 @@
#include "pybindings.h"
#include "arch_pybindings.h"
#include "jsonparse.h"
#include "json_frontend.h"
#include "log.h"
#include "nextpnr.h"
@ -53,7 +53,7 @@ void parse_json_shim(std::string filename, Context &d)
std::ifstream inf(filename);
if (!inf)
throw std::runtime_error("failed to open file " + filename);
parse_json_file(inf, filename, &d);
parse_json(inf, filename, &d);
}
// Create a new Chip and load design from json file
@ -131,7 +131,7 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
typedef std::unordered_map<IdString, Property> AttrMap;
typedef std::unordered_map<IdString, PortInfo> PortMap;
typedef std::unordered_map<IdString, IdString> PinMap;
typedef std::unordered_map<IdString, IdString> IdIdMap;
typedef std::unordered_map<IdString, std::unique_ptr<Region>> RegionMap;
class_<BaseCtx, BaseCtx *, boost::noncopyable>("BaseCtx", no_init);
@ -157,8 +157,8 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
conv_from_str<BelId>>::def_wrap(ci_cls, "bel");
readwrite_wrapper<CellInfo &, decltype(&CellInfo::belStrength), &CellInfo::belStrength, pass_through<PlaceStrength>,
pass_through<PlaceStrength>>::def_wrap(ci_cls, "belStrength");
readonly_wrapper<CellInfo &, decltype(&CellInfo::pins), &CellInfo::pins, wrap_context<PinMap &>>::def_wrap(ci_cls,
"pins");
readonly_wrapper<CellInfo &, decltype(&CellInfo::pins), &CellInfo::pins, wrap_context<IdIdMap &>>::def_wrap(ci_cls,
"pins");
fn_wrapper_1a_v<CellInfo &, decltype(&CellInfo::addInput), &CellInfo::addInput, conv_from_str<IdString>>::def_wrap(
ci_cls, "addInput");
@ -230,9 +230,25 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
readonly_wrapper<Region &, decltype(&Region::wires), &Region::wires, wrap_context<WireSet &>>::def_wrap(region_cls,
"wires");
auto hierarchy_cls = class_<ContextualWrapper<HierarchicalCell &>>("HierarchicalCell", no_init);
readwrite_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::name), &HierarchicalCell::name,
conv_to_str<IdString>, conv_from_str<IdString>>::def_wrap(hierarchy_cls, "name");
readwrite_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::type), &HierarchicalCell::type,
conv_to_str<IdString>, conv_from_str<IdString>>::def_wrap(hierarchy_cls, "type");
readwrite_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::parent), &HierarchicalCell::parent,
conv_to_str<IdString>, conv_from_str<IdString>>::def_wrap(hierarchy_cls, "parent");
readwrite_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::fullpath), &HierarchicalCell::fullpath,
conv_to_str<IdString>, conv_from_str<IdString>>::def_wrap(hierarchy_cls, "fullpath");
readonly_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::leaf_cells), &HierarchicalCell::leaf_cells,
wrap_context<IdIdMap &>>::def_wrap(hierarchy_cls, "leaf_cells");
readonly_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::nets), &HierarchicalCell::nets,
wrap_context<IdIdMap &>>::def_wrap(hierarchy_cls, "nets");
readonly_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::hier_cells), &HierarchicalCell::hier_cells,
wrap_context<IdIdMap &>>::def_wrap(hierarchy_cls, "hier_cells");
WRAP_MAP(AttrMap, conv_to_str<Property>, "AttrMap");
WRAP_MAP(PortMap, wrap_context<PortInfo &>, "PortMap");
WRAP_MAP(PinMap, conv_to_str<IdString>, "PinMap");
WRAP_MAP(IdIdMap, conv_to_str<IdString>, "IdIdMap");
WRAP_MAP(WireMap, wrap_context<PipMap &>, "WireMap");
WRAP_MAP_UPTR(RegionMap, "RegionMap");

View File

@ -19,6 +19,7 @@ Other structures used by these basic structures include:
`CellInfo` instances have the following fields:
- `name` and `type` are `IdString`s containing the instance name, and type
- `hierpath` is name of the hierarchical cell containing the instance, for designs with hierarchy
- `ports` is a map from port name `IdString` to `PortInfo` structures for each cell port
- `bel` and `belStrength` contain the ID of the Bel the cell is placed onto; and placement strength of the cell; if placed. Placement/ripup should always be done by `Arch::bindBel` and `Arch::unbindBel` rather than by manipulating these fields.
- `params` and `attrs` store parameters and attributes - from the input JSON or assigned in flows to add metadata - by mapping from parameter name `IdString` to `Property`.
@ -34,6 +35,7 @@ Other structures used by these basic structures include:
`NetInfo` instances have the following fields:
- `name` is the IdString name of the net - for nets with multiple names, one name is chosen according to a set of rules by the JSON frontend
- `hierpath` is name of the hierarchical cell containing the instance, for designs with hierarchy
- `driver` refers to the source of the net using `PortRef`; `driver.cell == nullptr` means that the net is undriven. Nets must have zero or one driver only. The corresponding cell port must be an output and its `PortInfo::net` must refer back to this net.
- `users` contains a list of `PortRef` references to sink ports on the net. Nets can have zero or more sinks. Each corresponding cell port must be an input or inout; and its `PortInfo::net` must refer back to this net.
- `wires` is a map that stores the routing tree of a net, if the net is routed.
@ -70,4 +72,18 @@ The second is `ArchCellInfo` and `ArchNetInfo`. These are provided by architectu
- `getNetinfoSourceWire` gets the physical wire `WireId` associated with the source of a net
- `getNetinfoSinkWire` gets the physical wire `WireId` associated with a given sink (specified by `PortRef`)
- `getNetinfoRouteDelay` gets the routing delay - actual if the net is fully routed, estimated otherwise - between the source and a given sink of a net
- `getNetByAlias` returns the pointer to a net given any of its aliases - this should be used in preference to a direct lookup in `nets` whenever a net name is provided by the user
- `getNetByAlias` returns the pointer to a net given any of its aliases - this should be used in preference to a direct lookup in `nets` whenever a net name is provided by the user
## Hierarchy
As most place and route algorithms require a flattened netlist to work with (consider - each leaf cell instance must have its own bel), the primary netlist structures are flattened. However, some tasks such as floorplanning require an understanding of hierarchy.
`HierarchicalCell` is the main data structure for storing hierarchy. This represents an instance of a hierarchical, rather than leaf cell (leaf cells are represented by a `CellInfo`).
- `name` and `type` are the instance name and cell type
- `parent` is the hierarchical path of the parent cell, and `fullpath` is the hierarchical path of this cell
- `leaf_cells`, `nets` map from a name inside the hierarchical cell to a 'global' name in the flattened netlist (i.e. one that indexes into `ctx->{cells,nets}`)
- `leaf_cells_by_gname`, `nets_by_gname` are the inverse of the above maps; going from `{CellInfo,NetInfo}::name` to an instance name inside the cell
- `hier_cells` maps instance names of sub-hierarchical (non-leaf) cells to global names (indexing into `ctx->hierarchy`)
To preserve hierarchy during passes such as packing, ensure that `hierpath` is set on new cells derived from existing ones, and call `fixupHierarchy()` at the end to rebuild `HierarchicalCell` structures.

View File

@ -49,6 +49,7 @@ void arch_wrap_python()
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
typedef std::unordered_map<IdString, IdString> AliasMap;
typedef std::unordered_map<IdString, HierarchicalCell> HierarchyMap;
auto belpin_cls = class_<ContextualWrapper<BelPin>>("BelPin", no_init);
readonly_wrapper<BelPin, decltype(&BelPin::bel), &BelPin::bel, conv_to_str<BelId>>::def_wrap(belpin_cls, "bel");
@ -64,6 +65,7 @@ void arch_wrap_python()
WRAP_MAP_UPTR(CellMap, "IdCellMap");
WRAP_MAP_UPTR(NetMap, "IdNetMap");
WRAP_MAP(HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
}
NEXTPNR_NAMESPACE_END

View File

@ -233,6 +233,8 @@ static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellI
void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut)
{
if (lc->hierpath == IdString())
lc->hierpath = ff->hierpath;
bool has_ff = lc->ports.at(ctx->id("Q0")).net != nullptr || lc->ports.at(ctx->id("Q1")).net != nullptr;
std::string reg = "REG" + std::to_string(index);
set_param_safe(has_ff, lc, ctx->id("SRMODE"), str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE"));
@ -271,6 +273,8 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive
void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index)
{
if (lc->hierpath == IdString())
lc->hierpath = lut->hierpath;
lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] =
get_or_default(lut->params, ctx->id("INIT"), Property(0, 16));
replace_port(lut, ctx->id("A"), lc, ctx->id("A" + std::to_string(index)));
@ -282,6 +286,8 @@ void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index)
void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc)
{
if (lc->hierpath == IdString())
lc->hierpath = ccu->hierpath;
lc->params[ctx->id("MODE")] = std::string("CCU2");
lc->params[ctx->id("LUT0_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT0"), Property(0, 16));
lc->params[ctx->id("LUT1_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT1"), Property(0, 16));
@ -309,6 +315,8 @@ void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc)
void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc)
{
if (lc->hierpath == IdString())
lc->hierpath = ram->hierpath;
lc->params[ctx->id("MODE")] = std::string("RAMW");
replace_port(ram, ctx->id("WAD[0]"), lc, ctx->id("D0"));
replace_port(ram, ctx->id("WAD[1]"), lc, ctx->id("B0"));
@ -340,6 +348,8 @@ static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit)
void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index)
{
if (lc->hierpath == IdString())
lc->hierpath = ram->hierpath;
lc->params[ctx->id("MODE")] = std::string("DPRAM");
lc->params[ctx->id("WREMUX")] = str_or_default(ram->params, ctx->id("WREMUX"), "WRE");
lc->params[ctx->id("WCKMUX")] = str_or_default(ram->params, ctx->id("WCKMUX"), "WCK");

731
frontend/frontend_base.h Normal file
View File

@ -0,0 +1,731 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2019 David Shah <dave@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.
*
*/
/*
* Generic Frontend Framework
*
* This is designed to make it possible to build frontends for parsing any format isomorphic to Yosys JSON [1]
* with maximal inlining and minimal need for overhead such as runtime polymorphism or extra wrapper types.
*
* [1] http://www.clifford.at/yosys/cmd_write_json.html
*
* The frontend should implement a class referred to as FrontendType that defines the following type(def)s and
* functions:
*
* Types:
* ModuleDataType: corresponds to a single entry in "modules"
* ModulePortDataType: corresponds to a single entry in "ports" of a module
* CellDataType: corresponds to a single entry in "cells"
* NetnameDataType: corresponds to a single entry in "netnames"
* BitVectorDataType: corresponds to a signal/constant bit vector (e.g. a "connections" field)
*
* Functions:
*
* void foreach_module(Func) const;
* calls Func(const std::string &name, const ModuleDataType &mod);
* for each module in the netlist
*
* void foreach_port(const ModuleDataType &mod, Func) const;
* calls Func(const std::string &name, const ModulePortDataType &port);
* for each port of mod
*
* void foreach_cell(const ModuleDataType &mod, Func) const;
* calls Func(const std::string &name, const CellDataType &cell)
* for each cell of mod
*
* void foreach_netname(const ModuleDataType &mod, Func) const;
* calls Func(const std::string &name, const NetnameDataType &cell);
* for each netname entry of mod
*
* PortType get_port_dir(const ModulePortDataType &port) const;
* gets the PortType direction of a module port
*
* int get_array_offset(const ModulePortDataType &port) const;
* gets the start bit number of a port or netname entry
*
* bool is_array_upto(const ModulePortDataType &port) const;
* returns true if a port/net is an "upto" type port or netname entry
*
* const BitVectorDataType &get_port_bits(const ModulePortDataType &port) const;
* gets the bit vector of a module port
*
* const std::string& get_cell_type(const CellDataType &cell) const;
* gets the type of a cell
*
* void foreach_attr(const {ModuleDataType|CellDataType|ModulePortDataType|NetnameDataType} &obj, Func) const;
* calls Func(const std::string &name, const Property &value);
* for each attribute on a module, cell, module port or net
*
* void foreach_param(const CellDataType &obj, Func) const;
* calls Func(const std::string &name, const Property &value);
* for each parameter of a cell
*
* void foreach_setting(const ModuleDataType &obj, Func) const;
* calls Func(const std::string &name, const Property &value);
* for each module-level setting
*
* void foreach_port_dir(const CellDataType &cell, Func) const;
* calls Func(const std::string &name, PortType dir);
* for each port direction of a cell
*
* void foreach_port_conn(const CellDataType &cell, Func) const;
* calls Func(const std::string &name, const BitVectorDataType &conn);
* for each port connection of a cell
*
* const BitVectorDataType &get_net_bits(const NetnameDataType &net) const;
* gets the BitVector corresponding to the bits entry of a netname field
*
* int get_vector_length(const BitVectorDataType &bits) const;
* gets the length of a BitVector
*
* bool is_vector_bit_constant(const BitVectorDataType &bits, int i) const;
* returns true if bit <i> of bits is constant
*
* char get_vector_bit_constval(const BitVectorDataType &bits, int i) const;
* returns a char [01xz] corresponding to the constant value of bit <i>
*
* int get_vector_bit_signal(const BitVectorDataType &bits, int i) const;
* returns the signal number of vector bit <i>
*
*/
#include "design_utils.h"
#include "log.h"
#include "nextpnr.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
namespace {
// Used for hierarchy resolution
struct ModuleInfo
{
bool is_top = false, is_blackbox = false, is_whitebox = false;
inline bool is_box() const { return is_blackbox || is_whitebox; }
std::unordered_set<IdString> instantiated_celltypes;
};
template <typename FrontendType> struct GenericFrontend
{
GenericFrontend(Context *ctx, const FrontendType &impl) : ctx(ctx), impl(impl) {}
void operator()()
{
// Find which module is top
find_top_module();
HierModuleState m;
m.is_toplevel = true;
m.prefix = "";
m.path = top;
ctx->top_module = top;
// Do the actual import, starting from the top level module
import_module(m, top.str(ctx), top.str(ctx), mod_refs.at(top));
}
Context *ctx;
const FrontendType &impl;
using mod_dat_t = typename FrontendType::ModuleDataType;
using mod_port_dat_t = typename FrontendType::ModulePortDataType;
using cell_dat_t = typename FrontendType::CellDataType;
using netname_dat_t = typename FrontendType::NetnameDataType;
using bitvector_t = typename FrontendType::BitVectorDataType;
std::unordered_map<IdString, ModuleInfo> mods;
std::unordered_map<IdString, const mod_dat_t &> mod_refs;
IdString top;
// Process the list of modules and determine
// the top module
void find_top_module()
{
impl.foreach_module([&](const std::string &name, const mod_dat_t &mod) {
IdString mod_id = ctx->id(name);
auto &mi = mods[mod_id];
mod_refs.emplace(mod_id, mod);
impl.foreach_attr(mod, [&](const std::string &name, const Property &value) {
if (name == "top")
mi.is_top = (value.intval != 0);
else if (name == "blackbox")
mi.is_blackbox = (value.intval != 0);
else if (name == "whitebox")
mi.is_whitebox = (value.intval != 0);
});
impl.foreach_cell(mod, [&](const std::string &name, const cell_dat_t &cell) {
mi.instantiated_celltypes.insert(ctx->id(impl.get_cell_type(cell)));
});
});
// First of all, see if a top module has been manually specified
if (ctx->settings.count(ctx->id("frontend/top"))) {
IdString user_top = ctx->id(ctx->settings.at(ctx->id("frontend/top")).as_string());
if (!mods.count(user_top))
log_error("Top module '%s' not found!\n", ctx->nameOf(user_top));
top = user_top;
return;
}
// If not, look for a module with the top attribute set
IdString top_by_attr;
for (auto &mod : mods) {
if (mod.second.is_top && !mod.second.is_box()) {
if (top_by_attr != IdString())
log_error("Found multiple modules with (* top *) set (including %s and %s).\n",
ctx->nameOf(top_by_attr), ctx->nameOf(mod.first));
top_by_attr = mod.first;
}
}
if (top_by_attr != IdString()) {
top = top_by_attr;
return;
}
// Finally, attempt to autodetect the top module using hierarchy
// (a module that is not a box and is not used as a cell by any other module)
std::unordered_set<IdString> candidate_top;
for (auto &mod : mods)
if (!mod.second.is_box())
candidate_top.insert(mod.first);
for (auto &mod : mods)
for (auto &c : mod.second.instantiated_celltypes)
candidate_top.erase(c);
if (candidate_top.size() != 1) {
if (candidate_top.size() == 0)
log_info("No candidate top level modules.\n");
else
for (auto ctp : sorted(candidate_top))
log_info("Candidate top module: '%s'\n", ctx->nameOf(ctp));
log_error("Failed to autodetect top module, please specify using --top.\n");
}
top = *(candidate_top.begin());
}
// Create a unique name (guaranteed collision free) for a net or a cell; based on
// a base name and suffix. __unique__i will be be appended with increasing i
// if a collision is found until no collision
IdString unique_name(const std::string &base, const std::string &suffix, bool is_net)
{
IdString name;
int incr = 0;
do {
std::string comb = base + suffix;
if (incr > 0) {
comb += "__unique__";
comb += std::to_string(incr);
}
name = ctx->id(comb);
incr++;
} while (is_net ? ctx->nets.count(name) : ctx->cells.count(name));
return name;
}
// A flat index of map; designed to cope with merging nets where pointers to nets would go stale
// A net's udata points into this index
std::vector<NetInfo *> net_flatindex;
std::vector<std::vector<int>> net_old_indices; // the other indices of a net in net_flatindex for merging
// This structure contains some structures specific to the import of a module at
// a certain point in the hierarchy
struct HierModuleState
{
bool is_toplevel;
std::string prefix;
IdString parent_path, path;
// Map from index in module to "flat" index of nets
std::vector<int> index_to_net_flatindex;
// Get a reference to index_to_net; resizing if
// appropriate
int &net_by_idx(int idx)
{
NPNR_ASSERT(idx >= 0);
if (idx >= int(index_to_net_flatindex.size()))
index_to_net_flatindex.resize(idx + 1, -1);
return index_to_net_flatindex.at(idx);
}
std::unordered_map<IdString, std::vector<int>> port_to_bus;
// All of the names given to a net
std::vector<std::vector<std::string>> net_names;
};
void import_module(HierModuleState &m, const std::string &name, const std::string &type, const mod_dat_t &data)
{
NPNR_ASSERT(!ctx->hierarchy.count(m.path));
ctx->hierarchy[m.path].name = ctx->id(name);
ctx->hierarchy[m.path].type = ctx->id(type);
ctx->hierarchy[m.path].parent = m.parent_path;
ctx->hierarchy[m.path].fullpath = m.path;
std::vector<NetInfo *> index_to_net;
if (!m.is_toplevel) {
// Import port connections; for submodules only
import_port_connections(m, data);
} else {
// Just create a list of ports for netname resolution
impl.foreach_port(data,
[&](const std::string &name, const mod_port_dat_t &) { m.port_to_bus[ctx->id(name)]; });
// Import module-level attributes
impl.foreach_attr(
data, [&](const std::string &name, const Property &value) { ctx->attrs[ctx->id(name)] = value; });
// Import settings
impl.foreach_setting(data, [&](const std::string &name, const Property &value) {
ctx->settings[ctx->id(name)] = value;
});
}
import_module_netnames(m, data);
import_module_cells(m, data);
if (m.is_toplevel) {
import_toplevel_ports(m, data);
// Mark design as loaded through nextpnr
ctx->settings[ctx->id("synth")] = 1;
// Process nextpnr-specific attributes
ctx->attributesToArchInfo();
}
}
// Multiple labels might refer to the same net. Resolve conflicts for the primary name thus:
// - (toplevel) ports are always preferred
// - names with fewer $ are always prefered
// - between equal $ counts, fewer .s are prefered
// - ties are resolved alphabetically
bool prefer_netlabel(HierModuleState &m, const std::string &a, const std::string &b)
{
if (m.port_to_bus.count(ctx->id(a)))
return true;
if (m.port_to_bus.count(ctx->id(b)))
return false;
if (b.empty())
return true;
long a_dollars = std::count(a.begin(), a.end(), '$'), b_dollars = std::count(b.begin(), b.end(), '$');
if (a_dollars < b_dollars)
return true;
else if (a_dollars > b_dollars)
return false;
long a_dots = std::count(a.begin(), a.end(), '.'), b_dots = std::count(b.begin(), b.end(), '.');
if (a_dots < b_dots)
return true;
else if (a_dots > b_dots)
return false;
return a < b;
};
// Get a net by index in modulestate (not flatindex); creating it if it doesn't already exist
NetInfo *create_or_get_net(HierModuleState &m, int idx)
{
auto &midx = m.net_by_idx(idx);
if (midx != -1) {
return net_flatindex.at(midx);
} else {
std::string name;
if (idx < int(m.net_names.size()) && !m.net_names.at(idx).empty()) {
// Use the rule above to find the preferred name for a net
name = m.net_names.at(idx).at(0);
for (size_t j = 1; j < m.net_names.at(idx).size(); j++)
if (prefer_netlabel(m, m.net_names.at(idx).at(j), name))
name = m.net_names.at(idx).at(j);
} else {
name = "$frontend$" + std::to_string(idx);
}
NetInfo *net = ctx->createNet(unique_name(m.prefix, name, true));
// Add to the flat index of nets
net->udata = int(net_flatindex.size());
net_flatindex.push_back(net);
// Add to the module-level index of netsd
midx = net->udata;
// Create aliases for all possible names
if (idx < int(m.net_names.size()) && !m.net_names.at(idx).empty()) {
for (const auto &name : m.net_names.at(idx)) {
IdString name_id = ctx->id(name);
net->aliases.push_back(name_id);
ctx->net_aliases[name_id] = net->name;
}
} else {
net->aliases.push_back(net->name);
ctx->net_aliases[net->name] = net->name;
}
return net;
}
}
// Get the name of a vector bit given basename; settings and index
std::string get_bit_name(const std::string &base, int index, int length, int offset = 0, bool upto = false)
{
std::string port = base;
if (length == 1 && offset == 0)
return port;
int real_index;
if (upto)
real_index = offset + length - index - 1; // reversed ports like [0:7]
else
real_index = offset + index; // normal 'downto' ports like [7:0]
port += '[';
port += std::to_string(real_index);
port += ']';
return port;
}
// Import the netnames section of a module
void import_module_netnames(HierModuleState &m, const mod_dat_t &data)
{
impl.foreach_netname(data, [&](const std::string &basename, const netname_dat_t &nn) {
bool upto = impl.is_array_upto(nn);
int offset = impl.get_array_offset(nn);
const auto &bits = impl.get_net_bits(nn);
int width = impl.get_vector_length(bits);
for (int i = 0; i < width; i++) {
if (impl.is_vector_bit_constant(bits, i))
continue;
std::string bit_name = get_bit_name(basename, i, width, offset, upto);
int net_bit = impl.get_vector_bit_signal(bits, i);
int mapped_bit = m.net_by_idx(net_bit);
if (mapped_bit == -1) {
// Net doesn't exist yet. Add the name here to the list of candidate names so we have that for when
// we create it later
if (net_bit >= int(m.net_names.size()))
m.net_names.resize(net_bit + 1);
m.net_names.at(net_bit).push_back(bit_name);
} else {
// Net already exists; add this name as an alias
NetInfo *ni = net_flatindex.at(mapped_bit);
IdString alias_name = ctx->id(m.prefix + bit_name);
if (ctx->net_aliases.count(alias_name))
continue; // don't add duplicate aliases
ctx->net_aliases[alias_name] = ni->name;
ni->aliases.push_back(alias_name);
}
}
});
}
// Create a new constant net; given a hint for what the name should be and its value
NetInfo *create_constant_net(HierModuleState &m, const std::string &name_hint, char constval)
{
IdString name = unique_name(m.prefix, name_hint, true);
NetInfo *ni = ctx->createNet(name);
add_constant_driver(m, ni, constval);
return ni;
}
// Import a leaf cell - (white|black)box
void import_leaf_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd)
{
IdString inst_name = unique_name(m.prefix, name, false);
ctx->hierarchy[m.path].leaf_cells_by_gname[inst_name] = ctx->id(name);
ctx->hierarchy[m.path].leaf_cells[ctx->id(name)] = inst_name;
CellInfo *ci = ctx->createCell(inst_name, ctx->id(impl.get_cell_type(cd)));
ci->hierpath = m.path;
// Import port directions
std::unordered_map<IdString, PortType> port_dirs;
impl.foreach_port_dir(cd, [&](const std::string &port, PortType dir) { port_dirs[ctx->id(port)] = dir; });
// Import port connectivity
impl.foreach_port_conn(cd, [&](const std::string &name, const bitvector_t &bits) {
if (!port_dirs.count(ctx->id(name)))
log_error("Failed to get direction for port '%s' of cell '%s'\n", name.c_str(), inst_name.c_str(ctx));
PortType dir = port_dirs.at(ctx->id(name));
int width = impl.get_vector_length(bits);
for (int i = 0; i < width; i++) {
std::string port_bit_name = get_bit_name(name, i, width);
IdString port_bit_ids = ctx->id(port_bit_name);
// Create cell port
ci->ports[port_bit_ids].name = port_bit_ids;
ci->ports[port_bit_ids].type = dir;
// Resolve connectivity
NetInfo *net;
if (impl.is_vector_bit_constant(bits, i)) {
// Create a constant driver if one is needed
net = create_constant_net(m, name + "." + port_bit_name + "$const",
impl.get_vector_bit_constval(bits, i));
} else {
// Otherwise, lookup (creating if needed) the net with this index
net = create_or_get_net(m, impl.get_vector_bit_signal(bits, i));
}
NPNR_ASSERT(net != nullptr);
// Check for multiple drivers
if (dir == PORT_OUT && net->driver.cell != nullptr)
log_error("Net '%s' is multiply driven by cell ports %s.%s and %s.%s\n", ctx->nameOf(net),
ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), ctx->nameOf(inst_name),
port_bit_name.c_str());
connect_port(ctx, net, ci, port_bit_ids);
}
});
// Import attributes and parameters
impl.foreach_attr(cd,
[&](const std::string &name, const Property &value) { ci->attrs[ctx->id(name)] = value; });
impl.foreach_param(cd,
[&](const std::string &name, const Property &value) { ci->params[ctx->id(name)] = value; });
}
// Import a submodule cell
void import_submodule_cell(HierModuleState &m, const std::string &name, const cell_dat_t &cd)
{
HierModuleState submod;
submod.is_toplevel = false;
// Create mapping from submodule port to nets (referenced by index in flatindex)
impl.foreach_port_conn(cd, [&](const std::string &name, const bitvector_t &bits) {
int width = impl.get_vector_length(bits);
for (int i = 0; i < width; i++) {
// Index of port net in flatindex
int net_ref = -1;
if (impl.is_vector_bit_constant(bits, i)) {
// Create a constant driver if one is needed
std::string port_bit_name = get_bit_name(name, i, width);
NetInfo *cnet = create_constant_net(m, name + "." + port_bit_name + "$const",
impl.get_vector_bit_constval(bits, i));
cnet->udata = int(net_flatindex.size());
net_flatindex.push_back(cnet);
net_ref = cnet->udata;
} else {
// Otherwise, lookup (creating if needed) the net with given in-module index
net_ref = create_or_get_net(m, impl.get_vector_bit_signal(bits, i))->udata;
}
NPNR_ASSERT(net_ref != -1);
submod.port_to_bus[ctx->id(name)].push_back(net_ref);
}
});
// Create prefix for submodule
submod.prefix = m.prefix;
submod.prefix += name;
submod.prefix += '.';
submod.parent_path = m.path;
submod.path = ctx->id(m.path.str(ctx) + "/" + name);
ctx->hierarchy[m.path].hier_cells[ctx->id(name)] = submod.path;
// Do the submodule import
auto type = impl.get_cell_type(cd);
import_module(submod, name, type, mod_refs.at(ctx->id(type)));
}
// Import the cells section of a module
void import_module_cells(HierModuleState &m, const mod_dat_t &data)
{
impl.foreach_cell(data, [&](const std::string &cellname, const cell_dat_t &cd) {
IdString type = ctx->id(impl.get_cell_type(cd));
if (mods.count(type) && !mods.at(type).is_box()) {
// Module type is known; and not boxed. Import as a submodule by flattening hierarchy
import_submodule_cell(m, cellname, cd);
} else {
// Module type is unknown or boxes. Import as a leaf cell (nextpnr CellInfo)
import_leaf_cell(m, cellname, cd);
}
});
}
// Create a top level input/output buffer
CellInfo *create_iobuf(NetInfo *net, PortType dir, const std::string &name)
{
// Skip IOBUF insertion if this is a design checkpoint (where they will already exist)
if (ctx->settings.count(ctx->id("synth")))
return nullptr;
IdString name_id = ctx->id(name);
if (ctx->cells.count(name_id))
log_error("Cell '%s' of type '%s' with the same name as a top-level IO is not allowed.\n", name.c_str(),
ctx->cells.at(name_id)->type.c_str(ctx));
CellInfo *iobuf = ctx->createCell(name_id, ctx->id("unknown_iob"));
// Copy attributes from net to IOB
for (auto &attr : net->attrs)
iobuf->attrs[attr.first] = attr.second;
// What we do now depends on port type
if (dir == PORT_IN) {
iobuf->type = ctx->id("$nextpnr_ibuf");
iobuf->addOutput(ctx->id("O"));
if (net->driver.cell != nullptr) {
CellInfo *drv = net->driver.cell;
if (drv->type != ctx->id("$nextpnr_iobuf"))
log_error("Net '%s' is multiply driven by cell port %s.%s and top level input '%s'.\n",
ctx->nameOf(net), ctx->nameOf(drv), ctx->nameOf(net->driver.port), name.c_str());
// Special case: input, etc, directly drives inout
// Use the input net of the inout instead
net = drv->ports.at(ctx->id("I")).net;
}
NPNR_ASSERT(net->driver.cell == nullptr);
// Connect IBUF output and net
connect_port(ctx, net, iobuf, ctx->id("O"));
} else if (dir == PORT_OUT) {
iobuf->type = ctx->id("$nextpnr_obuf");
iobuf->addInput(ctx->id("I"));
// Connect IBUF input and net
connect_port(ctx, net, iobuf, ctx->id("I"));
} else if (dir == PORT_INOUT) {
iobuf->type = ctx->id("$nextpnr_iobuf");
iobuf->addInput(ctx->id("I"));
iobuf->addOutput(ctx->id("O"));
// Need to bifurcate the net to avoid multiple drivers and split
// the input/output parts of an inout
// Create a new net connecting only the current net's driver and the IOBUF input
// Then use the IOBUF output to drive all of the current net's users
NetInfo *split_iobuf_i = ctx->createNet(unique_name("", "$" + name + "$iobuf_i", true));
auto drv = net->driver;
if (drv.cell != nullptr) {
disconnect_port(ctx, drv.cell, drv.port);
drv.cell->ports[drv.port].net = nullptr;
connect_port(ctx, split_iobuf_i, drv.cell, drv.port);
}
connect_port(ctx, split_iobuf_i, iobuf, ctx->id("I"));
NPNR_ASSERT(net->driver.cell == nullptr);
connect_port(ctx, net, iobuf, ctx->id("O"));
}
PortInfo pinfo;
pinfo.name = name_id;
pinfo.net = net;
pinfo.type = dir;
ctx->ports[pinfo.name] = pinfo;
return iobuf;
}
// Import ports of the top level module
void import_toplevel_ports(HierModuleState &m, const mod_dat_t &data)
{
// For correct handling of inout ports driving other ports
// first import non-inouts then import inouts so that they bifurcate correctly
for (bool inout : {false, true}) {
impl.foreach_port(data, [&](const std::string &portname, const mod_port_dat_t &pd) {
const auto &port_bv = impl.get_port_bits(pd);
int offset = impl.get_array_offset(pd);
bool is_upto = impl.is_array_upto(pd);
int width = impl.get_vector_length(port_bv);
PortType dir = impl.get_port_dir(pd);
if ((dir == PORT_INOUT) != inout)
return;
for (int i = 0; i < width; i++) {
std::string pbit_name = get_bit_name(portname, i, width, offset, is_upto);
NetInfo *port_net = nullptr;
if (impl.is_vector_bit_constant(port_bv, i)) {
// Port bit is constant. Need to create a new constant net.
port_net =
create_constant_net(m, pbit_name + "$const", impl.get_vector_bit_constval(port_bv, i));
} else {
// Port bit is a signal. Need to create/get the associated net
port_net = create_or_get_net(m, impl.get_vector_bit_signal(port_bv, i));
}
create_iobuf(port_net, dir, pbit_name);
}
});
}
}
// Add a constant-driving VCC or GND cell to make a net constant
// (constval can be [01xz], x and z or no-ops)
int const_autoidx = 0;
void add_constant_driver(HierModuleState &m, NetInfo *net, char constval)
{
if (constval == 'x' || constval == 'z')
return; // 'x' or 'z' is the same as undriven
NPNR_ASSERT(constval == '0' || constval == '1');
IdString cell_name = unique_name(
m.prefix, net->name.str(ctx) + (constval == '1' ? "$VCC$" : "$GND$") + std::to_string(const_autoidx++),
false);
CellInfo *cc = ctx->createCell(cell_name, ctx->id(constval == '1' ? "VCC" : "GND"));
cc->ports[ctx->id("Y")].name = ctx->id("Y");
cc->ports[ctx->id("Y")].type = PORT_OUT;
if (net->driver.cell != nullptr)
log_error("Net '%s' is multiply driven by port %s.%s and constant '%c'\n", ctx->nameOf(net),
ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), constval);
connect_port(ctx, net, cc, ctx->id("Y"));
}
// Merge two nets - e.g. if one net in a submodule bifurcates to two output bits and therefore two different
// parent nets
void merge_nets(NetInfo *base, NetInfo *mergee)
{
// Resolve drivers
if (mergee->driver.cell != nullptr) {
if (base->driver.cell != nullptr)
log_error("Attempting to merge nets '%s' and '%s' due to port connectivity; but this would result in a "
"multiply driven net\n",
ctx->nameOf(base), ctx->nameOf(mergee));
else {
mergee->driver.cell->ports[mergee->driver.port].net = base;
base->driver = mergee->driver;
}
}
// Combine users
for (auto &usr : mergee->users) {
usr.cell->ports[usr.port].net = base;
base->users.push_back(usr);
}
// Point aliases to the new net
for (IdString alias : mergee->aliases) {
ctx->net_aliases[alias] = base->name;
base->aliases.push_back(alias);
}
// Create a new alias from mergee's name to new base name
ctx->net_aliases[mergee->name] = base->name;
// Update flat index of nets
for (auto old_idx : net_old_indices.at(mergee->udata)) {
net_old_indices.at(base->udata).push_back(old_idx);
net_flatindex.at(old_idx) = base;
}
net_old_indices.at(base->udata).push_back(mergee->udata);
net_flatindex.at(mergee->udata) = base;
net_old_indices.at(mergee->udata).clear();
// Remove merged net from context
ctx->nets.erase(mergee->name);
}
// Import connections between a submodule and its parent
void import_port_connections(HierModuleState &m, const mod_dat_t &data)
{
impl.foreach_port(data, [&](const std::string &name, const mod_port_dat_t &port) {
// CHECK: should disconnected module inputs really just be skipped; or is it better
// to insert a ground driver?
if (!m.port_to_bus.count(ctx->id(name)))
return;
auto &p2b = m.port_to_bus.at(ctx->id(name));
// Get direction and vector of port bits
PortType dir = impl.get_port_dir(port);
const auto &bv = impl.get_port_bits(port);
int bv_size = impl.get_vector_length(bv);
// Iterate over bits of port; making connections
for (int i = 0; i < std::min<int>(bv_size, p2b.size()); i++) {
int conn_net = p2b.at(i);
if (conn_net == -1)
continue;
NetInfo *conn_ni = net_flatindex.at(conn_net);
NPNR_ASSERT(conn_ni != nullptr);
if (impl.is_vector_bit_constant(bv, i)) {
// It is a constant, we might need to insert a constant driver here to drive the corresponding
// net in the parent
char constval = impl.get_vector_bit_constval(bv, i);
// Inputs cannot be driving a constant back to the parent
if (dir == PORT_IN)
log_error("Input port %s%s[%d] cannot be driving a constant '%c'.\n", m.prefix.c_str(),
name.c_str(), i, constval);
// Insert the constant driver
add_constant_driver(m, conn_ni, constval);
} else {
// If not driving a constant; simply make the port bit net index in the submodule correspond
// to connected net in the parent module
int &submod_net = m.net_by_idx(impl.get_vector_bit_signal(bv, i));
if (submod_net == -1) {
// A net at this index doesn't yet exist
// We can simply set this index to point to the net in the parent
submod_net = conn_net;
} else {
// A net at this index already exists (this would usually be a submodule net
// connected to more than one I/O port)
merge_nets(net_flatindex.at(submod_net), net_flatindex.at(conn_net));
}
}
}
});
}
};
} // namespace
NEXTPNR_NAMESPACE_END

203
frontend/json_frontend.cc Normal file
View File

@ -0,0 +1,203 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2019 David Shah <dave@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 "json_frontend.h"
#include "frontend_base.h"
#include "json11.hpp"
#include "log.h"
#include "nextpnr.h"
#include <streambuf>
NEXTPNR_NAMESPACE_BEGIN
using namespace json11;
struct JsonFrontendImpl
{
// See specification in frontend_base.h
JsonFrontendImpl(Json &root) : root(root){};
Json &root;
typedef const Json &ModuleDataType;
typedef const Json &ModulePortDataType;
typedef const Json &CellDataType;
typedef const Json &NetnameDataType;
typedef const Json::array &BitVectorDataType;
template <typename TFunc> void foreach_module(TFunc Func) const
{
for (const auto &mod : root.object_items())
Func(mod.first, mod.second);
}
template <typename TFunc> void foreach_port(const ModuleDataType &mod, TFunc Func) const
{
const auto &ports = mod["ports"];
if (ports.is_null())
return;
for (const auto &port : ports.object_items())
Func(port.first, port.second);
}
template <typename TFunc> void foreach_cell(const ModuleDataType &mod, TFunc Func) const
{
const auto &cells = mod["cells"];
if (cells.is_null())
return;
for (const auto &cell : cells.object_items())
Func(cell.first, cell.second);
}
template <typename TFunc> void foreach_netname(const ModuleDataType &mod, TFunc Func) const
{
const auto &netnames = mod["netnames"];
if (netnames.is_null())
return;
for (const auto &netname : netnames.object_items())
Func(netname.first, netname.second);
}
PortType lookup_portdir(const std::string &dir) const
{
if (dir == "input")
return PORT_IN;
else if (dir == "inout")
return PORT_INOUT;
else if (dir == "output")
return PORT_OUT;
else
NPNR_ASSERT_FALSE("invalid json port direction");
}
PortType get_port_dir(const ModulePortDataType &port) const
{
return lookup_portdir(port["direction"].string_value());
}
int get_array_offset(const Json &obj) const
{
auto offset = obj["offset"];
return offset.is_null() ? 0 : offset.int_value();
}
bool is_array_upto(const Json &obj) const
{
auto upto = obj["upto"];
return upto.is_null() ? false : bool(upto.int_value());
}
const BitVectorDataType &get_port_bits(const ModulePortDataType &port) const { return port["bits"].array_items(); }
const std::string &get_cell_type(const CellDataType &cell) const { return cell["type"].string_value(); }
Property parse_property(const Json &val) const
{
if (val.is_number())
return Property(val.int_value(), 32);
else
return Property::from_string(val.string_value());
}
template <typename TFunc> void foreach_attr(const Json &obj, TFunc Func) const
{
const auto &attrs = obj["attributes"];
if (attrs.is_null())
return;
for (const auto &attr : attrs.object_items()) {
Func(attr.first, parse_property(attr.second));
}
}
template <typename TFunc> void foreach_param(const Json &obj, TFunc Func) const
{
const auto &params = obj["parameters"];
if (params.is_null())
return;
for (const auto &param : params.object_items()) {
Func(param.first, parse_property(param.second));
}
}
template <typename TFunc> void foreach_setting(const Json &obj, TFunc Func) const
{
const auto &settings = obj["settings"];
if (settings.is_null())
return;
for (const auto &setting : settings.object_items()) {
Func(setting.first, parse_property(setting.second));
}
}
template <typename TFunc> void foreach_port_dir(const CellDataType &cell, TFunc Func) const
{
for (const auto &pdir : cell["port_directions"].object_items())
Func(pdir.first, lookup_portdir(pdir.second.string_value()));
}
template <typename TFunc> void foreach_port_conn(const CellDataType &cell, TFunc Func) const
{
for (const auto &pconn : cell["connections"].object_items())
Func(pconn.first, pconn.second.array_items());
}
const BitVectorDataType &get_net_bits(const NetnameDataType &net) const { return net["bits"].array_items(); }
int get_vector_length(const BitVectorDataType &bits) const { return int(bits.size()); }
bool is_vector_bit_constant(const BitVectorDataType &bits, int i) const
{
NPNR_ASSERT(i < int(bits.size()));
return bits[i].is_string();
}
char get_vector_bit_constval(const BitVectorDataType &bits, int i) const
{
auto s = bits.at(i).string_value();
NPNR_ASSERT(s.size() == 1);
return s.at(0);
}
int get_vector_bit_signal(const BitVectorDataType &bits, int i) const
{
NPNR_ASSERT(bits.at(i).is_number());
return bits.at(i).int_value();
}
};
bool parse_json(std::istream &in, const std::string &filename, Context *ctx)
{
Json root;
{
if (!in)
log_error("Failed to open JSON file '%s'.\n", filename.c_str());
std::string json_str((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
std::string error;
root = Json::parse(json_str, error, JsonParse::COMMENTS);
if (root.is_null())
log_error("Failed to parse JSON file '%s': %s.\n", filename.c_str(), error.c_str());
root = root["modules"];
if (root.is_null())
log_error("JSON file '%s' doesn't look like a netlist (doesn't contain \"modules\" key)\n",
filename.c_str());
}
GenericFrontend<JsonFrontendImpl>(ctx, JsonFrontendImpl(root))();
return true;
}
NEXTPNR_NAMESPACE_END

View File

@ -1,7 +1,7 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 SymbioticEDA
* Copyright (C) 2019 David Shah <dave@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
@ -17,18 +17,10 @@
*
*/
#ifndef JSON_PARSER
#define JSON_PARSER
#include <istream>
#include <string>
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
extern bool parse_json_file(std::istream &, std::string &, Context *);
extern bool load_json_settings(std::istream &f, std::string &filename,
std::unordered_map<std::string, Property> &values);
NEXTPNR_NAMESPACE_END
bool parse_json(std::istream &in, const std::string &filename, Context *ctx);
#endif
NEXTPNR_NAMESPACE_END

View File

@ -141,6 +141,7 @@ void arch_wrap_python()
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
typedef std::unordered_map<IdString, HierarchicalCell> HierarchyMap;
readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
"cells");
@ -231,6 +232,7 @@ void arch_wrap_python()
WRAP_MAP_UPTR(CellMap, "IdCellMap");
WRAP_MAP_UPTR(NetMap, "IdNetMap");
WRAP_MAP(HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
WRAP_VECTOR(const std::vector<IdString>, conv_to_str<IdString>);
}

View File

@ -28,7 +28,6 @@
#include <fstream>
#include "designwidget.h"
#include "fpgaviewwidget.h"
#include "jsonparse.h"
#include "jsonwrite.h"
#include "log.h"
#include "mainwindow.h"

View File

@ -27,7 +27,6 @@
#include <fstream>
#include "bitstream.h"
#include "design_utils.h"
#include "jsonparse.h"
#include "log.h"
#include "pcf.h"

View File

@ -59,6 +59,7 @@ void arch_wrap_python()
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
typedef std::unordered_map<IdString, HierarchicalCell> HierarchyMap;
typedef std::unordered_map<IdString, IdString> AliasMap;
auto belpin_cls = class_<ContextualWrapper<BelPin>>("BelPin", no_init);
@ -75,6 +76,7 @@ void arch_wrap_python()
WRAP_MAP_UPTR(CellMap, "IdCellMap");
WRAP_MAP_UPTR(NetMap, "IdNetMap");
WRAP_MAP(HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
}
NEXTPNR_NAMESPACE_END

View File

@ -346,6 +346,8 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
{
if (lc->hierpath == IdString())
lc->hierpath = lut->hierpath;
lc->params[ctx->id("LUT_INIT")] = lut->params[ctx->id("LUT_INIT")].extract(0, 16, Property::State::S0);
replace_port(lut, ctx->id("I0"), lc, ctx->id("I0"));
replace_port(lut, ctx->id("I1"), lc, ctx->id("I1"));
@ -359,6 +361,8 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
{
if (lc->hierpath == IdString())
lc->hierpath = dff->hierpath;
lc->params[ctx->id("DFF_ENABLE")] = Property::State::S1;
std::string config = dff->type.str(ctx).substr(6);
auto citer = config.begin();

View File

@ -24,7 +24,6 @@
#include "bitstream.h"
#include "command.h"
#include "design_utils.h"
#include "jsonparse.h"
#include "log.h"
#include "pcf.h"
#include "timing.h"

View File

@ -527,7 +527,9 @@ static void pack_io(Context *ctx)
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))
if ((net != nullptr) && ((net->users.size() > 2) ||
(net->driver.cell != nullptr &&
net->driver.cell->type == ctx->id("$nextpnr_obuf") && 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));
}
@ -1485,6 +1487,7 @@ bool Arch::pack()
promote_globals(ctx);
ctx->assignArchInfo();
constrain_chains(ctx);
ctx->fixupHierarchy();
ctx->assignArchInfo();
ctx->settings[ctx->id("pack")] = 1;
archInfoToAttributes();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
def visit(indent, data):
istr = " " * indent
print("{}{}: {}".format(istr, data.name, data.type))
for lname, gname in data.leaf_cells:
print("{} {} -> {}".format(istr, lname, gname))
for lname, gname in data.hier_cells:
visit(indent + 4, ctx.hierarchy[gname])
visit(0, ctx.hierarchy[ctx.top_module])