1 /*
2  *  nextpnr -- Next Generation Place and Route
3  *
4  *  Copyright (C) 2018  Miodrag Milanovic <miodrag@symbioticeda.com>
5  *
6  *  Permission to use, copy, modify, and/or distribute this software for any
7  *  purpose with or without fee is hereby granted, provided that the above
8  *  copyright notice and this permission notice appear in all copies.
9  *
10  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  */
19 
20 #include "jsonwrite.h"
21 #include <assert.h>
22 #include <fstream>
23 #include <iostream>
24 #include <iterator>
25 #include <log.h>
26 #include <map>
27 #include <string>
28 #include "nextpnr.h"
29 #include "version.h"
30 
31 NEXTPNR_NAMESPACE_BEGIN
32 
33 namespace JsonWriter {
34 
get_string(std::string str)35 std::string get_string(std::string str)
36 {
37     std::string newstr = "\"";
38     for (char c : str) {
39         if (c == '\\')
40             newstr += c;
41         newstr += c;
42     }
43     return newstr + "\"";
44 }
45 
get_name(IdString name,Context * ctx)46 std::string get_name(IdString name, Context *ctx) { return get_string(name.c_str(ctx)); }
47 
write_parameters(std::ostream & f,Context * ctx,const std::unordered_map<IdString,Property> & parameters,bool for_module=false)48 void write_parameters(std::ostream &f, Context *ctx, const std::unordered_map<IdString, Property> &parameters,
49                       bool for_module = false)
50 {
51     bool first = true;
52     for (auto &param : parameters) {
53         f << stringf("%s\n", first ? "" : ",");
54         f << stringf("        %s%s: ", for_module ? "" : "    ", get_name(param.first, ctx).c_str());
55         f << get_string(param.second.to_string());
56         first = false;
57     }
58 }
59 
60 struct PortGroup
61 {
62     std::string name;
63     std::vector<int> bits;
64     PortType dir;
65 };
66 
group_ports(Context * ctx,const std::unordered_map<IdString,PortInfo> & ports,bool is_cell=false)67 std::vector<PortGroup> group_ports(Context *ctx, const std::unordered_map<IdString, PortInfo> &ports,
68                                    bool is_cell = false)
69 {
70     std::vector<PortGroup> groups;
71     std::unordered_map<std::string, size_t> base_to_group;
72     for (auto &pair : ports) {
73         std::string name = pair.second.name.str(ctx);
74         if ((name.back() != ']') || (name.find('[') == std::string::npos)) {
75             groups.push_back({name,
76                               {is_cell ? (pair.second.net ? pair.second.net->name.index : -1) : pair.first.index},
77                               pair.second.type});
78         } else {
79             int off1 = int(name.find_last_of('['));
80             std::string basename = name.substr(0, off1);
81             int index = std::stoi(name.substr(off1 + 1, name.size() - (off1 + 2)));
82 
83             if (!base_to_group.count(basename)) {
84                 base_to_group[basename] = groups.size();
85                 groups.push_back({basename, std::vector<int>(index + 1, -1), pair.second.type});
86             }
87 
88             auto &grp = groups.at(base_to_group[basename]);
89             if (int(grp.bits.size()) <= index)
90                 grp.bits.resize(index + 1, -1);
91             NPNR_ASSERT(grp.bits.at(index) == -1);
92             grp.bits.at(index) = pair.second.net ? pair.second.net->name.index : (is_cell ? -1 : pair.first.index);
93         }
94     }
95     return groups;
96 }
97 
format_port_bits(const PortGroup & port,int & dummy_idx)98 std::string format_port_bits(const PortGroup &port, int &dummy_idx)
99 {
100     std::stringstream s;
101     s << "[ ";
102     bool first = true;
103     if (port.bits.size() != 1 || port.bits.at(0) != -1) // skip single disconnected ports
104         for (auto bit : port.bits) {
105             if (!first)
106                 s << ", ";
107             if (bit == -1)
108                 s << (++dummy_idx);
109             else
110                 s << bit;
111             first = false;
112         }
113     s << " ]";
114     return s.str();
115 }
116 
write_module(std::ostream & f,Context * ctx)117 void write_module(std::ostream &f, Context *ctx)
118 {
119     auto val = ctx->attrs.find(ctx->id("module"));
120     int dummy_idx = int(ctx->idstring_idx_to_str->size()) + 1000;
121     if (val != ctx->attrs.end())
122         f << stringf("    %s: {\n", get_string(val->second.as_string()).c_str());
123     else
124         f << stringf("    %s: {\n", get_string("top").c_str());
125     f << stringf("      \"settings\": {");
126     write_parameters(f, ctx, ctx->settings, true);
127     f << stringf("\n      },\n");
128     f << stringf("      \"attributes\": {");
129     write_parameters(f, ctx, ctx->attrs, true);
130     f << stringf("\n      },\n");
131     f << stringf("      \"ports\": {");
132 
133     auto ports = group_ports(ctx, ctx->ports);
134     bool first = true;
135     for (auto &port : ports) {
136         f << stringf("%s\n", first ? "" : ",");
137         f << stringf("        %s: {\n", get_string(port.name).c_str());
138         f << stringf("          \"direction\": \"%s\",\n",
139                      port.dir == PORT_IN ? "input" : port.dir == PORT_INOUT ? "inout" : "output");
140         f << stringf("          \"bits\": %s\n", format_port_bits(port, dummy_idx).c_str());
141         f << stringf("        }");
142         first = false;
143     }
144     f << stringf("\n      },\n");
145 
146     f << stringf("      \"cells\": {");
147     first = true;
148     for (auto &pair : ctx->cells) {
149         auto &c = pair.second;
150         auto cell_ports = group_ports(ctx, c->ports, true);
151         f << stringf("%s\n", first ? "" : ",");
152         f << stringf("        %s: {\n", get_name(c->name, ctx).c_str());
153         f << stringf("          \"hide_name\": %s,\n", c->name.c_str(ctx)[0] == '$' ? "1" : "0");
154         f << stringf("          \"type\": %s,\n", get_name(c->type, ctx).c_str());
155         f << stringf("          \"parameters\": {");
156         write_parameters(f, ctx, c->params);
157         f << stringf("\n          },\n");
158         f << stringf("          \"attributes\": {");
159         write_parameters(f, ctx, c->attrs);
160         f << stringf("\n          },\n");
161         f << stringf("          \"port_directions\": {");
162         bool first2 = true;
163         for (auto &pg : cell_ports) {
164             std::string direction = (pg.dir == PORT_IN) ? "input" : (pg.dir == PORT_OUT) ? "output" : "inout";
165             f << stringf("%s\n", first2 ? "" : ",");
166             f << stringf("            %s: \"%s\"", get_string(pg.name).c_str(), direction.c_str());
167             first2 = false;
168         }
169         f << stringf("\n          },\n");
170         f << stringf("          \"connections\": {");
171         first2 = true;
172         for (auto &pg : cell_ports) {
173             f << stringf("%s\n", first2 ? "" : ",");
174             f << stringf("            %s: %s", get_string(pg.name).c_str(), format_port_bits(pg, dummy_idx).c_str());
175             first2 = false;
176         }
177         f << stringf("\n          }\n");
178 
179         f << stringf("        }");
180         first = false;
181     }
182 
183     f << stringf("\n      },\n");
184 
185     f << stringf("      \"netnames\": {");
186     first = true;
187     for (auto &pair : ctx->nets) {
188         auto &w = pair.second;
189         f << stringf("%s\n", first ? "" : ",");
190         f << stringf("        %s: {\n", get_name(w->name, ctx).c_str());
191         f << stringf("          \"hide_name\": %s,\n", w->name.c_str(ctx)[0] == '$' ? "1" : "0");
192         f << stringf("          \"bits\": [ %d ] ,\n", pair.first.index);
193         f << stringf("          \"attributes\": {");
194         write_parameters(f, ctx, w->attrs);
195         f << stringf("\n          }\n");
196         f << stringf("        }");
197         first = false;
198     }
199 
200     f << stringf("\n      }\n");
201     f << stringf("    }");
202 }
203 
write_context(std::ostream & f,Context * ctx)204 void write_context(std::ostream &f, Context *ctx)
205 {
206     f << stringf("{\n");
207     f << stringf("  \"creator\": %s,\n",
208                  get_string("Next Generation Place and Route (Version " GIT_DESCRIBE_STR ")").c_str());
209     f << stringf("  \"modules\": {\n");
210     write_module(f, ctx);
211     f << stringf("\n  }");
212     f << stringf("\n}\n");
213 }
214 
215 }; // End Namespace JsonWriter
216 
write_json_file(std::ostream & f,std::string & filename,Context * ctx)217 bool write_json_file(std::ostream &f, std::string &filename, Context *ctx)
218 {
219     try {
220         using namespace JsonWriter;
221         if (!f)
222             log_error("failed to open JSON file.\n");
223         write_context(f, ctx);
224         log_break();
225         return true;
226     } catch (log_execution_error_exception) {
227         return false;
228     }
229 }
230 
231 NEXTPNR_NAMESPACE_END
232