diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 673ad945a..de767b96a 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -19,14 +19,14 @@ #include -#include "kernel/yosys.h" #include "kernel/celltypes.h" -#include "passes/techmap/libparse.h" #include "kernel/cost.h" #include "kernel/gzip.h" -#include "libs/json11/json11.hpp" -#include "libs/nlohmann_json/json.hpp" #include "kernel/log_help.h" +#include "kernel/yosys.h" +#include "libs/json11/json11.hpp" +#include "passes/techmap/libparse.h" +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -34,99 +34,162 @@ PRIVATE_NAMESPACE_BEGIN struct cell_area_t { double area; bool is_sequential; + vector single_parameter_area; + vector> double_parameter_area; + vector parameter_names; }; -struct statdata_t -{ - #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ - X(num_ports) X(num_port_bits) X(num_memories) X(num_memory_bits) X(num_cells) \ - X(num_processes) +struct statdata_t { +#define STAT_INT_MEMBERS \ + X(num_wires) \ + X(num_wire_bits) \ + X(num_pub_wires) X(num_pub_wire_bits) X(num_ports) X(num_port_bits) X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) - #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) X(sequential_area) +#define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) X(sequential_area) - #define X(_name) unsigned int _name; +#define X(_name) unsigned int _name; STAT_INT_MEMBERS - #undef X +#undef X +#define X(_name) unsigned int local_##_name; + STAT_INT_MEMBERS +#undef X double area = 0; double sequential_area = 0; + double local_area = 0; + double local_sequential_area = 0; + double submodule_area = 0; + int num_submodules = 0; + std::map num_submodules_by_type; + std::map submodules_area_by_type; + + std::map local_num_cells_by_type; + std::map local_area_cells_by_type; + std::map local_seq_area_cells_by_type; string tech; std::map num_cells_by_type; + std::map area_cells_by_type; + std::map seq_area_cells_by_type; std::set unknown_cell_area; statdata_t operator+(const statdata_t &other) const { statdata_t sum = other; - #define X(_name) sum._name += _name; +#define X(_name) sum._name += _name; STAT_NUMERIC_MEMBERS - #undef X +#undef X for (auto &it : num_cells_by_type) sum.num_cells_by_type[it.first] += it.second; return sum; } - statdata_t operator*(unsigned int other) const { statdata_t sum = *this; - #define X(_name) sum._name *= other; +#define X(_name) sum._name *= other; STAT_NUMERIC_MEMBERS - #undef X +#undef X for (auto &it : sum.num_cells_by_type) it.second *= other; return sum; } + statdata_t add(const statdata_t &other) + { +#define X(_name) _name += other._name; + STAT_NUMERIC_MEMBERS +#undef X + for (auto &it : other.num_cells_by_type) { + if (num_cells_by_type.count(it.first)) + num_cells_by_type[it.first] += it.second; + else + num_cells_by_type[it.first] = it.second; + } + for (auto &it : other.submodules_area_by_type) { + if (submodules_area_by_type.count(it.first)) + submodules_area_by_type[it.first] += it.second; + else + submodules_area_by_type[it.first] = it.second; + } + for (auto &it : other.area_cells_by_type) { + if (area_cells_by_type.count(it.first)) + area_cells_by_type[it.first] += it.second; + else + area_cells_by_type[it.first] = it.second; + } + for (auto &it : other.seq_area_cells_by_type) { + if (seq_area_cells_by_type.count(it.first)) + seq_area_cells_by_type[it.first] += it.second; + else + seq_area_cells_by_type[it.first] = it.second; + } + unknown_cell_area.insert(other.unknown_cell_area.begin(), other.unknown_cell_area.end()); + return *this; + } statdata_t() { - #define X(_name) _name = 0; +#define X(_name) _name = 0; STAT_NUMERIC_MEMBERS - #undef X +#undef X } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict &cell_area, string techname) + statdata_t(cell_area_t &cell_data, string techname) + { + tech = techname; + area = cell_data.area; + if (cell_data.is_sequential) { + sequential_area = cell_data.area; + } + } + + statdata_t(const RTLIL::Design *design, const RTLIL::Module *mod, bool width_mode, dict &cell_area, string techname) { tech = techname; - #define X(_name) _name = 0; +#define X(_name) _name = 0; STAT_NUMERIC_MEMBERS - #undef X +#undef X +#define X(_name) local_##_name = 0; + STAT_NUMERIC_MEMBERS +#undef X + // additional_cell_area - for (auto wire : mod->selected_wires()) - { + for (auto wire : mod->selected_wires()) { if (wire->port_input || wire->port_output) { num_ports++; + local_num_ports++; num_port_bits += wire->width; + local_num_port_bits += wire->width; } if (wire->name.isPublic()) { num_pub_wires++; + local_num_pub_wires++; num_pub_wire_bits += wire->width; + local_num_pub_wire_bits += wire->width; } num_wires++; + local_num_wires++; num_wire_bits += wire->width; + local_num_wire_bits += wire->width; } for (auto &it : mod->memories) { if (!design->selected(mod, it.second)) continue; num_memories++; + local_num_memories++; num_memory_bits += it.second->width * it.second->size; + local_num_memory_bits += it.second->width * it.second->size; } - - for (auto cell : mod->selected_cells()) - { + for (auto cell : mod->selected_cells()) { RTLIL::IdString cell_type = cell->type; - - if (width_mode) - { - if (cell_type.in(ID($not), ID($pos), ID($neg), - ID($logic_not), ID($logic_and), ID($logic_or), - ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), - ID($lut), ID($and), ID($or), ID($xor), ID($xnor), - ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx), - ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt), - ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow), ID($alu))) { + if (width_mode) { + if (cell_type.in(ID($not), ID($pos), ID($neg), ID($logic_not), ID($logic_and), ID($logic_or), ID($reduce_and), + ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($lut), ID($and), ID($or), + ID($xor), ID($xnor), ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx), ID($lt), + ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt), ID($add), ID($sub), ID($mul), + ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow), ID($alu))) { int width_a = cell->hasPort(ID::A) ? GetSize(cell->getPort(ID::A)) : 0; int width_b = cell->hasPort(ID::B) ? GetSize(cell->getPort(ID::B)) : 0; int width_y = cell->hasPort(ID::Y) ? GetSize(cell->getPort(ID::Y)) : 0; @@ -146,27 +209,136 @@ struct statdata_t } if (!cell_area.empty()) { + // check if cell_area provides a area calculator + if (cell_area.count(cell->type)) { + cell_area_t cell_data = cell_area.at(cell->type); + if (cell_data.single_parameter_area.size() > 0) { + // assume that we just take the max of the A,B,Y ports + + int width_a = cell->hasPort(ID::A) ? GetSize(cell->getPort(ID::A)) : 0; + int width_b = cell->hasPort(ID::B) ? GetSize(cell->getPort(ID::B)) : 0; + int width_y = cell->hasPort(ID::Y) ? GetSize(cell->getPort(ID::Y)) : 0; + int width_q = cell->hasPort(ID::Q) ? GetSize(cell->getPort(ID::Q)) : 0; + int max_width = max({width_a, width_b, width_y, width_q}); + if (!cell_area.count(cell_type)) { + cell_area[cell_type] = cell_data; + } + if (cell_data.single_parameter_area.size() > max_width - 1u) { + cell_area.at(cell_type).area = cell_data.single_parameter_area.at(max_width - 1); + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + + } else { + log_warning("too small single_parameter_area %s width: %d size: %d\n", cell_type, max_width, + (int)cell_data.single_parameter_area.size()); + cell_area.at(cell_type).area = cell_data.single_parameter_area.back(); + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + } + } + vector widths; + if (cell_data.parameter_names.size() > 0) { + for (auto &it : cell_data.parameter_names) { + RTLIL::IdString port_name; + if (it == "A") { + port_name = ID::A; + } else if (it == "B") { + port_name = ID::B; + } else if (it == "Y") { + port_name = ID::Y; + } else if (it == "Q") { + port_name = ID::Q; + } else if (it == "S") { + port_name = ID::S; + } else { + port_name = ID(it); + } + if (cell->hasPort(port_name)) { + int width = GetSize(cell->getPort(port_name)); + widths.push_back(width); + } else { + widths.push_back(0); + } + } + } + + if (cell_data.double_parameter_area.size() > 0) { + if (!cell_area.count(cell_type)) { + cell_area[cell_type] = cell_data; + } + if (widths.size() == 2) { + unsigned int width_a = widths.at(0); + unsigned int width_b = widths.at(1); + if (width_a > 0 && width_b > 0) { + if (cell_data.double_parameter_area.size() > width_a - 1 && + cell_data.double_parameter_area.at(width_a - 1).size() > width_b - 1) { + cell_area.at(cell_type).area = + cell_data.double_parameter_area.at(width_a - 1).at(width_b - 1); + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + } else { + log_warning("too small double_parameter_area %s, width_a: %d, width_b: %d, size_a: %d, size_b: %d\n", cell_type, + width_a, width_b, (int)cell_data.double_parameter_area.size(), + (int)cell_data.double_parameter_area.at(width_a - 1).size()); + cell_area.at(cell_type).area = cell_data.double_parameter_area.back().back(); + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + } + } else { + cell_area.at(cell_type).area = cell_data.area; + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + } + } else { + log_error("double_parameter_area for %s has %d parameters, but only 2 are expected.\n", cell_type, + (int)cell_data.double_parameter_area.size()); + } + } + } + if (cell_area.count(cell_type)) { cell_area_t cell_data = cell_area.at(cell_type); if (cell_data.is_sequential) { sequential_area += cell_data.area; + local_sequential_area += cell_data.area; } area += cell_data.area; - } - else { - unknown_cell_area.insert(cell_type); - } - } + num_cells++; + num_cells_by_type[cell_type]++; + area_cells_by_type[cell_type] += cell_data.area; + seq_area_cells_by_type[cell_type] += cell_data.is_sequential ? cell_data.area : 0; + local_area_cells_by_type[cell_type] += cell_data.area; + local_seq_area_cells_by_type[cell_type] += cell_data.is_sequential ? cell_data.area : 0; + local_area += cell_data.area; + local_num_cells++; + local_num_cells_by_type[cell_type]++; - num_cells++; - num_cells_by_type[cell_type]++; + } else { + unknown_cell_area.insert(cell_type); + num_cells++; + num_cells_by_type[cell_type]++; + local_num_cells++; + local_num_cells_by_type[cell_type]++; + area_cells_by_type[cell_type] = 0; + seq_area_cells_by_type[cell_type] = 0; + local_area_cells_by_type[cell_type] = 0; + local_seq_area_cells_by_type[cell_type] = 0; + } + } else { + num_cells++; + num_cells_by_type[cell_type]++; + area_cells_by_type[cell_type] = 0; + seq_area_cells_by_type[cell_type] = 0; + local_num_cells++; + local_num_cells_by_type[cell_type]++; + local_area_cells_by_type[cell_type] = 0; + local_seq_area_cells_by_type[cell_type] = 0; + } } for (auto &it : mod->processes) { if (!design->selected(mod, it.second)) continue; num_processes++; + local_num_processes++; } + RTLIL::IdString cell_name = mod->name; + auto s = cell_name.str(); } unsigned int estimate_xilinx_lc() @@ -239,22 +411,132 @@ struct statdata_t return tran_cnt; } - void log_data(RTLIL::IdString mod_name, bool top_mod) + /* + format a floating point value to a of 8 characters, with at most 7 digits or scientific notation + uses - to mark zero or very small values + */ + std::string f_val(double value) { - log(" Number of wires: %6u\n", num_wires); - log(" Number of wire bits: %6u\n", num_wire_bits); - log(" Number of public wires: %6u\n", num_pub_wires); - log(" Number of public wire bits: %6u\n", num_pub_wire_bits); - log(" Number of ports: %6u\n", num_ports); - log(" Number of port bits: %6u\n", num_port_bits); - log(" Number of memories: %6u\n", num_memories); - log(" Number of memory bits: %6u\n", num_memory_bits); - log(" Number of processes: %6u\n", num_processes); - log(" Number of cells: %6u\n", num_cells); - for (auto &it : num_cells_by_type) - if (it.second) - log(" %-26s %6u\n", it.first.unescape(), it.second); + if (std::abs(value) < 1e-12) + return " -"; + char buf[16]; + + int len = snprintf(buf, sizeof(buf), "%.3f", value); + + while (len > 0 && buf[len - 1] == '0') + --len; + if (len > 0 && buf[len - 1] == '.') + --len; + buf[len] = '\0'; + + if (len <= 7) { + return std::string(8 - len, ' ') + std::string(buf); + } + + // use scientific notation, this should always fit in 8 characters + snprintf(buf, sizeof(buf), "%8.3G", value); + + return std::string(buf); + } + + void print_log_line(const std::string &name, unsigned int count_local, double area_local, unsigned int count_global, double area_global, + int spacer = 0, bool print_area = true, bool print_hierarchical = true, bool print_global_only = false) + { + const std::string indent(2 * spacer, ' '); + + std::string count_local_str = f_val(static_cast(count_local)); + std::string count_global_str = f_val(static_cast(count_global)); + std::string area_local_str = f_val(area_local); + std::string area_global_str = f_val(area_global); + + if (print_area) { + if (print_hierarchical) { + log(" %s %s %s %s %s%s\n", count_global_str, area_global_str, count_local_str, + area_local_str.c_str(), indent.c_str(), name.c_str()); + } else if (print_global_only) { + log(" %s %s %s%s\n", count_global_str, area_global_str, indent, name); + } else { + if (count_local > 0) + log(" %s %s %s%s\n", count_local_str, area_local_str, indent, name); + } + } else { + if (print_hierarchical) { + log(" %s %s %s%s\n", count_global_str, count_local_str, indent, name); + } else if (print_global_only) { + log(" %s %s%s\n", count_global_str, indent, name); + } else { + if (count_local > 0) + log(" %s %s%s\n", count_local_str, indent, name); + } + } + } + + void print_log_header(bool print_area = true, bool print_hierarchical = true, bool print_global_only = false) + { + if (print_area) { + if (print_hierarchical) { + log(" %8s-%8s-%8s-%8s-%s\n", "+", "--------", "--------", "--------", "Count including submodules."); + log(" %8s %8s-%8s-%8s-%s\n", "|", "+", "--------", "--------", "Area including submodules."); + log(" %8s %8s %8s-%8s-%s\n", "|", "|", "+", "--------", "Local count, excluding submodules."); + log(" %8s %8s %8s %8s-%s\n", "|", "|", "|", "+", "Local area, excluding submodules."); + log(" %8s %8s %8s %8s \n", "|", "|", "|", "|"); + } else if (print_global_only) { + log(" %8s-%8s-%s\n", "+", "--------", "Count including submodules."); + log(" %8s %8s-%s\n", "|", "+", "Area including submodules."); + log(" %8s %8s \n", "|", "|"); + } else { + log(" %8s-%8s-%s\n", "+", "--------", "Local Count, excluding submodules."); + log(" %8s %8s-%s\n", "|", "+", "Local Area, excluding submodules."); + log(" %8s %8s \n", "|", "|"); + } + } else { + if (print_hierarchical) { + log(" %8s-%8s-%8s-%s\n", "+", "--------", "--------", "Count including submodules."); + log(" %8s %8s-%8s-%s\n", "|", "+", "--------", "Local count, excluding submodules."); + log(" %8s %8s \n", "|", "|"); + } else if (print_global_only) { + log(" %8s-%8s-%s\n", "+", "--------", "Count including submodules."); + log(" %8s \n", "|"); + } else { + log(" %8s-%8s-%s\n", "+", "--------", "Local Count, excluding submodules."); + log(" %8s \n", "|"); + } + } + } + + void log_data(RTLIL::IdString mod_name, bool top_mod, bool print_area = true, bool print_hierarchical = true, bool print_global_only = false) + { + + print_log_header(print_area, print_hierarchical, print_global_only); + + print_log_line("wires", local_num_wires, 0, num_wires, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("wire bits", local_num_wire_bits, 0, num_wire_bits, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("public wires", local_num_pub_wires, 0, num_pub_wires, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("public wire bits", local_num_pub_wire_bits, 0, num_pub_wire_bits, 0, 0, print_area, print_hierarchical, + print_global_only); + print_log_line("ports", local_num_ports, 0, num_ports, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("port bits", local_num_port_bits, 0, num_port_bits, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("memories", local_num_memories, 0, num_memories, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("memory bits", local_num_memory_bits, 0, num_memory_bits, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("processes", local_num_processes, 0, num_processes, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("cells", local_num_cells, local_area, num_cells, area, 0, print_area, print_hierarchical, print_global_only); + for (auto &it : num_cells_by_type) + if (it.second) { + auto name = string(it.first.unescape()); + print_log_line(name, local_num_cells_by_type.count(it.first) ? local_num_cells_by_type.at(it.first) : 0, + local_area_cells_by_type.count(it.first) ? local_area_cells_by_type.at(it.first) : 0, it.second, + area_cells_by_type.at(it.first), 1, print_area, print_hierarchical, print_global_only); + } + if (num_submodules > 0) { + print_log_line("submodules", num_submodules, 0, num_submodules, submodule_area, 0, print_area, print_hierarchical, + print_global_only); + for (auto &it : num_submodules_by_type) + if (it.second) + print_log_line(string(it.first.unescape()), it.second, 0, it.second, + submodules_area_by_type.count(it.first) ? submodules_area_by_type.at(it.first) : 0, 1, + print_area, print_hierarchical, print_global_only); + } if (!unknown_cell_area.empty()) { log("\n"); for (auto cell_type : unknown_cell_area) @@ -263,18 +545,28 @@ struct statdata_t if (area != 0) { log("\n"); - log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area); - log(" of which used for sequential elements: %f (%.2f%%)\n", sequential_area, 100.0*sequential_area/area); + if (print_hierarchical || print_global_only) { + log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name, area); + log(" of which used for sequential elements: %f (%.2f%%)\n", sequential_area, 100.0 * sequential_area / area); + } else { + double local_area = 0; + for (auto &it : local_area_cells_by_type) + local_area += it.second; + double local_sequential_area = 0; + for (auto &it : local_seq_area_cells_by_type) + local_sequential_area += it.second; + log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name, local_area); + log(" of which used for sequential elements: %f (%.2f%%)\n", local_sequential_area, + 100.0 * local_sequential_area / local_area); + } } - if (tech == "xilinx" || tech == "analogdevices") - { + if (tech == "xilinx" || tech == "analogdevices") { log("\n"); log(" Estimated number of LCs: %10u\n", estimate_xilinx_lc()); } - if (tech == "cmos") - { + if (tech == "cmos") { bool tran_cnt_exact = true; unsigned int tran_cnt = cmos_transistor_count(&tran_cnt_exact); @@ -283,132 +575,315 @@ struct statdata_t } } - void log_data_json(const char *mod_name, bool first_module) + string json_line(unsigned int count_local, double area_local, unsigned int count_global, double area_global) + { + + return stringf("{ \"count\": \"%u\", \"area\": \"%f\", \"local_count\": \"%u\", \"local_area\": \"%f\" }", count_global, area_global, + count_local, area_local); + } + + void log_data_json(const char *mod_name, bool first_module, bool hierarchical = false, bool global_only = false) { if (!first_module) log(",\n"); - log(" %s: {\n", json11::Json(mod_name).dump().c_str()); - log(" \"num_wires\": %u,\n", num_wires); - log(" \"num_wire_bits\": %u,\n", num_wire_bits); - log(" \"num_pub_wires\": %u,\n", num_pub_wires); - log(" \"num_pub_wire_bits\": %u,\n", num_pub_wire_bits); - log(" \"num_ports\": %u,\n", num_ports); - log(" \"num_port_bits\": %u,\n", num_port_bits); - log(" \"num_memories\": %u,\n", num_memories); - log(" \"num_memory_bits\": %u,\n", num_memory_bits); - log(" \"num_processes\": %u,\n", num_processes); - log(" \"num_cells\": %u,\n", num_cells); - log(" \"area\": %f,\n", area); - log(" \"num_cells_by_type\": {\n"); - bool first_line = true; - for (auto &it : num_cells_by_type) - if (it.second) { - if (!first_line) - log(",\n"); - log(" %s: %u", json11::Json(it.first.unescape()).dump().c_str(), it.second); - first_line = false; + if (hierarchical) { + log(" %s: {\n", json11::Json(mod_name).dump()); + log(" \"num_wires\": %s,\n", json_line(local_num_wires, 0, num_wires, 0)); + log(" \"num_wire_bits\": %s,\n", json_line(local_num_wire_bits, 0, num_wire_bits, 0)); + log(" \"num_pub_wires\": %s,\n", json_line(local_num_pub_wires, 0, num_pub_wires, 0)); + log(" \"num_pub_wire_bits\": %s,\n", json_line(local_num_pub_wire_bits, 0, num_pub_wire_bits, 0)); + log(" \"num_ports\": %s,\n", json_line(local_num_ports, 0, num_ports, 0)); + log(" \"num_port_bits\": %s,\n", json_line(local_num_port_bits, 0, num_port_bits, 0)); + log(" \"num_memories\": %s,\n", json_line(local_num_memories, 0, num_memories, 0)); + log(" \"num_memory_bits\": %s,\n", json_line(local_num_memory_bits, 0, num_memory_bits, 0)); + log(" \"num_processes\": %s,\n", json_line(local_num_processes, 0, num_processes, 0)); + log(" \"num_cells\": %s,\n", json_line(local_num_cells, local_area, num_cells, area)); + log(" \"num_submodules\": %s,\n", json_line(0, 0, num_submodules, submodule_area)); + log(" \"sequential_area\": %s,\n", json_line(0, local_sequential_area, 0, sequential_area)); + + log(" \"num_cells_by_type\": {\n"); + bool first_line = true; + for (auto &it : num_cells_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %s", json11::Json(it.first.unescape()).dump(), + json_line(local_num_cells_by_type.count(it.first) ? local_num_cells_by_type.at(it.first) : 0, + local_area_cells_by_type.count(it.first) ? local_area_cells_by_type.at(it.first) : 0, it.second, + area_cells_by_type.at(it.first)) + .c_str()); + first_line = false; + } + log("\n },\n"); + log(" \"num_submodules_by_type\": {\n"); + first_line = true; + for (auto &it : num_submodules_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %s", json11::Json(it.first.unescape()).dump(), + json_line(0, 0, it.second, + submodules_area_by_type.count(it.first) ? submodules_area_by_type.at(it.first) : 0) + .c_str()); + first_line = false; + } + log("\n }\n"); + if (tech == "xilinx" || tech == "analogdevices") { + log(" \"estimated_num_lc\": %u,\n", estimate_xilinx_lc()); } - log("\n"); - log(" }"); - if (tech == "xilinx" || tech == "analogdevices") - { - log(",\n"); - log(" \"estimated_num_lc\": %u", estimate_xilinx_lc()); + if (tech == "cmos") { + bool tran_cnt_exact = true; + unsigned int tran_cnt = cmos_transistor_count(&tran_cnt_exact); + log(" \"estimated_num_transistors\": \"%u%s\"\n", tran_cnt, tran_cnt_exact ? "" : "+"); + } + log(" }"); + + } else { + if (global_only) { + log(" %s: {\n", json11::Json(mod_name).dump()); + log(" \"num_wires\": %u,\n", num_wires); + log(" \"num_wire_bits\": %u,\n", num_wire_bits); + log(" \"num_pub_wires\": %u,\n", num_pub_wires); + log(" \"num_pub_wire_bits\": %u,\n", num_pub_wire_bits); + log(" \"num_ports\": %u,\n", num_ports); + log(" \"num_port_bits\": %u,\n", num_port_bits); + log(" \"num_memories\": %u,\n", num_memories); + log(" \"num_memory_bits\": %u,\n", num_memory_bits); + log(" \"num_processes\": %u,\n", num_processes); + log(" \"num_cells\": %u,\n", num_cells); + log(" \"num_submodules\": %u,\n", num_submodules); + if (area != 0) { + log(" \"area\": %f,\n", area); + log(" \"sequential_area\": %f,\n", sequential_area); + } + log(" \"num_cells_by_type\": {\n"); + bool first_line = true; + for (auto &it : num_cells_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %u", json11::Json(it.first.unescape()).dump(), it.second); + first_line = false; + } + for (auto &it : num_submodules_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %u", json11::Json(it.first.unescape()).dump(), it.second); + first_line = false; + } + log("\n"); + log(" }"); + } else { + log(" %s: {\n", json11::Json(mod_name).dump()); + log(" \"num_wires\": %u,\n", local_num_wires); + log(" \"num_wire_bits\": %u,\n", local_num_wire_bits); + log(" \"num_pub_wires\": %u,\n", local_num_pub_wires); + log(" \"num_pub_wire_bits\": %u,\n", local_num_pub_wire_bits); + log(" \"num_ports\": %u,\n", local_num_ports); + log(" \"num_port_bits\": %u,\n", local_num_port_bits); + log(" \"num_memories\": %u,\n", local_num_memories); + log(" \"num_memory_bits\": %u,\n", local_num_memory_bits); + log(" \"num_processes\": %u,\n", local_num_processes); + log(" \"num_cells\": %u,\n", local_num_cells); + log(" \"num_submodules\": %u,\n", num_submodules); + if (area != 0) { + log(" \"area\": %f,\n", area); + log(" \"sequential_area\": %f,\n", sequential_area); + } + log(" \"num_cells_by_type\": {\n"); + bool first_line = true; + for (auto &it : local_num_cells_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %u", json11::Json(it.first.unescape()).dump(), it.second); + first_line = false; + } + for (auto &it : num_submodules_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %u", json11::Json(it.first.unescape()).dump(), it.second); + first_line = false; + } + log("\n"); + log(" }"); + } + if (tech == "xilinx" || tech == "analogdevices") { + log(",\n"); + log(" \"estimated_num_lc\": %u", estimate_xilinx_lc()); + } + if (tech == "cmos") { + bool tran_cnt_exact = true; + unsigned int tran_cnt = cmos_transistor_count(&tran_cnt_exact); + log(",\n"); + log(" \"estimated_num_transistors\": \"%u%s\"", tran_cnt, tran_cnt_exact ? "" : "+"); + } + log("\n"); + log(" }"); } - if (tech == "cmos") - { - bool tran_cnt_exact = true; - unsigned int tran_cnt = cmos_transistor_count(&tran_cnt_exact); - log(",\n"); - log(" \"estimated_num_transistors\": \"%u%s\"", tran_cnt, tran_cnt_exact ? "" : "+"); - } - log("\n"); - log(" }"); } }; -statdata_t hierarchy_worker(std::map &mod_stat, RTLIL::IdString mod, int level, bool quiet = false) +statdata_t hierarchy_worker(std::map &mod_stat, RTLIL::IdString mod, int level, bool quiet = false, bool has_area = true, + bool hierarchy_mode = true) { statdata_t mod_data = mod_stat.at(mod); - std::map num_cells_by_type; - num_cells_by_type.swap(mod_data.num_cells_by_type); - for (auto &it : num_cells_by_type) + for (auto &it : mod_data.num_submodules_by_type) { if (mod_stat.count(it.first) > 0) { if (!quiet) - log(" %*s%-*s %6u\n", 2*level, "", 26-2*level, it.first.unescape(), it.second); - mod_data = mod_data + hierarchy_worker(mod_stat, it.first, level+1, quiet) * it.second; - mod_data.num_cells -= it.second; - } else { - mod_data.num_cells_by_type[it.first] += it.second; + mod_data.print_log_line(string(it.first.unescape()), mod_stat.at(it.first).local_num_cells, + mod_stat.at(it.first).local_area, mod_stat.at(it.first).num_cells, mod_stat.at(it.first).area, + level, has_area, hierarchy_mode); + hierarchy_worker(mod_stat, it.first, level + 1, quiet, has_area, hierarchy_mode) * it.second; } + } return mod_data; } -void read_liberty_cellarea(dict &cell_area, string liberty_file) +statdata_t hierarchy_builder(const RTLIL::Design *design, const RTLIL::Module *top_mod, std::map &mod_stat, + bool width_mode, dict &cell_area, string techname) { - std::istream* f = uncompressed(liberty_file.c_str()); - yosys_input_files.insert(liberty_file); - LibertyParser libparser(*f, liberty_file); - delete f; - - for (auto cell : libparser.ast->children) - { - if (cell->id != "cell" || cell->args.size() != 1) - continue; - - const LibertyAst *ar = cell->find("area"); - bool is_flip_flop = cell->find("ff") != nullptr; - if (ar != nullptr && !ar->value.empty()) - cell_area["\\" + cell->args[0]] = {/*area=*/atof(ar->value.c_str()), is_flip_flop}; - } -} - -void read_libjson_cellarea(dict &cell_area, string liberty_file) -{ - std::istream *f = uncompressed(liberty_file.c_str()); - yosys_input_files.insert(liberty_file); - if (f->fail()) - log_cmd_error("Can't open liberty json file `%s': %s\n", liberty_file.c_str(), strerror(errno)); - nlohmann::json data = nlohmann::json::parse(*f); - delete f; - - nlohmann::json library = data["library"]; - if (library.contains("groups")) { - nlohmann::json groups = library["groups"]; - for (nlohmann::json::iterator it = groups.begin(); it != groups.end(); ++it) { - nlohmann::json group = it.value(); - if (group.contains("cell")) { - nlohmann::json cell = group["cell"]; - if (cell.contains("names")) { - nlohmann::json names = cell["names"]; - names = names[0]; - if (cell.contains("area")) { - nlohmann::json area = cell["area"]; - bool ff = false; - nlohmann::json cell_groups = cell["groups"]; - for (nlohmann::json::iterator it = cell_groups.begin(); it != cell_groups.end(); ++it) { - nlohmann::json cell_group = *it; - if (cell_group.contains("ff")) { - ff = true; - break; - } - } - std::string name_s = names.template get(); - float area_f = area.template get(); - std::cout << name_s << " : " <top_module(); + statdata_t mod_data(design, top_mod, width_mode, cell_area, techname); + for (auto cell : top_mod->selected_cells()) { + if (cell_area.count(cell->type) == 0) { + if (design->has(cell->type)) { + if (!(design->module(cell->type)->attributes.count(ID::blackbox))) { + // deal with modules + mod_data.add( + hierarchy_builder(design, design->module(cell->type), mod_stat, width_mode, cell_area, techname)); + mod_data.num_submodules_by_type[cell->type]++; + mod_data.submodules_area_by_type[cell->type] += mod_stat.at(cell->type).area; + mod_data.submodule_area += mod_stat.at(cell->type).area; + mod_data.num_submodules++; + mod_data.unknown_cell_area.erase(cell->type); + mod_data.num_cells -= + (mod_data.num_cells_by_type.count(cell->type) != 0) ? mod_data.num_cells_by_type.at(cell->type) : 0; + mod_data.num_cells_by_type.erase(cell->type); + mod_data.local_num_cells -= (mod_data.local_num_cells_by_type.count(cell->type) != 0) + ? mod_data.local_num_cells_by_type.at(cell->type) + : 0; + mod_data.local_num_cells_by_type.erase(cell->type); + mod_data.local_area_cells_by_type.erase(cell->type); + } else { + // deal with blackbox cells + if (design->module(cell->type)->attributes.count(ID::area) && + design->module(cell->type)->attributes.at(ID::area).size() == 0) { + mod_data.num_submodules_by_type[cell->type]++; + mod_data.num_submodules++; + mod_data.submodules_area_by_type[cell->type] += + double(design->module(cell->type)->attributes.at(ID::area).as_int()); + mod_data.area += double(design->module(cell->type)->attributes.at(ID::area).as_int()); + mod_data.unknown_cell_area.erase(cell->type); + mod_data.num_cells -= + (mod_data.num_cells_by_type.count(cell->type) != 0) ? mod_data.num_cells_by_type.at(cell->type) : 0; + mod_data.num_cells_by_type.erase(cell->type); + mod_data.local_num_cells -= (mod_data.local_num_cells_by_type.count(cell->type) != 0) + ? mod_data.local_num_cells_by_type.at(cell->type) + : 0; + mod_data.local_num_cells_by_type.erase(cell->type); + mod_data.local_area_cells_by_type.erase(cell->type); } } } } } + mod_stat[top_mod->name] = mod_data; + return mod_data; +} + +void read_liberty_cellarea(dict &cell_area, string liberty_file) +{ + std::istream *f = uncompressed(liberty_file.c_str()); + yosys_input_files.insert(liberty_file); + LibertyParser libparser(*f, liberty_file); + delete f; + + for (auto cell : libparser.ast->children) { + if (cell->id != "cell" || cell->args.size() != 1) + continue; + + const LibertyAst *ar = cell->find("area"); + bool is_flip_flop = cell->find("ff") != nullptr; + vector single_parameter_area; + vector> double_parameter_area; + vector port_names; + const LibertyAst *sar = cell->find("single_area_parameterised"); + if (sar != nullptr) { + for (const auto &s : sar->args) { + if (s.empty()) { + //catches trailing commas + continue; + } + try { + double value = std::stod(s); + single_parameter_area.push_back(value); + } catch (const std::exception &e) { + log_error("Failed to parse single parameter area value '%s': %s\n", s, e.what()); + } + } + if (single_parameter_area.size() == 0) + log_error("single parameter area has size 0: %s\n", sar->args[single_parameter_area.size() - 1]); + // check if it is a double parameterised area + } + const LibertyAst *dar = cell->find("double_area_parameterised"); + if (dar != nullptr) { + for (const auto &s : dar->args) { + + + vector sub_array; + std::string::size_type start = 0; + std::string::size_type end = s.find_first_of(",", start); + while (end != std::string::npos) { + sub_array.push_back(s.substr(start, end - start)); + start = end + 1; + end = s.find_first_of(",", start); + } + sub_array.push_back(s.substr(start, end)); + + vector cast_sub_array; + for (const auto &s : sub_array) { + double value = 0; + if (s.empty()) { + //catches trailing commas + continue; + } + try { + value = std::stod(s); + cast_sub_array.push_back(value); + } catch (const std::exception &e) { + log_error("Failed to parse double parameter area value for '%s': %s\n", s, e.what()); + } + } + double_parameter_area.push_back(cast_sub_array); + if (cast_sub_array.size() == 0) + log_error("double paramter array has size 0: %s\n", s); + } + } + const LibertyAst *par = cell->find("port_names"); + if (par != nullptr) { + for (const auto &s : par->args) { + port_names.push_back(s); + } + } + + if (ar != nullptr && !ar->value.empty()) { + string prefix = cell->args[0].substr(0, 1) == "$" ? "" : "\\"; + cell_area[prefix + cell->args[0]] = {atof(ar->value.c_str()), is_flip_flop, single_parameter_area, double_parameter_area, + port_names}; + } + } } struct StatPass : public Pass { - StatPass() : Pass("stat", "print some statistics") { } - bool formatted_help() override { + StatPass() : Pass("stat", "print some statistics") {} + bool formatted_help() override + { auto *help = PrettyHelp::get_current(); help->set_group("passes/status"); return false; @@ -421,6 +896,7 @@ struct StatPass : public Pass { log("\n"); log("Print some statistics (number of objects) on the selected portion of the\n"); log("design.\n"); + log("Extracts the area of cells from a liberty file, if provided.\n"); log("\n"); log(" -top \n"); log(" print design hierarchy with this module as top. if the design is fully\n"); @@ -430,9 +906,6 @@ struct StatPass : public Pass { log(" -liberty \n"); log(" use cell area information from the provided liberty file\n"); log("\n"); - log(" -libjson \n"); - log(" use cell area information from the provided JSON liberty file\n"); - log("\n"); log(" -tech \n"); log(" print area estimate for the specified technology. Currently supported\n"); log(" values for : xilinx, analogdevices, cmos\n"); @@ -445,35 +918,32 @@ struct StatPass : public Pass { log(" output the statistics in a machine-readable JSON format.\n"); log(" this is output to the console; use \"tee\" to output to a file.\n"); log("\n"); + log(" -hierarchy\n"); + log(" print hierarchical statistics, i.e. The area and number of cells include submodules.\n"); + log(" this changes the format of the json output.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { - bool width_mode = false, json_mode = false; + bool width_mode = false, json_mode = false, hierarchy_mode = false; RTLIL::Module *top_mod = nullptr; std::map mod_stat; dict cell_area; string techname; size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) - { + for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-width") { width_mode = true; continue; } - if (args[argidx] == "-liberty" && argidx+1 < args.size()) { + if (args[argidx] == "-liberty" && argidx + 1 < args.size()) { string liberty_file = args[++argidx]; rewrite_filename(liberty_file); read_liberty_cellarea(cell_area, liberty_file); continue; } - if (args[argidx] == "-libjson" && argidx+1 < args.size()) { - string liberty_file = args[++argidx]; - rewrite_filename(liberty_file); - read_libjson_cellarea(cell_area, liberty_file); - continue; - } - if (args[argidx] == "-tech" && argidx+1 < args.size()) { + if (args[argidx] == "-tech" && argidx + 1 < args.size()) { techname = args[++argidx]; continue; } @@ -487,11 +957,15 @@ struct StatPass : public Pass { json_mode = true; continue; } + if (args[argidx] == "-hierarchy") { + hierarchy_mode = true; + continue; + } break; } extra_args(args, argidx, design); - if(!json_mode) + if (!json_mode) log_header(design, "Printing statistics.\n"); if (techname != "" && techname != "xilinx" && techname != "analogdevices" && techname != "cmos" && !json_mode) @@ -506,24 +980,38 @@ struct StatPass : public Pass { log(" \"modules\": {\n"); } + if (top_mod != nullptr) { + hierarchy_builder(design, top_mod, mod_stat, width_mode, cell_area, techname); + } else { + for (auto mod : design->selected_modules()) { + if (mod_stat.count(mod->name) == 0) { + hierarchy_builder(design, mod, mod_stat, width_mode, cell_area, techname); + } + } + } + bool first_module = true; - for (auto mod : design->selected_modules()) - { + // determine if anything has a area. + bool has_area = false; + for (auto &it : mod_stat) { + if (it.second.area > 0 || it.second.sequential_area > 0) { + has_area = true; + break; + } + } + for (auto mod : design->selected_modules()) { if (!top_mod && design->full_selection()) if (mod->get_bool_attribute(ID::top)) top_mod = mod; - - statdata_t data(design, mod, width_mode, cell_area, techname); - mod_stat[mod->name] = data; - + statdata_t data = mod_stat.at(mod->name); if (json_mode) { - data.log_data_json(mod->name.c_str(), first_module); + data.log_data_json(mod->name.c_str(), first_module, hierarchy_mode); first_module = false; } else { log("\n"); log("=== %s%s ===\n", mod->name.unescape(), mod->is_selected_whole() ? "" : " (partially selected)"); log("\n"); - data.log_data(mod->name, false); + data.log_data(mod->name, false, has_area, hierarchy_mode); } } @@ -532,22 +1020,24 @@ struct StatPass : public Pass { log("%s", top_mod == nullptr ? " }\n" : " },\n"); } - if (top_mod != nullptr) - { + if (top_mod != nullptr) { if (!json_mode && GetSize(mod_stat) > 1) { log("\n"); log("=== design hierarchy ===\n"); log("\n"); - log(" %-28s %6d\n", top_mod->name.unescape(), 1); + mod_stat[top_mod->name].print_log_header(has_area, hierarchy_mode, true); + mod_stat[top_mod->name].print_log_line(top_mod->name.unescape(), mod_stat[top_mod->name].local_num_cells, + mod_stat[top_mod->name].local_area, mod_stat[top_mod->name].num_cells, + mod_stat[top_mod->name].area, 0, has_area, hierarchy_mode, true); } - statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0, /*quiet=*/json_mode); + statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0, /*quiet=*/json_mode, has_area, hierarchy_mode); if (json_mode) - data.log_data_json("design", true); + data.log_data_json("design", true, hierarchy_mode, true); else if (GetSize(mod_stat) > 1) { log("\n"); - data.log_data(top_mod->name, true); + data.log_data(top_mod->name, true, has_area, hierarchy_mode, true); } design->scratchpad_set_int("stat.num_wires", data.num_wires); diff --git a/tests/various/stat.ys b/tests/various/stat.ys index cf084a986..907e7c353 100644 --- a/tests/various/stat.ys +++ b/tests/various/stat.ys @@ -11,6 +11,8 @@ end EOT logger -expect log "Chip area for module '\\top': 9.072000" 1 logger -expect-no-warnings +logger -expect log " 1 9.072 cells" 1 +logger -expect log " 1 9.072 sg13g2_and2_1" 1 stat -liberty ../../tests/liberty/foundry_data/sg13g2_stdcell_typ_1p20V_25C.lib.filtered.gz @@ -70,5 +72,8 @@ end EOT logger -expect log "Chip area for top module '\\top': 112.492800" 1 logger -expect log "of which used for sequential elements: 94.348800" 1 +logger -expect log "2 18.144 cells" 1 +logger -expect log "4 112.493 cells" 1 +logger -expect log "2 94.349 sg13g2_dfrbp_1" 1 logger -expect-no-warnings stat -liberty ../../tests/liberty/foundry_data/sg13g2_stdcell_typ_1p20V_25C.lib.filtered.gz -top \top