2026-04-21 22:44:21 +02:00
/*
* yosys - - Yosys Open SYnthesis Suite
*
* Copyright ( C ) 2012 Claire Xenia Wolf < claire @ yosyshq . com >
* Copyright ( C ) 2018 Ruben Undheim < ruben . undheim @ gmail . com >
*
* 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/misc.h"
# include "passes/hierarchy/util/interfaces.h"
# include "passes/hierarchy/util/clean.h"
YOSYS_NAMESPACE_BEGIN
namespace Hierarchy {
RTLIL : : Module * check_if_top_has_changed ( Design * design , Module * top_mod )
{
if ( top_mod ! = NULL & & top_mod - > get_bool_attribute ( ID : : initial_top ) )
return top_mod ;
else {
for ( auto mod : design - > modules ( ) ) {
if ( mod - > get_bool_attribute ( ID : : top ) ) {
return mod ;
}
}
}
return NULL ;
}
// Check that the connections on the cell match those that are defined
// on the type: each named connection should match the name of a port
// and each positional connection should have an index smaller than
// the number of ports.
//
// Also do the same checks on the specified parameters.
2026-05-05 15:23:21 +02:00
static void check_cell_connections ( const RTLIL : : Module & module , RTLIL : : Cell & cell , RTLIL : : Module & mod )
2026-04-21 22:44:21 +02:00
{
int id ;
for ( auto & conn : cell . connections ( ) ) {
if ( read_id_num ( conn . first , & id ) ) {
if ( id < = 0 | | id > GetSize ( mod . ports ) )
log_error ( " Module `%s' referenced in module `%s' in cell `%s' "
2026-05-05 15:23:21 +02:00
" has only %d ports, requested port %d. \n " ,
log_id ( cell . type ) , log_id ( & module ) , log_id ( & cell ) ,
GetSize ( mod . ports ) , id ) ;
2026-04-21 22:44:21 +02:00
continue ;
}
const RTLIL : : Wire * wire = mod . wire ( conn . first ) ;
if ( ! wire | | wire - > port_id = = 0 ) {
log_error ( " Module `%s' referenced in module `%s' in cell `%s' "
2026-05-05 15:23:21 +02:00
" does not have a port named '%s'. \n " ,
log_id ( cell . type ) , log_id ( & module ) , log_id ( & cell ) ,
log_id ( conn . first ) ) ;
2026-04-21 22:44:21 +02:00
}
}
for ( auto & param : cell . parameters ) {
if ( read_id_num ( param . first , & id ) ) {
if ( id < = 0 | | id > GetSize ( mod . avail_parameters ) )
log_error ( " Module `%s' referenced in module `%s' in cell `%s' "
2026-05-05 15:23:21 +02:00
" has only %d parameters, requested parameter %d. \n " ,
log_id ( cell . type ) , log_id ( & module ) , log_id ( & cell ) ,
GetSize ( mod . avail_parameters ) , id ) ;
2026-04-21 22:44:21 +02:00
continue ;
}
if ( mod . avail_parameters . count ( param . first ) = = 0 & &
2026-05-05 15:23:21 +02:00
param . first [ 0 ] ! = ' $ ' & &
strchr ( param . first . c_str ( ) , ' . ' ) = = NULL ) {
2026-04-21 22:44:21 +02:00
log_error ( " Module `%s' referenced in module `%s' in cell `%s' "
2026-05-05 15:23:21 +02:00
" does not have a parameter named '%s'. \n " ,
log_id ( cell . type ) , log_id ( & module ) , log_id ( & cell ) ,
log_id ( param . first ) ) ;
2026-04-21 22:44:21 +02:00
}
}
}
// Get a module needed by a cell, either by deriving an abstract module or by
// loading one from a directory in libdirs.
//
// If the module can't be found and check is true then exit with an error
// message. Otherwise, return a pointer to the module if we derived or loaded
// something. or null otherwise (the module should be blackbox or we couldn't
// find it and check is not set).
2026-05-05 15:23:21 +02:00
RTLIL : : Module * get_module ( RTLIL : : Design & design ,
RTLIL : : Cell & cell ,
RTLIL : : Module & parent ,
bool check ,
const std : : vector < std : : string > & libdirs )
2026-04-21 22:44:21 +02:00
{
std : : string cell_type = cell . type . str ( ) ;
RTLIL : : Module * abs_mod = design . module ( " $abstract " + cell_type ) ;
if ( abs_mod ) {
cell . type = abs_mod - > derive ( & design , cell . parameters ) ;
cell . parameters . clear ( ) ;
RTLIL : : Module * mod = design . module ( cell . type ) ;
log_assert ( mod ) ;
return mod ;
}
// If the cell type starts with '$' and isn't '$abstract', we should
// treat it as a black box and skip.
if ( cell_type [ 0 ] = = ' $ ' )
return nullptr ;
for ( auto & dir : libdirs ) {
static const vector < pair < string , string > > extensions_list =
{
{ " .v " , " verilog " } ,
{ " .sv " , " verilog -sv " } ,
{ " .il " , " rtlil " }
} ;
for ( auto & ext : extensions_list ) {
std : : string filename = dir + " / " + RTLIL : : unescape_id ( cell . type ) + ext . first ;
if ( ! check_file_exists ( filename ) )
continue ;
2026-05-05 17:05:23 +02:00
Frontend : : frontend_call ( & design , nullptr , filename , ext . second ) ;
2026-04-21 22:44:21 +02:00
RTLIL : : Module * mod = design . module ( cell . type ) ;
if ( ! mod )
log_error ( " File `%s' from libdir does not declare module `%s'. \n " ,
2026-05-05 15:23:21 +02:00
filename . c_str ( ) , cell_type . c_str ( ) ) ;
2026-04-21 22:44:21 +02:00
return mod ;
}
}
// We couldn't find the module anywhere. Complain if check is set.
if ( check )
log_error ( " Module `%s' referenced in module `%s' in cell `%s' is not part of the design. \n " ,
2026-05-05 15:23:21 +02:00
cell_type . c_str ( ) , parent . name . c_str ( ) , cell . name . c_str ( ) ) ;
2026-04-21 22:44:21 +02:00
return nullptr ;
}
2026-05-05 15:31:43 +02:00
std : : set < RTLIL : : Module * , IdString : : compare_ptr_by_name < Module > > used_modules ( Design * design , Module * top_mod ) {
std : : set < RTLIL : : Module * , IdString : : compare_ptr_by_name < Module > > used ;
2026-05-05 17:05:23 +02:00
if ( top_mod ! = nullptr ) {
log_header ( design , " Analyzing design hierarchy (used).. \n " ) ;
2026-05-05 15:31:43 +02:00
mark_used ( design , used , top_mod , 0 ) ;
} else {
for ( auto mod : design - > modules ( ) )
used . insert ( mod ) ;
}
return used ;
}
2026-04-21 22:44:21 +02:00
2026-05-05 17:05:23 +02:00
// Delete modules marked as 'to_delete'
void delete_marked_modules ( Design * design ) {
std : : vector < RTLIL : : Module * > modules_to_delete ;
for ( auto mod : design - > modules ( ) ) {
if ( mod - > get_bool_attribute ( ID : : to_delete ) ) {
modules_to_delete . push_back ( mod ) ;
}
}
for ( size_t i = 0 ; i < modules_to_delete . size ( ) ; i + + ) {
design - > remove ( modules_to_delete [ i ] ) ;
}
}
void expand_all_interfaces ( Design * design , Module * & top_mod , bool flag_check , bool flag_simcheck , bool flag_smtcheck , const std : : vector < std : : string > & libdirs ) {
2026-05-05 15:23:21 +02:00
bool did_something = true ;
while ( did_something )
{
did_something = false ;
2026-05-05 15:31:43 +02:00
for ( auto module : used_modules ( design , top_mod ) ) {
2026-05-05 15:23:21 +02:00
if ( expand_module ( design , module , flag_check , flag_simcheck , flag_smtcheck , libdirs ) )
did_something = true ;
}
2026-05-05 17:05:23 +02:00
// The top module might have changed if interface instances have been detected in it
2026-05-05 15:23:21 +02:00
RTLIL : : Module * tmp_top_mod = check_if_top_has_changed ( design , top_mod ) ;
2026-05-05 17:05:23 +02:00
if ( tmp_top_mod ! = nullptr ) {
2026-05-05 15:23:21 +02:00
if ( tmp_top_mod ! = top_mod ) {
top_mod = tmp_top_mod ;
top_mod - > attributes [ ID : : initial_top ] = RTLIL : : Const ( 1 ) ;
did_something = true ;
}
}
2026-05-05 17:05:23 +02:00
delete_marked_modules ( design ) ;
}
}
2026-05-05 15:23:21 +02:00
2026-05-05 17:05:23 +02:00
struct CellArrays {
std : : map < RTLIL : : Cell * , std : : pair < int , int > > cells ;
std : : optional < std : : string > trim_and_register ( Cell * cell ) {
if ( auto a = try_make_array ( cell - > type . str ( ) ) ) {
int idx = atoi ( cell - > type . substr ( a - > pos_idx + 1 , a - > pos_num ) . c_str ( ) ) ;
int num = atoi ( cell - > type . substr ( a - > pos_num + 1 , a - > pos_type ) . c_str ( ) ) ;
cells [ cell ] = std : : pair < int , int > ( idx , num ) ;
return cell - > type . substr ( a - > pos_type + 1 ) ;
2026-05-05 15:23:21 +02:00
}
2026-05-05 17:05:23 +02:00
return std : : nullopt ;
}
void resolve ( ) {
for ( auto & it : cells )
{
Cell * cell = it . first ;
Module * module = cell - > module ;
log_assert ( module ) ;
Design * design = module - > design ;
int idx = it . second . first , num = it . second . second ;
if ( design - > module ( cell - > type ) = = nullptr )
log_error ( " Array cell `%s.%s' of unknown type `%s'. \n " , RTLIL : : id2cstr ( module - > name ) , RTLIL : : id2cstr ( cell - > name ) , RTLIL : : id2cstr ( cell - > type ) ) ;
RTLIL : : Module * mod = design - > module ( cell - > type ) ;
for ( auto & conn : cell - > connections_ ) {
int conn_size = conn . second . size ( ) ;
RTLIL : : IdString portname = conn . first ;
if ( portname . begins_with ( " $ " ) ) {
int port_id = atoi ( portname . substr ( 1 ) . c_str ( ) ) ;
for ( auto wire : mod - > wires ( ) )
if ( wire - > port_id = = port_id ) {
portname = wire - > name ;
break ;
}
}
if ( mod - > wire ( portname ) = = nullptr )
log_error ( " Array cell `%s.%s' connects to unknown port `%s'. \n " , RTLIL : : id2cstr ( module - > name ) , RTLIL : : id2cstr ( cell - > name ) , RTLIL : : id2cstr ( conn . first ) ) ;
int port_size = mod - > wire ( portname ) - > width ;
if ( conn_size = = port_size | | conn_size = = 0 )
continue ;
if ( conn_size ! = port_size * num )
log_error ( " Array cell `%s.%s' has invalid port vs. signal size for port `%s'. \n " , RTLIL : : id2cstr ( module - > name ) , RTLIL : : id2cstr ( cell - > name ) , RTLIL : : id2cstr ( conn . first ) ) ;
conn . second = conn . second . extract ( port_size * idx , port_size ) ;
}
2026-05-05 15:23:21 +02:00
}
}
2026-05-05 17:05:23 +02:00
} ;
2026-04-21 22:44:21 +02:00
bool expand_module ( RTLIL : : Design * design , RTLIL : : Module * module , bool flag_check , bool flag_simcheck , bool flag_smtcheck , const std : : vector < std : : string > & libdirs )
{
bool did_something = false ;
2026-05-05 17:05:23 +02:00
CellArrays cell_arrays ;
2026-04-21 22:44:21 +02:00
std : : string filename ;
bool has_interface_ports = false ;
// If any of the ports are actually interface ports, we will always need to
// reprocess the module:
if ( ! module - > get_bool_attribute ( ID : : interfaces_replaced_in_module ) ) {
for ( auto wire : module - > wires ( ) ) {
if ( ( wire - > port_input | | wire - > port_output ) & & wire - > get_bool_attribute ( ID : : is_interface ) )
has_interface_ports = true ;
}
}
2026-05-05 17:05:23 +02:00
IFModExpander if_mod_expander ( * design , * module ) ;
for ( auto cell : module - > cells ( ) ) {
if ( auto unarrayed_type = cell_arrays . trim_and_register ( cell ) )
cell - > type = * unarrayed_type ;
2026-04-21 22:44:21 +02:00
2026-05-05 17:05:23 +02:00
RTLIL : : Module * submod = design - > module ( cell - > type ) ;
if ( ! submod )
2026-04-21 22:44:21 +02:00
{
2026-05-05 17:05:23 +02:00
submod = get_module ( * design , * cell , * module , flag_check | | flag_simcheck | | flag_smtcheck , libdirs ) ;
2026-04-21 22:44:21 +02:00
// If we still don't have a module, treat the cell as a black box and skip
// it. Otherwise, we either loaded or derived something so should set the
// did_something flag before returning (to ensure we come back and expand
// the thing we just loaded).
2026-05-05 17:05:23 +02:00
if ( submod )
2026-04-21 22:44:21 +02:00
did_something = true ;
continue ;
}
2026-05-05 17:05:23 +02:00
log_assert ( submod ) ;
2026-04-21 22:44:21 +02:00
2026-05-05 17:05:23 +02:00
IFCellExpander if_expander = { if_mod_expander , cell } ;
2026-04-21 22:44:21 +02:00
// Go over all connections and check if any of them are SV
// interfaces.
2026-05-05 17:05:23 +02:00
if_expander . visit_connections ( * submod ) ;
2026-04-21 22:44:21 +02:00
if ( flag_check | | flag_simcheck | | flag_smtcheck )
2026-05-05 17:05:23 +02:00
check_cell_connections ( * module , * cell , * submod ) ;
2026-04-21 22:44:21 +02:00
2026-05-05 17:05:23 +02:00
if ( submod - > get_blackbox_attribute ( ) ) {
if ( flag_simcheck | | ( flag_smtcheck & & ! submod - > get_bool_attribute ( ID : : smtlib2_module ) ) )
2026-04-21 22:44:21 +02:00
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 ( ) ) ;
continue ;
}
// If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here:
if ( if_expander . has_interfaces_not_found ) {
2026-05-05 17:05:23 +02:00
// TODO special defer value instead
2026-04-21 22:44:21 +02:00
did_something = true ; // waiting for interfaces to be handled
continue ;
}
2026-05-05 17:05:23 +02:00
if_expander . rewrite_interface_connections ( ) ;
2026-04-21 22:44:21 +02:00
// 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 & &
2026-05-05 15:23:21 +02:00
( if_expander . interfaces_to_add_to_submodule . size ( ) = = 0 | |
! ( cell - > get_bool_attribute ( ID : : module_not_derived ) ) ) ) {
2026-04-21 22:44:21 +02:00
// 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.
2026-05-05 17:05:23 +02:00
if ( submod - > get_bool_attribute ( ID : : is_interface ) ) {
2026-04-21 22:44:21 +02:00
goto handle_interface_instance ;
}
continue ;
}
2026-05-05 17:05:23 +02:00
cell - > type = submod - > derive ( design ,
2026-04-21 22:44:21 +02:00
cell - > parameters ,
if_expander . interfaces_to_add_to_submodule ,
if_expander . modports_used_in_submodule ) ;
cell - > parameters . clear ( ) ;
did_something = true ;
handle_interface_instance :
// We add all the signals of the interface explicitly to the parent module. This is always needed when we encounter
// an interface instance:
2026-05-05 17:05:23 +02:00
if ( submod - > get_bool_attribute ( ID : : is_interface ) & & cell - > get_bool_attribute ( ID : : module_not_derived ) ) {
2026-04-21 22:44:21 +02:00
cell - > set_bool_attribute ( ID : : is_interface ) ;
RTLIL : : Module * derived_module = design - > module ( cell - > type ) ;
2026-05-05 17:05:23 +02:00
if_mod_expander . interfaces_in_module [ cell - > name ] = derived_module ;
2026-04-21 22:44:21 +02:00
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)
cell - > attributes . erase ( ID : : module_not_derived ) ;
}
// Clear the attribute 'cells_not_processed' such that it can be known that we
// have been through all cells at least once, and that we can know whether
// to flag an error because of interface instances not found:
module - > attributes . erase ( ID : : cells_not_processed ) ;
// If any interface instances or interface ports were found in the module, we need to rederive it completely:
2026-05-05 17:05:23 +02:00
if ( ( if_mod_expander . interfaces_in_module . size ( ) > 0 | | has_interface_ports ) & & ! module - > get_bool_attribute ( ID : : interfaces_replaced_in_module ) ) {
module - > expand_interfaces ( design , if_mod_expander . interfaces_in_module ) ;
2026-04-21 22:44:21 +02:00
return did_something ;
}
// Now that modules have been derived, we may want to reprocess this
// module given the additional available context.
if ( module - > reprocess_if_necessary ( design ) )
return true ;
2026-05-05 17:05:23 +02:00
cell_arrays . resolve ( ) ;
2026-04-21 22:44:21 +02:00
return did_something ;
}
2026-05-05 17:05:23 +02:00
IFModExpander : : IFModExpander ( RTLIL : : Design & design , RTLIL : : Module & m ) : module ( m )
2026-04-21 22:44:21 +02:00
{
2026-05-05 15:23:21 +02:00
// 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 ) ;
}
2026-04-21 22:44:21 +02:00
}
// 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.
2026-05-05 17:05:23 +02:00
void IFCellExpander : : on_missing_interface ( RTLIL : : IdString interface_name )
2026-04-21 22:44:21 +02:00
{
2026-05-05 15:23:21 +02:00
// If there are cells that haven't yet been processed, maybe
// we'll find this interface in the future.
2026-05-05 17:05:23 +02:00
if ( cell - > module - > get_bool_attribute ( ID : : cells_not_processed ) ) {
2026-05-05 15:23:21 +02:00
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 " ,
2026-05-05 17:05:23 +02:00
log_id ( interface_name ) , log_id ( cell - > module ) ) ;
2026-04-21 22:44:21 +02:00
}
// Handle an interface connection from the module
2026-05-05 17:05:23 +02:00
void IFCellExpander : : on_interface ( RTLIL : : Module & submodule ,
RTLIL : : IdString conn_name ,
const RTLIL : : SigSpec & conn_signals )
2026-04-21 22:44:21 +02:00
{
2026-05-05 15:23:21 +02:00
// Check if the connected wire is a potential interface in the parent module
std : : string interface_name_str = conn_signals [ 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
2026-05-05 17:05:23 +02:00
if ( ! cell - > module - > get_bool_attribute ( ID : : interfaces_replaced_in_module ) ) {
2026-05-05 15:23:21 +02:00
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
2026-05-05 17:05:23 +02:00
int nexactmatch = mod_expander . interfaces_in_module . count ( interface_name ) > 0 ;
2026-05-05 15:23:21 +02:00
std : : string interface_name_str2 = interface_name_str + " _inst_from_top_dummy " ;
RTLIL : : IdString interface_name2 = interface_name_str2 ;
2026-05-05 17:05:23 +02:00
int nmatch2 = mod_expander . interfaces_in_module . count ( interface_name2 ) > 0 ;
2026-05-05 15:23:21 +02:00
// 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 ;
2026-05-05 17:05:23 +02:00
RTLIL : : Module * mod_replace_ports = mod_expander . interfaces_in_module . at ( interface_name2 ) ;
2026-05-05 15:23:21 +02:00
// 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 ) ) ;
2026-05-05 17:05:23 +02:00
if ( cell - > module - > wire ( signal_name2 ) = = nullptr ) {
2026-05-05 15:23:21 +02:00
log_error ( " Could not find signal '%s' in '%s' \n " ,
2026-05-05 17:05:23 +02:00
signal_name2 . c_str ( ) , log_id ( cell - > module - > name ) ) ;
2026-05-05 15:23:21 +02:00
}
else {
2026-05-05 17:05:23 +02:00
RTLIL : : Wire * wire_in_parent = cell - > module - > wire ( signal_name2 ) ;
2026-05-05 15:23:21 +02:00
connections_to_add_signal . push_back ( wire_in_parent ) ;
}
}
connections_to_remove . push_back ( conn_name ) ;
2026-05-05 17:05:23 +02:00
interfaces_to_add_to_submodule [ conn_name ] = mod_expander . interfaces_in_module . at ( interface_name2 ) ;
2026-05-05 15:23:21 +02:00
// 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 ;
}
2026-04-21 22:44:21 +02:00
}
// Handle a single connection from the module, making a note to expand
// it if it's an interface connection.
2026-05-05 17:05:23 +02:00
void IFCellExpander : : on_connection ( RTLIL : : Module & submodule ,
RTLIL : : IdString conn_name ,
const RTLIL : : SigSpec & conn_signals )
2026-04-21 22:44:21 +02:00
{
2026-05-05 15:23:21 +02:00
// Does the connection look like an interface
if (
conn_signals . size ( ) ! = 1 | |
conn_signals [ 0 ] . wire = = nullptr | |
conn_signals [ 0 ] . wire - > get_bool_attribute ( ID : : is_interface ) = = false | |
conn_signals [ 0 ] . wire - > name . str ( ) . find ( " $dummywireforinterface " ) ! = 0
)
return ;
// Check if the connection is present as an interface in the sub-module's port list
int id ;
if ( read_id_num ( conn_name , & id ) ) {
/* Interface expansion is incompatible with positional arguments
* during expansion , the port list gets each interface signal
* inserted after the interface itself which means that the argument
* positions in the parent module no longer match .
*
* Supporting this would require expanding the interfaces in the
* parent module , renumbering the arguments to match , and then
* iterating over the ports list to find the matching interface
* ( refactoring on_interface to accept different conn_names on the
* parent and child ) .
*/
log_error ( " Unable to connect `%s' to submodule `%s' with positional interface argument `%s'! \n " ,
2026-05-05 17:05:23 +02:00
cell - > module - > name ,
2026-05-05 15:23:21 +02:00
submodule . name ,
conn_signals [ 0 ] . wire - > name . str ( ) . substr ( 23 )
) ;
} else {
// Lookup connection by name
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.
on_interface ( submodule , conn_name , conn_signals ) ;
2026-04-21 22:44:21 +02:00
}
// Iterate over the connections in a cell, tracking any interface
// connections
2026-05-05 17:05:23 +02:00
void IFCellExpander : : visit_connections ( RTLIL : : Module & submodule )
2026-04-21 22:44:21 +02:00
{
2026-05-05 17:05:23 +02:00
for ( const auto & conn : cell - > connections ( ) ) {
2026-05-05 15:23:21 +02:00
on_connection ( submodule , conn . first , conn . second ) ;
}
2026-04-21 22:44:21 +02:00
}
// Add/remove connections to the cell as necessary, replacing any SV
// interface port connection with the individual signal connections.
2026-05-05 17:05:23 +02:00
void IFCellExpander : : rewrite_interface_connections ( ) const
2026-04-21 22:44:21 +02:00
{
2026-05-05 15:23:21 +02:00
for ( unsigned int i = 0 ; i < connections_to_add_name . size ( ) ; i + + ) {
2026-05-05 17:05:23 +02:00
cell - > connections_ [ connections_to_add_name [ i ] ] = connections_to_add_signal [ i ] ;
2026-05-05 15:23:21 +02:00
}
// Remove the connection for the interface itself:
for ( unsigned int i = 0 ; i < connections_to_remove . size ( ) ; i + + ) {
2026-05-05 17:05:23 +02:00
cell - > connections_ . erase ( connections_to_remove [ i ] ) ;
2026-05-05 15:23:21 +02:00
}
2026-04-21 22:44:21 +02:00
}
} ;
YOSYS_NAMESPACE_END