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

  FileName    [mpmGates.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Configurable technology mapper.]

  Synopsis    [Standard-cell mapping.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 1, 2013.]

  Revision    [$Id: mpmGates.c,v 1.00 2013/06/01 00:00:00 alanmi Exp $]

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

#include "mpmInt.h"
#include "misc/st/st.h"
#include "map/mio/mio.h"
#include "map/scl/sclSize.h"
#include "map/scl/sclTime.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Finds matches fore each DSD class.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Wec_t * Mpm_ManFindDsdMatches( Mpm_Man_t * p, void * pScl )
{
    int fVerbose = p->pPars->fVeryVerbose;
    SC_Lib * pLib = (SC_Lib *)pScl;
    Vec_Wec_t * vClasses;
    Vec_Int_t * vClass;
    SC_Cell * pRepr;
    int i, Config, iClass;
    word Truth;
    vClasses = Vec_WecStart( 600 );
    SC_LibForEachCellClass( pLib, pRepr, i )
    {
        if ( pRepr->n_inputs > 6 || pRepr->n_outputs > 1 )
        {
            if ( fVerbose )
            printf( "Skipping cell %s with %d inputs and %d outputs\n", pRepr->pName, pRepr->n_inputs, pRepr->n_outputs );
            continue;
        }
        Truth = *Vec_WrdArray( SC_CellPin(pRepr, pRepr->n_inputs)->vFunc );
        Config = Mpm_CutCheckDsd6( p, Truth );
        if ( Config == -1 )
        {
            if ( fVerbose )
            printf( "Skipping cell %s with non-DSD function\n", pRepr->pName );
            continue;
        }
        iClass = Config >> 17;
        Config = (pRepr->Id << 17) | (Config & 0x1FFFF);
        // write gate and NPN config for this DSD class
        vClass = Vec_WecEntry( vClasses, iClass );
        Vec_IntPush( vClass, Config );
        if ( !fVerbose )
            continue;

        printf( "Gate %5d  %-30s : ", pRepr->Id, pRepr->pName );
        printf( "Class %3d  ", iClass );
        printf( "Area %10.3f  ", pRepr->area );
        Extra_PrintBinary( stdout, (unsigned *)&Config, 17 );
        printf( "   " );
        Kit_DsdPrintFromTruth( (unsigned *)&Truth, pRepr->n_inputs ); printf( "\n" );
    }
    return vClasses;
}

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

  Synopsis    [Find mapping of DSD classes into Genlib library cells.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Mpm_ManFindCells( Mio_Library_t * pMio, SC_Lib * pScl, Vec_Wec_t * vNpnConfigs )
{
    Vec_Ptr_t * vNpnGatesMio;
    Vec_Int_t * vClass;
    Mio_Gate_t * pMioGate;
    SC_Cell * pCell;
    int Config, iClass;
    vNpnGatesMio = Vec_PtrStart( Vec_WecSize(vNpnConfigs) );
    Vec_WecForEachLevel( vNpnConfigs, vClass, iClass )
    {
        if ( Vec_IntSize(vClass) == 0 )
            continue;
        Config = Vec_IntEntry(vClass, 0);
        pCell = SC_LibCell( pScl, (Config >> 17) );
        pMioGate = Mio_LibraryReadGateByName( pMio, pCell->pName, NULL );
        if ( pMioGate == NULL )
        {
            Vec_PtrFree( vNpnGatesMio );
            return NULL;
        }
        assert( pMioGate != NULL );
        Vec_PtrWriteEntry( vNpnGatesMio, iClass, pMioGate );
    }
    return vNpnGatesMio;
}

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

  Synopsis    [Derive mapped network as an ABC network.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Mpm_ManFindMappedNodes( Mpm_Man_t * p )
{
    Vec_Int_t * vNodes;
    Mig_Obj_t * pObj;
    vNodes = Vec_IntAlloc( 1000 );
    Mig_ManForEachObj( p->pMig, pObj )
        if ( Mig_ObjIsNode(pObj) && Mpm_ObjMapRef(p, pObj) )
            Vec_IntPush( vNodes, Mig_ObjId(pObj) );
    return vNodes;
}
Abc_Obj_t * Mpm_ManGetAbcNode( Abc_Ntk_t * pNtk, Vec_Int_t * vCopy, int iMigLit )
{
    Abc_Obj_t * pObj;
    int iObjId = Vec_IntEntry( vCopy, iMigLit );
    if ( iObjId >= 0 )
        return Abc_NtkObj( pNtk, iObjId );
    iObjId = Vec_IntEntry( vCopy, Abc_LitNot(iMigLit) );
    assert( iObjId >= 0 );
    pObj = Abc_NtkCreateNodeInv( pNtk, Abc_NtkObj(pNtk, iObjId) );
    Vec_IntWriteEntry( vCopy, iMigLit, Abc_ObjId(pObj) );
    return pObj;
}
Abc_Ntk_t * Mpm_ManDeriveMappedAbcNtk( Mpm_Man_t * p, Mio_Library_t * pMio )
{
    Abc_Ntk_t * pNtk;
    Vec_Ptr_t * vNpnGatesMio;
    Vec_Int_t * vNodes, * vCopy, * vClass;
    Abc_Obj_t * pObj, * pFanin;
    Mig_Obj_t * pNode;
    Mpm_Cut_t * pCutBest;
    int i, k, iNode, iMigLit, fCompl, Config;

    // find mapping of SCL cells into MIO cells
    vNpnGatesMio = Mpm_ManFindCells( pMio, (SC_Lib *)p->pPars->pScl, p->vNpnConfigs );
    if ( vNpnGatesMio == NULL )
    {
        printf( "Genlib library does not match SCL library.\n" );
        return NULL;
    }

    // create mapping for each phase of each node
    vCopy = Vec_IntStartFull( 2 * Mig_ManObjNum(p->pMig) );

    // get internal nodes
    vNodes = Mpm_ManFindMappedNodes( p );

    // start the network
    pNtk = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_MAP, 1 );
    pNtk->pName = Extra_UtilStrsav( p->pMig->pName );
    pNtk->pManFunc = pMio;

    // create primary inputs
    Mig_ManForEachCi( p->pMig, pNode, i )
    {
        pObj = Abc_NtkCreatePi(pNtk);
        Vec_IntWriteEntry( vCopy, Abc_Var2Lit( Mig_ObjId(pNode), 0 ), Abc_ObjId(pObj) );
    }
    Abc_NtkAddDummyPiNames( pNtk );

    // create constant nodes
    Mig_ManForEachCo( p->pMig, pNode, i )
        if ( Mig_ObjFaninLit(pNode, 0) == 0 )
        {
            pObj = Abc_NtkCreateNodeConst0(pNtk);
            Vec_IntWriteEntry( vCopy, Abc_Var2Lit( 0, 0 ), Abc_ObjId(pObj) );
            break;
        }
    Mig_ManForEachCo( p->pMig, pNode, i )
        if ( Mig_ObjFaninLit(pNode, 0) == 1 )
        {
            pObj = Abc_NtkCreateNodeConst1(pNtk);
            Vec_IntWriteEntry( vCopy, Abc_Var2Lit( 0, 1 ), Abc_ObjId(pObj) );
            break;
        }

    // create internal nodes
    Vec_IntForEachEntry( vNodes, iNode, i )
    {
        pCutBest = Mpm_ObjCutBestP( p, Mig_ManObj(p->pMig, iNode) );
        vClass = Vec_WecEntry( p->vNpnConfigs, Abc_Lit2Var(pCutBest->iFunc) );
        Config = Vec_IntEntry( vClass, 0 );
        pObj = Abc_NtkCreateNode( pNtk );
        pObj->pData = Vec_PtrEntry( vNpnGatesMio, Abc_Lit2Var(pCutBest->iFunc) );
        assert( pObj->pData != NULL );
        fCompl = pCutBest->fCompl ^ Abc_LitIsCompl(pCutBest->iFunc) ^ ((Config >> 16) & 1);
        Config &= 0xFFFF;
        for ( k = 0; k < (int)pCutBest->nLeaves; k++ )
        {
            assert( (Config >> 6) < 720 );
            iMigLit = pCutBest->pLeaves[ (int)(p->Perm6[Config >> 6][k]) ];
            pFanin = Mpm_ManGetAbcNode( pNtk, vCopy, Abc_LitNotCond(iMigLit, (Config >> k) & 1) );
            Abc_ObjAddFanin( pObj, pFanin );
        }
        Vec_IntWriteEntry( vCopy, Abc_Var2Lit(iNode, fCompl), Abc_ObjId(pObj) );
    }

    // create primary outputs
    Mig_ManForEachCo( p->pMig, pNode, i )
    {
        pObj = Abc_NtkCreatePo(pNtk);
        pFanin = Mpm_ManGetAbcNode( pNtk, vCopy, Mig_ObjFaninLit(pNode, 0) );
        Abc_ObjAddFanin( pObj, pFanin );
    }
    Abc_NtkAddDummyPoNames( pNtk );

    // clean up
    Vec_PtrFree( vNpnGatesMio );
    Vec_IntFree( vNodes );
    Vec_IntFree( vCopy );
    return pNtk;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Abc_Ntk_t * Mpm_ManPerformCellMapping( Mig_Man_t * pMig, Mpm_Par_t * pPars, Mio_Library_t * pMio )
{
    Abc_Ntk_t * pNew;
    Mpm_Man_t * p;
    assert( pPars->fMap4Gates );
    p = Mpm_ManStart( pMig, pPars );
    if ( p->pPars->fVerbose ) 
        Mpm_ManPrintStatsInit( p );
    p->vNpnConfigs = Mpm_ManFindDsdMatches( p, p->pPars->pScl );
    Mpm_ManPrepare( p );
    Mpm_ManPerform( p );
    if ( p->pPars->fVerbose ) 
        Mpm_ManPrintStats( p );
    pNew = Mpm_ManDeriveMappedAbcNtk( p, pMio );
    Mpm_ManStop( p );
    return pNew;
}
Abc_Ntk_t * Mpm_ManCellMapping( Gia_Man_t * pGia, Mpm_Par_t * pPars, void * pMio )
{
    Mig_Man_t * p;
    Abc_Ntk_t * pNew;
    assert( pMio != NULL );
    assert( pPars->pLib->LutMax <= MPM_VAR_MAX );
    assert( pPars->nNumCuts <= MPM_CUT_MAX );
    if ( pPars->fUseGates )
    {
        pGia = Gia_ManDupMuxes( pGia );
        p = Mig_ManCreate( pGia );
        Gia_ManStop( pGia );
    }
    else
        p = Mig_ManCreate( pGia );
    pNew = Mpm_ManPerformCellMapping( p, pPars, (Mio_Library_t *)pMio );
    Mig_ManStop( p );
    return pNew;
}

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


ABC_NAMESPACE_IMPL_END