pass jny: added connection output
[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 bool _include_connections;
42 bool _include_attributes;
43 bool _include_properties;
44
45 // XXX(aki): this was pulled from the json backend, needs to be pulled
46 // out possibly into some sort of utilities file, or integrated into rtlil.h
47 // directly
48 string get_string(string str)
49 {
50 string newstr = "\"";
51 for (char c : str) {
52 if (c == '\\')
53 newstr += c;
54 newstr += c;
55 }
56 return newstr + "\"";
57 }
58
59 // XXX(aki): I know this is far from ideal but i'm out of spoons and cant focus so
60 // it'll have to do for now,
61 void coalesce_cells(Module* mod)
62 {
63 for (auto cell : mod->cells()) {
64 const auto cell_type = get_string(RTLIL::unescape_id(cell->type));
65
66 if (_cells.find(cell_type) == _cells.end())
67 _cells.emplace(cell_type, std::vector<Cell*>());
68
69 _cells.at(cell_type).push_back(cell);
70 }
71 }
72
73 // XXX(aki): this is a lazy way to do this i know,,,
74 std::string gen_indent(const uint16_t level)
75 {
76 std::stringstream s;
77 for (uint16_t i = 0; i <= level; ++i)
78 {
79 s << " ";
80 }
81 return s.str();
82 }
83
84 public:
85 JnyWriter(std::ostream &f, bool use_selection, bool connections, bool attributes, bool properties) noexcept:
86 f{f}, _use_selection{use_selection},
87 _include_connections{connections}, _include_attributes{attributes}, _include_properties{properties}
88 { }
89
90 void write_metadata(Design *design, uint16_t indent_level = 0)
91 {
92 log_assert(design != nullptr);
93
94 design->sort();
95
96 f << "{\n";
97 f << stringf(" \"generator\": %s,\n", get_string(yosys_version_str).c_str());
98 // XXX(aki): Replace this with a proper version info eventually:tm:
99 f << " \"version\": \"0.0.0\",\n";
100
101 f << " \"features\": [";
102
103 size_t fnum{0};
104 if (_include_connections) {
105 ++fnum;
106 f << "\"connections\"";
107 }
108
109 if (_include_attributes) {
110 if (fnum > 0)
111 f << ", ";
112 ++fnum;
113 f << "\"attributes\"";
114 }
115
116 if (_include_properties) {
117 if (fnum > 0)
118 f << ", ";
119 ++fnum;
120 f << "\"properties\"";
121 }
122
123 f << "],\n";
124
125 f << " \"modules\": [\n";
126
127 bool first{true};
128 for (auto mod : _use_selection ? design->selected_modules() : design->modules()) {
129 if (!first)
130 f << ",\n";
131 write_module(mod, indent_level + 2);
132 first = false;
133 }
134
135 f << "\n";
136 f << " ]\n";
137 f << "}\n";
138 }
139
140 void write_sigspec(const RTLIL::SigSpec& sig, uint16_t indent_level = 0) {
141 const auto _indent = gen_indent(indent_level);
142
143 f << _indent << " {\n";
144 f << _indent << " \"width\": \"" << sig.size() << "\",\n";
145 f << _indent << " \"type\": \"";
146
147 if (sig.is_wire()) {
148 f << "wire";
149 } else if (sig.is_chunk()) {
150 f << "chunk";
151 } else if (sig.is_bit()) {
152 f << "bit";
153 } else {
154 f << "unknown";
155 }
156 f << "\",\n";
157
158 f << _indent << " \"const\": ";
159 if (sig.has_const()) {
160 f << "true";
161 } else {
162 f << "false";
163 }
164
165 f << "\n";
166
167 f << _indent << " }";
168 }
169
170 void write_mod_conn(const std::pair<RTLIL::SigSpec, RTLIL::SigSpec>& conn, uint16_t indent_level = 0) {
171 const auto _indent = gen_indent(indent_level);
172 f << _indent << " {\n";
173 f << _indent << " \"signals\": [\n";
174
175 write_sigspec(conn.first, indent_level + 2);
176 f << ",\n";
177 write_sigspec(conn.second, indent_level + 2);
178 f << "\n";
179
180 f << _indent << " ]\n";
181 f << _indent << " }";
182 }
183
184 void write_cell_conn(const std::pair<RTLIL::IdString, RTLIL::SigSpec>& sig, uint16_t indent_level = 0) {
185 const auto _indent = gen_indent(indent_level);
186 f << _indent << " {\n";
187 f << _indent << " \"name\": " << get_string(RTLIL::unescape_id(sig.first)) << ",\n";
188 f << _indent << " \"signals\": [\n";
189
190 write_sigspec(sig.second, indent_level + 2);
191 f << "\n";
192
193 f << _indent << " ]\n";
194 f << _indent << " }";
195 }
196
197 void write_module(Module* mod, uint16_t indent_level = 0) {
198 log_assert(mod != nullptr);
199
200 coalesce_cells(mod);
201
202 const auto _indent = gen_indent(indent_level);
203
204 f << _indent << "{\n";
205 f << stringf(" %s\"name\": %s,\n", _indent.c_str(), get_string(RTLIL::unescape_id(mod->name)).c_str());
206 f << _indent << " \"cell_sorts\": [\n";
207
208 bool first_sort{true};
209 for (auto& sort : _cells) {
210 if (!first_sort)
211 f << ",\n";
212 write_cell_sort(sort, indent_level + 2);
213 first_sort = false;
214 }
215 f << "\n";
216
217 f << _indent << " ]";
218 if (_include_connections) {
219 f << ",\n" << _indent << " \"connections\": [\n";
220
221 bool first_conn{true};
222 for (const auto& conn : mod->connections()) {
223 if (!first_conn)
224 f << ",\n";
225
226 write_mod_conn(conn, indent_level + 2);
227
228 first_conn = false;
229 }
230
231 f << _indent << " ]";
232 }
233 if (_include_attributes) {
234 f << ",\n" << _indent << " \"attributes\": {\n";
235
236 write_prams(mod->attributes, indent_level + 2);
237
238 f << "\n";
239 f << _indent << " }";
240 }
241 f << "\n" << _indent << "}";
242 }
243
244 void write_cell_ports(RTLIL::Cell* port_cell, uint64_t indent_level = 0) {
245 const auto _indent = gen_indent(indent_level);
246
247 bool first_port{true};
248 for (auto con : port_cell->connections()) {
249 if (!first_port)
250 f << ",\n";
251
252 f << _indent << " {\n";
253 f << stringf(" %s\"name\": %s,\n", _indent.c_str(), get_string(RTLIL::unescape_id(con.first)).c_str());
254 f << _indent << " \"direction\": \"";
255 if (port_cell->input(con.first))
256 f << "i";
257 if (port_cell->input(con.first))
258 f << "o";
259 f << "\",\n";
260 if (con.second.size() == 1)
261 f << _indent << " \"range\": [0, 0]\n";
262 else
263 f << stringf(" %s\"range\": [%d, %d]\n", _indent.c_str(), con.second.size(), 0);
264 f << _indent << " }";
265
266 first_port = false;
267 }
268 f << "\n";
269 }
270
271
272 void write_cell_sort(std::pair<const std::string, std::vector<Cell*>>& sort, uint16_t indent_level = 0) {
273 const auto port_cell = sort.second.front();
274 const auto _indent = gen_indent(indent_level);
275
276 f << _indent << "{\n";
277 f << stringf(" %s\"type\": %s,\n", _indent.c_str(), sort.first.c_str());
278 f << _indent << " \"ports\": [\n";
279
280 write_cell_ports(port_cell, indent_level + 2);
281
282 f << _indent << " ],\n" << _indent << " \"cells\": [\n";
283
284 bool first_cell{true};
285 for (auto& cell : sort.second) {
286 if (!first_cell)
287 f << ",\n";
288
289 write_cell(cell, indent_level + 2);
290
291 first_cell = false;
292 }
293
294 f << "\n";
295 f << _indent << " ]\n";
296 f << _indent << "}";
297 }
298
299 void write_param_val(const Const& v) {
300 if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) == RTLIL::ConstFlags::CONST_FLAG_STRING) {
301 const auto str = v.decode_string();
302
303 // XXX(aki): TODO, uh, yeah
304
305 f << get_string(str);
306 } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) == RTLIL::ConstFlags::CONST_FLAG_SIGNED) {
307 f << stringf("\"%dsd %d\"", v.size(), v.as_int());
308 } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_REAL) == RTLIL::ConstFlags::CONST_FLAG_REAL) {
309
310 } else {
311 f << get_string(v.as_string());
312 }
313 }
314
315 void write_prams(dict<RTLIL::IdString, RTLIL::Const>& params, uint16_t indent_level = 0) {
316 const auto _indent = gen_indent(indent_level);
317
318 bool first_param{true};
319 for (auto& param : params) {
320 if (!first_param)
321 f << stringf(",\n");
322 const auto param_val = param.second;
323 if (!param_val.empty()) {
324 f << stringf(" %s%s: ", _indent.c_str(), get_string(RTLIL::unescape_id(param.first)).c_str());
325 write_param_val(param_val);
326 } else {
327 f << stringf(" %s%s: true", _indent.c_str(), get_string(RTLIL::unescape_id(param.first)).c_str());
328 }
329
330 first_param = false;
331 }
332 }
333
334 void write_cell(Cell* cell, uint16_t indent_level = 0) {
335 const auto _indent = gen_indent(indent_level);
336 log_assert(cell != nullptr);
337
338 f << _indent << " {\n";
339 f << stringf(" %s\"name\": %s", _indent.c_str(), get_string(RTLIL::unescape_id(cell->name)).c_str());
340
341 if (_include_connections) {
342 f << ",\n" << _indent << " \"connections\": [\n";
343
344 bool first_conn{true};
345 for (const auto& conn : cell->connections()) {
346 if (!first_conn)
347 f << ",\n";
348
349 write_cell_conn(conn, indent_level + 2);
350
351 first_conn = false;
352 }
353
354 f << "\n";
355 f << _indent << " ]";
356 }
357
358 if (_include_attributes) {
359 f << ",\n" << _indent << " \"attributes\": {\n";
360
361 write_prams(cell->attributes, indent_level + 2);
362
363 f << "\n";
364 f << _indent << " }";
365 }
366
367 if (_include_properties) {
368 f << ",\n" << _indent << " \"parameters\": {\n";
369
370 write_prams(cell->parameters, indent_level + 2);
371
372 f << "\n";
373 f << _indent << " }";
374 }
375
376 f << "\n" << _indent << " }";
377 }
378 };
379
380 struct JnyBackend : public Backend {
381 JnyBackend() : Backend("jny", "generate design metadata") { }
382 void help() override {
383 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
384 log("\n");
385 log(" jny [options] [selection]\n");
386 log("\n");
387 log(" -connections\n");
388 log(" Include connection information in the netlist output.\n");
389 log("\n");
390 log(" -attributes\n");
391 log(" Include attributed information in the netlist output.\n");
392 log("\n");
393 log(" -properties\n");
394 log(" Include property information in the netlist output.\n");
395 log("\n");
396 log("Write a JSON metadata for the current design\n");
397 log("\n");
398 log("\n");
399 }
400
401 void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override {
402
403 bool connections{false};
404 bool attributes{false};
405 bool properties{false};
406
407 size_t argidx{1};
408 for (; argidx < args.size(); argidx++) {
409 if (args[argidx] == "-connections") {
410 connections = true;
411 continue;
412 }
413
414 if (args[argidx] == "-attributes") {
415 attributes = true;
416 continue;
417 }
418
419 if (args[argidx] == "-properties") {
420 properties = true;
421 continue;
422 }
423
424 break;
425 }
426 extra_args(f, filename, args, argidx);
427
428 log_header(design, "Executing jny backend.\n");
429
430 JnyWriter jny_writer(*f, false, connections, attributes, properties);
431 jny_writer.write_metadata(design);
432 }
433
434 } JnyBackend;
435
436
437 struct JnyPass : public Pass {
438 JnyPass() : Pass("jny", "write design and metadata") { }
439
440 void help() override {
441 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
442 log("\n");
443 log(" jny [options] [selection]\n");
444 log("\n");
445 log("Write a JSON netlist metadata for the current design\n");
446 log("\n");
447 log(" -o <filename>\n");
448 log(" write to the specified file.\n");
449 log("\n");
450 log(" -connections\n");
451 log(" Include connection information in the netlist output.\n");
452 log("\n");
453 log(" -attributes\n");
454 log(" Include attributed information in the netlist output.\n");
455 log("\n");
456 log(" -properties\n");
457 log(" Include property information in the netlist output.\n");
458 log("\n");
459 log("See 'help write_jny' for a description of the JSON format used.\n");
460 log("\n");
461 }
462 void execute(std::vector<std::string> args, RTLIL::Design *design) override {
463 std::string filename{};
464
465 bool connections{false};
466 bool attributes{false};
467 bool properties{false};
468
469 size_t argidx{1};
470 for (; argidx < args.size(); argidx++) {
471 if (args[argidx] == "-o" && argidx+1 < args.size()) {
472 filename = args[++argidx];
473 continue;
474 }
475
476 if (args[argidx] == "-connections") {
477 connections = true;
478 continue;
479 }
480
481 if (args[argidx] == "-attributes") {
482 attributes = true;
483 continue;
484 }
485
486 if (args[argidx] == "-properties") {
487 properties = true;
488 continue;
489 }
490
491 break;
492 }
493 extra_args(args, argidx, design);
494
495 std::ostream *f;
496 std::stringstream buf;
497
498 if (!filename.empty()) {
499 rewrite_filename(filename);
500 std::ofstream *ff = new std::ofstream;
501 ff->open(filename.c_str(), std::ofstream::trunc);
502 if (ff->fail()) {
503 delete ff;
504 log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
505 }
506 f = ff;
507 } else {
508 f = &buf;
509 }
510
511
512 JnyWriter jny_writer(*f, false, connections, attributes, properties);
513 jny_writer.write_metadata(design);
514
515 if (!filename.empty()) {
516 delete f;
517 } else {
518 log("%s", buf.str().c_str());
519 }
520 }
521
522 } JnyPass;
523
524 PRIVATE_NAMESPACE_END