From 7764d0ba1dcf064ae487ee985c43083a0909e7f4 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 5 Jan 2013 11:13:26 +0100 Subject: initial import --- kernel/show.cc | 343 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 kernel/show.cc (limited to 'kernel/show.cc') diff --git a/kernel/show.cc b/kernel/show.cc new file mode 100644 index 00000000..d7da62cd --- /dev/null +++ b/kernel/show.cc @@ -0,0 +1,343 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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/register.h" +#include "kernel/celltypes.h" +#include "kernel/log.h" +#include +#include + +#undef CLUSTER_CELLS_AND_PORTBOXES + +struct ShowWorker +{ + CellTypes ct; + + std::vector dot_escape_store; + std::map dot_id2num_store; + std::map autonames; + int single_idx_count; + + struct net_conn { std::set in, out; }; + std::map net_conn_map; + + FILE *f; + RTLIL::Design *design; + RTLIL::Module *module; + int page_counter; + + const char *escape(std::string id, bool is_name = false) + { + if (id.size() == 0) + return ""; + + if (id[0] == '$' && is_name) { + if (autonames.count(id) == 0) { + autonames[id] = autonames.size() + 1; + log("Generated short name for internal identifier: _%d_ -> %s\n", autonames[id], id.c_str()); + } + id = stringf("_%d_", autonames[id]); + } + + if (id[0] == '\\') + id = id.substr(1); + + std::string str; + for (char ch : id) { + if (ch == '\\' || ch == '"') + str += "\\"; + str += ch; + } + + dot_escape_store.push_back(str); + return dot_escape_store.back().c_str(); + } + + int id2num(RTLIL::IdString id) + { + if (dot_id2num_store.count(id) > 0) + return dot_id2num_store[id]; + return dot_id2num_store[id] = dot_id2num_store.size() + 1; + } + + std::string gen_signode_simple(RTLIL::SigSpec sig, bool range_check = true) + { + sig.optimize(); + + if (sig.chunks.size() == 0) { + fprintf(f, "v%d [ label=\"\" ];\n", single_idx_count); + return stringf("v%d", single_idx_count++); + } + + if (sig.chunks.size() == 1) { + RTLIL::SigChunk &c = sig.chunks[0]; + if (c.wire != NULL && design->selected_member(module->name, c.wire->name)) { + if (!range_check || c.wire->width == c.width) + return stringf("n%d", id2num(c.wire->name)); + } else { + fprintf(f, "v%d [ label=\"%s\" ];\n", single_idx_count, escape(log_signal(c), true)); + return stringf("v%d", single_idx_count++); + } + } + + return std::string(); + } + + std::string gen_portbox(std::string port, RTLIL::SigSpec sig, bool driver, std::string *node = NULL) + { + std::string code; + std::string net = gen_signode_simple(sig); + if (net.empty()) + { + std::string label_string; + sig.optimize(); + int pos = sig.width-1; + int idx = single_idx_count++; + for (int i = int(sig.chunks.size())-1; i >= 0; i--) { + RTLIL::SigChunk &c = sig.chunks[i]; + net = gen_signode_simple(c, false); + assert(!net.empty()); + if (driver) { + label_string += stringf(" %d:%d - %d:%d |", i, pos, pos-c.width+1, c.offset+c.width-1, c.offset); + net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i)); + } else { + label_string += stringf(" %d:%d - %d:%d |", i, c.offset+c.width-1, c.offset, pos, pos-c.width+1); + net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i)); + } + pos -= c.width; + } + if (label_string[label_string.size()-1] == '|') + label_string = label_string.substr(0, label_string.size()-1); + code += stringf("x%d [ shape=record, style=rounded, label=\"%s\" ];\n", idx, label_string.c_str()); + if (!port.empty()) { + if (driver) + code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both];\n", port.c_str(), idx); + else + code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both];\n", idx, port.c_str()); + } + if (node != NULL) + *node = stringf("x%d", idx); + } + else + { + if (!port.empty()) { + if (driver) + net_conn_map[net].in.insert(port); + else + net_conn_map[net].out.insert(port); + } + if (node != NULL) + *node = net; + } + return code; + } + + void handle_module() + { + single_idx_count = 0; + dot_escape_store.clear(); + dot_id2num_store.clear(); + net_conn_map.clear(); + + fprintf(f, "digraph \"%s\" {\n", escape(module->name)); + fprintf(f, "rankdir=\"LR\";\n"); + fprintf(f, "remincross=true;\n"); + + std::map wires_on_demand; + for (auto &it : module->wires) { + if (!design->selected_member(module->name, it.first)) + continue; + const char *shape = "diamond"; + if (it.second->port_input || it.second->port_output) + shape = "octagon"; + if (it.first[0] == '\\') + fprintf(f, "n%d [ shape=%s, label=\"%s\" ];\n", + id2num(it.first), shape, escape(it.first)); + else { + wires_on_demand[stringf("n%d", id2num(it.first))] = it.first; + } + } + + for (auto &it : module->cells) + { + if (!design->selected_member(module->name, it.first)) + continue; + + std::vector in_ports, out_ports; + + for (auto &conn : it.second->connections) { + if (ct.cell_input(it.second->type, conn.first)) + in_ports.push_back(conn.first); + else + out_ports.push_back(conn.first); + } + + std::string label_string = "{{"; + + for (auto &p : in_ports) + label_string += stringf(" %s|", id2num(p), escape(p)); + if (label_string[label_string.size()-1] == '|') + label_string = label_string.substr(0, label_string.size()-1); + + label_string += stringf("}|%s\\n%s|{", escape(it.first, true), escape(it.second->type)); + + for (auto &p : out_ports) + label_string += stringf(" %s|", id2num(p), escape(p)); + if (label_string[label_string.size()-1] == '|') + label_string = label_string.substr(0, label_string.size()-1); + + label_string += "}}"; + + std::string code; + for (auto &conn : it.second->connections) { + code += gen_portbox(stringf("c%d:p%d", id2num(it.first), id2num(conn.first)), + conn.second, !ct.cell_input(it.second->type, conn.first)); + } + +#ifdef CLUSTER_CELLS_AND_PORTBOXES + if (!code.empty()) + fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\" ];\n%s}\n", + id2num(it.first), id2num(it.first), label_string.c_str(), code.c_str()); + else +#endif + fprintf(f, "c%d [ shape=record, label=\"%s\" ];\n%s", + id2num(it.first), label_string.c_str(), code.c_str()); + } + + for (auto &conn : module->connections) + { + bool found_lhs_wire = false; + for (auto &c : conn.first.chunks) { + if (c.wire != NULL && design->selected_member(module->name, c.wire->name)) + found_lhs_wire = true; + } + bool found_rhs_wire = false; + for (auto &c : conn.second.chunks) { + if (c.wire != NULL && design->selected_member(module->name, c.wire->name)) + found_rhs_wire = true; + } + if (!found_lhs_wire || !found_rhs_wire) + continue; + + std::string code, left_node, right_node; + code += gen_portbox("", conn.second, false, &left_node); + code += gen_portbox("", conn.first, true, &right_node); + fprintf(f, "%s", code.c_str()); + + if (left_node[0] == 'x' && right_node[0] == 'x') + fprintf(f, "%s:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both];\n", left_node.c_str(), right_node.c_str()); + else if (left_node[0] == 'x') + net_conn_map[right_node].in.insert(left_node); + else if (right_node[0] == 'x') + net_conn_map[left_node].out.insert(right_node); + else { + net_conn_map[right_node].in.insert(stringf("x%d:e", single_idx_count)); + net_conn_map[left_node].out.insert(stringf("x%d:w", single_idx_count)); + fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\"];\n", single_idx_count++); + } + } + + for (auto &it : net_conn_map) + { + if (wires_on_demand.count(it.first) > 0) { + if (it.second.in.size() == 1 && it.second.out.size() == 1) { + fprintf(f, "%s:e -> %s:w;\n", it.second.in.begin()->c_str(), it.second.out.begin()->c_str()); + continue; + } + if (it.second.in.size() == 0 || it.second.out.size() == 0) + fprintf(f, "%s [ shape=diamond, label=\"%s\" ];\n", it.first.c_str(), escape(wires_on_demand[it.first], true)); + else + fprintf(f, "%s [ shape=point ];\n", it.first.c_str()); + } + for (auto &it2 : it.second.in) + fprintf(f, "%s:e -> %s:w;\n", it2.c_str(), it.first.c_str()); + for (auto &it2 : it.second.out) + fprintf(f, "%s:e -> %s:w;\n", it.first.c_str(), it2.c_str()); + } + + fprintf(f, "};\n"); + } + + ShowWorker(FILE *f, RTLIL::Design *design) : f(f), design(design) + { + ct.setup_internals(); + ct.setup_internals_mem(); + ct.setup_stdcells(); + ct.setup_stdcells_mem(); + + design->optimize(); + page_counter = 0; + for (auto &mod_it : design->modules) + { + module = mod_it.second; + if (!design->selected_module(module->name)) + continue; + if (design->selected_whole_module(module->name)) + log("Dumping module %s to page %d.\n", module->name.c_str(), ++page_counter); + else + log("Dumping selected parts of module %s to page %d.\n", module->name.c_str(), ++page_counter); + handle_module(); + } + } +}; + +struct ShowPass : public Pass { + ShowPass() : Pass("show") { } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Generating Graphviz representation of design.\n"); + + std::string viewer_exe; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-viewer" && argidx+1 < args.size()) { + viewer_exe = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + log("Writing dot description to `yosys-show.dot'.\n"); + FILE *f = fopen("yosys-show.dot", "w"); + if (f == NULL) + log_cmd_error("Can't open dot file `yosys-show.dot' for writing.\n"); + ShowWorker worker(f, design); + fclose(f); + + if (worker.page_counter == 0) + log_cmd_error("Nothing there to show.\n"); + + std::string cmd = stringf("dot -Tps -o yosys-show.ps yosys-show.dot"); + log("Exec: %s\n", cmd.c_str()); + if (system(cmd.c_str()) != 0) + log_cmd_error("Shell command failed!\n"); + + if (!viewer_exe.empty()) { + cmd = stringf("%s yosys-show.ps &", viewer_exe.c_str()); + log("Exec: %s\n", cmd.c_str()); + if (system(cmd.c_str()) != 0) + log_cmd_error("Shell command failed!\n"); + } + } +} ShowPass; + -- cgit v1.2.3