mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	There are some leftovers, but this is an easy regex-based approach that removes most of them.
		
			
				
	
	
		
			577 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			577 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  *  yosys -- Yosys Open SYnthesis Suite
 | |
|  *
 | |
|  *  Copyright (C) 2012  Aki "lethalbit" Van Ness <aki@yosyshq.com> <aki@lethalbit.net>
 | |
|  *
 | |
|  *  Permission to use, copy, modify, and/or distribute this software for any
 | |
|  *  purpose with or without fee is hereby granted, provided that the above
 | |
|  *  copyright notice and this permission notice appear in all copies.
 | |
|  *
 | |
|  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | |
|  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | |
|  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | |
|  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | |
|  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | |
|  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | |
|  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "kernel/rtlil.h"
 | |
| #include "kernel/register.h"
 | |
| #include "kernel/sigtools.h"
 | |
| #include "kernel/celltypes.h"
 | |
| #include "kernel/log.h"
 | |
| #include <string>
 | |
| #include <algorithm>
 | |
| #include <unordered_map>
 | |
| #include <vector>
 | |
| #include <sstream>
 | |
| #include <iterator>
 | |
| 
 | |
| USING_YOSYS_NAMESPACE
 | |
| PRIVATE_NAMESPACE_BEGIN
 | |
| 
 | |
| 
 | |
| struct JnyWriter
 | |
| {
 | |
|     private:
 | |
|         std::ostream &f;
 | |
|         bool _use_selection;
 | |
| 
 | |
|         // XXX(aki): TODO: this needs to be updated to us
 | |
|         // dict<T, V> and then coalesce_cells needs to be updated
 | |
|         // but for now for the PoC this looks to be sufficient
 | |
|         std::unordered_map<std::string, std::vector<Cell*>> _cells{};
 | |
| 
 | |
|         bool _include_connections;
 | |
|         bool _include_attributes;
 | |
|         bool _include_properties;
 | |
| 
 | |
|         string escape_string(string str) {
 | |
|             std::string newstr;
 | |
| 
 | |
|             auto itr = str.begin();
 | |
| 
 | |
|             for(; itr != str.end(); ++itr) {
 | |
|                 switch (*itr) {
 | |
|                     case '\\': {
 | |
|                         newstr += "\\\\";
 | |
|                         break;
 | |
|                     } case '\n': {
 | |
|                         newstr += "\\n";
 | |
|                         break;
 | |
|                     } case '\f': {
 | |
|                         newstr += "\\f";
 | |
|                         break;
 | |
|                     } case '\t': {
 | |
|                         newstr += "\\t";
 | |
|                         break;
 | |
|                     } case '\r': {
 | |
|                         newstr += "\\r";
 | |
|                         break;
 | |
|                     } case '\"': {
 | |
|                         newstr += "\\\"";
 | |
|                         break;
 | |
|                     } case '\b': {
 | |
|                         newstr += "\\b";
 | |
|                         break;
 | |
|                     } default: {
 | |
|                         newstr += *itr;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return newstr;
 | |
|         }
 | |
| 
 | |
|         // XXX(aki): I know this is far from ideal but i'm out of spoons and cant focus so
 | |
|         // it'll have to do for now,
 | |
|         void coalesce_cells(Module* mod)
 | |
|         {
 | |
|             _cells.clear();
 | |
|             for (auto cell : mod->cells()) {
 | |
|                 const auto cell_type = escape_string(RTLIL::unescape_id(cell->type));
 | |
| 
 | |
|                 if (_cells.find(cell_type) == _cells.end())
 | |
|                     _cells.emplace(cell_type, std::vector<Cell*>());
 | |
| 
 | |
|                 _cells.at(cell_type).push_back(cell);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // XXX(aki): this is a lazy way to do this i know,,,
 | |
|         std::string gen_indent(const uint16_t level)
 | |
|         {
 | |
|             std::stringstream s;
 | |
|             for (uint16_t i = 0; i <= level; ++i)
 | |
|             {
 | |
|                 s << "  ";
 | |
|             }
 | |
|             return s.str();
 | |
|         }
 | |
| 
 | |
|     public:
 | |
|     JnyWriter(std::ostream &f, bool use_selection, bool connections, bool attributes, bool properties) noexcept:
 | |
|         f(f), _use_selection(use_selection),
 | |
|         _include_connections(connections), _include_attributes(attributes), _include_properties(properties)
 | |
|          { }
 | |
| 
 | |
|     void write_metadata(Design *design, uint16_t indent_level = 0, std::string invk = "")
 | |
|     {
 | |
|         log_assert(design != nullptr);
 | |
| 
 | |
|         design->sort();
 | |
| 
 | |
|         f << "{\n";
 | |
|         f << "  \"$schema\": \"https://raw.githubusercontent.com/YosysHQ/yosys/main/misc/jny.schema.json\",\n";
 | |
|         f << stringf("  \"generator\": \"%s\",\n", escape_string(yosys_maybe_version()));
 | |
|         f << "  \"version\": \"0.0.1\",\n";
 | |
|         f << "  \"invocation\": \"" << escape_string(invk) << "\",\n";
 | |
|         f << "  \"features\": [";
 | |
| 
 | |
|         size_t fnum{0};
 | |
|         if (_include_connections) {
 | |
|             ++fnum;
 | |
|             f << "\"connections\"";
 | |
|         }
 | |
| 
 | |
|         if (_include_attributes) {
 | |
|             if (fnum > 0)
 | |
|                 f << ", ";
 | |
|             ++fnum;
 | |
|             f << "\"attributes\"";
 | |
|         }
 | |
| 
 | |
|         if (_include_properties) {
 | |
|             if (fnum > 0)
 | |
|                 f << ", ";
 | |
|             ++fnum;
 | |
|             f << "\"properties\"";
 | |
|         }
 | |
| 
 | |
|         f << "],\n";
 | |
| 
 | |
|         f << "  \"modules\": [\n";
 | |
| 
 | |
|         bool first{true};
 | |
|         for (auto mod : _use_selection ? design->selected_modules() : design->modules()) {
 | |
|             if (!first)
 | |
|                 f << ",\n";
 | |
|             write_module(mod, indent_level + 2);
 | |
|             first = false;
 | |
|         }
 | |
| 
 | |
|         f << "\n";
 | |
|         f << "  ]\n";
 | |
|         f << "}\n";
 | |
|     }
 | |
| 
 | |
|     void write_sigspec(const RTLIL::SigSpec& sig, uint16_t indent_level = 0) {
 | |
|         const auto _indent = gen_indent(indent_level);
 | |
| 
 | |
|         f << _indent << "  {\n";
 | |
|         f << _indent << "    \"width\": \"" << sig.size() << "\",\n";
 | |
|         f << _indent << "    \"type\": \"";
 | |
| 
 | |
|         if (sig.is_wire()) {
 | |
|             f << "wire";
 | |
|         } else if (sig.is_chunk()) {
 | |
|             f << "chunk";
 | |
|         } else if (sig.is_bit()) {
 | |
|             f << "bit";
 | |
|         } else {
 | |
|             f << "unknown";
 | |
|         }
 | |
|         f << "\",\n";
 | |
| 
 | |
|         f << _indent << "    \"const\": ";
 | |
|         if (sig.has_const()) {
 | |
|             f << "true";
 | |
|         } else {
 | |
|             f << "false";
 | |
|         }
 | |
| 
 | |
|         f << "\n";
 | |
| 
 | |
|         f << _indent << "  }";
 | |
|     }
 | |
| 
 | |
|     void write_mod_conn(const std::pair<RTLIL::SigSpec, RTLIL::SigSpec>& conn, uint16_t indent_level = 0) {
 | |
|         const auto _indent = gen_indent(indent_level);
 | |
|         f << _indent << "  {\n";
 | |
|         f << _indent << "    \"signals\": [\n";
 | |
| 
 | |
|         write_sigspec(conn.first, indent_level + 2);
 | |
|         f << ",\n";
 | |
|         write_sigspec(conn.second, indent_level + 2);
 | |
|         f << "\n";
 | |
| 
 | |
|         f << _indent << "     ]\n";
 | |
|         f << _indent << "  }";
 | |
|     }
 | |
| 
 | |
|     void write_cell_conn(const std::pair<RTLIL::IdString, RTLIL::SigSpec>& sig, uint16_t indent_level = 0) {
 | |
|         const auto _indent = gen_indent(indent_level);
 | |
|         f << _indent << "  {\n";
 | |
|         f << _indent << "    \"name\": \"" << escape_string(RTLIL::unescape_id(sig.first)) << "\",\n";
 | |
|         f << _indent << "    \"signals\": [\n";
 | |
| 
 | |
|         write_sigspec(sig.second, indent_level + 2);
 | |
|         f << "\n";
 | |
| 
 | |
|         f << _indent << "     ]\n";
 | |
|         f << _indent << "  }";
 | |
|     }
 | |
| 
 | |
|     void write_module(Module* mod, uint16_t indent_level = 0) {
 | |
|         log_assert(mod != nullptr);
 | |
| 
 | |
|         coalesce_cells(mod);
 | |
| 
 | |
|         const auto _indent = gen_indent(indent_level);
 | |
| 
 | |
|         f << _indent << "{\n";
 | |
|         f << stringf("  %s\"name\": \"%s\",\n", _indent, escape_string(RTLIL::unescape_id(mod->name)));
 | |
|         f << _indent << "  \"cell_sorts\": [\n";
 | |
| 
 | |
|         bool first_sort{true};
 | |
|         for (auto& sort : _cells) {
 | |
|             if (!first_sort)
 | |
|                 f << ",\n";
 | |
|             write_cell_sort(sort, indent_level + 2);
 | |
|             first_sort = false;
 | |
|         }
 | |
|         f << "\n";
 | |
| 
 | |
|         f << _indent << "  ]";
 | |
|         if (_include_connections) {
 | |
|             f << ",\n" << _indent << "  \"connections\": [\n";
 | |
| 
 | |
|             bool first_conn{true};
 | |
|             for (const auto& conn : mod->connections()) {
 | |
|                 if (!first_conn)
 | |
|                     f << ",\n";
 | |
| 
 | |
|                 write_mod_conn(conn, indent_level + 2);
 | |
| 
 | |
|                 first_conn = false;
 | |
|             }
 | |
| 
 | |
|             f << _indent << "  ]";
 | |
|         }
 | |
|         if (_include_attributes) {
 | |
|             f << ",\n" << _indent << "  \"attributes\": {\n";
 | |
| 
 | |
|             write_prams(mod->attributes, indent_level + 2);
 | |
| 
 | |
|             f << "\n";
 | |
|             f << _indent << "  }";
 | |
|         }
 | |
|         f << "\n" << _indent << "}";
 | |
|     }
 | |
| 
 | |
|     void write_cell_ports(RTLIL::Cell* port_cell, uint64_t indent_level = 0) {
 | |
|         const auto _indent = gen_indent(indent_level);
 | |
| 
 | |
|         bool first_port{true};
 | |
|         for (auto con : port_cell->connections()) {
 | |
|             if (!first_port)
 | |
|                 f << ",\n";
 | |
| 
 | |
|             f << _indent << "  {\n";
 | |
|             f << stringf("    %s\"name\": \"%s\",\n", _indent, escape_string(RTLIL::unescape_id(con.first)));
 | |
|             f << _indent << "    \"direction\": \"";
 | |
|             if (port_cell->input(con.first))
 | |
|                 f << "i";
 | |
|             if (port_cell->input(con.first))
 | |
|                 f << "o";
 | |
|             f << "\",\n";
 | |
|             if (con.second.size() == 1)
 | |
|                 f << _indent << "    \"range\": [0, 0]\n";
 | |
|             else
 | |
|                 f << stringf("    %s\"range\": [%d, %d]\n", _indent, con.second.size(), 0);
 | |
|             f << _indent << "  }";
 | |
| 
 | |
|             first_port = false;
 | |
|         }
 | |
|         f << "\n";
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void write_cell_sort(std::pair<const std::string, std::vector<Cell*>>& sort, uint16_t indent_level = 0) {
 | |
|         const auto port_cell = sort.second.front();
 | |
|         const auto _indent = gen_indent(indent_level);
 | |
| 
 | |
|         f << _indent << "{\n";
 | |
|         f << stringf("  %s\"type\": \"%s\",\n", _indent, sort.first);
 | |
|         f << _indent << "  \"ports\": [\n";
 | |
| 
 | |
|         write_cell_ports(port_cell, indent_level + 2);
 | |
| 
 | |
|         f << _indent << "  ],\n" << _indent << "  \"cells\": [\n";
 | |
| 
 | |
|         bool first_cell{true};
 | |
|         for (auto& cell : sort.second) {
 | |
|             if (!first_cell)
 | |
|                 f << ",\n";
 | |
| 
 | |
|             write_cell(cell, indent_level + 2);
 | |
| 
 | |
|             first_cell = false;
 | |
|         }
 | |
| 
 | |
|         f << "\n";
 | |
|         f << _indent << "  ]\n";
 | |
|         f << _indent << "}";
 | |
|     }
 | |
| 
 | |
|     void write_param_val(const Const& v) {
 | |
|         if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) == RTLIL::ConstFlags::CONST_FLAG_STRING) {
 | |
|             const auto str = v.decode_string();
 | |
| 
 | |
|             // XXX(aki): TODO, uh, yeah
 | |
| 
 | |
|             f << "\"" << escape_string(str) << "\"";
 | |
|         } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) == RTLIL::ConstFlags::CONST_FLAG_SIGNED) {
 | |
|             f << stringf("\"%dsd %d\"", v.size(), v.as_int(true));
 | |
|         } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_REAL) == RTLIL::ConstFlags::CONST_FLAG_REAL) {
 | |
| 
 | |
|         } else {
 | |
|             f << "\"" << escape_string(v.as_string()) << "\"";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void write_prams(dict<RTLIL::IdString, RTLIL::Const>& params, uint16_t indent_level = 0) {
 | |
|         const auto _indent = gen_indent(indent_level);
 | |
| 
 | |
|         bool first_param{true};
 | |
|         for (auto& param : params) {
 | |
|             if (!first_param)
 | |
|                 f << stringf(",\n");
 | |
|             const auto param_val = param.second;
 | |
|             if (!param_val.empty()) {
 | |
|                 f << stringf("  %s\"%s\": ", _indent, escape_string(RTLIL::unescape_id(param.first)));
 | |
|                 write_param_val(param_val);
 | |
|             } else {
 | |
|                 f << stringf("  %s\"%s\": true", _indent, escape_string(RTLIL::unescape_id(param.first)));
 | |
|             }
 | |
| 
 | |
|             first_param = false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void write_cell(Cell* cell, uint16_t indent_level = 0) {
 | |
|         const auto _indent = gen_indent(indent_level);
 | |
|         log_assert(cell != nullptr);
 | |
| 
 | |
|         f << _indent << "  {\n";
 | |
|         f << stringf("    %s\"name\": \"%s\"", _indent, escape_string(RTLIL::unescape_id(cell->name)));
 | |
| 
 | |
|         if (_include_connections) {
 | |
|             f << ",\n" << _indent << "    \"connections\": [\n";
 | |
| 
 | |
|             bool first_conn{true};
 | |
|             for (const auto& conn : cell->connections()) {
 | |
|                 if (!first_conn)
 | |
|                     f << ",\n";
 | |
| 
 | |
|                 write_cell_conn(conn, indent_level + 2);
 | |
| 
 | |
|                 first_conn = false;
 | |
|             }
 | |
| 
 | |
|             f << "\n";
 | |
|             f << _indent << "    ]";
 | |
|         }
 | |
| 
 | |
|         if (_include_attributes) {
 | |
|             f << ",\n" << _indent << "    \"attributes\": {\n";
 | |
| 
 | |
|             write_prams(cell->attributes, indent_level + 2);
 | |
| 
 | |
|             f << "\n";
 | |
|             f << _indent << "    }";
 | |
|         }
 | |
| 
 | |
|         if (_include_properties) {
 | |
|             f << ",\n" << _indent << "    \"parameters\": {\n";
 | |
| 
 | |
|             write_prams(cell->parameters, indent_level + 2);
 | |
| 
 | |
|             f << "\n";
 | |
|             f << _indent << "    }";
 | |
|         }
 | |
| 
 | |
|         f << "\n" << _indent << "  }";
 | |
|     }
 | |
| };
 | |
| 
 | |
| struct JnyBackend : public Backend {
 | |
|     JnyBackend() : Backend("jny", "generate design metadata") { }
 | |
|     void help() override {
 | |
|         //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | |
|         log("\n");
 | |
|         log("    jny [options] [selection]\n");
 | |
|         log("\n");
 | |
|         log("Write JSON netlist metadata for the current design\n");
 | |
|         log("\n");
 | |
|         log("    -no-connections\n");
 | |
|         log("        Don't include connection information in the netlist output.\n");
 | |
|         log("\n");
 | |
|         log("    -no-attributes\n");
 | |
|         log("        Don't include attributed information in the netlist output.\n");
 | |
|         log("\n");
 | |
|         log("    -no-properties\n");
 | |
|         log("        Don't include property information in the netlist output.\n");
 | |
|         log("\n");
 | |
|         log("The JSON schema for JNY output files is located in the \"jny.schema.json\" file\n");
 | |
|         log("which is located at \"https://raw.githubusercontent.com/YosysHQ/yosys/main/misc/jny.schema.json\"\n");
 | |
|         log("\n");
 | |
|     }
 | |
| 
 | |
|     void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override {
 | |
| 
 | |
|         bool connections{true};
 | |
|         bool attributes{true};
 | |
|         bool properties{true};
 | |
| 
 | |
|         size_t argidx{1};
 | |
|         for (; argidx < args.size(); argidx++) {
 | |
|             if (args[argidx] == "-no-connections") {
 | |
|                 connections = false;
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             if (args[argidx] == "-no-attributes") {
 | |
|                 attributes = false;
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             if (args[argidx] == "-no-properties") {
 | |
|                 properties = false;
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         // Compose invocation line
 | |
|         std::ostringstream invk;
 | |
|         if (!args.empty()) {
 | |
|             std::copy(args.begin(), args.end(),
 | |
|                 std::ostream_iterator<std::string>(invk, " ")
 | |
|             );
 | |
|         }
 | |
|         invk << filename;
 | |
| 
 | |
|         extra_args(f, filename, args, argidx);
 | |
| 
 | |
|         log_header(design, "Executing jny backend.\n");
 | |
| 
 | |
|         JnyWriter jny_writer(*f, false, connections, attributes, properties);
 | |
|         jny_writer.write_metadata(design, 0, invk.str());
 | |
|     }
 | |
| 
 | |
| } JnyBackend;
 | |
| 
 | |
| 
 | |
| struct JnyPass : public Pass {
 | |
|     JnyPass() : Pass("jny", "write design and metadata") { }
 | |
| 
 | |
|     void help() override {
 | |
|         //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | |
|         log("\n");
 | |
|         log("    jny [options] [selection]\n");
 | |
|         log("\n");
 | |
|         log("Write JSON netlist metadata for the current design\n");
 | |
|         log("\n");
 | |
|         log("    -o <filename>\n");
 | |
|         log("        write to the specified file.\n");
 | |
|         log("\n");
 | |
|         log("    -no-connections\n");
 | |
|         log("        Don't include connection information in the netlist output.\n");
 | |
|         log("\n");
 | |
|         log("    -no-attributes\n");
 | |
|         log("        Don't include attributed information in the netlist output.\n");
 | |
|         log("\n");
 | |
|         log("    -no-properties\n");
 | |
|         log("        Don't include property information in the netlist output.\n");
 | |
|         log("\n");
 | |
|         log("See 'help write_jny' for a description of the JSON format used.\n");
 | |
|         log("\n");
 | |
|     }
 | |
|     void execute(std::vector<std::string> args, RTLIL::Design *design) override {
 | |
|         std::string filename{};
 | |
| 
 | |
|         bool connections{true};
 | |
|         bool attributes{true};
 | |
|         bool properties{true};
 | |
| 
 | |
|         size_t argidx{1};
 | |
|         for (; argidx < args.size(); argidx++) {
 | |
|             if (args[argidx] == "-o" && argidx+1 < args.size()) {
 | |
|                 filename = args[++argidx];
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             if (args[argidx] == "-no-connections") {
 | |
|                 connections = false;
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             if (args[argidx] == "-no-attributes") {
 | |
|                 attributes = false;
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             if (args[argidx] == "-no-properties") {
 | |
|                 properties = false;
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         // Compose invocation line
 | |
|         std::ostringstream invk;
 | |
|         if (!args.empty()) {
 | |
|             std::copy(args.begin(), args.end(),
 | |
|                 std::ostream_iterator<std::string>(invk, " ")
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         extra_args(args, argidx, design);
 | |
| 
 | |
|         std::ostream *f;
 | |
|         std::stringstream buf;
 | |
|         bool empty = filename.empty();
 | |
| 
 | |
|         if (!empty) {
 | |
|             rewrite_filename(filename);
 | |
|             std::ofstream *ff = new std::ofstream;
 | |
|             ff->open(filename.c_str(), std::ofstream::trunc);
 | |
|             if (ff->fail()) {
 | |
|                 delete ff;
 | |
|                 log_error("Can't open file `%s' for writing: %s\n", filename, strerror(errno));
 | |
|             }
 | |
|             f = ff;
 | |
|             invk << filename;
 | |
|         } else {
 | |
|             f = &buf;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         JnyWriter jny_writer(*f, false, connections, attributes, properties);
 | |
|         jny_writer.write_metadata(design, 0, invk.str());
 | |
| 
 | |
|         if (!empty) {
 | |
|             delete f;
 | |
|         } else {
 | |
|             log("%s", buf.str());
 | |
|         }
 | |
|     }
 | |
| 
 | |
| } JnyPass;
 | |
| 
 | |
| PRIVATE_NAMESPACE_END
 |