2019-05-31 17:09:13 +08:00
|
|
|
/*
|
|
|
|
* 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 + "\"";
|
|
|
|
}
|
|
|
|
|
2019-06-26 00:19:25 +08:00
|
|
|
std::string get_name(IdString name, Context *ctx) { return get_string(name.c_str(ctx)); }
|
2019-05-31 17:09:13 +08:00
|
|
|
|
2019-08-01 21:28:21 +08:00
|
|
|
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 {
|
2019-08-07 21:22:03 +08:00
|
|
|
f << get_string(value.to_string());
|
2019-08-01 21:28:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-26 00:19:25 +08:00
|
|
|
void write_parameters(std::ostream &f, Context *ctx, const std::unordered_map<IdString, Property> ¶meters,
|
|
|
|
bool for_module = false)
|
2019-05-31 17:09:13 +08:00
|
|
|
{
|
|
|
|
bool first = true;
|
|
|
|
for (auto ¶m : parameters) {
|
|
|
|
f << stringf("%s\n", first ? "" : ",");
|
2019-06-26 00:19:25 +08:00
|
|
|
f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first, ctx).c_str());
|
2019-08-01 21:28:21 +08:00
|
|
|
write_parameter_value(f, param.second);
|
2019-05-31 17:09:13 +08:00
|
|
|
first = false;
|
|
|
|
}
|
2019-06-02 22:46:07 +08:00
|
|
|
}
|
|
|
|
|
2019-08-07 21:47:01 +08:00
|
|
|
struct PortGroup
|
|
|
|
{
|
|
|
|
std::string name;
|
|
|
|
std::vector<int> bits;
|
|
|
|
PortType dir;
|
|
|
|
};
|
|
|
|
|
2019-11-27 23:16:28 +08:00
|
|
|
std::vector<PortGroup> group_ports(Context *ctx, const std::unordered_map<IdString, PortInfo> &ports,
|
|
|
|
bool is_cell = false)
|
2019-08-07 21:47:01 +08:00
|
|
|
{
|
|
|
|
std::vector<PortGroup> groups;
|
|
|
|
std::unordered_map<std::string, size_t> base_to_group;
|
2019-11-27 23:16:28 +08:00
|
|
|
for (auto &pair : ports) {
|
2019-08-07 21:47:01 +08:00
|
|
|
std::string name = pair.second.name.str(ctx);
|
|
|
|
if ((name.back() != ']') || (name.find('[') == std::string::npos)) {
|
2019-11-27 23:16:28 +08:00
|
|
|
groups.push_back({name,
|
|
|
|
{is_cell ? (pair.second.net ? pair.second.net->name.index : -1) : pair.first.index},
|
|
|
|
pair.second.type});
|
2019-08-07 21:47:01 +08:00
|
|
|
} 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);
|
2019-11-27 23:16:28 +08:00
|
|
|
grp.bits.at(index) = pair.second.net ? pair.second.net->name.index : (is_cell ? -1 : pair.first.index);
|
2019-08-07 21:47:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return groups;
|
2019-11-27 23:16:28 +08:00
|
|
|
}
|
2019-08-07 21:47:01 +08:00
|
|
|
|
2019-11-27 23:16:28 +08:00
|
|
|
std::string format_port_bits(const PortGroup &port, int &dummy_idx)
|
2019-08-07 21:47:01 +08:00
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
s << "[ ";
|
|
|
|
bool first = true;
|
2019-11-27 23:16:28 +08:00
|
|
|
if (port.bits.size() != 1 || port.bits.at(0) != -1) // skip single disconnected ports
|
|
|
|
for (auto bit : port.bits) {
|
|
|
|
if (!first)
|
|
|
|
s << ", ";
|
|
|
|
if (bit == -1)
|
|
|
|
s << (++dummy_idx);
|
|
|
|
else
|
|
|
|
s << bit;
|
|
|
|
first = false;
|
|
|
|
}
|
2019-08-07 21:47:01 +08:00
|
|
|
s << " ]";
|
|
|
|
return s.str();
|
|
|
|
}
|
|
|
|
|
2019-05-31 17:09:13 +08:00
|
|
|
void write_module(std::ostream &f, Context *ctx)
|
|
|
|
{
|
2019-06-07 22:11:11 +08:00
|
|
|
auto val = ctx->attrs.find(ctx->id("module"));
|
2019-11-27 23:16:28 +08:00
|
|
|
int dummy_idx = int(ctx->idstring_idx_to_str->size()) + 1000;
|
2019-06-26 00:19:25 +08:00
|
|
|
if (val != ctx->attrs.end())
|
2019-08-01 21:28:21 +08:00
|
|
|
f << stringf(" %s: {\n", get_string(val->second.as_string()).c_str());
|
2019-06-07 22:11:11 +08:00
|
|
|
else
|
|
|
|
f << stringf(" %s: {\n", get_string("top").c_str());
|
2019-06-13 00:34:34 +08:00
|
|
|
f << stringf(" \"settings\": {");
|
2019-05-31 17:09:13 +08:00
|
|
|
write_parameters(f, ctx, ctx->settings, true);
|
2019-06-13 00:34:34 +08:00
|
|
|
f << stringf("\n },\n");
|
2019-05-31 17:09:13 +08:00
|
|
|
f << stringf(" \"attributes\": {");
|
2019-06-07 22:11:11 +08:00
|
|
|
write_parameters(f, ctx, ctx->attrs, true);
|
2019-05-31 17:09:13 +08:00
|
|
|
f << stringf("\n },\n");
|
|
|
|
f << stringf(" \"ports\": {");
|
2019-08-07 21:47:01 +08:00
|
|
|
|
2019-11-27 23:16:28 +08:00
|
|
|
auto ports = group_ports(ctx, ctx->ports);
|
2019-06-21 15:43:47 +08:00
|
|
|
bool first = true;
|
2019-08-07 21:47:01 +08:00
|
|
|
for (auto &port : ports) {
|
2019-06-21 15:43:47 +08:00
|
|
|
f << stringf("%s\n", first ? "" : ",");
|
2019-08-07 21:47:01 +08:00
|
|
|
f << stringf(" %s: {\n", get_string(port.name).c_str());
|
2019-06-26 00:19:25 +08:00
|
|
|
f << stringf(" \"direction\": \"%s\",\n",
|
2019-08-07 21:47:01 +08:00
|
|
|
port.dir == PORT_IN ? "input" : port.dir == PORT_INOUT ? "inout" : "output");
|
2019-11-27 23:16:28 +08:00
|
|
|
f << stringf(" \"bits\": %s\n", format_port_bits(port, dummy_idx).c_str());
|
2019-06-21 15:43:47 +08:00
|
|
|
f << stringf(" }");
|
|
|
|
first = false;
|
2019-06-26 00:19:25 +08:00
|
|
|
}
|
2019-05-31 17:09:13 +08:00
|
|
|
f << stringf("\n },\n");
|
|
|
|
|
|
|
|
f << stringf(" \"cells\": {");
|
2019-06-21 15:43:47 +08:00
|
|
|
first = true;
|
2019-06-26 00:19:25 +08:00
|
|
|
for (auto &pair : ctx->cells) {
|
2019-05-31 17:09:13 +08:00
|
|
|
auto &c = pair.second;
|
2019-11-27 23:16:28 +08:00
|
|
|
auto cell_ports = group_ports(ctx, c->ports, true);
|
2019-05-31 17:09:13 +08:00
|
|
|
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\": {");
|
2019-06-07 17:48:15 +08:00
|
|
|
write_parameters(f, ctx, c->attrs);
|
2019-05-31 17:09:13 +08:00
|
|
|
f << stringf("\n },\n");
|
|
|
|
f << stringf(" \"port_directions\": {");
|
|
|
|
bool first2 = true;
|
2019-11-27 23:16:28 +08:00
|
|
|
for (auto &pg : cell_ports) {
|
|
|
|
std::string direction = (pg.dir == PORT_IN) ? "input" : (pg.dir == PORT_OUT) ? "output" : "inout";
|
2019-05-31 17:50:49 +08:00
|
|
|
f << stringf("%s\n", first2 ? "" : ",");
|
2019-11-27 23:16:28 +08:00
|
|
|
f << stringf(" %s: \"%s\"", get_string(pg.name).c_str(), direction.c_str());
|
2019-05-31 17:50:49 +08:00
|
|
|
first2 = false;
|
2019-05-31 17:09:13 +08:00
|
|
|
}
|
|
|
|
f << stringf("\n },\n");
|
|
|
|
f << stringf(" \"connections\": {");
|
|
|
|
first2 = true;
|
2019-11-27 23:16:28 +08:00
|
|
|
for (auto &pg : cell_ports) {
|
2019-05-31 17:50:49 +08:00
|
|
|
f << stringf("%s\n", first2 ? "" : ",");
|
2019-11-27 23:16:28 +08:00
|
|
|
f << stringf(" %s: %s", get_string(pg.name).c_str(), format_port_bits(pg, dummy_idx).c_str());
|
2019-05-31 17:50:49 +08:00
|
|
|
first2 = false;
|
2019-05-31 17:09:13 +08:00
|
|
|
}
|
|
|
|
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");
|
2019-06-05 02:08:43 +08:00
|
|
|
f << stringf(" \"bits\": [ %d ] ,\n", pair.first.index);
|
2019-05-31 17:09:13 +08:00
|
|
|
f << stringf(" \"attributes\": {");
|
2019-06-07 17:48:15 +08:00
|
|
|
write_parameters(f, ctx, w->attrs);
|
2019-05-31 17:09:13 +08:00
|
|
|
f << stringf("\n }\n");
|
|
|
|
f << stringf(" }");
|
|
|
|
first = false;
|
|
|
|
}
|
2019-06-26 00:19:25 +08:00
|
|
|
|
2019-05-31 17:09:13 +08:00
|
|
|
f << stringf("\n }\n");
|
|
|
|
f << stringf(" }");
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_context(std::ostream &f, Context *ctx)
|
|
|
|
{
|
|
|
|
f << stringf("{\n");
|
2019-06-26 00:19:25 +08:00
|
|
|
f << stringf(" \"creator\": %s,\n",
|
|
|
|
get_string("Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")").c_str());
|
2019-05-31 17:09:13 +08:00
|
|
|
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 {
|
2019-06-26 00:19:25 +08:00
|
|
|
using namespace JsonWriter;
|
2019-05-31 17:09:13 +08:00
|
|
|
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
|