diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 469bbb180f2..5109c1d62af 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -26,6 +26,7 @@ #include "passes/hierarchy/util/positionals.h" #include "passes/hierarchy/util/verilog.h" #include "passes/hierarchy/util/generate.h" +#include "passes/hierarchy/util/ports.h" #include #include #include @@ -239,7 +240,11 @@ struct HierarchyPass : public Pass { if ((flag_simcheck || flag_smtcheck) && top_mod == nullptr) log_error("Design has no top module.\n"); - expand_all_interfaces(design, top_mod, flag_check, flag_simcheck, flag_smtcheck, libdirs); + Hierarchy::ConnectAccumulator connect_acc; + expand_all_interfaces(design, top_mod, flag_check, flag_simcheck, flag_smtcheck, libdirs, &connect_acc); + + log_header(design, "Resolving $connect directionality..\n"); + Hierarchy::resolve_acc_connects(design, connect_acc); if (top_mod != NULL) { log_header(design, "Analyzing design hierarchy..\n"); diff --git a/passes/hierarchy/util/Makefile.inc b/passes/hierarchy/util/Makefile.inc index 764fbe925ed..026cae25d1b 100644 --- a/passes/hierarchy/util/Makefile.inc +++ b/passes/hierarchy/util/Makefile.inc @@ -2,6 +2,7 @@ OBJS += passes/hierarchy/util/clean.o OBJS += passes/hierarchy/util/generate.o OBJS += passes/hierarchy/util/interfaces.o OBJS += passes/hierarchy/util/misc.o +OBJS += passes/hierarchy/util/ports.o OBJS += passes/hierarchy/util/positionals.o OBJS += passes/hierarchy/util/top.o OBJS += passes/hierarchy/util/verilog.o diff --git a/passes/hierarchy/util/interfaces.cc b/passes/hierarchy/util/interfaces.cc index ae73a2f9442..2b65f898ea7 100644 --- a/passes/hierarchy/util/interfaces.cc +++ b/passes/hierarchy/util/interfaces.cc @@ -172,14 +172,14 @@ void delete_marked_modules(Design* design) { } } -void expand_all_interfaces(Design* design, Module*& top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs) { +void expand_all_interfaces(Design* design, Module*& top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs, ConnectAccumulator* connect_acc) { bool did_something = true; while (did_something) { did_something = false; for (auto module : used_modules(design, top_mod)) { - if (expand_module(design, module, flag_check, flag_simcheck, flag_smtcheck, libdirs)) + if (expand_module(design, module, flag_check, flag_simcheck, flag_smtcheck, libdirs, connect_acc)) did_something = true; } @@ -246,7 +246,7 @@ struct CellArrays { } }; -bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs) +bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs, ConnectAccumulator* connect_acc) { bool did_something = false; CellArrays cell_arrays; @@ -264,6 +264,8 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check } IFModExpander if_mod_expander(*design, *module); for (auto cell : module->cells()) { + if (connect_acc) + connect_acc->collect(module, cell); if (auto unarrayed_type = cell_arrays.trim_and_register(cell)) cell->type = *unarrayed_type; diff --git a/passes/hierarchy/util/interfaces.h b/passes/hierarchy/util/interfaces.h index 138d25daf3c..558d4a68dd1 100644 --- a/passes/hierarchy/util/interfaces.h +++ b/passes/hierarchy/util/interfaces.h @@ -26,8 +26,23 @@ YOSYS_NAMESPACE_BEGIN namespace Hierarchy { -void expand_all_interfaces(Design* design, Module*& top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs); -bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs); +struct ConnectAccumulator { + dict> module_connect_cells; + + // Collect a $connect cell during hierarchy traversal + void collect(Module* module, Cell* cell) { + if (cell->type == ID($connect) && !cell->has_keep_attr()) { + SigSpec sig_a = cell->getPort(ID::A); + SigSpec sig_b = cell->getPort(ID::B); + if (sig_a.size() > 0 && sig_b.size() > 0) { + module_connect_cells[module->name].insert(cell->name); + } + } + } +}; + +void expand_all_interfaces(Design* design, Module*& top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs, ConnectAccumulator* connect_acc); +bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs, ConnectAccumulator* connect_acc); // For expanding a module's interface connections struct IFModExpander diff --git a/passes/hierarchy/util/ports.cc b/passes/hierarchy/util/ports.cc new file mode 100644 index 00000000000..40c49cd75e8 --- /dev/null +++ b/passes/hierarchy/util/ports.cc @@ -0,0 +1,283 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * 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/yosys_common.h" +#include "passes/hierarchy/util/ports.h" +#include "passes/hierarchy/util/interfaces.h" +#include "kernel/sigtools.h" + +YOSYS_NAMESPACE_BEGIN +namespace Hierarchy { + enum class SigDirection { + UNKNOWN, + INPUT, + OUTPUT, + INOUT, + DRIVEN, + CONFLICT + }; + + static void build_driven_signals_index(Module *module, SigMap &sigmap, SigPool &driven_signals) { + for (auto cell : module->cells()) { + for (const auto& [port, sig] : cell->connections()) { + if (cell->output(port)) { + SigSpec mapped_sig = sigmap(sig); + driven_signals.add(mapped_sig); + } + } + } + + for (auto &conn : module->connections()) { + if (conn.second.is_fully_const()) { + SigSpec lhs = sigmap(conn.first); + driven_signals.add(lhs); + } + } + } + + static SigDirection get_signal_direction(const SigSpec &sig, SigMap &sigmap, const SigPool &driven_signals) { + if (sig.is_fully_const()) + return SigDirection::DRIVEN; + + bool has_input = false; + bool has_output = false; + bool has_driven = false; + bool has_unknown = false; + + for (auto &chunk : sig.chunks()) { + if (chunk.is_wire()) { + Wire *w = chunk.wire; + + if (w->port_input && w->port_output) { + has_input = true; + has_output = true; + } else if (w->port_input) { + has_input = true; + } else if (w->port_output) { + has_output = true; + } else { + SigSpec chunk_sig = sigmap(SigSpec(chunk)); + + if (driven_signals.check_any(chunk_sig)) { + has_driven = true; + } else { + has_unknown = true; + } + } + } else { + has_driven = true; + } + } + + if (has_input && has_driven) + return SigDirection::CONFLICT; + if (has_input && has_output) + return SigDirection::INOUT; + if (has_output) + return SigDirection::OUTPUT; + if (has_driven) + return SigDirection::DRIVEN; + if (has_input) + return SigDirection::INPUT; + if (has_unknown) + return SigDirection::UNKNOWN; + + return SigDirection::DRIVEN; + } + + std::pair derive_blackbox_dynports(Module* module, Cell* cell, Design* design, std::set& blackbox_derivatives) { + bool boxed_params = false; + + if (!module->get_blackbox_attribute() || cell->parameters.empty()) { + return {module, boxed_params}; + } + + if (module->get_bool_attribute(ID::dynports)) { + IdString new_m_name = module->derive(design, cell->parameters, true); + + if (new_m_name.empty()) + return {nullptr, boxed_params}; + if (new_m_name != module->name) { + module = design->module(new_m_name); + blackbox_derivatives.insert(module); + } + } else { + boxed_params = true; + } + + return {module, boxed_params}; + } + + void check_and_adjust_ports(Module* module, std::set& blackbox_derivatives, bool keep_portwidths, bool top_is_from_verific) { + Design* design = module->design; + + for (auto cell : module->cells()) + { + Module *m = design->module(cell->type); + + if (m == nullptr) + continue; + + auto [derived_m, boxed_params] = derive_blackbox_dynports(m, cell, design, blackbox_derivatives); + if (derived_m == nullptr) + continue; + m = derived_m; + + for (const auto& [port, sig] : cell->connections()) + { + Wire *w = m->wire(port); + + if (w == nullptr || w->port_id == 0) + continue; + + if (GetSize(sig) == 0) + continue; + + SigSpec conn_sig = sig; + + bool resize_widths = !keep_portwidths && GetSize(w) != GetSize(sig); + if (resize_widths && top_is_from_verific && boxed_params) + log_debug("Ignoring width mismatch on %s.%s.%s from verific\n", + log_id(module), log_id(cell), log_id(port) + ); + else if (resize_widths) { + if (GetSize(w) < GetSize(sig)) + { + int n = GetSize(sig) - GetSize(w); + if (!w->port_input && w->port_output) + { + RTLIL::SigSpec out = conn_sig.extract(0, GetSize(w)); + out.extend_u0(GetSize(conn_sig), w->is_signed); + module->connect(conn_sig.extract(GetSize(w), n), out.extract(GetSize(w), n)); + } + conn_sig.remove(GetSize(w), n); + } + else + { + int n = GetSize(w) - GetSize(sig); + if (w->port_input && !w->port_output) + conn_sig.extend_u0(GetSize(w), conn_sig.is_wire() && conn_sig.as_wire()->is_signed); + else + conn_sig.append(module->addWire(NEW_ID, n)); + } + + if (!sig.is_fully_const() || !w->port_input || w->port_output) + log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell), + log_id(port), GetSize(sig), GetSize(conn_sig)); + cell->setPort(port, conn_sig); + } + + if (w->port_output && !w->port_input && conn_sig.has_const()) + log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n", + log_id(module), log_id(cell), log_id(port), log_id(cell->type), log_signal(conn_sig)); + } + } + } + + void resolve_acc_connects(Design* design, const ConnectAccumulator& connect_acc) { + std::vector sorted_module_names; + for (const auto& [mod_name, cell_names] : connect_acc.module_connect_cells) + sorted_module_names.push_back(mod_name); + std::sort(sorted_module_names.begin(), sorted_module_names.end()); + + for (auto mod_name : sorted_module_names) { + Module* module = design->module(mod_name); + if (!module) + continue; + + const pool& cell_names = connect_acc.module_connect_cells.at(mod_name); + pool remaining_cell_names = cell_names; + int iteration = 0; + + while (true) { + iteration++; + pool cells_to_remove; + vector new_connections; + SigMap sigmap(module); + SigPool driven_signals; + + build_driven_signals_index(module, sigmap, driven_signals); + + for (auto cell_name : remaining_cell_names) { + Cell* cell = module->cell(cell_name); + if (!cell || cell->type != ID($connect) || cell->has_keep_attr()) + continue; + + SigSpec sig_a = cell->getPort(ID::A); + SigSpec sig_b = cell->getPort(ID::B); + + if (sig_a.size() == 0 || sig_b.size() == 0) + continue; + + SigDirection dir_a = get_signal_direction(sig_a, sigmap, driven_signals); + SigDirection dir_b = get_signal_direction(sig_b, sigmap, driven_signals); + + if (dir_a == SigDirection::CONFLICT || dir_b == SigDirection::CONFLICT) + continue; + + SigSpec driver, driven; + bool can_resolve = false; + + if ((dir_a == SigDirection::OUTPUT || dir_a == SigDirection::DRIVEN) && dir_b == SigDirection::INPUT) { + driver = sig_a; + driven = sig_b; + can_resolve = true; + } + else if (dir_a == SigDirection::INPUT && (dir_b == SigDirection::OUTPUT || dir_b == SigDirection::DRIVEN)) { + driver = sig_b; + driven = sig_a; + can_resolve = true; + } + else if (dir_a == SigDirection::DRIVEN && dir_b == SigDirection::UNKNOWN) { + driver = sig_a; + driven = sig_b; + can_resolve = true; + } + else if (dir_a == SigDirection::UNKNOWN && dir_b == SigDirection::DRIVEN) { + driver = sig_b; + driven = sig_a; + can_resolve = true; + } + + if (can_resolve) { + log_debug("Resolving $connect %s: %s <- %s\n", log_id(cell), log_signal(driven), log_signal(driver)); + new_connections.push_back({driven, driver}); + cells_to_remove.insert(cell); + } + } + + for (auto &conn : new_connections) + module->connect(conn); + + for (auto cell : cells_to_remove) { + remaining_cell_names.erase(cell->name); + module->remove(cell); + } + + if (cells_to_remove.empty()) + break; + + log_debug("$connect res iteration %d: resolved %d cells\n", iteration, GetSize(cells_to_remove)); + } + } + } +}; + +YOSYS_NAMESPACE_END diff --git a/passes/hierarchy/util/ports.h b/passes/hierarchy/util/ports.h new file mode 100644 index 00000000000..2918249e064 --- /dev/null +++ b/passes/hierarchy/util/ports.h @@ -0,0 +1,37 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * 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 HIERARCHY_PORTS_H +#define HIERARCHY_PORTS_H + +#include "kernel/yosys.h" +#include "passes/hierarchy/util/interfaces.h" + +YOSYS_NAMESPACE_BEGIN + +namespace Hierarchy { + std::pair derive_blackbox_dynports(Module* module, Cell* cell, Design* design, std::set& blackbox_derivatives); + void check_and_adjust_ports(Module* module, std::set& blackbox_derivatives, bool keep_portwidths, bool top_is_from_verific); + void resolve_acc_connects(Design* design, const ConnectAccumulator& connect_acc); +}; + +YOSYS_NAMESPACE_END + +#endif /* HIERARCHY_PORTS_H */ diff --git a/passes/hierarchy/util/verilog.cc b/passes/hierarchy/util/verilog.cc index 0cd153a3555..b59ade0c08d 100644 --- a/passes/hierarchy/util/verilog.cc +++ b/passes/hierarchy/util/verilog.cc @@ -21,6 +21,7 @@ #include "kernel/yosys_common.h" #include "passes/hierarchy/util/verilog.h" #include "passes/hierarchy/util/positionals.h" +#include "passes/hierarchy/util/ports.h" PRIVATE_NAMESPACE_BEGIN USING_YOSYS_NAMESPACE @@ -74,8 +75,8 @@ namespace Hierarchy { if (keep_positionals) { bool found_positionals = false; - for (auto &conn : cell->connections()) - if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') + for (const auto& [port, sig] : cell->connections()) + if (port[0] == '$' && '0' <= port[1] && port[1] <= '9') found_positionals = true; if (found_positionals) continue; @@ -88,7 +89,10 @@ namespace Hierarchy { } for (auto module : design_modules) - resolve_wand_wor(module, blackbox_derivatives, keep_portwidths, top_is_from_verific); + resolve_wand_wor(module); + + for (auto module : design_modules) + check_and_adjust_ports(module, blackbox_derivatives, keep_portwidths, top_is_from_verific); for (auto module : blackbox_derivatives) design->remove(module); @@ -105,15 +109,10 @@ namespace Hierarchy { RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); // Need accurate port widths for error checking; so must derive blackboxes with dynamic port widths - if (submod->get_blackbox_attribute() && !cell->parameters.empty() && submod->get_bool_attribute(ID::dynports)) { - IdString new_m_name = submod->derive(design, cell->parameters, true); - if (new_m_name.empty()) - return; - if (new_m_name != submod->name) { - submod = design->module(new_m_name); - blackbox_derivatives.insert(submod); - } - } + auto [derived_submod, boxed_params] = derive_blackbox_dynports(submod, cell, design, blackbox_derivatives); + if (derived_submod == nullptr) + return; + submod = derived_submod; auto old_connections = cell->connections(); for (auto wire : submod->wires()) { @@ -141,8 +140,7 @@ namespace Hierarchy { cell->attributes.erase(ID::wildcard_port_conns); } - void resolve_wand_wor(Module* module, std::set& blackbox_derivatives, bool keep_portwidths, bool top_is_from_verific) { - Design* design = module->design; + void resolve_wand_wor(Module* module) { pool wand_wor_index; dict wand_map, wor_map; vector new_connections; @@ -194,15 +192,15 @@ namespace Hierarchy { if (!cell->known()) continue; - for (auto &conn : cell->connections()) + for (const auto& [port, sig] : cell->connections()) { - if (!cell->output(conn.first)) + if (!cell->output(port)) continue; SigSpec new_sig; bool update_port = false; - for (auto c : conn.second.chunks()) + for (auto c : sig.chunks()) { Wire *w = c.wire; @@ -216,18 +214,18 @@ namespace Hierarchy { update_port = true; if (wand_map.count(w)) { - SigSpec sig = SigSpec(State::S1, GetSize(w)); - sig.replace(c.offset, t); - wand_map.at(w).append(sig); + SigSpec mapped_sig = SigSpec(State::S1, GetSize(w)); + mapped_sig.replace(c.offset, t); + wand_map.at(w).append(mapped_sig); } else { - SigSpec sig = SigSpec(State::S0, GetSize(w)); - sig.replace(c.offset, t); - wor_map.at(w).append(sig); + SigSpec mapped_sig = SigSpec(State::S0, GetSize(w)); + mapped_sig.replace(c.offset, t); + wor_map.at(w).append(mapped_sig); } } if (update_port) - cell->setPort(conn.first, new_sig); + cell->setPort(port, new_sig); } } @@ -257,77 +255,6 @@ namespace Hierarchy { module->connect(w, s); } - for (auto cell : module->cells()) - { - Module *m = design->module(cell->type); - - if (m == nullptr) - continue; - - bool boxed_params = false; - if (m->get_blackbox_attribute() && !cell->parameters.empty()) { - if (m->get_bool_attribute(ID::dynports)) { - IdString new_m_name = m->derive(design, cell->parameters, true); - if (new_m_name.empty()) - continue; - if (new_m_name != m->name) { - m = design->module(new_m_name); - blackbox_derivatives.insert(m); - } - } else { - boxed_params = true; - } - } - - for (auto &conn : cell->connections()) - { - Wire *w = m->wire(conn.first); - - if (w == nullptr || w->port_id == 0) - continue; - - if (GetSize(conn.second) == 0) - continue; - - SigSpec sig = conn.second; - - bool resize_widths = !keep_portwidths && GetSize(w) != GetSize(conn.second); - if (resize_widths && top_is_from_verific && boxed_params) - log_debug("Ignoring width mismatch on %s.%s.%s from verific, is port width parametrizable?\n", - log_id(module), log_id(cell), log_id(conn.first) - ); - else if (resize_widths) { - if (GetSize(w) < GetSize(conn.second)) - { - int n = GetSize(conn.second) - GetSize(w); - if (!w->port_input && w->port_output) - { - RTLIL::SigSpec out = sig.extract(0, GetSize(w)); - out.extend_u0(GetSize(sig), w->is_signed); - module->connect(sig.extract(GetSize(w), n), out.extract(GetSize(w), n)); - } - sig.remove(GetSize(w), n); - } - else - { - int n = GetSize(w) - GetSize(conn.second); - if (w->port_input && !w->port_output) - sig.extend_u0(GetSize(w), sig.is_wire() && sig.as_wire()->is_signed); - else - sig.append(module->addWire(NEW_ID, n)); - } - - if (!conn.second.is_fully_const() || !w->port_input || w->port_output) - log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell), - log_id(conn.first), GetSize(conn.second), GetSize(sig)); - cell->setPort(conn.first, sig); - } - - if (w->port_output && !w->port_input && sig.has_const()) - log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n", - log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig)); - } - } } void check_supported_formal(Design* design) { for (auto mod : design->modules()) { diff --git a/passes/hierarchy/util/verilog.h b/passes/hierarchy/util/verilog.h index 4cd5f06dfde..428132beab4 100644 --- a/passes/hierarchy/util/verilog.h +++ b/passes/hierarchy/util/verilog.h @@ -28,7 +28,7 @@ YOSYS_NAMESPACE_BEGIN namespace Hierarchy { void resolve_verilog(Design* design, bool nodefaults, bool keep_positionals, bool keep_portwidths, bool top_is_from_verific); void resolve_wildcards(Cell* cell, std::set& blackbox_derivatives, bool nodefaults, dict>& defaults_db); - void resolve_wand_wor(Module* module, std::set& blackbox_derivatives, bool keep_portwidths, bool top_is_from_verific); + void resolve_wand_wor(Module* module); void check_supported_formal(Design* design); };