2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Aki "lethalbit" Van Ness <aki@yosyshq.com> <aki@lethalbit.net>
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.
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.
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"
28 #include <unordered_map>
32 PRIVATE_NAMESPACE_BEGIN
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
{};
46 bool _include_connections
;
47 bool _include_attributes
;
48 bool _include_properties
;
50 string
escape_string(string str
) {
53 auto itr
= str
.begin();
55 for(; itr
!= str
.end(); ++itr
) {
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
)
92 for (auto cell
: mod
->cells()) {
93 const auto cell_type
= escape_string(RTLIL::unescape_id(cell
->type
));
95 if (_cells
.find(cell_type
) == _cells
.end())
96 _cells
.emplace(cell_type
, std::vector
<Cell
*>());
98 _cells
.at(cell_type
).push_back(cell
);
102 // XXX(aki): this is a lazy way to do this i know,,,
103 std::string
gen_indent(const uint16_t level
)
106 for (uint16_t i
= 0; i
<= level
; ++i
)
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
)
119 void write_metadata(Design
*design
, uint16_t indent_level
= 0)
121 log_assert(design
!= nullptr);
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";
130 f
<< " \"features\": [";
133 if (_include_connections
) {
135 f
<< "\"connections\"";
138 if (_include_attributes
) {
142 f
<< "\"attributes\"";
145 if (_include_properties
) {
149 f
<< "\"properties\"";
154 f
<< " \"modules\": [\n";
157 for (auto mod
: _use_selection
? design
->selected_modules() : design
->modules()) {
160 write_module(mod
, indent_level
+ 2);
169 void write_sigspec(const RTLIL::SigSpec
& sig
, uint16_t indent_level
= 0) {
170 const auto _indent
= gen_indent(indent_level
);
172 f
<< _indent
<< " {\n";
173 f
<< _indent
<< " \"width\": \"" << sig
.size() << "\",\n";
174 f
<< _indent
<< " \"type\": \"";
178 } else if (sig
.is_chunk()) {
180 } else if (sig
.is_bit()) {
187 f
<< _indent
<< " \"const\": ";
188 if (sig
.has_const()) {
196 f
<< _indent
<< " }";
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";
204 write_sigspec(conn
.first
, indent_level
+ 2);
206 write_sigspec(conn
.second
, indent_level
+ 2);
209 f
<< _indent
<< " ]\n";
210 f
<< _indent
<< " }";
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";
219 write_sigspec(sig
.second
, indent_level
+ 2);
222 f
<< _indent
<< " ]\n";
223 f
<< _indent
<< " }";
226 void write_module(Module
* mod
, uint16_t indent_level
= 0) {
227 log_assert(mod
!= nullptr);
231 const auto _indent
= gen_indent(indent_level
);
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";
237 bool first_sort
{true};
238 for (auto& sort
: _cells
) {
241 write_cell_sort(sort
, indent_level
+ 2);
246 f
<< _indent
<< " ]";
247 if (_include_connections
) {
248 f
<< ",\n" << _indent
<< " \"connections\": [\n";
250 bool first_conn
{true};
251 for (const auto& conn
: mod
->connections()) {
255 write_mod_conn(conn
, indent_level
+ 2);
260 f
<< _indent
<< " ]";
262 if (_include_attributes
) {
263 f
<< ",\n" << _indent
<< " \"attributes\": {\n";
265 write_prams(mod
->attributes
, indent_level
+ 2);
268 f
<< _indent
<< " }";
270 f
<< "\n" << _indent
<< "}";
273 void write_cell_ports(RTLIL::Cell
* port_cell
, uint64_t indent_level
= 0) {
274 const auto _indent
= gen_indent(indent_level
);
276 bool first_port
{true};
277 for (auto con
: port_cell
->connections()) {
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
))
286 if (port_cell
->input(con
.first
))
289 if (con
.second
.size() == 1)
290 f
<< _indent
<< " \"range\": [0, 0]\n";
292 f
<< stringf(" %s\"range\": [%d, %d]\n", _indent
.c_str(), con
.second
.size(), 0);
293 f
<< _indent
<< " }";
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
);
305 f
<< _indent
<< "{\n";
306 f
<< stringf(" %s\"type\": \"%s\",\n", _indent
.c_str(), sort
.first
.c_str());
307 f
<< _indent
<< " \"ports\": [\n";
309 write_cell_ports(port_cell
, indent_level
+ 2);
311 f
<< _indent
<< " ],\n" << _indent
<< " \"cells\": [\n";
313 bool first_cell
{true};
314 for (auto& cell
: sort
.second
) {
318 write_cell(cell
, indent_level
+ 2);
324 f
<< _indent
<< " ]\n";
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();
332 // XXX(aki): TODO, uh, yeah
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
) {
340 f
<< "\"" << escape_string(v
.as_string()) << "\"";
344 void write_prams(dict
<RTLIL::IdString
, RTLIL::Const
>& params
, uint16_t indent_level
= 0) {
345 const auto _indent
= gen_indent(indent_level
);
347 bool first_param
{true};
348 for (auto& param
: params
) {
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
);
356 f
<< stringf(" %s\"%s\": true", _indent
.c_str(), escape_string(RTLIL::unescape_id(param
.first
)).c_str());
363 void write_cell(Cell
* cell
, uint16_t indent_level
= 0) {
364 const auto _indent
= gen_indent(indent_level
);
365 log_assert(cell
!= nullptr);
367 f
<< _indent
<< " {\n";
368 f
<< stringf(" %s\"name\": \"%s\"", _indent
.c_str(), escape_string(RTLIL::unescape_id(cell
->name
)).c_str());
370 if (_include_connections
) {
371 f
<< ",\n" << _indent
<< " \"connections\": [\n";
373 bool first_conn
{true};
374 for (const auto& conn
: cell
->connections()) {
378 write_cell_conn(conn
, indent_level
+ 2);
384 f
<< _indent
<< " ]";
387 if (_include_attributes
) {
388 f
<< ",\n" << _indent
<< " \"attributes\": {\n";
390 write_prams(cell
->attributes
, indent_level
+ 2);
393 f
<< _indent
<< " }";
396 if (_include_properties
) {
397 f
<< ",\n" << _indent
<< " \"parameters\": {\n";
399 write_prams(cell
->parameters
, indent_level
+ 2);
402 f
<< _indent
<< " }";
405 f
<< "\n" << _indent
<< " }";
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---|
415 log(" jny [options] [selection]\n");
417 log(" -no-connections\n");
418 log(" Don't include connection information in the netlist output.\n");
420 log(" -no-attributes\n");
421 log(" Don't include attributed information in the netlist output.\n");
423 log(" -no-properties\n");
424 log(" Don't include property information in the netlist output.\n");
426 log("Write a JSON metadata for the current design\n");
431 void execute(std::ostream
*&f
, std::string filename
, std::vector
<std::string
> args
, RTLIL::Design
*design
) override
{
433 bool connections
{true};
434 bool attributes
{true};
435 bool properties
{true};
438 for (; argidx
< args
.size(); argidx
++) {
439 if (args
[argidx
] == "-no-connections") {
444 if (args
[argidx
] == "-no-attributes") {
449 if (args
[argidx
] == "-no-properties") {
456 extra_args(f
, filename
, args
, argidx
);
458 log_header(design
, "Executing jny backend.\n");
460 JnyWriter
jny_writer(*f
, false, connections
, attributes
, properties
);
461 jny_writer
.write_metadata(design
);
467 struct JnyPass
: public Pass
{
468 JnyPass() : Pass("jny", "write design and metadata") { }
470 void help() override
{
471 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
473 log(" jny [options] [selection]\n");
475 log("Write a JSON netlist metadata for the current design\n");
477 log(" -o <filename>\n");
478 log(" write to the specified file.\n");
480 log(" -no-connections\n");
481 log(" Don't include connection information in the netlist output.\n");
483 log(" -no-attributes\n");
484 log(" Don't include attributed information in the netlist output.\n");
486 log(" -no-properties\n");
487 log(" Don't include property information in the netlist output.\n");
489 log("See 'help write_jny' for a description of the JSON format used.\n");
492 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) override
{
493 std::string filename
{};
495 bool connections
{true};
496 bool attributes
{true};
497 bool properties
{true};
500 for (; argidx
< args
.size(); argidx
++) {
501 if (args
[argidx
] == "-o" && argidx
+1 < args
.size()) {
502 filename
= args
[++argidx
];
506 if (args
[argidx
] == "-no-connections") {
511 if (args
[argidx
] == "-no-attributes") {
516 if (args
[argidx
] == "-no-properties") {
523 extra_args(args
, argidx
, design
);
526 std::stringstream buf
;
528 if (!filename
.empty()) {
529 rewrite_filename(filename
);
530 std::ofstream
*ff
= new std::ofstream
;
531 ff
->open(filename
.c_str(), std::ofstream::trunc
);
534 log_error("Can't open file `%s' for writing: %s\n", filename
.c_str(), strerror(errno
));
542 JnyWriter
jny_writer(*f
, false, connections
, attributes
, properties
);
543 jny_writer
.write_metadata(design
);
545 if (!filename
.empty()) {
548 log("%s", buf
.str().c_str());
554 PRIVATE_NAMESPACE_END