From f728927307b959f1e97b11bf9ecedee53220d7a1 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 19 Dec 2023 14:37:27 +0100 Subject: [PATCH 01/31] Add builtin celltype $scopeinfo Only declares the cell interface, doesn't make anything use or understand $scopeinfo yet. --- kernel/celltypes.h | 1 + kernel/rtlil.cc | 9 +++++++++ kernel/satgen.cc | 5 +++++ techlibs/common/simlib.v | 7 +++++++ 4 files changed, 22 insertions(+) diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 77cb3e324..fde6624e1 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -108,6 +108,7 @@ struct CellTypes setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool()); setup_type(ID($original_tag), {ID::A}, {ID::Y}); setup_type(ID($future_ff), {ID::A}, {ID::Y}); + setup_type(ID($scopeinfo), {}, {}); } void setup_internals_eval() diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 125730f29..8781b6a89 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1769,6 +1769,15 @@ namespace { return; } + if (cell->type == ID($scopeinfo)) { + param(ID::TYPE); + check_expected(); + std::string scope_type = cell->getParam(ID::TYPE).decode_string(); + if (scope_type != "module" && scope_type != "struct") + error(__LINE__); + return; + } + if (cell->type == ID($_BUF_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; } if (cell->type == ID($_NOT_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; } if (cell->type == ID($_AND_)) { port(ID::A,1); port(ID::B,1); port(ID::Y,1); check_expected(); return; } diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 3a2fa4735..baaf22d1f 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -1379,6 +1379,11 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } + if (cell->type == ID($scopeinfo)) + { + return true; + } + // Unsupported internal cell types: $pow $fsm $mem* // .. and all sequential cells with asynchronous inputs return false; diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 930d2000b..489281f26 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -2763,3 +2763,10 @@ assign Y = A; endmodule // -------------------------------------------------------- + +(* noblackbox *) +module \$scopeinfo (); + +parameter TYPE = ""; + +endmodule From 8902fc94b6736e74e8da105d6cc8f32bf2f44817 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 19 Dec 2023 16:23:38 +0100 Subject: [PATCH 02/31] Suport $scopeinfo in flatten and opt_clean --- passes/opt/opt_clean.cc | 11 +++- passes/techmap/flatten.cc | 114 ++++++++++++++++++++++++++++++++------ 2 files changed, 105 insertions(+), 20 deletions(-) diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 2b24944cf..b3e43c18c 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -35,10 +35,12 @@ struct keep_cache_t { Design *design; dict cache; + bool purge_mode = false; - void reset(Design *design = nullptr) + void reset(Design *design = nullptr, bool purge_mode = false) { this->design = design; + this->purge_mode = purge_mode; cache.clear(); } @@ -88,6 +90,9 @@ struct keep_cache_t if (cell->has_keep_attr()) return true; + if (!purge_mode && cell->type == ID($scopeinfo)) + return true; + if (cell->module && cell->module->design) return query(cell->module->design->module(cell->type)); @@ -236,6 +241,8 @@ int count_nontrivial_wire_attrs(RTLIL::Wire *w) { int count = w->attributes.size(); count -= w->attributes.count(ID::src); + count -= w->attributes.count(ID::hdlname); + count -= w->attributes.count(ID(scopename)); count -= w->attributes.count(ID::unused_bits); return count; } @@ -661,7 +668,7 @@ struct OptCleanPass : public Pass { } extra_args(args, argidx, design); - keep_cache.reset(design); + keep_cache.reset(design, purge_mode); ct_reg.setup_internals_mem(); ct_reg.setup_internals_anyinit(); diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc index 4ddc4aff1..ea5855a09 100644 --- a/passes/techmap/flatten.cc +++ b/passes/techmap/flatten.cc @@ -46,24 +46,6 @@ IdString map_name(RTLIL::Cell *cell, T *object) return cell->module->uniquify(concat_name(cell, object->name)); } -template -void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name) -{ - if (object->has_attribute(ID::src)) - object->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - - // Preserve original names via the hdlname attribute, but only for objects with a fully public name. - if (cell->name[0] == '\\' && (object->has_attribute(ID::hdlname) || orig_object_name[0] == '\\')) { - std::vector hierarchy; - if (object->has_attribute(ID::hdlname)) - hierarchy = object->get_hdlname_attribute(); - else - hierarchy.push_back(orig_object_name.str().substr(1)); - hierarchy.insert(hierarchy.begin(), cell->name.str().substr(1)); - object->set_hdlname_attribute(hierarchy); - } -} - void map_sigspec(const dict &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr) { vector chunks = sig; @@ -76,6 +58,54 @@ void map_sigspec(const dict &map, RTLIL::SigSpec &si struct FlattenWorker { bool ignore_wb = false; + bool create_scopeinfo = true; + bool create_scopename = false; + + template + void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name) + { + if (!create_scopeinfo && object->has_attribute(ID::src)) + object->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + + // Preserve original names via the hdlname attribute, but only for objects with a fully public name. + // If the '-scopename' option is used, also preserve the containing scope of private objects if their scope is fully public. + if (cell->name[0] == '\\') { + if (object->has_attribute(ID::hdlname) || orig_object_name[0] == '\\') { + std::string new_hdlname; + + if (cell->has_attribute(ID::hdlname)) { + new_hdlname = cell->get_string_attribute(ID(hdlname)); + } else { + log_assert(!cell->name.empty()); + new_hdlname = cell->name.c_str() + 1; + } + new_hdlname += ' '; + + if (object->has_attribute(ID::hdlname)) { + new_hdlname += object->get_string_attribute(ID(hdlname)); + } else { + log_assert(!orig_object_name.empty()); + new_hdlname += orig_object_name.c_str() + 1; + } + object->set_string_attribute(ID(hdlname), new_hdlname); + } else if (object->has_attribute(ID(scopename))) { + std::string new_scopename; + + if (cell->has_attribute(ID::hdlname)) { + new_scopename = cell->get_string_attribute(ID(hdlname)); + } else { + log_assert(!cell->name.empty()); + new_scopename = cell->name.c_str() + 1; + } + new_scopename += ' '; + new_scopename += object->get_string_attribute(ID(scopename)); + object->set_string_attribute(ID(scopename), new_scopename); + } else if (create_scopename) { + log_assert(!cell->name.empty()); + object->set_string_attribute(ID(scopename), cell->name.c_str() + 1); + } + } + } void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector &new_cells) { @@ -220,7 +250,33 @@ struct FlattenWorker sigmap.add(new_conn.first, new_conn.second); } + RTLIL::Cell *scopeinfo = nullptr; + RTLIL::IdString cell_name = cell->name; + + if (create_scopeinfo && cell_name.isPublic()) + { + // The $scopeinfo's name will be changed below after removing the flattened cell + scopeinfo = module->addCell(NEW_ID, ID($scopeinfo)); + scopeinfo->setParam(ID::TYPE, RTLIL::Const("module")); + + for (auto const &attr : cell->attributes) + { + if (attr.first == ID::hdlname) + scopeinfo->attributes.insert(attr); + else + scopeinfo->attributes.emplace(stringf("\\cell_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second); + } + + for (auto const &attr : tpl->attributes) + scopeinfo->attributes.emplace(stringf("\\module_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second); + + scopeinfo->attributes.emplace(ID(module), RTLIL::unescape_id(tpl->name)); + } + module->remove(cell); + + if (scopeinfo != nullptr) + module->rename(scopeinfo, cell_name); } void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool &used_modules) @@ -275,6 +331,20 @@ struct FlattenPass : public Pass { log(" -wb\n"); log(" Ignore the 'whitebox' attribute on cell implementations.\n"); log("\n"); + log(" -noscopeinfo\n"); + log(" Do not create '$scopeinfo' cells that preserve attributes of cells and\n"); + log(" modules that were removed during flattening. With this option, the\n"); + log(" 'src' attribute of a given cell is merged into all objects replacing\n"); + log(" that cell, with multiple distinct 'src' locations separated by '|'.\n"); + log(" Without this option these 'src' locations can be found via the\n"); + log(" cell_src' and 'module_src' attribute of '$scopeinfo' cells.\n"); + log("\n"); + log(" -scopename\n"); + log(" Create 'scopename' attributes for objects with a private name. This\n"); + log(" attribute records the 'hdlname' of the enclosing scope. For objects\n"); + log(" with a public name the enclosing scope can be found via their\n"); + log(" 'hdlname' attribute.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { @@ -289,6 +359,14 @@ struct FlattenPass : public Pass { worker.ignore_wb = true; continue; } + if (args[argidx] == "-noscopeinfo") { + worker.create_scopeinfo = false; + continue; + } + if (args[argidx] == "-scopename") { + worker.create_scopename = true; + continue; + } break; } extra_args(args, argidx, design); From 9288107f4345fba9f0364036415b5b9b88178b5b Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 19 Dec 2023 19:47:09 +0100 Subject: [PATCH 03/31] Test flatten and opt_clean's $scopeinfo handling --- tests/various/scopeinfo.ys | 110 +++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 tests/various/scopeinfo.ys diff --git a/tests/various/scopeinfo.ys b/tests/various/scopeinfo.ys new file mode 100644 index 000000000..f8d4ca31b --- /dev/null +++ b/tests/various/scopeinfo.ys @@ -0,0 +1,110 @@ +read_verilog < Date: Thu, 11 Jan 2024 14:10:13 +0100 Subject: [PATCH 04/31] Ignore $scopeinfo in opt_merge --- passes/opt/opt_merge.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 248ba8091..eb3aa462e 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -272,6 +272,9 @@ struct OptMergeWorker if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) continue; + if (cell->type == ID($scopeinfo)) + continue; + uint64_t hash = hash_cell_parameters_and_connections(cell); auto r = sharemap.insert(std::make_pair(hash, cell)); if (!r.second) { From 10d5d358d2b8f82767953e7c5be44637a245582b Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 11 Jan 2024 14:10:25 +0100 Subject: [PATCH 05/31] Ignore $scopeinfo in write_aiger While SBY's aiger flow already removes non-assertion driving logic, there are some uses of write_aiger outside of SBY that could end up with $scopeinfo cells, so we explicitly ignore them. The write_btor backend works differently and due to the way it recursively visits cells, it would never reach isolated cells like $scopeinfo. --- backends/aiger/aiger.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index f77a64978..fe4f7681d 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -320,6 +320,9 @@ struct AigerWriter continue; } + if (cell->type == ID($scopeinfo)) + continue; + log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); } From 5cfbc1604cb75b39ce92120bab61168cea84da35 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 11 Jan 2024 14:54:02 +0100 Subject: [PATCH 06/31] Ignore $scopeinfo in write_edif --- backends/edif/edif.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 00fd7f54e..553eb23d6 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -213,6 +213,9 @@ struct EdifBackend : public Backend { for (auto cell : module->cells()) { + if (cell->type == ID($scopeinfo)) + continue; + if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) { lib_cell_ports[cell->type]; for (auto p : cell->connections()) From 59a60c76fe1f52d0f1000bb2274bbdbc455af827 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 11 Jan 2024 14:57:34 +0100 Subject: [PATCH 07/31] Ignore $scopeinfo in write_blif --- backends/blif/blif.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index 8e2c088c4..788b7f951 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -226,6 +226,9 @@ struct BlifDumper for (auto cell : module->cells()) { + if (cell->type == ID($scopeinfo)) + continue; + if (config->unbuf_types.count(cell->type)) { auto portnames = config->unbuf_types.at(cell->type); f << stringf(".names %s %s\n1 1\n", From 55d8425468da6153a2b2bc9a08eb1fd4fec95cb4 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 11 Jan 2024 15:00:09 +0100 Subject: [PATCH 08/31] Ignore $scopeinfo in write_firrtl --- backends/firrtl/firrtl.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index fc1d62891..dc76dbeec 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -980,6 +980,9 @@ struct FirrtlWorker register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } + + if (cell->type == ID($scopeinfo)) + continue; log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); } From 418bf61b8d70b1683cb0644dfc4646a271031ce4 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 11 Jan 2024 15:11:11 +0100 Subject: [PATCH 09/31] Ignore $scopeinfo in write_smv --- backends/smv/smv.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index 49c2cc7a6..44e200384 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -573,6 +573,9 @@ struct SmvWorker continue; } + if (cell->type == ID($scopeinfo)) + continue; + if (cell->type[0] == '$') { if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n", From 5ee8bebde4ec9893f57917f9580700ad50756190 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 11 Jan 2024 15:12:32 +0100 Subject: [PATCH 10/31] Ignore $scopeinfo in write_spice --- backends/spice/spice.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index f260276eb..1160a01a1 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -72,6 +72,9 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De for (auto cell : module->cells()) { + if (cell->type == ID($scopeinfo)) + continue; + f << stringf("X%d", cell_counter++); std::vector port_sigs; From f31fb95963507d6ddd00cd25a2eaa90d6a191ae7 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Thu, 11 Jan 2024 15:18:37 +0100 Subject: [PATCH 11/31] Ignore $scopeinfo in write_verilog --- backends/verilog/verilog_backend.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 988eef658..05b7c6c40 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1871,6 +1871,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) { + // To keep the output compatible with other tools we ignore $scopeinfo + // cells that exist only to hold metadata. If in the future that metadata + // should be exposed as part of the write_verilog output it should be + // opt-in and/or represented as something else than a $scopeinfo cell. + if (cell->type == ID($scopeinfo)) + return; + // Handled by dump_memory if (cell->is_mem_cell()) return; From bbe39762aded86bf621e6f344a5b7e5b804c73d6 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Fri, 12 Jan 2024 14:14:01 +0100 Subject: [PATCH 12/31] Ignore $scopeinfo in write_json --- backends/json/json.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backends/json/json.cc b/backends/json/json.cc index fd2c922fd..2f442c494 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -192,6 +192,10 @@ struct JsonWriter for (auto c : module->cells()) { if (use_selection && !module->selected(c)) continue; + // Eventually we will want to emit $scopeinfo, but currently this + // will break JSON netlist consumers like nextpnr + if (c->type == ID($scopeinfo)) + continue; f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(c->name).c_str()); f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0"); From 0d5b48de989246ede8554fd93180c5d791e262b1 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Mon, 29 Jan 2024 14:46:39 +0100 Subject: [PATCH 13/31] Add scopeinfo index/lookup utils --- Makefile | 3 +- kernel/scopeinfo.cc | 129 +++++++++++++ kernel/scopeinfo.h | 432 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 563 insertions(+), 1 deletion(-) create mode 100644 kernel/scopeinfo.cc create mode 100644 kernel/scopeinfo.h diff --git a/Makefile b/Makefile index 901106802..ec7454674 100644 --- a/Makefile +++ b/Makefile @@ -630,6 +630,7 @@ $(eval $(call add_include_file,kernel/qcsat.h)) $(eval $(call add_include_file,kernel/register.h)) $(eval $(call add_include_file,kernel/rtlil.h)) $(eval $(call add_include_file,kernel/satgen.h)) +$(eval $(call add_include_file,kernel/scopeinfo.h)) $(eval $(call add_include_file,kernel/sigtools.h)) $(eval $(call add_include_file,kernel/timinginfo.h)) $(eval $(call add_include_file,kernel/utils.h)) @@ -656,7 +657,7 @@ $(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_v OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o OBJS += kernel/binding.o -OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o +OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o ifeq ($(ENABLE_ZLIB),1) OBJS += kernel/fstdata.o endif diff --git a/kernel/scopeinfo.cc b/kernel/scopeinfo.cc new file mode 100644 index 000000000..7ed9ebf33 --- /dev/null +++ b/kernel/scopeinfo.cc @@ -0,0 +1,129 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Jannis Harder + * + * 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/scopeinfo.h" + +YOSYS_NAMESPACE_BEGIN + +template void ModuleHdlnameIndex::index_items(I begin, I end, Filter filter) +{ + for (; begin != end; ++begin) { + auto const &item = *begin; + + if (!filter(item)) + continue; + std::vector path = parse_hdlname(item); + if (!path.empty()) + lookup.emplace(item, tree.insert(path, item)); + } +} + +void ModuleHdlnameIndex::index() +{ + index_wires(); + index_cells(); +} + +void ModuleHdlnameIndex::index_wires() +{ + auto wires = module->wires(); + index_items(wires.begin(), wires.end(), [](Wire *) { return true; }); +} + +void ModuleHdlnameIndex::index_cells() +{ + auto cells = module->cells(); + index_items(cells.begin(), cells.end(), [](Cell *) { return true; }); +} + +void ModuleHdlnameIndex::index_scopeinfo_cells() +{ + auto cells = module->cells(); + index_items(cells.begin(), cells.end(), [](Cell *cell) { return cell->type == ID($scopeinfo); }); +} + +std::vector ModuleHdlnameIndex::scope_sources(Cursor cursor) +{ + std::vector result; + + for (; !cursor.is_root(); cursor = cursor.parent()) { + if (!cursor.has_entry()) { + result.push_back(""); + result.push_back(""); + continue; + } + Cell *cell = cursor.entry().cell(); + if (cell == nullptr || cell->type != ID($scopeinfo)) { + result.push_back(""); + result.push_back(""); + continue; + } + result.push_back(scopeinfo_get_attribute(cell, ScopeinfoAttrs::Module, ID::src).decode_string()); + result.push_back(scopeinfo_get_attribute(cell, ScopeinfoAttrs::Cell, ID::src).decode_string()); + } + + result.push_back(module->get_src_attribute()); + + std::reverse(result.begin(), result.end()); + + return result; +} + +static const char *attr_prefix(ScopeinfoAttrs attrs) +{ + switch (attrs) { + case ScopeinfoAttrs::Cell: + return "\\cell_"; + case ScopeinfoAttrs::Module: + return "\\module_"; + default: + log_abort(); + } +} + +bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id) +{ + log_assert(scopeinfo->type == ID($scopeinfo)); + return scopeinfo->has_attribute(attr_prefix(attrs) + RTLIL::unescape_id(id)); +} + +RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id) +{ + log_assert(scopeinfo->type == ID($scopeinfo)); + auto found = scopeinfo->attributes.find(attr_prefix(attrs) + RTLIL::unescape_id(id)); + if (found == scopeinfo->attributes.end()) + return RTLIL::Const(); + return found->second; +} + +dict scopeinfo_attributes(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs) +{ + dict attributes; + + const char *prefix = attr_prefix(attrs); + int prefix_len = strlen(prefix); + + for (auto const &entry : scopeinfo->attributes) + if (entry.first.begins_with(prefix)) + attributes.emplace(RTLIL::escape_id(entry.first.c_str() + prefix_len), entry.second); + + return attributes; +} + +YOSYS_NAMESPACE_END diff --git a/kernel/scopeinfo.h b/kernel/scopeinfo.h new file mode 100644 index 000000000..71af70344 --- /dev/null +++ b/kernel/scopeinfo.h @@ -0,0 +1,432 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Jannis Harder + * + * 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. + * + */ + +#ifndef SCOPEINFO_H +#define SCOPEINFO_H + +#include +#include + +#include "kernel/yosys.h" +#include "kernel/celltypes.h" + +YOSYS_NAMESPACE_BEGIN + +template +class IdTree +{ +public: + struct Cursor; + +protected: + IdTree *parent = nullptr; + IdString scope_name; + int depth = 0; + + pool names; + dict entries; +public: // XXX + dict> subtrees; + + template + static Cursor do_insert(IdTree *tree, P begin, P end, T_ref &&value) + { + log_assert(begin != end && "path must be non-empty"); + while (true) { + IdString name = *begin; + ++begin; + log_assert(!name.empty()); + tree->names.insert(name); + if (begin == end) { + tree->entries.emplace(name, std::forward(value)); + return Cursor(tree, name); + } + auto &unique = tree->subtrees[name]; + if (!unique) { + unique.reset(new IdTree); + unique->scope_name = name; + unique->parent = tree; + unique->depth = tree->depth + 1; + } + tree = unique.get(); + } + } + +public: + IdTree() = default; + IdTree(const IdTree &) = delete; + IdTree(IdTree &&) = delete; + + // A cursor remains valid as long as the (sub-)IdTree it points at is alive + struct Cursor + { + friend class IdTree; + protected: + public: + IdTree *target; + IdString scope_name; + + Cursor() : target(nullptr) {} + Cursor(IdTree *target, IdString scope_name) : target(target), scope_name(scope_name) { + if (scope_name.empty()) + log_assert(target->parent == nullptr); + } + + Cursor do_first_child() { + IdTree *tree = nullptr; + if (scope_name.empty()) { + tree = target; + } else { + auto found = target->subtrees.find(scope_name); + if (found != target->subtrees.end()) { + tree = found->second.get(); + } else { + return Cursor(); + } + } + if (tree->names.empty()) { + return Cursor(); + } + return Cursor(tree, *tree->names.begin()); + } + + Cursor do_next_sibling() { + if (scope_name.empty()) + return Cursor(); + auto found = target->names.find(scope_name); + if (found == target->names.end()) + return Cursor(); + ++found; + if (found == target->names.end()) + return Cursor(); + return Cursor(target, *found); + } + + Cursor do_parent() { + if (scope_name.empty()) + return Cursor(); + if (target->parent != nullptr) + return Cursor(target->parent, target->scope_name); + return Cursor(target, IdString()); + } + + Cursor do_next_preorder() { + Cursor current = *this; + Cursor next = current.do_first_child(); + if (next.valid()) + return next; + while (current.valid()) { + if (next.valid()) + return next; + next = current.do_next_sibling(); + if (next.valid()) + return next; + current = current.do_parent(); + } + return current; + } + + Cursor do_child(IdString name) { + IdTree *tree = nullptr; + if (scope_name.empty()) { + tree = target; + } else { + auto found = target->subtrees.find(scope_name); + if (found != target->subtrees.end()) { + tree = found->second.get(); + } else { + return Cursor(); + } + } + auto found = tree->names.find(name); + if (found == tree->names.end()) { + return Cursor(); + } + return Cursor(tree, *found); + } + + public: + bool operator==(const Cursor &other) const { + return target == other.target && scope_name == other.scope_name; + } + bool operator!=(const Cursor &other) const { + return !(*this == other); + } + + bool valid() const { + return target != nullptr; + } + + int depth() const { + log_assert(valid()); + return target->depth + !scope_name.empty(); + } + + bool is_root() const { + return target != nullptr && scope_name.empty(); + } + + bool has_entry() const { + log_assert(valid()); + return !scope_name.empty() && target->entries.count(scope_name); + } + + T &entry() { + log_assert(!scope_name.empty()); + return target->entries.at(scope_name); + } + + void assign_path_to(std::vector &out_path) { + log_assert(valid()); + out_path.clear(); + if (scope_name.empty()) + return; + out_path.push_back(scope_name); + IdTree *current = target; + while (current->parent) { + out_path.push_back(current->scope_name); + current = current->parent; + } + std::reverse(out_path.begin(), out_path.end()); + } + + std::vector path() { + std::vector result; + assign_path_to(result); + return result; + } + + std::string path_str() { + std::string result; + for (const auto &item : path()) { + if (!result.empty()) + result.push_back(' '); + result += RTLIL::unescape_id(item); + } + return result; + } + + Cursor first_child() { + log_assert(valid()); + return do_first_child(); + } + + Cursor next_preorder() { + log_assert(valid()); + return do_next_preorder(); + } + + Cursor parent() { + log_assert(valid()); + return do_parent(); + } + + Cursor child(IdString name) { + log_assert(valid()); + return do_child(name); + } + + Cursor common_ancestor(Cursor other) { + Cursor current = *this; + + while (current != other) { + if (!current.valid() || !other.valid()) + return Cursor(); + int delta = current.depth() - other.depth(); + if (delta >= 0) + current = current.do_parent(); + if (delta <= 0) + other = other.do_parent(); + } + return current; + } + }; + + template + Cursor insert(P begin, P end, const T &value) { + return do_insert(this, begin, end, value); + } + + template + Cursor insert(P begin, P end, T &&value) { + return do_insert(this, begin, end, std::move(value)); + } + + template + Cursor insert(const P &path, const T &value) { + return do_insert(this, path.begin(), path.end(), value); + } + + template + Cursor insert(const P &path, T &&value) { + return do_insert(this, path.begin(), path.end(), std::move(value)); + } + + Cursor cursor() { + return parent ? Cursor(this->parent, this->scope_name) : Cursor(this, IdString()); + } + + template + Cursor cursor(P begin, P end) { + Cursor current = cursor(); + for (; begin != end; ++begin) { + current = current.do_child(*begin); + if (!current.valid()) + break; + } + return current; + } + + template + Cursor cursor(const P &path) { + return cursor(path.begin(), path.end()); + } +}; + + +struct ModuleItem { + enum class Type { + Wire, + Cell, + }; + Type type; + void *ptr; + + ModuleItem(Wire *wire) : type(Type::Wire), ptr(wire) {} + ModuleItem(Cell *cell) : type(Type::Cell), ptr(cell) {} + + bool is_wire() const { return type == Type::Wire; } + bool is_cell() const { return type == Type::Cell; } + + Wire *wire() const { return type == Type::Wire ? static_cast(ptr) : nullptr; } + Cell *cell() const { return type == Type::Cell ? static_cast(ptr) : nullptr; } + + bool operator==(const ModuleItem &other) const { return ptr == other.ptr && type == other.type; } + unsigned int hash() const { return (uintptr_t)ptr; } +}; + +static inline void log_dump_val_worker(typename IdTree::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); } + +template +static inline void log_dump_val_worker(const typename std::unique_ptr &cursor ) { log("unique %p", cursor.get()); } + +template +std::vector parse_hdlname(const O* object) +{ + std::vector path; + if (!object->name.isPublic()) + return path; + for (auto const &item : object->get_hdlname_attribute()) + path.push_back("\\" + item); + if (path.empty()) + path.push_back(object->name); + return path; +} + +template +std::pair, IdString> parse_scopename(const O* object) +{ + std::vector path; + IdString trailing = object->name; + if (object->name.isPublic()) { + for (auto const &item : object->get_hdlname_attribute()) + path.push_back("\\" + item); + if (!path.empty()) { + trailing = path.back(); + path.pop_back(); + } + } else { + for (auto const &item : split_tokens(object->get_string_attribute(ID(scopename)), " ")) + path.push_back("\\" + item); + + } + return {path, trailing}; +} + +struct ModuleHdlnameIndex { + typedef IdTree::Cursor Cursor; + + RTLIL::Module *module; + IdTree tree; + dict lookup; + + ModuleHdlnameIndex(RTLIL::Module *module) : module(module) {} + +private: + template + void index_items(I begin, I end, Filter filter); + +public: + // Index all wires and cells of the module + void index(); + + // Index all wires of the module + void index_wires(); + + // Index all cells of the module + void index_cells(); + + // Index only the $scopeinfo cells of the module. + // This is sufficient when using `containing_scope`. + void index_scopeinfo_cells(); + + + // Return the cursor for the containing scope of some RTLIL object (Wire/Cell/...) + template + std::pair containing_scope(O *object) { + auto pair = parse_scopename(object); + return {tree.cursor(pair.first), pair.second}; + } + + // Return a vector of source locations starting from the indexed module to + // the scope represented by the cursor. The vector alternates module and + // module item source locations, using empty strings for missing src + // attributes. + std::vector scope_sources(Cursor cursor); + + // Return a vector of source locations starting from the indexed module to + // the passed RTLIL object (Wire/Cell/...). The vector alternates module + // and module item source locations, using empty strings for missing src + // attributes. + template + std::vector sources(O *object) { + auto pair = parse_scopename(object); + std::vector result = scope_sources(tree.cursor(pair.first)); + result.push_back(object->get_src_attribute()); + return result; + } +}; + +enum class ScopeinfoAttrs { + Module, + Cell, +}; + +// Check whether the flattened module or flattened cell corresponding to a $scopeinfo cell had a specific attribute. +bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id); + +// Get a specific attribute from the flattened module or flattened cell corresponding to a $scopeinfo cell. +RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id); + +// Get all attribute from the flattened module or flattened cell corresponding to a $scopeinfo cell. +dict scopeinfo_attributes(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs); + +YOSYS_NAMESPACE_END + +#endif From 364bcfb8f1accacda63efa0e57c777c70ad5a7bc Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Mon, 29 Jan 2024 21:28:51 +0100 Subject: [PATCH 14/31] Example pass for the scopeinfo index/lookup utils --- examples/cxx-api/scopeinfo_example.cc | 144 ++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 examples/cxx-api/scopeinfo_example.cc diff --git a/examples/cxx-api/scopeinfo_example.cc b/examples/cxx-api/scopeinfo_example.cc new file mode 100644 index 000000000..f163dff9e --- /dev/null +++ b/examples/cxx-api/scopeinfo_example.cc @@ -0,0 +1,144 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2023 Jannis Harder + * + * 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. + * + */ + +// build: yosys-config --build scopeinfo_example.so scopeinfo_example.cc +// use: yosys -m scopeinfo_example.so + +#include "backends/rtlil/rtlil_backend.h" +#include "kernel/scopeinfo.h" +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ScopeinfoExamplePass : public Pass { + ScopeinfoExamplePass() : Pass("scopeinfo_example", "dump scopeinfo") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" scopeinfo_example [options] [selection]\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing SCOPEINFO_EXAMPLE pass.\n"); + + bool do_wires = false; + bool do_common = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-wires") { + do_wires = true; + continue; + } + if (args[argidx] == "-common") { + do_common = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + + if (do_wires) { + for (auto module : design->selected_modules()) { + log("Source hierarchy for all selected wires within %s:\n", log_id(module)); + ModuleHdlnameIndex index(module); + + index.index_scopeinfo_cells(); + + for (auto wire : module->selected_wires()) { + if (!wire->name.isPublic()) + continue; + + auto wire_scope = index.containing_scope(wire); + + if (!wire_scope.first.valid()) { + log_warning("Couldn't find containing scope for %s in index\n", log_id(wire)); + continue; + } + + log("%s %s\n", wire_scope.first.path_str().c_str(), log_id(wire_scope.second)); + for (auto src : index.sources(wire)) + log(" - %s\n", src.c_str()); + } + } + } + + if (do_common) { + for (auto module : design->selected_modules()) { + std::vector wires = module->selected_wires(); + + // Shuffle wires so this example produces more interesting outputs + std::sort(wires.begin(), wires.end(), [](Wire *a, Wire *b) { + return mkhash_xorshift(a->name.hash() * 0x2c9277b5) < mkhash_xorshift(b->name.hash() * 0x2c9277b5); + }); + + ModuleHdlnameIndex index(module); + + index.index_scopeinfo_cells(); + + for (auto wire_i = wires.begin(), wire_end = wires.end(); wire_i != wire_end; ++wire_i) { + if (!(*wire_i)->name.isPublic()) + continue; + + std::pair scope_i = index.containing_scope(*wire_i); + if (!scope_i.first.valid()) + continue; + + int limit = 0; + + for (auto wire_j = wire_i + 1; wire_j != wire_end; ++wire_j) { + if (!(*wire_j)->name.isPublic()) + continue; + + std::pair scope_j = index.containing_scope(*wire_j); + if (!scope_j.first.valid()) + continue; + + // Skip wires in the same hierarchy level + if (scope_i.first == scope_j.first) + continue; + + + ModuleHdlnameIndex::Cursor common = scope_i.first.common_ancestor(scope_j.first); + + // Try to show at least some non-root common ancestors + if (common.is_root() && limit > 5) + continue; + + log("common_ancestor(%s %s%s%s, %s %s%s%s) = %s %s\n", + log_id(module), scope_i.first.path_str().c_str(), scope_i.first.is_root() ? "" : " ", log_id(scope_i.second), + log_id(module), scope_j.first.path_str().c_str(), scope_j.first.is_root() ? "" : " ", log_id(scope_j.second), + log_id(module), common.path_str().c_str() + ); + + if (++limit == 10) + break; + } + } + } + } + } +} ScopeinfoExamplePass; + +PRIVATE_NAMESPACE_END From 8566489d8510af1e3086af9b692490f35db377a8 Mon Sep 17 00:00:00 2001 From: Ethan Mahintorabi Date: Fri, 9 Feb 2024 23:51:00 +0000 Subject: [PATCH 15/31] stat: Add sequential area output to stat -liberty Checks to see if a cell is of type ff in the liberty, and keeps track of an additional area value. ``` Chip area for module '\addr': 92.280720 Sequential area for module '\addr': 38.814720 ``` Signed-off-by: Ethan Mahintorabi --- passes/cmds/stat.cc | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index d34373c1c..9849d2537 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -28,6 +28,11 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +struct cell_data_t { + double area; + bool is_flip_flop; +}; + struct statdata_t { #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ @@ -39,6 +44,7 @@ struct statdata_t STAT_INT_MEMBERS #undef X double area; + double sequential_area; string tech; std::map techinfo; @@ -74,7 +80,7 @@ struct statdata_t #undef X } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict &cell_area, string techname) + statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict &cell_properties, string techname) { tech = techname; @@ -131,11 +137,17 @@ struct statdata_t cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Q))); } - if (!cell_area.empty()) { - if (cell_area.count(cell_type)) - area += cell_area.at(cell_type); - else + if (!cell_properties.empty()) { + if (cell_properties.count(cell_type)) { + cell_data_t cell_data = cell_properties.at(cell_type); + if (cell_data.is_flip_flop) { + sequential_area += cell_data.area; + } + area += cell_data.area; + } + else { unknown_cell_area.insert(cell_type); + } } num_cells++; @@ -244,6 +256,7 @@ 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(" Sequential area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), sequential_area); } if (tech == "xilinx") @@ -325,7 +338,7 @@ statdata_t hierarchy_worker(std::map &mod_stat, RTL return mod_data; } -void read_liberty_cellarea(dict &cell_area, string liberty_file) +void read_liberty_cellarea(dict &cell_properties, string liberty_file) { std::ifstream f; f.open(liberty_file.c_str()); @@ -341,8 +354,9 @@ void read_liberty_cellarea(dict &cell_area, string liberty_fil continue; LibertyAst *ar = cell->find("area"); + bool is_flip_flop = cell->find("ff") != nullptr; if (ar != nullptr && !ar->value.empty()) - cell_area["\\" + cell->args[0]] = atof(ar->value.c_str()); + cell_properties["\\" + cell->args[0]] = {/*area=*/atof(ar->value.c_str()), is_flip_flop}; } } @@ -383,7 +397,7 @@ struct StatPass : public Pass { bool width_mode = false, json_mode = false; RTLIL::Module *top_mod = nullptr; std::map mod_stat; - dict cell_area; + dict cell_properties; string techname; size_t argidx; @@ -396,7 +410,7 @@ struct StatPass : public Pass { if (args[argidx] == "-liberty" && argidx+1 < args.size()) { string liberty_file = args[++argidx]; rewrite_filename(liberty_file); - read_liberty_cellarea(cell_area, liberty_file); + read_liberty_cellarea(cell_properties, liberty_file); continue; } if (args[argidx] == "-tech" && argidx+1 < args.size()) { @@ -439,7 +453,7 @@ struct StatPass : public Pass { if (mod->get_bool_attribute(ID::top)) top_mod = mod; - statdata_t data(design, mod, width_mode, cell_area, techname); + statdata_t data(design, mod, width_mode, cell_properties, techname); mod_stat[mod->name] = data; if (json_mode) { From 10297127be683ac1eefbb6025feac6d9c1faf08d Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 12 Feb 2024 09:19:58 +0100 Subject: [PATCH 16/31] fix test for verific --- tests/svtypes/struct_sizebits.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/svtypes/struct_sizebits.sv b/tests/svtypes/struct_sizebits.sv index 0a94d57a5..aceec84b3 100644 --- a/tests/svtypes/struct_sizebits.sv +++ b/tests/svtypes/struct_sizebits.sv @@ -26,9 +26,9 @@ struct packed { always_comb begin assert ($dimensions(s) == 1); - assert ($dimensions(s.t) == 1); assert ($dimensions(s.x) == 1); `ifndef VERIFIC + assert ($dimensions(s.t) == 1); assert ($dimensions({3{s.x}}) == 1); `endif assert ($dimensions(s.sy.y) == 2); From ae7daf99f464a4a274519880307b8a188e7a2722 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 12 Feb 2024 09:53:47 +0100 Subject: [PATCH 17/31] Verific: Add attributes to module instantiation --- frontends/verific/verific.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index dff9c777b..faa0e1bcd 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -1990,6 +1990,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma } RTLIL::Cell *cell = module->addCell(inst_name, inst_type); + import_attributes(cell->attributes, inst); if (inst->IsPrimitive() && mode_keep) cell->attributes[ID::keep] = 1; From 353ccc9e58d12b2a9dcf9213bfa02116a754c230 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 12 Feb 2024 12:58:13 +0100 Subject: [PATCH 18/31] do not override existing shell variable --- tests/gen-tests-makefile.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gen-tests-makefile.sh b/tests/gen-tests-makefile.sh index 6bf91b4dc..2b26d8c98 100755 --- a/tests/gen-tests-makefile.sh +++ b/tests/gen-tests-makefile.sh @@ -16,8 +16,8 @@ generate_target() { # $ generate_ys_test ys_file [yosys_args] generate_ys_test() { ys_file=$1 - yosys_args=${2:-} - generate_target "$ys_file" "\"$YOSYS_BASEDIR/yosys\" -ql ${ys_file%.*}.log $yosys_args $ys_file" + yosys_args_=${2:-} + generate_target "$ys_file" "\"$YOSYS_BASEDIR/yosys\" -ql ${ys_file%.*}.log $yosys_args_ $ys_file" } # $ generate_bash_test bash_file From 54a97f8bb7b2977a71892651dfa844675c21d3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 12 Feb 2024 14:56:10 +0100 Subject: [PATCH 19/31] driver: Fix crashes on missing cli arguments --- kernel/driver.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/kernel/driver.cc b/kernel/driver.cc index 8d9ecc91a..58da1bc32 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -92,8 +92,15 @@ int getopt(int argc, char **argv, const char *optstring) return optopt; } - optarg = argv[++optind]; + if (++optind >= argc) { + fprintf(stderr, "%s: option '-%c' expects an argument\n", argv[0], optopt); + optopt = '?'; + return optopt; + } + + optarg = argv[optind]; optind++, optcur = 1; + return optopt; } From 606bbef30c391b280e35a8c8354b4d440e62ec64 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 00:16:18 +0000 Subject: [PATCH 20/31] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 72fb38a9b..748f155af 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.38+17 +YOSYS_VER := 0.38+37 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From ae1a67ba47ce5d9d531a1fd89c4e687ed558994e Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 13 Feb 2024 12:15:59 +0000 Subject: [PATCH 21/31] cxxrtl: fix debug information for zero-width items. Because all objects in C++ must have non-zero size, a `value<0>` has a size of 4 despite consisting of a `uint32_t chunks[0]`. The debug item assertions were not written expecting that and prevent any debug items for such values from compiling. The C API does not define exactly what happens for a zero-width debug item, but it seems OK to say that they should refer to some unique pointer that cannot be, in actuality, read or written. This allows some techniques or optimizations that use `curr` pointers as keys and assume they correspond 1-to-1 to simulation objects. --- .../cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h | 5 +++++ backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 20 ++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h index e5c84bf65..c2a9d37e1 100644 --- a/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h +++ b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h @@ -240,6 +240,11 @@ struct cxxrtl_object { // through wires, the bits are double buffered. To avoid race conditions, user code should // always read from `curr` and write to `next`. The `curr` pointer is always valid; for objects // that cannot be modified, or cannot be modified in a race-free way, `next` is NULL. + // + // In case where `width == 0`, `curr` is a non-NULL pointer unique for the wire. That is, + // there is a 1-to-1 correspondence between simulation objects and `curr` pointers, regardless + // of whether they have storage or not. (Aliases' `curr` pointer equals that of some other + // simulated object.) uint32_t *curr; uint32_t *next; diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index ee55011e8..8b7cf4da8 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1176,7 +1176,7 @@ struct debug_item : ::cxxrtl_object { template debug_item(value &item, size_t lsb_offset = 0, uint32_t flags_ = 0) { - static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = VALUE; flags = flags_; @@ -1192,7 +1192,7 @@ struct debug_item : ::cxxrtl_object { template debug_item(const value &item, size_t lsb_offset = 0) { - static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = VALUE; flags = DRIVEN_COMB; @@ -1208,8 +1208,9 @@ struct debug_item : ::cxxrtl_object { template debug_item(wire &item, size_t lsb_offset = 0, uint32_t flags_ = 0) { - static_assert(sizeof(item.curr) == value::chunks * sizeof(chunk_t) && - sizeof(item.next) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || + (sizeof(item.curr) == value::chunks * sizeof(chunk_t) && + sizeof(item.next) == value::chunks * sizeof(chunk_t)), "wire is not compatible with C layout"); type = WIRE; flags = flags_; @@ -1225,7 +1226,7 @@ struct debug_item : ::cxxrtl_object { template debug_item(memory &item, size_t zero_offset = 0) { - static_assert(sizeof(item.data[0]) == value::chunks * sizeof(chunk_t), + static_assert(Width == 0 || sizeof(item.data[0]) == value::chunks * sizeof(chunk_t), "memory is not compatible with C layout"); type = MEMORY; flags = 0; @@ -1241,7 +1242,7 @@ struct debug_item : ::cxxrtl_object { template debug_item(debug_alias, const value &item, size_t lsb_offset = 0) { - static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = ALIAS; flags = DRIVEN_COMB; @@ -1257,8 +1258,9 @@ struct debug_item : ::cxxrtl_object { template debug_item(debug_alias, const wire &item, size_t lsb_offset = 0) { - static_assert(sizeof(item.curr) == value::chunks * sizeof(chunk_t) && - sizeof(item.next) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || + (sizeof(item.curr) == value::chunks * sizeof(chunk_t) && + sizeof(item.next) == value::chunks * sizeof(chunk_t)), "wire is not compatible with C layout"); type = ALIAS; flags = DRIVEN_COMB; @@ -1274,7 +1276,7 @@ struct debug_item : ::cxxrtl_object { template debug_item(debug_outline &group, const value &item, size_t lsb_offset = 0) { - static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = OUTLINE; flags = DRIVEN_COMB; From c3c44225de0be8940373c2dba361442348ca96f5 Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 13 Feb 2024 12:38:03 +0000 Subject: [PATCH 22/31] cxxrtl: document some `module` invariants. NFC --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 8b7cf4da8..fd8dd578e 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1363,10 +1363,16 @@ struct debug_items { } }; -// Tag class to disambiguate the default constructor used by the toplevel module that calls reset(), +// Tag class to disambiguate the default constructor used by the toplevel module that calls `reset()`, // and the constructor of interior modules that should not call it. struct interior {}; +// The core API of the `module` class consists of only four virtual methods: `reset()`, `eval()`, +// `commit`, and `debug_info()`. (The virtual destructor is made necessary by C++.) Every other method +// is a convenience method, and exists solely to simplify some common pattern for C++ API consumers. +// No behavior may be added to such convenience methods that other parts of CXXRTL can rely on, since +// there is no guarantee they will be called (and, for example, other CXXRTL libraries will often call +// the `eval()` and `commit()` directly instead, as well as being exposed in the C API). struct module { module() {} virtual ~module() {} @@ -1382,8 +1388,14 @@ struct module { virtual void reset() = 0; + // The `eval()` callback object, `performer`, is included in the virtual call signature since + // the generated code has broadly identical performance properties. virtual bool eval(performer *performer = nullptr) = 0; - virtual bool commit() = 0; // commit observer isn't available since it avoids virtual calls + + // The `commit()` callback object, `observer`, is not included in the virtual call signature since + // the generated code is severely pessimized by it. To observe commit events, the non-virtual + // `commit(observer *)` overload must be called directly on a `module` subclass. + virtual bool commit() = 0; size_t step(performer *performer = nullptr) { size_t deltas = 0; From 42920c9bc0e6e8d7f4bbe07b0c8a0b6c665130a0 Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 13 Feb 2024 12:47:58 +0000 Subject: [PATCH 23/31] cxxrtl: rationalize `debug_items` accessors. Before this commit, `at()` and `operator[]` did the same thing, making one of them redundant. There was also a somewhat awkward `parts_at()`, which is more generic than `at()`. After this commit, `parts_at()` becomes `at()`, and `at()` becomes `operator[]`. Any quick-and-dirty accesses should use `items["name"]`, which expects a single-part item. Generic code should instead use `items.at("name")`, which will return multiple parts. Both will check for the existence of the name. This is unlikely to break downstream code since it's likely been using the shorter `operator[]`. (In any case this API is not stable.) --- backends/cxxrtl/runtime/cxxrtl/cxxrtl.h | 17 +++++++++++------ backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index fd8dd578e..f9d22ff9b 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1320,6 +1320,14 @@ namespace cxxrtl { using debug_attrs = ::_cxxrtl_attr_set; struct debug_items { + // Debug items may be composed of multiple parts, but the attributes are shared between all of them. + // There are additional invariants, not all of which are not checked by this code: + // - Memories and non-memories cannot be mixed together. + // - Bit indices (considering `lsb_at` and `width`) must not overlap. + // - Row indices (considering `depth` and `zero_at`) must be the same. + // - The `INPUT` and `OUTPUT` flags must be the same for all parts. + // Other than that, the parts can be quite different, e.g. it is OK to mix a value, a wire, an alias, + // and an outline, in the debug information for a single name in four parts. std::map> table; std::map> attrs_table; @@ -1344,20 +1352,17 @@ struct debug_items { return table.at(name).size(); } - const std::vector &parts_at(const std::string &name) const { + const std::vector &at(const std::string &name) const { return table.at(name); } - const debug_item &at(const std::string &name) const { + // Like `at()`, but operates only on single-part debug items. + const debug_item &operator [](const std::string &name) const { const std::vector &parts = table.at(name); assert(parts.size() == 1); return parts.at(0); } - const debug_item &operator [](const std::string &name) const { - return at(name); - } - const metadata_map &attrs(const std::string &name) const { return attrs_table.at(name)->map; } diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h index d824fdb83..b1cd9d9a1 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h @@ -641,7 +641,7 @@ public: assert(items.count(name) != 0); assert(part_index < items.count(name)); - const debug_item &part = items.parts_at(name).at(part_index); + const debug_item &part = items.at(name).at(part_index); assert(chunks == (part.width + sizeof(chunk_t) * 8 - 1) / (sizeof(chunk_t) * 8)); assert(depth == part.depth); From 91685355a082f1b5fbc539d0ec484f4d484f5baa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 00:15:26 +0000 Subject: [PATCH 24/31] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 748f155af..f9dd25361 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.38+37 +YOSYS_VER := 0.38+41 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From bbdfcfdf30bc3d82f53415868d6e45e291e37266 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 14 Feb 2024 11:42:27 +0100 Subject: [PATCH 25/31] clk2fflogic: Fix handling of $check cells Fixes a bug in the handling of the recently introduced $check cells. Both $check and $print cells in clk2fflogic are handled by the same code and the existing tests for that were only using $print cells. This missed a bug where the additional A signal of $check cells that is not present on $print cells was dropped due to a typo, rendering $check cells non-functional. Also updates the tests to explicitly cover both cell types such that they would have detected the now fixed bug. --- passes/sat/clk2fflogic.cc | 3 ++- tests/various/clk2fflogic_effects.sh | 32 +++++++++++++++++----------- tests/various/clk2fflogic_effects.sv | 23 ++++++++++++++++++-- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index 2c0e13f85..bcefa7d8f 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -238,7 +238,8 @@ struct Clk2fflogicPass : public Pass { cell->setPort(ID::EN, module->And(NEW_ID, sig_en_sampled, sig_trg_combined)); cell->setPort(ID::ARGS, sig_args_sampled); if (cell->type == ID($check)) { - SigBit sig_a_sampled = sample_data(module, sig_en, State::S1, false, false).sampled; + SigBit sig_a = cell->getPort(ID::A); + SigBit sig_a_sampled = sample_data(module, sig_a, State::S1, false, false).sampled; cell->setPort(ID::A, sig_a_sampled); } } diff --git a/tests/various/clk2fflogic_effects.sh b/tests/various/clk2fflogic_effects.sh index f44afc1dd..0d133ffdd 100755 --- a/tests/various/clk2fflogic_effects.sh +++ b/tests/various/clk2fflogic_effects.sh @@ -1,27 +1,35 @@ #!/usr/bin/env bash -set -ex +set -e -../../yosys -p " -read_verilog -formal -DFAST clk2fflogic_effects.sv +# TODO: when sim gets native $check support, remove the -DNO_ASSERT here +echo Running yosys sim +../../yosys -q -p " +read_verilog -formal -DNO_ASSERT clk2fflogic_effects.sv hierarchy -top top; proc;; -tee -o clk2fflogic_effects.sim.log sim -fst clk2fflogic_effects.sim.fst -q -n 16 -" -../../yosys -p " -read_verilog -formal -DFAST clk2fflogic_effects.sv +tee -q -o clk2fflogic_effects.sim.log sim -q -n 32 +" +echo Running yosys clk2fflogic sim +../../yosys -q -p " +read_verilog -formal clk2fflogic_effects.sv hierarchy -top top; proc;; clk2fflogic;; -tee -o clk2fflogic_effects.clk2fflogic.log sim -fst clk2fflogic_effects.clk2fflogic.fst -q -n 16 +logger -nowarn ^Assertion +tee -q -o clk2fflogic_effects.clk2fflogic.log sim -q -n 32 " -iverilog -g2012 -o clk2fflogic_effects.iv.out clk2fflogic_effects.sv +echo Running iverilog sim +iverilog -g2012 -DNO_ASSERT -o clk2fflogic_effects.iv.out clk2fflogic_effects.sv + ./clk2fflogic_effects.iv.out > clk2fflogic_effects.iv.log -sort clk2fflogic_effects.iv.log > clk2fflogic_effects.iv.sorted.log -tail -n +3 clk2fflogic_effects.sim.log | sort > clk2fflogic_effects.sim.sorted.log -tail -n +3 clk2fflogic_effects.clk2fflogic.log | sort > clk2fflogic_effects.clk2fflogic.sorted.log +gawk '/([0-9]+):/{T=$1;print};/^Failed/{print T,$0}' clk2fflogic_effects.iv.log | sort > clk2fflogic_effects.iv.sorted.log +gawk '/([0-9]+):/{T=$1;print};/^Failed/{print T,$0}' clk2fflogic_effects.sim.log | sort > clk2fflogic_effects.sim.sorted.log +gawk '/([0-9]+):/{T=$1;print};/^Failed/{print T,$0}' clk2fflogic_effects.clk2fflogic.log | sort > clk2fflogic_effects.clk2fflogic.sorted.log +echo Comparing iverilog sim vs yosys sim cmp clk2fflogic_effects.iv.sorted.log clk2fflogic_effects.sim.sorted.log +echo Comparing iverilog sim vs yosys clk2fflogic sim cmp clk2fflogic_effects.iv.sorted.log clk2fflogic_effects.clk2fflogic.sorted.log diff --git a/tests/various/clk2fflogic_effects.sv b/tests/various/clk2fflogic_effects.sv index b571cf3fe..b38aba3c9 100644 --- a/tests/various/clk2fflogic_effects.sv +++ b/tests/various/clk2fflogic_effects.sv @@ -7,7 +7,7 @@ reg clk = 0; always @(posedge gclk) clk <= !clk; -reg [4:0] counter = 0; +reg [5:0] counter = 0; reg eff_0_trg = '0; reg eff_0_en = '0; @@ -20,6 +20,10 @@ reg eff_2_trgA = '0; reg eff_2_trgB = '0; reg eff_2_en = '0; +reg eff_3_trg = '0; +reg eff_3_en = '0; +reg eff_3_a = '0; + `ifdef FAST always @(posedge gclk) begin `else @@ -37,6 +41,10 @@ always @(posedge clk) begin eff_2_trgA = counter[0]; eff_2_trgB = !counter[0]; eff_2_en <= 32'b00000000000000000000001111111100 >> counter; + + eff_3_trg = 32'b10101010101010101010101010101010 >> counter; + eff_3_en <= 32'b11101110010001001110111001000100 >> counter; + eff_3_a <= 32'b11111010111110100101000001010000 >> counter; end always @(posedge eff_0_trg) @@ -71,11 +79,22 @@ always @(posedge eff_2_trgA, posedge eff_2_trgB) if (eff_2_en) $display("repeated"); +always @(posedge eff_3_trg) + if (eff_3_en) begin + $display("%02d: eff3 vvv", counter); +`ifdef NO_ASSERT + if (!eff_3_a) + $display("Failed assertion eff3 at"); +`else + eff3: assert(eff_3_a); +`endif + end + `ifdef __ICARUS__ initial gclk = 0; always @(gclk) gclk <= #5 !gclk; always @(posedge gclk) - if (counter == 31) + if (counter == 32) $finish(0); `endif From 149c1a7fc6b838abe0503854a7fb046131b0b073 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 14 Feb 2024 12:07:47 +0100 Subject: [PATCH 26/31] tests: Support running `make test` with YOSYS_NOVERIFIC=1 A yosys build with verific support can act as a non-verific yosys with `YOSYS_NOVERIFIC=1` in the enviornment, which is useful for quickly testing code that works with either frontend without rebuilding yosys. Before this change, this did not work with `make test` as it would only consider the build time configuration to decide whether to run tests that depend on verific support, immediately failing on those tests when the enviornment contains `YOSYS_NOVERIFIC=1`. This adds logic to the makefile that checks this enviornment variable and also exports YOSYS_NOVERIFIC=1 to the enviornment when that is present as a make variable to support `make test YOSYS_NOVERIFIC=1` invocations. --- Makefile | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Makefile b/Makefile index f9dd25361..7acba0b11 100644 --- a/Makefile +++ b/Makefile @@ -848,9 +848,22 @@ else ABCOPT="" endif +# When YOSYS_NOVERIFIC is set as a make variable, also export it to the +# enviornment, so that `YOSYS_NOVERIFIC=1 make test` _and_ +# `make test YOSYS_NOVERIFIC=1` will run with verific disabled. +ifeq ($(YOSYS_NOVERIFIC),1) +export YOSYS_NOVERIFIC +endif + test: $(TARGETS) $(EXTRA_TARGETS) ifeq ($(ENABLE_VERIFIC),1) +ifeq ($(YOSYS_NOVERIFIC),1) + @echo + @echo "Running tests without verific support due to YOSYS_NOVERIFIC=1" + @echo +else +cd tests/verific && bash run-test.sh $(SEEDOPT) +endif endif +cd tests/simple && bash run-test.sh $(SEEDOPT) +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT) From 074b50e9c09e92501088a1570b9cf05c0fc64054 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 00:15:29 +0000 Subject: [PATCH 27/31] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7acba0b11..57e37351c 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.38+41 +YOSYS_VER := 0.38+46 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 2d8343d4234da07e026225682694e4192db75c3a Mon Sep 17 00:00:00 2001 From: Ethan Mahintorabi Date: Thu, 15 Feb 2024 23:59:19 +0000 Subject: [PATCH 28/31] update type and variable names Signed-off-by: Ethan Mahintorabi --- passes/cmds/stat.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 9849d2537..82ad80779 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -28,9 +28,9 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct cell_data_t { +struct cell_area_t { double area; - bool is_flip_flop; + bool is_sequential; }; struct statdata_t @@ -80,7 +80,7 @@ struct statdata_t #undef X } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict &cell_properties, string techname) + statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict &cell_properties, string techname) { tech = techname; @@ -139,8 +139,8 @@ struct statdata_t if (!cell_properties.empty()) { if (cell_properties.count(cell_type)) { - cell_data_t cell_data = cell_properties.at(cell_type); - if (cell_data.is_flip_flop) { + cell_area_t cell_data = cell_properties.at(cell_type); + if (cell_data.is_sequential) { sequential_area += cell_data.area; } area += cell_data.area; @@ -338,7 +338,7 @@ statdata_t hierarchy_worker(std::map &mod_stat, RTL return mod_data; } -void read_liberty_cellarea(dict &cell_properties, string liberty_file) +void read_liberty_cellarea(dict &cell_properties, string liberty_file) { std::ifstream f; f.open(liberty_file.c_str()); @@ -397,7 +397,7 @@ struct StatPass : public Pass { bool width_mode = false, json_mode = false; RTLIL::Module *top_mod = nullptr; std::map mod_stat; - dict cell_properties; + dict cell_properties; string techname; size_t argidx; From f0df0e3912bdc40075a2762530d21add11c7e6d0 Mon Sep 17 00:00:00 2001 From: Ethan Mahintorabi Date: Fri, 16 Feb 2024 00:01:44 +0000 Subject: [PATCH 29/31] update type and variable names Signed-off-by: Ethan Mahintorabi --- passes/cmds/stat.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 82ad80779..c74aa7c14 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -80,7 +80,7 @@ struct statdata_t #undef X } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict &cell_properties, string techname) + statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict &cell_area, string techname) { tech = techname; @@ -137,9 +137,9 @@ struct statdata_t cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Q))); } - if (!cell_properties.empty()) { - if (cell_properties.count(cell_type)) { - cell_area_t cell_data = cell_properties.at(cell_type); + if (!cell_area.empty()) { + 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; } @@ -338,7 +338,7 @@ statdata_t hierarchy_worker(std::map &mod_stat, RTL return mod_data; } -void read_liberty_cellarea(dict &cell_properties, string liberty_file) +void read_liberty_cellarea(dict &cell_area, string liberty_file) { std::ifstream f; f.open(liberty_file.c_str()); @@ -356,7 +356,7 @@ void read_liberty_cellarea(dict &cell_properties, string LibertyAst *ar = cell->find("area"); bool is_flip_flop = cell->find("ff") != nullptr; if (ar != nullptr && !ar->value.empty()) - cell_properties["\\" + cell->args[0]] = {/*area=*/atof(ar->value.c_str()), is_flip_flop}; + cell_area["\\" + cell->args[0]] = {/*area=*/atof(ar->value.c_str()), is_flip_flop}; } } @@ -397,7 +397,7 @@ struct StatPass : public Pass { bool width_mode = false, json_mode = false; RTLIL::Module *top_mod = nullptr; std::map mod_stat; - dict cell_properties; + dict cell_area; string techname; size_t argidx; @@ -410,7 +410,7 @@ struct StatPass : public Pass { if (args[argidx] == "-liberty" && argidx+1 < args.size()) { string liberty_file = args[++argidx]; rewrite_filename(liberty_file); - read_liberty_cellarea(cell_properties, liberty_file); + read_liberty_cellarea(cell_area, liberty_file); continue; } if (args[argidx] == "-tech" && argidx+1 < args.size()) { @@ -453,7 +453,7 @@ struct StatPass : public Pass { if (mod->get_bool_attribute(ID::top)) top_mod = mod; - statdata_t data(design, mod, width_mode, cell_properties, techname); + statdata_t data(design, mod, width_mode, cell_area, techname); mod_stat[mod->name] = data; if (json_mode) { From b8a1009de98406d6841357954724807bd034176e Mon Sep 17 00:00:00 2001 From: Ethan Mahintorabi Date: Fri, 16 Feb 2024 07:44:09 -0800 Subject: [PATCH 30/31] Update passes/cmds/stat.cc Make reporting line more clear about the non cumulative area of sequential cells Co-authored-by: N. Engelhardt --- passes/cmds/stat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index c74aa7c14..85107b68f 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -256,7 +256,7 @@ 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(" Sequential area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), sequential_area); + log(" of which used for sequential elements: %f (%.2f%%)\n", sequential_area, 100.0*sequential_area/area); } if (tech == "xilinx") From f8d4d7128cf72456cc03b0738a8651ac5dbe52e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 17 Feb 2024 00:15:42 +0000 Subject: [PATCH 31/31] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 57e37351c..109721c71 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.38+46 +YOSYS_VER := 0.38+54 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo