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