| /* |
| * nextpnr -- Next Generation Place and Route |
| * |
| * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * |
| */ |
| |
| #include "jsonwrite.h" |
| #include <assert.h> |
| #include <fstream> |
| #include <iostream> |
| #include <iterator> |
| #include <log.h> |
| #include <map> |
| #include <string> |
| #include "nextpnr.h" |
| #include "version.h" |
| |
| NEXTPNR_NAMESPACE_BEGIN |
| |
| namespace JsonWriter { |
| |
| std::string get_string(std::string str) |
| { |
| std::string newstr = "\""; |
| for (char c : str) { |
| if (c == '\\') |
| newstr += c; |
| newstr += c; |
| } |
| return newstr + "\""; |
| } |
| |
| std::string get_name(IdString name, Context *ctx) { return get_string(name.c_str(ctx)); } |
| |
| void write_parameter_value(std::ostream &f, const Property &value) |
| { |
| if (value.size() == 32 && value.is_fully_def()) { |
| f << stringf("%d", value.as_int64()); |
| } else { |
| f << get_string(value.to_string()); |
| } |
| } |
| |
| void write_parameters(std::ostream &f, Context *ctx, const std::unordered_map<IdString, Property> ¶meters, |
| bool for_module = false) |
| { |
| bool first = true; |
| for (auto ¶m : parameters) { |
| f << stringf("%s\n", first ? "" : ","); |
| f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first, ctx).c_str()); |
| write_parameter_value(f, param.second); |
| first = false; |
| } |
| } |
| |
| struct PortGroup |
| { |
| std::string name; |
| std::vector<int> bits; |
| PortType dir; |
| }; |
| |
| std::vector<PortGroup> group_ports(Context *ctx) |
| { |
| std::vector<PortGroup> groups; |
| std::unordered_map<std::string, size_t> base_to_group; |
| for (auto &pair : ctx->ports) { |
| std::string name = pair.second.name.str(ctx); |
| if ((name.back() != ']') || (name.find('[') == std::string::npos)) { |
| groups.push_back({name, {pair.first.index}, pair.second.type}); |
| } else { |
| int off1 = int(name.find_last_of('[')); |
| std::string basename = name.substr(0, off1); |
| int index = std::stoi(name.substr(off1 + 1, name.size() - (off1 + 2))); |
| |
| if (!base_to_group.count(basename)) { |
| base_to_group[basename] = groups.size(); |
| groups.push_back({basename, std::vector<int>(index + 1, -1), pair.second.type}); |
| } |
| |
| auto &grp = groups.at(base_to_group[basename]); |
| if (int(grp.bits.size()) <= index) |
| grp.bits.resize(index + 1, -1); |
| NPNR_ASSERT(grp.bits.at(index) == -1); |
| grp.bits.at(index) = pair.second.net ? pair.second.net->name.index : pair.first.index; |
| } |
| } |
| return groups; |
| }; |
| |
| std::string format_port_bits(const PortGroup &port) |
| { |
| std::stringstream s; |
| s << "[ "; |
| bool first = true; |
| for (auto bit : port.bits) { |
| if (!first) |
| s << ", "; |
| if (bit == -1) |
| s << "\"x\""; |
| else |
| s << bit; |
| first = false; |
| } |
| s << " ]"; |
| return s.str(); |
| } |
| |
| void write_module(std::ostream &f, Context *ctx) |
| { |
| auto val = ctx->attrs.find(ctx->id("module")); |
| if (val != ctx->attrs.end()) |
| f << stringf(" %s: {\n", get_string(val->second.as_string()).c_str()); |
| else |
| f << stringf(" %s: {\n", get_string("top").c_str()); |
| f << stringf(" \"settings\": {"); |
| write_parameters(f, ctx, ctx->settings, true); |
| f << stringf("\n },\n"); |
| f << stringf(" \"attributes\": {"); |
| write_parameters(f, ctx, ctx->attrs, true); |
| f << stringf("\n },\n"); |
| f << stringf(" \"ports\": {"); |
| |
| auto ports = group_ports(ctx); |
| bool first = true; |
| for (auto &port : ports) { |
| f << stringf("%s\n", first ? "" : ","); |
| f << stringf(" %s: {\n", get_string(port.name).c_str()); |
| f << stringf(" \"direction\": \"%s\",\n", |
| port.dir == PORT_IN ? "input" : port.dir == PORT_INOUT ? "inout" : "output"); |
| f << stringf(" \"bits\": %s\n", format_port_bits(port).c_str()); |
| f << stringf(" }"); |
| first = false; |
| } |
| f << stringf("\n },\n"); |
| |
| f << stringf(" \"cells\": {"); |
| first = true; |
| for (auto &pair : ctx->cells) { |
| auto &c = pair.second; |
| f << stringf("%s\n", first ? "" : ","); |
| f << stringf(" %s: {\n", get_name(c->name, ctx).c_str()); |
| f << stringf(" \"hide_name\": %s,\n", c->name.c_str(ctx)[0] == '$' ? "1" : "0"); |
| f << stringf(" \"type\": %s,\n", get_name(c->type, ctx).c_str()); |
| f << stringf(" \"parameters\": {"); |
| write_parameters(f, ctx, c->params); |
| f << stringf("\n },\n"); |
| f << stringf(" \"attributes\": {"); |
| write_parameters(f, ctx, c->attrs); |
| f << stringf("\n },\n"); |
| f << stringf(" \"port_directions\": {"); |
| bool first2 = true; |
| for (auto &conn : c->ports) { |
| auto &p = conn.second; |
| std::string direction = (p.type == PORT_IN) ? "input" : (p.type == PORT_OUT) ? "output" : "inout"; |
| f << stringf("%s\n", first2 ? "" : ","); |
| f << stringf(" %s: \"%s\"", get_name(conn.first, ctx).c_str(), direction.c_str()); |
| first2 = false; |
| } |
| f << stringf("\n },\n"); |
| f << stringf(" \"connections\": {"); |
| first2 = true; |
| for (auto &conn : c->ports) { |
| auto &p = conn.second; |
| f << stringf("%s\n", first2 ? "" : ","); |
| if (p.net) |
| f << stringf(" %s: [ %d ]", get_name(conn.first, ctx).c_str(), p.net->name.index); |
| else |
| f << stringf(" %s: [ ]", get_name(conn.first, ctx).c_str()); |
| |
| first2 = false; |
| } |
| f << stringf("\n }\n"); |
| |
| f << stringf(" }"); |
| first = false; |
| } |
| |
| f << stringf("\n },\n"); |
| |
| f << stringf(" \"netnames\": {"); |
| first = true; |
| for (auto &pair : ctx->nets) { |
| auto &w = pair.second; |
| f << stringf("%s\n", first ? "" : ","); |
| f << stringf(" %s: {\n", get_name(w->name, ctx).c_str()); |
| f << stringf(" \"hide_name\": %s,\n", w->name.c_str(ctx)[0] == '$' ? "1" : "0"); |
| f << stringf(" \"bits\": [ %d ] ,\n", pair.first.index); |
| f << stringf(" \"attributes\": {"); |
| write_parameters(f, ctx, w->attrs); |
| f << stringf("\n }\n"); |
| f << stringf(" }"); |
| first = false; |
| } |
| |
| f << stringf("\n }\n"); |
| f << stringf(" }"); |
| } |
| |
| void write_context(std::ostream &f, Context *ctx) |
| { |
| f << stringf("{\n"); |
| f << stringf(" \"creator\": %s,\n", |
| get_string("Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")").c_str()); |
| f << stringf(" \"modules\": {\n"); |
| write_module(f, ctx); |
| f << stringf("\n }"); |
| f << stringf("\n}\n"); |
| } |
| |
| }; // End Namespace JsonWriter |
| |
| bool write_json_file(std::ostream &f, std::string &filename, Context *ctx) |
| { |
| try { |
| using namespace JsonWriter; |
| if (!f) |
| log_error("failed to open JSON file.\n"); |
| write_context(f, ctx); |
| log_break(); |
| return true; |
| } catch (log_execution_error_exception) { |
| return false; |
| } |
| } |
| |
| NEXTPNR_NAMESPACE_END |