/**CFile****************************************************************

  FileName    [cgtCore.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Clock gating package.]

  Synopsis    []

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 20, 2005.]

  Revision    [$Id: cgtCore.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]

***********************************************************************/

#include "cgtInt.h"
#include "bar.h"

ABC_NAMESPACE_IMPL_START


////////////////////////////////////////////////////////////////////////
///                        DECLARATIONS                              ///
////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
///                     FUNCTION DEFINITIONS                         ///
////////////////////////////////////////////////////////////////////////

/**Function*************************************************************

  Synopsis    [This procedure sets default parameters.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Cgt_SetDefaultParams( Cgt_Par_t * p )
{
    memset( p, 0, sizeof(Cgt_Par_t) );
    p->nLevelMax  =    25;   // the max number of levels to look for clock-gates
    p->nCandMax   =  1000;   // the max number of candidates at each node
    p->nOdcMax    =     0;   // the max number of ODC levels to consider
    p->nConfMax   =    10;   // the max number of conflicts at a node
    p->nVarsMin   =  1000;   // the min number of vars to recycle the SAT solver
    p->nFlopsMin  =     5;   // the min number of flops to recycle the SAT solver
    p->fAreaOnly  =     0;   // derive clock-gating to minimize area
    p->fVerbose   =     1;   // verbosity flag
}

/**Function*************************************************************

  Synopsis    [Returns 1 if simulation does not filter out this candidate.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Cgt_SimulationFilter( Cgt_Man_t * p, Aig_Obj_t * pCandPart, Aig_Obj_t * pMiterPart )
{
    unsigned * pInfoCand, * pInfoMiter;
    int w, nWords = Aig_BitWordNum( p->nPatts );  
    pInfoCand  = (unsigned *)Vec_PtrEntry( p->vPatts, Aig_ObjId(Aig_Regular(pCandPart)) );
    pInfoMiter = (unsigned *)Vec_PtrEntry( p->vPatts, Aig_ObjId(pMiterPart) );
    // C => !M -- true   is the same as    C & M -- false
    if ( !Aig_IsComplement(pCandPart) )
    {
        for ( w = 0; w < nWords; w++ )
            if ( pInfoCand[w] & pInfoMiter[w] )
                return 0;
    }
    else
    {
        for ( w = 0; w < nWords; w++ )
            if ( ~pInfoCand[w] & pInfoMiter[w] )
                return 0;
    }
    return 1;
}

/**Function*************************************************************

  Synopsis    [Saves one simulation pattern.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Cgt_SimulationRecord( Cgt_Man_t * p )
{
    Aig_Obj_t * pObj;
    int i;
    Aig_ManForEachObj( p->pPart, pObj, i )
        if ( sat_solver_var_value( p->pSat, p->pCnf->pVarNums[i] ) )
            Aig_InfoSetBit( (unsigned *)Vec_PtrEntry(p->vPatts, i), p->nPatts );
    p->nPatts++;
    if ( p->nPatts == 32 * p->nPattWords )
    {
        Vec_PtrReallocSimInfo( p->vPatts );
        Vec_PtrCleanSimInfo( p->vPatts, p->nPattWords, 2 * p->nPattWords );
        p->nPattWords *= 2;
    }
}

/**Function*************************************************************

  Synopsis    [Performs clock-gating for the AIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Cgt_ClockGatingRangeCheck( Cgt_Man_t * p, int iStart, int nOutputs )
{
    Vec_Ptr_t * vNodes = p->vFanout;
    Aig_Obj_t * pMiter, * pCand, * pMiterFrame, * pCandFrame, * pMiterPart, * pCandPart;
    int i, k, RetValue, nCalls;
    assert( Vec_VecSize(p->vGatesAll) == Aig_ManPoNum(p->pFrame) );
    // go through all the registers inputs of this range
    for ( i = iStart; i < iStart + nOutputs; i++ )
    {
        nCalls = p->nCalls;
        pMiter = Saig_ManLi( p->pAig, i );
        Cgt_ManDetectCandidates( p->pAig, Aig_ObjFanin0(pMiter), p->pPars->nLevelMax, vNodes );
        // go through the candidates of this PO
        Vec_PtrForEachEntry( Aig_Obj_t *, vNodes, pCand, k )
        {
            // get the corresponding nodes from the frames
            pCandFrame  = (Aig_Obj_t *)pCand->pData;
            pMiterFrame = (Aig_Obj_t *)pMiter->pData;
            // get the corresponding nodes from the part
            pCandPart   = (Aig_Obj_t *)pCandFrame->pData;
            pMiterPart  = (Aig_Obj_t *)pMiterFrame->pData;
            // try direct polarity
            if ( Cgt_SimulationFilter( p, pCandPart, pMiterPart ) )
            {
                RetValue = Cgt_CheckImplication( p, pCandPart, pMiterPart );
                if ( RetValue == 1 )
                {
                    Vec_VecPush( p->vGatesAll, i, pCand );
                    continue;
                }
                if ( RetValue == 0 )
                    Cgt_SimulationRecord( p );
            }
            else
                p->nCallsFiltered++;
            // try reverse polarity
            if ( Cgt_SimulationFilter( p, Aig_Not(pCandPart), pMiterPart ) )
            {
                RetValue = Cgt_CheckImplication( p, Aig_Not(pCandPart), pMiterPart );
                if ( RetValue == 1 )
                {
                    Vec_VecPush( p->vGatesAll, i, Aig_Not(pCand) );
                    continue;
                }
                if ( RetValue == 0 )
                    Cgt_SimulationRecord( p );
            }
            else
                p->nCallsFiltered++;
        }

        if ( p->pPars->fVerbose )
        {
//            printf( "Flop %3d : Cand = %4d. Gate = %4d. SAT calls = %3d.\n", 
//                i, Vec_PtrSize(vNodes), Vec_PtrSize(Vec_VecEntry(p->vGatesAll, i)), p->nCalls-nCalls );
        }

    }
} 

/**Function*************************************************************

  Synopsis    [Performs clock-gating for the AIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Cgt_ClockGatingRange( Cgt_Man_t * p, int iStart )
{
    int nOutputs, iStop, clk, clkTotal = clock();
    int nCallsUnsat    = p->nCallsUnsat;
    int nCallsSat      = p->nCallsSat;
    int nCallsUndec    = p->nCallsUndec;
    int nCallsFiltered = p->nCallsFiltered;
clk = clock();
    p->pPart = Cgt_ManDupPartition( p->pFrame, p->pPars->nVarsMin, p->pPars->nFlopsMin, iStart, p->pCare, p->vSuppsInv, &nOutputs );
    p->pCnf  = Cnf_DeriveSimple( p->pPart, nOutputs );
    p->pSat  = (sat_solver *)Cnf_DataWriteIntoSolver( p->pCnf, 1, 0 );
    sat_solver_compress( p->pSat );
    p->vPatts = Vec_PtrAllocSimInfo( Aig_ManObjNumMax(p->pPart), p->nPattWords );
    Vec_PtrCleanSimInfo( p->vPatts, 0, p->nPattWords );
p->timePrepare += clock() - clk;
    Cgt_ClockGatingRangeCheck( p, iStart, nOutputs );
    iStop = iStart + nOutputs;
    if ( p->pPars->fVeryVerbose )
    {
        printf( "%5d : D =%4d. C =%5d. Var =%6d. Pr =%5d. Cex =%5d. F =%4d. Saved =%6d. ",
            iStart, iStop-iStart, Aig_ManPoNum(p->pPart)-nOutputs, p->pSat->size, 
            p->nCallsUnsat-nCallsUnsat, 
            p->nCallsSat  -nCallsSat, 
            p->nCallsUndec-nCallsUndec,
            p->nCallsFiltered-nCallsFiltered );
        ABC_PRT( "Time", clock() - clkTotal );
    }
    Cgt_ManClean( p );
    p->nRecycles++;
    return iStop;
}

/**Function*************************************************************

  Synopsis    [Performs clock-gating for the AIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Vec_t * Cgt_ClockGatingCandidates( Aig_Man_t * pAig, Aig_Man_t * pCare, Cgt_Par_t * pPars )
{
    Bar_Progress_t * pProgress = NULL;
    Cgt_Par_t Pars; 
    Cgt_Man_t * p;
    Vec_Vec_t * vGatesAll;
    int iStart, clk = clock(), clkTotal = clock();
    // reset random numbers
    Aig_ManRandom( 1 );
    if ( pPars == NULL )
        Cgt_SetDefaultParams( pPars = &Pars );    
    p = Cgt_ManCreate( pAig, pCare, pPars );
    p->pFrame = Cgt_ManDeriveAigForGating( p );
p->timeAig += clock() - clk;
    assert( Aig_ManPoNum(p->pFrame) == Saig_ManRegNum(p->pAig) );
    pProgress = Bar_ProgressStart( stdout, Aig_ManPoNum(p->pFrame) );
    for ( iStart = 0; iStart < Aig_ManPoNum(p->pFrame); )
    {
        Bar_ProgressUpdate( pProgress, iStart, NULL );
        iStart = Cgt_ClockGatingRange( p, iStart );
    }
    Bar_ProgressStop( pProgress );
    vGatesAll = p->vGatesAll;
    p->vGatesAll = NULL;
p->timeTotal = clock() - clkTotal;
    Cgt_ManStop( p );
    return vGatesAll;
}

/**Function*************************************************************

  Synopsis    [Performs clock-gating for the AIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Aig_Man_t * Cgt_ClockGating( Aig_Man_t * pAig, Aig_Man_t * pCare, Cgt_Par_t * pPars )
{
    Aig_Man_t * pGated;
    Vec_Vec_t * vGatesAll;
    Vec_Vec_t * vGates;
    int nNodesUsed, clk = clock();
    vGatesAll = Cgt_ClockGatingCandidates( pAig, pCare, pPars );
    if ( pPars->fAreaOnly )
        vGates = Cgt_ManDecideArea( pAig, vGatesAll, pPars->nOdcMax, pPars->fVerbose );
    else
        vGates = Cgt_ManDecideSimple( pAig, vGatesAll, pPars->nOdcMax, pPars->fVerbose );
    if ( pPars->fVerbose )
    {
//        printf( "Before CG: " );
//        Aig_ManPrintStats( pAig );
    }
    pGated = Cgt_ManDeriveGatedAig( pAig, vGates, pPars->fAreaOnly, &nNodesUsed );
    if ( pPars->fVerbose )
    {
//        printf( "After  CG: " );
//        Aig_ManPrintStats( pGated );
        printf( "Nodes: Before CG = %6d. After CG = %6d. (%6.2f %%).  Total after CG = %6d.\n", 
            Aig_ManNodeNum(pAig), nNodesUsed, 
            100.0*nNodesUsed/Aig_ManNodeNum(pAig), 
            Aig_ManNodeNum(pGated) );
    }
    Vec_VecFree( vGates );
    Vec_VecFree( vGatesAll );
    return pGated;
}

////////////////////////////////////////////////////////////////////////
///                       END OF FILE                                ///
////////////////////////////////////////////////////////////////////////


ABC_NAMESPACE_IMPL_END

