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