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