pass jny: added filter options for including connections, attributes, and properties
[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_module(Module* mod, uint16_t indent_level = 0) {
141 log_assert(mod != nullptr);
142
143 coalesce_cells(mod);
144
145 const auto _indent = gen_indent(indent_level);
146
147 f << _indent << "{\n";
148 f << stringf(" %s\"name\": %s,\n", _indent.c_str(), get_string(RTLIL::unescape_id(mod->name)).c_str());
149 f << _indent << " \"cell_sorts\": [\n";
150
151 bool first_sort{true};
152 for (auto& sort : _cells) {
153 if (!first_sort)
154 f << ",\n";
155 write_cell_sort(sort, indent_level + 2);
156 first_sort = false;
157 }
158 f << "\n";
159
160 f << _indent << " ]";
161 if (_include_connections) {
162 f << _indent << ",\n \"connections\": [\n";
163
164 f << _indent << " ]";
165 }
166 if (_include_attributes) {
167 f << _indent << ",\n \"attributes\": {\n";
168
169 write_prams(mod->attributes, indent_level + 2);
170
171 f << "\n";
172 f << _indent << " }";
173 }
174 f << "\n" << _indent << "}";
175 }
176
177 void write_cell_ports(RTLIL::Cell* port_cell, uint64_t indent_level = 0) {
178 const auto _indent = gen_indent(indent_level);
179
180 bool first_port{true};
181 for (auto con : port_cell->connections()) {
182 if (!first_port)
183 f << ",\n";
184
185 f << _indent << " {\n";
186 f << stringf(" %s\"name\": %s,\n", _indent.c_str(), get_string(RTLIL::unescape_id(con.first)).c_str());
187 f << _indent << " \"direction\": \"";
188 if (port_cell->input(con.first))
189 f << "i";
190 if (port_cell->input(con.first))
191 f << "o";
192 f << "\",\n";
193 if (con.second.size() == 1)
194 f << _indent << " \"range\": [0, 0]\n";
195 else
196 f << stringf(" %s\"range\": [%d, %d]\n", _indent.c_str(), con.second.size(), 0);
197 f << _indent << " }";
198
199 first_port = false;
200 }
201 f << "\n";
202 }
203
204
205 void write_cell_sort(std::pair<const std::string, std::vector<Cell*>>& sort, uint16_t indent_level = 0) {
206 const auto port_cell = sort.second.front();
207 const auto _indent = gen_indent(indent_level);
208
209 f << _indent << "{\n";
210 f << stringf(" %s\"type\": %s,\n", _indent.c_str(), sort.first.c_str());
211 f << _indent << " \"ports\": [\n";
212
213 write_cell_ports(port_cell, indent_level + 2);
214
215 f << _indent << " ],\n" << _indent << " \"cells\": [\n";
216
217 bool first_cell{true};
218 for (auto& cell : sort.second) {
219 if (!first_cell)
220 f << ",\n";
221
222 write_cell(cell, indent_level + 2);
223
224 first_cell = false;
225 }
226
227 f << "\n";
228 f << _indent << " ]\n";
229 f << _indent << "}";
230 }
231
232 void write_param_val(const Const& v) {
233 if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) == RTLIL::ConstFlags::CONST_FLAG_STRING) {
234 const auto str = v.decode_string();
235
236 // XXX(aki): TODO, uh, yeah
237
238 f << get_string(str);
239 } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) == RTLIL::ConstFlags::CONST_FLAG_SIGNED) {
240 f << stringf("\"%dsd %d\"", v.size(), v.as_int());
241 } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_REAL) == RTLIL::ConstFlags::CONST_FLAG_REAL) {
242
243 } else {
244 f << get_string(v.as_string());
245 }
246 }
247
248 void write_prams(dict<RTLIL::IdString, RTLIL::Const>& params, uint16_t indent_level = 0) {
249 const auto _indent = gen_indent(indent_level);
250
251 bool first_param{true};
252 for (auto& param : params) {
253 if (!first_param)
254 f << stringf(",\n");
255 const auto param_val = param.second;
256 if (!param_val.empty()) {
257 f << stringf(" %s%s: ", _indent.c_str(), get_string(RTLIL::unescape_id(param.first)).c_str());
258 write_param_val(param_val);
259 } else {
260 f << stringf(" %s%s: true", _indent.c_str(), get_string(RTLIL::unescape_id(param.first)).c_str());
261 }
262
263 first_param = false;
264 }
265 }
266
267 void write_cell(Cell* cell, uint16_t indent_level = 0) {
268 const auto _indent = gen_indent(indent_level);
269 log_assert(cell != nullptr);
270
271 f << _indent << " {\n";
272 f << stringf(" %s\"name\": %s", _indent.c_str(), get_string(RTLIL::unescape_id(cell->name)).c_str());
273
274 if (_include_attributes) {
275 f << _indent << ",\n \"attributes\": {\n";
276
277 write_prams(cell->attributes, indent_level + 2);
278
279 f << "\n";
280 f << _indent << " }";
281 }
282
283 if (_include_properties) {
284 f << _indent << ",\n \"parameters\": {\n";
285
286 write_prams(cell->parameters, indent_level + 2);
287
288 f << "\n";
289 f << _indent << " }";
290 }
291
292 f << "\n" << _indent << " }";
293 }
294 };
295
296 struct JnyBackend : public Backend {
297 JnyBackend() : Backend("jny", "generate design metadata") { }
298 void help() override {
299 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
300 log("\n");
301 log(" jny [options] [selection]\n");
302 log("\n");
303 log(" -connections\n");
304 log(" Include connection information in the netlist output.\n");
305 log("\n");
306 log(" -attributes\n");
307 log(" Include attributed information in the netlist output.\n");
308 log("\n");
309 log(" -properties\n");
310 log(" Include property information in the netlist output.\n");
311 log("\n");
312 log("Write a JSON metadata for the current design\n");
313 log("\n");
314 log("\n");
315 }
316
317 void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override {
318
319 bool connections{false};
320 bool attributes{false};
321 bool properties{false};
322
323 size_t argidx{1};
324 for (; argidx < args.size(); argidx++) {
325 if (args[argidx] == "-connections") {
326 connections = true;
327 continue;
328 }
329
330 if (args[argidx] == "-attributes") {
331 attributes = true;
332 continue;
333 }
334
335 if (args[argidx] == "-properties") {
336 properties = true;
337 continue;
338 }
339
340 break;
341 }
342 extra_args(f, filename, args, argidx);
343
344 log_header(design, "Executing jny backend.\n");
345
346 JnyWriter jny_writer(*f, false, connections, attributes, properties);
347 jny_writer.write_metadata(design);
348 }
349
350 } JnyBackend;
351
352
353 struct JnyPass : public Pass {
354 JnyPass() : Pass("jny", "write design and metadata") { }
355
356 void help() override {
357 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
358 log("\n");
359 log(" jny [options] [selection]\n");
360 log("\n");
361 log("Write a JSON netlist metadata for the current design\n");
362 log("\n");
363 log(" -o <filename>\n");
364 log(" write to the specified file.\n");
365 log("\n");
366 log(" -connections\n");
367 log(" Include connection information in the netlist output.\n");
368 log("\n");
369 log(" -attributes\n");
370 log(" Include attributed information in the netlist output.\n");
371 log("\n");
372 log(" -properties\n");
373 log(" Include property information in the netlist output.\n");
374 log("\n");
375 log("See 'help write_jny' for a description of the JSON format used.\n");
376 log("\n");
377 }
378 void execute(std::vector<std::string> args, RTLIL::Design *design) override {
379 std::string filename{};
380
381 bool connections{false};
382 bool attributes{false};
383 bool properties{false};
384
385 size_t argidx{1};
386 for (; argidx < args.size(); argidx++) {
387 if (args[argidx] == "-o" && argidx+1 < args.size()) {
388 filename = args[++argidx];
389 continue;
390 }
391
392 if (args[argidx] == "-connections") {
393 connections = true;
394 continue;
395 }
396
397 if (args[argidx] == "-attributes") {
398 attributes = true;
399 continue;
400 }
401
402 if (args[argidx] == "-properties") {
403 properties = true;
404 continue;
405 }
406
407 break;
408 }
409 extra_args(args, argidx, design);
410
411 std::ostream *f;
412 std::stringstream buf;
413
414 if (!filename.empty()) {
415 rewrite_filename(filename);
416 std::ofstream *ff = new std::ofstream;
417 ff->open(filename.c_str(), std::ofstream::trunc);
418 if (ff->fail()) {
419 delete ff;
420 log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
421 }
422 f = ff;
423 } else {
424 f = &buf;
425 }
426
427
428 JnyWriter jny_writer(*f, false, connections, attributes, properties);
429 jny_writer.write_metadata(design);
430
431 if (!filename.empty()) {
432 delete f;
433 } else {
434 log("%s", buf.str().c_str());
435 }
436 }
437
438 } JnyPass;
439
440 PRIVATE_NAMESPACE_END