2018-12-04 18:23:22 -06:00
/*
* yosys - - Yosys Open SYnthesis Suite
*
* Copyright ( C ) 2018 whitequark < whitequark @ whitequark . org >
*
* 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.h"
# include "kernel/sigtools.h"
# include "kernel/modtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
2024-01-15 05:35:02 -06:00
// Type represents the following constraint: Preserve connections to dedicated
// logic cell <cell_type> that has ports connected to LUT inputs. This includes
// the case where both LUT and dedicated logic input are connected to the same
// constant.
2021-07-29 09:55:15 -05:00
struct dlogic_t {
IdString cell_type ;
// LUT input idx -> hard cell's port name
dict < int , IdString > lut_input_port ;
} ;
2018-12-05 06:26:41 -06:00
struct OptLutWorker
2018-12-04 18:23:22 -06:00
{
2021-07-29 09:55:15 -05:00
const std : : vector < dlogic_t > & dlogic ;
2018-12-05 06:26:41 -06:00
RTLIL : : Module * module ;
ModIndex index ;
SigMap sigmap ;
2018-12-04 18:23:22 -06:00
2018-12-05 06:35:27 -06:00
pool < RTLIL : : Cell * > luts ;
dict < RTLIL : : Cell * , int > luts_arity ;
2021-07-29 09:55:15 -05:00
dict < RTLIL : : Cell * , pool < std : : pair < int , RTLIL : : Cell * > > > luts_dlogics ;
2018-12-05 09:26:40 -06:00
dict < RTLIL : : Cell * , pool < int > > luts_dlogic_inputs ;
2018-12-05 06:35:27 -06:00
2019-01-02 02:40:01 -06:00
int eliminated_count = 0 , combined_count = 0 ;
2018-12-05 06:35:27 -06:00
2018-12-05 06:26:41 -06:00
bool evaluate_lut ( RTLIL : : Cell * lut , dict < SigBit , bool > inputs )
2018-12-04 18:23:22 -06:00
{
2019-08-15 16:50:10 -05:00
SigSpec lut_input = sigmap ( lut - > getPort ( ID : : A ) ) ;
2020-04-02 11:51:32 -05:00
int lut_width = lut - > getParam ( ID : : WIDTH ) . as_int ( ) ;
Const lut_table = lut - > getParam ( ID : : LUT ) ;
2018-12-05 06:26:41 -06:00
int lut_index = 0 ;
2018-12-04 18:23:22 -06:00
2018-12-05 06:26:41 -06:00
for ( int i = 0 ; i < lut_width ; i + + )
2018-12-04 18:23:22 -06:00
{
2018-12-05 06:26:41 -06:00
SigBit input = sigmap ( lut_input [ i ] ) ;
if ( inputs . count ( input ) )
2018-12-04 18:23:22 -06:00
{
2018-12-05 06:26:41 -06:00
lut_index | = inputs [ input ] < < i ;
}
else
{
lut_index | = SigSpec ( lut_input [ i ] ) . as_bool ( ) < < i ;
2018-12-04 18:23:22 -06:00
}
}
2018-12-05 13:12:02 -06:00
return lut_table . extract ( lut_index ) . as_bool ( ) ;
2018-12-05 06:26:41 -06:00
}
2018-12-04 18:23:22 -06:00
2018-12-05 06:35:27 -06:00
void show_stats_by_arity ( )
{
dict < int , int > arity_counts ;
2021-07-29 09:55:15 -05:00
std : : vector < int > dlogic_counts ( dlogic . size ( ) ) ;
2018-12-05 06:35:27 -06:00
int max_arity = 0 ;
2018-12-05 09:26:40 -06:00
2018-12-05 06:35:27 -06:00
for ( auto lut_arity : luts_arity )
{
max_arity = max ( max_arity , lut_arity . second ) ;
arity_counts [ lut_arity . second ] + + ;
}
2018-12-05 09:26:40 -06:00
for ( auto & lut_dlogics : luts_dlogics )
{
for ( auto & lut_dlogic : lut_dlogics . second )
{
2021-07-29 09:55:15 -05:00
dlogic_counts [ lut_dlogic . first ] + + ;
2018-12-05 09:26:40 -06:00
}
}
2019-07-31 04:49:48 -05:00
log ( " Number of LUTs: %8d \n " , GetSize ( luts ) ) ;
2018-12-05 06:35:27 -06:00
for ( int arity = 1 ; arity < = max_arity ; arity + + )
{
if ( arity_counts [ arity ] )
2018-12-05 09:26:40 -06:00
log ( " %d-LUT %16d \n " , arity , arity_counts [ arity ] ) ;
}
2021-07-29 09:55:15 -05:00
for ( int i = 0 ; i < GetSize ( dlogic ) ; i + + )
2018-12-05 09:26:40 -06:00
{
2021-07-29 09:55:15 -05:00
log ( " with %-12s (#%d) %4d \n " , dlogic [ i ] . cell_type . c_str ( ) , i , dlogic_counts [ i ] ) ;
2018-12-05 06:35:27 -06:00
}
}
2021-07-29 09:55:15 -05:00
OptLutWorker ( const std : : vector < dlogic_t > & dlogic , RTLIL : : Module * module , int limit ) :
2018-12-05 09:26:40 -06:00
dlogic ( dlogic ) , module ( module ) , index ( module ) , sigmap ( module )
2018-12-04 18:23:22 -06:00
{
2018-12-05 06:26:41 -06:00
log ( " Discovering LUTs. \n " ) ;
for ( auto cell : module - > selected_cells ( ) )
2018-12-04 18:23:22 -06:00
{
2019-08-09 11:58:14 -05:00
if ( cell - > type = = ID ( $ lut ) )
2018-12-04 18:23:22 -06:00
{
2019-08-07 15:12:28 -05:00
if ( cell - > has_keep_attr ( ) )
continue ;
2019-08-15 16:50:10 -05:00
SigBit lut_output = cell - > getPort ( ID : : Y ) ;
2019-08-15 16:51:12 -05:00
if ( lut_output . wire - > get_bool_attribute ( ID : : keep ) )
2019-08-07 15:12:28 -05:00
continue ;
2020-04-02 11:51:32 -05:00
int lut_width = cell - > getParam ( ID : : WIDTH ) . as_int ( ) ;
2019-08-15 16:50:10 -05:00
SigSpec lut_input = cell - > getPort ( ID : : A ) ;
2018-12-05 06:26:41 -06:00
int lut_arity = 0 ;
2018-12-04 18:23:22 -06:00
2019-07-13 11:49:56 -05:00
log_debug ( " Found $lut \\ WIDTH=%d cell %s.%s. \n " , lut_width , log_id ( module ) , log_id ( cell ) ) ;
2018-12-05 09:26:40 -06:00
luts . insert ( cell ) ;
// First, find all dedicated logic we're connected to. This results in an overapproximation
// of such connections.
2021-07-29 09:55:15 -05:00
pool < std : : pair < int , RTLIL : : Cell * > > lut_all_dlogics ;
2018-12-05 09:26:40 -06:00
for ( int i = 0 ; i < lut_width ; i + + )
{
SigBit bit = lut_input [ i ] ;
for ( auto & port : index . query_ports ( bit ) )
{
2021-07-29 09:55:15 -05:00
for ( int j = 0 ; j < GetSize ( dlogic ) ; j + + )
2018-12-05 09:26:40 -06:00
{
2021-07-29 09:55:15 -05:00
if ( dlogic [ j ] . cell_type = = port . cell - > type )
2018-12-05 09:26:40 -06:00
{
2021-07-29 09:55:15 -05:00
if ( port . port = = dlogic [ j ] . lut_input_port . at ( i , IdString ( ) ) )
2018-12-05 09:26:40 -06:00
{
2021-07-29 09:55:15 -05:00
lut_all_dlogics . insert ( { j , port . cell } ) ;
2018-12-05 09:26:40 -06:00
}
}
}
}
}
// Second, make sure that the connection to dedicated logic is legal. If it is not legal,
// it means one of the two things:
// * The connection is spurious. I.e. this is dedicated logic that will be packed
2018-12-07 13:14:07 -06:00
// with some other LUT, and it just happens to be connected to this LUT as well.
2018-12-05 09:26:40 -06:00
// * The connection is illegal.
// In either of these cases, we don't need to concern ourselves with preserving the connection
// between this LUT and this dedicated logic cell.
2021-07-29 09:55:15 -05:00
pool < std : : pair < int , RTLIL : : Cell * > > lut_legal_dlogics ;
2018-12-05 09:26:40 -06:00
pool < int > lut_dlogic_inputs ;
for ( auto lut_dlogic : lut_all_dlogics )
{
2021-07-29 09:55:15 -05:00
auto & dlogic_map = dlogic [ lut_dlogic . first ] . lut_input_port ;
2018-12-05 09:26:40 -06:00
bool legal = true ;
for ( auto & dlogic_conn : dlogic_map )
{
if ( lut_width < = dlogic_conn . first )
{
2021-07-29 09:55:15 -05:00
log_debug ( " LUT has illegal connection to %s cell %s.%s. \n " , lut_dlogic . second - > type . c_str ( ) , log_id ( module ) , log_id ( lut_dlogic . second ) ) ;
2019-07-13 11:49:56 -05:00
log_debug ( " LUT input A[%d] not present. \n " , dlogic_conn . first ) ;
2018-12-05 09:26:40 -06:00
legal = false ;
break ;
}
2024-02-08 10:46:00 -06:00
if ( lut_dlogic . second - > getPort ( dlogic_conn . second ) . size ( ) ! = 1 )
continue ;
if ( sigmap ( lut_input [ dlogic_conn . first ] ) ! = sigmap ( lut_dlogic . second - > getPort ( dlogic_conn . second ) [ 0 ] ) )
2018-12-05 09:26:40 -06:00
{
2021-07-29 09:55:15 -05:00
log_debug ( " LUT has illegal connection to %s cell %s.%s. \n " , lut_dlogic . second - > type . c_str ( ) , log_id ( module ) , log_id ( lut_dlogic . second ) ) ;
log_debug ( " LUT input A[%d] (wire %s) not connected to %s port %s (wire %s). \n " , dlogic_conn . first , log_signal ( lut_input [ dlogic_conn . first ] ) , lut_dlogic . second - > type . c_str ( ) , dlogic_conn . second . c_str ( ) , log_signal ( lut_dlogic . second - > getPort ( dlogic_conn . second ) ) ) ;
2018-12-05 09:26:40 -06:00
legal = false ;
break ;
}
}
if ( legal )
{
2021-07-29 09:55:15 -05:00
log_debug ( " LUT has legal connection to %s cell %s.%s. \n " , lut_dlogic . second - > type . c_str ( ) , log_id ( module ) , log_id ( lut_dlogic . second ) ) ;
2018-12-05 09:26:40 -06:00
lut_legal_dlogics . insert ( lut_dlogic ) ;
for ( auto & dlogic_conn : dlogic_map )
lut_dlogic_inputs . insert ( dlogic_conn . first ) ;
}
}
// Third, determine LUT arity. An n-wide LUT that has k constant inputs and m inputs shared with dedicated
// logic implements an (n-k-m)-ary function.
for ( int i = 0 ; i < lut_width ; i + + )
2018-12-04 18:23:22 -06:00
{
2018-12-05 09:26:40 -06:00
SigBit bit = lut_input [ i ] ;
if ( bit . wire | | lut_dlogic_inputs . count ( i ) )
2018-12-05 06:26:41 -06:00
lut_arity + + ;
2018-12-04 18:23:22 -06:00
}
2019-07-13 11:49:56 -05:00
log_debug ( " Cell implements a %d-LUT. \n " , lut_arity ) ;
2018-12-05 06:26:41 -06:00
luts_arity [ cell ] = lut_arity ;
2018-12-05 09:26:40 -06:00
luts_dlogics [ cell ] = lut_legal_dlogics ;
luts_dlogic_inputs [ cell ] = lut_dlogic_inputs ;
2018-12-05 06:26:41 -06:00
}
}
2018-12-05 06:35:27 -06:00
show_stats_by_arity ( ) ;
2018-12-04 18:23:22 -06:00
2018-12-31 17:53:23 -06:00
log ( " \n " ) ;
log ( " Eliminating LUTs. \n " ) ;
2019-01-02 03:36:32 -06:00
pool < RTLIL : : Cell * > worklist = luts ;
while ( worklist . size ( ) )
2018-12-31 17:53:23 -06:00
{
2019-01-02 02:40:01 -06:00
if ( limit = = 0 )
{
log ( " Limit reached. \n " ) ;
break ;
}
2019-01-02 03:36:32 -06:00
auto lut = worklist . pop ( ) ;
2019-08-15 16:50:10 -05:00
SigSpec lut_input = sigmap ( lut - > getPort ( ID : : A ) ) ;
2018-12-31 17:53:23 -06:00
pool < int > & lut_dlogic_inputs = luts_dlogic_inputs [ lut ] ;
vector < SigBit > lut_inputs ;
for ( auto & bit : lut_input )
{
if ( bit . wire )
lut_inputs . push_back ( sigmap ( bit ) ) ;
}
bool const0_match = true ;
bool const1_match = true ;
vector < bool > input_matches ;
for ( size_t i = 0 ; i < lut_inputs . size ( ) ; i + + )
input_matches . push_back ( true ) ;
for ( int eval = 0 ; eval < 1 < < lut_inputs . size ( ) ; eval + + )
{
dict < SigBit , bool > eval_inputs ;
for ( size_t i = 0 ; i < lut_inputs . size ( ) ; i + + )
eval_inputs [ lut_inputs [ i ] ] = ( eval > > i ) & 1 ;
bool value = evaluate_lut ( lut , eval_inputs ) ;
if ( value ! = 0 )
const0_match = false ;
if ( value ! = 1 )
const1_match = false ;
for ( size_t i = 0 ; i < lut_inputs . size ( ) ; i + + )
{
if ( value ! = eval_inputs [ lut_inputs [ i ] ] )
input_matches [ i ] = false ;
}
}
int input_match = - 1 ;
for ( size_t i = 0 ; i < lut_inputs . size ( ) ; i + + )
if ( input_matches [ i ] )
input_match = i ;
if ( const0_match | | const1_match | | input_match ! = - 1 )
{
2019-07-13 11:49:56 -05:00
log_debug ( " Found redundant cell %s.%s. \n " , log_id ( module ) , log_id ( lut ) ) ;
2018-12-31 17:53:23 -06:00
SigBit value ;
if ( const0_match )
{
2019-07-13 11:49:56 -05:00
log_debug ( " Cell evaluates constant 0. \n " ) ;
2018-12-31 17:53:23 -06:00
value = State : : S0 ;
}
if ( const1_match )
{
2019-07-13 11:49:56 -05:00
log_debug ( " Cell evaluates constant 1. \n " ) ;
2018-12-31 17:53:23 -06:00
value = State : : S1 ;
}
if ( input_match ! = - 1 ) {
2019-07-13 11:49:56 -05:00
log_debug ( " Cell evaluates signal %s. \n " , log_signal ( lut_inputs [ input_match ] ) ) ;
2018-12-31 17:53:23 -06:00
value = lut_inputs [ input_match ] ;
}
if ( lut_dlogic_inputs . size ( ) )
2019-07-13 11:49:56 -05:00
log_debug ( " Not eliminating cell (connected to dedicated logic). \n " ) ;
2018-12-31 17:53:23 -06:00
else
{
2019-08-15 16:50:10 -05:00
SigSpec lut_output = lut - > getPort ( ID : : Y ) ;
2019-01-02 03:36:32 -06:00
for ( auto & port : index . query_ports ( lut_output ) )
{
if ( port . cell ! = lut & & luts . count ( port . cell ) )
worklist . insert ( port . cell ) ;
}
2018-12-31 17:53:23 -06:00
module - > connect ( lut_output , value ) ;
2019-01-02 04:21:58 -06:00
sigmap . add ( lut_output , value ) ;
2018-12-31 17:53:23 -06:00
luts . erase ( lut ) ;
luts_arity . erase ( lut ) ;
luts_dlogics . erase ( lut ) ;
luts_dlogic_inputs . erase ( lut ) ;
2019-01-02 02:40:01 -06:00
2020-12-21 20:23:42 -06:00
module - > remove ( lut ) ;
2019-01-02 02:40:01 -06:00
eliminated_count + + ;
if ( limit > 0 )
limit - - ;
2018-12-31 17:53:23 -06:00
}
}
}
show_stats_by_arity ( ) ;
2018-12-05 06:26:41 -06:00
log ( " \n " ) ;
log ( " Combining LUTs. \n " ) ;
2019-01-02 03:36:32 -06:00
worklist = luts ;
2018-12-05 06:26:41 -06:00
while ( worklist . size ( ) )
{
2018-12-07 10:31:15 -06:00
if ( limit = = 0 )
{
log ( " Limit reached. \n " ) ;
break ;
}
2018-12-05 06:26:41 -06:00
auto lutA = worklist . pop ( ) ;
2019-08-15 16:50:10 -05:00
SigSpec lutA_input = sigmap ( lutA - > getPort ( ID : : A ) ) ;
2024-02-08 10:46:00 -06:00
SigBit lutA_output = sigmap ( lutA - > getPort ( ID : : Y ) [ 0 ] ) ;
2020-04-02 11:51:32 -05:00
int lutA_width = lutA - > getParam ( ID : : WIDTH ) . as_int ( ) ;
2018-12-05 06:26:41 -06:00
int lutA_arity = luts_arity [ lutA ] ;
2018-12-05 09:26:40 -06:00
pool < int > & lutA_dlogic_inputs = luts_dlogic_inputs [ lutA ] ;
2018-12-05 06:26:41 -06:00
2019-08-15 16:50:10 -05:00
auto lutA_output_ports = index . query_ports ( lutA - > getPort ( ID : : Y ) ) ;
2018-12-05 06:26:41 -06:00
if ( lutA_output_ports . size ( ) ! = 2 )
continue ;
2018-12-04 18:23:22 -06:00
2018-12-05 09:26:40 -06:00
for ( auto & port : lutA_output_ports )
2018-12-05 06:26:41 -06:00
{
if ( port . cell = = lutA )
continue ;
if ( luts . count ( port . cell ) )
2018-12-04 18:23:22 -06:00
{
2018-12-05 06:26:41 -06:00
auto lutB = port . cell ;
2019-08-15 16:50:10 -05:00
SigSpec lutB_input = sigmap ( lutB - > getPort ( ID : : A ) ) ;
SigSpec lutB_output = sigmap ( lutB - > getPort ( ID : : Y ) [ 0 ] ) ;
2020-04-02 11:51:32 -05:00
int lutB_width = lutB - > getParam ( ID : : WIDTH ) . as_int ( ) ;
2018-12-05 06:26:41 -06:00
int lutB_arity = luts_arity [ lutB ] ;
2018-12-05 09:26:40 -06:00
pool < int > & lutB_dlogic_inputs = luts_dlogic_inputs [ lutB ] ;
2018-12-05 06:26:41 -06:00
2019-07-13 11:49:56 -05:00
log_debug ( " Found %s.%s (cell A) feeding %s.%s (cell B). \n " , log_id ( module ) , log_id ( lutA ) , log_id ( module ) , log_id ( lutB ) ) ;
2018-12-05 06:26:41 -06:00
2019-08-15 16:50:10 -05:00
if ( index . query_is_output ( lutA - > getPort ( ID : : Y ) ) )
2018-12-07 11:13:52 -06:00
{
2019-07-13 11:49:56 -05:00
log_debug ( " Not combining LUTs (cascade connection feeds module output). \n " ) ;
2018-12-07 11:13:52 -06:00
continue ;
}
2018-12-05 06:26:41 -06:00
pool < SigBit > lutA_inputs ;
pool < SigBit > lutB_inputs ;
for ( auto & bit : lutA_input )
2018-12-04 18:23:22 -06:00
{
2018-12-05 06:26:41 -06:00
if ( bit . wire )
lutA_inputs . insert ( sigmap ( bit ) ) ;
2018-12-04 18:23:22 -06:00
}
2018-12-05 06:26:41 -06:00
for ( auto & bit : lutB_input )
2018-12-04 18:23:22 -06:00
{
2018-12-05 09:26:40 -06:00
if ( bit . wire )
2018-12-05 06:26:41 -06:00
lutB_inputs . insert ( sigmap ( bit ) ) ;
2018-12-04 18:23:22 -06:00
}
2018-12-05 06:26:41 -06:00
pool < SigBit > common_inputs ;
for ( auto & bit : lutA_inputs )
2018-12-04 18:23:22 -06:00
{
2018-12-05 06:26:41 -06:00
if ( lutB_inputs . count ( bit ) )
common_inputs . insert ( bit ) ;
2018-12-04 18:23:22 -06:00
}
2018-12-05 06:26:41 -06:00
int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs . size ( ) ;
2018-12-05 09:26:40 -06:00
if ( lutA_dlogic_inputs . size ( ) )
2019-07-31 04:49:48 -05:00
log_debug ( " Cell A is a %d-LUT with %d dedicated connections. " , lutA_arity , GetSize ( lutA_dlogic_inputs ) ) ;
2018-12-05 09:26:40 -06:00
else
2019-07-13 11:49:56 -05:00
log_debug ( " Cell A is a %d-LUT. " , lutA_arity ) ;
2018-12-05 09:26:40 -06:00
if ( lutB_dlogic_inputs . size ( ) )
2019-07-31 04:49:48 -05:00
log_debug ( " Cell B is a %d-LUT with %d dedicated connections. \n " , lutB_arity , GetSize ( lutB_dlogic_inputs ) ) ;
2018-12-05 09:26:40 -06:00
else
2019-07-13 11:49:56 -05:00
log_debug ( " Cell B is a %d-LUT. \n " , lutB_arity ) ;
2019-07-31 04:49:48 -05:00
log_debug ( " Cells share %d input(s) and can be merged into one %d-LUT. \n " , GetSize ( common_inputs ) , lutM_arity ) ;
2018-12-04 18:23:22 -06:00
2018-12-05 07:14:44 -06:00
const int COMBINE_A = 1 , COMBINE_B = 2 , COMBINE_EITHER = COMBINE_A | COMBINE_B ;
int combine_mask = 0 ;
if ( lutM_arity > lutA_width )
2019-07-13 11:49:56 -05:00
log_debug ( " Not combining LUTs into cell A (combined LUT wider than cell A). \n " ) ;
2018-12-05 09:26:40 -06:00
else if ( lutB_dlogic_inputs . size ( ) > 0 )
2019-07-13 11:49:56 -05:00
log_debug ( " Not combining LUTs into cell A (cell B is connected to dedicated logic). \n " ) ;
2020-04-02 11:51:32 -05:00
else if ( lutB - > get_bool_attribute ( ID : : lut_keep ) )
2019-07-13 11:49:56 -05:00
log_debug ( " Not combining LUTs into cell A (cell B has attribute \\ lut_keep). \n " ) ;
2018-12-05 07:14:44 -06:00
else
combine_mask | = COMBINE_A ;
if ( lutM_arity > lutB_width )
2019-07-13 11:49:56 -05:00
log_debug ( " Not combining LUTs into cell B (combined LUT wider than cell B). \n " ) ;
2018-12-05 09:26:40 -06:00
else if ( lutA_dlogic_inputs . size ( ) > 0 )
2019-07-13 11:49:56 -05:00
log_debug ( " Not combining LUTs into cell B (cell A is connected to dedicated logic). \n " ) ;
2020-04-02 11:51:32 -05:00
else if ( lutA - > get_bool_attribute ( ID : : lut_keep ) )
2019-07-13 11:49:56 -05:00
log_debug ( " Not combining LUTs into cell B (cell A has attribute \\ lut_keep). \n " ) ;
2018-12-05 07:14:44 -06:00
else
combine_mask | = COMBINE_B ;
int combine = combine_mask ;
if ( combine = = COMBINE_EITHER )
{
2019-07-13 11:49:56 -05:00
log_debug ( " Can combine into either cell. \n " ) ;
2018-12-05 07:14:44 -06:00
if ( lutA_arity = = 1 )
{
2019-07-13 11:49:56 -05:00
log_debug ( " Cell A is a buffer or inverter, combining into cell B. \n " ) ;
2018-12-05 07:14:44 -06:00
combine = COMBINE_B ;
}
else if ( lutB_arity = = 1 )
2018-12-05 06:26:41 -06:00
{
2019-07-13 11:49:56 -05:00
log_debug ( " Cell B is a buffer or inverter, combining into cell A. \n " ) ;
2018-12-05 07:14:44 -06:00
combine = COMBINE_A ;
2018-12-05 06:26:41 -06:00
}
2018-12-05 07:14:44 -06:00
else
2018-12-05 06:26:41 -06:00
{
2019-07-13 11:49:56 -05:00
log_debug ( " Arbitrarily combining into cell A. \n " ) ;
2018-12-05 07:14:44 -06:00
combine = COMBINE_A ;
2018-12-05 06:26:41 -06:00
}
}
2018-12-04 18:23:22 -06:00
2018-12-05 06:26:41 -06:00
RTLIL : : Cell * lutM , * lutR ;
pool < SigBit > lutM_inputs , lutR_inputs ;
2018-12-05 09:26:40 -06:00
pool < int > lutM_dlogic_inputs ;
2018-12-05 07:14:44 -06:00
if ( combine = = COMBINE_A )
2018-12-04 18:23:22 -06:00
{
2019-07-13 11:49:56 -05:00
log_debug ( " Combining LUTs into cell A. \n " ) ;
2018-12-05 06:26:41 -06:00
lutM = lutA ;
lutM_inputs = lutA_inputs ;
2018-12-05 09:26:40 -06:00
lutM_dlogic_inputs = lutA_dlogic_inputs ;
2018-12-05 06:26:41 -06:00
lutR = lutB ;
lutR_inputs = lutB_inputs ;
2018-12-04 18:23:22 -06:00
}
2018-12-05 07:14:44 -06:00
else if ( combine = = COMBINE_B )
2018-12-04 18:23:22 -06:00
{
2019-07-13 11:49:56 -05:00
log_debug ( " Combining LUTs into cell B. \n " ) ;
2018-12-05 06:26:41 -06:00
lutM = lutB ;
lutM_inputs = lutB_inputs ;
2018-12-05 09:26:40 -06:00
lutM_dlogic_inputs = lutB_dlogic_inputs ;
2018-12-05 06:26:41 -06:00
lutR = lutA ;
lutR_inputs = lutA_inputs ;
2018-12-04 18:23:22 -06:00
}
else
{
2019-07-13 11:49:56 -05:00
log_debug ( " Cannot combine LUTs. \n " ) ;
2018-12-05 06:26:41 -06:00
continue ;
2018-12-04 18:23:22 -06:00
}
2018-12-05 06:26:41 -06:00
pool < SigBit > lutR_unique ;
for ( auto & bit : lutR_inputs )
2018-12-04 18:23:22 -06:00
{
2018-12-05 06:26:41 -06:00
if ( ! common_inputs . count ( bit ) & & bit ! = lutA_output )
lutR_unique . insert ( bit ) ;
}
2020-04-02 11:51:32 -05:00
int lutM_width = lutM - > getParam ( ID : : WIDTH ) . as_int ( ) ;
2019-08-15 16:50:10 -05:00
SigSpec lutM_input = sigmap ( lutM - > getPort ( ID : : A ) ) ;
2018-12-05 06:26:41 -06:00
std : : vector < SigBit > lutM_new_inputs ;
for ( int i = 0 ; i < lutM_width ; i + + )
{
2018-12-05 09:26:40 -06:00
bool input_unused = false ;
if ( sigmap ( lutM_input [ i ] ) = = lutA_output )
input_unused = true ;
if ( ! lutM_input [ i ] . wire & & ! lutM_dlogic_inputs . count ( i ) )
input_unused = true ;
if ( input_unused & & lutR_unique . size ( ) )
2018-12-05 06:26:41 -06:00
{
SigBit new_input = lutR_unique . pop ( ) ;
2019-07-13 11:49:56 -05:00
log_debug ( " Connecting input %d as %s. \n " , i , log_signal ( new_input ) ) ;
2018-12-05 06:26:41 -06:00
lutM_new_inputs . push_back ( new_input ) ;
}
else if ( sigmap ( lutM_input [ i ] ) = = lutA_output )
{
2019-07-13 11:49:56 -05:00
log_debug ( " Disconnecting cascade input %d. \n " , i ) ;
2018-12-05 06:26:41 -06:00
lutM_new_inputs . push_back ( SigBit ( ) ) ;
}
else
{
2019-07-13 11:49:56 -05:00
log_debug ( " Leaving input %d as %s. \n " , i , log_signal ( lutM_input [ i ] ) ) ;
2018-12-05 06:26:41 -06:00
lutM_new_inputs . push_back ( lutM_input [ i ] ) ;
}
}
log_assert ( lutR_unique . size ( ) = = 0 ) ;
RTLIL : : Const lutM_new_table ( State : : Sx , 1 < < lutM_width ) ;
for ( int eval = 0 ; eval < 1 < < lutM_width ; eval + + )
{
dict < SigBit , bool > eval_inputs ;
for ( size_t i = 0 ; i < lutM_new_inputs . size ( ) ; i + + )
{
eval_inputs [ lutM_new_inputs [ i ] ] = ( eval > > i ) & 1 ;
}
eval_inputs [ lutA_output ] = evaluate_lut ( lutA , eval_inputs ) ;
lutM_new_table [ eval ] = ( RTLIL : : State ) evaluate_lut ( lutB , eval_inputs ) ;
2018-12-04 18:23:22 -06:00
}
2020-04-02 11:51:32 -05:00
log_debug ( " Cell A truth table: %s. \n " , lutA - > getParam ( ID : : LUT ) . as_string ( ) . c_str ( ) ) ;
log_debug ( " Cell B truth table: %s. \n " , lutB - > getParam ( ID : : LUT ) . as_string ( ) . c_str ( ) ) ;
2019-07-13 11:49:56 -05:00
log_debug ( " Merged truth table: %s. \n " , lutM_new_table . as_string ( ) . c_str ( ) ) ;
2018-12-04 18:23:22 -06:00
2020-04-02 11:51:32 -05:00
lutM - > setParam ( ID : : LUT , lutM_new_table ) ;
2019-08-15 16:50:10 -05:00
lutM - > setPort ( ID : : A , lutM_new_inputs ) ;
lutM - > setPort ( ID : : Y , lutB_output ) ;
2018-12-04 18:23:22 -06:00
2018-12-05 06:26:41 -06:00
luts_arity [ lutM ] = lutM_arity ;
luts . erase ( lutR ) ;
luts_arity . erase ( lutR ) ;
2018-12-04 18:23:22 -06:00
2018-12-05 06:26:41 -06:00
worklist . insert ( lutM ) ;
worklist . erase ( lutR ) ;
2018-12-05 06:35:27 -06:00
2020-12-21 20:23:42 -06:00
lutR - > module - > remove ( lutR ) ;
2018-12-05 06:35:27 -06:00
combined_count + + ;
2018-12-07 10:31:15 -06:00
if ( limit > 0 )
limit - - ;
2018-12-05 06:26:41 -06:00
}
2018-12-04 18:23:22 -06:00
}
}
2018-12-05 06:35:27 -06:00
show_stats_by_arity ( ) ;
2018-12-04 18:23:22 -06:00
}
2018-12-05 06:26:41 -06:00
} ;
2018-12-04 18:23:22 -06:00
struct OptLutPass : public Pass {
OptLutPass ( ) : Pass ( " opt_lut " , " optimize LUT cells " ) { }
2020-06-18 18:34:52 -05:00
void help ( ) override
2018-12-04 18:23:22 -06:00
{
2018-12-05 09:26:40 -06:00
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
2018-12-04 18:23:22 -06:00
log ( " \n " ) ;
log ( " opt_lut [options] [selection] \n " ) ;
log ( " \n " ) ;
log ( " This pass combines cascaded $lut cells with unused inputs. \n " ) ;
log ( " \n " ) ;
2024-01-15 05:35:02 -06:00
log ( " -tech ice40 \n " ) ;
log ( " treat the design as a LUT-mapped circuit for the iCE40 architecture \n " ) ;
log ( " and preserve connections to SB_CARRY as appropriate \n " ) ;
log ( " \n " ) ;
2018-12-07 10:31:15 -06:00
log ( " -limit N \n " ) ;
log ( " only perform the first N combines, then stop. useful for debugging. \n " ) ;
log ( " \n " ) ;
2018-12-04 18:23:22 -06:00
}
2020-06-18 18:34:52 -05:00
void execute ( std : : vector < std : : string > args , RTLIL : : Design * design ) override
2018-12-04 18:23:22 -06:00
{
log_header ( design , " Executing OPT_LUT pass (optimize LUTs). \n " ) ;
2021-07-29 09:55:15 -05:00
std : : vector < dlogic_t > dlogic ;
2018-12-07 10:31:15 -06:00
int limit = - 1 ;
2018-12-05 09:26:40 -06:00
2018-12-04 18:23:22 -06:00
size_t argidx ;
for ( argidx = 1 ; argidx < args . size ( ) ; argidx + + )
{
2024-01-15 05:35:02 -06:00
if ( args [ argidx ] = = " -tech " & & argidx + 1 < args . size ( ) )
2018-12-07 10:31:15 -06:00
{
2024-01-15 05:35:02 -06:00
std : : string tech = args [ + + argidx ] ;
if ( tech ! = " ice40 " )
log_cmd_error ( " Unsupported -tech argument: %s \n " , tech . c_str ( ) ) ;
dlogic = { {
ID ( SB_CARRY ) ,
dict < int , IdString > {
std : : make_pair ( 1 , ID ( I0 ) ) ,
std : : make_pair ( 2 , ID ( I1 ) ) ,
std : : make_pair ( 3 , ID ( CI ) )
}
} , {
ID ( SB_CARRY ) ,
dict < int , IdString > {
std : : make_pair ( 3 , ID ( CO ) )
}
} } ;
2018-12-05 09:26:40 -06:00
continue ;
}
2024-01-15 05:35:02 -06:00
if ( args [ argidx ] = = " -limit " & & argidx + 1 < args . size ( ) ) {
2018-12-07 10:31:15 -06:00
limit = atoi ( args [ + + argidx ] . c_str ( ) ) ;
continue ;
}
2018-12-04 18:23:22 -06:00
break ;
}
extra_args ( args , argidx , design ) ;
2019-01-02 02:40:01 -06:00
int eliminated_count = 0 , combined_count = 0 ;
2018-12-04 18:23:22 -06:00
for ( auto module : design - > selected_modules ( ) )
2018-12-05 06:26:41 -06:00
{
2019-01-02 02:40:01 -06:00
OptLutWorker worker ( dlogic , module , limit - eliminated_count - combined_count ) ;
eliminated_count + = worker . eliminated_count ;
combined_count + = worker . combined_count ;
2018-12-05 06:26:41 -06:00
}
2019-01-02 02:40:01 -06:00
if ( eliminated_count )
design - > scratchpad_set_bool ( " opt.did_something " , true ) ;
if ( combined_count )
2018-12-05 06:35:27 -06:00
design - > scratchpad_set_bool ( " opt.did_something " , true ) ;
log ( " \n " ) ;
2019-01-02 02:40:01 -06:00
log ( " Eliminated %d LUTs. \n " , eliminated_count ) ;
log ( " Combined %d LUTs. \n " , combined_count ) ;
2018-12-04 18:23:22 -06:00
}
} OptLutPass ;
PRIVATE_NAMESPACE_END