pass jny: large chunk of refactoring to make the JSON output more pretty and the...
[yosys.git] / backends / jny / jny.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Aki "lethalbit" Van Ness <aki@yosyshq.com> <aki@lethalbit.net>
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 "kernel/rtlil.h"
21 #include "kernel/register.h"
22 #include "kernel/sigtools.h"
23 #include "kernel/celltypes.h"
24 #include "kernel/cellaigs.h"
25 #include "kernel/log.h"
26 #include <string>
27 #include <unordered_map>
28 #include <vector>
29
30 USING_YOSYS_NAMESPACE
31 PRIVATE_NAMESPACE_BEGIN
32
33
34 struct JnyWriter
35 {
36 private:
37 std::ostream &f;
38 bool _use_selection;
39 std::unordered_map<std::string, std::vector<Cell*>> _cells{};
40
41 // XXX(aki): this was pulled from the json backend, needs to be pulled
42 // out possibly into some sort of utilities file, or integrated into rtlil.h
43 // directly
44 string get_string(string str)
45 {
46 string newstr = "\"";
47 for (char c : str) {
48 if (c == '\\')
49 newstr += c;
50 newstr += c;
51 }
52 return newstr + "\"";
53 }
54
55 // XXX(aki): I know this is far from ideal but i'm out of spoons and cant focus so
56 // it'll have to do for now,
57 void coalesce_cells(Module* mod)
58 {
59 for (auto cell : mod->cells()) {
60 const auto cell_type = get_string(RTLIL::unescape_id(cell->type));
61
62 if (_cells.find(cell_type) == _cells.end())
63 _cells.emplace(cell_type, std::vector<Cell*>());
64
65 _cells.at(cell_type).push_back(cell);
66 }
67 }
68
69 // XXX(aki): this is a lazy way to do this i know,,,
70 std::string gen_indent(const uint16_t level)
71 {
72 std::stringstream s;
73 for (uint16_t i = 0; i <= level; ++i)
74 {
75 s << " ";
76 }
77 return s.str();
78 }
79
80 public:
81 JnyWriter(std::ostream &f, bool use_selection) noexcept: f(f), _use_selection(use_selection) { }
82
83 void write_metadata(Design *design, uint16_t indent_level = 0)
84 {
85 log_assert(design != nullptr);
86
87 design->sort();
88
89 f << "{\n";
90 f << stringf(" \"generator\": %s,\n", get_string(yosys_version_str).c_str());
91 // XXX(aki): Replace this with a proper version info eventually:tm:
92 f << " \"version\": \"0.0.0\",\n";
93 f << " \"modules\": [\n";
94
95 bool first{true};
96 for (auto mod : _use_selection ? design->selected_modules() : design->modules()) {
97 if (!first)
98 f << ",\n";
99 write_module(mod, indent_level + 2);
100 first = false;
101 }
102
103 f << "\n";
104 f << " ]\n";
105 f << "}\n";
106 }
107
108 void write_module(Module* mod, uint16_t indent_level = 0) {
109 log_assert(mod != nullptr);
110
111 coalesce_cells(mod);
112
113 const auto _indent = gen_indent(indent_level);
114
115 f << _indent << "{\n";
116 f << stringf(" %s\"name\": %s,\n", _indent.c_str(), get_string(RTLIL::unescape_id(mod->name)).c_str());
117 f << _indent << " \"cell_sorts\": [\n";
118
119 bool first_sort{true};
120 for (auto& sort : _cells) {
121 if (!first_sort)
122 f << ",\n";
123 write_cell_sort(sort, indent_level + 2);
124 first_sort = false;
125 }
126 f << "\n";
127
128 f << _indent << " ],\n";
129 f << _indent << " \"connections\": [\n";
130
131 f << _indent << " ],\n";
132 f << _indent << " \"attributes\": {\n";
133
134 write_prams(mod->attributes, indent_level + 2);
135
136 f << "\n";
137
138 f << _indent << " }\n";
139 f << _indent << "}";
140 }
141
142 void write_cell_ports(RTLIL::Cell* port_cell, uint64_t indent_level = 0) {
143 const auto _indent = gen_indent(indent_level);
144
145 bool first_port{true};
146 for (auto con : port_cell->connections()) {
147 if (!first_port)
148 f << ",\n";
149
150 f << _indent << " {\n";
151 f << stringf(" %s\"name\": %s,\n", _indent.c_str(), get_string(RTLIL::unescape_id(con.first)).c_str());
152 f << _indent << " \"direction\": \"";
153 if (port_cell->input(con.first))
154 f << "i";
155 if (port_cell->input(con.first))
156 f << "o";
157 f << "\",\n";
158 if (con.second.size() == 1)
159 f << _indent << " \"range\": [0, 0]\n";
160 else
161 f << stringf(" %s\"range\": [%d, %d]\n", _indent.c_str(), con.second.size(), 0);
162 f << _indent << " }";
163
164 first_port = false;
165 }
166 f << "\n";
167 }
168
169
170 void write_cell_sort(std::pair<const std::string, std::vector<Cell*>>& sort, uint16_t indent_level = 0) {
171 const auto port_cell = sort.second.front();
172 const auto _indent = gen_indent(indent_level);
173
174 f << _indent << "{\n";
175 f << stringf(" %s\"type\": %s,\n", _indent.c_str(), sort.first.c_str());
176 f << _indent << " \"ports\": [\n";
177
178 write_cell_ports(port_cell, indent_level + 2);
179
180 f << _indent << " ],\n" << _indent << " \"cells\": [\n";
181
182 bool first_cell{true};
183 for (auto& cell : sort.second) {
184 if (!first_cell)
185 f << ",\n";
186
187 write_cell(cell, indent_level + 2);
188
189 first_cell = false;
190 }
191
192 f << "\n";
193 f << _indent << " ]\n";
194 f << _indent << "}";
195 }
196
197 void write_param_val(const Const& v) {
198 if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) == RTLIL::ConstFlags::CONST_FLAG_STRING) {
199 const auto str = v.decode_string();
200
201 // XXX(aki): TODO, uh, yeah
202
203 f << get_string(str);
204 } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) == RTLIL::ConstFlags::CONST_FLAG_SIGNED) {
205 f << stringf("\"%dsd %d\"", v.size(), v.as_int());
206 } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_REAL) == RTLIL::ConstFlags::CONST_FLAG_REAL) {
207
208 } else {
209 f << get_string(v.as_string());
210 }
211 }
212
213 void write_prams(dict<RTLIL::IdString, RTLIL::Const>& params, uint16_t indent_level = 0) {
214 const auto _indent = gen_indent(indent_level);
215
216 bool first_param{true};
217 for (auto& param : params) {
218 if (!first_param)
219 f << stringf(",\n");
220 const auto param_val = param.second;
221 if (!param_val.empty()) {
222 f << stringf(" %s%s: ", _indent.c_str(), get_string(RTLIL::unescape_id(param.first)).c_str());
223 write_param_val(param_val);
224 } else {
225 f << stringf(" %s%s: true", _indent.c_str(), get_string(RTLIL::unescape_id(param.first)).c_str());
226 }
227
228 first_param = false;
229 }
230 }
231
232 void write_cell(Cell* cell, uint16_t indent_level = 0) {
233 const auto _indent = gen_indent(indent_level);
234 log_assert(cell != nullptr);
235
236 f << _indent << " {\n";
237 f << stringf(" %s\"name\": %s,\n", _indent.c_str(), get_string(RTLIL::unescape_id(cell->name)).c_str());
238 f << _indent << " \"attributes\": {\n";
239
240 write_prams(cell->attributes, indent_level + 2);
241
242 f << "\n";
243
244 f << _indent << " },\n";
245 f << _indent << " \"parameters\": {\n";
246
247 write_prams(cell->parameters, indent_level + 2);
248
249 f << "\n";
250
251 f << _indent << " }\n";
252 f << _indent << " }";
253 }
254 };
255
256 struct JnyBackend : public Backend {
257 JnyBackend() : Backend("jny", "generate design metadata") { }
258 void help() override {
259 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
260 log("\n");
261 log(" jny [options] [selection]\n");
262 log("\n");
263 log("Write a JSON metadata for the current design\n");
264 log("\n");
265 log("\n");
266 }
267
268 void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override {
269 size_t argidx{1};
270 extra_args(f, filename, args, argidx);
271
272 log_header(design, "Executing jny backend.\n");
273
274 JnyWriter jny_writer(*f, false);
275 jny_writer.write_metadata(design);
276 }
277
278 } JnyBackend;
279
280
281 struct JnyPass : public Pass {
282 JnyPass() : Pass("jny", "write design and metadata") { }
283
284 void help() override {
285 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
286 log("\n");
287 log(" jny [options] [selection]\n");
288 log("\n");
289 log("Write a JSON netlist metadata for the current design\n");
290 log("\n");
291 log(" -o <filename>\n");
292 log(" write to the specified file.\n");
293 log("\n");
294 log("See 'help write_jny' for a description of the JSON format used.\n");
295 log("\n");
296 }
297 void execute(std::vector<std::string> args, RTLIL::Design *design) override {
298 std::string filename{};
299
300 size_t argidx;
301 for (argidx = 1; argidx < args.size(); argidx++)
302 {
303 if (args[argidx] == "-o" && argidx+1 < args.size()) {
304 filename = args[++argidx];
305 continue;
306 }
307 break;
308 }
309 extra_args(args, argidx, design);
310
311 std::ostream *f;
312 std::stringstream buf;
313
314 if (!filename.empty()) {
315 rewrite_filename(filename);
316 std::ofstream *ff = new std::ofstream;
317 ff->open(filename.c_str(), std::ofstream::trunc);
318 if (ff->fail()) {
319 delete ff;
320 log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
321 }
322 f = ff;
323 } else {
324 f = &buf;
325 }
326
327
328 JnyWriter jny_writer(*f, false);
329 jny_writer.write_metadata(design);
330
331 if (!filename.empty()) {
332 delete f;
333 } else {
334 log("%s", buf.str().c_str());
335 }
336 }
337
338 } JnyPass;
339
340 PRIVATE_NAMESPACE_END