2014-08-03 15:02:05 +02:00
/*
* yosys - - Yosys Open SYnthesis Suite
*
2021-06-08 00:39:36 +02:00
* Copyright ( C ) 2012 Claire Xenia Wolf < claire @ yosyshq . com >
2015-07-02 11:14:30 +02:00
*
2014-08-03 15:02:05 +02:00
* 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 .
2015-07-02 11:14:30 +02:00
*
2014-08-03 15:02:05 +02:00
* 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"
2020-07-19 02:19:23 +02:00
# include "kernel/ffinit.h"
2024-12-11 15:35:43 +01:00
# include "kernel/utils.h"
2014-08-03 15:02:05 +02:00
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct WreduceConfig
{
2014-12-26 21:59:41 +01:00
pool < IdString > supported_cell_types ;
2019-05-20 15:36:13 +02:00
bool keepdc = false ;
2022-06-08 20:32:04 +02:00
bool mux_undef = false ;
2014-08-03 15:02:05 +02:00
WreduceConfig ( )
{
2014-12-26 21:59:41 +01:00
supported_cell_types = pool < IdString > ( {
2019-08-09 18:58:14 +02:00
ID ( $ not ) , ID ( $ pos ) , ID ( $ neg ) ,
ID ( $ and ) , ID ( $ or ) , ID ( $ xor ) , ID ( $ xnor ) ,
ID ( $ shl ) , ID ( $ shr ) , ID ( $ sshl ) , ID ( $ sshr ) , ID ( $ shift ) , ID ( $ shiftx ) ,
ID ( $ lt ) , ID ( $ le ) , ID ( $ eq ) , ID ( $ ne ) , ID ( $ eqx ) , ID ( $ nex ) , ID ( $ ge ) , ID ( $ gt ) ,
2020-04-21 12:51:58 +02:00
ID ( $ add ) , ID ( $ sub ) , ID ( $ mul ) , // ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
2019-08-09 18:58:14 +02:00
ID ( $ mux ) , ID ( $ pmux ) ,
2020-04-09 00:26:17 +02:00
ID ( $ dff ) , ID ( $ dffe ) , ID ( $ adff ) , ID ( $ adffe ) , ID ( $ sdff ) , ID ( $ sdffe ) , ID ( $ sdffce ) ,
ID ( $ dlatch ) , ID ( $ adlatch ) ,
2014-10-14 23:10:53 +02:00
} ) ;
2014-08-03 15:02:05 +02:00
}
} ;
struct WreduceWorker
{
WreduceConfig * config ;
Module * module ;
ModIndex mi ;
std : : set < Cell * , IdString : : compare_ptr_by_name < Cell > > work_queue_cells ;
std : : set < SigBit > work_queue_bits ;
2015-10-15 14:57:28 +02:00
pool < SigBit > keep_bits ;
2020-07-19 02:19:23 +02:00
FfInitVals initvals ;
2014-08-03 15:02:05 +02:00
WreduceWorker ( WreduceConfig * config , Module * module ) :
config ( config ) , module ( module ) , mi ( module ) { }
2014-08-05 19:01:41 +02:00
void run_cell_mux ( Cell * cell )
2014-08-05 12:49:53 +02:00
{
2014-08-05 13:11:04 +02:00
// Reduce size of MUX if inputs agree on a value for a bit or a output bit is unused
2019-08-15 23:50:10 +02:00
SigSpec sig_a = mi . sigmap ( cell - > getPort ( ID : : A ) ) ;
SigSpec sig_b = mi . sigmap ( cell - > getPort ( ID : : B ) ) ;
2020-04-02 18:51:32 +02:00
SigSpec sig_s = mi . sigmap ( cell - > getPort ( ID : : S ) ) ;
2019-08-15 23:50:10 +02:00
SigSpec sig_y = mi . sigmap ( cell - > getPort ( ID : : Y ) ) ;
2014-08-05 12:49:53 +02:00
std : : vector < SigBit > bits_removed ;
2015-10-24 13:44:35 +02:00
if ( sig_y . has_const ( ) )
return ;
2014-10-10 16:59:44 +02:00
for ( int i = GetSize ( sig_y ) - 1 ; i > = 0 ; i - - )
2014-08-05 12:49:53 +02:00
{
auto info = mi . query ( sig_y [ i ] ) ;
2015-10-15 14:57:28 +02:00
if ( ! info - > is_output & & GetSize ( info - > ports ) < = 1 & & ! keep_bits . count ( mi . sigmap ( sig_y [ i ] ) ) ) {
2019-08-16 21:36:45 +02:00
bits_removed . push_back ( State : : Sx ) ;
2014-08-05 12:49:53 +02:00
continue ;
}
SigBit ref = sig_a [ i ] ;
2014-10-10 16:59:44 +02:00
for ( int k = 0 ; k < GetSize ( sig_s ) ; k + + ) {
2022-06-08 20:32:04 +02:00
if ( ( config - > keepdc | | ! config - > mux_undef | | ( ref ! = State : : Sx & & sig_b [ k * GetSize ( sig_a ) + i ] ! = State : : Sx ) ) & & ref ! = sig_b [ k * GetSize ( sig_a ) + i ] )
2014-08-05 12:49:53 +02:00
goto no_match_ab ;
2019-08-16 21:36:45 +02:00
if ( sig_b [ k * GetSize ( sig_a ) + i ] ! = State : : Sx )
2014-10-10 16:59:44 +02:00
ref = sig_b [ k * GetSize ( sig_a ) + i ] ;
2014-08-05 12:49:53 +02:00
}
if ( 0 )
no_match_ab :
break ;
bits_removed . push_back ( ref ) ;
}
2014-08-05 13:11:04 +02:00
if ( bits_removed . empty ( ) )
2014-08-05 19:01:41 +02:00
return ;
2014-08-05 12:49:53 +02:00
2014-08-05 13:11:04 +02:00
SigSpec sig_removed ;
2014-10-10 16:59:44 +02:00
for ( int i = GetSize ( bits_removed ) - 1 ; i > = 0 ; i - - )
2020-03-13 16:17:39 +01:00
sig_removed . append ( bits_removed [ i ] ) ;
2014-08-05 12:49:53 +02:00
2014-10-10 16:59:44 +02:00
if ( GetSize ( bits_removed ) = = GetSize ( sig_y ) ) {
2014-08-05 19:01:41 +02:00
log ( " Removed cell %s.%s (%s). \n " , log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
module - > connect ( sig_y , sig_removed ) ;
module - > remove ( cell ) ;
return ;
}
2014-08-05 13:11:04 +02:00
log ( " Removed top %d bits (of %d) from mux cell %s.%s (%s). \n " ,
2014-10-10 16:59:44 +02:00
GetSize ( sig_removed ) , GetSize ( sig_y ) , log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
2014-08-05 12:49:53 +02:00
2014-10-10 16:59:44 +02:00
int n_removed = GetSize ( sig_removed ) ;
int n_kept = GetSize ( sig_y ) - GetSize ( sig_removed ) ;
2014-08-05 12:49:53 +02:00
2014-08-05 13:11:04 +02:00
SigSpec new_work_queue_bits ;
new_work_queue_bits . append ( sig_a . extract ( n_kept , n_removed ) ) ;
new_work_queue_bits . append ( sig_y . extract ( n_kept , n_removed ) ) ;
2014-08-05 12:49:53 +02:00
2014-08-05 13:11:04 +02:00
SigSpec new_sig_a = sig_a . extract ( 0 , n_kept ) ;
SigSpec new_sig_y = sig_y . extract ( 0 , n_kept ) ;
SigSpec new_sig_b ;
2014-08-05 12:49:53 +02:00
2014-10-10 16:59:44 +02:00
for ( int k = 0 ; k < GetSize ( sig_s ) ; k + + ) {
new_sig_b . append ( sig_b . extract ( k * GetSize ( sig_a ) , n_kept ) ) ;
new_work_queue_bits . append ( sig_b . extract ( k * GetSize ( sig_a ) + n_kept , n_removed ) ) ;
2014-08-05 13:11:04 +02:00
}
2014-08-05 12:49:53 +02:00
2014-08-05 13:11:04 +02:00
for ( auto bit : new_work_queue_bits )
work_queue_bits . insert ( bit ) ;
2014-08-05 12:49:53 +02:00
2019-08-15 23:50:10 +02:00
cell - > setPort ( ID : : A , new_sig_a ) ;
cell - > setPort ( ID : : B , new_sig_b ) ;
cell - > setPort ( ID : : Y , new_sig_y ) ;
2014-08-05 13:11:04 +02:00
cell - > fixup_parameters ( ) ;
module - > connect ( sig_y . extract ( n_kept , n_removed ) , sig_removed ) ;
2014-08-05 12:49:53 +02:00
}
2019-02-20 16:36:42 +01:00
void run_cell_dff ( Cell * cell )
{
// Reduce size of FF if inputs are just sign/zero extended or output bit is not used
2020-04-02 18:51:32 +02:00
SigSpec sig_d = mi . sigmap ( cell - > getPort ( ID : : D ) ) ;
SigSpec sig_q = mi . sigmap ( cell - > getPort ( ID : : Q ) ) ;
2020-04-09 00:26:17 +02:00
bool has_reset = false ;
2020-07-19 02:19:23 +02:00
Const initval = initvals ( sig_q ) , rst_value ;
2019-02-20 16:36:42 +01:00
int width_before = GetSize ( sig_q ) ;
if ( width_before = = 0 )
return ;
2020-04-02 18:51:32 +02:00
if ( cell - > parameters . count ( ID : : ARST_VALUE ) ) {
2020-04-09 00:26:17 +02:00
rst_value = cell - > parameters [ ID : : ARST_VALUE ] ;
has_reset = true ;
} else if ( cell - > parameters . count ( ID : : SRST_VALUE ) ) {
rst_value = cell - > parameters [ ID : : SRST_VALUE ] ;
has_reset = true ;
2019-11-14 19:43:15 +01:00
}
2019-02-20 16:36:42 +01:00
bool zero_ext = sig_d [ GetSize ( sig_d ) - 1 ] = = State : : S0 ;
bool sign_ext = ! zero_ext ;
for ( int i = GetSize ( sig_q ) - 1 ; i > = 0 ; i - - )
{
2022-07-20 11:31:06 +02:00
if ( zero_ext & & sig_d [ i ] = = State : : S0 & & ( initval [ i ] = = State : : S0 | | ( ! config - > keepdc & & initval [ i ] = = State : : Sx ) ) & &
( ! has_reset | | i > = GetSize ( rst_value ) | | rst_value [ i ] = = State : : S0 | | ( ! config - > keepdc & & rst_value [ i ] = = State : : Sx ) ) ) {
2019-02-20 16:36:42 +01:00
module - > connect ( sig_q [ i ] , State : : S0 ) ;
2020-07-19 02:19:23 +02:00
initvals . remove_init ( sig_q [ i ] ) ;
2019-02-20 16:36:42 +01:00
sig_d . remove ( i ) ;
sig_q . remove ( i ) ;
continue ;
}
2022-07-20 11:31:06 +02:00
if ( sign_ext & & i > 0 & & sig_d [ i ] = = sig_d [ i - 1 ] & & initval [ i ] = = initval [ i - 1 ] & & ( ! config - > keepdc | | initval [ i ] ! = State : : Sx ) & &
( ! has_reset | | i > = GetSize ( rst_value ) | | ( rst_value [ i ] = = rst_value [ i - 1 ] & & ( ! config - > keepdc | | rst_value [ i ] ! = State : : Sx ) ) ) ) {
2019-02-20 16:36:42 +01:00
module - > connect ( sig_q [ i ] , sig_q [ i - 1 ] ) ;
2020-07-19 02:19:23 +02:00
initvals . remove_init ( sig_q [ i ] ) ;
2019-02-20 16:36:42 +01:00
sig_d . remove ( i ) ;
sig_q . remove ( i ) ;
continue ;
}
auto info = mi . query ( sig_q [ i ] ) ;
2019-04-30 22:20:45 +02:00
if ( info = = nullptr )
return ;
2019-03-01 02:24:46 +01:00
if ( ! info - > is_output & & GetSize ( info - > ports ) = = 1 & & ! keep_bits . count ( mi . sigmap ( sig_q [ i ] ) ) ) {
2020-07-19 02:19:23 +02:00
initvals . remove_init ( sig_q [ i ] ) ;
2019-02-20 16:36:42 +01:00
sig_d . remove ( i ) ;
sig_q . remove ( i ) ;
zero_ext = false ;
sign_ext = false ;
continue ;
}
break ;
}
if ( width_before = = GetSize ( sig_q ) )
return ;
if ( GetSize ( sig_q ) = = 0 ) {
log ( " Removed cell %s.%s (%s). \n " , log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
2019-03-01 02:24:46 +01:00
module - > remove ( cell ) ;
2019-02-20 16:36:42 +01:00
return ;
}
2019-03-01 02:24:46 +01:00
log ( " Removed top %d bits (of %d) from FF cell %s.%s (%s). \n " , width_before - GetSize ( sig_q ) , width_before ,
2019-02-20 16:36:42 +01:00
log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
for ( auto bit : sig_d )
work_queue_bits . insert ( bit ) ;
for ( auto bit : sig_q )
work_queue_bits . insert ( bit ) ;
2019-02-22 19:28:28 +01:00
// Narrow ARST_VALUE parameter to new size.
2020-04-02 18:51:32 +02:00
if ( cell - > parameters . count ( ID : : ARST_VALUE ) ) {
2024-10-09 19:39:45 +02:00
rst_value . bits ( ) . resize ( GetSize ( sig_q ) ) ;
2020-04-09 00:26:17 +02:00
cell - > setParam ( ID : : ARST_VALUE , rst_value ) ;
} else if ( cell - > parameters . count ( ID : : SRST_VALUE ) ) {
2024-10-09 19:39:45 +02:00
rst_value . bits ( ) . resize ( GetSize ( sig_q ) ) ;
2020-04-09 00:26:17 +02:00
cell - > setParam ( ID : : SRST_VALUE , rst_value ) ;
2019-02-22 19:28:28 +01:00
}
2020-04-02 18:51:32 +02:00
cell - > setPort ( ID : : D , sig_d ) ;
cell - > setPort ( ID : : Q , sig_q ) ;
2019-02-20 16:36:42 +01:00
cell - > fixup_parameters ( ) ;
}
2014-08-05 19:01:41 +02:00
void run_reduce_inport ( Cell * cell , char port , int max_port_size , bool & port_signed , bool & did_something )
2014-08-03 15:02:05 +02:00
{
2014-08-05 19:01:41 +02:00
port_signed = cell - > getParam ( stringf ( " \\ %c_SIGNED " , port ) ) . as_bool ( ) ;
2014-08-03 15:02:05 +02:00
SigSpec sig = mi . sigmap ( cell - > getPort ( stringf ( " \\ %c " , port ) ) ) ;
2019-08-09 18:58:14 +02:00
if ( port = = ' B ' & & cell - > type . in ( ID ( $ shl ) , ID ( $ shr ) , ID ( $ sshl ) , ID ( $ sshr ) ) )
2025-04-04 23:27:38 +02:00
port_signed = true ; // SILIMATE: HAD TO CHANGE THIS TO RESOLVE CUSTOMER_SHL_BLOWUP_2. REAL FIX IS TO MAKE SURE THAT YOSYS DOES THE RIGHT THING WITH SIGNEDNESS.
2014-08-05 13:11:04 +02:00
2014-08-03 15:02:05 +02:00
int bits_removed = 0 ;
2014-10-10 16:59:44 +02:00
if ( GetSize ( sig ) > max_port_size ) {
bits_removed = GetSize ( sig ) - max_port_size ;
2014-08-05 13:11:04 +02:00
for ( auto bit : sig . extract ( max_port_size , bits_removed ) )
work_queue_bits . insert ( bit ) ;
sig = sig . extract ( 0 , max_port_size ) ;
}
2014-08-05 19:01:41 +02:00
if ( port_signed ) {
2014-10-10 16:59:44 +02:00
while ( GetSize ( sig ) > 1 & & sig [ GetSize ( sig ) - 1 ] = = sig [ GetSize ( sig ) - 2 ] )
work_queue_bits . insert ( sig [ GetSize ( sig ) - 1 ] ) , sig . remove ( GetSize ( sig ) - 1 ) , bits_removed + + ;
2014-08-03 15:02:05 +02:00
} else {
2019-08-16 21:36:45 +02:00
while ( GetSize ( sig ) > 1 & & sig [ GetSize ( sig ) - 1 ] = = State : : S0 )
2014-10-10 16:59:44 +02:00
work_queue_bits . insert ( sig [ GetSize ( sig ) - 1 ] ) , sig . remove ( GetSize ( sig ) - 1 ) , bits_removed + + ;
2014-08-03 15:02:05 +02:00
}
2014-08-05 19:01:41 +02:00
if ( bits_removed ) {
log ( " Removed top %d bits (of %d) from port %c of cell %s.%s (%s). \n " ,
2014-10-10 16:59:44 +02:00
bits_removed , GetSize ( sig ) + bits_removed , port , log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
2014-08-05 19:01:41 +02:00
cell - > setPort ( stringf ( " \\ %c " , port ) , sig ) ;
did_something = true ;
}
2014-08-03 15:02:05 +02:00
}
2024-12-16 12:56:44 +01:00
int reduced_opsize ( const SigSpec & inp , bool signed_ )
{
int size = GetSize ( inp ) ;
if ( signed_ ) {
while ( size > = 2 & & inp [ size - 1 ] = = inp [ size - 2 ] )
size - - ;
} else {
while ( size > = 1 & & inp [ size - 1 ] = = State : : S0 )
size - - ;
}
return size ;
}
2014-08-05 19:01:41 +02:00
void run_cell ( Cell * cell )
2014-08-03 15:02:05 +02:00
{
2014-08-05 13:11:04 +02:00
bool did_something = false ;
2025-01-30 12:01:30 +01:00
if ( ! config - > supported_cell_types . count ( cell - > type ) )
2014-08-05 19:01:41 +02:00
return ;
2014-08-05 13:11:04 +02:00
2019-08-09 18:58:14 +02:00
if ( cell - > type . in ( ID ( $ mux ) , ID ( $ pmux ) ) )
2014-08-05 13:11:04 +02:00
return run_cell_mux ( cell ) ;
2020-04-09 00:26:17 +02:00
if ( cell - > type . in ( ID ( $ dff ) , ID ( $ dffe ) , ID ( $ adff ) , ID ( $ adffe ) , ID ( $ sdff ) , ID ( $ sdffe ) , ID ( $ sdffce ) , ID ( $ dlatch ) , ID ( $ adlatch ) ) )
2019-02-20 16:36:42 +01:00
return run_cell_dff ( cell ) ;
2019-08-15 23:50:10 +02:00
SigSpec sig = mi . sigmap ( cell - > getPort ( ID : : Y ) ) ;
2015-10-24 13:44:35 +02:00
if ( sig . has_const ( ) )
return ;
2014-08-03 15:02:05 +02:00
2014-08-05 13:11:04 +02:00
// Reduce size of ports A and B based on constant input bits and size of output port
2019-08-15 23:50:10 +02:00
int max_port_a_size = cell - > hasPort ( ID : : A ) ? GetSize ( cell - > getPort ( ID : : A ) ) : - 1 ;
int max_port_b_size = cell - > hasPort ( ID : : B ) ? GetSize ( cell - > getPort ( ID : : B ) ) : - 1 ;
2014-08-05 13:11:04 +02:00
2019-08-09 18:58:14 +02:00
if ( cell - > type . in ( ID ( $ not ) , ID ( $ pos ) , ID ( $ neg ) , ID ( $ and ) , ID ( $ or ) , ID ( $ xor ) , ID ( $ add ) , ID ( $ sub ) ) ) {
2015-10-25 19:30:49 +01:00
max_port_a_size = min ( max_port_a_size , GetSize ( sig ) ) ;
max_port_b_size = min ( max_port_b_size , GetSize ( sig ) ) ;
2014-08-05 12:49:53 +02:00
}
2014-08-05 19:01:41 +02:00
bool port_a_signed = false ;
bool port_b_signed = false ;
2025-01-03 12:54:34 +01:00
// For some operations if the output is no wider than either of the inputs
// we are free to choose the signedness of the operands
2024-12-16 12:56:44 +01:00
if ( cell - > type . in ( ID ( $ mul ) , ID ( $ add ) , ID ( $ sub ) ) & &
max_port_a_size = = GetSize ( sig ) & &
max_port_b_size = = GetSize ( sig ) ) {
SigSpec sig_a = mi . sigmap ( cell - > getPort ( ID : : A ) ) , sig_b = mi . sigmap ( cell - > getPort ( ID : : B ) ) ;
// Remove top bits from sig_a and sig_b which are not visible on the output
sig_a . extend_u0 ( max_port_a_size ) ;
sig_b . extend_u0 ( max_port_b_size ) ;
2025-01-03 12:54:34 +01:00
int signed_cost , unsigned_cost ;
2024-12-16 12:56:44 +01:00
if ( cell - > type = = ID ( $ mul ) ) {
2025-01-03 12:54:34 +01:00
signed_cost = reduced_opsize ( sig_a , true ) * reduced_opsize ( sig_b , true ) ;
unsigned_cost = reduced_opsize ( sig_a , false ) * reduced_opsize ( sig_b , false ) ;
2024-12-16 12:56:44 +01:00
} else {
2025-01-03 12:54:34 +01:00
signed_cost = max ( reduced_opsize ( sig_a , true ) , reduced_opsize ( sig_b , true ) ) ;
unsigned_cost = max ( reduced_opsize ( sig_a , false ) , reduced_opsize ( sig_b , false ) ) ;
2024-12-16 12:56:44 +01:00
}
2025-01-03 12:54:34 +01:00
if ( ! port_a_signed & & ! port_b_signed & & signed_cost < unsigned_cost ) {
2024-12-16 12:56:44 +01:00
log ( " Converting cell %s.%s (%s) from unsigned to signed. \n " ,
log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
cell - > setParam ( ID : : A_SIGNED , 1 ) ;
cell - > setParam ( ID : : B_SIGNED , 1 ) ;
port_a_signed = true ;
port_b_signed = true ;
did_something = true ;
2025-01-03 12:54:34 +01:00
} else if ( port_a_signed & & port_b_signed & & unsigned_cost < signed_cost ) {
2024-12-16 12:56:44 +01:00
log ( " Converting cell %s.%s (%s) from signed to unsigned. \n " ,
log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
cell - > setParam ( ID : : A_SIGNED , 0 ) ;
cell - > setParam ( ID : : B_SIGNED , 0 ) ;
port_a_signed = false ;
port_b_signed = false ;
did_something = true ;
}
}
2019-08-09 18:58:14 +02:00
if ( max_port_a_size > = 0 & & cell - > type ! = ID ( $ shiftx ) )
2014-08-05 19:01:41 +02:00
run_reduce_inport ( cell , ' A ' , max_port_a_size , port_a_signed , did_something ) ;
2014-08-05 13:11:04 +02:00
if ( max_port_b_size > = 0 )
2014-08-05 19:01:41 +02:00
run_reduce_inport ( cell , ' B ' , max_port_b_size , port_b_signed , did_something ) ;
2014-08-03 15:02:05 +02:00
2019-08-15 23:50:10 +02:00
if ( cell - > hasPort ( ID : : A ) & & cell - > hasPort ( ID : : B ) & & port_a_signed & & port_b_signed ) {
SigSpec sig_a = mi . sigmap ( cell - > getPort ( ID : : A ) ) , sig_b = mi . sigmap ( cell - > getPort ( ID : : B ) ) ;
2015-10-31 13:39:30 +01:00
if ( GetSize ( sig_a ) > 0 & & sig_a [ GetSize ( sig_a ) - 1 ] = = State : : S0 & &
GetSize ( sig_b ) > 0 & & sig_b [ GetSize ( sig_b ) - 1 ] = = State : : S0 ) {
log ( " Converting cell %s.%s (%s) from signed to unsigned. \n " ,
log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
2020-04-02 18:51:32 +02:00
cell - > setParam ( ID : : A_SIGNED , 0 ) ;
cell - > setParam ( ID : : B_SIGNED , 0 ) ;
2015-10-31 13:39:30 +01:00
port_a_signed = false ;
port_b_signed = false ;
did_something = true ;
}
}
2019-08-15 23:50:10 +02:00
if ( cell - > hasPort ( ID : : A ) & & ! cell - > hasPort ( ID : : B ) & & port_a_signed ) {
SigSpec sig_a = mi . sigmap ( cell - > getPort ( ID : : A ) ) ;
2015-10-31 13:39:30 +01:00
if ( GetSize ( sig_a ) > 0 & & sig_a [ GetSize ( sig_a ) - 1 ] = = State : : S0 ) {
log ( " Converting cell %s.%s (%s) from signed to unsigned. \n " ,
log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
2020-04-02 18:51:32 +02:00
cell - > setParam ( ID : : A_SIGNED , 0 ) ;
2015-10-31 13:39:30 +01:00
port_a_signed = false ;
did_something = true ;
}
}
2014-08-03 15:02:05 +02:00
2014-08-05 13:11:04 +02:00
// Reduce size of port Y based on sizes for A and B and unused bits in Y
2014-08-03 15:02:05 +02:00
int bits_removed = 0 ;
2019-08-09 18:58:14 +02:00
if ( port_a_signed & & cell - > type = = ID ( $ shr ) ) {
2014-08-05 19:01:41 +02:00
// do not reduce size of output on $shr cells with signed A inputs
} else {
2014-10-10 16:59:44 +02:00
while ( GetSize ( sig ) > 0 )
2014-08-05 19:01:41 +02:00
{
2018-12-31 16:34:27 +01:00
auto bit = sig [ GetSize ( sig ) - 1 ] ;
if ( keep_bits . count ( bit ) )
break ;
2014-08-03 20:02:42 +02:00
2018-12-31 16:34:27 +01:00
auto info = mi . query ( bit ) ;
2014-10-10 16:59:44 +02:00
if ( info - > is_output | | GetSize ( info - > ports ) > 1 )
2014-08-05 19:01:41 +02:00
break ;
2014-08-03 20:02:42 +02:00
2014-10-10 16:59:44 +02:00
sig . remove ( GetSize ( sig ) - 1 ) ;
2014-08-05 19:01:41 +02:00
bits_removed + + ;
}
2014-08-03 15:02:05 +02:00
}
2019-08-09 18:58:14 +02:00
if ( cell - > type . in ( ID ( $ pos ) , ID ( $ add ) , ID ( $ mul ) , ID ( $ and ) , ID ( $ or ) , ID ( $ xor ) , ID ( $ sub ) ) )
2014-08-03 15:02:05 +02:00
{
2020-04-02 18:51:32 +02:00
bool is_signed = cell - > getParam ( ID : : A_SIGNED ) . as_bool ( ) | | cell - > type = = ID ( $ sub ) ;
2014-08-03 15:02:05 +02:00
int a_size = 0 , b_size = 0 ;
2019-08-15 23:50:10 +02:00
if ( cell - > hasPort ( ID : : A ) ) a_size = GetSize ( cell - > getPort ( ID : : A ) ) ;
if ( cell - > hasPort ( ID : : B ) ) b_size = GetSize ( cell - > getPort ( ID : : B ) ) ;
2014-08-03 15:02:05 +02:00
2015-10-25 19:30:49 +01:00
int max_y_size = max ( a_size , b_size ) ;
2014-08-03 20:02:42 +02:00
2019-08-09 18:58:14 +02:00
if ( cell - > type . in ( ID ( $ add ) , ID ( $ sub ) ) )
2014-08-03 20:02:42 +02:00
max_y_size + + ;
2019-08-09 18:58:14 +02:00
if ( cell - > type = = ID ( $ mul ) )
2014-08-03 20:02:42 +02:00
max_y_size = a_size + b_size ;
2023-08-02 20:10:18 +02:00
max_y_size = std : : max ( max_y_size , 1 ) ;
if ( GetSize ( sig ) > max_y_size ) {
SigSpec extra_bits = sig . extract ( max_y_size , GetSize ( sig ) - max_y_size ) ;
bits_removed + = GetSize ( extra_bits ) ;
sig . remove ( max_y_size , GetSize ( extra_bits ) ) ;
SigBit padbit = is_signed ? sig [ GetSize ( sig ) - 1 ] : State : : S0 ;
module - > connect ( extra_bits , SigSpec ( padbit , GetSize ( extra_bits ) ) ) ;
2014-08-03 15:02:05 +02:00
}
}
2014-10-10 16:59:44 +02:00
if ( GetSize ( sig ) = = 0 ) {
2014-08-05 19:01:41 +02:00
log ( " Removed cell %s.%s (%s). \n " , log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
module - > remove ( cell ) ;
return ;
}
2014-08-03 15:02:05 +02:00
if ( bits_removed ) {
2019-08-07 00:25:50 +02:00
log ( " Removed top %d bits (of %d) from port Y of cell %s.%s (%s). \n " ,
2014-10-10 16:59:44 +02:00
bits_removed , GetSize ( sig ) + bits_removed , log_id ( module ) , log_id ( cell ) , log_id ( cell - > type ) ) ;
2019-08-15 23:50:10 +02:00
cell - > setPort ( ID : : Y , sig ) ;
2014-08-05 13:11:04 +02:00
did_something = true ;
2014-08-03 15:02:05 +02:00
}
2014-08-05 19:01:41 +02:00
if ( did_something ) {
2014-08-05 13:11:04 +02:00
cell - > fixup_parameters ( ) ;
2014-08-05 19:01:41 +02:00
run_cell ( cell ) ;
}
2014-08-03 15:02:05 +02:00
}
2014-08-05 14:47:03 +02:00
static int count_nontrivial_wire_attrs ( RTLIL : : Wire * w )
{
int count = w - > attributes . size ( ) ;
2020-04-02 18:51:32 +02:00
count - = w - > attributes . count ( ID : : src ) ;
count - = w - > attributes . count ( ID : : unused_bits ) ;
2014-08-05 14:47:03 +02:00
return count ;
}
2014-08-03 15:02:05 +02:00
void run ( )
{
2019-03-22 11:42:19 +01:00
// create a copy as mi.sigmap will be updated as we process the module
SigMap init_attr_sigmap = mi . sigmap ;
2020-07-19 02:19:23 +02:00
initvals . set ( & init_attr_sigmap , module ) ;
2019-03-22 11:42:19 +01:00
2019-03-01 02:24:46 +01:00
for ( auto w : module - > wires ( ) ) {
2019-08-15 23:51:12 +02:00
if ( w - > get_bool_attribute ( ID : : keep ) )
2015-10-15 14:57:28 +02:00
for ( auto bit : mi . sigmap ( w ) )
keep_bits . insert ( bit ) ;
2019-03-01 02:24:46 +01:00
}
2015-10-15 14:57:28 +02:00
2014-08-03 15:02:05 +02:00
for ( auto c : module - > selected_cells ( ) )
work_queue_cells . insert ( c ) ;
while ( ! work_queue_cells . empty ( ) )
{
work_queue_bits . clear ( ) ;
for ( auto c : work_queue_cells )
2014-08-05 19:01:41 +02:00
run_cell ( c ) ;
2014-08-03 15:02:05 +02:00
work_queue_cells . clear ( ) ;
for ( auto bit : work_queue_bits )
for ( auto port : mi . query_ports ( bit ) )
2014-08-05 14:47:03 +02:00
if ( module - > selected ( port . cell ) )
work_queue_cells . insert ( port . cell ) ;
}
2015-02-16 09:08:00 +01:00
pool < SigSpec > complete_wires ;
for ( auto w : module - > wires ( ) )
complete_wires . insert ( mi . sigmap ( w ) ) ;
2014-08-05 14:47:03 +02:00
for ( auto w : module - > selected_wires ( ) )
{
int unused_top_bits = 0 ;
if ( w - > port_id > 0 | | count_nontrivial_wire_attrs ( w ) > 0 )
continue ;
2014-10-10 16:59:44 +02:00
for ( int i = GetSize ( w ) - 1 ; i > = 0 ; i - - ) {
2014-08-05 14:47:03 +02:00
SigBit bit ( w , i ) ;
auto info = mi . query ( bit ) ;
2014-10-10 16:59:44 +02:00
if ( info & & ( info - > is_input | | info - > is_output | | GetSize ( info - > ports ) > 0 ) )
2014-08-05 14:47:03 +02:00
break ;
unused_top_bits + + ;
}
2015-02-16 09:08:00 +01:00
if ( unused_top_bits = = 0 | | unused_top_bits = = GetSize ( w ) )
continue ;
if ( complete_wires [ mi . sigmap ( w ) . extract ( 0 , GetSize ( w ) - unused_top_bits ) ] )
continue ;
log ( " Removed top %d bits (of %d) from wire %s.%s. \n " , unused_top_bits , GetSize ( w ) , log_id ( module ) , log_id ( w ) ) ;
2025-04-07 16:35:39 +02:00
Wire * nw = module - > addWire ( module - > uniquify ( IdString ( w - > name . str ( ) + " _wreduce " ) ) , GetSize ( w ) - unused_top_bits ) ;
2015-02-16 09:08:00 +01:00
module - > connect ( nw , SigSpec ( w ) . extract ( 0 , GetSize ( nw ) ) ) ;
module - > swap_names ( w , nw ) ;
2014-08-03 15:02:05 +02:00
}
}
} ;
struct WreducePass : public Pass {
2015-02-17 13:02:16 +01:00
WreducePass ( ) : Pass ( " wreduce " , " reduce the word size of operations if possible " ) { }
2020-06-19 01:34:52 +02:00
void help ( ) override
2014-08-03 15:02:05 +02:00
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log ( " \n " ) ;
log ( " wreduce [options] [selection] \n " ) ;
log ( " \n " ) ;
2014-08-05 14:47:03 +02:00
log ( " This command reduces the word size of operations. For example it will replace \n " ) ;
log ( " the 32 bit adders in the following code with adders of more appropriate widths: \n " ) ;
log ( " \n " ) ;
log ( " module test(input [3:0] a, b, c, output [7:0] y); \n " ) ;
log ( " assign y = a + b + c + 1; \n " ) ;
log ( " endmodule \n " ) ;
2014-08-03 15:02:05 +02:00
log ( " \n " ) ;
2016-08-20 12:52:50 +02:00
log ( " Options: \n " ) ;
log ( " \n " ) ;
log ( " -memx \n " ) ;
log ( " Do not change the width of memory address ports. Use this options in \n " ) ;
log ( " flows that use the 'memory_memx' pass. \n " ) ;
log ( " \n " ) ;
2022-06-08 20:32:04 +02:00
log ( " -mux_undef \n " ) ;
log ( " remove 'undef' inputs from $mux, $pmux and $_MUX_ cells \n " ) ;
log ( " \n " ) ;
2019-05-20 15:36:13 +02:00
log ( " -keepdc \n " ) ;
2019-07-09 19:14:23 +02:00
log ( " Do not optimize explicit don't-care values. \n " ) ;
2019-05-20 15:36:13 +02:00
log ( " \n " ) ;
2014-08-03 15:02:05 +02:00
}
2020-06-19 01:34:52 +02:00
void execute ( std : : vector < std : : string > args , Design * design ) override
2014-08-03 15:02:05 +02:00
{
WreduceConfig config ;
2016-08-20 12:52:50 +02:00
bool opt_memx = false ;
2014-08-03 15:02:05 +02:00
2016-04-21 23:28:37 +02:00
log_header ( design , " Executing WREDUCE pass (reducing word size of cells). \n " ) ;
2014-08-03 15:02:05 +02:00
size_t argidx ;
for ( argidx = 1 ; argidx < args . size ( ) ; argidx + + ) {
2016-08-20 12:52:50 +02:00
if ( args [ argidx ] = = " -memx " ) {
opt_memx = true ;
continue ;
}
2019-05-20 15:36:13 +02:00
if ( args [ argidx ] = = " -keepdc " ) {
config . keepdc = true ;
continue ;
}
2022-06-08 20:32:04 +02:00
if ( args [ argidx ] = = " -mux_undef " ) {
config . mux_undef = true ;
continue ;
}
2014-08-03 15:02:05 +02:00
break ;
}
extra_args ( args , argidx , design ) ;
for ( auto module : design - > selected_modules ( ) )
{
if ( module - > has_processes_warn ( ) )
continue ;
2015-04-13 19:27:49 +02:00
for ( auto c : module - > selected_cells ( ) )
2016-08-19 18:38:25 +02:00
{
2019-08-09 18:58:14 +02:00
if ( c - > type . in ( ID ( $ reduce_and ) , ID ( $ reduce_or ) , ID ( $ reduce_xor ) , ID ( $ reduce_xnor ) , ID ( $ reduce_bool ) ,
ID ( $ lt ) , ID ( $ le ) , ID ( $ eq ) , ID ( $ ne ) , ID ( $ eqx ) , ID ( $ nex ) , ID ( $ ge ) , ID ( $ gt ) ,
2019-08-15 23:50:10 +02:00
ID ( $ logic_not ) , ID ( $ logic_and ) , ID ( $ logic_or ) ) & & GetSize ( c - > getPort ( ID : : Y ) ) > 1 ) {
SigSpec sig = c - > getPort ( ID : : Y ) ;
2015-10-24 13:44:35 +02:00
if ( ! sig . has_const ( ) ) {
2019-08-15 23:50:10 +02:00
c - > setPort ( ID : : Y , sig [ 0 ] ) ;
2020-04-02 18:51:32 +02:00
c - > setParam ( ID : : Y_WIDTH , 1 ) ;
2015-10-24 13:44:35 +02:00
sig . remove ( 0 ) ;
module - > connect ( sig , Const ( 0 , GetSize ( sig ) ) ) ;
}
2015-04-13 19:27:49 +02:00
}
2019-04-30 11:25:15 +02:00
2020-04-21 12:51:58 +02:00
if ( c - > type . in ( ID ( $ div ) , ID ( $ mod ) , ID ( $ divfloor ) , ID ( $ modfloor ) , ID ( $ pow ) ) )
2019-04-30 11:25:15 +02:00
{
2019-08-15 23:50:10 +02:00
SigSpec A = c - > getPort ( ID : : A ) ;
2019-04-30 11:25:15 +02:00
int original_a_width = GetSize ( A ) ;
2020-04-02 18:51:32 +02:00
if ( c - > getParam ( ID : : A_SIGNED ) . as_bool ( ) ) {
2019-04-30 11:25:15 +02:00
while ( GetSize ( A ) > 1 & & A [ GetSize ( A ) - 1 ] = = State : : S0 & & A [ GetSize ( A ) - 2 ] = = State : : S0 )
A . remove ( GetSize ( A ) - 1 , 1 ) ;
} else {
while ( GetSize ( A ) > 0 & & A [ GetSize ( A ) - 1 ] = = State : : S0 )
A . remove ( GetSize ( A ) - 1 , 1 ) ;
}
if ( original_a_width ! = GetSize ( A ) ) {
log ( " Removed top %d bits (of %d) from port A of cell %s.%s (%s). \n " ,
original_a_width - GetSize ( A ) , original_a_width , log_id ( module ) , log_id ( c ) , log_id ( c - > type ) ) ;
2019-08-15 23:50:10 +02:00
c - > setPort ( ID : : A , A ) ;
2020-04-02 18:51:32 +02:00
c - > setParam ( ID : : A_WIDTH , GetSize ( A ) ) ;
2019-04-30 11:25:15 +02:00
}
2019-08-15 23:50:10 +02:00
SigSpec B = c - > getPort ( ID : : B ) ;
2019-04-30 11:25:15 +02:00
int original_b_width = GetSize ( B ) ;
2020-04-02 18:51:32 +02:00
if ( c - > getParam ( ID : : B_SIGNED ) . as_bool ( ) ) {
2019-04-30 11:25:15 +02:00
while ( GetSize ( B ) > 1 & & B [ GetSize ( B ) - 1 ] = = State : : S0 & & B [ GetSize ( B ) - 2 ] = = State : : S0 )
B . remove ( GetSize ( B ) - 1 , 1 ) ;
} else {
while ( GetSize ( B ) > 0 & & B [ GetSize ( B ) - 1 ] = = State : : S0 )
B . remove ( GetSize ( B ) - 1 , 1 ) ;
}
if ( original_b_width ! = GetSize ( B ) ) {
log ( " Removed top %d bits (of %d) from port B of cell %s.%s (%s). \n " ,
original_b_width - GetSize ( B ) , original_b_width , log_id ( module ) , log_id ( c ) , log_id ( c - > type ) ) ;
2019-08-15 23:50:10 +02:00
c - > setPort ( ID : : B , B ) ;
2020-04-02 18:51:32 +02:00
c - > setParam ( ID : : B_WIDTH , GetSize ( B ) ) ;
2019-04-30 11:25:15 +02:00
}
}
2021-05-27 20:54:29 +02:00
if ( ! opt_memx & & c - > type . in ( ID ( $ memrd ) , ID ( $ memrd_v2 ) , ID ( $ memwr ) , ID ( $ memwr_v2 ) , ID ( $ meminit ) , ID ( $ meminit_v2 ) ) ) {
2020-04-02 18:51:32 +02:00
IdString memid = c - > getParam ( ID : : MEMID ) . decode_string ( ) ;
2016-08-19 18:38:25 +02:00
RTLIL : : Memory * mem = module - > memories . at ( memid ) ;
2016-08-20 12:52:50 +02:00
if ( mem - > start_offset > = 0 ) {
2020-04-02 18:51:32 +02:00
int cur_addrbits = c - > getParam ( ID : : ABITS ) . as_int ( ) ;
2016-08-20 12:52:50 +02:00
int max_addrbits = ceil_log2 ( mem - > start_offset + mem - > size ) ;
2016-08-19 18:38:25 +02:00
if ( cur_addrbits > max_addrbits ) {
log ( " Removed top %d address bits (of %d) from memory %s port %s.%s (%s). \n " ,
cur_addrbits - max_addrbits , cur_addrbits ,
2019-08-09 18:58:14 +02:00
c - > type = = ID ( $ memrd ) ? " read " : c - > type = = ID ( $ memwr ) ? " write " : " init " ,
2016-08-19 18:38:25 +02:00
log_id ( module ) , log_id ( c ) , log_id ( memid ) ) ;
2020-04-02 18:51:32 +02:00
c - > setParam ( ID : : ABITS , max_addrbits ) ;
c - > setPort ( ID : : ADDR , c - > getPort ( ID : : ADDR ) . extract ( 0 , max_addrbits ) ) ;
2016-08-19 18:38:25 +02:00
}
}
}
}
2015-04-13 19:27:49 +02:00
2014-08-03 15:02:05 +02:00
WreduceWorker worker ( & config , module ) ;
worker . run ( ) ;
}
}
} WreducePass ;
PRIVATE_NAMESPACE_END