mirror of https://github.com/YosysHQ/yosys.git
commit
edb95c69a9
3
Makefile
3
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/register.h))
|
||||||
$(eval $(call add_include_file,kernel/rtlil.h))
|
$(eval $(call add_include_file,kernel/rtlil.h))
|
||||||
$(eval $(call add_include_file,kernel/satgen.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/sigtools.h))
|
||||||
$(eval $(call add_include_file,kernel/timinginfo.h))
|
$(eval $(call add_include_file,kernel/timinginfo.h))
|
||||||
$(eval $(call add_include_file,kernel/utils.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/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
|
||||||
OBJS += kernel/binding.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)
|
ifeq ($(ENABLE_ZLIB),1)
|
||||||
OBJS += kernel/fstdata.o
|
OBJS += kernel/fstdata.o
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -320,6 +320,9 @@ struct AigerWriter
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
|
log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -226,6 +226,9 @@ struct BlifDumper
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
{
|
{
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (config->unbuf_types.count(cell->type)) {
|
if (config->unbuf_types.count(cell->type)) {
|
||||||
auto portnames = config->unbuf_types.at(cell->type);
|
auto portnames = config->unbuf_types.at(cell->type);
|
||||||
f << stringf(".names %s %s\n1 1\n",
|
f << stringf(".names %s %s\n1 1\n",
|
||||||
|
|
|
@ -213,6 +213,9 @@ struct EdifBackend : public Backend {
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
{
|
{
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) {
|
if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) {
|
||||||
lib_cell_ports[cell->type];
|
lib_cell_ports[cell->type];
|
||||||
for (auto p : cell->connections())
|
for (auto p : cell->connections())
|
||||||
|
|
|
@ -980,6 +980,9 @@ struct FirrtlWorker
|
||||||
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
|
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
|
||||||
continue;
|
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));
|
log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,6 +192,10 @@ struct JsonWriter
|
||||||
for (auto c : module->cells()) {
|
for (auto c : module->cells()) {
|
||||||
if (use_selection && !module->selected(c))
|
if (use_selection && !module->selected(c))
|
||||||
continue;
|
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", first ? "" : ",");
|
||||||
f << stringf(" %s: {\n", get_name(c->name).c_str());
|
f << stringf(" %s: {\n", get_name(c->name).c_str());
|
||||||
f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0");
|
f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0");
|
||||||
|
|
|
@ -573,6 +573,9 @@ struct SmvWorker
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (cell->type[0] == '$') {
|
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)) {
|
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",
|
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n",
|
||||||
|
|
|
@ -72,6 +72,9 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
{
|
{
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
f << stringf("X%d", cell_counter++);
|
f << stringf("X%d", cell_counter++);
|
||||||
|
|
||||||
std::vector<RTLIL::SigSpec> port_sigs;
|
std::vector<RTLIL::SigSpec> port_sigs;
|
||||||
|
|
|
@ -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)
|
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
|
// Handled by dump_memory
|
||||||
if (cell->is_mem_cell())
|
if (cell->is_mem_cell())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 Jannis Harder <jix@yosyshq.com> <me@jix.one>
|
||||||
|
*
|
||||||
|
* 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<std::string> 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<Wire *> 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<ModuleHdlnameIndex::Cursor, IdString> 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<ModuleHdlnameIndex::Cursor, IdString> 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
|
|
@ -108,6 +108,7 @@ struct CellTypes
|
||||||
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool<RTLIL::IdString>());
|
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool<RTLIL::IdString>());
|
||||||
setup_type(ID($original_tag), {ID::A}, {ID::Y});
|
setup_type(ID($original_tag), {ID::A}, {ID::Y});
|
||||||
setup_type(ID($future_ff), {ID::A}, {ID::Y});
|
setup_type(ID($future_ff), {ID::A}, {ID::Y});
|
||||||
|
setup_type(ID($scopeinfo), {}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_internals_eval()
|
void setup_internals_eval()
|
||||||
|
|
|
@ -1769,6 +1769,15 @@ namespace {
|
||||||
return;
|
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($_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($_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; }
|
if (cell->type == ID($_AND_)) { port(ID::A,1); port(ID::B,1); port(ID::Y,1); check_expected(); return; }
|
||||||
|
|
|
@ -1379,6 +1379,11 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Unsupported internal cell types: $pow $fsm $mem*
|
// Unsupported internal cell types: $pow $fsm $mem*
|
||||||
// .. and all sequential cells with asynchronous inputs
|
// .. and all sequential cells with asynchronous inputs
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Jannis Harder <jix@yosyshq.com> <me@jix.one>
|
||||||
|
*
|
||||||
|
* 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 <typename I, typename Filter> void ModuleHdlnameIndex::index_items(I begin, I end, Filter filter)
|
||||||
|
{
|
||||||
|
for (; begin != end; ++begin) {
|
||||||
|
auto const &item = *begin;
|
||||||
|
|
||||||
|
if (!filter(item))
|
||||||
|
continue;
|
||||||
|
std::vector<IdString> 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<std::string> ModuleHdlnameIndex::scope_sources(Cursor cursor)
|
||||||
|
{
|
||||||
|
std::vector<std::string> 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<RTLIL::IdString, RTLIL::Const> scopeinfo_attributes(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs)
|
||||||
|
{
|
||||||
|
dict<RTLIL::IdString, RTLIL::Const> 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
|
|
@ -0,0 +1,432 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Jannis Harder <jix@yosyshq.com> <me@jix.one>
|
||||||
|
*
|
||||||
|
* 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 <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "kernel/yosys.h"
|
||||||
|
#include "kernel/celltypes.h"
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class IdTree
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Cursor;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
IdTree *parent = nullptr;
|
||||||
|
IdString scope_name;
|
||||||
|
int depth = 0;
|
||||||
|
|
||||||
|
pool<IdString> names;
|
||||||
|
dict<IdString, T> entries;
|
||||||
|
public: // XXX
|
||||||
|
dict<IdString, std::unique_ptr<IdTree>> subtrees;
|
||||||
|
|
||||||
|
template<typename P, typename T_ref>
|
||||||
|
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<T_ref>(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<IdString> &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<IdString> path() {
|
||||||
|
std::vector<IdString> 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<typename P>
|
||||||
|
Cursor insert(P begin, P end, const T &value) {
|
||||||
|
return do_insert(this, begin, end, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename P>
|
||||||
|
Cursor insert(P begin, P end, T &&value) {
|
||||||
|
return do_insert(this, begin, end, std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename P>
|
||||||
|
Cursor insert(const P &path, const T &value) {
|
||||||
|
return do_insert(this, path.begin(), path.end(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename P>
|
||||||
|
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<typename P>
|
||||||
|
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<typename P>
|
||||||
|
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<Wire *>(ptr) : nullptr; }
|
||||||
|
Cell *cell() const { return type == Type::Cell ? static_cast<Cell *>(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<ModuleItem>::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static inline void log_dump_val_worker(const typename std::unique_ptr<T> &cursor ) { log("unique %p", cursor.get()); }
|
||||||
|
|
||||||
|
template<typename O>
|
||||||
|
std::vector<IdString> parse_hdlname(const O* object)
|
||||||
|
{
|
||||||
|
std::vector<IdString> 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<typename O>
|
||||||
|
std::pair<std::vector<IdString>, IdString> parse_scopename(const O* object)
|
||||||
|
{
|
||||||
|
std::vector<IdString> 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<ModuleItem>::Cursor Cursor;
|
||||||
|
|
||||||
|
RTLIL::Module *module;
|
||||||
|
IdTree<ModuleItem> tree;
|
||||||
|
dict<ModuleItem, Cursor> lookup;
|
||||||
|
|
||||||
|
ModuleHdlnameIndex(RTLIL::Module *module) : module(module) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename I, typename Filter>
|
||||||
|
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<typename O>
|
||||||
|
std::pair<Cursor, IdString> 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<std::string> 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<typename O>
|
||||||
|
std::vector<std::string> sources(O *object) {
|
||||||
|
auto pair = parse_scopename(object);
|
||||||
|
std::vector<std::string> 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<RTLIL::IdString, RTLIL::Const> scopeinfo_attributes(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs);
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif
|
|
@ -35,10 +35,12 @@ struct keep_cache_t
|
||||||
{
|
{
|
||||||
Design *design;
|
Design *design;
|
||||||
dict<Module*, bool> cache;
|
dict<Module*, bool> cache;
|
||||||
|
bool purge_mode = false;
|
||||||
|
|
||||||
void reset(Design *design = nullptr)
|
void reset(Design *design = nullptr, bool purge_mode = false)
|
||||||
{
|
{
|
||||||
this->design = design;
|
this->design = design;
|
||||||
|
this->purge_mode = purge_mode;
|
||||||
cache.clear();
|
cache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +90,9 @@ struct keep_cache_t
|
||||||
if (cell->has_keep_attr())
|
if (cell->has_keep_attr())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (!purge_mode && cell->type == ID($scopeinfo))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (cell->module && cell->module->design)
|
if (cell->module && cell->module->design)
|
||||||
return query(cell->module->design->module(cell->type));
|
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();
|
int count = w->attributes.size();
|
||||||
count -= w->attributes.count(ID::src);
|
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);
|
count -= w->attributes.count(ID::unused_bits);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
@ -661,7 +668,7 @@ struct OptCleanPass : public Pass {
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
keep_cache.reset(design);
|
keep_cache.reset(design, purge_mode);
|
||||||
|
|
||||||
ct_reg.setup_internals_mem();
|
ct_reg.setup_internals_mem();
|
||||||
ct_reg.setup_internals_anyinit();
|
ct_reg.setup_internals_anyinit();
|
||||||
|
|
|
@ -272,6 +272,9 @@ struct OptMergeWorker
|
||||||
if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known())
|
if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
uint64_t hash = hash_cell_parameters_and_connections(cell);
|
uint64_t hash = hash_cell_parameters_and_connections(cell);
|
||||||
auto r = sharemap.insert(std::make_pair(hash, cell));
|
auto r = sharemap.insert(std::make_pair(hash, cell));
|
||||||
if (!r.second) {
|
if (!r.second) {
|
||||||
|
|
|
@ -46,24 +46,6 @@ IdString map_name(RTLIL::Cell *cell, T *object)
|
||||||
return cell->module->uniquify(concat_name(cell, object->name));
|
return cell->module->uniquify(concat_name(cell, object->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
|
||||||
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<std::string> 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<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr)
|
void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr)
|
||||||
{
|
{
|
||||||
vector<SigChunk> chunks = sig;
|
vector<SigChunk> chunks = sig;
|
||||||
|
@ -76,6 +58,54 @@ void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &si
|
||||||
struct FlattenWorker
|
struct FlattenWorker
|
||||||
{
|
{
|
||||||
bool ignore_wb = false;
|
bool ignore_wb = false;
|
||||||
|
bool create_scopeinfo = true;
|
||||||
|
bool create_scopename = false;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
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<RTLIL::Cell*> &new_cells)
|
void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells)
|
||||||
{
|
{
|
||||||
|
@ -220,7 +250,33 @@ struct FlattenWorker
|
||||||
sigmap.add(new_conn.first, new_conn.second);
|
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);
|
module->remove(cell);
|
||||||
|
|
||||||
|
if (scopeinfo != nullptr)
|
||||||
|
module->rename(scopeinfo, cell_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules)
|
void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules)
|
||||||
|
@ -275,6 +331,20 @@ struct FlattenPass : public Pass {
|
||||||
log(" -wb\n");
|
log(" -wb\n");
|
||||||
log(" Ignore the 'whitebox' attribute on cell implementations.\n");
|
log(" Ignore the 'whitebox' attribute on cell implementations.\n");
|
||||||
log("\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<std::string> args, RTLIL::Design *design) override
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
{
|
{
|
||||||
|
@ -289,6 +359,14 @@ struct FlattenPass : public Pass {
|
||||||
worker.ignore_wb = true;
|
worker.ignore_wb = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-noscopeinfo") {
|
||||||
|
worker.create_scopeinfo = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-scopename") {
|
||||||
|
worker.create_scopename = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
|
@ -2763,3 +2763,10 @@ assign Y = A;
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
(* noblackbox *)
|
||||||
|
module \$scopeinfo ();
|
||||||
|
|
||||||
|
parameter TYPE = "";
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
read_verilog <<EOT
|
||||||
|
(* module_attr = "module_attr" *)
|
||||||
|
module some_mod(input a, output y);
|
||||||
|
assign y = a;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top(input a, output y);
|
||||||
|
(* inst_attr = "inst_attr" *)
|
||||||
|
some_mod some_inst(.a(a), .y(y));
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
hierarchy -top top
|
||||||
|
flatten
|
||||||
|
|
||||||
|
select -assert-count 1 top/n:some_inst top/t:$scopeinfo %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/r:TYPE=module %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:cell_inst_attr=inst_attr %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:module_module_attr=module_attr %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:cell_src %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:module_src %i
|
||||||
|
|
||||||
|
opt_clean
|
||||||
|
select -assert-count 1 top/t:$scopeinfo
|
||||||
|
opt_clean -purge
|
||||||
|
select -assert-count 0 top/t:$scopeinfo
|
||||||
|
|
||||||
|
design -reset
|
||||||
|
|
||||||
|
read_verilog <<EOT
|
||||||
|
(* module_attr = "module_attr_deep" *)
|
||||||
|
module some_mod_deep(input a, output y);
|
||||||
|
assign y = a;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
(* module_attr = "module_attr" *)
|
||||||
|
module some_mod(input a, output y);
|
||||||
|
(* inst_attr = "inst_attr_deep" *)
|
||||||
|
some_mod_deep some_inst_deep(.a(a), .y(y));
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top(input a, output y);
|
||||||
|
(* inst_attr = "inst_attr" *)
|
||||||
|
some_mod some_inst(.a(a), .y(y));
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
hierarchy -top top
|
||||||
|
flatten
|
||||||
|
|
||||||
|
select -assert-count 2 top/t:$scopeinfo
|
||||||
|
select -assert-count 1 top/n:some_inst top/t:$scopeinfo %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep
|
||||||
|
|
||||||
|
select -assert-count 1 top/n:some_inst top/r:TYPE=module %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:cell_inst_attr=inst_attr %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:module_module_attr=module_attr %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:cell_src %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:module_src %i
|
||||||
|
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/r:TYPE=module %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/a:cell_inst_attr=inst_attr_deep %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/a:module_module_attr=module_attr_deep %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/a:cell_src %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/a:module_src %i
|
||||||
|
|
||||||
|
|
||||||
|
design -reset
|
||||||
|
|
||||||
|
read_verilog <<EOT
|
||||||
|
(* module_attr = "module_attr_deep" *)
|
||||||
|
(* keep_hierarchy *)
|
||||||
|
module some_mod_deep(input a, output y);
|
||||||
|
assign y = a;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
(* module_attr = "module_attr" *)
|
||||||
|
module some_mod(input a, output y);
|
||||||
|
(* inst_attr = "inst_attr_deep" *)
|
||||||
|
some_mod_deep some_inst_deep(.a(a), .y(y));
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top(input a, output y);
|
||||||
|
(* inst_attr = "inst_attr" *)
|
||||||
|
some_mod some_inst(.a(a), .y(y));
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
hierarchy -top top
|
||||||
|
flatten top
|
||||||
|
|
||||||
|
select -assert-count 1 top/t:$scopeinfo
|
||||||
|
setattr -mod -unset keep_hierarchy *
|
||||||
|
flatten
|
||||||
|
|
||||||
|
select -assert-count 2 top/t:$scopeinfo
|
||||||
|
select -assert-count 1 top/n:some_inst top/t:$scopeinfo %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep
|
||||||
|
|
||||||
|
select -assert-count 1 top/n:some_inst top/r:TYPE=module %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:cell_inst_attr=inst_attr %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:module_module_attr=module_attr %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:cell_src %i
|
||||||
|
select -assert-count 1 top/n:some_inst top/a:module_src %i
|
||||||
|
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/r:TYPE=module %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/a:cell_inst_attr=inst_attr_deep %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/a:module_module_attr=module_attr_deep %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/a:cell_src %i
|
||||||
|
select -assert-count 1 top/a:hdlname=some_inst?some_inst_deep top/a:module_src %i
|
Loading…
Reference in New Issue