2015-01-22 07:03:18 -06:00
/*
* yosys - - Yosys Open SYnthesis Suite
*
* Copyright ( C ) 2012 Clifford Wolf < clifford @ clifford . at >
2015-07-02 04:14:30 -05:00
*
2015-01-22 07:03:18 -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
*
2015-01-22 07:03:18 -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 .
*
*/
# include "kernel/yosys.h"
# include "kernel/satgen.h"
# include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct EquivInductWorker
{
Module * module ;
SigMap sigmap ;
vector < Cell * > cells ;
pool < Cell * > workset ;
2015-02-21 05:15:41 -06:00
ezSatPtr ez ;
2015-01-22 07:03:18 -06:00
SatGen satgen ;
int max_seq ;
int success_counter ;
dict < int , int > ez_step_is_consistent ;
pool < Cell * > cell_warn_cache ;
2015-01-31 06:58:04 -06:00
SigPool undriven_signals ;
2015-01-22 07:03:18 -06:00
2015-01-31 06:58:04 -06:00
EquivInductWorker ( Module * module , const pool < Cell * > & unproven_equiv_cells , bool model_undef , int max_seq ) : module ( module ) , sigmap ( module ) ,
2015-02-21 05:15:41 -06:00
cells ( module - > selected_cells ( ) ) , workset ( unproven_equiv_cells ) ,
satgen ( ez . get ( ) , & sigmap ) , max_seq ( max_seq ) , success_counter ( 0 )
2015-01-22 07:03:18 -06:00
{
2015-01-31 06:58:04 -06:00
satgen . model_undef = model_undef ;
2015-01-22 07:03:18 -06:00
}
void create_timestep ( int step )
{
vector < int > ez_equal_terms ;
2015-01-31 06:58:04 -06:00
2015-01-22 07:03:18 -06:00
for ( auto cell : cells ) {
if ( ! satgen . importCell ( cell , step ) & & ! cell_warn_cache . count ( cell ) ) {
2021-03-29 21:00:45 -05:00
if ( RTLIL : : builtin_ff_cell_types ( ) . count ( cell - > type ) )
log_warning ( " 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_warning ( " No SAT model available for cell %s (%s). \n " , log_id ( cell ) , log_id ( cell - > type ) ) ;
2015-01-22 07:03:18 -06:00
cell_warn_cache . insert ( cell ) ;
}
2020-04-02 11:51:32 -05:00
if ( cell - > type = = ID ( $ equiv ) ) {
2020-03-12 14:57:01 -05:00
SigBit bit_a = sigmap ( cell - > getPort ( ID : : A ) ) . as_bit ( ) ;
SigBit bit_b = sigmap ( cell - > getPort ( ID : : B ) ) . as_bit ( ) ;
2015-01-22 07:03:18 -06:00
if ( bit_a ! = bit_b ) {
int ez_a = satgen . importSigBit ( bit_a , step ) ;
int ez_b = satgen . importSigBit ( bit_b , step ) ;
2015-02-21 05:15:41 -06:00
int cond = ez - > IFF ( ez_a , ez_b ) ;
2020-07-27 11:28:01 -05:00
if ( satgen . model_undef ) {
cond = ez - > AND ( cond , ez - > NOT ( satgen . importUndefSigBit ( bit_b , step ) ) ) ;
2015-02-21 05:15:41 -06:00
cond = ez - > OR ( cond , satgen . importUndefSigBit ( bit_a , step ) ) ;
2020-07-27 11:28:01 -05:00
}
2015-01-31 06:58:04 -06:00
ez_equal_terms . push_back ( cond ) ;
2015-01-22 07:03:18 -06:00
}
}
}
2015-01-31 06:58:04 -06:00
if ( satgen . model_undef ) {
for ( auto bit : undriven_signals . export_all ( ) )
2015-02-21 05:15:41 -06:00
ez - > assume ( ez - > NOT ( satgen . importUndefSigBit ( bit , step ) ) ) ;
2015-01-31 06:58:04 -06:00
}
2015-01-22 07:03:18 -06:00
log_assert ( ! ez_step_is_consistent . count ( step ) ) ;
2015-02-21 05:15:41 -06:00
ez_step_is_consistent [ step ] = ez - > expression ( ez - > OpAnd , ez_equal_terms ) ;
2015-01-22 07:03:18 -06:00
}
void run ( )
{
log ( " Found %d unproven $equiv cells in module %s: \n " , GetSize ( workset ) , log_id ( module ) ) ;
2015-01-31 06:58:04 -06:00
if ( satgen . model_undef ) {
for ( auto cell : cells )
if ( yosys_celltypes . cell_known ( cell - > type ) )
for ( auto & conn : cell - > connections ( ) )
if ( yosys_celltypes . cell_input ( cell - > type , conn . first ) )
undriven_signals . add ( sigmap ( conn . second ) ) ;
for ( auto cell : cells )
if ( yosys_celltypes . cell_known ( cell - > type ) )
for ( auto & conn : cell - > connections ( ) )
if ( yosys_celltypes . cell_output ( cell - > type , conn . first ) )
undriven_signals . del ( sigmap ( conn . second ) ) ;
}
2015-01-22 07:03:18 -06:00
create_timestep ( 1 ) ;
2015-01-31 06:58:04 -06:00
if ( satgen . model_undef ) {
for ( auto bit : satgen . initial_state . export_all ( ) )
2015-02-21 05:15:41 -06:00
ez - > assume ( ez - > NOT ( satgen . importUndefSigBit ( bit , 1 ) ) ) ;
2015-01-31 06:58:04 -06:00
log ( " Undef modelling: force def on %d initial reg values and %d inputs. \n " ,
GetSize ( satgen . initial_state ) , GetSize ( undriven_signals ) ) ;
}
2015-01-22 07:03:18 -06:00
for ( int step = 1 ; step < = max_seq ; step + + )
{
2015-02-21 05:15:41 -06:00
ez - > assume ( ez_step_is_consistent [ step ] ) ;
2015-01-22 07:03:18 -06:00
2015-02-21 05:15:41 -06:00
log ( " Proving existence of base case for step %d. (%d clauses over %d variables) \n " , step , ez - > numCnfClauses ( ) , ez - > numCnfVariables ( ) ) ;
if ( ! ez - > solve ( ) ) {
2015-01-22 07:03:18 -06:00
log ( " Proof for base case failed. Circuit inherently diverges! \n " ) ;
2015-01-23 17:16:17 -06:00
return ;
2015-01-22 07:03:18 -06:00
}
create_timestep ( step + 1 ) ;
2015-02-21 05:15:41 -06:00
int new_step_not_consistent = ez - > NOT ( ez_step_is_consistent [ step + 1 ] ) ;
ez - > bind ( new_step_not_consistent ) ;
2015-01-22 07:03:18 -06:00
2015-02-21 05:15:41 -06:00
log ( " Proving induction step %d. (%d clauses over %d variables) \n " , step , ez - > numCnfClauses ( ) , ez - > numCnfVariables ( ) ) ;
if ( ! ez - > solve ( new_step_not_consistent ) ) {
2015-01-22 07:03:18 -06:00
log ( " Proof for induction step holds. Entire workset of %d cells proven! \n " , GetSize ( workset ) ) ;
for ( auto cell : workset )
2020-03-12 14:57:01 -05:00
cell - > setPort ( ID : : B , cell - > getPort ( ID : : A ) ) ;
2015-01-22 07:03:18 -06:00
success_counter + = GetSize ( workset ) ;
return ;
}
log ( " Proof for induction step failed. %s \n " , step ! = max_seq ? " Extending to next time step. " : " Trying to prove individual $equiv from workset. " ) ;
}
2015-01-23 17:13:27 -06:00
workset . sort ( ) ;
2015-01-22 07:03:18 -06:00
for ( auto cell : workset )
{
2020-03-12 14:57:01 -05:00
SigBit bit_a = sigmap ( cell - > getPort ( ID : : A ) ) . as_bit ( ) ;
SigBit bit_b = sigmap ( cell - > getPort ( ID : : B ) ) . as_bit ( ) ;
2015-01-22 07:03:18 -06:00
2020-03-12 14:57:01 -05:00
log ( " Trying to prove $equiv for %s: " , log_signal ( sigmap ( cell - > getPort ( ID : : Y ) ) ) ) ;
2015-01-31 06:58:04 -06:00
2015-01-22 07:03:18 -06:00
int ez_a = satgen . importSigBit ( bit_a , max_seq + 1 ) ;
int ez_b = satgen . importSigBit ( bit_b , max_seq + 1 ) ;
2015-02-21 05:15:41 -06:00
int cond = ez - > XOR ( ez_a , ez_b ) ;
2015-01-22 07:03:18 -06:00
2015-01-31 06:58:04 -06:00
if ( satgen . model_undef )
2015-02-21 05:15:41 -06:00
cond = ez - > AND ( cond , ez - > NOT ( satgen . importUndefSigBit ( bit_a , max_seq + 1 ) ) ) ;
2015-01-31 06:58:04 -06:00
2015-02-21 05:15:41 -06:00
if ( ! ez - > solve ( cond ) ) {
2015-01-22 07:03:18 -06:00
log ( " success! \n " ) ;
2020-03-12 14:57:01 -05:00
cell - > setPort ( ID : : B , cell - > getPort ( ID : : A ) ) ;
2015-01-22 07:03:18 -06:00
success_counter + + ;
} else {
log ( " failed. \n " ) ;
}
}
}
} ;
struct EquivInductPass : public Pass {
EquivInductPass ( ) : Pass ( " equiv_induct " , " proving $equiv cells using temporal induction " ) { }
2020-06-18 18:34:52 -05:00
void help ( ) override
2015-01-22 07:03:18 -06:00
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log ( " \n " ) ;
log ( " equiv_induct [options] [selection] \n " ) ;
log ( " \n " ) ;
log ( " Uses a version of temporal induction to prove $equiv cells. \n " ) ;
log ( " \n " ) ;
log ( " Only selected $equiv cells are proven and only selected cells are used to \n " ) ;
log ( " perform the proof. \n " ) ;
log ( " \n " ) ;
2015-01-31 06:58:04 -06:00
log ( " -undef \n " ) ;
log ( " enable modelling of undef states \n " ) ;
log ( " \n " ) ;
2015-01-22 07:03:18 -06:00
log ( " -seq <N> \n " ) ;
log ( " the max. number of time steps to be considered (default = 4) \n " ) ;
log ( " \n " ) ;
2015-01-22 14:23:01 -06:00
log ( " This command is very effective in proving complex sequential circuits, when \n " ) ;
log ( " the internal state of the circuit quickly propagates to $equiv cells. \n " ) ;
log ( " \n " ) ;
log ( " However, this command uses a weak definition of 'equivalence': This command \n " ) ;
log ( " proves that the two circuits will not diverge after they produce equal \n " ) ;
log ( " outputs (observable points via $equiv) for at least <N> cycles (the <N> \n " ) ;
log ( " specified via -seq). \n " ) ;
log ( " \n " ) ;
log ( " Combined with simulation this is very powerful because simulation can give \n " ) ;
log ( " you confidence that the circuits start out synced for at least <N> cycles \n " ) ;
log ( " after reset. \n " ) ;
log ( " \n " ) ;
2015-01-22 07:03:18 -06:00
}
2020-06-18 18:34:52 -05:00
void execute ( std : : vector < std : : string > args , Design * design ) override
2015-01-22 07:03:18 -06:00
{
int success_counter = 0 ;
2015-01-31 06:58:04 -06:00
bool model_undef = false ;
2015-01-22 07:03:18 -06:00
int max_seq = 4 ;
2016-04-21 16:28:37 -05:00
log_header ( design , " Executing EQUIV_INDUCT pass. \n " ) ;
2015-01-22 07:03:18 -06:00
size_t argidx ;
for ( argidx = 1 ; argidx < args . size ( ) ; argidx + + ) {
2015-01-31 06:58:04 -06:00
if ( args [ argidx ] = = " -undef " ) {
model_undef = true ;
continue ;
}
2015-01-22 07:03:18 -06:00
if ( args [ argidx ] = = " -seq " & & argidx + 1 < args . size ( ) ) {
max_seq = atoi ( args [ + + argidx ] . c_str ( ) ) ;
continue ;
}
break ;
}
extra_args ( args , argidx , design ) ;
for ( auto module : design - > selected_modules ( ) )
{
pool < Cell * > unproven_equiv_cells ;
for ( auto cell : module - > selected_cells ( ) )
2020-04-02 11:51:32 -05:00
if ( cell - > type = = ID ( $ equiv ) ) {
2020-03-12 14:57:01 -05:00
if ( cell - > getPort ( ID : : A ) ! = cell - > getPort ( ID : : B ) )
2015-01-22 07:03:18 -06:00
unproven_equiv_cells . insert ( cell ) ;
}
if ( unproven_equiv_cells . empty ( ) ) {
log ( " No selected unproven $equiv cells found in %s. \n " , log_id ( module ) ) ;
continue ;
}
2015-01-31 06:58:04 -06:00
EquivInductWorker worker ( module , unproven_equiv_cells , model_undef , max_seq ) ;
2015-01-22 07:03:18 -06:00
worker . run ( ) ;
success_counter + = worker . success_counter ;
}
log ( " Proved %d previously unproven $equiv cells. \n " , success_counter ) ;
}
} EquivInductPass ;
PRIVATE_NAMESPACE_END