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

  FileName    [ifLibLut.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [FPGA mapping based on priority cuts.]

  Synopsis    [LUT library.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - November 21, 2006.]

  Revision    [$Id: ifLibLut.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $]

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

#include "if.h"
#include "base/main/mainInt.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Reads the description of LUTs from the LUT library file.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
If_LibLut_t * If_LibLutReadString( char * pStr )
{
    If_LibLut_t * p;
    Vec_Ptr_t * vStrs;
    char * pToken, * pBuffer, * pStrNew, * pStrMem;
    int i, k, j;

    if ( pStr == NULL || pStr[0] == 0 )
        return NULL;

    vStrs = Vec_PtrAlloc( 1000 );
    pStrNew = pStrMem = Abc_UtilStrsav( pStr );
    while ( *pStrNew )
    {
        Vec_PtrPush( vStrs, pStrNew );
        while ( *pStrNew != '\n' )
            pStrNew++;
        while ( *pStrNew == '\n' )
            *pStrNew++ = '\0';
    }

    p = ABC_ALLOC( If_LibLut_t, 1 );
    memset( p, 0, sizeof(If_LibLut_t) );

    i = 1;
    //while ( fgets( pBuffer, 1000, pFile ) != NULL )
    Vec_PtrForEachEntry( char *, vStrs, pBuffer, j )
    {
        if ( pBuffer[0] == 0 )
            continue;
        pToken = strtok( pBuffer, " \t\n" );
        if ( pToken == NULL )
            continue;
        if ( pToken[0] == '#' )
            continue;
        if ( i != atoi(pToken) )
        {
            Abc_Print( 1, "Error in the LUT library string.\n" );
            ABC_FREE( p->pName );
            ABC_FREE( p );
            ABC_FREE( pStrMem );
            Vec_PtrFree( vStrs );
            return NULL;
        }

        // read area
        pToken = strtok( NULL, " \t\n" );
        p->pLutAreas[i] = (float)atof(pToken);

        // read delays
        k = 0;
        while ( (pToken = strtok( NULL, " \t\n" )) )
            p->pLutDelays[i][k++] = (float)atof(pToken);

        // check for out-of-bound
        if ( k > i )
        {
            Abc_Print( 1, "LUT %d has too many pins (%d). Max allowed is %d.\n", i, k, i );
            ABC_FREE( p->pName );
            ABC_FREE( p );
            ABC_FREE( pStrMem );
            Vec_PtrFree( vStrs );
            return NULL;
        }

        // check if var delays are specified
        if ( k > 1 )
            p->fVarPinDelays = 1;

        if ( i == IF_MAX_LUTSIZE )
        {
            Abc_Print( 1, "Skipping LUTs of size more than %d.\n", i );
            ABC_FREE( p->pName );
            ABC_FREE( p );
            ABC_FREE( pStrMem );
            Vec_PtrFree( vStrs );
            return NULL;
        }
        i++;
    }
    p->LutMax = i-1;

    // check the library
    if ( p->fVarPinDelays )
    {
        for ( i = 1; i <= p->LutMax; i++ )
            for ( k = 0; k < i; k++ )
            {
                if ( p->pLutDelays[i][k] <= 0.0 )
                    Abc_Print( 0, "Pin %d of LUT %d has delay %f. Pin delays should be non-negative numbers. Technology mapping may not work correctly.\n", 
                        k, i, p->pLutDelays[i][k] );
                if ( k && p->pLutDelays[i][k-1] > p->pLutDelays[i][k] )
                    Abc_Print( 0, "Pin %d of LUT %d has delay %f. Pin %d of LUT %d has delay %f. Pin delays should be in non-decreasing order. Technology mapping may not work correctly.\n", 
                        k-1, i, p->pLutDelays[i][k-1], 
                        k, i, p->pLutDelays[i][k] );
            }
    }
    else
    {
        for ( i = 1; i <= p->LutMax; i++ )
        {
            if ( p->pLutDelays[i][0] <= 0.0 )
                Abc_Print( 0, "LUT %d has delay %f. Pin delays should be non-negative numbers. Technology mapping may not work correctly.\n", 
                    i, p->pLutDelays[i][0] );
        }
    }

    // cleanup
    ABC_FREE( pStrMem );
    Vec_PtrFree( vStrs );
    return p;
}

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

  Synopsis    [Sets the library associated with the string.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_FrameSetLutLibrary( Abc_Frame_t * pAbc, char * pLutLibString )
{
    If_LibLut_t * pLib = If_LibLutReadString( pLutLibString );
    if ( pLib == NULL )
    {
        fprintf( stdout, "Reading LUT library from string has failed.\n" );
        return 0;
    }
    // replace the current library
    If_LibLutFree( (If_LibLut_t *)Abc_FrameReadLibLut() );
    Abc_FrameSetLibLut( pLib );
    return 1;
}
int Abc_FrameSetLutLibraryTest( Abc_Frame_t * pAbc )
{
    char * pStr = "1 1.00  1000\n2 1.00  1000 1200\n3 1.00  1000 1200 1400\n4 1.00  1000 1200 1400 1600\n5 1.00  1000 1200 1400 1600 1800\n6 1.00  1000 1200 1400 1600 1800 2000\n\n\n";
    Abc_FrameSetLutLibrary( pAbc, pStr );
    return 1;
}


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

  Synopsis    [Reads the description of LUTs from the LUT library file.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
If_LibLut_t * If_LibLutRead( char * FileName )
{
    char pBuffer[1000], * pToken;
    If_LibLut_t * p;
    FILE * pFile;
    int i, k;

    pFile = fopen( FileName, "r" );
    if ( pFile == NULL )
    {
        Abc_Print( -1, "Cannot open LUT library file \"%s\".\n", FileName );
        return NULL;
    }

    p = ABC_ALLOC( If_LibLut_t, 1 );
    memset( p, 0, sizeof(If_LibLut_t) );
    p->pName = Abc_UtilStrsav( FileName );

    i = 1;
    while ( fgets( pBuffer, 1000, pFile ) != NULL )
    {
        pToken = strtok( pBuffer, " \t\n" );
        if ( pToken == NULL )
            continue;
        if ( pToken[0] == '#' )
            continue;
        if ( i != atoi(pToken) )
        {
            Abc_Print( 1, "Error in the LUT library file \"%s\".\n", FileName );
            ABC_FREE( p->pName );
            ABC_FREE( p );
            fclose( pFile );
            return NULL;
        }

        // read area
        pToken = strtok( NULL, " \t\n" );
        p->pLutAreas[i] = (float)atof(pToken);

        // read delays
        k = 0;
        while ( (pToken = strtok( NULL, " \t\n" )) )
            p->pLutDelays[i][k++] = (float)atof(pToken);

        // check for out-of-bound
        if ( k > i )
        {
            ABC_FREE( p->pName );
            ABC_FREE( p );
            Abc_Print( 1, "LUT %d has too many pins (%d). Max allowed is %d.\n", i, k, i );
            fclose( pFile );
            return NULL;
        }

        // check if var delays are specified
        if ( k > 1 )
            p->fVarPinDelays = 1;

        if ( i == IF_MAX_LUTSIZE )
        {
            ABC_FREE( p->pName );
            ABC_FREE( p );
            Abc_Print( 1, "Skipping LUTs of size more than %d.\n", i );
            fclose( pFile );
            return NULL;
        }
        i++;
    }
    p->LutMax = i-1;

    // check the library
    if ( p->fVarPinDelays )
    {
        for ( i = 1; i <= p->LutMax; i++ )
            for ( k = 0; k < i; k++ )
            {
                if ( p->pLutDelays[i][k] <= 0.0 )
                    Abc_Print( 0, "Pin %d of LUT %d has delay %f. Pin delays should be non-negative numbers. Technology mapping may not work correctly.\n", 
                        k, i, p->pLutDelays[i][k] );
                if ( k && p->pLutDelays[i][k-1] > p->pLutDelays[i][k] )
                    Abc_Print( 0, "Pin %d of LUT %d has delay %f. Pin %d of LUT %d has delay %f. Pin delays should be in non-decreasing order. Technology mapping may not work correctly.\n", 
                        k-1, i, p->pLutDelays[i][k-1], 
                        k, i, p->pLutDelays[i][k] );
            }
    }
    else
    {
        for ( i = 1; i <= p->LutMax; i++ )
        {
            if ( p->pLutDelays[i][0] <= 0.0 )
                Abc_Print( 0, "LUT %d has delay %f. Pin delays should be non-negative numbers. Technology mapping may not work correctly.\n", 
                    i, p->pLutDelays[i][0] );
        }
    }

    fclose( pFile );
    return p;
}

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

  Synopsis    [Duplicates the LUT library.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
If_LibLut_t * If_LibLutDup( If_LibLut_t * p )
{
    If_LibLut_t * pNew;
    pNew = ABC_ALLOC( If_LibLut_t, 1 );
    *pNew = *p;
    pNew->pName = Abc_UtilStrsav( pNew->pName );
    return pNew;
}

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

  Synopsis    [Frees the LUT library.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void If_LibLutFree( If_LibLut_t * pLutLib )
{
    if ( pLutLib == NULL )
        return;
    ABC_FREE( pLutLib->pName );
    ABC_FREE( pLutLib );
}


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

  Synopsis    [Prints the LUT library.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void If_LibLutPrint( If_LibLut_t * pLutLib )
{
    int i, k;
    Abc_Print( 1, "# The area/delay of k-variable LUTs:\n" );
    Abc_Print( 1, "# k    area     delay\n" );
    if ( pLutLib->fVarPinDelays )
    {
        for ( i = 1; i <= pLutLib->LutMax; i++ )
        {
            Abc_Print( 1, "%d   %7.2f  ", i, pLutLib->pLutAreas[i] );
            for ( k = 0; k < i; k++ )
                Abc_Print( 1, " %7.2f", pLutLib->pLutDelays[i][k] );
            Abc_Print( 1, "\n" );
        }
    }
    else
        for ( i = 1; i <= pLutLib->LutMax; i++ )
            Abc_Print( 1, "%d   %7.2f   %7.2f\n", i, pLutLib->pLutAreas[i], pLutLib->pLutDelays[i][0] );
}

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

  Synopsis    [Allocates the cell library structure.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
If_LibCell_t * If_LibCellAlloc( void )
{
    If_LibCell_t * p;
    p = ABC_ALLOC( If_LibCell_t, 1 );
    memset( p, 0, sizeof(If_LibCell_t) );
    return p;
}

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

  Synopsis    [Computes the record size.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static int If_LibCellComputeRecordSize( char * pFuncDesc, int nInputs )
{
    int i, nMapBytes, nRecordSize;
    assert( pFuncDesc != NULL );
    nMapBytes = (Abc_Base2Log(nInputs + 2) + 7) / 8;
    nRecordSize = 1 + nInputs * nMapBytes;
    for ( i = 0; pFuncDesc[i]; i++ )
    {
        int nLutVars = 0;
        if ( pFuncDesc[i] != '{' )
            continue;
        for ( i++; pFuncDesc[i] && pFuncDesc[i] != '}'; i++ )
            if ( pFuncDesc[i] >= 'a' && pFuncDesc[i] <= 'z' )
                nLutVars++;
        assert( nLutVars < 31 );
        nRecordSize += ((1 << nLutVars) + 7) / 8;
        if ( pFuncDesc[i] == 0 )
            break;
    }
    return nRecordSize;
}

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

  Synopsis    [Reads the description of cells from the cell library file.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
If_LibCell_t * If_LibCellRead( char * FileName )
{
    char pBuffer[1000], * pToken;
    If_LibCell_t * p;
    FILE * pFile;
    int i, k;
    int CellId;
    char * FuncDesc;

    pFile = fopen( FileName, "r" );
    if ( pFile == NULL )
    {
        Abc_Print( -1, "Cannot open cell library file \"%s\".\n", FileName );
        return NULL;
    }

    p = If_LibCellAlloc();
    p->pName = Abc_UtilStrsav( FileName );
    p->nCellNum = 0;

    // Read each line of the file
    while ( fgets( pBuffer, 1000, pFile ) != NULL )
    {
        pToken = strtok( pBuffer, " \t\n" );
        if ( pToken == NULL )
            continue;
        if ( pToken[0] == '#' )
            continue;

        // Read CellId
        CellId = atoi(pToken);
        if ( CellId < 0 || CellId >= IF_MAX_LUTSIZE )
        {
            Abc_Print( -1, "Cell ID %d is out of bounds (0-%d).\n", CellId, IF_MAX_LUTSIZE-1 );
            If_LibCellFree( p );
            fclose( pFile );
            return NULL;
        }

        // Read FuncDesc
        pToken = strtok( NULL, " \t\n" );
        if ( pToken == NULL )
        {
            Abc_Print( -1, "Missing function description for cell %d.\n", CellId );
            If_LibCellFree( p );
            fclose( pFile );
            return NULL;
        }
        FuncDesc = Abc_UtilStrsav( pToken );
        p->pCellNames[CellId] = FuncDesc;

        // Determine number of inputs from function description
        int nInputs = 0;
        if ( FuncDesc[0] >= 'a' && FuncDesc[0] <= 'z' )
        {
            // If it begins with a letter, that letter indicates the output
            // and the number of inputs is that letter - 'a'
            nInputs = FuncDesc[0] - 'a';
        }
        else
        {
            // Otherwise, find the largest letter in the formula
            char maxChar = 'a' - 1;
            for ( i = 0; FuncDesc[i]; i++ )
            {
                if ( FuncDesc[i] >= 'a' && FuncDesc[i] <= 'z' && FuncDesc[i] > maxChar )
                    maxChar = FuncDesc[i];
            }
            if ( maxChar >= 'a' )
                nInputs = maxChar - 'a' + 1;
        }
        p->nCellInputs[CellId] = nInputs;
        p->pCellRecordSizes[CellId] = If_LibCellComputeRecordSize( FuncDesc, nInputs );

        // Read Area
        pToken = strtok( NULL, " \t\n" );
        if ( pToken == NULL )
        {
            Abc_Print( -1, "Missing area for cell %d.\n", CellId );
            If_LibCellFree( p );
            fclose( pFile );
            return NULL;
        }
        p->pCellAreas[CellId] = (float)atof(pToken);

        // Read all available delays
        k = 0;
        while ( (pToken = strtok( NULL, " \t\n" )) != NULL && k < IF_MAX_LUTSIZE )
        {
            p->pCellPinDelays[CellId][k] = atoi(pToken);
            k++;
        }

        // Check if number of delays matches number of inputs
        if ( k != nInputs )
        {
            Abc_Print( 0, "Warning: Cell %d has %d inputs but %d delays specified.\n", CellId, nInputs, k );
        }

        p->nCellNum++;
    }

    fclose( pFile );

    // Validate the library
    for ( i = 0; i < IF_MAX_LUTSIZE; i++ )
    {
        if ( p->pCellNames[i] == NULL )
            continue;
        for ( k = 0; k < IF_MAX_LUTSIZE && p->pCellPinDelays[i][k] > 0; k++ )
        {
            if ( p->pCellPinDelays[i][k] < 0 )
            {
                Abc_Print( 0, "Pin %d of cell %d has delay %d. Pin delays should be non-negative. Technology mapping may not work correctly.\n",
                    k, i, p->pCellPinDelays[i][k] );
            }
        }
    }

    return p;
}

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

  Synopsis    [Duplicates the cell library.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
If_LibCell_t * If_LibCellDup( If_LibCell_t * p )
{
    If_LibCell_t * pNew;
    int i;
    pNew = ABC_ALLOC( If_LibCell_t, 1 );
    *pNew = *p;
    pNew->pName = Abc_UtilStrsav( p->pName );
    for ( i = 0; i < IF_MAX_LUTSIZE; i++ )
        if ( p->pCellNames[i] )
            pNew->pCellNames[i] = Abc_UtilStrsav( p->pCellNames[i] );
    return pNew;
}

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

  Synopsis    [Frees the cell library.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void If_LibCellFree( If_LibCell_t * pCellLib )
{
    int i;
    if ( pCellLib == NULL )
        return;
    ABC_FREE( pCellLib->pName );
    for ( i = 0; i < IF_MAX_LUTSIZE; i++ )
        ABC_FREE( pCellLib->pCellNames[i] );
    ABC_FREE( pCellLib );
}

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

  Synopsis    [Returns the maximum number of inputs in the cell library.]

  Description [Used for auto-detecting K value for &if command.]

  SideEffects []

  SeeAlso     []

***********************************************************************/
int If_LibCellGetMaxInputs( If_LibCell_t * pCellLib )
{
    int i, nMaxInputs = 0;
    if ( pCellLib == NULL )
        return 0;
    for ( i = 0; i < pCellLib->nCellNum; i++ )
        if ( pCellLib->nCellInputs[i] > nMaxInputs )
            nMaxInputs = pCellLib->nCellInputs[i];
    return nMaxInputs;
}

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

  Synopsis    [Prints the cell library.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void If_LibCellPrint( If_LibCell_t * pCellLib )
{
    int i, k;
    if ( pCellLib == NULL )
    {
        Abc_Print( 1, "Cell library is not available.\n" );
        return;
    }
    Abc_Print( 1, "# Cell library: %s\n", pCellLib->pName ? pCellLib->pName : "Unknown" );
    Abc_Print( 1, "# Number of cells: %d\n", pCellLib->nCellNum );
    Abc_Print( 1, "# CellId  Inputs  Cell Description                     Area     Delays\n" );

    for ( i = 0; i < IF_MAX_LUTSIZE; i++ )
    {
        if ( pCellLib->pCellNames[i] == NULL )
            continue;

        Abc_Print( 1, "%3d  %6d       %-32s   %6.2f  ", i, pCellLib->nCellInputs[i], pCellLib->pCellNames[i], pCellLib->pCellAreas[i] );

        // Print all non-zero delays
        for ( k = 0; k < IF_MAX_LUTSIZE && pCellLib->pCellPinDelays[i][k] > 0; k++ )
            Abc_Print( 1, " %4d", pCellLib->pCellPinDelays[i][k] );
        Abc_Print( 1, "\n" );
    }
}

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

  Synopsis    [Returns 1 if the delays are discrete.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int If_LibLutDelaysAreDiscrete( If_LibLut_t * pLutLib )
{
    float Delay;
    int i;
    for ( i = 1; i <= pLutLib->LutMax; i++ )
    {
        Delay = pLutLib->pLutDelays[i][0];
        if ( ((float)((int)Delay)) != Delay )
            return 0;
    }
    return 1;
}

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

  Synopsis    [Returns 1 if the delays are discrete.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int If_LibLutDelaysAreDifferent( If_LibLut_t * pLutLib )
{
    int i, k;
    float Delay = pLutLib->pLutDelays[1][0];
    if ( pLutLib->fVarPinDelays )
    {
        for ( i = 2; i <= pLutLib->LutMax; i++ )
        for ( k = 0; k < i; k++ )
            if ( pLutLib->pLutDelays[i][k] != Delay )
                return 1;
    }
    else
    {
        for ( i = 2; i <= pLutLib->LutMax; i++ )
            if ( pLutLib->pLutDelays[i][0] != Delay )
                return 1;
    }
    return 0;
}

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

  Synopsis    [Sets simple LUT library.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
If_LibLut_t * If_LibLutSetSimple( int nLutSize )
{
    If_LibLut_t s_LutLib10= { "lutlib",10, 0, {0,1,1,1,1,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1},{1},{1},{1},{1}} };
    If_LibLut_t s_LutLib9 = { "lutlib", 9, 0, {0,1,1,1,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1},{1},{1},{1}} };
    If_LibLut_t s_LutLib8 = { "lutlib", 8, 0, {0,1,1,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1},{1},{1}} };
    If_LibLut_t s_LutLib7 = { "lutlib", 7, 0, {0,1,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1},{1}} };
    If_LibLut_t s_LutLib6 = { "lutlib", 6, 0, {0,1,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1},{1}} };
    If_LibLut_t s_LutLib5 = { "lutlib", 5, 0, {0,1,1,1,1,1}, {{0},{1},{1},{1},{1},{1}} };
    If_LibLut_t s_LutLib4 = { "lutlib", 4, 0, {0,1,1,1,1}, {{0},{1},{1},{1},{1}} };
    If_LibLut_t s_LutLib3 = { "lutlib", 3, 0, {0,1,1,1}, {{0},{1},{1},{1}} };
    If_LibLut_t * pLutLib;
    assert( nLutSize >= 3 && nLutSize <= 10 );
    switch ( nLutSize )
    {
        case 3:  pLutLib = &s_LutLib3; break;
        case 4:  pLutLib = &s_LutLib4; break;
        case 5:  pLutLib = &s_LutLib5; break;
        case 6:  pLutLib = &s_LutLib6; break;
        case 7:  pLutLib = &s_LutLib7; break;
        case 8:  pLutLib = &s_LutLib8; break;
        case 9:  pLutLib = &s_LutLib9; break;
        case 10: pLutLib = &s_LutLib10; break;
        default: pLutLib = NULL; break;
    }
    if ( pLutLib == NULL )
        return NULL;
    return If_LibLutDup(pLutLib);
}

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

  Synopsis    [Gets the delay of the fastest pin.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float If_LibLutFastestPinDelay( If_LibLut_t * p )
{
    return !p? 1.0 : p->pLutDelays[p->LutMax][0];
}

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

  Synopsis    [Gets the delay of the slowest pin.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float If_LibLutSlowestPinDelay( If_LibLut_t * p )
{
    return !p? 1.0 : (p->fVarPinDelays? p->pLutDelays[p->LutMax][p->LutMax-1]: p->pLutDelays[p->LutMax][0]);
}

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


ABC_NAMESPACE_IMPL_END

