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