blob: b5125ec7127331361aafc1335b65921a47511fbc [file] [log] [blame]
/**CFile****************************************************************
FileName [bbrReach.c]
SystemName [ABC: Logic synthesis and verification system.]
PackageName [BDD-based reachability analysis.]
Synopsis [Procedures to perform reachability analysis.]
Author [Alan Mishchenko]
Affiliation [UC Berkeley]
Date [Ver. 1.0. Started - June 20, 2005.]
Revision [$Id: bbrReach.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]
***********************************************************************/
#include "bbr.h"
ABC_NAMESPACE_IMPL_START
////////////////////////////////////////////////////////////////////////
/// DECLARATIONS ///
////////////////////////////////////////////////////////////////////////
extern Abc_Cex_t * Aig_ManVerifyUsingBddsCountExample( Aig_Man_t * p, DdManager * dd,
DdNode ** pbParts, Vec_Ptr_t * vOnionRings, DdNode * bCubeFirst,
int iOutput, int fVerbose, int fSilent );
////////////////////////////////////////////////////////////////////////
/// FUNCTION DEFINITIONS ///
////////////////////////////////////////////////////////////////////////
/**Function*************************************************************
Synopsis [This procedure sets default resynthesis parameters.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
void Bbr_ManSetDefaultParams( Saig_ParBbr_t * p )
{
memset( p, 0, sizeof(Saig_ParBbr_t) );
p->TimeLimit = 0;
p->nBddMax = 50000;
p->nIterMax = 1000;
p->fPartition = 1;
p->fReorder = 1;
p->fReorderImage = 1;
p->fVerbose = 0;
p->fSilent = 0;
p->iFrame = -1;
}
/**Function********************************************************************
Synopsis [Performs the reordering-sensitive step of Extra_bddMove().]
Description []
SideEffects []
SeeAlso []
******************************************************************************/
DdNode * Bbr_bddComputeRangeCube( DdManager * dd, int iStart, int iStop )
{
DdNode * bTemp, * bProd;
int i;
assert( iStart <= iStop );
assert( iStart >= 0 && iStart <= dd->size );
assert( iStop >= 0 && iStop <= dd->size );
bProd = (dd)->one; Cudd_Ref( bProd );
for ( i = iStart; i < iStop; i++ )
{
bProd = Cudd_bddAnd( dd, bTemp = bProd, dd->vars[i] ); Cudd_Ref( bProd );
Cudd_RecursiveDeref( dd, bTemp );
}
Cudd_Deref( bProd );
return bProd;
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
void Bbr_StopManager( DdManager * dd )
{
int RetValue;
// check for remaining references in the package
RetValue = Cudd_CheckZeroRef( dd );
if ( RetValue > 0 )
printf( "\nThe number of referenced nodes = %d\n\n", RetValue );
// Cudd_PrintInfo( dd, stdout );
Cudd_Quit( dd );
}
/**Function*************************************************************
Synopsis [Computes the initial state and sets up the variable map.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
DdNode * Aig_ManInitStateVarMap( DdManager * dd, Aig_Man_t * p, int fVerbose )
{
DdNode ** pbVarsX, ** pbVarsY;
DdNode * bTemp, * bProd;
Aig_Obj_t * pLatch;
int i;
// set the variable mapping for Cudd_bddVarMap()
pbVarsX = ABC_ALLOC( DdNode *, dd->size );
pbVarsY = ABC_ALLOC( DdNode *, dd->size );
bProd = (dd)->one; Cudd_Ref( bProd );
Saig_ManForEachLo( p, pLatch, i )
{
pbVarsX[i] = dd->vars[ Saig_ManPiNum(p) + i ];
pbVarsY[i] = dd->vars[ Saig_ManCiNum(p) + i ];
// get the initial value of the latch
bProd = Cudd_bddAnd( dd, bTemp = bProd, Cudd_Not(pbVarsX[i]) ); Cudd_Ref( bProd );
Cudd_RecursiveDeref( dd, bTemp );
}
Cudd_SetVarMap( dd, pbVarsX, pbVarsY, Saig_ManRegNum(p) );
ABC_FREE( pbVarsX );
ABC_FREE( pbVarsY );
Cudd_Deref( bProd );
return bProd;
}
/**Function*************************************************************
Synopsis [Collects the array of output BDDs.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
DdNode ** Aig_ManCreateOutputs( DdManager * dd, Aig_Man_t * p )
{
DdNode ** pbOutputs;
Aig_Obj_t * pNode;
int i;
// compute the transition relation
pbOutputs = ABC_ALLOC( DdNode *, Saig_ManPoNum(p) );
Saig_ManForEachPo( p, pNode, i )
{
pbOutputs[i] = Aig_ObjGlobalBdd(pNode); Cudd_Ref( pbOutputs[i] );
}
return pbOutputs;
}
/**Function*************************************************************
Synopsis [Collects the array of partition BDDs.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
DdNode ** Aig_ManCreatePartitions( DdManager * dd, Aig_Man_t * p, int fReorder, int fVerbose )
{
DdNode ** pbParts;
DdNode * bVar;
Aig_Obj_t * pNode;
int i;
// extand the BDD manager to represent NS variables
assert( dd->size == Saig_ManCiNum(p) );
Cudd_bddIthVar( dd, Saig_ManCiNum(p) + Saig_ManRegNum(p) - 1 );
// enable reordering
if ( fReorder )
Cudd_AutodynEnable( dd, CUDD_REORDER_SYMM_SIFT );
else
Cudd_AutodynDisable( dd );
// compute the transition relation
pbParts = ABC_ALLOC( DdNode *, Saig_ManRegNum(p) );
Saig_ManForEachLi( p, pNode, i )
{
bVar = Cudd_bddIthVar( dd, Saig_ManCiNum(p) + i );
pbParts[i] = Cudd_bddXnor( dd, bVar, Aig_ObjGlobalBdd(pNode) ); Cudd_Ref( pbParts[i] );
}
// free global BDDs
Aig_ManFreeGlobalBdds( p, dd );
// reorder and disable reordering
if ( fReorder )
{
if ( fVerbose )
fprintf( stdout, "BDD nodes in the partitions before reordering %d.\n", Cudd_SharingSize(pbParts,Saig_ManRegNum(p)) );
Cudd_ReduceHeap( dd, CUDD_REORDER_SYMM_SIFT, 100 );
Cudd_AutodynDisable( dd );
if ( fVerbose )
fprintf( stdout, "BDD nodes in the partitions after reordering %d.\n", Cudd_SharingSize(pbParts,Saig_ManRegNum(p)) );
}
return pbParts;
}
/**Function*************************************************************
Synopsis [Computes the set of unreachable states.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
int Aig_ManComputeReachable( DdManager * dd, Aig_Man_t * p, DdNode ** pbParts, DdNode * bInitial, DdNode ** pbOutputs, Saig_ParBbr_t * pPars, int fCheckOutputs )
{
int fInternalReorder = 0;
Bbr_ImageTree_t * pTree = NULL; // Suppress "might be used uninitialized"
Bbr_ImageTree2_t * pTree2 = NULL; // Supprses "might be used uninitialized"
DdNode * bReached, * bCubeCs;
DdNode * bCurrent;
DdNode * bNext = NULL; // Suppress "might be used uninitialized"
DdNode * bTemp;
Cudd_ReorderingType method;
int i, nIters, nBddSize = 0, status;
int nThreshold = 10000;
abctime clk = Abc_Clock();
Vec_Ptr_t * vOnionRings;
int fixedPoint = 0;
status = Cudd_ReorderingStatus( dd, &method );
if ( status )
Cudd_AutodynDisable( dd );
// start the image computation
bCubeCs = Bbr_bddComputeRangeCube( dd, Saig_ManPiNum(p), Saig_ManCiNum(p) ); Cudd_Ref( bCubeCs );
if ( pPars->fPartition )
pTree = Bbr_bddImageStart( dd, bCubeCs, Saig_ManRegNum(p), pbParts, Saig_ManRegNum(p), dd->vars+Saig_ManCiNum(p), pPars->nBddMax, pPars->fVerbose );
else
pTree2 = Bbr_bddImageStart2( dd, bCubeCs, Saig_ManRegNum(p), pbParts, Saig_ManRegNum(p), dd->vars+Saig_ManCiNum(p), pPars->fVerbose );
Cudd_RecursiveDeref( dd, bCubeCs );
if ( pTree == NULL )
{
if ( !pPars->fSilent )
printf( "BDDs blew up during qualitification scheduling. " );
return -1;
}
if ( status )
Cudd_AutodynEnable( dd, method );
// start the onion rings
vOnionRings = Vec_PtrAlloc( 1000 );
// perform reachability analysis
bCurrent = bInitial; Cudd_Ref( bCurrent );
bReached = bInitial; Cudd_Ref( bReached );
Vec_PtrPush( vOnionRings, bCurrent ); Cudd_Ref( bCurrent );
for ( nIters = 0; nIters < pPars->nIterMax; nIters++ )
{
// check the runtime limit
if ( pPars->TimeLimit && pPars->TimeLimit <= (Abc_Clock()-clk)/CLOCKS_PER_SEC )
{
printf( "Reached timeout after image computation (%d seconds).\n", pPars->TimeLimit );
Vec_PtrFree( vOnionRings );
// undo the image tree
if ( pPars->fPartition )
Bbr_bddImageTreeDelete( pTree );
else
Bbr_bddImageTreeDelete2( pTree2 );
pPars->iFrame = nIters - 1;
return -1;
}
// compute the next states
if ( pPars->fPartition )
bNext = Bbr_bddImageCompute( pTree, bCurrent );
else
bNext = Bbr_bddImageCompute2( pTree2, bCurrent );
if ( bNext == NULL )
{
if ( !pPars->fSilent )
printf( "BDDs blew up during image computation. " );
if ( pPars->fPartition )
Bbr_bddImageTreeDelete( pTree );
else
Bbr_bddImageTreeDelete2( pTree2 );
Vec_PtrFree( vOnionRings );
pPars->iFrame = nIters - 1;
return -1;
}
Cudd_Ref( bNext );
Cudd_RecursiveDeref( dd, bCurrent );
// remap these states into the current state vars
bNext = Cudd_bddVarMap( dd, bTemp = bNext ); Cudd_Ref( bNext );
Cudd_RecursiveDeref( dd, bTemp );
// check if there are any new states
if ( Cudd_bddLeq( dd, bNext, bReached ) ) {
fixedPoint = 1;
break;
}
// check the BDD size
nBddSize = Cudd_DagSize(bNext);
if ( nBddSize > pPars->nBddMax )
break;
// check the result
for ( i = 0; i < Saig_ManPoNum(p); i++ )
{
if ( fCheckOutputs && !Cudd_bddLeq( dd, bNext, Cudd_Not(pbOutputs[i]) ) )
{
DdNode * bIntersect;
bIntersect = Cudd_bddIntersect( dd, bNext, pbOutputs[i] ); Cudd_Ref( bIntersect );
assert( p->pSeqModel == NULL );
p->pSeqModel = Aig_ManVerifyUsingBddsCountExample( p, dd, pbParts,
vOnionRings, bIntersect, i, pPars->fVerbose, pPars->fSilent );
Cudd_RecursiveDeref( dd, bIntersect );
if ( !pPars->fSilent )
Abc_Print( 1, "Output %d of miter \"%s\" was asserted in frame %d. ", i, p->pName, Vec_PtrSize(vOnionRings) );
Cudd_RecursiveDeref( dd, bReached );
bReached = NULL;
pPars->iFrame = nIters;
break;
}
}
if ( i < Saig_ManPoNum(p) )
break;
// get the new states
bCurrent = Cudd_bddAnd( dd, bNext, Cudd_Not(bReached) ); Cudd_Ref( bCurrent );
Vec_PtrPush( vOnionRings, bCurrent ); Cudd_Ref( bCurrent );
// minimize the new states with the reached states
// bCurrent = Cudd_bddConstrain( dd, bTemp = bCurrent, Cudd_Not(bReached) ); Cudd_Ref( bCurrent );
// Cudd_RecursiveDeref( dd, bTemp );
// add to the reached states
bReached = Cudd_bddOr( dd, bTemp = bReached, bNext ); Cudd_Ref( bReached );
Cudd_RecursiveDeref( dd, bTemp );
Cudd_RecursiveDeref( dd, bNext );
if ( pPars->fVerbose )
fprintf( stdout, "Frame = %3d. BDD = %5d. ", nIters, nBddSize );
if ( fInternalReorder && pPars->fReorder && nBddSize > nThreshold )
{
if ( pPars->fVerbose )
fprintf( stdout, "Reordering... Before = %5d. ", Cudd_DagSize(bReached) );
Cudd_ReduceHeap( dd, CUDD_REORDER_SYMM_SIFT, 100 );
Cudd_AutodynDisable( dd );
if ( pPars->fVerbose )
fprintf( stdout, "After = %5d.\r", Cudd_DagSize(bReached) );
nThreshold *= 2;
}
if ( pPars->fVerbose )
// fprintf( stdout, "\r" );
fprintf( stdout, "\n" );
if ( pPars->fVerbose )
{
double nMints = Cudd_CountMinterm(dd, bReached, Saig_ManRegNum(p) );
// Extra_bddPrint( dd, bReached );printf( "\n" );
fprintf( stdout, "Reachable states = %.0f. (Ratio = %.4f %%)\n", nMints, 100.0*nMints/pow(2.0, Saig_ManRegNum(p)) );
fflush( stdout );
}
}
Cudd_RecursiveDeref( dd, bNext );
// free the onion rings
Vec_PtrForEachEntry( DdNode *, vOnionRings, bTemp, i )
Cudd_RecursiveDeref( dd, bTemp );
Vec_PtrFree( vOnionRings );
// undo the image tree
if ( pPars->fPartition )
Bbr_bddImageTreeDelete( pTree );
else
Bbr_bddImageTreeDelete2( pTree2 );
if ( bReached == NULL )
return 0; // proved reachable
// report the stats
if ( pPars->fVerbose )
{
double nMints = Cudd_CountMinterm(dd, bReached, Saig_ManRegNum(p) );
if ( nIters > pPars->nIterMax || nBddSize > pPars->nBddMax )
fprintf( stdout, "Reachability analysis is stopped after %d frames.\n", nIters );
else
fprintf( stdout, "Reachability analysis completed after %d frames.\n", nIters );
fprintf( stdout, "Reachable states = %.0f. (Ratio = %.4f %%)\n", nMints, 100.0*nMints/pow(2.0, Saig_ManRegNum(p)) );
fflush( stdout );
}
//ABC_PRB( dd, bReached );
Cudd_RecursiveDeref( dd, bReached );
// SPG
//
if ( fixedPoint ) {
if ( !pPars->fSilent ) {
printf( "The miter is proved unreachable after %d iterations. ", nIters );
}
pPars->iFrame = nIters - 1;
return 1;
}
assert(nIters >= pPars->nIterMax || nBddSize >= pPars->nBddMax);
if ( !pPars->fSilent )
printf( "Verified only for states reachable in %d frames. ", nIters );
pPars->iFrame = nIters - 1;
return -1; // undecided
}
/**Function*************************************************************
Synopsis [Performs reachability to see if any PO can be asserted.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
int Aig_ManVerifyUsingBdds_int( Aig_Man_t * p, Saig_ParBbr_t * pPars )
{
int fCheckOutputs = !pPars->fSkipOutCheck;
DdManager * dd;
DdNode ** pbParts, ** pbOutputs;
DdNode * bInitial, * bTemp;
int RetValue, i;
abctime clk = Abc_Clock();
Vec_Ptr_t * vOnionRings;
assert( Saig_ManRegNum(p) > 0 );
// compute the global BDDs of the latches
dd = Aig_ManComputeGlobalBdds( p, pPars->nBddMax, 1, pPars->fReorder, pPars->fVerbose );
if ( dd == NULL )
{
if ( !pPars->fSilent )
printf( "The number of intermediate BDD nodes exceeded the limit (%d).\n", pPars->nBddMax );
return -1;
}
if ( pPars->fVerbose )
printf( "Shared BDD size is %6d nodes.\n", Cudd_ReadKeys(dd) - Cudd_ReadDead(dd) );
// check the runtime limit
if ( pPars->TimeLimit && pPars->TimeLimit <= (Abc_Clock()-clk)/CLOCKS_PER_SEC )
{
printf( "Reached timeout after constructing global BDDs (%d seconds).\n", pPars->TimeLimit );
Cudd_Quit( dd );
return -1;
}
// start the onion rings
vOnionRings = Vec_PtrAlloc( 1000 );
// save outputs
pbOutputs = Aig_ManCreateOutputs( dd, p );
// create partitions
pbParts = Aig_ManCreatePartitions( dd, p, pPars->fReorder, pPars->fVerbose );
// create the initial state and the variable map
bInitial = Aig_ManInitStateVarMap( dd, p, pPars->fVerbose ); Cudd_Ref( bInitial );
// set reordering
if ( pPars->fReorderImage )
Cudd_AutodynEnable( dd, CUDD_REORDER_SYMM_SIFT );
// check the result
RetValue = -1;
for ( i = 0; i < Saig_ManPoNum(p); i++ )
{
if ( fCheckOutputs && !Cudd_bddLeq( dd, bInitial, Cudd_Not(pbOutputs[i]) ) )
{
DdNode * bIntersect;
bIntersect = Cudd_bddIntersect( dd, bInitial, pbOutputs[i] ); Cudd_Ref( bIntersect );
assert( p->pSeqModel == NULL );
p->pSeqModel = Aig_ManVerifyUsingBddsCountExample( p, dd, pbParts,
vOnionRings, bIntersect, i, pPars->fVerbose, pPars->fSilent );
Cudd_RecursiveDeref( dd, bIntersect );
if ( !pPars->fSilent )
Abc_Print( 1, "Output %d of miter \"%s\" was asserted in frame %d. ", i, p->pName, -1 );
RetValue = 0;
break;
}
}
// free the onion rings
Vec_PtrForEachEntry( DdNode *, vOnionRings, bTemp, i )
Cudd_RecursiveDeref( dd, bTemp );
Vec_PtrFree( vOnionRings );
// explore reachable states
if ( RetValue == -1 )
RetValue = Aig_ManComputeReachable( dd, p, pbParts, bInitial, pbOutputs, pPars, fCheckOutputs );
// cleanup
Cudd_RecursiveDeref( dd, bInitial );
for ( i = 0; i < Saig_ManRegNum(p); i++ )
Cudd_RecursiveDeref( dd, pbParts[i] );
ABC_FREE( pbParts );
for ( i = 0; i < Saig_ManPoNum(p); i++ )
Cudd_RecursiveDeref( dd, pbOutputs[i] );
ABC_FREE( pbOutputs );
// if ( RetValue == -1 )
Cudd_Quit( dd );
// else
// Bbr_StopManager( dd );
// report the runtime
if ( !pPars->fSilent )
{
ABC_PRT( "Time", Abc_Clock() - clk );
fflush( stdout );
}
return RetValue;
}
/**Function*************************************************************
Synopsis [Performs reachability to see if any PO can be asserted.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
int Aig_ManVerifyUsingBdds( Aig_Man_t * pInit, Saig_ParBbr_t * pPars )
{
Abc_Cex_t * pCexOld, * pCexNew;
Aig_Man_t * p;
Aig_Obj_t * pObj;
Vec_Int_t * vInputMap;
int i, k, Entry, iBitOld, iBitNew, RetValue;
// pPars->fVerbose = 1;
// check if there are PIs without fanout
Saig_ManForEachPi( pInit, pObj, i )
if ( Aig_ObjRefs(pObj) == 0 )
break;
if ( i == Saig_ManPiNum(pInit) )
return Aig_ManVerifyUsingBdds_int( pInit, pPars );
// create new AIG
p = Aig_ManDupTrim( pInit );
assert( Aig_ManCiNum(p) < Aig_ManCiNum(pInit) );
assert( Aig_ManRegNum(p) == Aig_ManRegNum(pInit) );
RetValue = Aig_ManVerifyUsingBdds_int( p, pPars );
if ( RetValue != 0 )
{
Aig_ManStop( p );
return RetValue;
}
// the problem is satisfiable - remap the pattern
pCexOld = p->pSeqModel;
assert( pCexOld != NULL );
// create input map
vInputMap = Vec_IntAlloc( Saig_ManPiNum(pInit) );
Saig_ManForEachPi( pInit, pObj, i )
if ( pObj->pData != NULL )
Vec_IntPush( vInputMap, Aig_ObjCioId((Aig_Obj_t *)pObj->pData) );
else
Vec_IntPush( vInputMap, -1 );
// create new pattern
pCexNew = Abc_CexAlloc( Saig_ManRegNum(pInit), Saig_ManPiNum(pInit), pCexOld->iFrame+1 );
pCexNew->iFrame = pCexOld->iFrame;
pCexNew->iPo = pCexOld->iPo;
// copy the bit-data
for ( iBitOld = 0; iBitOld < pCexOld->nRegs; iBitOld++ )
if ( Abc_InfoHasBit( pCexOld->pData, iBitOld ) )
Abc_InfoSetBit( pCexNew->pData, iBitOld );
// copy the primary input data
iBitNew = iBitOld;
for ( i = 0; i <= pCexNew->iFrame; i++ )
{
Vec_IntForEachEntry( vInputMap, Entry, k )
{
if ( Entry == -1 )
continue;
if ( Abc_InfoHasBit( pCexOld->pData, iBitOld + Entry ) )
Abc_InfoSetBit( pCexNew->pData, iBitNew + k );
}
iBitOld += Saig_ManPiNum(p);
iBitNew += Saig_ManPiNum(pInit);
}
assert( iBitOld < iBitNew );
assert( iBitOld == pCexOld->nBits );
assert( iBitNew == pCexNew->nBits );
Vec_IntFree( vInputMap );
pInit->pSeqModel = pCexNew;
Aig_ManStop( p );
return 0;
}
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////
ABC_NAMESPACE_IMPL_END