2015-01-19 15:08:44 +01: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
*
2015-01-19 15:08:44 +01: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
*
2015-01-19 15:08:44 +01: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/satgen.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct EquivSimpleWorker
{
Module * module ;
2015-02-01 22:41:03 +01:00
const vector < Cell * > & equiv_cells ;
2025-07-16 11:04:41 +02:00
const vector < Cell * > & assume_cells ;
2025-08-07 23:18:57 +02:00
struct Cone {
pool < Cell * > cells ;
pool < SigBit > bits ;
void clear ( ) {
cells . clear ( ) ;
bits . clear ( ) ;
}
} ;
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
struct DesignModel {
const SigMap & sigmap ;
dict < SigBit , Cell * > & bit2driver ;
} ;
DesignModel model ;
2015-01-19 15:08:44 +01:00
2015-02-21 12:15:41 +01:00
ezSatPtr ez ;
2015-01-19 15:08:44 +01:00
SatGen satgen ;
2025-08-07 23:18:57 +02:00
struct Config {
bool verbose = false ;
bool short_cones = false ;
bool model_undef = false ;
bool nogroup = false ;
bool set_assumes = false ;
int max_seq = 1 ;
} ;
Config cfg ;
2015-01-19 15:08:44 +01:00
2015-02-01 22:41:03 +01:00
pool < pair < Cell * , int > > imported_cells_cache ;
2025-08-07 23:18:57 +02:00
EquivSimpleWorker ( const vector < Cell * > & equiv_cells , const vector < Cell * > & assume_cells , DesignModel model , Config cfg ) :
module ( equiv_cells . front ( ) - > module ) , equiv_cells ( equiv_cells ) , assume_cells ( assume_cells ) ,
model ( model ) , satgen ( ez . get ( ) , & model . sigmap ) , cfg ( cfg )
2015-01-19 15:08:44 +01:00
{
2025-08-07 23:18:57 +02:00
satgen . model_undef = cfg . model_undef ;
2015-01-19 15:08:44 +01:00
}
2025-08-07 23:18:57 +02:00
struct ConeFinder {
DesignModel model ;
// Bits we should also analyze in a later iteration (flop inputs)
pool < SigBit > & next_seed ;
// Cells and bits we've seen so far while traversing
Cone & cone ;
// We're not allowed to traverse past cells and bits in `stop`
const Cone & stop ;
// Input bits are bits that no longer can be traversed
// Tracking these is optional
pool < SigBit > * input_bits ;
// Recursively traverses backwards from a cell to find all cells in its input cone
// Adds cell to cone.cells, stops at cells in 'stop' set
// Returns true if stopped on a stop cell
bool find_input_cone ( Cell * cell )
{
if ( cone . cells . count ( cell ) )
return false ;
cone . cells . insert ( cell ) ;
if ( stop . cells . count ( cell ) )
return true ;
for ( auto & conn : cell - > connections ( ) )
if ( yosys_celltypes . cell_input ( cell - > type , conn . first ) )
for ( auto bit : model . sigmap ( conn . second ) ) {
2025-09-17 05:23:52 +02:00
if ( cell - > is_builtin_ff ( ) ) {
2025-08-07 23:18:57 +02:00
if ( ! conn . first . in ( ID : : CLK , ID : : C ) )
next_seed . insert ( bit ) ;
} else
find_input_cone ( bit ) ;
}
2015-01-31 13:06:41 +01:00
return false ;
2025-08-07 23:18:57 +02:00
}
void find_input_cone ( SigBit bit )
{
if ( cone . bits . count ( bit ) )
return ;
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
cone . bits . insert ( bit ) ;
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
if ( stop . bits . count ( bit ) ) {
if ( input_bits ! = nullptr ) input_bits - > insert ( bit ) ;
return ;
}
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
if ( ! model . bit2driver . count ( bit ) )
return ;
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
// If the input cone of the driver cell reaches a stop bit,
// then `bit` is an "input bit"
if ( find_input_cone ( model . bit2driver . at ( bit ) ) )
if ( input_bits ! = nullptr ) input_bits - > insert ( bit ) ;
}
void find_input_cone ( pool < SigBit > bits )
{
for ( auto bit : bits )
find_input_cone ( bit ) ;
}
} ;
// Builds (full or short) input cones from the seeds
// Creates full cones (no stops) and optionally short cones (stop at other side's cone)
// Updates seed_a/seed_b with next iteration's FF inputs
// Returns input bits and cone structures for SAT problem construction
std : : tuple < pool < SigBit > , Cone , Cone > init_iter ( pool < SigBit > & seed_a , pool < SigBit > & seed_b ) const
2015-01-19 15:08:44 +01:00
{
2025-08-07 23:18:57 +02:00
// Empty, never inserted to, to traverse full cones
const Cone no_stop ;
Cone full_cone_a , full_cone_b ;
// Values of seed_* for the next iteration
pool < SigBit > next_seed_a , next_seed_b ;
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
{
ConeFinder finder_a { model , next_seed_a , full_cone_a , no_stop , nullptr } ;
finder_a . find_input_cone ( seed_a ) ;
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
ConeFinder finder_b { model , next_seed_b , full_cone_b , no_stop , nullptr } ;
finder_b . find_input_cone ( seed_b ) ;
2015-01-31 13:06:41 +01:00
}
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
Cone short_cone_a , short_cone_b ;
pool < SigBit > input_bits ;
if ( cfg . short_cones )
{
// Rebuild cones with the knowledge of the full cones.
// Avoids stuffing overlaps in input cones into the solver
// e.g. for A by using the full B cone as stops
next_seed_a . clear ( ) ;
ConeFinder short_finder_a = { model , next_seed_a , short_cone_a , short_cone_b , & input_bits } ;
short_finder_a . find_input_cone ( seed_a ) ;
next_seed_a . swap ( seed_a ) ;
next_seed_b . clear ( ) ;
ConeFinder short_finder_b = { model , next_seed_b , short_cone_b , short_cone_a , & input_bits } ;
short_finder_b . find_input_cone ( seed_b ) ;
next_seed_b . swap ( seed_b ) ;
}
else
{
short_cone_a = full_cone_a ;
next_seed_a . swap ( seed_a ) ;
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
short_cone_b = full_cone_b ;
next_seed_b . swap ( seed_b ) ;
}
return std : : make_tuple ( input_bits , short_cone_a , short_cone_b ) ;
2015-01-19 15:08:44 +01:00
}
2025-08-07 23:18:57 +02:00
void report_new_cells ( const pool < Cell * > & cells , const Cone & cone_a , const Cone & cone_b ) const
2015-01-19 15:08:44 +01:00
{
2025-08-07 23:18:57 +02:00
log ( " Adding %d new cells to the problem (%d A, %d B, %d shared). \n " ,
GetSize ( cells ) , GetSize ( cone_a . cells ) , GetSize ( cone_b . cells ) ,
( GetSize ( cone_a . cells ) + GetSize ( cone_b . cells ) ) - GetSize ( cells ) ) ;
#if 0
for ( auto cell : short_cells_cone_a )
log ( " A-side cell: %s \n " , log_id ( cell ) ) ;
for ( auto cell : short_cells_cone_b )
log ( " B-side cell: %s \n " , log_id ( cell ) ) ;
# endif
}
void report_new_assume_cells ( const pool < Cell * > & extra_problem_cells , int old_size , const pool < Cell * > & problem_cells ) const
{
if ( cfg . verbose ) {
log ( " Adding %d new cells to check assumptions (and reusing %d). \n " ,
GetSize ( problem_cells ) - old_size ,
old_size - ( GetSize ( problem_cells ) - GetSize ( extra_problem_cells ) ) ) ;
#if 0
for ( auto cell : extra_problem_cells )
log ( " cell: %s \n " , log_id ( cell ) ) ;
# endif
}
}
// Ensure the input cones of $assume cells get modelled by the problem
pool < Cell * > add_assumes_to_problem ( const Cone & cone_a , const Cone & cone_b ) const
{
pool < Cell * > extra_problem_cells ;
for ( auto assume : assume_cells ) {
pool < SigBit > assume_seed , dummy_next_seed , overlap_bits ;
assume_seed . insert ( model . sigmap ( assume - > getPort ( ID : : A ) ) . as_bit ( ) ) ;
assume_seed . insert ( model . sigmap ( assume - > getPort ( ID : : EN ) ) . as_bit ( ) ) ;
2025-08-08 02:22:07 +02:00
for ( auto & cone : { cone_a , cone_b } ) {
2025-08-07 23:18:57 +02:00
Cone assume_cone ;
ConeFinder { model , dummy_next_seed , assume_cone , cone , & overlap_bits }
. find_input_cone ( assume_seed ) ;
if ( GetSize ( overlap_bits ) ) {
extra_problem_cells . insert ( assume ) ;
extra_problem_cells . insert ( assume_cone . cells . begin ( ) , assume_cone . cells . end ( ) ) ;
overlap_bits . clear ( ) ;
}
assume_cone . clear ( ) ;
dummy_next_seed . clear ( ) ;
}
}
return extra_problem_cells ;
}
static void report_missing_model ( Cell * cell )
{
2025-09-17 05:23:52 +02:00
if ( cell - > is_builtin_ff ( ) )
2025-08-07 23:18:57 +02:00
log_cmd_error ( " No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first. \n " , log_id ( cell ) , log_id ( cell - > type ) ) ;
else
log_cmd_error ( " No SAT model available for cell %s (%s). \n " , log_id ( cell ) , log_id ( cell - > type ) ) ;
}
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
void prepare_ezsat ( int ez_context , SigBit bit_a , SigBit bit_b )
{
2015-01-31 13:06:41 +01:00
if ( satgen . model_undef )
{
2025-08-07 23:18:57 +02:00
int ez_a = satgen . importSigBit ( bit_a , cfg . max_seq + 1 ) ;
int ez_b = satgen . importDefSigBit ( bit_b , cfg . max_seq + 1 ) ;
int ez_undef_a = satgen . importUndefSigBit ( bit_a , cfg . max_seq + 1 ) ;
2015-01-31 13:06:41 +01:00
2015-02-21 12:15:41 +01:00
ez - > assume ( ez - > XOR ( ez_a , ez_b ) , ez_context ) ;
ez - > assume ( ez - > NOT ( ez_undef_a ) , ez_context ) ;
2015-01-31 13:06:41 +01:00
}
else
{
2025-08-07 23:18:57 +02:00
int ez_a = satgen . importSigBit ( bit_a , cfg . max_seq + 1 ) ;
int ez_b = satgen . importSigBit ( bit_b , cfg . max_seq + 1 ) ;
2015-02-21 12:15:41 +01:00
ez - > assume ( ez - > XOR ( ez_a , ez_b ) , ez_context ) ;
2015-01-31 13:06:41 +01:00
}
2025-08-07 23:18:57 +02:00
}
void construct_ezsat ( const pool < SigBit > & input_bits , int step )
{
if ( cfg . set_assumes ) {
if ( cfg . verbose & & step = = cfg . max_seq ) {
RTLIL : : SigSpec assumes_a , assumes_en ;
satgen . getAssumes ( assumes_a , assumes_en , step + 1 ) ;
for ( int i = 0 ; i < GetSize ( assumes_a ) ; i + + )
log ( " Import constraint from assume cell: %s when %s (%d). \n " , log_signal ( assumes_a [ i ] ) , log_signal ( assumes_en [ i ] ) , step ) ;
}
ez - > assume ( satgen . importAssumes ( step + 1 ) ) ;
}
if ( satgen . model_undef ) {
for ( auto bit : input_bits )
ez - > assume ( ez - > NOT ( satgen . importUndefSigBit ( bit , step + 1 ) ) ) ;
}
if ( cfg . verbose )
log ( " Problem size at t=%d: %d literals, %d clauses \n " , step , ez - > numCnfVariables ( ) , ez - > numCnfClauses ( ) ) ;
}
bool prove_equiv_cell ( Cell * cell )
{
SigBit bit_a = model . sigmap ( cell - > getPort ( ID : : A ) ) . as_bit ( ) ;
SigBit bit_b = model . sigmap ( cell - > getPort ( ID : : B ) ) . as_bit ( ) ;
int ez_context = ez - > frozen_literal ( ) ;
prepare_ezsat ( ez_context , bit_a , bit_b ) ;
2015-01-22 00:59:58 +01:00
2025-08-07 23:18:57 +02:00
// Two bits, bit_a, and bit_b, have been marked equivalent in the design
// We will be traversing the input cones for each of them
// In the first iteration, we will using those as starting points
2015-01-22 00:59:58 +01:00
pool < SigBit > seed_a = { bit_a } ;
pool < SigBit > seed_b = { bit_b } ;
2025-08-07 23:18:57 +02:00
if ( cfg . verbose ) {
log ( " Trying to prove $equiv cell %s: \n " , log_id ( cell ) ) ;
log ( " A = %s, B = %s, Y = %s \n " , log_signal ( bit_a ) , log_signal ( bit_b ) , log_signal ( cell - > getPort ( ID : : Y ) ) ) ;
2015-01-22 13:40:26 +01:00
} else {
2025-08-07 23:18:57 +02:00
log ( " Trying to prove $equiv for %s: " , log_signal ( cell - > getPort ( ID : : Y ) ) ) ;
2015-01-22 13:40:26 +01:00
}
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
int step = cfg . max_seq ;
2015-01-22 00:59:58 +01:00
while ( 1 )
{
2025-08-07 23:18:57 +02:00
// Traverse input cones of seed_a and seed_b, potentially finding new seeds
auto [ input_bits , cone_a , cone_b ] = init_iter ( seed_a , seed_b ) ;
2015-01-22 00:59:58 +01:00
2025-08-07 23:18:57 +02:00
// Cells to model in SAT solver
2015-01-22 00:59:58 +01:00
pool < Cell * > problem_cells ;
2025-08-07 23:18:57 +02:00
problem_cells . insert ( cone_a . cells . begin ( ) , cone_a . cells . end ( ) ) ;
problem_cells . insert ( cone_b . cells . begin ( ) , cone_b . cells . end ( ) ) ;
2015-01-22 00:59:58 +01:00
2025-08-07 23:18:57 +02:00
if ( cfg . verbose )
report_new_cells ( problem_cells , cone_a , cone_b ) ;
2017-04-28 18:54:53 +02:00
2025-08-07 23:18:57 +02:00
if ( cfg . set_assumes ) {
auto extras = add_assumes_to_problem ( cone_a , cone_b ) ;
if ( GetSize ( extras ) ) {
2025-07-16 11:04:41 +02:00
auto old_size = GetSize ( problem_cells ) ;
2025-08-07 23:18:57 +02:00
problem_cells . insert ( extras . begin ( ) , extras . end ( ) ) ;
report_new_assume_cells ( extras , old_size , problem_cells ) ;
2025-07-16 11:04:41 +02:00
}
}
2015-02-01 22:41:03 +01:00
for ( auto cell : problem_cells ) {
auto key = pair < Cell * , int > ( cell , step + 1 ) ;
2021-03-30 04:00:45 +02:00
if ( ! imported_cells_cache . count ( key ) & & ! satgen . importCell ( cell , step + 1 ) ) {
2025-08-07 23:18:57 +02:00
report_missing_model ( cell ) ;
2021-03-30 04:00:45 +02:00
}
2015-02-01 22:41:03 +01:00
imported_cells_cache . insert ( key ) ;
}
2015-01-22 00:59:58 +01:00
2025-08-07 23:18:57 +02:00
construct_ezsat ( input_bits , step ) ;
2015-01-22 00:59:58 +01:00
2015-02-21 12:15:41 +01:00
if ( ! ez - > solve ( ez_context ) ) {
2025-07-10 07:24:59 +02:00
log ( " %s " , cfg . verbose ? " Proved equivalence! Marking $equiv cell as proven. \n " : " success! \n " ) ;
2025-08-07 23:18:57 +02:00
// Replace $equiv cell with a short
cell - > setPort ( ID : : B , cell - > getPort ( ID : : A ) ) ;
2015-02-21 12:15:41 +01:00
ez - > assume ( ez - > NOT ( ez_context ) ) ;
2015-01-22 00:59:58 +01:00
return true ;
}
2015-01-19 15:08:44 +01:00
2025-08-07 23:18:57 +02:00
if ( cfg . verbose )
log ( " Failed to prove equivalence with sequence length %d. \n " , cfg . max_seq - step ) ;
2015-01-19 15:08:44 +01:00
2015-01-22 00:59:58 +01:00
if ( - - step < 0 ) {
2025-08-07 23:18:57 +02:00
if ( cfg . verbose )
2015-01-22 13:40:26 +01:00
log ( " Reached sequence limit. \n " ) ;
2015-01-22 00:59:58 +01:00
break ;
}
if ( seed_a . empty ( ) & & seed_b . empty ( ) ) {
2025-08-07 23:18:57 +02:00
if ( cfg . verbose )
2015-01-22 13:40:26 +01:00
log ( " No nets to continue in previous time step. \n " ) ;
2015-01-22 00:59:58 +01:00
break ;
}
if ( seed_a . empty ( ) ) {
2025-08-07 23:18:57 +02:00
if ( cfg . verbose )
2015-01-22 13:40:26 +01:00
log ( " No nets on A-side to continue in previous time step. \n " ) ;
2015-01-22 00:59:58 +01:00
break ;
}
if ( seed_b . empty ( ) ) {
2025-08-07 23:18:57 +02:00
if ( cfg . verbose )
2015-01-22 13:40:26 +01:00
log ( " No nets on B-side to continue in previous time step. \n " ) ;
2015-01-22 00:59:58 +01:00
break ;
}
2025-08-07 23:18:57 +02:00
if ( cfg . verbose ) {
2015-01-22 13:40:26 +01:00
#if 0
log ( " Continuing analysis in previous time step with the following nets: \n " ) ;
for ( auto bit : seed_a )
log ( " A: %s \n " , log_signal ( bit ) ) ;
for ( auto bit : seed_b )
log ( " B: %s \n " , log_signal ( bit ) ) ;
# else
log ( " Continuing analysis in previous time step with %d A- and %d B-nets. \n " , GetSize ( seed_a ) , GetSize ( seed_b ) ) ;
# endif
}
2015-01-19 15:08:44 +01:00
}
2025-08-07 23:18:57 +02:00
if ( ! cfg . verbose )
2015-01-22 13:40:26 +01:00
log ( " failed. \n " ) ;
2015-02-01 22:41:03 +01:00
2015-02-21 12:15:41 +01:00
ez - > assume ( ez - > NOT ( ez_context ) ) ;
2015-01-19 15:08:44 +01:00
return false ;
}
2015-02-01 22:41:03 +01:00
int run ( )
{
if ( GetSize ( equiv_cells ) > 1 ) {
SigSpec sig ;
for ( auto c : equiv_cells )
2025-08-07 23:18:57 +02:00
sig . append ( model . sigmap ( c - > getPort ( ID : : Y ) ) ) ;
2015-02-01 22:41:03 +01:00
log ( " Grouping SAT models for %s: \n " , log_signal ( sig ) ) ;
}
int counter = 0 ;
for ( auto c : equiv_cells ) {
2025-08-07 23:18:57 +02:00
if ( prove_equiv_cell ( c ) )
2015-02-01 22:41:03 +01:00
counter + + ;
}
return counter ;
}
2015-01-19 15:08:44 +01:00
} ;
struct EquivSimplePass : public Pass {
EquivSimplePass ( ) : Pass ( " equiv_simple " , " try proving simple $equiv instances " ) { }
2020-06-19 01:34:52 +02:00
void help ( ) override
2015-01-19 15:08:44 +01:00
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log ( " \n " ) ;
log ( " equiv_simple [options] [selection] \n " ) ;
log ( " \n " ) ;
log ( " This command tries to prove $equiv cells using a simple direct SAT approach. \n " ) ;
log ( " \n " ) ;
2015-01-22 13:40:26 +01:00
log ( " -v \n " ) ;
log ( " verbose output \n " ) ;
log ( " \n " ) ;
2015-01-31 13:06:41 +01:00
log ( " -undef \n " ) ;
log ( " enable modelling of undef states \n " ) ;
log ( " \n " ) ;
2017-04-28 18:54:53 +02:00
log ( " -short \n " ) ;
log ( " create shorter input cones that stop at shared nodes. This yields \n " ) ;
log ( " simpler SAT problems but sometimes fails to prove equivalence. \n " ) ;
log ( " \n " ) ;
2015-02-01 22:41:03 +01:00
log ( " -nogroup \n " ) ;
log ( " disabling grouping of $equiv cells by output wire \n " ) ;
log ( " \n " ) ;
2015-01-22 00:59:58 +01:00
log ( " -seq <N> \n " ) ;
log ( " the max. number of time steps to be considered (default = 1) \n " ) ;
log ( " \n " ) ;
2025-07-16 11:04:41 +02:00
log ( " -set-assumes \n " ) ;
log ( " set all assumptions provided via $assume cells \n " ) ;
log ( " \n " ) ;
2015-01-19 15:08:44 +01:00
}
2020-06-19 01:34:52 +02:00
void execute ( std : : vector < std : : string > args , Design * design ) override
2015-01-19 15:08:44 +01:00
{
2025-08-07 23:18:57 +02:00
EquivSimpleWorker : : Config cfg = { } ;
2015-01-19 15:08:44 +01:00
int success_counter = 0 ;
2016-04-21 23:28:37 +02:00
log_header ( design , " Executing EQUIV_SIMPLE pass. \n " ) ;
2015-01-19 15:08:44 +01:00
size_t argidx ;
for ( argidx = 1 ; argidx < args . size ( ) ; argidx + + ) {
2015-01-22 13:40:26 +01:00
if ( args [ argidx ] = = " -v " ) {
2025-08-07 23:18:57 +02:00
cfg . verbose = true ;
2015-01-22 13:40:26 +01:00
continue ;
}
2017-04-28 18:54:53 +02:00
if ( args [ argidx ] = = " -short " ) {
2025-08-07 23:18:57 +02:00
cfg . short_cones = true ;
2017-04-28 18:54:53 +02:00
continue ;
}
2015-01-31 13:06:41 +01:00
if ( args [ argidx ] = = " -undef " ) {
2025-08-07 23:18:57 +02:00
cfg . model_undef = true ;
2015-01-31 13:06:41 +01:00
continue ;
}
2015-02-01 22:41:03 +01:00
if ( args [ argidx ] = = " -nogroup " ) {
2025-08-07 23:18:57 +02:00
cfg . nogroup = true ;
2015-02-01 22:41:03 +01:00
continue ;
}
2015-01-22 00:59:58 +01:00
if ( args [ argidx ] = = " -seq " & & argidx + 1 < args . size ( ) ) {
2025-08-07 23:18:57 +02:00
cfg . max_seq = atoi ( args [ + + argidx ] . c_str ( ) ) ;
2015-01-22 00:59:58 +01:00
continue ;
}
2025-07-16 11:04:41 +02:00
if ( args [ argidx ] = = " -set-assumes " ) {
2025-08-07 23:18:57 +02:00
cfg . set_assumes = true ;
2025-07-16 11:04:41 +02:00
continue ;
}
2015-01-19 15:08:44 +01:00
break ;
}
extra_args ( args , argidx , design ) ;
CellTypes ct ;
ct . setup_internals ( ) ;
ct . setup_stdcells ( ) ;
2024-02-21 12:03:37 +01:00
ct . setup_internals_ff ( ) ;
ct . setup_stdcells_mem ( ) ;
2015-01-19 15:08:44 +01:00
for ( auto module : design - > selected_modules ( ) )
{
2015-02-01 22:41:03 +01:00
SigMap sigmap ( module ) ;
dict < SigBit , Cell * > bit2driver ;
dict < SigBit , dict < SigBit , Cell * > > unproven_equiv_cells ;
2025-07-16 11:04:41 +02:00
vector < Cell * > assumes ;
2015-02-01 22:41:03 +01:00
int unproven_cells_counter = 0 ;
2015-01-19 15:08:44 +01:00
2025-07-16 11:04:41 +02:00
for ( auto cell : module - > selected_cells ( ) ) {
2020-04-02 18:51:32 +02:00
if ( cell - > type = = ID ( $ equiv ) & & cell - > getPort ( ID : : A ) ! = cell - > getPort ( ID : : B ) ) {
2020-03-12 20:57:01 +01:00
auto bit = sigmap ( cell - > getPort ( ID : : Y ) . as_bit ( ) ) ;
2015-02-01 22:41:03 +01:00
auto bit_group = bit ;
2025-08-07 23:18:57 +02:00
if ( ! cfg . nogroup & & bit_group . wire )
2015-02-01 22:41:03 +01:00
bit_group . offset = 0 ;
unproven_equiv_cells [ bit_group ] [ bit ] = cell ;
unproven_cells_counter + + ;
2025-07-16 11:04:41 +02:00
} else if ( cell - > type = = ID ( $ assume ) ) {
assumes . push_back ( cell ) ;
2015-02-01 22:41:03 +01:00
}
2025-07-16 11:04:41 +02:00
}
2015-01-19 15:08:44 +01:00
if ( unproven_equiv_cells . empty ( ) )
continue ;
2015-02-01 22:41:03 +01:00
log ( " Found %d unproven $equiv cells (%d groups) in %s: \n " ,
unproven_cells_counter , GetSize ( unproven_equiv_cells ) , log_id ( module ) ) ;
2015-01-19 15:08:44 +01:00
2015-01-22 00:59:58 +01:00
for ( auto cell : module - > cells ( ) ) {
2023-10-02 11:07:02 +02:00
if ( ! ct . cell_known ( cell - > type ) )
2015-01-19 15:08:44 +01:00
continue ;
for ( auto & conn : cell - > connections ( ) )
2015-01-22 00:59:58 +01:00
if ( yosys_celltypes . cell_output ( cell - > type , conn . first ) )
2015-01-19 15:08:44 +01:00
for ( auto bit : sigmap ( conn . second ) )
bit2driver [ bit ] = cell ;
}
2015-02-01 22:41:03 +01:00
unproven_equiv_cells . sort ( ) ;
2025-08-07 23:18:57 +02:00
for ( auto [ _ , d ] : unproven_equiv_cells )
2015-02-01 22:41:03 +01:00
{
2025-08-07 23:18:57 +02:00
d . sort ( ) ;
2015-02-01 22:41:03 +01:00
vector < Cell * > cells ;
2025-08-07 23:18:57 +02:00
for ( auto [ _ , cell ] : d )
cells . push_back ( cell ) ;
2015-02-01 22:41:03 +01:00
2025-08-07 23:18:57 +02:00
EquivSimpleWorker : : DesignModel model { sigmap , bit2driver } ;
EquivSimpleWorker worker ( cells , assumes , model , cfg ) ;
2015-02-01 22:41:03 +01:00
success_counter + = worker . run ( ) ;
2015-01-19 15:08:44 +01:00
}
}
2015-01-22 00:59:58 +01:00
log ( " Proved %d previously unproven $equiv cells. \n " , success_counter ) ;
2015-01-19 15:08:44 +01:00
}
} EquivSimplePass ;
PRIVATE_NAMESPACE_END