2013-01-05 04:13:26 -06:00
/*
* yosys - - Yosys Open SYnthesis Suite
*
2021-06-07 17:39:36 -05:00
* Copyright ( C ) 2012 Claire Xenia Wolf < claire @ yosyshq . com >
2015-07-02 04:14:30 -05:00
*
2013-01-05 04:13:26 -06: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 04:14:30 -05:00
*
2013-01-05 04:13:26 -06: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 .
*
*/
2016-02-01 04:49:11 -06:00
# include "kernel/yosys.h"
# include "kernel/sigtools.h"
2013-01-05 04:13:26 -06:00
# include "libparse.h"
# include <string.h>
2014-03-11 08:24:24 -05:00
# include <errno.h>
2013-01-05 04:13:26 -06:00
2014-09-27 09:17:53 -05:00
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
2013-01-05 04:13:26 -06:00
struct cell_mapping {
2019-08-15 12:05:08 -05:00
IdString cell_name ;
2019-08-16 03:22:04 -05:00
std : : map < std : : string , char > ports ;
2013-01-05 04:13:26 -06:00
} ;
2014-08-02 06:11:01 -05:00
static std : : map < RTLIL : : IdString , cell_mapping > cell_mappings ;
2013-01-05 04:13:26 -06:00
2019-08-15 12:05:08 -05:00
static void logmap ( IdString dff )
2013-01-05 04:13:26 -06:00
{
if ( cell_mappings . count ( dff ) = = 0 ) {
log ( " unmapped dff cell: %s \n " , dff . c_str ( ) ) ;
} else {
2013-10-24 11:20:06 -05:00
log ( " %s %s ( " , cell_mappings [ dff ] . cell_name . c_str ( ) , dff . substr ( 1 ) . c_str ( ) ) ;
2013-01-05 04:13:26 -06:00
bool first = true ;
for ( auto & port : cell_mappings [ dff ] . ports ) {
char arg [ 3 ] = { port . second , 0 , 0 } ;
if ( ' a ' < = arg [ 0 ] & & arg [ 0 ] < = ' z ' )
arg [ 1 ] = arg [ 0 ] - ( ' a ' - ' A ' ) , arg [ 0 ] = ' ~ ' ;
2013-10-24 11:20:06 -05:00
else
arg [ 1 ] = arg [ 0 ] , arg [ 0 ] = ' ' ;
2013-01-05 04:13:26 -06:00
log ( " %s.%s(%s) " , first ? " " : " , " , port . first . c_str ( ) , arg ) ;
first = false ;
}
log ( " ); \n " ) ;
}
}
static void logmap_all ( )
{
2019-08-15 12:05:08 -05:00
logmap ( ID ( $ _DFF_N_ ) ) ;
logmap ( ID ( $ _DFF_P_ ) ) ;
logmap ( ID ( $ _DFF_NN0_ ) ) ;
logmap ( ID ( $ _DFF_NN1_ ) ) ;
logmap ( ID ( $ _DFF_NP0_ ) ) ;
logmap ( ID ( $ _DFF_NP1_ ) ) ;
logmap ( ID ( $ _DFF_PN0_ ) ) ;
logmap ( ID ( $ _DFF_PN1_ ) ) ;
logmap ( ID ( $ _DFF_PP0_ ) ) ;
logmap ( ID ( $ _DFF_PP1_ ) ) ;
2024-11-03 10:24:40 -06:00
logmap ( ID ( $ _DFFE_NN_ ) ) ;
logmap ( ID ( $ _DFFE_NP_ ) ) ;
logmap ( ID ( $ _DFFE_PN_ ) ) ;
logmap ( ID ( $ _DFFE_PP_ ) ) ;
2019-08-15 12:05:08 -05:00
logmap ( ID ( $ _DFFSR_NNN_ ) ) ;
logmap ( ID ( $ _DFFSR_NNP_ ) ) ;
logmap ( ID ( $ _DFFSR_NPN_ ) ) ;
logmap ( ID ( $ _DFFSR_NPP_ ) ) ;
logmap ( ID ( $ _DFFSR_PNN_ ) ) ;
logmap ( ID ( $ _DFFSR_PNP_ ) ) ;
logmap ( ID ( $ _DFFSR_PPN_ ) ) ;
logmap ( ID ( $ _DFFSR_PPP_ ) ) ;
2013-01-05 04:13:26 -06:00
}
2024-11-03 10:24:40 -06:00
static bool parse_next_state ( const LibertyAst * cell , const LibertyAst * attr , std : : string & data_name , bool & data_not_inverted , std : : string & enable_name , bool & enable_not_inverted )
{
2024-11-06 18:51:23 -06:00
static pool < std : : string > warned_cells { } ;
2024-11-03 10:24:40 -06:00
if ( cell = = nullptr | | attr = = nullptr | | attr - > value . empty ( ) )
return false ;
2024-11-06 18:51:23 -06:00
auto expr = attr - > value ;
auto cell_name = cell - > args [ 0 ] ;
2024-11-03 10:24:40 -06:00
for ( size_t pos = expr . find_first_of ( " \" \t " ) ; pos ! = std : : string : : npos ; pos = expr . find_first_of ( " \" \t " ) )
expr . erase ( pos , 1 ) ;
// if this isn't an enable flop, the next_state variable is usually just the input pin name.
if ( expr [ expr . size ( ) - 1 ] = = ' \' ' ) {
data_name = expr . substr ( 0 , expr . size ( ) - 1 ) ;
data_not_inverted = false ;
} else if ( expr [ 0 ] = = ' ! ' ) {
data_name = expr . substr ( 1 , expr . size ( ) - 1 ) ;
data_not_inverted = false ;
} else {
data_name = expr ;
data_not_inverted = true ;
}
for ( auto child : cell - > children )
if ( child - > id = = " pin " & & child - > args . size ( ) = = 1 & & child - > args [ 0 ] = = data_name )
return true ;
// the next_state variable isn't just a pin name; perhaps this is an enable?
2024-11-06 18:51:23 -06:00
auto helper = LibertyExpression : : Lexer ( expr ) ;
auto tree = LibertyExpression : : parse ( helper ) ;
2024-11-03 10:24:40 -06:00
2024-11-06 18:51:23 -06:00
if ( tree . kind = = LibertyExpression : : Kind : : EMPTY ) {
if ( ! warned_cells . count ( cell_name ) ) {
log_warning ( " Invalid expression '%s' in next_state attribute of cell '%s' - skipping. \n " , expr . c_str ( ) , cell_name . c_str ( ) ) ;
warned_cells . insert ( cell_name ) ;
}
2024-11-03 10:24:40 -06:00
return false ;
}
auto pin_names = pool < std : : string > { } ;
tree . get_pin_names ( pin_names ) ;
// from the `ff` block, we know the flop output signal name for loopback.
auto ff = cell - > find ( " ff " ) ;
if ( ff = = nullptr | | ff - > args . size ( ) ! = 2 )
return false ;
auto ff_output = ff - > args . at ( 0 ) ;
// This test is redundant with the one in enable_pin, but we're in a
// position that gives better diagnostics here.
if ( ! pin_names . count ( ff_output ) ) {
2024-11-06 18:51:23 -06:00
if ( ! warned_cells . count ( cell_name ) ) {
log_warning ( " Inference failed on expression '%s' in next_state attribute of cell '%s' because it does not contain ff output '%s' - skipping. \n " , expr . c_str ( ) , cell_name . c_str ( ) , ff_output . c_str ( ) ) ;
warned_cells . insert ( cell_name ) ;
}
2024-11-03 10:24:40 -06:00
return false ;
}
2024-11-06 18:51:23 -06:00
data_not_inverted = true ;
data_name = " " ;
enable_not_inverted = true ;
enable_name = " " ;
if ( pin_names . size ( ) = = 3 & & pin_names . count ( ff_output ) ) {
pin_names . erase ( ff_output ) ;
auto pins = std : : vector < std : : string > ( pin_names . begin ( ) , pin_names . end ( ) ) ;
int lut = 0 ;
for ( int n = 0 ; n < 8 ; n + + ) {
auto values = dict < std : : string , bool > { } ;
values . insert ( std : : make_pair ( pins [ 0 ] , ( n & 1 ) = = 1 ) ) ;
values . insert ( std : : make_pair ( pins [ 1 ] , ( n & 2 ) = = 2 ) ) ;
values . insert ( std : : make_pair ( ff_output , ( n & 4 ) = = 4 ) ) ;
if ( tree . eval ( values ) )
lut | = 1 < < n ;
}
// the ff output Q is in a known bit location, so we now just have to compare the LUT mask to known values to find the enable pin and polarity.
if ( lut = = 0xD8 ) {
data_name = pins [ 1 ] ;
enable_name = pins [ 0 ] ;
return true ;
}
if ( lut = = 0xB8 ) {
data_name = pins [ 0 ] ;
enable_name = pins [ 1 ] ;
return true ;
}
enable_not_inverted = false ;
if ( lut = = 0xE4 ) {
data_name = pins [ 1 ] ;
enable_name = pins [ 0 ] ;
return true ;
}
if ( lut = = 0xE2 ) {
data_name = pins [ 0 ] ;
enable_name = pins [ 1 ] ;
return true ;
}
// this does not match an enable flop.
2024-11-03 10:24:40 -06:00
}
2024-11-06 18:51:23 -06:00
if ( ! warned_cells . count ( cell_name ) ) {
log_warning ( " Inference failed on expression '%s' in next_state attribute of cell '%s' because it does not evaluate to an enable flop - skipping. \n " , expr . c_str ( ) , cell_name . c_str ( ) ) ;
warned_cells . insert ( cell_name ) ;
}
return false ;
2024-11-03 10:24:40 -06:00
}
2024-08-13 11:43:31 -05:00
static bool parse_pin ( const LibertyAst * cell , const LibertyAst * attr , std : : string & pin_name , bool & pin_pol )
2013-01-05 04:13:26 -06:00
{
2020-04-05 22:02:40 -05:00
if ( cell = = nullptr | | attr = = nullptr | | attr - > value . empty ( ) )
2013-01-05 04:13:26 -06:00
return false ;
2015-07-02 04:14:30 -05:00
2013-01-05 04:13:26 -06:00
std : : string value = attr - > value ;
2013-10-15 23:32:35 -05:00
for ( size_t pos = value . find_first_of ( " \" \t () " ) ; pos ! = std : : string : : npos ; pos = value . find_first_of ( " \" \t () " ) )
2013-01-05 04:13:26 -06:00
value . erase ( pos , 1 ) ;
if ( value [ value . size ( ) - 1 ] = = ' \' ' ) {
pin_name = value . substr ( 0 , value . size ( ) - 1 ) ;
pin_pol = false ;
2013-10-15 23:32:35 -05:00
} else if ( value [ 0 ] = = ' ! ' ) {
pin_name = value . substr ( 1 , value . size ( ) - 1 ) ;
pin_pol = false ;
2013-01-05 04:13:26 -06:00
} else {
pin_name = value ;
pin_pol = true ;
}
for ( auto child : cell - > children )
if ( child - > id = = " pin " & & child - > args . size ( ) = = 1 & & child - > args [ 0 ] = = pin_name )
return true ;
2018-11-03 12:07:51 -05:00
2018-11-06 05:11:52 -06:00
/* If we end up here, the pin specified in the attribute does not exist, which is an error,
or , the attribute contains an expression which we do not yet support .
For now , we ' ll simply produce a warning to let the user know something is up .
*/
if ( pin_name . find_first_of ( " ^*|& " ) = = std : : string : : npos ) {
log_warning ( " Malformed liberty file - cannot find pin '%s' in cell '%s' - skipping. \n " , pin_name . c_str ( ) , cell - > args [ 0 ] . c_str ( ) ) ;
}
else {
log_warning ( " Found unsupported expression '%s' in pin attribute of cell '%s' - skipping. \n " , pin_name . c_str ( ) , cell - > args [ 0 ] . c_str ( ) ) ;
}
2018-11-03 12:07:51 -05:00
2013-01-05 04:13:26 -06:00
return false ;
}
2024-11-03 10:24:40 -06:00
static void find_cell ( const LibertyAst * ast , IdString cell_type , bool clkpol , bool has_reset , bool rstpol , bool rstval , bool has_enable , bool enapol , std : : vector < std : : string > & dont_use_cells )
2013-01-05 04:13:26 -06:00
{
2024-08-13 11:43:31 -05:00
const LibertyAst * best_cell = nullptr ;
2019-08-16 03:22:04 -05:00
std : : map < std : : string , char > best_cell_ports ;
2013-01-05 04:13:26 -06:00
int best_cell_pins = 0 ;
2016-04-05 05:51:04 -05:00
bool best_cell_noninv = false ;
2014-10-18 08:20:38 -05:00
double best_cell_area = 0 ;
2013-01-05 04:13:26 -06:00
if ( ast - > id ! = " library " )
log_error ( " Format error in liberty file. \n " ) ;
for ( auto cell : ast - > children )
{
if ( cell - > id ! = " cell " | | cell - > args . size ( ) ! = 1 )
continue ;
2024-08-13 11:43:31 -05:00
const LibertyAst * dn = cell - > find ( " dont_use " ) ;
2020-04-05 22:02:40 -05:00
if ( dn ! = nullptr & & dn - > value = = " true " )
2015-05-31 00:51:12 -05:00
continue ;
2024-02-19 12:00:18 -06:00
bool dont_use = false ;
for ( std : : string & dont_use_cell : dont_use_cells )
{
2024-02-20 10:04:55 -06:00
if ( patmatch ( dont_use_cell . c_str ( ) , cell - > args [ 0 ] . c_str ( ) ) )
2024-02-19 12:00:18 -06:00
{
dont_use = true ;
break ;
}
}
if ( dont_use )
continue ;
2024-08-13 11:43:31 -05:00
const LibertyAst * ff = cell - > find ( " ff " ) ;
2020-04-05 22:02:40 -05:00
if ( ff = = nullptr )
2013-01-05 04:13:26 -06:00
continue ;
2024-11-03 10:24:40 -06:00
std : : string cell_clk_pin , cell_rst_pin , cell_next_pin , cell_enable_pin ;
bool cell_clk_pol , cell_rst_pol , cell_next_pol , cell_enable_pol ;
2013-01-05 04:13:26 -06:00
if ( ! parse_pin ( cell , ff - > find ( " clocked_on " ) , cell_clk_pin , cell_clk_pol ) | | cell_clk_pol ! = clkpol )
continue ;
2024-11-03 10:24:40 -06:00
if ( ! parse_next_state ( cell , ff - > find ( " next_state " ) , cell_next_pin , cell_next_pol , cell_enable_pin , cell_enable_pol ) | | ( has_enable & & ( cell_enable_pin . empty ( ) | | cell_enable_pol ! = enapol ) ) )
2013-01-05 04:13:26 -06:00
continue ;
if ( has_reset & & rstval = = false ) {
if ( ! parse_pin ( cell , ff - > find ( " clear " ) , cell_rst_pin , cell_rst_pol ) | | cell_rst_pol ! = rstpol )
continue ;
}
if ( has_reset & & rstval = = true ) {
if ( ! parse_pin ( cell , ff - > find ( " preset " ) , cell_rst_pin , cell_rst_pol ) | | cell_rst_pol ! = rstpol )
continue ;
}
2019-08-16 03:22:04 -05:00
std : : map < std : : string , char > this_cell_ports ;
2013-01-05 04:13:26 -06:00
this_cell_ports [ cell_clk_pin ] = ' C ' ;
if ( has_reset )
this_cell_ports [ cell_rst_pin ] = ' R ' ;
2024-11-03 10:24:40 -06:00
if ( has_enable )
this_cell_ports [ cell_enable_pin ] = ' E ' ;
2013-01-05 04:13:26 -06:00
this_cell_ports [ cell_next_pin ] = ' D ' ;
2014-10-18 08:20:38 -05:00
double area = 0 ;
2024-08-13 11:43:31 -05:00
const LibertyAst * ar = cell - > find ( " area " ) ;
2020-04-05 22:02:40 -05:00
if ( ar ! = nullptr & & ! ar - > value . empty ( ) )
2013-12-21 01:42:37 -06:00
area = atof ( ar - > value . c_str ( ) ) ;
2013-01-05 04:13:26 -06:00
int num_pins = 0 ;
bool found_output = false ;
2016-04-05 05:51:04 -05:00
bool found_noninv_output = false ;
2013-01-05 04:13:26 -06:00
for ( auto pin : cell - > children )
{
if ( pin - > id ! = " pin " | | pin - > args . size ( ) ! = 1 )
continue ;
2024-08-13 11:43:31 -05:00
const LibertyAst * dir = pin - > find ( " direction " ) ;
2020-04-05 22:02:40 -05:00
if ( dir = = nullptr | | dir - > value = = " internal " )
2013-01-05 04:13:26 -06:00
continue ;
num_pins + + ;
if ( dir - > value = = " input " & & this_cell_ports . count ( pin - > args [ 0 ] ) = = 0 )
goto continue_cell_loop ;
2024-08-13 11:43:31 -05:00
const LibertyAst * func = pin - > find ( " function " ) ;
2020-04-05 22:02:40 -05:00
if ( dir - > value = = " output " & & func ! = nullptr ) {
2013-01-05 04:13:26 -06:00
std : : string value = func - > value ;
for ( size_t pos = value . find_first_of ( " \" \t " ) ; pos ! = std : : string : : npos ; pos = value . find_first_of ( " \" \t " ) )
value . erase ( pos , 1 ) ;
2016-02-01 04:49:11 -06:00
if ( value = = ff - > args [ 0 ] ) {
this_cell_ports [ pin - > args [ 0 ] ] = cell_next_pol ? ' Q ' : ' q ' ;
2016-04-05 05:51:04 -05:00
if ( cell_next_pol )
found_noninv_output = true ;
2016-02-01 04:49:11 -06:00
found_output = true ;
} else
if ( value = = ff - > args [ 1 ] ) {
this_cell_ports [ pin - > args [ 0 ] ] = cell_next_pol ? ' q ' : ' Q ' ;
2016-04-05 05:51:04 -05:00
if ( ! cell_next_pol )
found_noninv_output = true ;
2013-01-05 04:13:26 -06:00
found_output = true ;
}
}
if ( this_cell_ports . count ( pin - > args [ 0 ] ) = = 0 )
this_cell_ports [ pin - > args [ 0 ] ] = 0 ;
}
2020-04-05 22:02:40 -05:00
if ( ! found_output | | ( best_cell ! = nullptr & & ( num_pins > best_cell_pins | | ( best_cell_noninv & & ! found_noninv_output ) ) ) )
2013-01-05 04:13:26 -06:00
continue ;
2020-04-05 22:02:40 -05:00
if ( best_cell ! = nullptr & & num_pins = = best_cell_pins & & area > best_cell_area )
2013-12-21 01:42:37 -06:00
continue ;
2013-01-05 04:13:26 -06:00
best_cell = cell ;
best_cell_pins = num_pins ;
2013-12-21 01:42:37 -06:00
best_cell_area = area ;
2016-04-05 05:51:04 -05:00
best_cell_noninv = found_noninv_output ;
2013-01-05 04:13:26 -06:00
best_cell_ports . swap ( this_cell_ports ) ;
continue_cell_loop : ;
}
2020-04-05 22:02:40 -05:00
if ( best_cell ! = nullptr ) {
2016-04-05 05:51:04 -05:00
log ( " cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s. \n " ,
best_cell - > args [ 0 ] . c_str ( ) , best_cell_noninv ? " non " : " " , best_cell_pins , best_cell_area , cell_type . c_str ( ) ) ;
2020-07-02 11:22:43 -05:00
cell_mappings [ cell_type ] . cell_name = RTLIL : : escape_id ( best_cell - > args [ 0 ] ) ;
cell_mappings [ cell_type ] . ports = best_cell_ports ;
2013-01-05 04:13:26 -06:00
}
}
2024-11-03 10:24:40 -06:00
static void find_cell_sr ( const LibertyAst * ast , IdString cell_type , bool clkpol , bool setpol , bool clrpol , bool has_enable , bool enapol , std : : vector < std : : string > & dont_use_cells )
2013-10-24 11:20:06 -05:00
{
2024-08-13 11:43:31 -05:00
const LibertyAst * best_cell = nullptr ;
2019-08-16 03:22:04 -05:00
std : : map < std : : string , char > best_cell_ports ;
2013-10-24 11:20:06 -05:00
int best_cell_pins = 0 ;
2016-04-05 05:51:04 -05:00
bool best_cell_noninv = false ;
2014-10-18 08:20:38 -05:00
double best_cell_area = 0 ;
2013-10-24 11:20:06 -05:00
2024-11-06 18:51:23 -06:00
log_assert ( ! enapol & & " set/reset cell with enable is unimplemented due to lack of cells for testing " ) ;
2013-10-24 11:20:06 -05:00
if ( ast - > id ! = " library " )
log_error ( " Format error in liberty file. \n " ) ;
for ( auto cell : ast - > children )
{
if ( cell - > id ! = " cell " | | cell - > args . size ( ) ! = 1 )
continue ;
2024-08-13 11:43:31 -05:00
const LibertyAst * dn = cell - > find ( " dont_use " ) ;
2020-04-05 22:02:40 -05:00
if ( dn ! = nullptr & & dn - > value = = " true " )
2018-04-05 04:01:32 -05:00
continue ;
2024-02-19 12:00:18 -06:00
bool dont_use = false ;
for ( std : : string & dont_use_cell : dont_use_cells )
{
2024-02-20 10:04:55 -06:00
if ( patmatch ( dont_use_cell . c_str ( ) , cell - > args [ 0 ] . c_str ( ) ) )
2024-02-19 12:00:18 -06:00
{
dont_use = true ;
break ;
}
}
if ( dont_use )
continue ;
2024-08-13 11:43:31 -05:00
const LibertyAst * ff = cell - > find ( " ff " ) ;
2020-04-05 22:02:40 -05:00
if ( ff = = nullptr )
2013-10-24 11:20:06 -05:00
continue ;
2024-11-03 10:24:40 -06:00
std : : string cell_clk_pin , cell_set_pin , cell_clr_pin , cell_next_pin , cell_enable_pin ;
bool cell_clk_pol , cell_set_pol , cell_clr_pol , cell_next_pol , cell_enable_pol ;
2013-10-24 11:20:06 -05:00
if ( ! parse_pin ( cell , ff - > find ( " clocked_on " ) , cell_clk_pin , cell_clk_pol ) | | cell_clk_pol ! = clkpol )
continue ;
2024-11-03 10:24:40 -06:00
if ( ! parse_next_state ( cell , ff - > find ( " next_state " ) , cell_next_pin , cell_next_pol , cell_enable_pin , cell_enable_pol ) )
2013-10-24 11:20:06 -05:00
continue ;
if ( ! parse_pin ( cell , ff - > find ( " preset " ) , cell_set_pin , cell_set_pol ) | | cell_set_pol ! = setpol )
continue ;
if ( ! parse_pin ( cell , ff - > find ( " clear " ) , cell_clr_pin , cell_clr_pol ) | | cell_clr_pol ! = clrpol )
continue ;
2019-08-16 03:22:04 -05:00
std : : map < std : : string , char > this_cell_ports ;
2013-10-24 11:20:06 -05:00
this_cell_ports [ cell_clk_pin ] = ' C ' ;
this_cell_ports [ cell_set_pin ] = ' S ' ;
this_cell_ports [ cell_clr_pin ] = ' R ' ;
2024-11-03 10:24:40 -06:00
if ( has_enable )
this_cell_ports [ cell_enable_pin ] = ' E ' ;
2013-10-24 11:20:06 -05:00
this_cell_ports [ cell_next_pin ] = ' D ' ;
2014-10-18 08:20:38 -05:00
double area = 0 ;
2024-08-13 11:43:31 -05:00
const LibertyAst * ar = cell - > find ( " area " ) ;
2020-04-05 22:02:40 -05:00
if ( ar ! = nullptr & & ! ar - > value . empty ( ) )
2013-12-21 01:42:37 -06:00
area = atof ( ar - > value . c_str ( ) ) ;
2013-10-24 11:20:06 -05:00
int num_pins = 0 ;
bool found_output = false ;
2016-04-05 05:51:04 -05:00
bool found_noninv_output = false ;
2013-10-24 11:20:06 -05:00
for ( auto pin : cell - > children )
{
if ( pin - > id ! = " pin " | | pin - > args . size ( ) ! = 1 )
continue ;
2024-08-13 11:43:31 -05:00
const LibertyAst * dir = pin - > find ( " direction " ) ;
2020-04-05 22:02:40 -05:00
if ( dir = = nullptr | | dir - > value = = " internal " )
2013-10-24 11:20:06 -05:00
continue ;
num_pins + + ;
if ( dir - > value = = " input " & & this_cell_ports . count ( pin - > args [ 0 ] ) = = 0 )
goto continue_cell_loop ;
2024-08-13 11:43:31 -05:00
const LibertyAst * func = pin - > find ( " function " ) ;
2020-04-05 22:02:40 -05:00
if ( dir - > value = = " output " & & func ! = nullptr ) {
2013-10-24 11:20:06 -05:00
std : : string value = func - > value ;
for ( size_t pos = value . find_first_of ( " \" \t " ) ; pos ! = std : : string : : npos ; pos = value . find_first_of ( " \" \t " ) )
value . erase ( pos , 1 ) ;
2016-02-01 04:49:11 -06:00
if ( value = = ff - > args [ 0 ] ) {
this_cell_ports [ pin - > args [ 0 ] ] = cell_next_pol ? ' Q ' : ' q ' ;
2016-04-05 05:51:04 -05:00
if ( cell_next_pol )
found_noninv_output = true ;
2016-02-01 04:49:11 -06:00
found_output = true ;
} else
if ( value = = ff - > args [ 1 ] ) {
this_cell_ports [ pin - > args [ 0 ] ] = cell_next_pol ? ' q ' : ' Q ' ;
2016-04-05 05:51:04 -05:00
if ( ! cell_next_pol )
found_noninv_output = true ;
2013-10-24 11:20:06 -05:00
found_output = true ;
}
}
if ( this_cell_ports . count ( pin - > args [ 0 ] ) = = 0 )
this_cell_ports [ pin - > args [ 0 ] ] = 0 ;
}
2020-04-05 22:02:40 -05:00
if ( ! found_output | | ( best_cell ! = nullptr & & ( num_pins > best_cell_pins | | ( best_cell_noninv & & ! found_noninv_output ) ) ) )
2013-10-24 11:20:06 -05:00
continue ;
2020-04-05 22:02:40 -05:00
if ( best_cell ! = nullptr & & num_pins = = best_cell_pins & & area > best_cell_area )
2013-12-21 01:42:37 -06:00
continue ;
2013-10-24 11:20:06 -05:00
best_cell = cell ;
best_cell_pins = num_pins ;
2013-12-21 01:42:37 -06:00
best_cell_area = area ;
2016-04-05 05:51:04 -05:00
best_cell_noninv = found_noninv_output ;
2013-10-24 11:20:06 -05:00
best_cell_ports . swap ( this_cell_ports ) ;
continue_cell_loop : ;
}
2020-04-05 22:02:40 -05:00
if ( best_cell ! = nullptr ) {
2016-04-05 05:51:04 -05:00
log ( " cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s. \n " ,
best_cell - > args [ 0 ] . c_str ( ) , best_cell_noninv ? " non " : " " , best_cell_pins , best_cell_area , cell_type . c_str ( ) ) ;
2020-07-02 11:22:43 -05:00
cell_mappings [ cell_type ] . cell_name = RTLIL : : escape_id ( best_cell - > args [ 0 ] ) ;
cell_mappings [ cell_type ] . ports = best_cell_ports ;
2013-10-24 11:20:06 -05:00
}
}
2020-07-02 11:22:43 -05:00
static void dfflibmap ( RTLIL : : Design * design , RTLIL : : Module * module )
2013-01-05 04:13:26 -06:00
{
log ( " Mapping DFF cells in module `%s': \n " , module - > name . c_str ( ) ) ;
2016-02-01 04:49:11 -06:00
dict < SigBit , pool < Cell * > > notmap ;
SigMap sigmap ( module ) ;
2013-01-05 04:13:26 -06:00
std : : vector < RTLIL : : Cell * > cell_list ;
2020-04-05 22:02:40 -05:00
for ( auto cell : module - > cells ( ) ) {
if ( design - > selected ( module , cell ) & & cell_mappings . count ( cell - > type ) > 0 )
cell_list . push_back ( cell ) ;
if ( cell - > type = = ID ( $ _NOT_ ) )
notmap [ sigmap ( cell - > getPort ( ID : : A ) ) ] . insert ( cell ) ;
2013-01-05 04:13:26 -06:00
}
std : : map < std : : string , int > stats ;
2014-07-25 08:05:18 -05:00
for ( auto cell : cell_list )
{
auto cell_type = cell - > type ;
auto cell_name = cell - > name ;
2014-07-26 07:32:50 -05:00
auto cell_connections = cell - > connections ( ) ;
2017-08-31 15:51:56 -05:00
std : : string src = cell - > get_src_attribute ( ) ;
2017-08-29 15:46:35 -05:00
2014-07-25 08:05:18 -05:00
module - > remove ( cell ) ;
cell_mapping & cm = cell_mappings [ cell_type ] ;
2020-07-02 11:22:43 -05:00
RTLIL : : Cell * new_cell = module - > addCell ( cell_name , cm . cell_name ) ;
2014-07-25 08:05:18 -05:00
2017-08-31 15:51:56 -05:00
new_cell - > set_src_attribute ( src ) ;
2017-08-29 15:46:35 -05:00
2016-02-01 04:49:11 -06:00
bool has_q = false , has_qn = false ;
for ( auto & port : cm . ports ) {
if ( port . second = = ' Q ' ) has_q = true ;
if ( port . second = = ' q ' ) has_qn = true ;
}
2013-01-05 04:13:26 -06:00
for ( auto & port : cm . ports ) {
RTLIL : : SigSpec sig ;
if ( ' A ' < = port . second & & port . second < = ' Z ' ) {
2014-07-25 08:05:18 -05:00
sig = cell_connections [ std : : string ( " \\ " ) + port . second ] ;
2013-01-05 04:13:26 -06:00
} else
2014-08-27 12:44:12 -05:00
if ( port . second = = ' q ' ) {
RTLIL : : SigSpec old_sig = cell_connections [ std : : string ( " \\ " ) + char ( port . second - ( ' a ' - ' A ' ) ) ] ;
2014-10-10 09:59:44 -05:00
sig = module - > addWire ( NEW_ID , GetSize ( old_sig ) ) ;
2016-02-01 04:49:11 -06:00
if ( has_q & & has_qn ) {
for ( auto & it : notmap [ sigmap ( old_sig ) ] ) {
2019-08-15 16:50:10 -05:00
module - > connect ( it - > getPort ( ID : : Y ) , sig ) ;
it - > setPort ( ID : : Y , module - > addWire ( NEW_ID , GetSize ( old_sig ) ) ) ;
2016-02-01 04:49:11 -06:00
}
} else {
module - > addNotGate ( NEW_ID , sig , old_sig ) ;
}
2014-08-27 12:44:12 -05:00
} else
2013-01-05 04:13:26 -06:00
if ( ' a ' < = port . second & & port . second < = ' z ' ) {
2014-07-25 08:05:18 -05:00
sig = cell_connections [ std : : string ( " \\ " ) + char ( port . second - ( ' a ' - ' A ' ) ) ] ;
2014-08-15 07:11:40 -05:00
sig = module - > NotGate ( NEW_ID , sig ) ;
2013-10-24 11:20:06 -05:00
} else
if ( port . second = = ' 0 ' | | port . second = = ' 1 ' ) {
sig = RTLIL : : SigSpec ( port . second = = ' 0 ' ? 0 : 1 , 1 ) ;
} else
2016-02-01 04:49:11 -06:00
if ( port . second = = 0 ) {
sig = module - > addWire ( NEW_ID ) ;
} else
2013-10-24 11:20:06 -05:00
log_abort ( ) ;
2019-08-16 03:22:04 -05:00
new_cell - > setPort ( " \\ " + port . first , sig ) ;
2013-01-05 04:13:26 -06:00
}
2014-07-25 08:05:18 -05:00
stats [ stringf ( " mapped %%d %s cells to %s cells. \n " , cell_type . c_str ( ) , new_cell - > type . c_str ( ) ) ] + + ;
2013-01-05 04:13:26 -06:00
}
for ( auto & stat : stats )
log ( stat . first . c_str ( ) , stat . second ) ;
}
struct DfflibmapPass : public Pass {
2013-02-28 04:14:59 -06:00
DfflibmapPass ( ) : Pass ( " dfflibmap " , " technology mapping of flip-flops " ) { }
2020-06-18 18:34:52 -05:00
void help ( ) override
2013-02-28 04:14:59 -06:00
{
log ( " \n " ) ;
2024-02-19 12:00:18 -06:00
log ( " dfflibmap [-prepare] [-map-only] [-info] [-dont_use <cell_name>] -liberty <file> [selection] \n " ) ;
2013-02-28 04:14:59 -06:00
log ( " \n " ) ;
log ( " Map internal flip-flop cells to the flip-flop cells in the technology \n " ) ;
log ( " library specified in the given liberty file. \n " ) ;
log ( " \n " ) ;
log ( " This pass may add inverters as needed. Therefore it is recommended to \n " ) ;
log ( " first run this pass and then map the logic paths to the target technology. \n " ) ;
log ( " \n " ) ;
2014-12-24 05:56:05 -06:00
log ( " When called with -prepare, this command will convert the internal FF cells \n " ) ;
log ( " to the internal cell types that best match the cells found in the given \n " ) ;
2020-07-02 11:22:43 -05:00
log ( " liberty file, but won't actually map them to the target cells. \n " ) ;
log ( " \n " ) ;
log ( " When called with -map-only, this command will only map internal cell \n " ) ;
log ( " types that are already of exactly the right type to match the target \n " ) ;
log ( " cells, leaving remaining internal cells untouched. \n " ) ;
log ( " \n " ) ;
log ( " When called with -info, this command will only print the target cell \n " ) ;
2022-08-23 17:28:27 -05:00
log ( " list, along with their associated internal cell types, and the arguments \n " ) ;
2020-07-02 11:22:43 -05:00
log ( " that would be passed to the dfflegalize pass. The design will not be \n " ) ;
log ( " changed. \n " ) ;
2014-12-24 05:56:05 -06:00
log ( " \n " ) ;
2024-02-19 12:00:18 -06:00
log ( " When called with -dont_use, this command will not map to the specified cell \n " ) ;
log ( " name as an alternative to setting the dont_use property in the Liberty file. \n " ) ;
log ( " This argument can be called multiple times with different cell names. This \n " ) ;
log ( " argument also supports simple glob patterns in the cell name. \n " ) ;
log ( " \n " ) ;
2013-02-28 04:14:59 -06:00
}
2020-06-18 18:34:52 -05:00
void execute ( std : : vector < std : : string > args , RTLIL : : Design * design ) override
2013-01-05 04:13:26 -06:00
{
2016-04-21 16:28:37 -05:00
log_header ( design , " Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file). \n " ) ;
2020-07-02 11:22:43 -05:00
log_push ( ) ;
2013-01-05 04:13:26 -06:00
std : : string liberty_file ;
2014-12-24 05:19:20 -06:00
bool prepare_mode = false ;
2020-07-02 11:22:43 -05:00
bool map_only_mode = false ;
bool info_mode = false ;
2013-01-05 04:13:26 -06:00
2024-02-19 12:00:18 -06:00
std : : vector < std : : string > dont_use_cells ;
2013-01-05 04:13:26 -06:00
size_t argidx ;
for ( argidx = 1 ; argidx < args . size ( ) ; argidx + + )
{
std : : string arg = args [ argidx ] ;
if ( arg = = " -liberty " & & argidx + 1 < args . size ( ) ) {
liberty_file = args [ + + argidx ] ;
2015-09-18 04:55:57 -05:00
rewrite_filename ( liberty_file ) ;
2013-01-05 04:13:26 -06:00
continue ;
}
2014-12-24 05:19:20 -06:00
if ( arg = = " -prepare " ) {
prepare_mode = true ;
continue ;
}
2020-07-02 11:22:43 -05:00
if ( arg = = " -map-only " ) {
map_only_mode = true ;
continue ;
}
if ( arg = = " -info " ) {
info_mode = true ;
continue ;
}
2024-02-19 12:00:18 -06:00
if ( arg = = " -dont_use " & & argidx + 1 < args . size ( ) ) {
dont_use_cells . push_back ( args [ + + argidx ] ) ;
continue ;
}
2013-01-05 04:13:26 -06:00
break ;
}
extra_args ( args , argidx , design ) ;
2020-07-02 11:22:43 -05:00
int modes = 0 ;
if ( prepare_mode )
modes + + ;
if ( map_only_mode )
modes + + ;
if ( info_mode )
modes + + ;
if ( modes > 1 )
log_cmd_error ( " Only one of -prepare, -map-only, or -info options should be given! \n " ) ;
2013-01-05 04:13:26 -06:00
if ( liberty_file . empty ( ) )
log_cmd_error ( " Missing `-liberty liberty_file' option! \n " ) ;
2014-08-23 08:03:55 -05:00
std : : ifstream f ;
f . open ( liberty_file . c_str ( ) ) ;
if ( f . fail ( ) )
2013-01-05 04:13:26 -06:00
log_cmd_error ( " Can't open liberty file `%s': %s \n " , liberty_file . c_str ( ) , strerror ( errno ) ) ;
2014-01-14 11:57:47 -06:00
LibertyParser libparser ( f ) ;
2014-08-23 08:03:55 -05:00
f . close ( ) ;
2013-01-05 04:13:26 -06:00
2024-11-03 10:24:40 -06:00
find_cell ( libparser . ast , ID ( $ _DFF_N_ ) , false , false , false , false , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFF_P_ ) , true , false , false , false , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFF_NN0_ ) , false , true , false , false , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFF_NN1_ ) , false , true , false , true , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFF_NP0_ ) , false , true , true , false , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFF_NP1_ ) , false , true , true , true , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFF_PN0_ ) , true , true , false , false , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFF_PN1_ ) , true , true , false , true , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFF_PP0_ ) , true , true , true , false , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFF_PP1_ ) , true , true , true , true , false , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFFE_NN_ ) , false , false , false , false , true , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFFE_NP_ ) , false , false , false , false , true , true , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFFE_PN_ ) , true , false , false , false , true , false , dont_use_cells ) ;
find_cell ( libparser . ast , ID ( $ _DFFE_PP_ ) , true , false , false , false , true , true , dont_use_cells ) ;
find_cell_sr ( libparser . ast , ID ( $ _DFFSR_NNN_ ) , false , false , false , false , false , dont_use_cells ) ;
find_cell_sr ( libparser . ast , ID ( $ _DFFSR_NNP_ ) , false , false , true , false , false , dont_use_cells ) ;
find_cell_sr ( libparser . ast , ID ( $ _DFFSR_NPN_ ) , false , true , false , false , false , dont_use_cells ) ;
find_cell_sr ( libparser . ast , ID ( $ _DFFSR_NPP_ ) , false , true , true , false , false , dont_use_cells ) ;
find_cell_sr ( libparser . ast , ID ( $ _DFFSR_PNN_ ) , true , false , false , false , false , dont_use_cells ) ;
find_cell_sr ( libparser . ast , ID ( $ _DFFSR_PNP_ ) , true , false , true , false , false , dont_use_cells ) ;
find_cell_sr ( libparser . ast , ID ( $ _DFFSR_PPN_ ) , true , true , false , false , false , dont_use_cells ) ;
find_cell_sr ( libparser . ast , ID ( $ _DFFSR_PPP_ ) , true , true , true , false , false , dont_use_cells ) ;
2016-02-01 04:49:11 -06:00
2019-02-25 00:08:52 -06:00
log ( " final dff cell mappings: \n " ) ;
logmap_all ( ) ;
2013-01-05 04:13:26 -06:00
2020-07-02 11:22:43 -05:00
if ( ! map_only_mode ) {
std : : string dfflegalize_cmd = " dfflegalize " ;
for ( auto it : cell_mappings )
dfflegalize_cmd + = stringf ( " -cell %s 01 " , it . first . c_str ( ) ) ;
dfflegalize_cmd + = " t:$_DFF* t:$_SDFF* " ;
if ( info_mode ) {
log ( " dfflegalize command line: %s \n " , dfflegalize_cmd . c_str ( ) ) ;
} else {
Pass : : call ( design , dfflegalize_cmd ) ;
}
}
if ( ! prepare_mode & & ! info_mode ) {
for ( auto module : design - > selected_modules ( ) )
if ( ! module - > get_blackbox_attribute ( ) )
dfflibmap ( design , module ) ;
}
2013-01-05 04:13:26 -06:00
2020-07-02 11:22:43 -05:00
log_pop ( ) ;
2013-01-05 04:13:26 -06:00
cell_mappings . clear ( ) ;
}
} DfflibmapPass ;
2015-07-02 04:14:30 -05:00
2014-09-27 09:17:53 -05:00
PRIVATE_NAMESPACE_END