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

  FileName    [wlcSim.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Verilog parser.]

  Synopsis    [Performs sequential simulation of word-level network.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 04, 2015.]

  Revision    [$Id: wlcSim.c,v 1.00 2015/06/04 00:00:00 alanmi Exp $]

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

#include "wlc.h"

ABC_NAMESPACE_IMPL_START

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

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

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

  Synopsis    [Internal simulation APIs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline word * Wlc_ObjSim( Gia_Man_t * p, int iObj )
{
    return Vec_WrdEntryP( p->vSims, p->iPatsPi * iObj );
}
static inline void Wlc_ObjSimPi( Gia_Man_t * p, int iObj )
{
    int w;
    word * pSim = Wlc_ObjSim( p, iObj );
    for ( w = 0; w < p->iPatsPi; w++ )
        pSim[w] = Gia_ManRandomW( 0 );
}
static inline void Wlc_ObjSimRo( Gia_Man_t * p, int iObj )
{
    int w;
    word * pSimRo = Wlc_ObjSim( p, iObj );
    word * pSimRi = Wlc_ObjSim( p, Gia_ObjRoToRiId(p, iObj) );
    for ( w = 0; w < p->iPatsPi; w++ )
        pSimRo[w] = pSimRi[w];
}
static inline void Wlc_ObjSimCo( Gia_Man_t * p, int iObj )
{
    int w;
    Gia_Obj_t * pObj = Gia_ManObj( p, iObj );
    word * pSimCo  = Wlc_ObjSim( p, iObj );
    word * pSimDri = Wlc_ObjSim( p, Gia_ObjFaninId0(pObj, iObj) );
    if ( Gia_ObjFaninC0(pObj) )
        for ( w = 0; w < p->iPatsPi; w++ )
            pSimCo[w] = ~pSimDri[w];
    else
        for ( w = 0; w < p->iPatsPi; w++ )
            pSimCo[w] =  pSimDri[w];
}
static inline void Wlc_ObjSimAnd( Gia_Man_t * p, int iObj )
{
    int w;
    Gia_Obj_t * pObj = Gia_ManObj( p, iObj );
    word * pSim  = Wlc_ObjSim( p, iObj );
    word * pSim0 = Wlc_ObjSim( p, Gia_ObjFaninId0(pObj, iObj) );
    word * pSim1 = Wlc_ObjSim( p, Gia_ObjFaninId1(pObj, iObj) );
    if ( Gia_ObjFaninC0(pObj) && Gia_ObjFaninC1(pObj) )
        for ( w = 0; w < p->iPatsPi; w++ )
            pSim[w] = ~pSim0[w] & ~pSim1[w];
    else if ( Gia_ObjFaninC0(pObj) && !Gia_ObjFaninC1(pObj) )
        for ( w = 0; w < p->iPatsPi; w++ )
            pSim[w] = ~pSim0[w] & pSim1[w];
    else if ( !Gia_ObjFaninC0(pObj) && Gia_ObjFaninC1(pObj) )
        for ( w = 0; w < p->iPatsPi; w++ )
            pSim[w] = pSim0[w] & ~pSim1[w];
    else
        for ( w = 0; w < p->iPatsPi; w++ )
            pSim[w] = pSim0[w] & pSim1[w];
}


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

  Synopsis    [Performs simulation of a word-level network.]

  Description [Returns vRes, a 2D array of simulation information for 
  the output of each bit of each object listed in vNodes. In particular, 
  Vec_Ptr_t * vSimObj = (Vec_Ptr_t *)Vec_PtrEntry(vRes, iObj) and
  Vec_Ptr_t * vSimObjBit = (Vec_Ptr_t *)Vec_PtrEntry(vSimObj, iBit)
  are arrays containing the simulation info for each object (vSimObj) 
  and for each output bit of this object (vSimObjBit). Alternatively,
  Vec_Ptr_t * vSimObjBit = Vec_VecEntryEntry( (Vec_Vec_t *)vRes, iObj, iBit ).
  The output bitwidth of an object is Wlc_ObjRange( Wlc_NtkObj(pNtk, iObj) ).
  Simulation information is binary data constaining the given number (nWords)
  of 64-bit machine words for the given number (nFrames) of consecutive 
  timeframes.  The total number of timeframes is nWords * nFrames for 
  each bit of each object.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_NtkDeleteSim( Vec_Ptr_t * p )
{
    word * pInfo; int i, k;
    Vec_Vec_t * vVec = (Vec_Vec_t *)p;
    Vec_VecForEachEntry( word *, vVec, pInfo, i, k )
        ABC_FREE( pInfo );
    Vec_VecFree( vVec );
}
Vec_Ptr_t * Wlc_NtkSimulate( Wlc_Ntk_t * p, Vec_Int_t * vNodes, int nWords, int nFrames )
{
    Gia_Obj_t * pObj; 
    Vec_Ptr_t * vOne, * vRes;
    Gia_Man_t * pGia = Wlc_NtkBitBlast( p, NULL, 0 );
    Wlc_Obj_t * pWlcObj;
    int f, i, k, w, nBits, Counter = 0;
    // allocate simulation info for one timeframe
    Vec_WrdFreeP( &pGia->vSims );
    pGia->vSims = Vec_WrdStart( Gia_ManObjNum(pGia) * nWords );
    pGia->iPatsPi = nWords;
    // allocate resulting simulation info
    vRes = Vec_PtrAlloc( Vec_IntSize(vNodes) );
    Wlc_NtkForEachObjVec( vNodes, p, pWlcObj, i )
    {
        nBits = Wlc_ObjRange(pWlcObj);
        vOne = Vec_PtrAlloc( nBits );
        for ( k = 0; k < nBits; k++ )
            Vec_PtrPush( vOne, ABC_CALLOC(word, nWords * nFrames) );
        Vec_PtrPush( vRes, vOne ); 
    }
    // perform simulation (const0 and flop outputs are already initialized)
    Gia_ManRandomW( 1 );
    for ( f = 0; f < nFrames; f++ )
    {
        Gia_ManForEachObj1( pGia, pObj, i )
        {
            if ( Gia_ObjIsAnd(pObj) )
                Wlc_ObjSimAnd( pGia, i );
            else if ( Gia_ObjIsCo(pObj) )
                Wlc_ObjSimCo( pGia, i );
            else if ( Gia_ObjIsPi(pGia, pObj) )
                Wlc_ObjSimPi( pGia, i );
            else if ( Gia_ObjIsRo(pGia, pObj) )
                Wlc_ObjSimRo( pGia, i );
        }
        // collect simulation data
        Wlc_NtkForEachObjVec( vNodes, p, pWlcObj, i )
        {
            int nBits = Wlc_ObjRange(pWlcObj);
            int iFirst = Vec_IntEntry( &p->vCopies, Wlc_ObjId(p, pWlcObj) );
            for ( k = 0; k < nBits; k++ )
            {
                int iLit = Vec_IntEntry( &p->vBits, iFirst + k );
                word * pInfo = (word*)Vec_VecEntryEntry( (Vec_Vec_t *)vRes, i, k );
                if ( iLit == -1 )
                {
                    Counter++;
                    for ( w = 0; w < nWords; w++ )
                        pInfo[f * nWords + w] = 0;
                }
                else
                {
                    word * pInfoObj = Wlc_ObjSim( pGia, Abc_Lit2Var(iLit) );
                    for ( w = 0; w < nWords; w++ )
                        pInfo[f * nWords + w] = Abc_LitIsCompl(iLit) ? ~pInfoObj[w] : pInfoObj[w];
                }
            }
        }
        if ( f == 0 && Counter )
            printf( "Replaced %d dangling internal bits with constant 0.\n", Counter );
    }
    Vec_WrdFreeP( &pGia->vSims );
    pGia->iPatsPi = 0;
    Gia_ManStop( pGia );
    return vRes;
}

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

  Synopsis    [Testing procedure.]

  Description [This testing procedure assumes that the WLC network has 
  one node, which is a multiplier. It simulates the node and checks the 
  word-level interpretation of the bit-level simulation info to make sure 
  that it indeed represents multiplication.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_NtkSimulatePrint( Wlc_Ntk_t * p, Vec_Int_t * vNodes, Vec_Ptr_t * vRes, int nWords, int nFrames )
{
    Wlc_Obj_t * pWlcObj; 
    int f, w, b, i, k, iPat = 0;
    for ( f = 0; f < nFrames; f++, printf("\n") )
      for ( w = 0; w < nWords; w++ )
        for ( b = 0; b < 64; b++, iPat++, printf("\n") )
        {
            Wlc_NtkForEachObjVec( vNodes, p, pWlcObj, i )
            {
                int nBits = Wlc_ObjRange(pWlcObj);
                for ( k = nBits-1; k >= 0; k-- )
                {
                    word * pInfo = (word*)Vec_VecEntryEntry( (Vec_Vec_t *)vRes, i, k );
                    printf( "%d", Abc_InfoHasBit((unsigned *)pInfo, iPat) );
                }
                printf( " " );
            }
        }
}
void Wlc_NtkSimulateTest( Wlc_Ntk_t * p )
{
    int nWords = 2;
    int nFrames = 2;
    Vec_Ptr_t * vRes;
    Vec_Int_t * vNodes = Vec_IntAlloc( 3 );
    Vec_IntPush( vNodes, 1 );
    Vec_IntPush( vNodes, 2 );
    Vec_IntPush( vNodes, 3 );
    vRes = Wlc_NtkSimulate( p, vNodes, nWords, nFrames );
    Wlc_NtkSimulatePrint( p, vNodes, vRes, nWords, nFrames );
    Wlc_NtkDeleteSim( vRes );
    Vec_IntFree( vNodes );
}

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


ABC_NAMESPACE_IMPL_END

