2024-05-01 05:48:04 -05:00
/*
* yosys - - Yosys Open SYnthesis Suite
*
* Copyright ( C ) 2024 Emily Schmidt < emily @ yosyshq . com >
2024-08-13 08:04:04 -05:00
* Copyright ( C ) 2024 National Technology and Engineering Solutions of Sandia , LLC
2024-05-01 05:48:04 -05: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 .
*
* 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 .
*
*/
2024-07-25 06:10:59 -05:00
# include "kernel/functional.h"
# include "kernel/topo_scc.h"
2024-07-24 11:37:17 -05:00
# include "ff.h"
# include "ffinit.h"
2024-08-22 05:13:58 -05:00
# include <deque>
2024-05-01 05:48:04 -05:00
YOSYS_NAMESPACE_BEGIN
2024-07-25 06:10:59 -05:00
namespace Functional {
2024-05-01 05:48:04 -05:00
2024-07-25 06:10:59 -05:00
const char * fn_to_string ( Fn fn ) {
2024-07-17 05:53:42 -05:00
switch ( fn ) {
2024-07-25 06:10:59 -05:00
case Fn : : invalid : return " invalid " ;
case Fn : : buf : return " buf " ;
case Fn : : slice : return " slice " ;
case Fn : : zero_extend : return " zero_extend " ;
case Fn : : sign_extend : return " sign_extend " ;
case Fn : : concat : return " concat " ;
case Fn : : add : return " add " ;
case Fn : : sub : return " sub " ;
case Fn : : mul : return " mul " ;
case Fn : : unsigned_div : return " unsigned_div " ;
case Fn : : unsigned_mod : return " unsigned_mod " ;
case Fn : : bitwise_and : return " bitwise_and " ;
case Fn : : bitwise_or : return " bitwise_or " ;
case Fn : : bitwise_xor : return " bitwise_xor " ;
case Fn : : bitwise_not : return " bitwise_not " ;
case Fn : : reduce_and : return " reduce_and " ;
case Fn : : reduce_or : return " reduce_or " ;
case Fn : : reduce_xor : return " reduce_xor " ;
case Fn : : unary_minus : return " unary_minus " ;
case Fn : : equal : return " equal " ;
case Fn : : not_equal : return " not_equal " ;
case Fn : : signed_greater_than : return " signed_greater_than " ;
case Fn : : signed_greater_equal : return " signed_greater_equal " ;
case Fn : : unsigned_greater_than : return " unsigned_greater_than " ;
case Fn : : unsigned_greater_equal : return " unsigned_greater_equal " ;
case Fn : : logical_shift_left : return " logical_shift_left " ;
case Fn : : logical_shift_right : return " logical_shift_right " ;
case Fn : : arithmetic_shift_right : return " arithmetic_shift_right " ;
case Fn : : mux : return " mux " ;
case Fn : : constant : return " constant " ;
case Fn : : input : return " input " ;
case Fn : : state : return " state " ;
case Fn : : memory_read : return " memory_read " ;
case Fn : : memory_write : return " memory_write " ;
2024-07-17 05:53:42 -05:00
}
2024-07-25 06:10:59 -05:00
log_error ( " fn_to_string: unknown Functional::Fn value %d " , ( int ) fn ) ;
2024-07-17 05:53:42 -05:00
}
2024-08-28 06:39:41 -05:00
vector < IRInput const * > IR : : inputs ( IdString kind ) const {
2024-08-06 03:56:28 -05:00
vector < IRInput const * > ret ;
for ( const auto & [ name , input ] : _inputs )
2024-08-28 06:39:41 -05:00
if ( input . kind = = kind )
2024-08-06 03:56:28 -05:00
ret . push_back ( & input ) ;
return ret ;
}
2024-08-28 06:39:41 -05:00
vector < IROutput const * > IR : : outputs ( IdString kind ) const {
2024-08-06 03:56:28 -05:00
vector < IROutput const * > ret ;
for ( const auto & [ name , output ] : _outputs )
2024-08-28 06:39:41 -05:00
if ( output . kind = = kind )
2024-08-06 03:56:28 -05:00
ret . push_back ( & output ) ;
return ret ;
}
2024-08-28 06:39:41 -05:00
vector < IRState const * > IR : : states ( IdString kind ) const {
2024-08-06 03:56:28 -05:00
vector < IRState const * > ret ;
for ( const auto & [ name , state ] : _states )
2024-08-28 06:39:41 -05:00
if ( state . kind = = kind )
2024-08-06 03:56:28 -05:00
ret . push_back ( & state ) ;
return ret ;
}
vector < IRInput const * > IR : : all_inputs ( ) const {
vector < IRInput const * > ret ;
for ( const auto & [ name , input ] : _inputs )
ret . push_back ( & input ) ;
return ret ;
}
vector < IROutput const * > IR : : all_outputs ( ) const {
vector < IROutput const * > ret ;
for ( const auto & [ name , output ] : _outputs )
ret . push_back ( & output ) ;
return ret ;
}
vector < IRState const * > IR : : all_states ( ) const {
vector < IRState const * > ret ;
for ( const auto & [ name , state ] : _states )
ret . push_back ( & state ) ;
return ret ;
}
2024-07-25 06:10:59 -05:00
struct PrintVisitor : DefaultVisitor < std : : string > {
2024-07-17 05:53:42 -05:00
std : : function < std : : string ( Node ) > np ;
PrintVisitor ( std : : function < std : : string ( Node ) > np ) : np ( np ) { }
// as a general rule the default handler is good enough iff the only arguments are of type Node
std : : string slice ( Node , Node a , int offset , int out_width ) override { return " slice( " + np ( a ) + " , " + std : : to_string ( offset ) + " , " + std : : to_string ( out_width ) + " ) " ; }
std : : string zero_extend ( Node , Node a , int out_width ) override { return " zero_extend( " + np ( a ) + " , " + std : : to_string ( out_width ) + " ) " ; }
std : : string sign_extend ( Node , Node a , int out_width ) override { return " sign_extend( " + np ( a ) + " , " + std : : to_string ( out_width ) + " ) " ; }
2024-07-24 11:37:17 -05:00
std : : string constant ( Node , RTLIL : : Const const & value ) override { return " constant( " + value . as_string ( ) + " ) " ; }
2024-08-28 06:39:41 -05:00
std : : string input ( Node , IdString name , IdString kind ) override { return " input( " + name . str ( ) + " , " + kind . str ( ) + " ) " ; }
std : : string state ( Node , IdString name , IdString kind ) override { return " state( " + name . str ( ) + " , " + kind . str ( ) + " ) " ; }
2024-07-17 05:53:42 -05:00
std : : string default_handler ( Node self ) override {
2024-07-25 06:10:59 -05:00
std : : string ret = fn_to_string ( self . fn ( ) ) ;
2024-07-17 05:53:42 -05:00
ret + = " ( " ;
for ( size_t i = 0 ; i < self . arg_count ( ) ; i + + ) {
if ( i > 0 ) ret + = " , " ;
ret + = np ( self . arg ( i ) ) ;
}
ret + = " ) " ;
return ret ;
}
} ;
2024-07-25 06:10:59 -05:00
std : : string Node : : to_string ( )
2024-07-17 05:53:42 -05:00
{
return to_string ( [ ] ( Node n ) { return RTLIL : : unescape_id ( n . name ( ) ) ; } ) ;
}
2024-07-25 06:10:59 -05:00
std : : string Node : : to_string ( std : : function < std : : string ( Node ) > np )
2024-07-17 05:53:42 -05:00
{
return visit ( PrintVisitor ( np ) ) ;
}
2024-05-01 05:48:04 -05:00
class CellSimplifier {
2024-07-25 06:10:59 -05:00
Factory & factory ;
2024-07-17 06:42:24 -05:00
Node sign ( Node a ) {
return factory . slice ( a , a . width ( ) - 1 , 1 ) ;
2024-07-04 10:58:07 -05:00
}
2024-07-17 06:42:24 -05:00
Node neg_if ( Node a , Node s ) {
return factory . mux ( a , factory . unary_minus ( a ) , s ) ;
2024-07-04 10:58:07 -05:00
}
2024-07-17 06:42:24 -05:00
Node abs ( Node a ) {
return neg_if ( a , sign ( a ) ) ;
2024-07-04 10:58:07 -05:00
}
2024-07-17 07:26:53 -05:00
Node handle_shift ( Node a , Node b , bool is_right , bool is_signed ) {
// to prevent new_width == 0, we handle this case separately
if ( a . width ( ) = = 1 ) {
if ( ! is_signed )
return factory . bitwise_and ( a , factory . bitwise_not ( factory . reduce_or ( b ) ) ) ;
else
return a ;
}
int new_width = ceil_log2 ( a . width ( ) ) ;
Node b_truncated = factory . extend ( b , new_width , false ) ;
Node y =
! is_right ? factory . logical_shift_left ( a , b_truncated ) :
! is_signed ? factory . logical_shift_right ( a , b_truncated ) :
factory . arithmetic_shift_right ( a , b_truncated ) ;
if ( b . width ( ) < = new_width )
return y ;
Node overflow = factory . unsigned_greater_equal ( b , factory . constant ( RTLIL : : Const ( a . width ( ) , b . width ( ) ) ) ) ;
Node y_if_overflow = is_signed ? factory . extend ( sign ( a ) , a . width ( ) , true ) : factory . constant ( RTLIL : : Const ( State : : S0 , a . width ( ) ) ) ;
return factory . mux ( y , y_if_overflow , overflow ) ;
2024-05-01 05:48:04 -05:00
}
2024-07-17 07:26:53 -05:00
public :
Node logical_shift_left ( Node a , Node b ) { return handle_shift ( a , b , false , false ) ; }
Node logical_shift_right ( Node a , Node b ) { return handle_shift ( a , b , true , false ) ; }
Node arithmetic_shift_right ( Node a , Node b ) { return handle_shift ( a , b , true , true ) ; }
2024-07-17 06:42:24 -05:00
Node bitwise_mux ( Node a , Node b , Node s ) {
Node aa = factory . bitwise_and ( a , factory . bitwise_not ( s ) ) ;
Node bb = factory . bitwise_and ( b , s ) ;
return factory . bitwise_or ( aa , bb ) ;
2024-06-12 05:27:10 -05:00
}
2024-07-25 06:10:59 -05:00
CellSimplifier ( Factory & f ) : factory ( f ) { }
2024-07-04 10:58:07 -05:00
private :
2024-07-17 06:42:24 -05:00
Node handle_pow ( Node a0 , Node b , int y_width , bool is_signed ) {
Node a = factory . extend ( a0 , y_width , is_signed ) ;
Node r = factory . constant ( Const ( 1 , y_width ) ) ;
for ( int i = 0 ; i < b . width ( ) ; i + + ) {
Node b_bit = factory . slice ( b , i , 1 ) ;
r = factory . mux ( r , factory . mul ( r , a ) , b_bit ) ;
a = factory . mul ( a , a ) ;
2024-07-04 10:58:07 -05:00
}
if ( is_signed ) {
2024-07-17 06:42:24 -05:00
Node a_ge_1 = factory . unsigned_greater_than ( abs ( a0 ) , factory . constant ( Const ( 1 , a0 . width ( ) ) ) ) ;
Node zero_result = factory . bitwise_and ( a_ge_1 , sign ( b ) ) ;
r = factory . mux ( r , factory . constant ( Const ( 0 , y_width ) ) , zero_result ) ;
2024-07-04 10:58:07 -05:00
}
return r ;
}
2024-07-17 06:42:24 -05:00
Node handle_bmux ( Node a , Node s , int a_offset , int width , int sn ) {
2024-07-12 04:45:58 -05:00
if ( sn < 1 )
2024-07-17 06:42:24 -05:00
return factory . slice ( a , a_offset , width ) ;
2024-07-12 04:45:58 -05:00
else {
2024-07-17 06:42:24 -05:00
Node y0 = handle_bmux ( a , s , a_offset , width , sn - 1 ) ;
Node y1 = handle_bmux ( a , s , a_offset + ( width < < ( sn - 1 ) ) , width , sn - 1 ) ;
return factory . mux ( y0 , y1 , factory . slice ( s , sn - 1 , 1 ) ) ;
2024-07-12 04:45:58 -05:00
}
}
2024-07-17 07:35:58 -05:00
Node handle_pmux ( Node a , Node b , Node s ) {
// TODO : what to do about multiple b bits set ?
log_assert ( b . width ( ) = = a . width ( ) * s . width ( ) ) ;
Node y = a ;
for ( int i = 0 ; i < s . width ( ) ; i + + )
y = factory . mux ( y , factory . slice ( b , a . width ( ) * i , a . width ( ) ) , factory . slice ( s , i , 1 ) ) ;
return y ;
}
2024-07-17 10:33:34 -05:00
dict < IdString , Node > handle_fa ( Node a , Node b , Node c ) {
Node t1 = factory . bitwise_xor ( a , b ) ;
Node t2 = factory . bitwise_and ( a , b ) ;
Node t3 = factory . bitwise_and ( c , t1 ) ;
Node y = factory . bitwise_xor ( c , t1 ) ;
Node x = factory . bitwise_or ( t2 , t3 ) ;
return { { ID ( X ) , x } , { ID ( Y ) , y } } ;
}
dict < IdString , Node > handle_alu ( Node a_in , Node b_in , int y_width , bool is_signed , Node ci , Node bi ) {
Node a = factory . extend ( a_in , y_width , is_signed ) ;
Node b_uninverted = factory . extend ( b_in , y_width , is_signed ) ;
Node b = factory . mux ( b_uninverted , factory . bitwise_not ( b_uninverted ) , bi ) ;
Node x = factory . bitwise_xor ( a , b ) ;
2024-07-18 01:50:49 -05:00
// we can compute the carry into each bit using (a+b+c)^a^b. since we want the carry out,
// i.e. the carry into the next bit, we have to add an extra bit to a and b, and
// then slice off the bottom bit of the result.
2024-07-17 10:33:34 -05:00
Node a_extra = factory . extend ( a , y_width + 1 , false ) ;
Node b_extra = factory . extend ( b , y_width + 1 , false ) ;
Node y_extra = factory . add ( factory . add ( a_extra , b_extra ) , factory . extend ( ci , a . width ( ) + 1 , false ) ) ;
Node y = factory . slice ( y_extra , 0 , y_width ) ;
Node carries = factory . bitwise_xor ( y_extra , factory . bitwise_xor ( a_extra , b_extra ) ) ;
Node co = factory . slice ( carries , 1 , y_width ) ;
return { { ID ( X ) , x } , { ID ( Y ) , y } , { ID ( CO ) , co } } ;
}
2024-07-18 01:50:49 -05:00
Node handle_lcu ( Node p , Node g , Node ci ) {
return handle_alu ( g , factory . bitwise_or ( p , g ) , g . width ( ) , false , ci , factory . constant ( Const ( State : : S0 , 1 ) ) ) . at ( ID ( CO ) ) ;
}
2024-07-04 10:58:07 -05:00
public :
2024-08-06 03:56:28 -05:00
std : : variant < dict < IdString , Node > , Node > handle ( IdString cellName , IdString cellType , dict < IdString , Const > parameters , dict < IdString , Node > inputs )
2024-05-01 05:48:04 -05:00
{
int a_width = parameters . at ( ID ( A_WIDTH ) , Const ( - 1 ) ) . as_int ( ) ;
int b_width = parameters . at ( ID ( B_WIDTH ) , Const ( - 1 ) ) . as_int ( ) ;
int y_width = parameters . at ( ID ( Y_WIDTH ) , Const ( - 1 ) ) . as_int ( ) ;
bool a_signed = parameters . at ( ID ( A_SIGNED ) , Const ( 0 ) ) . as_bool ( ) ;
bool b_signed = parameters . at ( ID ( B_SIGNED ) , Const ( 0 ) ) . as_bool ( ) ;
2024-07-04 10:58:07 -05:00
if ( cellType . in ( { ID ( $ add ) , ID ( $ sub ) , ID ( $ and ) , ID ( $ or ) , ID ( $ xor ) , ID ( $ xnor ) , ID ( $ mul ) } ) ) {
2024-05-01 05:48:04 -05:00
bool is_signed = a_signed & & b_signed ;
2024-07-17 06:42:24 -05:00
Node a = factory . extend ( inputs . at ( ID ( A ) ) , y_width , is_signed ) ;
Node b = factory . extend ( inputs . at ( ID ( B ) ) , y_width , is_signed ) ;
2024-05-01 05:48:04 -05:00
if ( cellType = = ID ( $ add ) )
2024-07-17 06:42:24 -05:00
return factory . add ( a , b ) ;
2024-05-01 05:48:04 -05:00
else if ( cellType = = ID ( $ sub ) )
2024-07-17 06:42:24 -05:00
return factory . sub ( a , b ) ;
2024-07-04 10:58:07 -05:00
else if ( cellType = = ID ( $ mul ) )
2024-07-17 06:42:24 -05:00
return factory . mul ( a , b ) ;
2024-05-01 05:48:04 -05:00
else if ( cellType = = ID ( $ and ) )
2024-07-17 06:42:24 -05:00
return factory . bitwise_and ( a , b ) ;
2024-05-01 05:48:04 -05:00
else if ( cellType = = ID ( $ or ) )
2024-07-17 06:42:24 -05:00
return factory . bitwise_or ( a , b ) ;
2024-05-01 05:48:04 -05:00
else if ( cellType = = ID ( $ xor ) )
2024-07-17 06:42:24 -05:00
return factory . bitwise_xor ( a , b ) ;
2024-05-01 05:48:04 -05:00
else if ( cellType = = ID ( $ xnor ) )
2024-07-17 06:42:24 -05:00
return factory . bitwise_not ( factory . bitwise_xor ( a , b ) ) ;
2024-05-01 05:48:04 -05:00
else
log_abort ( ) ;
} else if ( cellType . in ( { ID ( $ eq ) , ID ( $ ne ) , ID ( $ eqx ) , ID ( $ nex ) , ID ( $ le ) , ID ( $ lt ) , ID ( $ ge ) , ID ( $ gt ) } ) ) {
bool is_signed = a_signed & & b_signed ;
int width = max ( a_width , b_width ) ;
2024-07-17 06:42:24 -05:00
Node a = factory . extend ( inputs . at ( ID ( A ) ) , width , is_signed ) ;
Node b = factory . extend ( inputs . at ( ID ( B ) ) , width , is_signed ) ;
2024-05-01 05:48:04 -05:00
if ( cellType . in ( { ID ( $ eq ) , ID ( $ eqx ) } ) )
2024-07-17 06:42:24 -05:00
return factory . extend ( factory . equal ( a , b ) , y_width , false ) ;
2024-06-12 05:27:10 -05:00
else if ( cellType . in ( { ID ( $ ne ) , ID ( $ nex ) } ) )
2024-07-17 06:42:24 -05:00
return factory . extend ( factory . not_equal ( a , b ) , y_width , false ) ;
2024-05-01 05:48:04 -05:00
else if ( cellType = = ID ( $ lt ) )
2024-07-17 06:42:24 -05:00
return factory . extend ( is_signed ? factory . signed_greater_than ( b , a ) : factory . unsigned_greater_than ( b , a ) , y_width , false ) ;
2024-05-01 05:48:04 -05:00
else if ( cellType = = ID ( $ le ) )
2024-07-17 06:42:24 -05:00
return factory . extend ( is_signed ? factory . signed_greater_equal ( b , a ) : factory . unsigned_greater_equal ( b , a ) , y_width , false ) ;
2024-05-01 05:48:04 -05:00
else if ( cellType = = ID ( $ gt ) )
2024-07-17 06:42:24 -05:00
return factory . extend ( is_signed ? factory . signed_greater_than ( a , b ) : factory . unsigned_greater_than ( a , b ) , y_width , false ) ;
2024-05-01 05:48:04 -05:00
else if ( cellType = = ID ( $ ge ) )
2024-07-17 06:42:24 -05:00
return factory . extend ( is_signed ? factory . signed_greater_equal ( a , b ) : factory . unsigned_greater_equal ( a , b ) , y_width , false ) ;
2024-05-01 05:48:04 -05:00
else
log_abort ( ) ;
} else if ( cellType . in ( { ID ( $ logic_or ) , ID ( $ logic_and ) } ) ) {
2024-07-17 06:42:24 -05:00
Node a = factory . reduce_or ( inputs . at ( ID ( A ) ) ) ;
Node b = factory . reduce_or ( inputs . at ( ID ( B ) ) ) ;
Node y = cellType = = ID ( $ logic_and ) ? factory . bitwise_and ( a , b ) : factory . bitwise_or ( a , b ) ;
return factory . extend ( y , y_width , false ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ not ) ) {
2024-07-17 06:42:24 -05:00
Node a = factory . extend ( inputs . at ( ID ( A ) ) , y_width , a_signed ) ;
return factory . bitwise_not ( a ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ pos ) ) {
2024-07-17 06:42:24 -05:00
return factory . extend ( inputs . at ( ID ( A ) ) , y_width , a_signed ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ neg ) ) {
2024-07-17 06:42:24 -05:00
Node a = factory . extend ( inputs . at ( ID ( A ) ) , y_width , a_signed ) ;
return factory . unary_minus ( a ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ logic_not ) ) {
2024-07-17 06:42:24 -05:00
Node a = factory . reduce_or ( inputs . at ( ID ( A ) ) ) ;
Node y = factory . bitwise_not ( a ) ;
return factory . extend ( y , y_width , false ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType . in ( { ID ( $ reduce_or ) , ID ( $ reduce_bool ) } ) ) {
2024-07-17 06:42:24 -05:00
Node a = factory . reduce_or ( inputs . at ( ID ( A ) ) ) ;
return factory . extend ( a , y_width , false ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ reduce_and ) ) {
2024-07-17 06:42:24 -05:00
Node a = factory . reduce_and ( inputs . at ( ID ( A ) ) ) ;
return factory . extend ( a , y_width , false ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType . in ( { ID ( $ reduce_xor ) , ID ( $ reduce_xnor ) } ) ) {
2024-07-17 06:42:24 -05:00
Node a = factory . reduce_xor ( inputs . at ( ID ( A ) ) ) ;
Node y = cellType = = ID ( $ reduce_xnor ) ? factory . bitwise_not ( a ) : a ;
return factory . extend ( y , y_width , false ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ shl ) | | cellType = = ID ( $ sshl ) ) {
2024-07-17 06:42:24 -05:00
Node a = factory . extend ( inputs . at ( ID ( A ) ) , y_width , a_signed ) ;
Node b = inputs . at ( ID ( B ) ) ;
return logical_shift_left ( a , b ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ shr ) | | cellType = = ID ( $ sshr ) ) {
int width = max ( a_width , y_width ) ;
2024-07-17 06:42:24 -05:00
Node a = factory . extend ( inputs . at ( ID ( A ) ) , width , a_signed ) ;
Node b = inputs . at ( ID ( B ) ) ;
Node y = a_signed & & cellType = = ID ( $ sshr ) ?
arithmetic_shift_right ( a , b ) :
logical_shift_right ( a , b ) ;
return factory . extend ( y , y_width , a_signed ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ shiftx ) | | cellType = = ID ( $ shift ) ) {
int width = max ( a_width , y_width ) ;
2024-07-17 06:42:24 -05:00
Node a = factory . extend ( inputs . at ( ID ( A ) ) , width , cellType = = ID ( $ shift ) & & a_signed ) ;
Node b = inputs . at ( ID ( B ) ) ;
Node shr = logical_shift_right ( a , b ) ;
2024-05-01 05:48:04 -05:00
if ( b_signed ) {
2024-07-17 06:42:24 -05:00
Node shl = logical_shift_left ( a , factory . unary_minus ( b ) ) ;
Node y = factory . mux ( shr , shl , sign ( b ) ) ;
return factory . extend ( y , y_width , false ) ;
2024-05-01 05:48:04 -05:00
} else {
2024-07-17 06:42:24 -05:00
return factory . extend ( shr , y_width , false ) ;
2024-05-01 05:48:04 -05:00
}
} else if ( cellType = = ID ( $ mux ) ) {
2024-07-17 06:42:24 -05:00
return factory . mux ( inputs . at ( ID ( A ) ) , inputs . at ( ID ( B ) ) , inputs . at ( ID ( S ) ) ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ pmux ) ) {
2024-07-17 07:35:58 -05:00
return handle_pmux ( inputs . at ( ID ( A ) ) , inputs . at ( ID ( B ) ) , inputs . at ( ID ( S ) ) ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ concat ) ) {
2024-07-17 06:42:24 -05:00
Node a = inputs . at ( ID ( A ) ) ;
Node b = inputs . at ( ID ( B ) ) ;
return factory . concat ( a , b ) ;
2024-05-01 05:48:04 -05:00
} else if ( cellType = = ID ( $ slice ) ) {
int offset = parameters . at ( ID ( OFFSET ) ) . as_int ( ) ;
2024-07-17 06:42:24 -05:00
Node a = inputs . at ( ID ( A ) ) ;
return factory . slice ( a , offset , y_width ) ;
2024-07-04 10:58:07 -05:00
} else if ( cellType . in ( { ID ( $ div ) , ID ( $ mod ) , ID ( $ divfloor ) , ID ( $ modfloor ) } ) ) {
int width = max ( a_width , b_width ) ;
bool is_signed = a_signed & & b_signed ;
2024-07-17 06:42:24 -05:00
Node a = factory . extend ( inputs . at ( ID ( A ) ) , width , is_signed ) ;
Node b = factory . extend ( inputs . at ( ID ( B ) ) , width , is_signed ) ;
2024-07-04 10:58:07 -05:00
if ( is_signed ) {
if ( cellType = = ID ( $ div ) ) {
2024-07-16 11:55:26 -05:00
// divide absolute values, then flip the sign if input signs differ
// but extend the width first, to handle the case (most negative value) / (-1)
2024-07-17 06:42:24 -05:00
Node abs_y = factory . unsigned_div ( abs ( a ) , abs ( b ) ) ;
Node out_sign = factory . not_equal ( sign ( a ) , sign ( b ) ) ;
return neg_if ( factory . extend ( abs_y , y_width , false ) , out_sign ) ;
2024-07-04 10:58:07 -05:00
} else if ( cellType = = ID ( $ mod ) ) {
2024-07-16 11:55:26 -05:00
// similar to division but output sign == divisor sign
2024-07-17 06:42:24 -05:00
Node abs_y = factory . unsigned_mod ( abs ( a ) , abs ( b ) ) ;
return neg_if ( factory . extend ( abs_y , y_width , false ) , sign ( a ) ) ;
2024-07-04 10:58:07 -05:00
} else if ( cellType = = ID ( $ divfloor ) ) {
2024-07-16 11:55:26 -05:00
// if b is negative, flip both signs so that b is positive
2024-07-17 06:42:24 -05:00
Node b_sign = sign ( b ) ;
Node a1 = neg_if ( a , b_sign ) ;
Node b1 = neg_if ( b , b_sign ) ;
2024-07-16 11:55:26 -05:00
// if a is now negative, calculate ~((~a) / b) = -((-a - 1) / b + 1)
// which equals the negative of (-a) / b with rounding up rather than down
// note that to handle the case where a = most negative value properly,
2024-07-17 06:42:24 -05:00
// we have to calculate a1_sign from the original values rather than using sign(a1)
Node a1_sign = factory . bitwise_and ( factory . not_equal ( sign ( a ) , sign ( b ) ) , factory . reduce_or ( a ) ) ;
Node a2 = factory . mux ( a1 , factory . bitwise_not ( a1 ) , a1_sign ) ;
Node y1 = factory . unsigned_div ( a2 , b1 ) ;
Node y2 = factory . extend ( y1 , y_width , false ) ;
return factory . mux ( y2 , factory . bitwise_not ( y2 ) , a1_sign ) ;
2024-07-04 10:58:07 -05:00
} else if ( cellType = = ID ( $ modfloor ) ) {
2024-07-16 11:55:26 -05:00
// calculate |a| % |b| and then subtract from |b| if input signs differ and the remainder is non-zero
2024-07-17 06:42:24 -05:00
Node abs_b = abs ( b ) ;
Node abs_y = factory . unsigned_mod ( abs ( a ) , abs_b ) ;
Node flip_y = factory . bitwise_and ( factory . bitwise_xor ( sign ( a ) , sign ( b ) ) , factory . reduce_or ( abs_y ) ) ;
Node y_flipped = factory . mux ( abs_y , factory . sub ( abs_b , abs_y ) , flip_y ) ;
2024-07-16 11:55:26 -05:00
// since y_flipped is strictly less than |b|, the top bit is always 0 and we can just sign extend the flipped result
2024-07-17 06:42:24 -05:00
Node y = neg_if ( y_flipped , sign ( b ) ) ;
return factory . extend ( y , y_width , true ) ;
2024-07-04 10:58:07 -05:00
} else
log_error ( " unhandled cell in CellSimplifier %s \n " , cellType . c_str ( ) ) ;
} else {
if ( cellType . in ( { ID ( $ mod ) , ID ( $ modfloor ) } ) )
2024-07-17 06:42:24 -05:00
return factory . extend ( factory . unsigned_mod ( a , b ) , y_width , false ) ;
2024-07-04 10:58:07 -05:00
else
2024-07-17 06:42:24 -05:00
return factory . extend ( factory . unsigned_div ( a , b ) , y_width , false ) ;
2024-07-04 10:58:07 -05:00
}
} else if ( cellType = = ID ( $ pow ) ) {
2024-07-17 06:42:24 -05:00
return handle_pow ( inputs . at ( ID ( A ) ) , inputs . at ( ID ( B ) ) , y_width , a_signed & & b_signed ) ;
2024-07-07 17:07:07 -05:00
} else if ( cellType = = ID ( $ lut ) ) {
2024-07-12 04:45:58 -05:00
int width = parameters . at ( ID ( WIDTH ) ) . as_int ( ) ;
Const lut_table = parameters . at ( ID ( LUT ) ) ;
2024-07-16 11:55:26 -05:00
lut_table . extu ( 1 < < width ) ;
2024-07-17 06:42:24 -05:00
return handle_bmux ( factory . constant ( lut_table ) , inputs . at ( ID ( A ) ) , 0 , 1 , width ) ;
2024-07-12 04:45:58 -05:00
} else if ( cellType = = ID ( $ bwmux ) ) {
2024-07-17 06:42:24 -05:00
Node a = inputs . at ( ID ( A ) ) ;
Node b = inputs . at ( ID ( B ) ) ;
Node s = inputs . at ( ID ( S ) ) ;
2024-07-12 04:45:58 -05:00
return factory . bitwise_or (
2024-07-17 06:42:24 -05:00
factory . bitwise_and ( a , factory . bitwise_not ( s ) ) ,
factory . bitwise_and ( b , s ) ) ;
2024-07-12 04:45:58 -05:00
} else if ( cellType = = ID ( $ bweqx ) ) {
2024-07-17 06:42:24 -05:00
Node a = inputs . at ( ID ( A ) ) ;
Node b = inputs . at ( ID ( B ) ) ;
return factory . bitwise_not ( factory . bitwise_xor ( a , b ) ) ;
2024-07-12 04:45:58 -05:00
} else if ( cellType = = ID ( $ bmux ) ) {
int width = parameters . at ( ID ( WIDTH ) ) . as_int ( ) ;
int s_width = parameters . at ( ID ( S_WIDTH ) ) . as_int ( ) ;
2024-07-17 06:42:24 -05:00
return handle_bmux ( inputs . at ( ID ( A ) ) , inputs . at ( ID ( S ) ) , 0 , width , s_width ) ;
2024-07-12 04:45:58 -05:00
} else if ( cellType = = ID ( $ demux ) ) {
int width = parameters . at ( ID ( WIDTH ) ) . as_int ( ) ;
int s_width = parameters . at ( ID ( S_WIDTH ) ) . as_int ( ) ;
int y_width = width < < s_width ;
2024-07-17 07:26:53 -05:00
int b_width = ceil_log2 ( y_width ) ;
2024-07-17 06:42:24 -05:00
Node a = factory . extend ( inputs . at ( ID ( A ) ) , y_width , false ) ;
Node s = factory . extend ( inputs . at ( ID ( S ) ) , b_width , false ) ;
Node b = factory . mul ( s , factory . constant ( Const ( width , b_width ) ) ) ;
return factory . logical_shift_left ( a , b ) ;
2024-07-17 10:33:34 -05:00
} else if ( cellType = = ID ( $ fa ) ) {
return handle_fa ( inputs . at ( ID ( A ) ) , inputs . at ( ID ( B ) ) , inputs . at ( ID ( C ) ) ) ;
} else if ( cellType = = ID ( $ lcu ) ) {
return handle_lcu ( inputs . at ( ID ( P ) ) , inputs . at ( ID ( G ) ) , inputs . at ( ID ( CI ) ) ) ;
} else if ( cellType = = ID ( $ alu ) ) {
return handle_alu ( inputs . at ( ID ( A ) ) , inputs . at ( ID ( B ) ) , y_width , a_signed & & b_signed , inputs . at ( ID ( CI ) ) , inputs . at ( ID ( BI ) ) ) ;
2024-08-06 03:56:28 -05:00
} else if ( cellType . in ( { ID ( $ assert ) , ID ( $ assume ) , ID ( $ live ) , ID ( $ fair ) , ID ( $ cover ) } ) ) {
Node a = factory . mux ( factory . constant ( Const ( State : : S1 , 1 ) ) , inputs . at ( ID ( A ) ) , inputs . at ( ID ( EN ) ) ) ;
auto & output = factory . add_output ( cellName , cellType , Sort ( 1 ) ) ;
output . set_value ( a ) ;
return { } ;
} else if ( cellType . in ( { ID ( $ anyconst ) , ID ( $ allconst ) , ID ( $ anyseq ) , ID ( $ allseq ) } ) ) {
int width = parameters . at ( ID ( WIDTH ) ) . as_int ( ) ;
auto & input = factory . add_input ( cellName , cellType , Sort ( width ) ) ;
return factory . value ( input ) ;
} else if ( cellType = = ID ( $ initstate ) ) {
if ( factory . ir ( ) . has_state ( ID ( $ initstate ) , ID ( $ state ) ) )
return factory . value ( factory . ir ( ) . state ( ID ( $ initstate ) ) ) ;
else {
auto & state = factory . add_state ( ID ( $ initstate ) , ID ( $ state ) , Sort ( 1 ) ) ;
state . set_initial_value ( RTLIL : : Const ( State : : S1 , 1 ) ) ;
state . set_next_value ( factory . constant ( RTLIL : : Const ( State : : S0 , 1 ) ) ) ;
return factory . value ( state ) ;
}
} else if ( cellType = = ID ( $ check ) ) {
log_error ( " The design contains a $check cell `%s'. This is not supported by the functional backend. Call `chformal -lower' to avoid this error. \n " , cellName . c_str ( ) ) ;
2024-07-12 04:45:58 -05:00
} else {
2024-07-17 10:33:34 -05:00
log_error ( " `%s' cells are not supported by the functional backend \n " , cellType . c_str ( ) ) ;
2024-05-01 05:48:04 -05:00
}
}
} ;
2024-06-20 10:40:02 -05:00
class FunctionalIRConstruction {
2024-07-17 10:33:34 -05:00
std : : deque < std : : variant < DriveSpec , Cell * > > queue ;
2024-07-17 06:42:24 -05:00
dict < DriveSpec , Node > graph_nodes ;
2024-07-17 10:33:34 -05:00
dict < std : : pair < Cell * , IdString > , Node > cell_outputs ;
2024-05-01 05:48:04 -05:00
DriverMap driver_map ;
2024-07-25 06:10:59 -05:00
Factory & factory ;
2024-07-17 06:42:24 -05:00
CellSimplifier simplifier ;
2024-06-12 05:27:10 -05:00
vector < Mem > memories_vector ;
dict < Cell * , Mem * > memories ;
2024-07-24 11:37:17 -05:00
SigMap sig_map ; // TODO: this is only for FfInitVals, remove this once FfInitVals supports DriverMap
FfInitVals ff_initvals ;
2024-05-01 05:48:04 -05:00
2024-07-17 06:42:24 -05:00
Node enqueue ( DriveSpec const & spec )
2024-05-01 05:48:04 -05:00
{
auto it = graph_nodes . find ( spec ) ;
if ( it = = graph_nodes . end ( ) ) {
auto node = factory . create_pending ( spec . size ( ) ) ;
graph_nodes . insert ( { spec , node } ) ;
queue . emplace_back ( spec ) ;
return node ;
} else
return it - > second ;
}
2024-07-17 10:33:34 -05:00
Node enqueue_cell ( Cell * cell , IdString port_name )
{
auto it = cell_outputs . find ( { cell , port_name } ) ;
if ( it = = cell_outputs . end ( ) ) {
queue . emplace_back ( cell ) ;
std : : optional < Node > rv ;
for ( auto const & [ name , sigspec ] : cell - > connections ( ) )
if ( driver_map . celltypes . cell_output ( cell - > type , name ) ) {
auto node = factory . create_pending ( sigspec . size ( ) ) ;
factory . suggest_name ( node , cell - > name . str ( ) + " $ " + name . str ( ) ) ;
cell_outputs . emplace ( { cell , name } , node ) ;
if ( name = = port_name )
rv = node ;
}
return * rv ;
} else
return it - > second ;
}
2024-05-01 05:48:04 -05:00
public :
2024-07-25 06:10:59 -05:00
FunctionalIRConstruction ( Module * module , Factory & f )
2024-07-24 11:37:17 -05:00
: factory ( f )
, simplifier ( f )
, sig_map ( module )
, ff_initvals ( & sig_map , module )
2024-05-01 05:48:04 -05:00
{
driver_map . add ( module ) ;
for ( auto cell : module - > cells ( ) ) {
2024-08-06 03:56:28 -05:00
if ( cell - > type . in ( ID ( $ assert ) , ID ( $ assume ) , ID ( $ live ) , ID ( $ fair ) , ID ( $ cover ) , ID ( $ check ) ) )
2024-07-17 10:33:34 -05:00
queue . emplace_back ( cell ) ;
2024-05-01 05:48:04 -05:00
}
for ( auto wire : module - > wires ( ) ) {
2024-07-24 11:37:17 -05:00
if ( wire - > port_input )
2024-08-06 03:56:28 -05:00
factory . add_input ( wire - > name , ID ( $ input ) , Sort ( wire - > width ) ) ;
2024-05-01 05:48:04 -05:00
if ( wire - > port_output ) {
2024-08-06 03:56:28 -05:00
auto & output = factory . add_output ( wire - > name , ID ( $ output ) , Sort ( wire - > width ) ) ;
output . set_value ( enqueue ( DriveChunk ( DriveChunkWire ( wire , 0 , wire - > width ) ) ) ) ;
2024-05-01 05:48:04 -05:00
}
}
2024-06-12 05:27:10 -05:00
memories_vector = Mem : : get_all_memories ( module ) ;
for ( auto & mem : memories_vector ) {
if ( mem . cell ! = nullptr )
memories [ mem . cell ] = & mem ;
}
}
2024-08-08 09:38:24 -05:00
private :
2024-07-18 08:10:15 -05:00
Node concatenate_read_results ( Mem * mem , vector < Node > results )
2024-06-12 05:27:10 -05:00
{
2024-07-18 08:10:15 -05:00
// sanity check: all read ports concatenated should equal to the RD_DATA port
const SigSpec & rd_data = mem - > cell - > connections ( ) . at ( ID ( RD_DATA ) ) ;
int current = 0 ;
for ( size_t i = 0 ; i < mem - > rd_ports . size ( ) ; i + + ) {
int width = mem - > width < < mem - > rd_ports [ i ] . wide_log2 ;
log_assert ( results [ i ] . width ( ) = = width ) ;
log_assert ( mem - > rd_ports [ i ] . data = = rd_data . extract ( current , width ) ) ;
current + = width ;
}
log_assert ( current = = rd_data . size ( ) ) ;
log_assert ( ! results . empty ( ) ) ;
2024-07-17 06:42:24 -05:00
Node node = results [ 0 ] ;
for ( size_t i = 1 ; i < results . size ( ) ; i + + )
node = factory . concat ( node , results [ i ] ) ;
2024-06-12 05:27:10 -05:00
return node ;
}
2024-07-17 06:42:24 -05:00
Node handle_memory ( Mem * mem )
2024-06-12 05:27:10 -05:00
{
2024-07-18 08:10:15 -05:00
// To simplify memory handling, the functional backend makes the following assumptions:
// - Since async2sync or clk2fflogic must be run to use the functional backend,
// we can assume that all ports are asynchronous.
// - Async rd/wr are always transparent and so we must do reads after writes,
// but we can ignore transparency_mask.
// - We ignore collision_x_mask because x is a dont care value for us anyway.
// - Since wr port j can only have priority over wr port i if j > i, if we do writes in
// ascending index order the result will obey the priorty relation.
2024-07-17 06:42:24 -05:00
vector < Node > read_results ;
2024-08-06 03:56:28 -05:00
auto & state = factory . add_state ( mem - > cell - > name , ID ( $ state ) , Sort ( ceil_log2 ( mem - > size ) , mem - > width ) ) ;
state . set_initial_value ( MemContents ( mem ) ) ;
Node node = factory . value ( state ) ;
2024-07-18 08:10:15 -05:00
for ( size_t i = 0 ; i < mem - > wr_ports . size ( ) ; i + + ) {
const auto & wr = mem - > wr_ports [ i ] ;
if ( wr . clk_enable )
log_error ( " Write port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
" Call async2sync or clk2fflogic to avoid this error. \n " , i , log_id ( mem - > module ) , log_id ( mem - > memid ) ) ;
2024-07-17 06:42:24 -05:00
Node en = enqueue ( driver_map ( DriveSpec ( wr . en ) ) ) ;
Node addr = enqueue ( driver_map ( DriveSpec ( wr . addr ) ) ) ;
Node new_data = enqueue ( driver_map ( DriveSpec ( wr . data ) ) ) ;
Node old_data = factory . memory_read ( node , addr ) ;
Node wr_data = simplifier . bitwise_mux ( old_data , new_data , en ) ;
node = factory . memory_write ( node , addr , wr_data ) ;
2024-06-12 05:27:10 -05:00
}
2024-07-18 08:10:15 -05:00
if ( mem - > rd_ports . empty ( ) )
log_error ( " Memory %s.%s has no read ports. This is not supported by the functional backend. "
" Call opt_clean to remove it. " , log_id ( mem - > module ) , log_id ( mem - > memid ) ) ;
for ( size_t i = 0 ; i < mem - > rd_ports . size ( ) ; i + + ) {
const auto & rd = mem - > rd_ports [ i ] ;
if ( rd . clk_enable )
log_error ( " Read port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
" Call memory_nordff to avoid this error. \n " , i , log_id ( mem - > module ) , log_id ( mem - > memid ) ) ;
Node addr = enqueue ( driver_map ( DriveSpec ( rd . addr ) ) ) ;
read_results . push_back ( factory . memory_read ( node , addr ) ) ;
}
2024-08-06 03:56:28 -05:00
state . set_next_value ( node ) ;
2024-06-12 05:27:10 -05:00
return concatenate_read_results ( mem , read_results ) ;
2024-05-01 05:48:04 -05:00
}
2024-07-17 10:33:34 -05:00
void process_cell ( Cell * cell )
{
if ( cell - > is_mem_cell ( ) ) {
Mem * mem = memories . at ( cell , nullptr ) ;
2024-07-18 08:10:15 -05:00
if ( mem = = nullptr ) {
log_assert ( cell - > has_memid ( ) ) ;
log_error ( " The design contains an unpacked memory at %s. This is not supported by the functional backend. "
" Call memory_collect to avoid this error. \n " , log_const ( cell - > parameters . at ( ID ( MEMID ) ) ) ) ;
}
2024-07-17 10:33:34 -05:00
Node node = handle_memory ( mem ) ;
factory . update_pending ( cell_outputs . at ( { cell , ID ( RD_DATA ) } ) , node ) ;
2024-07-24 11:37:17 -05:00
} else if ( RTLIL : : builtin_ff_cell_types ( ) . count ( cell - > type ) ) {
FfData ff ( & ff_initvals , cell ) ;
if ( ! ff . has_gclk )
log_error ( " The design contains a %s flip-flop at %s. This is not supported by the functional backend. "
" Call async2sync or clk2fflogic to avoid this error. \n " , log_id ( cell - > type ) , log_id ( cell ) ) ;
2024-08-06 03:56:28 -05:00
auto & state = factory . add_state ( ff . name , ID ( $ state ) , Sort ( ff . width ) ) ;
Node q_value = factory . value ( state ) ;
2024-07-24 11:37:17 -05:00
factory . suggest_name ( q_value , ff . name ) ;
factory . update_pending ( cell_outputs . at ( { cell , ID ( Q ) } ) , q_value ) ;
2024-08-06 03:56:28 -05:00
state . set_next_value ( enqueue ( ff . sig_d ) ) ;
state . set_initial_value ( ff . val_init ) ;
2024-07-17 10:33:34 -05:00
} else {
dict < IdString , Node > connections ;
IdString output_name ; // for the single output case
int n_outputs = 0 ;
for ( auto const & [ name , sigspec ] : cell - > connections ( ) ) {
2024-08-06 03:56:28 -05:00
if ( driver_map . celltypes . cell_input ( cell - > type , name ) & & sigspec . size ( ) > 0 )
2024-07-17 10:33:34 -05:00
connections . insert ( { name , enqueue ( DriveChunkPort ( cell , { name , sigspec } ) ) } ) ;
if ( driver_map . celltypes . cell_output ( cell - > type , name ) ) {
output_name = name ;
n_outputs + + ;
}
}
2024-08-06 03:56:28 -05:00
std : : variant < dict < IdString , Node > , Node > outputs = simplifier . handle ( cell - > name , cell - > type , cell - > parameters , connections ) ;
2024-07-17 10:33:34 -05:00
if ( auto * nodep = std : : get_if < Node > ( & outputs ) ; nodep ! = nullptr ) {
log_assert ( n_outputs = = 1 ) ;
factory . update_pending ( cell_outputs . at ( { cell , output_name } ) , * nodep ) ;
} else {
for ( auto [ name , node ] : std : : get < dict < IdString , Node > > ( outputs ) )
factory . update_pending ( cell_outputs . at ( { cell , name } ) , node ) ;
}
}
}
2024-08-08 09:38:24 -05:00
void undriven ( const char * name ) {
log_error ( " The design contains an undriven signal %s. This is not supported by the functional backend. "
" Call setundef with appropriate options to avoid this error. \n " , name ) ;
}
// we perform this check separately to give better error messages that include the wire or port name
void check_undriven ( DriveSpec const & spec , std : : string const & name ) {
for ( auto const & chunk : spec . chunks ( ) )
if ( chunk . is_none ( ) )
undriven ( name . c_str ( ) ) ;
}
public :
2024-05-01 05:48:04 -05:00
void process_queue ( )
{
for ( ; ! queue . empty ( ) ; queue . pop_front ( ) ) {
2024-07-17 10:33:34 -05:00
if ( auto p = std : : get_if < Cell * > ( & queue . front ( ) ) ; p ! = nullptr ) {
process_cell ( * p ) ;
continue ;
}
DriveSpec spec = std : : get < DriveSpec > ( queue . front ( ) ) ;
2024-07-17 06:42:24 -05:00
Node pending = graph_nodes . at ( spec ) ;
2024-05-01 05:48:04 -05:00
if ( spec . chunks ( ) . size ( ) > 1 ) {
auto chunks = spec . chunks ( ) ;
2024-07-17 06:42:24 -05:00
Node node = enqueue ( chunks [ 0 ] ) ;
for ( size_t i = 1 ; i < chunks . size ( ) ; i + + )
node = factory . concat ( node , enqueue ( chunks [ i ] ) ) ;
2024-05-01 05:48:04 -05:00
factory . update_pending ( pending , node ) ;
} else if ( spec . chunks ( ) . size ( ) = = 1 ) {
DriveChunk chunk = spec . chunks ( ) [ 0 ] ;
if ( chunk . is_wire ( ) ) {
DriveChunkWire wire_chunk = chunk . wire ( ) ;
if ( wire_chunk . is_whole ( ) ) {
if ( wire_chunk . wire - > port_input ) {
2024-08-06 03:56:28 -05:00
Node node = factory . value ( factory . ir ( ) . input ( wire_chunk . wire - > name ) ) ;
2024-05-01 05:48:04 -05:00
factory . suggest_name ( node , wire_chunk . wire - > name ) ;
factory . update_pending ( pending , node ) ;
} else {
DriveSpec driver = driver_map ( DriveSpec ( wire_chunk ) ) ;
2024-08-08 09:38:24 -05:00
check_undriven ( driver , RTLIL : : unescape_id ( wire_chunk . wire - > name ) ) ;
2024-07-17 06:42:24 -05:00
Node node = enqueue ( driver ) ;
2024-05-01 05:48:04 -05:00
factory . suggest_name ( node , wire_chunk . wire - > name ) ;
factory . update_pending ( pending , node ) ;
}
} else {
DriveChunkWire whole_wire ( wire_chunk . wire , 0 , wire_chunk . wire - > width ) ;
2024-07-17 06:42:24 -05:00
Node node = factory . slice ( enqueue ( whole_wire ) , wire_chunk . offset , wire_chunk . width ) ;
2024-05-01 05:48:04 -05:00
factory . update_pending ( pending , node ) ;
}
} else if ( chunk . is_port ( ) ) {
DriveChunkPort port_chunk = chunk . port ( ) ;
if ( port_chunk . is_whole ( ) ) {
if ( driver_map . celltypes . cell_output ( port_chunk . cell - > type , port_chunk . port ) ) {
2024-07-24 11:37:17 -05:00
Node node = enqueue_cell ( port_chunk . cell , port_chunk . port ) ;
factory . update_pending ( pending , node ) ;
2024-05-01 05:48:04 -05:00
} else {
DriveSpec driver = driver_map ( DriveSpec ( port_chunk ) ) ;
2024-08-08 09:38:24 -05:00
check_undriven ( driver , RTLIL : : unescape_id ( port_chunk . cell - > name ) + " port " + RTLIL : : unescape_id ( port_chunk . port ) ) ;
2024-05-01 05:48:04 -05:00
factory . update_pending ( pending , enqueue ( driver ) ) ;
}
} else {
DriveChunkPort whole_port ( port_chunk . cell , port_chunk . port , 0 , GetSize ( port_chunk . cell - > connections ( ) . at ( port_chunk . port ) ) ) ;
2024-07-17 06:42:24 -05:00
Node node = factory . slice ( enqueue ( whole_port ) , port_chunk . offset , port_chunk . width ) ;
2024-05-01 05:48:04 -05:00
factory . update_pending ( pending , node ) ;
}
} else if ( chunk . is_constant ( ) ) {
2024-07-17 06:42:24 -05:00
Node node = factory . constant ( chunk . constant ( ) ) ;
2024-05-01 05:48:04 -05:00
factory . suggest_name ( node , " $const " + std : : to_string ( chunk . size ( ) ) + " b " + chunk . constant ( ) . as_string ( ) ) ;
factory . update_pending ( pending , node ) ;
} else if ( chunk . is_multiple ( ) ) {
2024-07-18 08:23:00 -05:00
log_error ( " Signal %s has multiple drivers. This is not supported by the functional backend. "
" If tristate drivers are used, call tristate -formal to avoid this error. \n " , log_signal ( chunk ) ) ;
2024-05-01 05:48:04 -05:00
} else if ( chunk . is_none ( ) ) {
2024-08-08 09:38:24 -05:00
undriven ( log_signal ( chunk ) ) ;
2024-05-01 05:48:04 -05:00
} else {
log_error ( " unhandled drivespec: %s \n " , log_signal ( chunk ) ) ;
log_abort ( ) ;
}
} else {
log_abort ( ) ;
}
}
}
} ;
2024-07-25 06:10:59 -05:00
IR IR : : from_module ( Module * module ) {
IR ir ;
2024-06-20 10:40:02 -05:00
auto factory = ir . factory ( ) ;
2024-07-24 11:37:17 -05:00
FunctionalIRConstruction ctor ( module , factory ) ;
2024-06-20 10:40:02 -05:00
ctor . process_queue ( ) ;
ir . topological_sort ( ) ;
ir . forward_buf ( ) ;
return ir ;
}
2024-07-25 06:10:59 -05:00
void IR : : topological_sort ( ) {
2024-06-20 10:40:02 -05:00
Graph : : SccAdaptor compute_graph_scc ( _graph ) ;
bool scc = false ;
std : : vector < int > perm ;
2024-07-25 04:35:32 -05:00
TopoSortedSccs toposort ( compute_graph_scc , [ & ] ( int * begin , int * end ) {
2024-06-20 10:40:02 -05:00
perm . insert ( perm . end ( ) , begin , end ) ;
if ( end > begin + 1 )
{
2024-07-25 04:35:32 -05:00
log_warning ( " Combinational loop: \n " ) ;
for ( int * i = begin ; i ! = end ; + + i ) {
Node node ( _graph [ * i ] ) ;
log ( " - %s = %s \n " , RTLIL : : unescape_id ( node . name ( ) ) . c_str ( ) , node . to_string ( ) . c_str ( ) ) ;
}
2024-06-20 10:40:02 -05:00
log ( " \n " ) ;
scc = true ;
}
2024-07-25 04:35:32 -05:00
} ) ;
2024-08-06 03:56:28 -05:00
for ( const auto & [ name , state ] : _states )
if ( state . has_next_value ( ) )
toposort . process ( state . next_value ( ) . id ( ) ) ;
for ( const auto & [ name , output ] : _outputs )
if ( output . has_value ( ) )
toposort . process ( output . value ( ) . id ( ) ) ;
2024-07-25 04:35:32 -05:00
// any nodes untouched by this point are dead code and will be removed by permute
2024-06-20 10:40:02 -05:00
_graph . permute ( perm ) ;
2024-07-25 04:35:32 -05:00
if ( scc ) log_error ( " The design contains combinational loops. This is not supported by the functional backend. "
" Try `scc -select; simplemap; select -clear` to avoid this error. \n " ) ;
2024-06-20 10:40:02 -05:00
}
2024-05-01 05:48:04 -05:00
2024-07-16 11:55:26 -05:00
static IdString merge_name ( IdString a , IdString b ) {
2024-06-27 07:16:57 -05:00
if ( a [ 0 ] = = ' $ ' & & b [ 0 ] = = ' \\ ' )
return b ;
else
return a ;
}
2024-07-25 06:10:59 -05:00
void IR : : forward_buf ( ) {
2024-06-20 10:40:02 -05:00
std : : vector < int > perm , alias ;
perm . clear ( ) ;
for ( int i = 0 ; i < _graph . size ( ) ; + + i )
{
auto node = _graph [ i ] ;
if ( node . function ( ) . fn ( ) = = Fn : : buf & & node . arg ( 0 ) . index ( ) < i )
{
int target_index = alias [ node . arg ( 0 ) . index ( ) ] ;
auto target_node = _graph [ perm [ target_index ] ] ;
2024-06-27 07:16:57 -05:00
if ( node . has_sparse_attr ( ) ) {
if ( target_node . has_sparse_attr ( ) ) {
IdString id = merge_name ( node . sparse_attr ( ) , target_node . sparse_attr ( ) ) ;
target_node . sparse_attr ( ) = id ;
} else {
IdString id = node . sparse_attr ( ) ;
target_node . sparse_attr ( ) = id ;
}
}
2024-06-20 10:40:02 -05:00
alias . push_back ( target_index ) ;
}
else
{
alias . push_back ( GetSize ( perm ) ) ;
perm . push_back ( i ) ;
}
}
_graph . permute ( perm , alias ) ;
}
2024-07-17 05:53:42 -05:00
// Quoting routine to make error messages nicer
2024-07-04 04:22:51 -05:00
static std : : string quote_fmt ( const char * fmt )
{
std : : string r ;
for ( const char * p = fmt ; * p ! = 0 ; p + + ) {
switch ( * p ) {
case ' \n ' : r + = " \\ n " ; break ;
case ' \t ' : r + = " \\ t " ; break ;
case ' " ' : r + = " \\ \" " ; break ;
case ' \\ ' : r + = " \\ \\ " ; break ;
default : r + = * p ; break ;
}
}
return r ;
}
2024-07-25 06:10:59 -05:00
void Writer : : print_impl ( const char * fmt , vector < std : : function < void ( ) > > & fns )
2024-07-04 04:22:51 -05:00
{
size_t next_index = 0 ;
for ( const char * p = fmt ; * p ! = 0 ; p + + )
switch ( * p ) {
case ' { ' :
if ( * + + p = = ' { ' ) {
* os < < ' { ' ;
} else {
char * pe ;
size_t index = strtoul ( p , & pe , 10 ) ;
if ( * pe ! = ' } ' )
log_error ( " invalid format string: expected {<number>}, {} or {{, got \" %s \" : \" %s \" \n " ,
quote_fmt ( std : : string ( p - 1 , pe - p + 2 ) . c_str ( ) ) . c_str ( ) ,
quote_fmt ( fmt ) . c_str ( ) ) ;
if ( p = = pe )
index = next_index ;
else
p = pe ;
if ( index > = fns . size ( ) )
log_error ( " invalid format string: index %zu out of bounds (%zu): \" %s \" \n " , index , fns . size ( ) , quote_fmt ( fmt ) . c_str ( ) ) ;
fns [ index ] ( ) ;
next_index = index + 1 ;
}
break ;
case ' } ' :
p + + ;
if ( * p ! = ' } ' )
log_error ( " invalid format string: unescaped }: \" %s \" \n " , quote_fmt ( fmt ) . c_str ( ) ) ;
* os < < ' } ' ;
break ;
default :
* os < < * p ;
}
}
2024-07-25 06:10:59 -05:00
}
2024-06-20 10:40:02 -05:00
YOSYS_NAMESPACE_END