/**CFile*********************************************************************** FileName [eqvVerify.c] PackageName [eqv] Synopsis [This file contains the two routines which do combinational and sequential verification.] Description [] SeeAlso [] Author [Shaz Qadeer] Copyright [Copyright (c) 1994-1996 The Regents of the Univ. of California. All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.] ******************************************************************************/ #include "eqvInt.h" static char rcsid[] UNUSED = "$Id: eqvVerify.c,v 1.10 2009/04/11 01:40:06 fabio Exp $"; /**AutomaticStart*************************************************************/ /*---------------------------------------------------------------------------*/ /* Static function prototypes */ /*---------------------------------------------------------------------------*/ /**AutomaticEnd***************************************************************/ /*---------------------------------------------------------------------------*/ /* Definition of exported functions */ /*---------------------------------------------------------------------------*/ /**Function******************************************************************** Synopsis [This function does combinational verification between network1 and network2.] Description [roootsMap and leavesMap contain corresponding nodes in network1 and network2. It is assumed that the set of leaf nodes forms a complete support of the set of root nodes in both the networks. This function verifies the combinational equivalence of corresponding roots in terms of the leaves. method1 and method2 specify the partition methods to be used for partitioning network1 and network2 respectively. AssignCommonOrder is a pointer to a function which takes two networks, whose mdd managers are identical, and a hash table containing corresponding leaf nodes whose mdd Id should be the same. AssignCommonOrder() orders the two networks ensuring that identical mddIds are assigned to the corresponding leaf nodes in the hash table. This function has to be supplied by the user. This function returns TRUE if all the root pairs are combinationally equivalent otherwise it returns FALSE. It is assumed that network1 already has a mdd manager. If a partition is registered with network1, and it has vertices corresponding to the specified roots and leaves it is used otherwise a new partition is created. A new partition is always created for network2.] SideEffects [] ******************************************************************************/ boolean Eqv_NetworkVerifyCombinationalEquivalence( Ntk_Network_t *network1, Ntk_Network_t *network2, st_table *leavesMap, /* the mapping between the inputs of the networks; * hash table from Ntk_Node_t * to Ntk_Node_t * */ st_table *rootsMap, /* the mapping between the outputs of the networks; * hash table from Ntk_Node_t * to Ntk_Node_t * */ OFT AssignCommonOrder, Part_PartitionMethod method1, Part_PartitionMethod method2) { mdd_manager *mddMgr; lsList leavesList1, leavesList2; lsList rootsList1, rootsList2; st_table *leaves1, *leaves2; array_t *roots1, *roots2; st_generator *gen; lsGen listGen; graph_t *rootsToLeaves1, *rootsToLeaves2; Ntk_Node_t *node1, *node2; char *name1, *name2, *name; Mvf_Function_t *func1, *func2; int num, i, j; array_t *mvfRoots1, *mvfRoots2; boolean equivalent = TRUE; lsList dummy = (lsList) 0; int mddId1, mddId2; vertex_t *vertex; lsGeneric data; boolean partValid = FALSE; mdd_manager *mgr; int mddId; array_t *mddIdArray; mdd_t *aMinterm, *diff; char *mintermName; mdd_t *comp1, *comp2; int numComponents; /* First check whether a partition is registered with network1 */ if(Ntk_NetworkReadApplInfo(network1, PART_NETWORK_APPL_KEY) != NIL(void)) { /* * Check whether the registered partition can be used to compute the roots * in terms of the leaves. If it can be used, then i won't create a new * one. */ partValid = TestPartitionIsValid(network1, rootsMap, leavesMap); /* * If a partition already exists, an mddManager also exists. * So, don't allocate a new one. */ mddMgr = Ntk_NetworkReadMddManager(network1); } else { mddMgr = Ntk_NetworkInitializeMddManager(network1); } assert(mddMgr != NIL(mdd_manager)); Ntk_NetworkSetMddManager(network2, mddMgr); (*AssignCommonOrder)(network1, network2, leavesMap); /* Create lists of leaves and roots for creating partitions. */ leavesList1 = lsCreate(); leavesList2 = lsCreate(); st_foreach_item(leavesMap, gen, &node1, &node2) { mddId1 = Ntk_NodeReadMddId(node1); mddId2 = Ntk_NodeReadMddId(node2); assert(mddId1 == mddId2); lsNewEnd(leavesList1, (lsGeneric) node1, LS_NH); lsNewEnd(leavesList2, (lsGeneric) node2, LS_NH); } rootsList1 = lsCreate(); rootsList2 = lsCreate(); st_foreach_item(rootsMap, gen, &node1, &node2) { lsNewEnd(rootsList1, (lsGeneric) node1, LS_NH); lsNewEnd(rootsList2, (lsGeneric) node2, LS_NH); } if(partValid) { rootsToLeaves1 = Part_NetworkReadPartition(network1); } else { rootsToLeaves1 = Part_NetworkCreatePartition(network1, NIL(Hrc_Node_t), "dummy", rootsList1, leavesList1, NIL(mdd_t), method1, dummy, FALSE, FALSE, TRUE); } /* * Generate arrays of root and leaf vertices in the first partition * to pass as arguments to Part_PartitionCollapse(). */ roots1 = array_alloc(vertex_t *, 0); lsForEachItem(rootsList1, listGen, data) { name = Ntk_NodeReadName((Ntk_Node_t *) data); vertex = Part_PartitionFindVertexByName(rootsToLeaves1, name); assert(vertex != NIL(vertex_t)); array_insert_last(vertex_t *, roots1, vertex); } leaves1 = st_init_table(st_ptrcmp, st_ptrhash); lsForEachItem(leavesList1, listGen, data) { name = Ntk_NodeReadName((Ntk_Node_t *) data); vertex = Part_PartitionFindVertexByName(rootsToLeaves1, name); /* assert(vertex != NIL(vertex_t)); */ if(vertex != NIL(vertex_t)) { /* * If a leaf is not actually in the support of the roots then * it will not be present in the partition. */ mddId = Part_VertexReadMddId(vertex); st_insert(leaves1, (char *) vertex, (char *) (long) mddId); } } lsDestroy(rootsList1, (void (*) (lsGeneric)) 0); lsDestroy(leavesList1, (void (*) (lsGeneric)) 0); rootsToLeaves2 = Part_NetworkCreatePartition(network2, NIL(Hrc_Node_t), "dummy", rootsList2, leavesList2, NIL(mdd_t), method2, dummy, FALSE, FALSE, TRUE); /* * Generate arrays of root and leaf vertices in the second partition * to pass as arguments to Part_PartitionCollapse(). */ roots2 = array_alloc(vertex_t *, 0); lsForEachItem(rootsList2, listGen, data) { name = Ntk_NodeReadName((Ntk_Node_t *) data); vertex = Part_PartitionFindVertexByName(rootsToLeaves2, name); assert(vertex != NIL(vertex_t)); array_insert_last(vertex_t *, roots2, vertex); } leaves2 = st_init_table(st_ptrcmp, st_ptrhash); lsForEachItem(leavesList2, listGen, data) { name = Ntk_NodeReadName((Ntk_Node_t *) data); vertex = Part_PartitionFindVertexByName(rootsToLeaves2, name); /* assert(vertex != NIL(vertex_t)); */ if(vertex != NIL(vertex_t)) { /* * If a leaf is not actually in the support of the roots then * it will not be present in the partition. */ mddId = Part_VertexReadMddId(vertex); st_insert(leaves2, (char *) vertex, (char *) (long) mddId); } } lsDestroy(rootsList2, (void (*) (lsGeneric)) 0); lsDestroy(leavesList2, (void (*) (lsGeneric)) 0); assert(Part_PartitionReadMddManager(rootsToLeaves1) == Part_PartitionReadMddManager(rootsToLeaves2)); mvfRoots1 = Part_PartitionCollapse(rootsToLeaves1, roots1, leaves1, NIL(mdd_t)); mvfRoots2 = Part_PartitionCollapse(rootsToLeaves2, roots2, leaves2, NIL(mdd_t)); assert(array_n(mvfRoots1) == array_n(mvfRoots2)); num = array_n(mvfRoots1); mddIdArray = array_alloc(int, 0); st_foreach_item_int(leaves1, gen, &vertex, &mddId) { array_insert_last(int, mddIdArray, mddId); } /* * For each pair of corresponding outputs in the two arrays mvfRoots1 and * mvfRoots2, I will compute the mdd representing all the input combinations * for which they are different. For each pair, I will print one input * combination for which they are different. */ for(i = 0; i= 0); array_insert_last(int, inputIdArray, id); } mddTemp = mdd_smooth(mddMgr, badStates, inputIdArray); mdd_free(badStates); badStates = mddTemp; array_free(inputIdArray); modelFsm = Fsm_FsmCreateFromNetworkWithPartition(network2, NIL(graph_t)); assert(modelFsm != NIL(Fsm_Fsm_t)); Ntk_NetworkAddApplInfo(network2, FSM_NETWORK_APPL_KEY, (Ntk_ApplInfoFreeFn) Fsm_FsmFreeCallback, (void *) modelFsm); onionRings = array_alloc(mdd_t *, 0); initialStates = Fsm_FsmComputeInitialStates(modelFsm); tautology = mdd_one(mddMgr); /* Model checking function */ if (useBackwardReach == TRUE) { array_insert(mdd_t *, careStatesArray, 0, tautology); inequivalent = Mc_FsmComputeStatesReachingToSet(modelFsm, badStates, careStatesArray, initialStates, onionRings, McVerbosityNone_c, McDcLevelNone_c); array_free(careStatesArray); mdd_free(badStates); mdd_free(tautology); if (inequivalent) { mdd_t *outerOnionRing = array_fetch_last(mdd_t *, onionRings); mdd_t *badInitialStates = mdd_and(initialStates, outerOnionRing, 1, 1); array_t *psVarMddIdArray = Fsm_FsmReadPresentStateVars(modelFsm); mdd_t *aBadInitialState = Mc_ComputeAMinterm(initialStates, psVarMddIdArray, mddMgr); mdd_free(badInitialStates); aPath = Mc_BuildPathToCore(aBadInitialState, onionRings, modelFsm, McGreaterOrEqualZero_c); (void) Mc_PrintPath(aPath, modelFsm, TRUE); /* TRUE -> print inputs on path */ mdd_free(aBadInitialState); mdd_free(initialStates); mdd_array_free(aPath); mdd_array_free(onionRings); return FALSE; } else { mdd_free(initialStates); return TRUE; } } else { /* do forward search */ array_insert(mdd_t *, careStatesArray, 0, tautology); inequivalent = Mc_FsmComputeStatesReachableFromSet(modelFsm, initialStates, careStatesArray, badStates, onionRings, McVerbosityNone_c, McDcLevelNone_c); array_free(careStatesArray); mdd_free(tautology); mdd_free(initialStates); if (inequivalent) { mdd_t *outerOnionRing = array_fetch_last(mdd_t *, onionRings); mdd_t *reachableBadStates = mdd_and(badStates, outerOnionRing, 1, 1); array_t *psVarMddIdArray = Fsm_FsmReadPresentStateVars(modelFsm); mdd_t *aBadReachableState = Mc_ComputeAMinterm(reachableBadStates, psVarMddIdArray, mddMgr); mdd_free(reachableBadStates); psVarMddIdArray = Fsm_FsmReadPresentStateVars(modelFsm); aPath = Mc_BuildPathFromCore(aBadReachableState, onionRings, modelFsm, McGreaterOrEqualZero_c); (void) Mc_PrintPath(aPath, modelFsm, TRUE); /* TRUE -> print inputs on path */ mdd_free(aBadReachableState); mdd_free(badStates); mdd_array_free(aPath); mdd_array_free(onionRings); return FALSE; } else { mdd_free(badStates); return TRUE; } } } /*---------------------------------------------------------------------------*/ /* Definition of internal functions */ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* Definition of static functions */ /*---------------------------------------------------------------------------*/