Move interface expansion in hierarchy.cc into a helper class

There should be no functional change, but this splits up the control
flow across functions, using class fields to hold the state that's
being tracked. The result should be a bit easier to read.

This is part of work to add bind support, but I'm doing some
refactoring in the hierarchy pass to make the code a bit easier to
work with. The idea is that (eventually) the IFExpander object will
hold all the logic for expanding interfaces, and then other code can
do bind insertion.
This commit is contained in:
Rupert Swarbrick 2020-05-26 17:46:10 +01:00 committed by Zachary Snow
parent f2c2d73f36
commit e2c9580024
1 changed files with 189 additions and 100 deletions

View File

@ -156,6 +156,168 @@ std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
return basicType;
}
// A helper struct for expanding a module's interface connections in expand_module
struct IFExpander
{
IFExpander (RTLIL::Design &design, RTLIL::Module &m)
: module(m), has_interfaces_not_found(false)
{
// Keep track of all derived interfaces available in the current
// module in 'interfaces_in_module':
for (auto cell : module.cells()) {
if(!cell->get_bool_attribute(ID::is_interface))
continue;
interfaces_in_module[cell->name] = design.module(cell->type);
}
}
RTLIL::Module &module;
dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module;
bool has_interfaces_not_found;
std::vector<RTLIL::IdString> connections_to_remove;
std::vector<RTLIL::IdString> connections_to_add_name;
std::vector<RTLIL::SigSpec> connections_to_add_signal;
dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule;
dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule;
// Reset the per-cell state
void start_cell()
{
has_interfaces_not_found = false;
connections_to_remove.clear();
connections_to_add_name.clear();
connections_to_add_signal.clear();
interfaces_to_add_to_submodule.clear();
modports_used_in_submodule.clear();
}
// Set has_interfaces_not_found if there are pending interfaces that
// haven't been found yet (and might be found in the future). Print a
// warning if we've already gone over all the cells in the module.
void on_missing_interface(RTLIL::IdString interface_name)
{
// If there are cells that haven't yet been processed, maybe
// we'll find this interface in the future.
if (module.get_bool_attribute(ID::cells_not_processed)) {
has_interfaces_not_found = true;
return;
}
// Otherwise, we have already gone over all cells in this
// module and the interface has still not been found. Warn
// about it and don't set has_interfaces_not_found (to avoid a
// loop).
log_warning("Could not find interface instance for `%s' in `%s'\n",
log_id(interface_name), log_id(&module));
}
// Handle an interface connection from the module
void on_interface(RTLIL::Module &submodule,
RTLIL::IdString conn_name,
const RTLIL::SigSpec &conn_signals)
{
// Check if the connected wire is a potential interface in the parent module
std::string interface_name_str = conn_signals.bits()[0].wire->name.str();
// Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
interface_name_str.replace(0,23,"");
interface_name_str = "\\" + interface_name_str;
RTLIL::IdString interface_name = interface_name_str;
// If 'interfaces' in the cell have not be been handled yet, we aren't
// ready to derive the sub-module either
if (!module.get_bool_attribute(ID::interfaces_replaced_in_module)) {
on_missing_interface(interface_name);
return;
}
// Check if the interface instance is present in module. Interface
// instances may either have the plain name or the name appended with
// '_inst_from_top_dummy'. Check for both of them here
int nexactmatch = interfaces_in_module.count(interface_name) > 0;
std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy";
RTLIL::IdString interface_name2 = interface_name_str2;
int nmatch2 = interfaces_in_module.count(interface_name2) > 0;
// If we can't find either name, this is a missing interface.
if (! (nexactmatch || nmatch2)) {
on_missing_interface(interface_name);
return;
}
if (nexactmatch != 0) // Choose the one with the plain name if it exists
interface_name2 = interface_name;
RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2);
// Go over all wires in interface, and add replacements to lists.
for (auto mod_wire : mod_replace_ports->wires()) {
std::string signal_name1 = conn_name.str() + "." + log_id(mod_wire->name);
std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire);
connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
if(module.wire(signal_name2) == nullptr) {
log_error("Could not find signal '%s' in '%s'\n",
signal_name2.c_str(), log_id(module.name));
}
else {
RTLIL::Wire *wire_in_parent = module.wire(signal_name2);
connections_to_add_signal.push_back(wire_in_parent);
}
}
connections_to_remove.push_back(conn_name);
interfaces_to_add_to_submodule[conn_name] = interfaces_in_module.at(interface_name2);
// Find if the sub-module has set a modport for the current interface
// connection. Add any modports to a dict which will be passed to
// AstModule::derive
string modport_name = submodule.wire(conn_name)->get_string_attribute(ID::interface_modport);
if (!modport_name.empty()) {
modports_used_in_submodule[conn_name] = "\\" + modport_name;
}
}
// Handle a single connection from the module, making a note to expand
// it if it's an interface connection.
void on_connection(RTLIL::Module &submodule,
RTLIL::IdString conn_name,
const RTLIL::SigSpec &conn_signals)
{
// Check if the connection is present as an interface in the sub-module's port list
const RTLIL::Wire *wire = submodule.wire(conn_name);
if (!wire || !wire->get_bool_attribute(ID::is_interface))
return;
// If the connection looks like an interface, handle it.
const auto &bits = conn_signals.bits();
if (bits.size() == 1 && bits[0].wire->get_bool_attribute(ID::is_interface))
on_interface(submodule, conn_name, conn_signals);
}
// Iterate over the connections in a cell, tracking any interface
// connections
void visit_connections(const RTLIL::Cell &cell,
RTLIL::Module &submodule)
{
for (const auto &conn : cell.connections()) {
on_connection(submodule, conn.first, conn.second);
}
}
// Add/remove connections to the cell as necessary, replacing any SV
// interface port connection with the individual signal connections.
void rewrite_interface_connections(RTLIL::Cell &cell) const
{
for(unsigned int i=0;i<connections_to_add_name.size();i++) {
cell.connections_[connections_to_add_name[i]] = connections_to_add_signal[i];
}
// Remove the connection for the interface itself:
for(unsigned int i=0;i<connections_to_remove.size();i++) {
cell.connections_.erase(connections_to_remove[i]);
}
}
};
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
{
bool did_something = false;
@ -173,23 +335,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
}
}
// Always keep track of all derived interfaces available in the current module in 'interfaces_in_module':
dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module;
for (auto cell : module->cells())
{
if(cell->get_bool_attribute(ID::is_interface)) {
RTLIL::Module *intf_module = design->module(cell->type);
interfaces_in_module[cell->name] = intf_module;
}
}
IFExpander if_expander(*design, *module);
for (auto cell : module->cells())
{
bool has_interfaces_not_found = false;
std::vector<RTLIL::IdString> connections_to_remove;
std::vector<RTLIL::IdString> connections_to_add_name;
std::vector<RTLIL::SigSpec> connections_to_add_signal;
if_expander.start_cell();
if (cell->type.begins_with("$array:")) {
int pos[3];
@ -202,10 +352,9 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
array_cells[cell] = std::pair<int, int>(idx, num);
cell->type = cell->type.substr(pos_type + 1);
}
dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule;
dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule;
if (design->module(cell->type) == nullptr)
RTLIL::Module *mod = design->module(cell->type);
if (mod == nullptr)
{
if (design->module("$abstract" + cell->type.str()) != nullptr)
{
@ -243,77 +392,15 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
continue;
loaded_module:
if (design->module(cell->type) == nullptr)
mod = design->module(cell->type);
if (mod == nullptr)
log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str());
did_something = true;
} else {
RTLIL::Module *mod = design->module(cell->type);
// Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to
// some lists, so that the ports for sub-modules can be replaced further down:
for (auto &conn : cell->connections()) {
if(mod->wire(conn.first) != nullptr && mod->wire(conn.first)->get_bool_attribute(ID::is_interface)) { // Check if the connection is present as an interface in the sub-module's port list
if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute(ID::is_interface)) { // Check if the connected wire is a potential interface in the parent module
std::string interface_name_str = conn.second.bits()[0].wire->name.str();
interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
interface_name_str = "\\" + interface_name_str;
RTLIL::IdString interface_name = interface_name_str;
bool not_found_interface = false;
if(module->get_bool_attribute(ID::interfaces_replaced_in_module)) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either
// Check if the interface instance is present in module:
// Interface instances may either have the plain name or the name appended with '_inst_from_top_dummy'.
// Check for both of them here
int nexactmatch = interfaces_in_module.count(interface_name) > 0;
std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy";
RTLIL::IdString interface_name2 = interface_name_str2;
int nmatch2 = interfaces_in_module.count(interface_name2) > 0;
if (nexactmatch > 0 || nmatch2 > 0) {
if (nexactmatch != 0) // Choose the one with the plain name if it exists
interface_name2 = interface_name;
RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2);
for (auto mod_wire : mod_replace_ports->wires()) { // Go over all wires in interface, and add replacements to lists.
std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire->name);
std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire);
connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
if(module->wire(signal_name2) == nullptr) {
log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name));
}
else {
RTLIL::Wire *wire_in_parent = module->wire(signal_name2);
connections_to_add_signal.push_back(wire_in_parent);
}
}
connections_to_remove.push_back(conn.first);
interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2);
// Find if the sub-module has set a modport for the current
// interface connection. Add any modports to a dict which will
// be passed to AstModule::derive
string modport_name = mod->wire(conn.first)->get_string_attribute(ID::interface_modport);
if (!modport_name.empty()) {
modports_used_in_submodule[conn.first] = "\\" + modport_name;
}
}
else not_found_interface = true;
}
else not_found_interface = true;
// If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found"
// which will delay the expansion of this cell:
if (not_found_interface) {
// If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error:
if(!(module->get_bool_attribute(ID::cells_not_processed))) {
log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module));
}
else {
// Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop:
has_interfaces_not_found = true;
}
}
}
}
}
//
// Go over all connections and check if any of them are SV
// interfaces.
if_expander.visit_connections(*cell, *mod);
if (flag_check || flag_simcheck)
{
@ -340,9 +427,13 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
}
}
RTLIL::Module *mod = design->module(cell->type);
if (design->module(cell->type)->get_blackbox_attribute()) {
// If we make it out of the if/else block above without leaving
// this iteration, mod will equal design->module(cell->type) and
// will be non-null.
log_assert(mod);
if (mod->get_blackbox_attribute()) {
if (flag_simcheck)
log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n",
cell->type.c_str(), module->name.c_str(), cell->name.c_str());
@ -350,23 +441,18 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
}
// If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here:
if(has_interfaces_not_found) {
if(if_expander.has_interfaces_not_found) {
did_something = true; // waiting for interfaces to be handled
continue;
}
// Do the actual replacements of the SV interface port connection with the individual signal connections:
for(unsigned int i=0;i<connections_to_add_name.size();i++) {
cell->connections_[connections_to_add_name[i]] = connections_to_add_signal[i];
}
// Remove the connection for the interface itself:
for(unsigned int i=0;i<connections_to_remove.size();i++) {
cell->connections_.erase(connections_to_remove[i]);
}
if_expander.rewrite_interface_connections(*cell);
// If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type
// for the cell:
if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute(ID::module_not_derived)))) {
if (cell->parameters.size() == 0 &&
(if_expander.interfaces_to_add_to_submodule.size() == 0 ||
!(cell->get_bool_attribute(ID::module_not_derived)))) {
// If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:",
// so that the signals of the interface are added to the parent module.
if (mod->get_bool_attribute(ID::is_interface)) {
@ -375,7 +461,10 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
continue;
}
cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule);
cell->type = mod->derive(design,
cell->parameters,
if_expander.interfaces_to_add_to_submodule,
if_expander.modports_used_in_submodule);
cell->parameters.clear();
did_something = true;
@ -386,7 +475,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
if (mod->get_bool_attribute(ID::is_interface) && cell->get_bool_attribute(ID::module_not_derived)) {
cell->set_bool_attribute(ID::is_interface);
RTLIL::Module *derived_module = design->module(cell->type);
interfaces_in_module[cell->name] = derived_module;
if_expander.interfaces_in_module[cell->name] = derived_module;
did_something = true;
}
// We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell)
@ -399,8 +488,8 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
// If any interface instances or interface ports were found in the module, we need to rederive it completely:
if ((interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) {
module->reprocess_module(design, interfaces_in_module);
if ((if_expander.interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) {
module->reprocess_module(design, if_expander.interfaces_in_module);
return did_something;
}