/**CFile**************************************************************** FileName [abcReach.c] SystemName [ABC: Logic synthesis and verification system.] PackageName [Network and node package.] Synopsis [Performs reachability analysis.] Author [Alan Mishchenko] Affiliation [UC Berkeley] Date [Ver. 1.0. Started - June 20, 2005.] Revision [$Id: abcReach.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] ***********************************************************************/ #include "base/abc/abc.h" #include "misc/extra/extraBdd.h" ABC_NAMESPACE_IMPL_START //////////////////////////////////////////////////////////////////////// /// DECLARATIONS /// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// /// FUNCTION DEFINITIONS /// //////////////////////////////////////////////////////////////////////// /**Function************************************************************* Synopsis [Computes the initial state and sets up the variable map.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ DdNode * Abc_NtkInitStateVarMap( DdManager * dd, Abc_Ntk_t * pNtk, int fVerbose ) { DdNode ** pbVarsX, ** pbVarsY; DdNode * bTemp, * bProd, * bVar; Abc_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 = b1; Cudd_Ref( bProd ); Abc_NtkForEachLatch( pNtk, pLatch, i ) { pbVarsX[i] = dd->vars[ Abc_NtkPiNum(pNtk) + i ]; pbVarsY[i] = dd->vars[ Abc_NtkCiNum(pNtk) + i ]; // get the initial value of the latch bVar = Cudd_NotCond( pbVarsX[i], !Abc_LatchIsInit1(pLatch) ); bProd = Cudd_bddAnd( dd, bTemp = bProd, bVar ); Cudd_Ref( bProd ); Cudd_RecursiveDeref( dd, bTemp ); } Cudd_SetVarMap( dd, pbVarsX, pbVarsY, Abc_NtkLatchNum(pNtk) ); ABC_FREE( pbVarsX ); ABC_FREE( pbVarsY ); Cudd_Deref( bProd ); return bProd; } /**Function************************************************************* Synopsis [] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ DdNode ** Abc_NtkCreatePartitions( DdManager * dd, Abc_Ntk_t * pNtk, int fReorder, int fVerbose ) { DdNode ** pbParts; DdNode * bVar; Abc_Obj_t * pNode; int i; // extand the BDD manager to represent NS variables assert( dd->size == Abc_NtkCiNum(pNtk) ); Cudd_bddIthVar( dd, Abc_NtkCiNum(pNtk) + Abc_NtkLatchNum(pNtk) - 1 ); // enable reordering if ( fReorder ) Cudd_AutodynEnable( dd, CUDD_REORDER_SYMM_SIFT ); else Cudd_AutodynDisable( dd ); // compute the transition relation pbParts = ABC_ALLOC( DdNode *, Abc_NtkLatchNum(pNtk) ); Abc_NtkForEachLatch( pNtk, pNode, i ) { bVar = Cudd_bddIthVar( dd, Abc_NtkCiNum(pNtk) + i ); pbParts[i] = Cudd_bddXnor( dd, bVar, (DdNode *)Abc_ObjGlobalBdd(Abc_ObjFanin0(pNode)) ); Cudd_Ref( pbParts[i] ); } // free the global BDDs Abc_NtkFreeGlobalBdds( pNtk, 0 ); // reorder and disable reordering if ( fReorder ) { if ( fVerbose ) fprintf( stdout, "BDD nodes in the partitions before reordering %d.\n", Cudd_SharingSize(pbParts,Abc_NtkLatchNum(pNtk)) ); 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,Abc_NtkLatchNum(pNtk)) ); } return pbParts; } /**Function************************************************************* Synopsis [Computes the set of unreachable states.] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ DdNode * Abc_NtkComputeReachable( DdManager * dd, Abc_Ntk_t * pNtk, DdNode ** pbParts, DdNode * bInitial, DdNode * bOutput, int nBddMax, int nIterMax, int fPartition, int fReorder, int fVerbose ) { int fInternalReorder = 0; Extra_ImageTree_t * pTree = NULL; Extra_ImageTree2_t * pTree2 = NULL; DdNode * bReached, * bCubeCs; DdNode * bCurrent, * bNext = NULL, * bTemp; DdNode ** pbVarsY; Abc_Obj_t * pLatch; int i, nIters, nBddSize; int nThreshold = 10000; // collect the NS variables // set the variable mapping for Cudd_bddVarMap() pbVarsY = ABC_ALLOC( DdNode *, dd->size ); Abc_NtkForEachLatch( pNtk, pLatch, i ) pbVarsY[i] = dd->vars[ Abc_NtkCiNum(pNtk) + i ]; // start the image computation bCubeCs = Extra_bddComputeRangeCube( dd, Abc_NtkPiNum(pNtk), Abc_NtkCiNum(pNtk) ); Cudd_Ref( bCubeCs ); if ( fPartition ) pTree = Extra_bddImageStart( dd, bCubeCs, Abc_NtkLatchNum(pNtk), pbParts, Abc_NtkLatchNum(pNtk), pbVarsY, fVerbose ); else pTree2 = Extra_bddImageStart2( dd, bCubeCs, Abc_NtkLatchNum(pNtk), pbParts, Abc_NtkLatchNum(pNtk), pbVarsY, fVerbose ); ABC_FREE( pbVarsY ); Cudd_RecursiveDeref( dd, bCubeCs ); // perform reachability analisys bCurrent = bInitial; Cudd_Ref( bCurrent ); bReached = bInitial; Cudd_Ref( bReached ); assert( nIterMax > 1 ); // required to not deref uninitialized bNext for ( nIters = 1; nIters <= nIterMax; nIters++ ) { // compute the next states if ( fPartition ) bNext = Extra_bddImageCompute( pTree, bCurrent ); else bNext = Extra_bddImageCompute2( pTree2, bCurrent ); 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 ) ) break; // check the BDD size nBddSize = Cudd_DagSize(bNext); if ( nBddSize > nBddMax ) break; // check the result if ( !Cudd_bddLeq( dd, bNext, Cudd_Not(bOutput) ) ) { printf( "The miter is proved REACHABLE in %d iterations. ", nIters ); Cudd_RecursiveDeref( dd, bReached ); bReached = NULL; break; } // get the new states bCurrent = Cudd_bddAnd( dd, bNext, Cudd_Not(bReached) ); 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 ( fVerbose ) fprintf( stdout, "Iteration = %3d. BDD = %5d. ", nIters, nBddSize ); if ( fInternalReorder && fReorder && nBddSize > nThreshold ) { if ( fVerbose ) fprintf( stdout, "Reordering... Before = %5d. ", Cudd_DagSize(bReached) ); Cudd_ReduceHeap( dd, CUDD_REORDER_SYMM_SIFT, 100 ); Cudd_AutodynDisable( dd ); if ( fVerbose ) fprintf( stdout, "After = %5d.\r", Cudd_DagSize(bReached) ); nThreshold *= 2; } if ( fVerbose ) fprintf( stdout, "\r" ); } Cudd_RecursiveDeref( dd, bNext ); // undo the image tree if ( fPartition ) Extra_bddImageTreeDelete( pTree ); else Extra_bddImageTreeDelete2( pTree2 ); if ( bReached == NULL ) return NULL; // report the stats if ( fVerbose ) { double nMints = Cudd_CountMinterm(dd, bReached, Abc_NtkLatchNum(pNtk) ); if ( nIters > nIterMax || Cudd_DagSize(bReached) > nBddMax ) fprintf( stdout, "Reachability analysis is stopped after %d iterations.\n", nIters ); else fprintf( stdout, "Reachability analysis completed in %d iterations.\n", nIters ); fprintf( stdout, "Reachable states = %.0f. (Ratio = %.4f %%)\n", nMints, 100.0*nMints/pow(2.0, Abc_NtkLatchNum(pNtk)) ); fflush( stdout ); } //ABC_PRB( dd, bReached ); Cudd_Deref( bReached ); if ( nIters > nIterMax || Cudd_DagSize(bReached) > nBddMax ) printf( "Verified ONLY FOR STATES REACHED in %d iterations. \n", nIters ); printf( "The miter is proved unreachable in %d iteration. ", nIters ); return bReached; } /**Function************************************************************* Synopsis [Performs reachability to see if any .] Description [] SideEffects [] SeeAlso [] ***********************************************************************/ void Abc_NtkVerifyUsingBdds( Abc_Ntk_t * pNtk, int nBddMax, int nIterMax, int fPartition, int fReorder, int fVerbose ) { DdManager * dd; DdNode ** pbParts; DdNode * bOutput, * bReached, * bInitial; int i; abctime clk = Abc_Clock(); assert( Abc_NtkIsStrash(pNtk) ); assert( Abc_NtkPoNum(pNtk) == 1 ); assert( Abc_ObjFanoutNum(Abc_NtkPo(pNtk,0)) == 0 ); // PO should go first // compute the global BDDs of the latches dd = (DdManager *)Abc_NtkBuildGlobalBdds( pNtk, nBddMax, 1, fReorder, fVerbose ); if ( dd == NULL ) { printf( "The number of intermediate BDD nodes exceeded the limit (%d).\n", nBddMax ); return; } if ( fVerbose ) printf( "Shared BDD size is %6d nodes.\n", Cudd_ReadKeys(dd) - Cudd_ReadDead(dd) ); // save the output BDD bOutput = (DdNode *)Abc_ObjGlobalBdd(Abc_NtkPo(pNtk,0)); Cudd_Ref( bOutput ); // create partitions pbParts = Abc_NtkCreatePartitions( dd, pNtk, fReorder, fVerbose ); // create the initial state and the variable map bInitial = Abc_NtkInitStateVarMap( dd, pNtk, fVerbose ); Cudd_Ref( bInitial ); // check the result if ( !Cudd_bddLeq( dd, bInitial, Cudd_Not(bOutput) ) ) printf( "The miter is proved REACHABLE in the initial state. " ); else { // compute the reachable states bReached = Abc_NtkComputeReachable( dd, pNtk, pbParts, bInitial, bOutput, nBddMax, nIterMax, fPartition, fReorder, fVerbose ); if ( bReached != NULL ) { Cudd_Ref( bReached ); Cudd_RecursiveDeref( dd, bReached ); } } // cleanup Cudd_RecursiveDeref( dd, bOutput ); Cudd_RecursiveDeref( dd, bInitial ); for ( i = 0; i < Abc_NtkLatchNum(pNtk); i++ ) Cudd_RecursiveDeref( dd, pbParts[i] ); ABC_FREE( pbParts ); Extra_StopManager( dd ); // report the runtime ABC_PRT( "Time", Abc_Clock() - clk ); fflush( stdout ); } //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// ABC_NAMESPACE_IMPL_END