diff --git a/Makefile b/Makefile index 7d9e109ac..b1c5a9f47 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ OS := $(shell uname -s) MODULES := \ $(wildcard src/ext*) \ src/base/abc src/base/abci src/base/cmd src/base/io src/base/main src/base/exor \ - src/base/ver src/base/wlc src/base/wln src/base/acb src/base/pla src/base/test \ + src/base/ver src/base/wlc src/base/wln src/base/sn src/base/acb src/base/pla src/base/test \ src/map/mapper src/map/mio src/map/super src/map/if src/map/if/acd \ src/map/amap src/map/cov src/map/scl src/map/mpm src/map/emap \ src/misc/extra src/misc/mvc src/misc/st src/misc/util src/misc/nm \ diff --git a/abclib.dsp b/abclib.dsp index f52060616..9148a2ec6 100644 --- a/abclib.dsp +++ b/abclib.dsp @@ -971,10 +971,6 @@ SOURCE=.\src\base\acb\acbAig.c # End Source File # Begin Source File -SOURCE=.\src\base\acb\acbCom.c -# End Source File -# Begin Source File - SOURCE=.\src\base\acb\acbFunc.c # End Source File # Begin Source File @@ -1008,6 +1004,14 @@ SOURCE=.\src\base\acb\acbUtil.c # End Group # Begin Group "wln" +SOURCE=.\src\base\acb\acbXec.c +# End Source File +# Begin Source File + +SOURCE=.\src\base\acb\acbXec.h +# End Source File +# Begin Source File + # PROP Default_Filter "" # Begin Source File @@ -1066,6 +1070,74 @@ SOURCE=.\src\base\wln\wlnWlc.c SOURCE=.\src\base\wln\wlnWriteVer.c # End Source File # End Group +# Begin Group "sn" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\base\sn\sn.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snTech.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snMapDsp.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snMapMem.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snMapAdd.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snMapTech.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snCheck.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snBoundary.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snMapLut.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snPth.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snBlast.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snMiniAig.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snMiniLut.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snMiniGate.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snMux.h +# End Source File +# Begin Source File + +SOURCE=.\src\base\sn\snCom.c +# End Source File +# End Group # End Group # Begin Group "bdd" diff --git a/src/aig/miniaig/miniaig.h b/src/aig/miniaig/miniaig.h index c7ce14e96..82457336b 100644 --- a/src/aig/miniaig/miniaig.h +++ b/src/aig/miniaig/miniaig.h @@ -351,6 +351,20 @@ static int Mini_AigAndMulti( Mini_Aig_t * p, int * pLits, int nLits ) } return pLits[0]; } +static int Mini_AigXorMulti( Mini_Aig_t * p, int * pLits, int nLits ) +{ + int i; + assert( nLits > 0 ); + while ( nLits > 1 ) + { + for ( i = 0; i < nLits/2; i++ ) + pLits[i] = Mini_AigXor(p, pLits[2*i], pLits[2*i+1]); + if ( nLits & 1 ) + pLits[i++] = pLits[nLits-1]; + nLits = i; + } + return pLits[0]; +} static int Mini_AigMuxMulti( Mini_Aig_t * p, int * pCtrl, int nCtrl, int * pData, int nData ) { int i, c; @@ -847,4 +861,3 @@ ABC_NAMESPACE_HEADER_END //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// - diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 6dcfe964c..7d63dc8ff 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -7813,14 +7813,17 @@ usage: ***********************************************************************/ int Abc_CommandRunTest( Abc_Frame_t * pAbc, int argc, char ** argv ) { - extern void Acb_NtkRunTest( char * pFileNames[4], int fFancy, int fVerbose ); + extern void Acb_NtkRunTest( char * pFileNames[4], int fFancy, int fVerbose, int fUseCadical ); char * pFileNames[4] = {NULL}; - int c, fFancy = 0, fVerbose = 0; + int c, fFancy = 0, fVerbose = 0, fUseCadical = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "fvh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "cfvh" ) ) != EOF ) { switch ( c ) { + case 'c': + fUseCadical ^= 1; + break; case 'f': fFancy ^= 1; break; @@ -7840,12 +7843,13 @@ int Abc_CommandRunTest( Abc_Frame_t * pAbc, int argc, char ** argv ) } for ( c = 0; c < argc - globalUtilOptind; c++ ) pFileNames[c] = argv[globalUtilOptind+c]; - Acb_NtkRunTest( pFileNames, fFancy, fVerbose ); + Acb_NtkRunTest( pFileNames, fFancy, fVerbose, fUseCadical ); return 0; usage: - Abc_Print( -2, "usage: xec [-fvh] \n" ); + Abc_Print( -2, "usage: xec [-cfvh] \n" ); Abc_Print( -2, "\t combinational equivalence checking with x-values\n" ); + Abc_Print( -2, "\t-c : toggle using CaDiCaL SAT-only solving [default = %s]\n", fUseCadical? "yes": "no" ); Abc_Print( -2, "\t-f : toggle using experimental feature [default = %s]\n", fFancy? "yes": "no" ); Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); Abc_Print( -2, "\t-h : print the command usage\n"); @@ -43736,6 +43740,29 @@ static Gia_Man_t * Abc_ReadAigerOrVerilogFile( char * pFileName, char * pFileNam return pGia; } +/**Function************************************************************* + + Synopsis [Returns 1 if all outputs of the swept miter are constant 0.] + + Description [The equivalence check below concludes from the swept miter + having no AND nodes. An AND-free GIA can still have outputs that are + constant 1 or CI literals, which are satisfiable, so the outputs are + checked here as well.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Abc_CecSweptMiterIsConst0( Gia_Man_t * p ) +{ + int i; + for ( i = 0; i < Gia_ManPoNum(p); i++ ) + if ( !Gia_ManPoIsConst0(p, i) ) + return 0; + return 1; +} + /**Function************************************************************* Synopsis [] @@ -44164,10 +44191,12 @@ int Abc_CommandAbc9Cec( Abc_Frame_t * pAbc, int argc, char ** argv ) abctime clk = Abc_Clock(); extern Gia_Man_t * Cec4_ManSimulateTest3( Gia_Man_t * p, int nBTLimit, int fVerbose ); Gia_Man_t * pNew = Cec4_ManSimulateTest3( pMiter, pPars->nBTLimit, pPars->fVerbose ); - if ( Gia_ManAndNum(pNew) == 0 ) + if ( Gia_ManAndNum(pNew) != 0 ) + Abc_Print( 1, "Networks are UNDECIDED. " ); + else if ( Abc_CecSweptMiterIsConst0(pNew) ) Abc_Print( 1, "Networks are equivalent. " ); else - Abc_Print( 1, "Networks are UNDECIDED. " ); + Abc_Print( 1, "Networks are NOT equivalent. " ); Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); Gia_ManStop( pNew ); } @@ -44176,10 +44205,12 @@ int Abc_CommandAbc9Cec( Abc_Frame_t * pAbc, int argc, char ** argv ) abctime clk = Abc_Clock(); extern Gia_Man_t * Cec5_ManSimulateTest3( Gia_Man_t * p, int nBTLimit, int fVerbose ); Gia_Man_t * pNew = Cec5_ManSimulateTest3( pMiter, pPars->nBTLimit, pPars->fVerbose ); - if ( Gia_ManAndNum(pNew) == 0 ) + if ( Gia_ManAndNum(pNew) != 0 ) + Abc_Print( 1, "Networks are UNDECIDED. " ); + else if ( Abc_CecSweptMiterIsConst0(pNew) ) Abc_Print( 1, "Networks are equivalent. " ); else - Abc_Print( 1, "Networks are UNDECIDED. " ); + Abc_Print( 1, "Networks are NOT equivalent. " ); Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); Gia_ManStop( pNew ); } @@ -44380,10 +44411,12 @@ int Abc_CommandAbc9ICec( Abc_Frame_t * pAbc, int argc, char ** argv ) abctime clk = Abc_Clock(); extern Gia_Man_t * Cec4_ManSimulateTest3( Gia_Man_t * p, int nBTLimit, int fVerbose ); Gia_Man_t * pNew = Cec4_ManSimulateTest3( pMiter, pPars->nBTLimit, pPars->fVerbose ); - if ( Gia_ManAndNum(pNew) == 0 ) + if ( Gia_ManAndNum(pNew) != 0 ) + Abc_Print( 1, "Networks are UNDECIDED. " ); + else if ( Abc_CecSweptMiterIsConst0(pNew) ) Abc_Print( 1, "Networks are equivalent. " ); else - Abc_Print( 1, "Networks are UNDECIDED. " ); + Abc_Print( 1, "Networks are NOT equivalent. " ); Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); Gia_ManStop( pNew ); } diff --git a/src/base/abci/abcFraig.c b/src/base/abci/abcFraig.c index 2cfb46bb0..bf40bd582 100644 --- a/src/base/abci/abcFraig.c +++ b/src/base/abci/abcFraig.c @@ -670,13 +670,29 @@ int Abc_NtkFraigStore( Abc_Ntk_t * pNtkAdd ) extern int Abc_NodeCompareCiCo( Abc_Ntk_t * pNtkOld, Abc_Ntk_t * pNtkNew ); if ( !Abc_NodeCompareCiCo(pNtk, (Abc_Ntk_t *)Vec_PtrEntry(vStore, 0)) ) { + // Abc_NtkCompareSignals() sorts the PIs/POs/boxes of both networks by name as a + // side effect, which is what makes the comparison meaningful when the two do use + // the same names. When they do not, the comparison fails, the store is reset and + // this network is kept -- so the sort has to be undone here. Otherwise the stored + // network is a permutation of the one the caller read in, and everything after it + // is off by that permutation with nothing to indicate it. + Vec_Ptr_t * vPis = Vec_PtrDup( pNtk->vPis ); + Vec_Ptr_t * vPos = Vec_PtrDup( pNtk->vPos ); + Vec_Ptr_t * vBoxes = Vec_PtrDup( pNtk->vBoxes ); // reorder PIs of pNtk2 according to pNtk1 if ( !Abc_NtkCompareSignals( pNtk, (Abc_Ntk_t *)Vec_PtrEntry(vStore, 0), 1, 1 ) ) { + Vec_PtrFree( pNtk->vPis ); pNtk->vPis = vPis; vPis = NULL; + Vec_PtrFree( pNtk->vPos ); pNtk->vPos = vPos; vPos = NULL; + Vec_PtrFree( pNtk->vBoxes ); pNtk->vBoxes = vBoxes; vBoxes = NULL; + Abc_NtkOrderCisCos( pNtk ); printf( "Trying to store the network with different primary inputs.\n" ); printf( "The previously stored networks are deleted and this one is added.\n" ); Abc_NtkFraigStoreClean(); } + if ( vPis ) Vec_PtrFree( vPis ); + if ( vPos ) Vec_PtrFree( vPos ); + if ( vBoxes ) Vec_PtrFree( vBoxes ); } } Vec_PtrPush( vStore, pNtk ); diff --git a/src/base/acb/acbCom.c b/src/base/acb/acbCom.c deleted file mode 100644 index 3d62454d3..000000000 --- a/src/base/acb/acbCom.c +++ /dev/null @@ -1,735 +0,0 @@ -/**CFile**************************************************************** - - FileName [acbCom.c] - - SystemName [ABC: Logic synthesis and verification system.] - - PackageName [Hierarchical word-level netlist.] - - Synopsis [Command handlers.] - - Author [Alan Mishchenko] - - Affiliation [UC Berkeley] - - Date [Ver. 1.0. Started - November 29, 2014.] - - Revision [$Id: acbCom.c,v 1.00 2014/11/29 00:00:00 alanmi Exp $] - -***********************************************************************/ - -#include "acb.h" -#include "proof/cec/cec.h" -#include "base/main/mainInt.h" - -ABC_NAMESPACE_IMPL_START - -#if 0 - -//////////////////////////////////////////////////////////////////////// -/// DECLARATIONS /// -//////////////////////////////////////////////////////////////////////// - -static int Acb_CommandRead ( Abc_Frame_t * pAbc, int argc, char ** argv ); -static int Acb_CommandWrite ( Abc_Frame_t * pAbc, int argc, char ** argv ); -static int Acb_CommandPs ( Abc_Frame_t * pAbc, int argc, char ** argv ); -static int Acb_CommandPut ( Abc_Frame_t * pAbc, int argc, char ** argv ); -static int Acb_CommandGet ( Abc_Frame_t * pAbc, int argc, char ** argv ); -static int Acb_CommandClp ( Abc_Frame_t * pAbc, int argc, char ** argv ); -static int Acb_CommandBlast ( Abc_Frame_t * pAbc, int argc, char ** argv ); -static int Acb_CommandCec ( Abc_Frame_t * pAbc, int argc, char ** argv ); -static int Acb_CommandTest ( Abc_Frame_t * pAbc, int argc, char ** argv ); - -static inline Acb_Man_t * Acb_AbcGetMan( Abc_Frame_t * pAbc ) { return (Acb_Man_t *)pAbc->pAbcCba; } -static inline void Acb_AbcFreeMan( Abc_Frame_t * pAbc ) { if ( pAbc->pAbcCba ) Acb_ManFree(Acb_AbcGetMan(pAbc)); } -static inline void Acb_AbcUpdateMan( Abc_Frame_t * pAbc, Acb_Man_t * p ) { Acb_AbcFreeMan(pAbc); pAbc->pAbcCba = p; } - -//////////////////////////////////////////////////////////////////////// -/// FUNCTION DEFINITIONS /// -//////////////////////////////////////////////////////////////////////// - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -void Acb_Init( Abc_Frame_t * pAbc ) -{ - Cmd_CommandAdd( pAbc, "New word level", "@read", Acb_CommandRead, 0 ); - Cmd_CommandAdd( pAbc, "New word level", "@write", Acb_CommandWrite, 0 ); - Cmd_CommandAdd( pAbc, "New word level", "@ps", Acb_CommandPs, 0 ); - Cmd_CommandAdd( pAbc, "New word level", "@put", Acb_CommandPut, 0 ); - Cmd_CommandAdd( pAbc, "New word level", "@get", Acb_CommandGet, 0 ); - Cmd_CommandAdd( pAbc, "New word level", "@clp", Acb_CommandClp, 0 ); - Cmd_CommandAdd( pAbc, "New word level", "@blast", Acb_CommandBlast, 0 ); - Cmd_CommandAdd( pAbc, "New word level", "@cec", Acb_CommandCec, 0 ); - Cmd_CommandAdd( pAbc, "New word level", "@test", Acb_CommandTest, 0 ); -} - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -void Acb_End( Abc_Frame_t * pAbc ) -{ - Acb_AbcFreeMan( pAbc ); -} - - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -int Acb_CommandRead( Abc_Frame_t * pAbc, int argc, char ** argv ) -{ - FILE * pFile; - Acb_Man_t * p = NULL; - char * pFileName = NULL; - int c, fTest = 0, fDfs = 0, fVerbose = 0; - Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "tdvh" ) ) != EOF ) - { - switch ( c ) - { - case 't': - fTest ^= 1; - break; - case 'd': - fDfs ^= 1; - break; - case 'v': - fVerbose ^= 1; - break; - case 'h': - goto usage; - default: - goto usage; - } - } - if ( argc != globalUtilOptind + 1 ) - { - printf( "Acb_CommandRead(): Input file name should be given on the command line.\n" ); - return 0; - } - // get the file name - pFileName = argv[globalUtilOptind]; - if ( (pFile = fopen( pFileName, "r" )) == NULL ) - { - Abc_Print( 1, "Cannot open input file \"%s\". ", pFileName ); - if ( (pFileName = Extra_FileGetSimilarName( pFileName, ".v", ".blif", ".smt", ".acb", NULL )) ) - Abc_Print( 1, "Did you mean \"%s\"?", pFileName ); - Abc_Print( 1, "\n" ); - return 0; - } - fclose( pFile ); - if ( fTest ) - { - if ( !strcmp( Extra_FileNameExtension(pFileName), "blif" ) ) - Prs_ManReadBlifTest( pFileName ); - else if ( !strcmp( Extra_FileNameExtension(pFileName), "v" ) ) - Prs_ManReadVerilogTest( pFileName ); - else - { - printf( "Unrecognized input file extension.\n" ); - return 0; - } - return 0; - } - if ( !strcmp( Extra_FileNameExtension(pFileName), "blif" ) ) - p = Acb_ManReadBlif( pFileName ); - else if ( !strcmp( Extra_FileNameExtension(pFileName), "v" ) ) - p = Acb_ManReadVerilog( pFileName ); - else if ( !strcmp( Extra_FileNameExtension(pFileName), "acb" ) ) - p = Acb_ManReadCba( pFileName ); - else - { - printf( "Unrecognized input file extension.\n" ); - return 0; - } - if ( fDfs ) - { - Acb_Man_t * pTemp; - p = Acb_ManDup( pTemp = p, Acb_NtkCollectDfs ); - Acb_ManFree( pTemp ); - } - Acb_AbcUpdateMan( pAbc, p ); - return 0; -usage: - Abc_Print( -2, "usage: @read [-tdvh] \n" ); - Abc_Print( -2, "\t reads hierarchical design\n" ); - Abc_Print( -2, "\t-t : toggle testing the parser [default = %s]\n", fTest? "yes": "no" ); - Abc_Print( -2, "\t-d : toggle computing DFS ordering [default = %s]\n", fDfs? "yes": "no" ); - Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); - Abc_Print( -2, "\t-h : print the command usage\n"); - return 1; -} - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -int Acb_CommandWrite( Abc_Frame_t * pAbc, int argc, char ** argv ) -{ - Acb_Man_t * p = Acb_AbcGetMan(pAbc); - char * pFileName = NULL; - int fInclineCats = 0; - int c, fVerbose = 0; - Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "cvh" ) ) != EOF ) - { - switch ( c ) - { - case 'c': - fInclineCats ^= 1; - break; - case 'v': - fVerbose ^= 1; - break; - case 'h': - goto usage; - default: - goto usage; - } - } - if ( p == NULL ) - { - Abc_Print( 1, "Acb_CommandWrite(): There is no current design.\n" ); - return 0; - } - - if ( argc == globalUtilOptind + 1 ) - pFileName = argv[globalUtilOptind]; - else if ( argc == globalUtilOptind && p ) - { - pFileName = Extra_FileNameGenericAppend( Acb_ManSpec(p) ? Acb_ManSpec(p) : Acb_ManName(p), "_out.v" ); - printf( "Generated output file name \"%s\".\n", pFileName ); - } - else - { - printf( "Output file name should be given on the command line.\n" ); - return 0; - } - // perform writing - if ( !strcmp( Extra_FileNameExtension(pFileName), "blif" ) ) - Acb_ManWriteBlif( pFileName, p ); - else if ( !strcmp( Extra_FileNameExtension(pFileName), "v" ) ) - Acb_ManWriteVerilog( pFileName, p, fInclineCats ); - else if ( !strcmp( Extra_FileNameExtension(pFileName), "acb" ) ) - Acb_ManWriteCba( pFileName, p ); - else - { - printf( "Unrecognized output file extension.\n" ); - return 0; - } - return 0; -usage: - Abc_Print( -2, "usage: @write [-cvh]\n" ); - Abc_Print( -2, "\t writes the design into a file in BLIF or Verilog\n" ); - Abc_Print( -2, "\t-c : toggle inlining input concatenations [default = %s]\n", fInclineCats? "yes": "no" ); - Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); - Abc_Print( -2, "\t-h : print the command usage\n"); - return 1; -} - - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -int Acb_CommandPs( Abc_Frame_t * pAbc, int argc, char ** argv ) -{ - Acb_Man_t * p = Acb_AbcGetMan(pAbc); - int nModules = 0; - int fShowMulti = 0; - int fShowAdder = 0; - int fDistrib = 0; - int c, fVerbose = 0; - Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "Mmadvh" ) ) != EOF ) - { - switch ( c ) - { - case 'M': - if ( globalUtilOptind >= argc ) - { - Abc_Print( -1, "Command line switch \"-M\" should be followed by an integer.\n" ); - goto usage; - } - nModules = atoi(argv[globalUtilOptind]); - globalUtilOptind++; - if ( nModules < 0 ) - goto usage; - break; - case 'm': - fShowMulti ^= 1; - break; - case 'a': - fShowAdder ^= 1; - break; - case 'd': - fDistrib ^= 1; - break; - case 'v': - fVerbose ^= 1; - break; - case 'h': - goto usage; - default: - goto usage; - } - } - if ( p == NULL ) - { - Abc_Print( 1, "Acb_CommandPs(): There is no current design.\n" ); - return 0; - } - if ( nModules ) - { - Acb_ManPrintStats( p, nModules, fVerbose ); - return 0; - } - Acb_NtkPrintStatsFull( Acb_ManRoot(p), fDistrib, fVerbose ); - if ( fShowMulti ) - Acb_NtkPrintNodes( Acb_ManRoot(p), ABC_OPER_ARI_MUL ); - if ( fShowAdder ) - Acb_NtkPrintNodes( Acb_ManRoot(p), ABC_OPER_ARI_ADD ); - return 0; -usage: - Abc_Print( -2, "usage: @ps [-M num] [-madvh]\n" ); - Abc_Print( -2, "\t prints statistics\n" ); - Abc_Print( -2, "\t-M num : the number of first modules to report [default = %d]\n", nModules ); - Abc_Print( -2, "\t-m : toggle printing multipliers [default = %s]\n", fShowMulti? "yes": "no" ); - Abc_Print( -2, "\t-a : toggle printing adders [default = %s]\n", fShowAdder? "yes": "no" ); - Abc_Print( -2, "\t-d : toggle printing distrubition [default = %s]\n", fDistrib? "yes": "no" ); - Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); - Abc_Print( -2, "\t-h : print the command usage\n"); - return 1; -} - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -int Acb_CommandPut( Abc_Frame_t * pAbc, int argc, char ** argv ) -{ - Acb_Man_t * p = Acb_AbcGetMan(pAbc); - Gia_Man_t * pGia = NULL; - int c, fBarBufs = 1, fSeq = 0, fVerbose = 0; - Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "bsvh" ) ) != EOF ) - { - switch ( c ) - { - case 'b': - fBarBufs ^= 1; - break; - case 's': - fSeq ^= 1; - break; - case 'v': - fVerbose ^= 1; - break; - case 'h': - goto usage; - default: - goto usage; - } - } - if ( p == NULL ) - { - Abc_Print( 1, "Acb_CommandPut(): There is no current design.\n" ); - return 0; - } - pGia = Acb_ManBlast( p, fBarBufs, fSeq, fVerbose ); - if ( pGia == NULL ) - { - Abc_Print( 1, "Acb_CommandPut(): Conversion to AIG has failed.\n" ); - return 0; - } - Abc_FrameUpdateGia( pAbc, pGia ); - return 0; -usage: - Abc_Print( -2, "usage: @put [-bsvh]\n" ); - Abc_Print( -2, "\t extracts AIG from the hierarchical design\n" ); - Abc_Print( -2, "\t-b : toggle using barrier buffers [default = %s]\n", fBarBufs? "yes": "no" ); - Abc_Print( -2, "\t-s : toggle blasting sequential elements [default = %s]\n", fSeq? "yes": "no" ); - Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); - Abc_Print( -2, "\t-h : print the command usage\n"); - return 1; -} - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -int Acb_CommandGet( Abc_Frame_t * pAbc, int argc, char ** argv ) -{ - Acb_Man_t * pNew = NULL, * p = Acb_AbcGetMan(pAbc); - int c, fMapped = 0, fVerbose = 0; - Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "mvh" ) ) != EOF ) - { - switch ( c ) - { - case 'm': - fMapped ^= 1; - break; - case 'v': - fVerbose ^= 1; - break; - case 'h': - goto usage; - default: - goto usage; - } - } - if ( p == NULL ) - { - Abc_Print( 1, "Acb_CommandGet(): There is no current design.\n" ); - return 0; - } - - if ( fMapped ) - { - if ( pAbc->pNtkCur == NULL ) - { - Abc_Print( 1, "Acb_CommandGet(): There is no current mapped design.\n" ); - return 0; - } - pNew = Acb_ManInsertAbc( p, pAbc->pNtkCur ); - } - else - { - if ( pAbc->pGia == NULL ) - { - Abc_Print( 1, "Acb_CommandGet(): There is no current AIG.\n" ); - return 0; - } - pNew = Acb_ManInsertGia( p, pAbc->pGia ); - } - Acb_AbcUpdateMan( pAbc, pNew ); - return 0; -usage: - Abc_Print( -2, "usage: @get [-mvh]\n" ); - Abc_Print( -2, "\t extracts AIG or mapped network into the hierarchical design\n" ); - Abc_Print( -2, "\t-m : toggle using mapped network from main-space [default = %s]\n", fMapped? "yes": "no" ); - Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); - Abc_Print( -2, "\t-h : print the command usage\n"); - return 1; -} - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -int Acb_CommandClp( Abc_Frame_t * pAbc, int argc, char ** argv ) -{ - Acb_Man_t * pNew = NULL, * p = Acb_AbcGetMan(pAbc); - int c, fVerbose = 0; - Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) - { - switch ( c ) - { - case 'v': - fVerbose ^= 1; - break; - case 'h': - goto usage; - default: - goto usage; - } - } - if ( p == NULL ) - { - Abc_Print( 1, "Acb_CommandGet(): There is no current design.\n" ); - return 0; - } - pNew = Acb_ManCollapse( p ); - Acb_AbcUpdateMan( pAbc, pNew ); - return 0; -usage: - Abc_Print( -2, "usage: @clp [-vh]\n" ); - Abc_Print( -2, "\t collapses the current hierarchical design\n" ); - Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); - Abc_Print( -2, "\t-h : print the command usage\n"); - return 1; -} - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -int Acb_CommandBlast( Abc_Frame_t * pAbc, int argc, char ** argv ) -{ - Gia_Man_t * pNew = NULL; - Acb_Man_t * p = Acb_AbcGetMan(pAbc); - int c, fSeq = 0, fVerbose = 0; - Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "svh" ) ) != EOF ) - { - switch ( c ) - { - case 's': - fSeq ^= 1; - break; - case 'v': - fVerbose ^= 1; - break; - case 'h': - goto usage; - default: - goto usage; - } - } - if ( p == NULL ) - { - Abc_Print( 1, "Acb_CommandBlast(): There is no current design.\n" ); - return 0; - } - pNew = Acb_ManBlast( p, 0, fSeq, fVerbose ); - if ( pNew == NULL ) - { - Abc_Print( 1, "Acb_CommandBlast(): Bit-blasting has failed.\n" ); - return 0; - } - Abc_FrameUpdateGia( pAbc, pNew ); - return 0; -usage: - Abc_Print( -2, "usage: @blast [-svh]\n" ); - Abc_Print( -2, "\t performs bit-blasting of the word-level design\n" ); - Abc_Print( -2, "\t-s : toggle blasting sequential elements [default = %s]\n", fSeq? "yes": "no" ); - Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); - Abc_Print( -2, "\t-h : print the command usage\n"); - return 1; -} - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -int Acb_CommandCec( Abc_Frame_t * pAbc, int argc, char ** argv ) -{ - Acb_Man_t * p = Acb_AbcGetMan(pAbc), * pTemp; - Gia_Man_t * pFirst, * pSecond, * pMiter; - Cec_ParCec_t ParsCec, * pPars = &ParsCec; - char * pFileName, * pStr, ** pArgvNew; - int c, nArgcNew, fDumpMiter = 0; - FILE * pFile; - Cec_ManCecSetDefaultParams( pPars ); - Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) - { - switch ( c ) - { - case 'v': - pPars->fVerbose ^= 1; - break; - case 'h': - goto usage; - default: - goto usage; - } - } - if ( p == NULL ) - { - Abc_Print( 1, "Acb_CommandCec(): There is no current design.\n" ); - return 0; - } - - pArgvNew = argv + globalUtilOptind; - nArgcNew = argc - globalUtilOptind; - if ( nArgcNew != 1 ) - { - if ( p->pSpec == NULL ) - { - Abc_Print( -1, "File name is not given on the command line.\n" ); - return 1; - } - pFileName = p->pSpec; - } - else - pFileName = pArgvNew[0]; - // fix the wrong symbol - for ( pStr = pFileName; *pStr; pStr++ ) - if ( *pStr == '>' ) - *pStr = '\\'; - if ( (pFile = fopen( pFileName, "r" )) == NULL ) - { - Abc_Print( -1, "Cannot open input file \"%s\". ", pFileName ); - if ( (pFileName = Extra_FileGetSimilarName( pFileName, ".v", ".blif", NULL, NULL, NULL )) ) - Abc_Print( 1, "Did you mean \"%s\"?", pFileName ); - Abc_Print( 1, "\n" ); - return 1; - } - fclose( pFile ); - - // extract AIG from the current design - pFirst = Acb_ManBlast( p, 0, 0, 0 ); - if ( pFirst == NULL ) - { - Abc_Print( -1, "Extracting AIG from the current design has failed.\n" ); - return 0; - } - // extract AIG from the second design - - if ( !strcmp( Extra_FileNameExtension(pFileName), "blif" ) ) - pTemp = Acb_ManReadBlif( pFileName ); - else if ( !strcmp( Extra_FileNameExtension(pFileName), "v" ) ) - pTemp = Acb_ManReadVerilog( pFileName ); - else if ( !strcmp( Extra_FileNameExtension(pFileName), "acb" ) ) - pTemp = Acb_ManReadCba( pFileName ); - else assert( 0 ); - pSecond = Acb_ManBlast( pTemp, 0, 0, 0 ); - Acb_ManFree( pTemp ); - if ( pSecond == NULL ) - { - Gia_ManStop( pFirst ); - Abc_Print( -1, "Extracting AIG from the original design has failed.\n" ); - return 0; - } - // compute the miter - pMiter = Gia_ManMiter( pFirst, pSecond, 0, 1, 0, 0, pPars->fVerbose ); - if ( pMiter ) - { - if ( fDumpMiter ) - { - Abc_Print( 0, "The verification miter is written into file \"%s\".\n", "cec_miter.aig" ); - Gia_AigerWrite( pMiter, "cec_miter.aig", 0, 0, 0 ); - } - pAbc->Status = Cec_ManVerify( pMiter, pPars ); - //Abc_FrameReplaceCex( pAbc, &pAbc->pGia->pCexComb ); - Gia_ManStop( pMiter ); - } - Gia_ManStop( pFirst ); - Gia_ManStop( pSecond ); - return 0; -usage: - Abc_Print( -2, "usage: @cec [-vh]\n" ); - Abc_Print( -2, "\t combinational equivalence checking\n" ); - Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", pPars->fVerbose? "yes": "no" ); - Abc_Print( -2, "\t-h : print the command usage\n"); - return 1; -} - -/**Function******************************************************************** - - Synopsis [] - - Description [] - - SideEffects [] - - SeeAlso [] - -******************************************************************************/ -int Acb_CommandTest( Abc_Frame_t * pAbc, int argc, char ** argv ) -{ - Acb_Man_t * p = Acb_AbcGetMan(pAbc); - int c, fVerbose = 0; - Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) - { - switch ( c ) - { - case 'v': - fVerbose ^= 1; - break; - case 'h': - goto usage; - default: - goto usage; - } - } - if ( p == NULL ) - { - Abc_Print( 1, "Acb_CommandTest(): There is no current design.\n" ); - return 0; - } - return 0; -usage: - Abc_Print( -2, "usage: @test [-vh]\n" ); - Abc_Print( -2, "\t experiments with word-level networks\n" ); - Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); - Abc_Print( -2, "\t-h : print the command usage\n"); - return 1; -} - -#endif - -//////////////////////////////////////////////////////////////////////// -/// END OF FILE /// -//////////////////////////////////////////////////////////////////////// - - -ABC_NAMESPACE_IMPL_END - diff --git a/src/base/acb/acbTest.c b/src/base/acb/acbTest.c index c22d4b55b..aac003264 100644 --- a/src/base/acb/acbTest.c +++ b/src/base/acb/acbTest.c @@ -9,7 +9,7 @@ Synopsis [] Author [Alan Mishchenko] - + Affiliation [UC Berkeley] Date [Ver. 1.0. Started - July 21, 2015.] @@ -19,10 +19,17 @@ ***********************************************************************/ #include "acb.h" +#include "acbXec.h" #include "aig/saig/saig.h" #include "aig/gia/giaAig.h" #include "base/abc/abc.h" #include "proof/fraig/fraig.h" +#include "proof/cec/cec.h" +#include "proof/dch/dch.h" +#include "proof/acec/acec.h" +#include "opt/dar/dar.h" +#include "sat/cadical/cadicalSolver.h" +#include "sat/cnf/cnf.h" #include "misc/util/utilTruth.h" ABC_NAMESPACE_IMPL_START @@ -31,7 +38,182 @@ ABC_NAMESPACE_IMPL_START /// DECLARATIONS /// //////////////////////////////////////////////////////////////////////// -static int fForceZero = 0; +#define ACB_FORCE_ZERO 0 +#define ACB_XEC_RECURSION_LIMIT 8192 + +typedef enum Acb_CexCheckStatus_t_ +{ + ACB_CEX_UNSUPPORTED = -1, + ACB_CEX_INVALID = 0, + ACB_CEX_VALID = 1 +} Acb_CexCheckStatus_t; + +typedef struct Acb_XecCtx_t_ +{ + struct Acb_XecParams_t_ + { + int nScratchVecInit; /* Initial capacity for per-run hard-output vectors. */ + int nOverlapMinPermille; /* Minimum cone overlap for grouping outputs in one SAT cluster. */ + int nOverlapSizePermille; /* Minimum smaller/larger cone-size ratio for output clustering. */ + int nBranchMinOutputSec; /* Keep this much branch budget before starting another PO solve. */ + int nBranchLocalOptAndMin; /* Try local optimization/abstraction only for large branch cones. */ + int nBranchLocalOptSec; /* Time cap for local optimized branch-cone SAT. */ + int nBranchFrontierAbsSec; /* Time cap for frontier abstraction probe. */ + int nBranchHardConflictMin; /* Report/isolate branch outputs above this conflict delta. */ + int nBranchHardTimeMin; /* Report/isolate branch outputs above this runtime delta. */ + int nBranchSchedulePrintMax; /* Max branch output ids printed in the clustered schedule. */ + int nLocalManyPoThreshold; /* Above this PO count, local sweep uses quick SAT-hunting probes. */ + int nLocalMediumPoMin; /* Lower PO count for medium sweep behavior. */ + int nLocalMediumPoMax; /* Upper PO count for medium sweep behavior. */ + int nLocalQuickMaxUndec; /* Quick many-output sweep stops after this many undecided probes. */ + int nLocalQuickPoSec; /* Per-output limit for quick many-output probes. */ + int nLocalMediumPoSec; /* Per-output limit for medium local sweep. */ + int nLocalMediumHardPoSec; /* Per-output limit after first hard output in medium sweep. */ + int nLocalConeCompressAndMin; /* Compress local cone only when it has at least this many ANDs. */ + int nSimLargeAndMin; /* Use larger random simulation only above this miter size. */ + int nSimSmallWords; /* Random-simulation words for small miters. */ + int nSimLargeWords; /* Random-simulation words for large miters. */ + int nMainLargeAndMin; /* Enter heavy xec proof orchestration above this AND count. */ + int nMainLargePiMin; /* Enter heavy xec proof orchestration above this PI count. */ + int nMainLargePoMin; /* Enter heavy xec proof orchestration above this PO count. */ + int nSharedDcPiMin; /* Prefer shared whole-miter SAT for few-control DC above this PI count. */ + int nSharedDcPoMin; /* Prefer shared whole-miter SAT for few-control DC above this PO count. */ + int nSharedDcPoMax; /* Upper PO bound for the few-control high-PI DC shape. */ + int nSharedDcAndMin; /* Lower AND bound for the few-control high-PI DC shape. */ + int nSharedDcAndMax; /* Upper AND bound for the few-control high-PI DC shape. */ + int nSharedDcObjMin; /* Lower DC-object count for the few-control high-PI DC shape. */ + int nSharedDcObjMax; /* Upper DC-object count for the few-control high-PI DC shape. */ + int nSharedDcWholeSec; /* Whole-miter SAT time cap for the few-control high-PI DC shape. */ + } Pars; + int LastHardPo; + Vec_Int_t * vLastHardPos; + Vec_Int_t * vLastProvenPos; + Vec_Int_t * vLastBranchHardPos; + int LastHardDirectTried; +} Acb_XecCtx_t; + +static inline void Acb_XecParamsSetDefault( Acb_XecCtx_t * p ) +{ + p->Pars.nScratchVecInit = 8; + p->Pars.nOverlapMinPermille = 700; + p->Pars.nOverlapSizePermille = 450; + p->Pars.nBranchMinOutputSec = 60; + p->Pars.nBranchLocalOptAndMin = 10000; + p->Pars.nBranchLocalOptSec = 300; + p->Pars.nBranchFrontierAbsSec = 60; + p->Pars.nBranchHardConflictMin = 1000000; + p->Pars.nBranchHardTimeMin = 60; + p->Pars.nBranchSchedulePrintMax = 12; + p->Pars.nLocalManyPoThreshold = 64; + p->Pars.nLocalMediumPoMin = 8; + p->Pars.nLocalMediumPoMax = 64; + p->Pars.nLocalQuickMaxUndec = 12; + p->Pars.nLocalQuickPoSec = 5; + p->Pars.nLocalMediumPoSec = 60; + p->Pars.nLocalMediumHardPoSec = 15; + p->Pars.nLocalConeCompressAndMin = 1000; + p->Pars.nSimLargeAndMin = 5000; + p->Pars.nSimSmallWords = 1; + p->Pars.nSimLargeWords = 256; + p->Pars.nMainLargeAndMin = 30000; + p->Pars.nMainLargePiMin = 256; + p->Pars.nMainLargePoMin = 64; + p->Pars.nSharedDcPiMin = 4096; + p->Pars.nSharedDcPoMin = 80; + p->Pars.nSharedDcPoMax = 128; + p->Pars.nSharedDcAndMin = 100000; + p->Pars.nSharedDcAndMax = 200000; + p->Pars.nSharedDcObjMin = 160; + p->Pars.nSharedDcObjMax = 256; + p->Pars.nSharedDcWholeSec = 1800; +} + +static inline int Acb_XecIsSharedDcWholeMiterShape( Gia_Man_t * pGia, Vec_Int_t * vMuxSelectorsG, Vec_Int_t * vIntDcObjsG, Vec_Int_t * vIntDcCtrlsG, Acb_XecCtx_t * pCtx ) +{ + if ( pGia == NULL || pCtx == NULL ) + return 0; + if ( vMuxSelectorsG && Vec_IntSize(vMuxSelectorsG) > 0 ) + return 0; + if ( vIntDcObjsG == NULL || vIntDcCtrlsG == NULL ) + return 0; + if ( Vec_IntSize(vIntDcCtrlsG) != 2 ) + return 0; + if ( Vec_IntSize(vIntDcObjsG) < pCtx->Pars.nSharedDcObjMin || Vec_IntSize(vIntDcObjsG) > pCtx->Pars.nSharedDcObjMax ) + return 0; + if ( Gia_ManCiNum(pGia) < pCtx->Pars.nSharedDcPiMin ) + return 0; + if ( Gia_ManCoNum(pGia) < pCtx->Pars.nSharedDcPoMin || Gia_ManCoNum(pGia) > pCtx->Pars.nSharedDcPoMax ) + return 0; + if ( Gia_ManAndNum(pGia) < pCtx->Pars.nSharedDcAndMin || Gia_ManAndNum(pGia) > pCtx->Pars.nSharedDcAndMax ) + return 0; + return 1; +} + +static inline void Acb_XecCtxInit( Acb_XecCtx_t * p ) +{ + memset( p, 0, sizeof(*p) ); + Acb_XecParamsSetDefault( p ); + p->LastHardPo = -1; +} + +static inline void Acb_XecCtxFree( Acb_XecCtx_t * p ) +{ + Vec_IntFreeP( &p->vLastHardPos ); + Vec_IntFreeP( &p->vLastProvenPos ); + Vec_IntFreeP( &p->vLastBranchHardPos ); + p->LastHardPo = -1; + p->LastHardDirectTried = 0; +} + +static inline void Acb_XecCtxResetLocalSweep( Acb_XecCtx_t * p ) +{ + p->LastHardPo = -1; + p->LastHardDirectTried = 0; + Vec_IntFreeP( &p->vLastHardPos ); + p->vLastHardPos = Vec_IntAlloc( p->Pars.nScratchVecInit ); + Vec_IntFreeP( &p->vLastProvenPos ); + p->vLastProvenPos = Vec_IntAlloc( p->Pars.nScratchVecInit ); +} + +static inline void Acb_XecCtxResetBranchSweep( Acb_XecCtx_t * p, int nOuts ) +{ + Vec_IntFreeP( &p->vLastBranchHardPos ); + p->vLastBranchHardPos = Vec_IntAlloc( nOuts ); +} + +Gia_Man_t * Acb_GiaDupOnePoTrimmed( Gia_Man_t * p, int iPo, Vec_Int_t * vSuppMap ) +{ + Gia_Obj_t * pPo; + Gia_Man_t * pNew; + int iLit, iPoObj; + if ( vSuppMap ) + Vec_IntClear( vSuppMap ); + if ( p == NULL || iPo < 0 || iPo >= Gia_ManCoNum(p) ) + return NULL; + pPo = Gia_ManCo( p, iPo ); + iLit = Gia_ObjFaninLit0p( p, pPo ); + if ( Gia_ManIsConst0Lit(iLit) || Gia_ManIsConst1Lit(iLit) ) + { + Gia_Man_t * pNew = Gia_ManStart( 1 ); + pNew->pName = Abc_UtilStrsav( p->pName ); + Gia_ManAppendCo( pNew, Gia_ManIsConst1Lit(iLit) ); + return pNew; + } + iPoObj = Gia_ObjFaninId0p( p, pPo ); + if ( vSuppMap ) + { + Gia_ManCollectCis( p, &iPoObj, 1, vSuppMap ); + Vec_IntSort( vSuppMap, 0 ); + } + pNew = Gia_ManDupCones( p, &iPo, 1, 1 ); + if ( pNew == NULL && vSuppMap ) + Vec_IntClear( vSuppMap ); + return pNew; +} + +int * Acb_NtkSolveCadicalLocalConeSweepSkipCtx( Gia_Man_t * p, int fVerbose, int * pStatus, int nSatTimeLimit, int nPoTimeLimit, Vec_Int_t * vSkipUnsat, Acb_XecCtx_t * pCtx ); +int Acb_GiaRequiredLiteralUnitProof( Gia_Man_t * p, int iPo, int fVerbose, int nSatTimeLimit ); +int * Acb_NtkSolveMuxDcControlTargetList( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, Vec_Int_t * vHardPos, Vec_Int_t * vCutObjsG, Vec_Int_t * vMuxSelectorsG, Vec_Int_t * vMuxPoSelIdsG, int fSelBranch, Vec_Int_t * vDcObjsG, Vec_Int_t * vDcCtrlsG, Vec_Int_t * vDcCtrlIdsG, int fVerbose, int * pStatus, int nBranchLimit ); //////////////////////////////////////////////////////////////////////// /// FUNCTION DEFINITIONS /// @@ -42,16 +224,21 @@ static int fForceZero = 0; Synopsis [] Description [] - + SideEffects [] SeeAlso [] ***********************************************************************/ -void Gia_ManSimTry( Gia_Man_t * pF, Gia_Man_t * pG ) +int * Acb_NtkFindSimCex( Gia_Man_t * pF, Gia_Man_t * pG, int nWords, int fVerbose ) { - int i, j, n, nWords = 500; Vec_Wrd_t * vSimsF, * vSimsG; + Gia_Obj_t * pObjFb, * pObjFx, * pObjGb, * pObjGx; + word * pSimFb, * pSimFx, * pSimGb, * pSimGx, * pSimPi; + int i, k, b, nBits = 64 * nWords; + int * pModel = NULL; + assert( Gia_ManCiNum(pF) == Gia_ManCiNum(pG) ); + assert( Gia_ManCoNum(pF) == Gia_ManCoNum(pG) ); Abc_Random(1); Vec_WrdFreeP( &pF->vSimsPi ); Vec_WrdFreeP( &pG->vSimsPi ); @@ -59,75 +246,182 @@ void Gia_ManSimTry( Gia_Man_t * pF, Gia_Man_t * pG ) pG->vSimsPi = Vec_WrdDup( pF->vSimsPi ); vSimsF = Gia_ManSimPatSim( pF ); vSimsG = Gia_ManSimPatSim( pG ); - assert( Gia_ManObjNum(pF) * nWords == Vec_WrdSize(vSimsF) ); - for ( i = 0; i < Gia_ManCoNum(pF)/2; i++ ) + for ( i = 0; i < Gia_ManCoNum(pF)/2 && pModel == NULL; i++ ) { - Gia_Obj_t * pObjFb = Gia_ManCo( pF, 2*i+0 ); - Gia_Obj_t * pObjFx = Gia_ManCo( pF, 2*i+1 ); - Gia_Obj_t * pObjGb = Gia_ManCo( pG, 2*i+0 ); - Gia_Obj_t * pObjGx = Gia_ManCo( pG, 2*i+1 ); - word * pSimFb = Vec_WrdEntryP(vSimsF, Gia_ObjId(pF, pObjFb)*nWords); - word * pSimFx = Vec_WrdEntryP(vSimsF, Gia_ObjId(pF, pObjFx)*nWords); - word * pSimGb = Vec_WrdEntryP(vSimsG, Gia_ObjId(pG, pObjGb)*nWords); - word * pSimGx = Vec_WrdEntryP(vSimsG, Gia_ObjId(pG, pObjGx)*nWords); - - int nBitsFx = Abc_TtCountOnesVec(pSimFx, nWords); - int nBitsF1 = Abc_TtCountOnesVecMask(pSimFx, pSimFb, nWords, 1); - int nBitsF0 = nWords*64 - nBitsFx - nBitsF1; - - int nBitsGx = Abc_TtCountOnesVec(pSimGx, nWords); - int nBitsG1 = Abc_TtCountOnesVecMask(pSimGx, pSimGb, nWords, 1); - int nBitsG0 = nWords*64 - nBitsGx - nBitsG1; - - printf( "Output %4d : ", i ); - - printf( " RF : " ); - printf( "0 =%7.3f %% ", 100.0*nBitsF0/64/nWords ); - printf( "1 =%7.3f %% ", 100.0*nBitsF1/64/nWords ); - printf( "X =%7.3f %% ", 100.0*nBitsFx/64/nWords ); - - printf( " GF : " ); - printf( "0 =%7.3f %% ", 100.0*nBitsG0/64/nWords ); - printf( "1 =%7.3f %% ", 100.0*nBitsG1/64/nWords ); - printf( "X =%7.3f %% ", 100.0*nBitsGx/64/nWords ); - - printf( "\n" ); - if ( i == 20 ) - break; - } - - printf( "\n" ); - for ( j = 0; j < 20; j++ ) - { - for ( n = 0; n < 2; n++ ) - { - for ( i = 0; i < Gia_ManCoNum(pF)/2; i++ ) + pObjFb = Gia_ManCo( pF, 2*i+0 ); + pObjFx = Gia_ManCo( pF, 2*i+1 ); + pObjGb = Gia_ManCo( pG, 2*i+0 ); + pObjGx = Gia_ManCo( pG, 2*i+1 ); + pSimFb = Vec_WrdEntryP(vSimsF, Gia_ObjId(pF, pObjFb)*nWords); + pSimFx = Vec_WrdEntryP(vSimsF, Gia_ObjId(pF, pObjFx)*nWords); + pSimGb = Vec_WrdEntryP(vSimsG, Gia_ObjId(pG, pObjGb)*nWords); + pSimGx = Vec_WrdEntryP(vSimsG, Gia_ObjId(pG, pObjGx)*nWords); + for ( b = 0; b < nBits; b++ ) + if ( !Abc_TtGetBit(pSimGx, b) && (Abc_TtGetBit(pSimFx, b) || (Abc_TtGetBit(pSimFb, b) ^ Abc_TtGetBit(pSimGb, b))) ) { - Gia_Obj_t * pObjFb = Gia_ManCo( pF, 2*i+0 ); - Gia_Obj_t * pObjFx = Gia_ManCo( pF, 2*i+1 ); - Gia_Obj_t * pObjGb = Gia_ManCo( pG, 2*i+0 ); - Gia_Obj_t * pObjGx = Gia_ManCo( pG, 2*i+1 ); - word * pSimFb = Vec_WrdEntryP(vSimsF, Gia_ObjId(pF, pObjFb)*nWords); - word * pSimFx = Vec_WrdEntryP(vSimsF, Gia_ObjId(pF, pObjFx)*nWords); - word * pSimGb = Vec_WrdEntryP(vSimsG, Gia_ObjId(pG, pObjGb)*nWords); - word * pSimGx = Vec_WrdEntryP(vSimsG, Gia_ObjId(pG, pObjGx)*nWords); - word * pSimb = n ? pSimGb : pSimFb; - word * pSimx = n ? pSimGx : pSimFx; - if ( Abc_TtGetBit(pSimx, j) ) - printf( "x" ); - else if ( Abc_TtGetBit(pSimb, j) ) - printf( "1" ); - else - printf( "0" ); + pModel = ABC_ALLOC( int, Gia_ManCiNum(pF) ); + for ( k = 0; k < Gia_ManCiNum(pF); k++ ) + { + pSimPi = Vec_WrdEntryP( pF->vSimsPi, k*nWords ); + pModel[k] = Abc_TtGetBit( pSimPi, b ); + } + if ( fVerbose ) + printf( "Random simulation found mismatch at output %d, pattern %d.\n", i, b ); + break; } - printf( "\n" ); - } - printf( "\n" ); } - + if ( fVerbose && pModel == NULL ) + printf( "Random simulation tried %d patterns and found no mismatch.\n", nBits ); Vec_WrdFree( vSimsF ); Vec_WrdFree( vSimsG ); - printf( "\n" ); + Vec_WrdFreeP( &pF->vSimsPi ); + Vec_WrdFreeP( &pG->vSimsPi ); + return pModel; +} +int * Acb_GiaFindOnePoSimCex( Gia_Man_t * p, int nWords, int fVerbose, char * pLabel ) +{ + Vec_Wrd_t * vSims = NULL; + Gia_Obj_t * pObjPo; + word * pSimPo, * pSimPi; + int k, b, nBits = 64 * nWords; + int * pModel = NULL; + if ( p == NULL || Gia_ManCoNum(p) != 1 || Gia_ManCiNum(p) <= 0 || nWords <= 0 ) + return NULL; + Abc_Random( 1 ); + Vec_WrdFreeP( &p->vSimsPi ); + p->vSimsPi = Vec_WrdStartRandom( Gia_ManCiNum(p) * nWords ); + vSims = Gia_ManSimPatSim( p ); + pObjPo = Gia_ManCo( p, 0 ); + pSimPo = Vec_WrdEntryP( vSims, Gia_ObjId(p, pObjPo) * nWords ); + for ( b = 0; b < nBits; b++ ) + { + if ( !Abc_TtGetBit(pSimPo, b) ) + continue; + pModel = ABC_ALLOC( int, Gia_ManCiNum(p) ); + for ( k = 0; k < Gia_ManCiNum(p); k++ ) + { + pSimPi = Vec_WrdEntryP( p->vSimsPi, k * nWords ); + pModel[k] = Abc_TtGetBit( pSimPi, b ); + } + if ( fVerbose ) + printf( "%s simulation found bad pattern at pattern %d/%d.\n", + pLabel ? pLabel : "Hard-output", b, nBits ); + break; + } + if ( fVerbose && pModel == NULL ) + printf( "%s simulation tried %d patterns and found no bad pattern.\n", + pLabel ? pLabel : "Hard-output", nBits ); + Vec_WrdFreeP( &vSims ); + Vec_WrdFreeP( &p->vSimsPi ); + return pModel; +} + +int Acb_NtkCheckModelCex( Gia_Man_t * pF, Gia_Man_t * pG, int * pModel, int fVerbose ) +{ + Gia_Obj_t * pObj; + int i, Fb, Fx, Gb, Gx; + if ( pModel == NULL ) + return 0; + Gia_ManConst0(pF)->Value = 0; + Gia_ManConst0(pG)->Value = 0; + Gia_ManForEachCi( pF, pObj, i ) + pObj->Value = pModel[i] ? 1 : 0; + Gia_ManForEachCi( pG, pObj, i ) + pObj->Value = pModel[i] ? 1 : 0; + Gia_ManForEachAnd( pF, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj) & Gia_ObjFanin1Copy(pObj); + Gia_ManForEachAnd( pG, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj) & Gia_ObjFanin1Copy(pObj); + for ( i = 0; i < Gia_ManCoNum(pF)/2; i++ ) + { + Fb = Gia_ObjFanin0Copy( Gia_ManCo(pF, 2*i+0) ); + Fx = Gia_ObjFanin0Copy( Gia_ManCo(pF, 2*i+1) ); + Gb = Gia_ObjFanin0Copy( Gia_ManCo(pG, 2*i+0) ); + Gx = Gia_ObjFanin0Copy( Gia_ManCo(pG, 2*i+1) ); + if ( !Gx && (Fx || (Fb ^ Gb)) ) + { + if ( fVerbose ) + printf( "Validated SAT counterexample at output %d.\n", i ); + return 1; + } + } + if ( fVerbose ) + printf( "SAT model validation failed: no compatible mismatch is observed.\n" ); + return 0; +} +int Acb_NtkEvalModelBool( Acb_Ntk_t * p, int * pModel, Vec_Int_t * vVals ) +{ + int i, k, iObj, Type, * pFans; + Vec_IntFill( vVals, Acb_NtkObjNumMax(p), 0 ); + Acb_NtkForEachCi( p, iObj, i ) + Vec_IntWriteEntry( vVals, iObj, pModel[i] ? 1 : 0 ); + Acb_NtkForEachObj( p, iObj ) + { + int z = 0; + if ( Acb_ObjIsCio(p, iObj) ) + continue; + Type = Acb_ObjType( p, iObj ); + pFans = Acb_ObjFanins( p, iObj ); + if ( Type == ABC_OPER_CONST_F ) + z = 0; + else if ( Type == ABC_OPER_CONST_T ) + z = 1; + else if ( Type == ABC_OPER_BIT_BUF ) + z = Vec_IntEntry(vVals, pFans[1]); + else if ( Type == ABC_OPER_BIT_INV ) + z = !Vec_IntEntry(vVals, pFans[1]); + else if ( Type == ABC_OPER_BIT_AND || Type == ABC_OPER_BIT_NAND ) + { + z = 1; + for ( k = 0; k < pFans[0]; k++ ) + z &= Vec_IntEntry(vVals, pFans[k+1]); + if ( Type == ABC_OPER_BIT_NAND ) + z = !z; + } + else if ( Type == ABC_OPER_BIT_OR || Type == ABC_OPER_BIT_NOR ) + { + z = 0; + for ( k = 0; k < pFans[0]; k++ ) + z |= Vec_IntEntry(vVals, pFans[k+1]); + if ( Type == ABC_OPER_BIT_NOR ) + z = !z; + } + else if ( Type == ABC_OPER_BIT_XOR || Type == ABC_OPER_BIT_NXOR ) + { + z = 0; + for ( k = 0; k < pFans[0]; k++ ) + z ^= Vec_IntEntry(vVals, pFans[k+1]); + if ( Type == ABC_OPER_BIT_NXOR ) + z = !z; + } + else + return 0; + Vec_IntWriteEntry( vVals, iObj, z ); + } + return 1; +} +int Acb_NtkCheckModelCexAcbBool( Acb_Ntk_t * pF, Acb_Ntk_t * pG, int * pModel, int fVerbose ) +{ + Vec_Int_t * vF = Vec_IntAlloc( Acb_NtkObjNumMax(pF) ); + Vec_Int_t * vG = Vec_IntAlloc( Acb_NtkObjNumMax(pG) ); + int i, iCoF, iCoG, Ret = ACB_CEX_INVALID; + if ( pModel && Acb_NtkEvalModelBool(pF, pModel, vF) && Acb_NtkEvalModelBool(pG, pModel, vG) ) + { + Acb_NtkForEachCo( pF, iCoF, i ) + { + iCoG = Acb_NtkCo( pG, i ); + if ( Vec_IntEntry(vF, Acb_ObjFanin(pF, iCoF, 0)) != Vec_IntEntry(vG, Acb_ObjFanin(pG, iCoG, 0)) ) + { + if ( fVerbose ) + printf( "Original ACB Boolean validation found SAT counterexample at output %d.\n", i ); + Ret = ACB_CEX_VALID; + break; + } + } + } + Vec_IntFree( vF ); + Vec_IntFree( vG ); + return Ret; } /**Function************************************************************* @@ -135,7 +429,7 @@ void Gia_ManSimTry( Gia_Man_t * pF, Gia_Man_t * pG ) Synopsis [] Description [] - + SideEffects [] SeeAlso [] @@ -145,41 +439,18 @@ void Gia_ManDualNot( Gia_Man_t * p, int LitA[2], int LitZ[2] ) { LitZ[0] = Abc_LitNot(LitA[0]); LitZ[1] = LitA[1]; - - if ( fForceZero ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); + + if ( ACB_FORCE_ZERO ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); } // computes Z = XOR(A, B) where A, B, Z belong to {0,1,x} encoded as 0=00, 1=01, x=1- void Gia_ManDualXor2( Gia_Man_t * p, int LitA[2], int LitB[2], int LitZ[2] ) { LitZ[0] = Gia_ManHashXor( p, LitA[0], LitB[0] ); LitZ[1] = Gia_ManHashOr( p, LitA[1], LitB[1] ); - - if ( fForceZero ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); -} -void Gia_ManDualXorN( Gia_Man_t * p, int * pLits, int n, int LitZ[2] ) -{ - int i; - LitZ[0] = 0; - LitZ[1] = 0; - for ( i = 0; i < n; i++ ) - { - LitZ[0] = Gia_ManHashXor( p, LitZ[0], pLits[2*i] ); - LitZ[1] = Gia_ManHashOr ( p, LitZ[1], pLits[2*i+1] ); - } + + if ( ACB_FORCE_ZERO ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); } // computes Z = AND(A, B) where A, B, Z belong to {0,1,x} encoded as 0=00, 1=01, z=1- -void Gia_ManDualAnd2( Gia_Man_t * p, int LitA[2], int LitB[2], int LitZ[2] ) -{ - int ZeroA = Gia_ManHashAnd( p, Abc_LitNot(LitA[0]), Abc_LitNot(LitA[1]) ); - int ZeroB = Gia_ManHashAnd( p, Abc_LitNot(LitB[0]), Abc_LitNot(LitB[1]) ); - int ZeroZ = Gia_ManHashOr( p, ZeroA, ZeroB ); - LitZ[0] = Gia_ManHashAnd( p, LitA[0], LitB[0] ); - LitZ[1] = Gia_ManHashAnd( p, Gia_ManHashOr( p, LitA[1], LitB[1] ), Abc_LitNot(ZeroZ) ); - - //LitZ[0] = Gia_ManHashAnd( p, Gia_ManHashAnd(p, LitA[0], Abc_LitNot(LitA[1])), Gia_ManHashAnd(p, LitB[0], Abc_LitNot(LitB[1])) ); - //LitZ[1] = Gia_ManHashAnd( p, Gia_ManHashOr(p, LitA[0], LitA[1]), Gia_ManHashOr(p, LitB[0], LitB[1]) ); - //LitZ[1] = Gia_ManHashAnd( p, LitZ[1], Abc_LitNot(LitZ[0]) ); -} void Gia_ManDualAndN( Gia_Man_t * p, int * pLits, int n, int LitZ[2] ) { int i, LitZero = 0, LitOne = 0; @@ -192,8 +463,8 @@ void Gia_ManDualAndN( Gia_Man_t * p, int * pLits, int n, int LitZ[2] ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], pLits[2*i] ); } LitZ[1] = Gia_ManHashAnd( p, LitOne, Abc_LitNot(LitZero) ); - - if ( fForceZero ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); + + if ( ACB_FORCE_ZERO ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); } /* module _DC(O, C, D); @@ -207,8 +478,8 @@ void Gia_ManDualDc( Gia_Man_t * p, int LitC[2], int LitD[2], int LitZ[2] ) LitZ[0] = LitC[0]; // LitZ[0] = Gia_ManHashMux( p, LitD[0], 0, LitC[0] ); LitZ[1] = Gia_ManHashOr(p, Gia_ManHashOr(p,LitD[0],LitD[1]), LitC[1] ); - - if ( fForceZero ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); + + if ( ACB_FORCE_ZERO ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); } void Gia_ManDualMux( Gia_Man_t * p, int LitC[2], int LitT[2], int LitE[2], int LitZ[2] ) { @@ -233,7 +504,7 @@ void Gia_ManDualMux( Gia_Man_t * p, int LitC[2], int LitT[2], int LitE[2], int L LitZ[0] = Gia_ManHashMux( p, LitC[0], LitT[0], LitE[0] ); LitZ[1] = Gia_ManHashMux( p, LitC[1], XVal1, XVal0 ); - if ( fForceZero ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); + if ( ACB_FORCE_ZERO ) LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); } int Gia_ManDualCompare( Gia_Man_t * p, int LitF[2], int LitS[2] ) { @@ -242,84 +513,127 @@ int Gia_ManDualCompare( Gia_Man_t * p, int LitF[2], int LitS[2] ) iMiter = Gia_ManHashAnd( p, Abc_LitNot(LitS[1]), iMiter ); return iMiter; } +static inline void Gia_ManDualForceZero( Gia_Man_t * p, int LitZ[2], int fForceZero ) +{ + if ( fForceZero ) + LitZ[0] = Gia_ManHashAnd( p, LitZ[0], Abc_LitNot(LitZ[1]) ); +} /**Function************************************************************* Synopsis [] Description [] - + SideEffects [] SeeAlso [] ***********************************************************************/ -void Acb_ObjToGiaDual( Gia_Man_t * pNew, Acb_Ntk_t * p, int iObj, Vec_Int_t * vTemp, Vec_Int_t * vCopies, int pRes[2] ) +int Acb_ObjToGiaDual( Gia_Man_t * pNew, Acb_Ntk_t * p, int iObj, Vec_Int_t * vTemp, Vec_Int_t * vCopies, int pRes[2], Vec_Int_t * vDcBranchObjs, Vec_Int_t * vDcBranchVals, int fDcBranchOne, int fForceZero ) { //char * pName = Abc_NamStr( p->pDesign->pStrs, Acb_ObjName(p, iObj) ); int * pFanin, iFanin, k, Type; - assert( !Acb_ObjIsCio(p, iObj) ); + if ( Acb_ObjIsCio(p, iObj) ) + { + Acb_NtkPrintUnsupportedObj( p, iObj, "ACB dual translation", -1, 0 ); + return 0; + } Vec_IntClear( vTemp ); Acb_ObjForEachFaninFast( p, iObj, pFanin, iFanin, k ) { int * pLits = Vec_IntEntryP( vCopies, 2*iFanin ); - assert( pLits[0] >= 0 && pLits[1] >= 0 ); + if ( pLits[0] < 0 || pLits[1] < 0 ) + { + Acb_NtkPrintUnsupportedObj( p, iObj, "ACB dual translation has unmapped fanin", -1, k ); + return 0; + } Vec_IntPushTwo( vTemp, pLits[0], pLits[1] ); } Type = Acb_ObjType( p, iObj ); - if ( Type == ABC_OPER_CONST_F ) + if ( Type == ABC_OPER_CONST_F ) { pRes[0] = 0; pRes[1] = 0; - return; + return 1; } - if ( Type == ABC_OPER_CONST_T ) + if ( Type == ABC_OPER_CONST_T ) { pRes[0] = 1; pRes[1] = 0; - return; + return 1; } - if ( Type == ABC_OPER_CONST_X ) + if ( Type == ABC_OPER_CONST_X ) { pRes[0] = 0; pRes[1] = 1; - return; + return 1; } - if ( Type == ABC_OPER_BIT_BUF ) + if ( Type == ABC_OPER_BIT_BUF ) { pRes[0] = Vec_IntEntry(vTemp, 0); pRes[1] = Vec_IntEntry(vTemp, 1); - return; + Gia_ManDualForceZero( pNew, pRes, fForceZero ); + return 1; } - if ( Type == ABC_OPER_BIT_INV ) + if ( Type == ABC_OPER_BIT_INV ) { Gia_ManDualNot( pNew, Vec_IntArray(vTemp), pRes ); - return; + Gia_ManDualForceZero( pNew, pRes, fForceZero ); + return 1; } - if ( Type == ABC_OPER_TRI ) + if ( Type == ABC_OPER_TRI ) { // in the file inputs are ordered as follows: _DC \n6_5[9] ( .O(\108 ), .C(\96 ), .D(\107 )); // in this code, we expect them as follows: void Gia_ManDualDc( Gia_Man_t * p, int LitC[2], int LitD[2], int LitZ[2] ) - assert( Vec_IntSize(vTemp) == 4 ); + if ( Vec_IntSize(vTemp) != 4 ) + { + Acb_NtkPrintUnsupportedObj( p, iObj, "ACB dual TRI translation", 2, Vec_IntSize(vTemp)/2 ); + return 0; + } + if ( vDcBranchObjs && Vec_IntFind(vDcBranchObjs, iObj) >= 0 ) + { + int iPos = Vec_IntFind(vDcBranchObjs, iObj); + int fOne = vDcBranchVals ? Vec_IntEntry(vDcBranchVals, iPos) : fDcBranchOne; + if ( fOne ) + { + pRes[0] = 0; + pRes[1] = 1; + } + else + { + pRes[0] = Vec_IntEntry(vTemp, 0); + pRes[1] = Vec_IntEntry(vTemp, 1); + } + Gia_ManDualForceZero( pNew, pRes, fForceZero ); + return 1; + } Gia_ManDualDc( pNew, Vec_IntArray(vTemp), Vec_IntArray(vTemp) + 2, pRes ); - return; + Gia_ManDualForceZero( pNew, pRes, fForceZero ); + return 1; } - if ( Type == ABC_OPER_BIT_MUX ) + if ( Type == ABC_OPER_BIT_MUX ) { // in the file inputs are ordered as follows: _HMUX \U$1 ( .O(\282 ), .I0(1'b1), .I1(\277 ), .S(\281 )); // in this code, we expect them as follows: void Gia_ManDualMux( Gia_Man_t * p, int LitC[2], int LitT[2], int LitE[2], int LitZ[2] ) - assert( Vec_IntSize(vTemp) == 6 ); + if ( Vec_IntSize(vTemp) != 6 ) + { + Acb_NtkPrintUnsupportedObj( p, iObj, "ACB dual MUX translation", 3, Vec_IntSize(vTemp)/2 ); + return 0; + } ABC_SWAP( int, Vec_IntArray(vTemp)[0], Vec_IntArray(vTemp)[4] ); ABC_SWAP( int, Vec_IntArray(vTemp)[1], Vec_IntArray(vTemp)[5] ); Gia_ManDualMux( pNew, Vec_IntArray(vTemp), Vec_IntArray(vTemp) + 2, Vec_IntArray(vTemp) + 4, pRes ); - return; + Gia_ManDualForceZero( pNew, pRes, fForceZero ); + return 1; } if ( Type == ABC_OPER_BIT_AND || Type == ABC_OPER_BIT_NAND ) { Gia_ManDualAndN( pNew, Vec_IntArray(vTemp), Vec_IntSize(vTemp)/2, pRes ); if ( Type == ABC_OPER_BIT_NAND ) pRes[0] = Abc_LitNot( pRes[0] ); - return; + Gia_ManDualForceZero( pNew, pRes, fForceZero ); + return 1; } if ( Type == ABC_OPER_BIT_OR || Type == ABC_OPER_BIT_NOR ) { @@ -329,19 +643,26 @@ void Acb_ObjToGiaDual( Gia_Man_t * pNew, Acb_Ntk_t * p, int iObj, Vec_Int_t * vT Gia_ManDualAndN( pNew, pArray, Vec_IntSize(vTemp)/2, pRes ); if ( Type == ABC_OPER_BIT_OR ) pRes[0] = Abc_LitNot( pRes[0] ); - return; + Gia_ManDualForceZero( pNew, pRes, fForceZero ); + return 1; } if ( Type == ABC_OPER_BIT_XOR || Type == ABC_OPER_BIT_NXOR ) { - assert( Vec_IntSize(vTemp) == 4 ); + if ( Vec_IntSize(vTemp) != 4 ) + { + Acb_NtkPrintUnsupportedObj( p, iObj, "ACB dual XOR translation", 2, Vec_IntSize(vTemp)/2 ); + return 0; + } Gia_ManDualXor2( pNew, Vec_IntArray(vTemp), Vec_IntArray(vTemp) + 2, pRes ); if ( Type == ABC_OPER_BIT_NXOR ) pRes[0] = Abc_LitNot( pRes[0] ); - return; + Gia_ManDualForceZero( pNew, pRes, fForceZero ); + return 1; } - assert( 0 ); + Acb_NtkPrintUnsupportedObj( p, iObj, "ACB dual translation", -1, Vec_IntSize(vTemp)/2 ); + return 0; } -Gia_Man_t * Acb_NtkGiaDeriveDual( Acb_Ntk_t * p ) +Gia_Man_t * Acb_NtkGiaDeriveDualTargetsBranchValuesForceZero( Acb_Ntk_t * p, Vec_Int_t * vTargets, Vec_Int_t * vDcBranchObjs, Vec_Int_t * vDcBranchVals, int fDcBranchOne, int fForceZero ) { extern Vec_Int_t * Acb_NtkFindNodes2( Acb_Ntk_t * p ); Gia_Man_t * pNew, * pOne; @@ -365,11 +686,27 @@ Gia_Man_t * Acb_NtkGiaDeriveDual( Acb_Ntk_t * p ) Vec_IntForEachEntry( vNodes, iObj, i ) { pLits = Vec_IntEntryP( vCopies, 2*iObj ); - Acb_ObjToGiaDual( pNew, p, iObj, vFanins, vCopies, pLits ); + if ( !Acb_ObjToGiaDual( pNew, p, iObj, vFanins, vCopies, pLits, vDcBranchObjs, vDcBranchVals, fDcBranchOne, fForceZero ) ) + { + Vec_IntFree( vNodes ); + Vec_IntFree( vFanins ); + Vec_IntFree( vCopies ); + Gia_ManStop( pNew ); + return NULL; + } } Vec_IntFree( vNodes ); Vec_IntFree( vFanins ); - Acb_NtkForEachCo( p, iObj, i ) + if ( vTargets ) + { + Vec_IntForEachEntry( vTargets, iObj, i ) + { + pLits = Vec_IntEntryP( vCopies, 2*iObj ); + Gia_ManAppendCo( pNew, pLits[0] ); + Gia_ManAppendCo( pNew, pLits[1] ); + } + } + else Acb_NtkForEachCo( p, iObj, i ) { pLits = Vec_IntEntryP( vCopies, 2*Acb_ObjFanin(p, iObj, 0) ); Gia_ManAppendCo( pNew, pLits[0] ); @@ -380,13 +717,279 @@ Gia_Man_t * Acb_NtkGiaDeriveDual( Acb_Ntk_t * p ) Gia_ManStop( pOne ); return pNew; } +Gia_Man_t * Acb_NtkGiaDeriveDualTargetsBranchValues( Acb_Ntk_t * p, Vec_Int_t * vTargets, Vec_Int_t * vDcBranchObjs, Vec_Int_t * vDcBranchVals, int fDcBranchOne ) +{ + return Acb_NtkGiaDeriveDualTargetsBranchValuesForceZero( p, vTargets, vDcBranchObjs, vDcBranchVals, fDcBranchOne, 0 ); +} +Gia_Man_t * Acb_NtkGiaDeriveDualTargetsBranch( Acb_Ntk_t * p, Vec_Int_t * vTargets, Vec_Int_t * vDcBranchObjs, int fDcBranchOne ) +{ + return Acb_NtkGiaDeriveDualTargetsBranchValues( p, vTargets, vDcBranchObjs, NULL, fDcBranchOne ); +} +Gia_Man_t * Acb_NtkGiaDeriveDualTargets( Acb_Ntk_t * p, Vec_Int_t * vTargets ) +{ + return Acb_NtkGiaDeriveDualTargetsBranch( p, vTargets, NULL, 0 ); +} +Gia_Man_t * Acb_NtkGiaDeriveDualTargetsForceZero( Acb_Ntk_t * p, Vec_Int_t * vTargets ) +{ + return Acb_NtkGiaDeriveDualTargetsBranchValuesForceZero( p, vTargets, NULL, NULL, 0, 1 ); +} + +Gia_Man_t * Acb_NtkGiaDeriveDualTargetsCutLeaves( Acb_Ntk_t * p, Vec_Int_t * vTargets, Vec_Int_t * vCutObjs ) +{ + extern Vec_Int_t * Acb_NtkFindNodes2( Acb_Ntk_t * p ); + Gia_Man_t * pNew, * pOne; + Vec_Int_t * vFanins, * vNodes; + Vec_Int_t * vCopies = Vec_IntStartFull( 2*Acb_NtkObjNum(p) ); + Vec_Int_t * vCutMap = Vec_IntStart( Acb_NtkObjNumMax(p) ); + int i, iObj, * pLits; + pNew = Gia_ManStart( 5 * Acb_NtkObjNum(p) ); + pNew->pName = Abc_UtilStrsav(Acb_NtkName(p)); + Gia_ManHashAlloc( pNew ); + pLits = Vec_IntEntryP( vCopies, 0 ); + pLits[0] = 0; + pLits[1] = 0; + Acb_NtkForEachCi( p, iObj, i ) + { + pLits = Vec_IntEntryP( vCopies, 2*iObj ); + pLits[0] = Gia_ManAppendCi(pNew); + pLits[1] = 0; + } + if ( vCutObjs ) + Vec_IntForEachEntry( vCutObjs, iObj, i ) + { + pLits = Vec_IntEntryP( vCopies, 2*iObj ); + pLits[0] = Gia_ManAppendCi(pNew); + pLits[1] = Gia_ManAppendCi(pNew); + Vec_IntWriteEntry( vCutMap, iObj, 1 ); + } + vFanins = Vec_IntAlloc( 4 ); + vNodes = Acb_NtkFindNodes2( p ); + Vec_IntForEachEntry( vNodes, iObj, i ) + { + if ( Vec_IntEntry(vCutMap, iObj) ) + continue; + pLits = Vec_IntEntryP( vCopies, 2*iObj ); + if ( !Acb_ObjToGiaDual( pNew, p, iObj, vFanins, vCopies, pLits, NULL, NULL, 0, 0 ) ) + { + Vec_IntFree( vNodes ); + Vec_IntFree( vFanins ); + Vec_IntFree( vCutMap ); + Vec_IntFree( vCopies ); + Gia_ManStop( pNew ); + return NULL; + } + } + Vec_IntFree( vNodes ); + Vec_IntFree( vFanins ); + if ( vTargets ) + { + Vec_IntForEachEntry( vTargets, iObj, i ) + { + pLits = Vec_IntEntryP( vCopies, 2*iObj ); + Gia_ManAppendCo( pNew, pLits[0] ); + Gia_ManAppendCo( pNew, pLits[1] ); + } + } + else Acb_NtkForEachCo( p, iObj, i ) + { + pLits = Vec_IntEntryP( vCopies, 2*Acb_ObjFanin(p, iObj, 0) ); + Gia_ManAppendCo( pNew, pLits[0] ); + Gia_ManAppendCo( pNew, pLits[1] ); + } + Vec_IntFree( vCutMap ); + Vec_IntFree( vCopies ); + pNew = Gia_ManCleanup( pOne = pNew ); + Gia_ManStop( pOne ); + return pNew; +} +Gia_Man_t * Acb_NtkGiaDeriveDual( Acb_Ntk_t * p ) +{ + return Acb_NtkGiaDeriveDualTargets( p, NULL ); +} + +Vec_Int_t * Acb_NtkCollectPoMuxCutpoints( Acb_Ntk_t * p ) +{ + Vec_Int_t * vCutObjs = Vec_IntAlloc( Acb_NtkCoNum(p) ); + int i, iObj, iFanin; + Acb_NtkForEachCo( p, iObj, i ) + { + iFanin = Acb_ObjFanin( p, iObj, 0 ); + while ( !Acb_ObjIsCio(p, iFanin) && Acb_ObjType(p, iFanin) == ABC_OPER_BIT_BUF ) + iFanin = Acb_ObjFanin( p, iFanin, 0 ); + if ( !Acb_ObjIsCio(p, iFanin) && Acb_ObjType(p, iFanin) == ABC_OPER_BIT_MUX ) + Vec_IntPush( vCutObjs, iFanin ); + } + if ( Vec_IntSize(vCutObjs) != Acb_NtkCoNum(p) ) + Vec_IntClear( vCutObjs ); + return vCutObjs; +} + +Vec_Int_t * Acb_NtkCollectPoMuxSelectors( Acb_Ntk_t * p, Vec_Int_t * vCutObjs ) +{ + Vec_Int_t * vSelectors = Vec_IntAlloc( 4 ); + int i, iObj, iSel; + if ( vCutObjs == NULL ) + return vSelectors; + Vec_IntForEachEntry( vCutObjs, iObj, i ) + { + assert( !Acb_ObjIsCio(p, iObj) && Acb_ObjType(p, iObj) == ABC_OPER_BIT_MUX ); + iSel = Acb_ObjFanin( p, iObj, 2 ); + if ( Vec_IntFind(vSelectors, iSel) == -1 ) + Vec_IntPush( vSelectors, iSel ); + } + return vSelectors; +} + +Vec_Int_t * Acb_NtkCollectPoMuxSelectorIds( Acb_Ntk_t * p, Vec_Int_t * vCutObjs, Vec_Int_t * vSelectors ) +{ + Vec_Int_t * vIds = Vec_IntAlloc( Acb_NtkCoNum(p) ); + int i, iObj, iSel, iSelId; + if ( vCutObjs == NULL || vSelectors == NULL ) + return vIds; + Vec_IntForEachEntry( vCutObjs, iObj, i ) + { + assert( !Acb_ObjIsCio(p, iObj) && Acb_ObjType(p, iObj) == ABC_OPER_BIT_MUX ); + iSel = Acb_ObjFanin( p, iObj, 2 ); + iSelId = Vec_IntFind( vSelectors, iSel ); + assert( iSelId >= 0 ); + Vec_IntPush( vIds, iSelId ); + } + return vIds; +} + +Vec_Int_t * Acb_NtkCollectCoDriversForSelector( Acb_Ntk_t * p, Vec_Int_t * vPoSelIds, int iSelId ) +{ + Vec_Int_t * vDrivers = Vec_IntAlloc( Acb_NtkCoNum(p) ); + int i, iObj; + Acb_NtkForEachCo( p, iObj, i ) + if ( Vec_IntEntry(vPoSelIds, i) == iSelId ) + Vec_IntPush( vDrivers, Acb_ObjFanin(p, iObj, 0) ); + return vDrivers; +} + +Vec_Int_t * Acb_NtkCollectPoIdsForSelector( Acb_Ntk_t * p, Vec_Int_t * vPoSelIds, int iSelId ) +{ + Vec_Int_t * vPos = Vec_IntAlloc( Acb_NtkCoNum(p) ); + int i, iObj; + Acb_NtkForEachCo( p, iObj, i ) + { + (void)iObj; + if ( Vec_IntEntry(vPoSelIds, i) == iSelId ) + Vec_IntPush( vPos, i ); + } + return vPos; +} + +Vec_Int_t * Acb_NtkCollectPoMuxBranchTargets( Acb_Ntk_t * p, Vec_Int_t * vCutObjs, Vec_Int_t * vPoSelIds, int iSelId, int fUseOneBranch ) +{ + Vec_Int_t * vTargets = Vec_IntAlloc( Acb_NtkCoNum(p) ); + int i, iObj; + Vec_IntForEachEntry( vCutObjs, iObj, i ) + { + if ( Vec_IntEntry(vPoSelIds, i) != iSelId ) + continue; + assert( !Acb_ObjIsCio(p, iObj) && Acb_ObjType(p, iObj) == ABC_OPER_BIT_MUX ); + Vec_IntPush( vTargets, Acb_ObjFanin(p, iObj, fUseOneBranch ? 1 : 0) ); + } + return vTargets; +} +Vec_Int_t * Acb_NtkCollectPoMuxCubeTargets( Acb_Ntk_t * p, Vec_Int_t * vCutObjs, Vec_Int_t * vPoSelIds, Vec_Int_t * vCubeVals ) +{ + Vec_Int_t * vTargets = Vec_IntAlloc( Acb_NtkCoNum(p) + 2 * Vec_IntSize(vCubeVals) ); + int i, iObj, iSelId, fUseOneBranch; + Vec_IntForEachEntry( vCutObjs, iObj, i ) + { + assert( !Acb_ObjIsCio(p, iObj) && Acb_ObjType(p, iObj) == ABC_OPER_BIT_MUX ); + iSelId = Vec_IntEntry( vPoSelIds, i ); + assert( iSelId >= 0 && iSelId < Vec_IntSize(vCubeVals) ); + fUseOneBranch = Vec_IntEntry( vCubeVals, iSelId ); + Vec_IntPush( vTargets, Acb_ObjFanin(p, iObj, fUseOneBranch ? 1 : 0) ); + } + return vTargets; +} + +int Acb_NtkCollectInternalDcControls( Acb_Ntk_t * p, Vec_Int_t ** pvDcObjs, Vec_Int_t ** pvDcCtrls, Vec_Int_t ** pvDcCtrlIds ) +{ + extern Vec_Int_t * Acb_NtkFindNodes2( Acb_Ntk_t * p ); + Vec_Int_t * vNodes = Acb_NtkFindNodes2( p ); + Vec_Int_t * vDcObjs = Vec_IntAlloc( 16 ); + Vec_Int_t * vDcCtrls = Vec_IntAlloc( 4 ); + Vec_Int_t * vDcCtrlIds = Vec_IntAlloc( 16 ); + int i, iObj, iCtrl, iCtrlId; + Vec_IntForEachEntry( vNodes, iObj, i ) + { + if ( Acb_ObjIsCio(p, iObj) || Acb_ObjType(p, iObj) != ABC_OPER_TRI ) + continue; + iCtrl = Acb_ObjFanin( p, iObj, 1 ); + iCtrlId = Vec_IntFind( vDcCtrls, iCtrl ); + if ( iCtrlId == -1 ) + { + iCtrlId = Vec_IntSize( vDcCtrls ); + Vec_IntPush( vDcCtrls, iCtrl ); + } + Vec_IntPush( vDcObjs, iObj ); + Vec_IntPush( vDcCtrlIds, iCtrlId ); + } + Vec_IntFree( vNodes ); + *pvDcObjs = vDcObjs; + *pvDcCtrls = vDcCtrls; + *pvDcCtrlIds = vDcCtrlIds; + return Vec_IntSize( vDcObjs ); +} + +Vec_Int_t * Acb_NtkCollectDcObjsForControl( Vec_Int_t * vDcObjs, Vec_Int_t * vDcCtrlIds, int iCtrlId ) +{ + Vec_Int_t * vRes = Vec_IntAlloc( Vec_IntSize(vDcObjs) ); + int i, iObj; + Vec_IntForEachEntry( vDcObjs, iObj, i ) + if ( Vec_IntEntry(vDcCtrlIds, i) == iCtrlId ) + Vec_IntPush( vRes, iObj ); + return vRes; +} + +int Acb_NtkCollectPoDcCutpoints( Acb_Ntk_t * p, Vec_Int_t ** pvDataObjs, Vec_Int_t ** pvCtrlObjs ) +{ + Vec_Int_t * vDataObjs = Vec_IntAlloc( Acb_NtkCoNum(p) ); + Vec_Int_t * vCtrlObjs = Vec_IntAlloc( Acb_NtkCoNum(p) ); + int i, iObj, iFanin; + Acb_NtkForEachCo( p, iObj, i ) + { + iFanin = Acb_ObjFanin( p, iObj, 0 ); + while ( !Acb_ObjIsCio(p, iFanin) && Acb_ObjType(p, iFanin) == ABC_OPER_BIT_BUF ) + iFanin = Acb_ObjFanin( p, iFanin, 0 ); + if ( Acb_ObjIsCio(p, iFanin) || Acb_ObjType(p, iFanin) != ABC_OPER_TRI ) + break; + Vec_IntPush( vDataObjs, Acb_ObjFanin(p, iFanin, 0) ); + Vec_IntPush( vCtrlObjs, Acb_ObjFanin(p, iFanin, 1) ); + } + if ( i != Acb_NtkCoNum(p) ) + { + Vec_IntFree( vDataObjs ); + Vec_IntFree( vCtrlObjs ); + *pvDataObjs = NULL; + *pvCtrlObjs = NULL; + return 0; + } + *pvDataObjs = vDataObjs; + *pvCtrlObjs = vCtrlObjs; + return 1; +} + +Vec_Int_t * Acb_NtkCollectCoDrivers( Acb_Ntk_t * p ) +{ + Vec_Int_t * vDrivers = Vec_IntAlloc( Acb_NtkCoNum(p) ); + int i, iObj; + Acb_NtkForEachCo( p, iObj, i ) + Vec_IntPush( vDrivers, Acb_ObjFanin(p, iObj, 0) ); + return vDrivers; +} /**Function************************************************************* Synopsis [] Description [] - + SideEffects [] SeeAlso [] @@ -437,6 +1040,16 @@ Gia_Man_t * Acb_NtkGiaDeriveMiter( Gia_Man_t * pOne, Gia_Man_t * pTwo, int Type Gia_ManAppendCo( pNew, pLitsS[1] ); } } + else if ( Type == 3 ) // raw dual-rail outputs of the two designs + { + for ( i = 0; i < Gia_ManCoNum(pOne); i += 2 ) + { + Gia_ManAppendCo( pNew, Gia_ManCo(pOne, i)->Value ); + Gia_ManAppendCo( pNew, Gia_ManCo(pOne, i+1)->Value ); + Gia_ManAppendCo( pNew, Gia_ManCo(pTwo, i)->Value ); + Gia_ManAppendCo( pNew, Gia_ManCo(pTwo, i+1)->Value ); + } + } else // comparator of the two { for ( i = 0; i < Gia_ManCoNum(pOne); i += 2 ) @@ -452,19 +1065,166 @@ Gia_Man_t * Acb_NtkGiaDeriveMiter( Gia_Man_t * pOne, Gia_Man_t * pTwo, int Type return pNew; } +Gia_Man_t * Acb_NtkGiaDeriveMiterWithSecondExtras( Gia_Man_t * pOne, Gia_Man_t * pTwo, int nExtraPairs ) +{ + Gia_Man_t * pNew, * pTemp; + Gia_Obj_t * pObj; + int i, nCompareCos = Gia_ManCoNum(pOne); + assert( Gia_ManCiNum(pOne) == Gia_ManCiNum(pTwo) ); + assert( Gia_ManCoNum(pTwo) == Gia_ManCoNum(pOne) + 2*nExtraPairs ); + pNew = Gia_ManStart( Gia_ManObjNum(pOne) + Gia_ManObjNum(pTwo) + 5*nCompareCos/2 + 2*nExtraPairs ); + pNew->pName = Abc_UtilStrsav( "miter_with_selectors" ); + pNew->pSpec = NULL; + Gia_ManHashAlloc( pNew ); + Gia_ManConst0(pOne)->Value = 0; + Gia_ManConst0(pTwo)->Value = 0; + Gia_ManForEachCi( pOne, pObj, i ) + pObj->Value = Gia_ManAppendCi( pNew ); + Gia_ManForEachCi( pTwo, pObj, i ) + pObj->Value = Gia_ManCi(pOne, i)->Value; + Gia_ManForEachAnd( pOne, pObj, i ) + pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); + Gia_ManForEachAnd( pTwo, pObj, i ) + pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); + Gia_ManForEachCo( pOne, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + Gia_ManForEachCo( pTwo, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + for ( i = 0; i < nCompareCos; i += 2 ) + { + int pLitsF[2] = { (int)Gia_ManCo(pOne, i)->Value, (int)Gia_ManCo(pOne, i+1)->Value }; + int pLitsS[2] = { (int)Gia_ManCo(pTwo, i)->Value, (int)Gia_ManCo(pTwo, i+1)->Value }; + Gia_ManAppendCo( pNew, Gia_ManDualCompare( pNew, pLitsF, pLitsS ) ); + } + for ( i = nCompareCos; i < Gia_ManCoNum(pTwo); i++ ) + Gia_ManAppendCo( pNew, (int)Gia_ManCo(pTwo, i)->Value ); + Gia_ManHashStop( pNew ); + pNew = Gia_ManCleanup( pTemp = pNew ); + Gia_ManStop( pTemp ); + return pNew; +} + +Gia_Man_t * Acb_GiaDeriveBranchConditionMiter( Gia_Man_t * p, int nMiterOuts, int fUseOneBranch ) +{ + Gia_Man_t * pNew, * pTemp; + Gia_Obj_t * pObj; + int i, LitSel, LitSelX, LitCond; + assert( nMiterOuts > 0 ); + assert( Gia_ManCoNum(p) == nMiterOuts + 2 ); + pNew = Gia_ManStart( Gia_ManObjNum(p) + nMiterOuts + 4 ); + pNew->pName = Abc_UtilStrsav( "branch_condition_miter" ); + Gia_ManHashAlloc( pNew ); + Gia_ManConst0(p)->Value = 0; + Gia_ManForEachCi( p, pObj, i ) + pObj->Value = Gia_ManAppendCi( pNew ); + Gia_ManForEachAnd( p, pObj, i ) + pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); + Gia_ManForEachCo( p, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + LitSel = (int)Gia_ManCo(p, nMiterOuts)->Value; + LitSelX = (int)Gia_ManCo(p, nMiterOuts + 1)->Value; + LitCond = Gia_ManHashAnd( pNew, Abc_LitNot(LitSelX), fUseOneBranch ? LitSel : Abc_LitNot(LitSel) ); + for ( i = 0; i < nMiterOuts; i++ ) + Gia_ManAppendCo( pNew, Gia_ManHashAnd( pNew, (int)Gia_ManCo(p, i)->Value, LitCond ) ); + Gia_ManHashStop( pNew ); + pNew = Gia_ManCleanup( pTemp = pNew ); + Gia_ManStop( pTemp ); + return pNew; +} +Gia_Man_t * Acb_GiaDeriveCubeConditionMiter( Gia_Man_t * p, int nMiterOuts, Vec_Int_t * vCubeVals ) +{ + Gia_Man_t * pNew, * pTemp; + Gia_Obj_t * pObj; + int i, k, LitSel, LitSelX, LitCond = 1; + int nCubes = Vec_IntSize(vCubeVals); + assert( nMiterOuts > 0 ); + assert( Gia_ManCoNum(p) == nMiterOuts + 2*nCubes ); + pNew = Gia_ManStart( Gia_ManObjNum(p) + nMiterOuts + 4*nCubes + 4 ); + pNew->pName = Abc_UtilStrsav( "cube_condition_miter" ); + Gia_ManHashAlloc( pNew ); + Gia_ManConst0(p)->Value = 0; + Gia_ManForEachCi( p, pObj, i ) + pObj->Value = Gia_ManAppendCi( pNew ); + Gia_ManForEachAnd( p, pObj, i ) + pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); + Gia_ManForEachCo( p, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + for ( k = 0; k < nCubes; k++ ) + { + LitSel = (int)Gia_ManCo(p, nMiterOuts + 2*k)->Value; + LitSelX = (int)Gia_ManCo(p, nMiterOuts + 2*k + 1)->Value; + LitSel = Vec_IntEntry(vCubeVals, k) ? LitSel : Abc_LitNot(LitSel); + LitCond = Gia_ManHashAnd( pNew, LitCond, Gia_ManHashAnd( pNew, Abc_LitNot(LitSelX), LitSel ) ); + } + for ( i = 0; i < nMiterOuts; i++ ) + Gia_ManAppendCo( pNew, Gia_ManHashAnd( pNew, (int)Gia_ManCo(p, i)->Value, LitCond ) ); + Gia_ManHashStop( pNew ); + pNew = Gia_ManCleanup( pTemp = pNew ); + Gia_ManStop( pTemp ); + return pNew; +} + +Gia_Man_t * Acb_NtkGiaDeriveMiterDcGuard( Gia_Man_t * pOne, Gia_Man_t * pData, Gia_Man_t * pCtrl ) +{ + Gia_Man_t * pNew, * pTemp; + Gia_Obj_t * pObj; + int i; + assert( Gia_ManCiNum(pOne) == Gia_ManCiNum(pData) ); + assert( Gia_ManCiNum(pOne) == Gia_ManCiNum(pCtrl) ); + assert( Gia_ManCoNum(pOne) == Gia_ManCoNum(pData) ); + assert( Gia_ManCoNum(pOne) == Gia_ManCoNum(pCtrl) ); + pNew = Gia_ManStart( Gia_ManObjNum(pOne) + Gia_ManObjNum(pData) + Gia_ManObjNum(pCtrl) + 6*Gia_ManCoNum(pOne)/2 ); + pNew->pName = Abc_UtilStrsav( "dc_guard_miter" ); + Gia_ManHashAlloc( pNew ); + Gia_ManConst0(pOne)->Value = 0; + Gia_ManConst0(pData)->Value = 0; + Gia_ManConst0(pCtrl)->Value = 0; + Gia_ManForEachCi( pOne, pObj, i ) + pObj->Value = Gia_ManAppendCi( pNew ); + Gia_ManForEachCi( pData, pObj, i ) + pObj->Value = Gia_ManCi(pOne, i)->Value; + Gia_ManForEachCi( pCtrl, pObj, i ) + pObj->Value = Gia_ManCi(pOne, i)->Value; + Gia_ManForEachAnd( pOne, pObj, i ) + pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); + Gia_ManForEachAnd( pData, pObj, i ) + pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); + Gia_ManForEachAnd( pCtrl, pObj, i ) + pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); + Gia_ManForEachCo( pOne, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + Gia_ManForEachCo( pData, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + Gia_ManForEachCo( pCtrl, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + for ( i = 0; i < Gia_ManCoNum(pOne); i += 2 ) + { + int pLitsF[2] = { (int)Gia_ManCo(pOne, i)->Value, (int)Gia_ManCo(pOne, i+1)->Value }; + int pLitsS[2] = { (int)Gia_ManCo(pData, i)->Value, (int)Gia_ManCo(pData, i+1)->Value }; + int Ctrl0 = (int)Gia_ManCo(pCtrl, i)->Value; + int Ctrl1 = (int)Gia_ManCo(pCtrl, i+1)->Value; + pLitsS[1] = Gia_ManHashOr( pNew, pLitsS[1], Gia_ManHashOr( pNew, Ctrl0, Ctrl1 ) ); + Gia_ManAppendCo( pNew, Gia_ManDualCompare( pNew, pLitsF, pLitsS ) ); + } + Gia_ManHashStop( pNew ); + pNew = Gia_ManCleanup( pTemp = pNew ); + Gia_ManStop( pTemp ); + return pNew; +} + /**Function************************************************************* Synopsis [] Description [] - + SideEffects [] SeeAlso [] ***********************************************************************/ -void Acb_OutputFile( char * pFileName, Acb_Ntk_t * pNtkF, int * pModel ) +void Acb_OutputFile( char * pFileName, Acb_Ntk_t * pNtkF, int * pModel, int Status ) { const char * pFileName0 = pFileName? pFileName : "output"; FILE * pFile = fopen( pFileName0, "wb" ); @@ -473,9 +1233,11 @@ void Acb_OutputFile( char * pFileName, Acb_Ntk_t * pNtkF, int * pModel ) printf( "Cannot open results file \"%s\".\n", pFileName0 ); return; } - if ( pModel == NULL ) + if ( Status == ACB_XEC_UNDEC ) + fprintf( pFile, "UNDECIDED\n" ); + else if ( pModel == NULL ) fprintf( pFile, "EQ\n" ); - else + else { /* NEQ @@ -491,7 +1253,7 @@ void Acb_OutputFile( char * pFileName, Acb_Ntk_t * pNtkF, int * pModel ) fclose( pFile ); printf( "Produced output file \"%s\".\n\n", pFileName0 ); } -int * Acb_NtkSolve( Gia_Man_t * p ) +int * Acb_NtkSolve( Gia_Man_t * p, int fVerbose, int * pStatus ) { extern Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); Aig_Man_t * pMan = Gia_ManToAig( p, 0 ); @@ -499,7 +1261,7 @@ int * Acb_NtkSolve( Gia_Man_t * p ) Prove_Params_t Params, * pParams = &Params; Prove_ParamsSetDefault( pParams ); pParams->fUseRewriting = 1; - pParams->fVerbose = 0; + pParams->fVerbose = fVerbose; Aig_ManStop( pMan ); if ( pNtkTemp ) { @@ -508,11 +1270,3302 @@ int * Acb_NtkSolve( Gia_Man_t * p ) int * pModel = pNtkTemp->pModel; pNtkTemp->pModel = NULL; Abc_NtkDelete( pNtkTemp ); + *pStatus = RetValue; printf( "The networks are %s. ", RetValue == 1 ? "equivalent" : (RetValue == 0 ? "NOT equivalent" : "UNDECIDED") ); Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); if ( RetValue == 0 ) return pModel; } + *pStatus = ACB_XEC_UNDEC; + return NULL; +} +int * Acb_NtkSolveIvyPrecheck( Gia_Man_t * p, int fVerbose, int * pStatus ) +{ + extern Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); + Aig_Man_t * pMan = Gia_ManToAig( p, 0 ); + Abc_Ntk_t * pNtkTemp = Abc_NtkFromAigPhase( pMan ); + Prove_Params_t Params, * pParams = &Params; + Prove_ParamsSetDefault( pParams ); + pParams->fUseFraiging = 1; + pParams->fUseRewriting = 1; + pParams->fUseBdds = 0; + pParams->nItersMax = 6; + pParams->nMiteringLimitStart = 5000; + pParams->nMiteringLimitMulti = 2.0; + pParams->nFraigingLimitStart = 2; + pParams->nFraigingLimitMulti = 8.0; + pParams->nMiteringLimitLast = 0; + pParams->nTotalBacktrackLimit = 750000; + pParams->fVerbose = fVerbose; + Aig_ManStop( pMan ); + if ( pNtkTemp ) + { + abctime clk = Abc_Clock(); + int RetValue; + int * pModel; + if ( fVerbose ) + printf( "Trying bounded Ivy/FRAIG precheck before CaDiCaL: And = %d. PO = %d. total conflict limit = %d.\n", + Gia_ManAndNum(p), Gia_ManCoNum(p), (int)pParams->nTotalBacktrackLimit ); + RetValue = Abc_NtkIvyProve( &pNtkTemp, pParams ); + pModel = pNtkTemp->pModel; + pNtkTemp->pModel = NULL; + Abc_NtkDelete( pNtkTemp ); + *pStatus = RetValue; + printf( "The networks are %s by bounded Ivy/FRAIG precheck. ", + RetValue == 1 ? "equivalent" : (RetValue == 0 ? "NOT equivalent" : "UNDECIDED") ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + if ( RetValue == 0 ) + return pModel; + ABC_FREE( pModel ); + return NULL; + } + *pStatus = ACB_XEC_UNDEC; + return NULL; +} +int * Acb_NtkSolveNormalPrecheck( Gia_Man_t * p, int fVerbose, int * pStatus, int nBacktrackLimit ) +{ + extern Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); + Aig_Man_t * pMan = Gia_ManToAig( p, 0 ); + Abc_Ntk_t * pNtkTemp = Abc_NtkFromAigPhase( pMan ); + Prove_Params_t Params, * pParams = &Params; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + Prove_ParamsSetDefault( pParams ); + pParams->fUseRewriting = 1; + pParams->nTotalBacktrackLimit = nBacktrackLimit; + pParams->fVerbose = fVerbose; + Aig_ManStop( pMan ); + if ( pNtkTemp ) + { + abctime clk = Abc_Clock(); + int RetValue; + int * pModel; + if ( fVerbose ) + printf( "Trying normal XEC precheck before CaDiCaL-specific UNSAT passes: And = %d. PO = %d. backtrack limit = %d.\n", + Gia_ManAndNum(p), Gia_ManCoNum(p), nBacktrackLimit ); + RetValue = Abc_NtkIvyProve( &pNtkTemp, pParams ); + pModel = pNtkTemp->pModel; + pNtkTemp->pModel = NULL; + Abc_NtkDelete( pNtkTemp ); + if ( pStatus ) + *pStatus = RetValue; + printf( "The networks are %s by normal XEC precheck. ", + RetValue == 1 ? "equivalent" : (RetValue == 0 ? "NOT equivalent" : "UNDECIDED") ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + if ( RetValue == 0 ) + return pModel; + ABC_FREE( pModel ); + return NULL; + } + return NULL; +} +Gia_Man_t * Acb_NtkFraigEquivReduce( Gia_Man_t * p, int fVerbose, char * pLabel, char * pPhase, int nWords, int nConfLimit, int nSatVarMax, int nMinGain ) +{ + Dch_Pars_t Pars, * pPars = &Pars; + Gia_Man_t * pWork = NULL, * pNew = NULL, * pTemp = NULL; + int nAndStart = Gia_ManAndNum( p ); + abctime clk = Abc_Clock(); + if ( Gia_ManCoNum(p) == 0 || nAndStart < 1000 ) + return NULL; + Dch_ManSetDefaultParams( pPars ); + pPars->nWords = nWords; + pPars->nBTLimit = nConfLimit; + pPars->nSatVarMax = nSatVarMax; + pPars->fSynthesis = 0; + pPars->fPolarFlip = 1; + pPars->fSimulateTfo= 1; + pPars->fVerbose = 0; + pWork = Gia_ManDup( p ); + if ( pWork == NULL ) + return NULL; + if ( fVerbose ) + printf( "%s %s FRAIG equivalence reduction: And = %d. PO = %d. words = %d. node-conf = %d. sat-var-max = %d.\n", + pLabel ? pLabel : "XEC", pPhase ? pPhase : "structural", nAndStart, Gia_ManCoNum(p), nWords, nConfLimit, nSatVarMax ); + pNew = Gia_ManFraigSweepSimple( pWork, pPars ); + Gia_ManStop( pWork ); + if ( pNew == NULL ) + return NULL; + pTemp = Gia_ManCompress2( pNew, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pNew ); + pNew = pTemp; + } + if ( fVerbose ) + { + printf( "%s %s FRAIG equivalence reduction: And = %d -> %d. Lev = %d -> %d. ", + pLabel ? pLabel : "XEC", pPhase ? pPhase : "structural", + nAndStart, Gia_ManAndNum(pNew), Gia_ManLevelNum(p), Gia_ManLevelNum(pNew) ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + if ( Gia_ManCoNum(pNew) != Gia_ManCoNum(p) || Gia_ManAndNum(pNew) >= nAndStart - nMinGain ) + { + if ( fVerbose && Gia_ManCoNum(pNew) == Gia_ManCoNum(p) ) + printf( "%s %s FRAIG equivalence reduction skipped because the proven merge gain is too small.\n", + pLabel ? pLabel : "XEC", pPhase ? pPhase : "structural" ); + Gia_ManStop( pNew ); + return NULL; + } + return pNew; +} +int Acb_NtkObjIsConstTypeThroughBuf( Acb_Ntk_t * p, int iObj, Acb_ObjType_t Type ) +{ + while ( !Acb_ObjIsCio(p, iObj) && Acb_ObjType(p, iObj) == ABC_OPER_BIT_BUF ) + iObj = Acb_ObjFanin(p, iObj, 0); + return !Acb_ObjIsCio(p, iObj) && Acb_ObjType(p, iObj) == Type; +} +int Acb_NtkDcObjIsConstXSeed( Acb_Ntk_t * p, int iObj ) +{ + if ( iObj <= 0 || Acb_ObjIsCio(p, iObj) || Acb_ObjType(p, iObj) != ABC_OPER_TRI ) + return 0; + if ( Acb_ObjFaninNum(p, iObj) != 2 ) + return 0; + return Acb_NtkObjIsConstTypeThroughBuf( p, Acb_ObjFanin(p, iObj, 0), ABC_OPER_CONST_F ) && + Acb_NtkObjIsConstTypeThroughBuf( p, Acb_ObjFanin(p, iObj, 1), ABC_OPER_CONST_T ); +} +int Acb_NtkAllDcObjsAreConstXSeeds( Acb_Ntk_t * p, Vec_Int_t * vDcObjs ) +{ + int i, iObj; + if ( vDcObjs == NULL || Vec_IntSize(vDcObjs) == 0 ) + return 0; + Vec_IntForEachEntry( vDcObjs, iObj, i ) + if ( !Acb_NtkDcObjIsConstXSeed(p, iObj) ) + return 0; + return 1; +} +int * Acb_NtkSolveConstXSeedCanonical( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, Vec_Int_t * vDcObjsG, int fVerbose, int * pStatus, int nSatTimeLimit ) +{ + Vec_Int_t * vTargetsF = NULL, * vTargetsG = NULL; + Gia_Man_t * pGiaF = NULL, * pGiaG = NULL, * pGia = NULL, * pTemp = NULL; + int Status = ACB_XEC_UNDEC, * pModel = NULL; + abctime clk = Abc_Clock(); + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( !Acb_NtkAllDcObjsAreConstXSeeds(pNtkG, vDcObjsG) ) + return NULL; + vTargetsF = Acb_NtkCollectCoDrivers( pNtkF ); + vTargetsG = Acb_NtkCollectCoDrivers( pNtkG ); + pGiaF = Acb_NtkGiaDeriveDualTargets( pNtkF, vTargetsF ); + pGiaG = Acb_NtkGiaDeriveDualTargetsForceZero( pNtkG, vTargetsG ); + pGia = Acb_NtkGiaDeriveMiter( pGiaF, pGiaG, 2 ); + if ( fVerbose ) + printf( "Trying constant-X seed canonical proof: DC seeds = %d. And = %d. PO = %d. limit = %d sec.\n", + Vec_IntSize(vDcObjsG), Gia_ManAndNum(pGia), Gia_ManCoNum(pGia), nSatTimeLimit ); + if ( Gia_ManAndNum(pGia) > 5000 ) + { + pTemp = Gia_ManCompress2( pGia, 1, 0 ); + if ( pTemp ) + { + if ( fVerbose ) + printf( "Constant-X seed canonical compression: And = %d -> %d. PO = %d.\n", + Gia_ManAndNum(pGia), Gia_ManAndNum(pTemp), Gia_ManCoNum(pTemp) ); + Gia_ManStop( pGia ); + pGia = pTemp; + pTemp = NULL; + } + } + if ( Gia_ManAndNum(pGia) > 8000 ) + { + pTemp = Acb_NtkFraigEquivReduce( pGia, fVerbose, + "Constant-X seed canonical proof", "canonical dual", 32, 300, 12000, + Abc_MaxInt( 50, Gia_ManAndNum(pGia) / 200 ) ); + if ( pTemp ) + { + Gia_ManStop( pGia ); + pGia = pTemp; + pTemp = NULL; + } + } + if ( Gia_ManCoNum(pGia) == 0 || Acb_GiaAllPosConst0(pGia) ) + { + Status = ACB_XEC_EQ; + if ( fVerbose ) + printf( "Constant-X seed canonical proof: all miter outputs are constant-0 after canonicalization.\n" ); + } + else + pModel = Acb_NtkSolveCadicalLimit( pGia, 0, fVerbose, &Status, nSatTimeLimit, + "constant-X seed canonical CaDiCaL", 0 ); + if ( pStatus ) + *pStatus = Status; + if ( Status == ACB_XEC_EQ ) + { + printf( "The networks are equivalent by constant-X seed canonical proof. " ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + else if ( fVerbose ) + { + printf( "The networks are %s by constant-X seed canonical proof. ", + Status == ACB_XEC_NEQ ? "NOT equivalent" : "UNDECIDED" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + Gia_ManStop( pGia ); + Gia_ManStop( pGiaG ); + Gia_ManStop( pGiaF ); + Vec_IntFree( vTargetsG ); + Vec_IntFree( vTargetsF ); + return pModel; +} +Gia_Man_t * Acb_NtkBranchSweepReduce( Gia_Man_t * p, int fVerbose, char * pLabel ) +{ + Cec_ParFra_t Pars, * pPars = &Pars; + Gia_Man_t * pNew = NULL, * pBest = NULL, * pFraig = NULL; + abctime clk = Abc_Clock(); + int nAndStart = Gia_ManAndNum( p ); + if ( Gia_ManCoNum(p) < 8 || nAndStart < 8000 || nAndStart > 60000 ) + return NULL; + Cec_ManFraSetDefaultParams( pPars ); + pPars->nWords = 64; + pPars->nRounds = 8; + pPars->nItersMax = 6; + pPars->nBTLimit = 800; + pPars->nBTLimitPo = 0; + pPars->TimeLimit = 90; + pPars->fCheckMiter = 0; + pPars->fSatSweeping = 1; + pPars->fUseCones = 1; + pPars->fRewriting = 1; + pPars->fVerbose = 0; + if ( fVerbose ) + printf( "%s branch SAT-sweeping reduction: And = %d. PO = %d. limit = %d sec. node-conf = %d.\n", + pLabel ? pLabel : "XEC", nAndStart, Gia_ManCoNum(p), pPars->TimeLimit, pPars->nBTLimit ); + pNew = Cec_ManSatSweeping( p, pPars, 1 ); + if ( pNew == NULL ) + { + if ( fVerbose ) + printf( "%s branch SAT-sweeping reduction produced no network.\n", pLabel ? pLabel : "XEC" ); + } + else if ( fVerbose ) + { + printf( "%s branch SAT-sweeping reduction: And = %d -> %d. Lev = %d -> %d. ", + pLabel ? pLabel : "XEC", nAndStart, Gia_ManAndNum(pNew), Gia_ManLevelNum(p), Gia_ManLevelNum(pNew) ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + if ( pNew && Gia_ManCoNum(pNew) == Gia_ManCoNum(p) && Gia_ManAndNum(pNew) < nAndStart ) + pBest = pNew, pNew = NULL; + if ( pNew ) + Gia_ManStop( pNew ); + if ( nAndStart >= 15000 ) + pFraig = Acb_NtkFraigEquivReduce( pBest ? pBest : p, fVerbose, pLabel, "branch", 32, 300, 12000, Abc_MaxInt( 50, nAndStart / 200 ) ); + if ( pFraig ) + { + if ( pBest ) + Gia_ManStop( pBest ); + pBest = pFraig; + } + if ( pBest == NULL || Gia_ManAndNum(pBest) >= nAndStart - Abc_MaxInt( 50, nAndStart / 100 ) ) + { + if ( fVerbose ) + printf( "%s branch structural reduction skipped because reduction is too small.\n", pLabel ? pLabel : "XEC" ); + if ( pBest ) + Gia_ManStop( pBest ); + return NULL; + } + return pBest; +} +int Acb_GiaAndObligationsUniq( Gia_Man_t * p, Vec_Int_t * vReq ) +{ + Vec_Int_t * vSeen = Vec_IntStart( Gia_ManObjNum(p) ); + int i, Lit, Var, Sign, Prev, nOut = 0; + Vec_IntForEachEntry( vReq, Lit, i ) + { + if ( Lit < 2 ) + continue; + Var = Abc_Lit2Var( Lit ); + Sign = Abc_LitIsCompl( Lit ) ? 2 : 1; + Prev = Vec_IntEntry( vSeen, Var ); + if ( Prev && Prev != Sign ) + { + Vec_IntFree( vSeen ); + return 1; + } + if ( Prev == Sign ) + continue; + Vec_IntWriteEntry( vSeen, Var, Sign ); + Vec_IntWriteEntry( vReq, nOut++, Lit ); + } + Vec_IntShrink( vReq, nOut ); + Vec_IntFree( vSeen ); + return 0; +} +int Acb_GiaCollectLinearLit_rec( Gia_Man_t * p, int Lit, word * pRow, int nWords, int * pConst, int Depth ) +{ + Gia_Obj_t * pObj, * pFan0 = NULL, * pFan1 = NULL; + int iVar, Lit0, Lit1; + if ( Depth > ACB_XEC_RECURSION_LIMIT ) + return 0; + if ( Lit < 2 ) + { + if ( Lit == 1 ) + *pConst ^= 1; + return 1; + } + pObj = Gia_ManObj( p, Abc_Lit2Var(Lit) ); + if ( Abc_LitIsCompl(Lit) ) + *pConst ^= 1; + if ( Gia_ObjIsCi(pObj) ) + { + iVar = Gia_ObjCioId(pObj); + pRow[iVar >> 6] ^= ((word)1) << (iVar & 63); + return 1; + } + if ( Gia_ObjIsXor(pObj) ) + { + if ( !Acb_GiaCollectLinearLit_rec( p, Gia_ObjFaninLit0p(p, pObj), pRow, nWords, pConst, Depth + 1 ) ) + return 0; + if ( !Acb_GiaCollectLinearLit_rec( p, Gia_ObjFaninLit1p(p, pObj), pRow, nWords, pConst, Depth + 1 ) ) + return 0; + return 1; + } + if ( Gia_ObjRecognizeExor( pObj, &pFan0, &pFan1 ) ) + { + Lit0 = Abc_Var2Lit( Gia_ObjId(p, Gia_Regular(pFan0)), Gia_IsComplement(pFan0) ); + Lit1 = Abc_Var2Lit( Gia_ObjId(p, Gia_Regular(pFan1)), Gia_IsComplement(pFan1) ); + if ( !Acb_GiaCollectLinearLit_rec( p, Lit0, pRow, nWords, pConst, Depth + 1 ) ) + return 0; + if ( !Acb_GiaCollectLinearLit_rec( p, Lit1, pRow, nWords, pConst, Depth + 1 ) ) + return 0; + return 1; + } + return 0; +} +int Acb_GiaSolveLinearObligations( Gia_Man_t * p, Vec_Int_t * vReq, int fVerbose ) +{ + int nVars, nWords, nRows, i, k, Lit, Const, Pivot, PivotRow, nRank = 0, fLinear = 1; + word * pRows = NULL; + unsigned char * pRhs = NULL; + if ( p == NULL || vReq == NULL || Vec_IntSize(vReq) == 0 || Gia_ManCiNum(p) > 4096 ) + return ACB_XEC_UNDEC; + nVars = Gia_ManCiNum(p); + nWords = Abc_BitWordNum(nVars); + nRows = Vec_IntSize(vReq); + pRows = ABC_CALLOC( word, nRows * nWords ); + pRhs = ABC_CALLOC( unsigned char, nRows ); + Vec_IntForEachEntry( vReq, Lit, i ) + { + Const = 0; + if ( !Acb_GiaCollectLinearLit_rec( p, Lit, pRows + i * nWords, nWords, &Const, 0 ) ) + { + if ( fVerbose ) + printf( "Required-literal XOR-linear proof: obligation %d is non-linear; skipping.\n", i ); + fLinear = 0; + break; + } + pRhs[i] = Const ^ 1; + } + for ( Pivot = 0; fLinear && Pivot < nVars && nRank < nRows; Pivot++ ) + { + word Mask = ((word)1) << (Pivot & 63); + int WordId = Pivot >> 6; + PivotRow = -1; + for ( i = nRank; i < nRows; i++ ) + if ( pRows[i*nWords + WordId] & Mask ) + { + PivotRow = i; + break; + } + if ( PivotRow < 0 ) + continue; + if ( PivotRow != nRank ) + { + for ( k = 0; k < nWords; k++ ) + { + word Temp = pRows[nRank*nWords + k]; + pRows[nRank*nWords + k] = pRows[PivotRow*nWords + k]; + pRows[PivotRow*nWords + k] = Temp; + } + ABC_SWAP( unsigned char, pRhs[nRank], pRhs[PivotRow] ); + } + for ( i = 0; i < nRows; i++ ) + { + if ( i == nRank || !(pRows[i*nWords + WordId] & Mask) ) + continue; + for ( k = WordId; k < nWords; k++ ) + pRows[i*nWords + k] ^= pRows[nRank*nWords + k]; + pRhs[i] ^= pRhs[nRank]; + } + nRank++; + } + for ( i = 0; fLinear && i < nRows; i++ ) + { + int fZero = 1; + for ( k = 0; k < nWords; k++ ) + if ( pRows[i*nWords + k] ) + { + fZero = 0; + break; + } + if ( fZero && pRhs[i] ) + { + if ( fVerbose ) + printf( "Required-literal XOR-linear proof: UNSAT. equations = %d. rank = %d.\n", nRows, nRank ); + ABC_FREE( pRows ); + ABC_FREE( pRhs ); + return ACB_XEC_EQ; + } + } + if ( fLinear && fVerbose ) + printf( "Required-literal XOR-linear proof: consistent. equations = %d. rank = %d.\n", nRows, nRank ); + ABC_FREE( pRows ); + ABC_FREE( pRhs ); + return ACB_XEC_UNDEC; +} +Gia_Man_t * Acb_GiaDupWithObligationOutputs( Gia_Man_t * p, Vec_Int_t * vReq ) +{ + Gia_Man_t * pNew, * pTemp; + Gia_Obj_t * pObj; + int i, Lit; + pNew = Gia_ManStart( Gia_ManObjNum(p) + Vec_IntSize(vReq) + 100 ); + pNew->pName = Abc_UtilStrsav( "and_obligations" ); + Gia_ManHashAlloc( pNew ); + Gia_ManFillValue( p ); + Gia_ManConst0(p)->Value = 0; + Gia_ManForEachCi( p, pObj, i ) + pObj->Value = Gia_ManAppendCi( pNew ); + Gia_ManForEachAnd( p, pObj, i ) + pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); + Vec_IntForEachEntry( vReq, Lit, i ) + Gia_ManAppendCo( pNew, Gia_ObjLitCopy(p, Lit) ); + Gia_ManHashStop( pNew ); + pNew = Gia_ManCleanup( pTemp = pNew ); + Gia_ManStop( pTemp ); + return pNew; +} +int Acb_CnfWriteIntoCadical( cadical_solver * pSat, Cnf_Dat_t * pCnf ) +{ + int i, * pBeg, * pEnd; + if ( pSat == NULL || pCnf == NULL ) + return 0; + cadical_solver_setnvars( pSat, pCnf->nVars ); + Cnf_CnfForClause( pCnf, pBeg, pEnd, i ) + if ( !cadical_solver_addclause( pSat, pBeg, pEnd ) ) + return 0; + return 1; +} +int Acb_GiaSolveObligationListUnit( Gia_Man_t * p, Vec_Int_t * vReq, int fVerbose, int nSatTimeLimit, char * pLabel ) +{ + Gia_Man_t * pObl = NULL; + Aig_Man_t * pMan = NULL; + Cnf_Dat_t * pCnf = NULL; + cadical_solver * pSat = NULL; + int i, Lit, Ret, Status = ACB_XEC_UNDEC; + abctime clk = Abc_Clock(); + (void)nSatTimeLimit; + if ( p == NULL || vReq == NULL || Vec_IntSize(vReq) == 0 ) + return ACB_XEC_UNDEC; + if ( Acb_GiaAndObligationsUniq( p, vReq ) ) + return ACB_XEC_EQ; + Status = Acb_GiaSolveLinearObligations( p, vReq, 0 ); + if ( Status == ACB_XEC_EQ ) + return Status; + pObl = Acb_GiaDupWithObligationOutputs( p, vReq ); + pMan = pObl ? Gia_ManToAig( pObl, 0 ) : NULL; + pCnf = pMan ? Cnf_Derive( pMan, Aig_ManCoNum(pMan) ) : NULL; + pSat = pCnf ? cadical_solver_new() : NULL; + if ( pCnf == NULL || pSat == NULL || !Acb_CnfWriteIntoCadical(pSat, pCnf) ) + goto cleanup; + for ( i = 0; i < Gia_ManCoNum(pObl); i++ ) + { + Ret = Acb_CnfCoDriverLit( pCnf, i, &Lit ); + if ( Ret < 0 ) + { + Status = Ret == -1 ? ACB_XEC_EQ : ACB_XEC_UNDEC; + goto cleanup; + } + if ( Ret > 0 && !cadical_solver_addclause( pSat, &Lit, &Lit + 1 ) ) + { + Status = ACB_XEC_EQ; + goto cleanup; + } + } + Ret = cadical_solver_solve( pSat, NULL, NULL, 0, 0, 0, 0 ); + Status = Ret == -1 ? ACB_XEC_EQ : ACB_XEC_UNDEC; +cleanup: + if ( fVerbose ) + { + printf( "%s: %s. obligations = %d. ", + pLabel ? pLabel : "Exact obligation unit branch", + Status == ACB_XEC_EQ ? "UNSAT" : "UNDECIDED", Vec_IntSize(vReq) ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + if ( pSat ) + cadical_solver_delete( pSat ); + if ( pCnf ) + Cnf_DataFree( pCnf ); + if ( pMan ) + Aig_ManStop( pMan ); + if ( pObl ) + Gia_ManStop( pObl ); + return Status; +} +typedef struct Acb_SplitPoOrder_t_ Acb_SplitPoOrder_t; +struct Acb_SplitPoOrder_t_ +{ + int iPo; + int nAnds; +}; +void Acb_NtkSortSplitOutputsLimit( Gia_Man_t * p, Acb_SplitPoOrder_t * pOrder, int nPos ); +void Acb_NtkSortSplitOutputs( Gia_Man_t * p, Acb_SplitPoOrder_t * pOrder ); +int * Acb_NtkSolveCadicalPoSweepLabel( Gia_Man_t * p, int fVerbose, int * pStatus, int nSatTimeLimit, int nPoConfLimit, char * pLabel, int fStopOnUndec ); +int Acb_GiaMarkCone_rec( Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vMarks, int Mark ) +{ + int Id; + if ( Gia_ObjIsConst0(pObj) || Gia_ObjIsCi(pObj) ) + return 0; + assert( Gia_ObjIsAnd(pObj) ); + Id = Gia_ObjId( p, pObj ); + if ( Vec_IntEntry(vMarks, Id) == Mark ) + return 0; + Vec_IntWriteEntry( vMarks, Id, Mark ); + return 1 + Acb_GiaMarkCone_rec( p, Gia_ObjFanin0(pObj), vMarks, Mark ) + + Acb_GiaMarkCone_rec( p, Gia_ObjFanin1(pObj), vMarks, Mark ); +} +int Acb_GiaCountConeOverlap_rec( Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vMarks, int Mark ) +{ + int Id; + if ( Gia_ObjIsTravIdCurrent(p, pObj) ) + return 0; + Gia_ObjSetTravIdCurrent( p, pObj ); + if ( Gia_ObjIsConst0(pObj) || Gia_ObjIsCi(pObj) ) + return 0; + assert( Gia_ObjIsAnd(pObj) ); + Id = Gia_ObjId( p, pObj ); + return (int)(Vec_IntEntry(vMarks, Id) == Mark) + + Acb_GiaCountConeOverlap_rec( p, Gia_ObjFanin0(pObj), vMarks, Mark ) + + Acb_GiaCountConeOverlap_rec( p, Gia_ObjFanin1(pObj), vMarks, Mark ); +} +void Acb_GiaCollectFrontier_rec( Gia_Man_t * p, Gia_Obj_t * pObj, int LevelCut, Vec_Int_t * vFrontier ) +{ + if ( Gia_ObjIsConst0(pObj) || Gia_ObjIsCi(pObj) ) + return; + assert( Gia_ObjIsAnd(pObj) ); + if ( Gia_ObjIsTravIdCurrent(p, pObj) ) + return; + Gia_ObjSetTravIdCurrent( p, pObj ); + if ( Gia_ObjLevel(p, pObj) <= LevelCut ) + { + Vec_IntPush( vFrontier, Gia_ObjId(p, pObj) ); + return; + } + Acb_GiaCollectFrontier_rec( p, Gia_ObjFanin0(pObj), LevelCut, vFrontier ); + Acb_GiaCollectFrontier_rec( p, Gia_ObjFanin1(pObj), LevelCut, vFrontier ); +} +Vec_Int_t * Acb_GiaCollectPoFrontier( Gia_Man_t * p, int iPo, int * pLevelRoot, int * pLevelCut ) +{ + Gia_Obj_t * pRoot = Gia_ObjFanin0( Gia_ManCo(p, iPo) ); + Vec_Int_t * vFrontier = Vec_IntAlloc( 64 ); + int LevelRoot = 0, LevelCut = 0; + Gia_ManLevelNum( p ); + if ( !Gia_ObjIsConst0(pRoot) && !Gia_ObjIsCi(pRoot) ) + { + LevelRoot = Gia_ObjLevel( p, pRoot ); + LevelCut = Abc_MaxInt( 1, LevelRoot / 2 ); + Gia_ManIncrementTravId( p ); + Gia_ObjSetTravIdCurrent( p, Gia_ManConst0(p) ); + Acb_GiaCollectFrontier_rec( p, pRoot, LevelCut, vFrontier ); + } + if ( pLevelRoot ) + *pLevelRoot = LevelRoot; + if ( pLevelCut ) + *pLevelCut = LevelCut; + return vFrontier; +} +void Acb_GiaPrintHardPoFrontier( Gia_Man_t * p, int iPo, int fVerbose ) +{ + Vec_Int_t * vFrontier; + int i, iObj, LevelRoot, LevelCut, nFront, nPrint; + if ( !fVerbose ) + return; + vFrontier = Acb_GiaCollectPoFrontier( p, iPo, &LevelRoot, &LevelCut ); + nFront = Vec_IntSize( vFrontier ); + printf( " frontier: root level = %d, cut level = %d, candidates = %d", + LevelRoot, LevelCut, nFront ); + nPrint = Abc_MinInt( nFront, 8 ); + if ( nPrint ) + { + printf( ", sample obj/level/cone =" ); + for ( i = 0; i < nPrint; i++ ) + { + iObj = Vec_IntEntry( vFrontier, i ); + printf( " %d/%d/%d", iObj, Gia_ObjLevelId(p, iObj), Gia_ManConeSize(p, &iObj, 1) ); + } + } + printf( ".\n" ); + Vec_IntFree( vFrontier ); +} +int Acb_GiaDupPoFrontier_rec( Gia_Man_t * p, Gia_Man_t * pNew, Gia_Obj_t * pObj, Vec_Int_t * vFrontMarks, int * pNFrontPis ) +{ + int Id, Lit0, Lit1; + if ( Gia_ObjIsConst0(pObj) ) + return 0; + if ( ~pObj->Value ) + return pObj->Value; + Id = Gia_ObjId( p, pObj ); + if ( Gia_ObjIsCi(pObj) || Vec_IntEntry(vFrontMarks, Id) ) + { + (*pNFrontPis)++; + return pObj->Value = Gia_ManAppendCi( pNew ); + } + assert( Gia_ObjIsAnd(pObj) ); + Lit0 = Acb_GiaDupPoFrontier_rec( p, pNew, Gia_ObjFanin0(pObj), vFrontMarks, pNFrontPis ); + Lit1 = Acb_GiaDupPoFrontier_rec( p, pNew, Gia_ObjFanin1(pObj), vFrontMarks, pNFrontPis ); + return pObj->Value = Gia_ManHashAnd( pNew, Abc_LitNotCond(Lit0, Gia_ObjFaninC0(pObj)), Abc_LitNotCond(Lit1, Gia_ObjFaninC1(pObj)) ); +} +Gia_Man_t * Acb_GiaDerivePoFrontierAbstract( Gia_Man_t * p, int iPo, int LevelCut, int * pNFrontier, int * pNFrontPis ) +{ + Gia_Man_t * pNew, * pTemp; + Gia_Obj_t * pRoot = Gia_ObjFanin0( Gia_ManCo(p, iPo) ); + Vec_Int_t * vFrontier, * vMarks; + int i, iObj, Lit, LevelRoot = 0, nFrontPis = 0; + assert( iPo >= 0 && iPo < Gia_ManCoNum(p) ); + Gia_ManLevelNum( p ); + if ( !Gia_ObjIsConst0(pRoot) && !Gia_ObjIsCi(pRoot) ) + LevelRoot = Gia_ObjLevel( p, pRoot ); + if ( LevelCut <= 0 || LevelCut >= LevelRoot ) + LevelCut = Abc_MaxInt( 1, LevelRoot / 2 ); + Gia_ManIncrementTravId( p ); + Gia_ObjSetTravIdCurrent( p, Gia_ManConst0(p) ); + vFrontier = Vec_IntAlloc( 64 ); + Acb_GiaCollectFrontier_rec( p, pRoot, LevelCut, vFrontier ); + vMarks = Vec_IntStart( Gia_ManObjNum(p) ); + Vec_IntForEachEntry( vFrontier, iObj, i ) + Vec_IntWriteEntry( vMarks, iObj, 1 ); + Gia_ManFillValue( p ); + Gia_ManConst0(p)->Value = 0; + pNew = Gia_ManStart( Abc_MaxInt( 1000, 2 * Vec_IntSize(vFrontier) + 100 ) ); + pNew->pName = Abc_UtilStrsav( "frontier_abs" ); + Gia_ManHashStart( pNew ); + Lit = Acb_GiaDupPoFrontier_rec( p, pNew, pRoot, vMarks, &nFrontPis ); + Lit = Abc_LitNotCond( Lit, Gia_ObjFaninC0(Gia_ManCo(p, iPo)) ); + Gia_ManAppendCo( pNew, Lit ); + Gia_ManHashStop( pNew ); + pNew = Gia_ManCleanup( pTemp = pNew ); + Gia_ManStop( pTemp ); + if ( pNFrontier ) + *pNFrontier = Vec_IntSize( vFrontier ); + if ( pNFrontPis ) + *pNFrontPis = nFrontPis; + Vec_IntFree( vMarks ); + Vec_IntFree( vFrontier ); + return pNew; +} +int Acb_NtkTryFrontierAbstractPo( Gia_Man_t * p, int iPo, int fVerbose, int nSatTimeLimit, int iSelId, int fUseOneBranch, char * pLabel ) +{ + Gia_Obj_t * pRoot = Gia_ObjFanin0( Gia_ManCo(p, iPo) ); + int Cuts[3], c, Status = -1, nFrontier = 0, nFrontPis = 0; + int LevelRoot = 0, nAndBest = -1; + abctime clk = Abc_Clock(); + if ( nSatTimeLimit <= 0 ) + return -1; + Gia_ManLevelNum( p ); + if ( Gia_ObjIsConst0(pRoot) ) + return Gia_ObjFaninC0(Gia_ManCo(p, iPo)) ? -1 : 1; + if ( Gia_ObjIsCi(pRoot) ) + return -1; + LevelRoot = Gia_ObjLevel( p, pRoot ); + Cuts[0] = Abc_MaxInt( 1, LevelRoot / 2 ); + Cuts[1] = Abc_MaxInt( 1, (2 * LevelRoot) / 3 ); + Cuts[2] = Abc_MaxInt( 1, LevelRoot / 3 ); + for ( c = 0; c < 3; c++ ) + { + Gia_Man_t * pAbs, * pOpt = NULL, * pSolve; + int nLimit = Abc_MinInt( nSatTimeLimit, c == 0 ? 30 : 15 ); + if ( c && Cuts[c] == Cuts[c-1] ) + continue; + pAbs = Acb_GiaDerivePoFrontierAbstract( p, iPo, Cuts[c], &nFrontier, &nFrontPis ); + nAndBest = Gia_ManAndNum( pAbs ); + pOpt = nAndBest > 100 ? Gia_ManCompress2( pAbs, 1, 0 ) : NULL; + pSolve = pOpt ? pOpt : pAbs; + if ( fVerbose ) + printf( "%s frontier abstraction: selector %d branch %d output %d. level %d/%d, frontier = %d, abs PIs = %d, And = %d -> %d, limit = %d sec.\n", + pLabel, iSelId, fUseOneBranch, iPo, Cuts[c], LevelRoot, nFrontier, nFrontPis, nAndBest, Gia_ManAndNum(pSolve), nLimit ); + if ( Gia_ManAndNum(pSolve) == 0 ) + { + Gia_Obj_t * pCo = Gia_ManCo( pSolve, 0 ); + if ( Gia_ObjIsConst0(Gia_ObjFanin0(pCo)) && !Gia_ObjFaninC0(pCo) ) + Status = 1; + } + if ( Status != 1 ) + { + int StatusSat = -1; + int * pModel = Acb_NtkSolveCadicalLimit( pSolve, 0, 0, &StatusSat, nLimit, NULL, 0 ); + if ( pModel ) + ABC_FREE( pModel ); + Status = StatusSat == 1 ? 1 : -1; + } + if ( fVerbose ) + { + printf( "%s frontier abstraction: output %d %s. ", + pLabel, iPo, Status == 1 ? "UNSAT" : "inconclusive" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + if ( pOpt ) + Gia_ManStop( pOpt ); + Gia_ManStop( pAbs ); + if ( Status == 1 ) + return 1; + } + return -1; +} +int Acb_GiaSolveSmallConeInternalFrontier( Gia_Man_t * p, int fVerbose, int nSatTimeLimit ) +{ + Gia_Obj_t * pRoot; + int Cuts[8], nCuts = 0, c, LevelRoot, Status = ACB_XEC_UNDEC; + abctime clk = Abc_Clock(); + abctime clkLimit = nSatTimeLimit > 0 ? clk + nSatTimeLimit * CLOCKS_PER_SEC : 0; + if ( p == NULL || Gia_ManCoNum(p) != 1 || Gia_ManAndNum(p) <= 0 || Gia_ManAndNum(p) > 5000 || nSatTimeLimit < 10 ) + return ACB_XEC_UNDEC; + pRoot = Gia_ObjFanin0( Gia_ManCo(p, 0) ); + if ( Gia_ObjIsConst0(pRoot) || Gia_ObjIsCi(pRoot) ) + return ACB_XEC_UNDEC; + Gia_ManLevelNum( p ); + LevelRoot = Gia_ObjLevel( p, pRoot ); + if ( LevelRoot < 8 ) + return ACB_XEC_UNDEC; +#define ACB_ADD_FRONTIER_CUT(cut_) do { \ + int Cut_ = (cut_); \ + int t_; \ + if ( Cut_ > 0 && Cut_ < LevelRoot ) \ + { \ + for ( t_ = 0; t_ < nCuts; t_++ ) \ + if ( Cuts[t_] == Cut_ ) \ + break; \ + if ( t_ == nCuts && nCuts < (int)(sizeof(Cuts)/sizeof(Cuts[0])) ) \ + Cuts[nCuts++] = Cut_; \ + } \ + } while (0) + ACB_ADD_FRONTIER_CUT( LevelRoot / 4 ); + ACB_ADD_FRONTIER_CUT( LevelRoot / 3 ); + ACB_ADD_FRONTIER_CUT( LevelRoot / 2 ); + ACB_ADD_FRONTIER_CUT( (2 * LevelRoot) / 3 ); + ACB_ADD_FRONTIER_CUT( (3 * LevelRoot) / 4 ); + ACB_ADD_FRONTIER_CUT( Abc_MaxInt(1, LevelRoot - 32) ); + ACB_ADD_FRONTIER_CUT( Abc_MaxInt(1, LevelRoot - 16) ); +#undef ACB_ADD_FRONTIER_CUT + if ( fVerbose ) + printf( "Trying small-cone internal frontier proof: CI = %d. AND = %d. levels = %d. cuts = %d. limit = %d sec.\n", + Gia_ManCiNum(p), Gia_ManAndNum(p), LevelRoot, nCuts, nSatTimeLimit ); + for ( c = 0; c < nCuts; c++ ) + { + Gia_Man_t * pAbs = NULL, * pTemp = NULL, * pSolve = NULL; + int nFront = 0, nFrontPis = 0, nRemain, nThisLimit, StatusOne = ACB_XEC_UNDEC; + int * pModel = NULL; + if ( clkLimit ) + { + nRemain = (int)((clkLimit - Abc_Clock()) / CLOCKS_PER_SEC); + if ( nRemain < 5 ) + break; + } + else + nRemain = nSatTimeLimit; + nThisLimit = Abc_MinInt( nRemain, c < 3 ? 45 : 30 ); + pAbs = Acb_GiaDerivePoFrontierAbstract( p, 0, Cuts[c], &nFront, &nFrontPis ); + if ( pAbs == NULL ) + continue; + if ( Gia_ManPoIsConst0(pAbs, 0) ) + { + Status = ACB_XEC_EQ; + Gia_ManStop( pAbs ); + break; + } + if ( Gia_ManAndNum(pAbs) > 200 ) + { + pTemp = Gia_ManCompress2( pAbs, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pAbs ); + pAbs = pTemp; + pTemp = NULL; + } + } + if ( Gia_ManAndNum(pAbs) > 1000 && nThisLimit >= 20 ) + { + pTemp = Acb_NtkFraigEquivReduce( pAbs, 0, "Small-cone internal frontier", "abstraction", 32, 300, 20000, 1 ); + if ( pTemp ) + { + Gia_ManStop( pAbs ); + pAbs = pTemp; + pTemp = NULL; + } + } + pSolve = pAbs; + if ( fVerbose ) + printf( "Small-cone internal frontier proof: cut = %d/%d. frontier = %d. abs PIs = %d. And = %d. limit = %d sec.\n", + Cuts[c], LevelRoot, nFront, nFrontPis, Gia_ManAndNum(pSolve), nThisLimit ); + pModel = Acb_NtkSolveCadicalLimit( pSolve, 0, 0, &StatusOne, nThisLimit, NULL, 0 ); + ABC_FREE( pModel ); + if ( StatusOne == ACB_XEC_EQ ) + { + Status = ACB_XEC_EQ; + Gia_ManStop( pAbs ); + break; + } + Gia_ManStop( pAbs ); + } + if ( fVerbose ) + { + printf( "Small-cone internal frontier proof: %s. ", Status == ACB_XEC_EQ ? "UNSAT" : "UNDECIDED" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + return Status; +} +int * Acb_NtkSolveCadicalLocalOptPo( Gia_Man_t * p, int iPo, int fVerbose, int * pStatus, int nSatTimeLimit, int iSelId, int fUseOneBranch, char * pLabel ) +{ + Gia_Man_t * pOne, * pOpt = NULL, * pSyn = NULL, * pTemp, * pBase, * pSolve; + int Status = -1; + int fSkipped = 0; + int * pModel; + int nAndBefore, nAndAfter; + abctime clk = Abc_Clock(); + assert( iPo >= 0 && iPo < Gia_ManCoNum(p) ); + if ( nSatTimeLimit <= 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + return NULL; + } + pOne = Gia_ManDupCones( p, &iPo, 1, 0 ); + nAndBefore = Gia_ManAndNum( pOne ); + if ( fVerbose ) + printf( "%s local optimized cone: selector %d branch %d output %d. And = %d. limit = %d sec.\n", + pLabel, iSelId, fUseOneBranch, iPo, nAndBefore, nSatTimeLimit ); + pOpt = Gia_ManCompress2( pOne, 1, fVerbose ); + pBase = pOpt ? pOpt : pOne; + pSyn = Gia_ManAigSyn2( pBase, 0, 1, 0, 100, 0, 0, 0 ); + if ( pSyn ) + { + pTemp = Gia_ManCompress2( pSyn, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pSyn ); + pSyn = pTemp; + } + } + pSolve = pSyn ? pSyn : pBase; + nAndAfter = Gia_ManAndNum( pSolve ); + if ( fVerbose ) + printf( "%s local optimized cone: optimized And = %d -> %d.\n", pLabel, nAndBefore, nAndAfter ); + if ( 10 * nAndAfter > 9 * nAndBefore ) + { + if ( fVerbose ) + printf( "%s local optimized cone: output %d skipped because reduction is below 10%%. ", pLabel, iPo ); + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + fSkipped = 1; + pModel = NULL; + goto cleanup; + } + pModel = Acb_NtkSolveCadicalLimit( pSolve, 0, fVerbose, &Status, nSatTimeLimit, NULL, 0 ); +cleanup: + if ( pSyn ) + Gia_ManStop( pSyn ); + if ( pOpt ) + Gia_ManStop( pOpt ); + Gia_ManStop( pOne ); + if ( pStatus ) + *pStatus = Status; + if ( fVerbose ) + { + printf( "%s local optimized cone: output %d %s. ", + pLabel, iPo, fSkipped ? "SKIPPED" : (Status == 0 ? "SAT" : (Status == 1 ? "UNSAT" : "UNDECIDED")) ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + return pModel; +} +Vec_Int_t * Acb_GiaCollectPoConeAnds( Gia_Man_t * p, int iPo ) +{ + Vec_Int_t * vCone = Vec_IntAlloc( 1000 ); + int iObj = Gia_ObjId( p, Gia_ManCo(p, iPo) ); + Gia_ManIncrementTravId( p ); + Gia_ManCollectAnds( p, &iObj, 1, vCone, NULL ); + return vCone; +} +int Acb_GiaConeOverlapPermille( Vec_Int_t * vCone0, Vec_Int_t * vCone1, Vec_Int_t * vMarks ) +{ + Vec_Int_t * vSmall = Vec_IntSize(vCone0) <= Vec_IntSize(vCone1) ? vCone0 : vCone1; + Vec_Int_t * vLarge = Vec_IntSize(vCone0) <= Vec_IntSize(vCone1) ? vCone1 : vCone0; + int i, iObj, nInter = 0; + if ( Vec_IntSize(vSmall) == 0 ) + return Vec_IntSize(vLarge) == 0 ? 1000 : 0; + Vec_IntForEachEntry( vSmall, iObj, i ) + Vec_IntWriteEntry( vMarks, iObj, 1 ); + Vec_IntForEachEntry( vLarge, iObj, i ) + nInter += Vec_IntEntry( vMarks, iObj ); + Vec_IntForEachEntry( vSmall, iObj, i ) + Vec_IntWriteEntry( vMarks, iObj, 0 ); + return 1000 * nInter / Vec_IntSize(vSmall); +} +int Acb_GiaBuildOverlapSchedule( Gia_Man_t * p, Acb_SplitPoOrder_t * pOrder, int nMiterOuts, int * pSched, int * pGroupStart, int fVerbose, char * pLabel, Acb_XecCtx_t * pCtx ) +{ + Vec_Int_t ** ppCones = ABC_CALLOC( Vec_Int_t *, nMiterOuts ); + Vec_Int_t * vMarks = Vec_IntStart( Gia_ManObjNum(p) ); + Vec_Int_t * vCluster = Vec_IntAlloc( nMiterOuts ); + unsigned char * pUsed = ABC_CALLOC( unsigned char, nMiterOuts ); + int i, k, s, l, r, iPo, iSeedPo, nSched = 0, nGroups = 0; + for ( i = 0; i < nMiterOuts; i++ ) + { + ppCones[i] = Acb_GiaCollectPoConeAnds( p, i ); + pGroupStart[i] = 0; + } + for ( s = 0; s < nMiterOuts; s++ ) + { + int nSeedSize, nAdded = 0; + if ( pUsed[s] ) + continue; + Vec_IntClear( vCluster ); + pGroupStart[nSched] = 1; + Vec_IntPush( vCluster, s ); + pUsed[s] = 1; + nGroups++; + iSeedPo = pOrder[s].iPo; + nSeedSize = Vec_IntSize( ppCones[iSeedPo] ); + for ( k = s + 1; k < nMiterOuts; k++ ) + { + int nSize, nMin, nMax, nOverlap; + if ( pUsed[k] ) + continue; + iPo = pOrder[k].iPo; + nSize = Vec_IntSize( ppCones[iPo] ); + nMin = Abc_MinInt( nSeedSize, nSize ); + nMax = Abc_MaxInt( nSeedSize, nSize ); + if ( nMax == 0 || 1000 * nMin < pCtx->Pars.nOverlapSizePermille * nMax ) + continue; + nOverlap = Acb_GiaConeOverlapPermille( ppCones[iSeedPo], ppCones[iPo], vMarks ); + if ( nOverlap < pCtx->Pars.nOverlapMinPermille ) + continue; + Vec_IntPush( vCluster, k ); + pUsed[k] = 1; + nAdded++; + } + for ( l = 0, r = Vec_IntSize(vCluster) - 1; l <= r; l++, r-- ) + { + if ( nGroups == 1 ) + { + pSched[nSched++] = Vec_IntEntry( vCluster, l ); + if ( l < r ) + pSched[nSched++] = Vec_IntEntry( vCluster, r ); + } + else + { + pSched[nSched++] = Vec_IntEntry( vCluster, r ); + if ( l < r ) + pSched[nSched++] = Vec_IntEntry( vCluster, l ); + } + } + if ( fVerbose && nAdded ) + printf( "%s support-overlap cluster %d: seed output %d, members = %d.\n", + pLabel, nGroups - 1, iSeedPo, nAdded + 1 ); + } + if ( fVerbose ) + printf( "%s support-overlap clustering: outputs = %d. clusters = %d. overlap >= %d.%d%%, size ratio >= %d.%d%%.\n", + pLabel, nMiterOuts, nGroups, + pCtx->Pars.nOverlapMinPermille / 10, pCtx->Pars.nOverlapMinPermille % 10, + pCtx->Pars.nOverlapSizePermille / 10, pCtx->Pars.nOverlapSizePermille % 10 ); + for ( i = 0; i < nMiterOuts; i++ ) + Vec_IntFree( ppCones[i] ); + ABC_FREE( ppCones ); + ABC_FREE( pUsed ); + Vec_IntFree( vCluster ); + Vec_IntFree( vMarks ); + return nGroups; +} +int * Acb_NtkSolveCadicalSelectorBranch( Gia_Man_t * p, int nMiterOuts, int fUseOneBranch, int fVerbose, int * pStatus, int nSatTimeLimit, int iSelId, int fStopOnUndec, char * pLabel, Acb_XecCtx_t * pCtx ) +{ + int * pModel = NULL; + int * pPoStatus = NULL, * pPoCone = NULL, * pPoConf = NULL, * pPoLearn = NULL, * pPoTime = NULL, * pPoSlot = NULL; + int * pPoConfTotal = NULL, * pPoLearnTotal = NULL; + Acb_SplitPoOrder_t * pOrder = NULL; + Gia_Man_t * pCond = NULL, * pOpt = NULL, * pSweep = NULL, * pCnfGia = NULL; + Aig_Man_t * pMan = NULL; + Cnf_Dat_t * pCnf = NULL; + cadical_solver * pSat = NULL; + int Lit, Status = 0, i, nUnsat = 0, nUndec = 0; + int nGroups = 0, nIsolations = 0; + int * pSched = NULL, * pGroupStart = NULL, nSched = 0; + int nConflicts = 0, nLearned = 0; + int nMinOutTime = nSatTimeLimit > 0 ? Abc_MinInt( pCtx->Pars.nBranchMinOutputSec, Abc_MaxInt( 1, nSatTimeLimit / 10 ) ) : pCtx->Pars.nBranchMinOutputSec; + int nAndCond; + abctime clk = Abc_Clock(); + abctime clkLimit = nSatTimeLimit > 0 ? clk + nSatTimeLimit * CLOCKS_PER_SEC : 0; + assert( nMiterOuts > 0 ); + assert( pCtx != NULL ); + Acb_XecCtxResetBranchSweep( pCtx, nMiterOuts ); + assert( Gia_ManCoNum(p) == nMiterOuts + 2 ); + pCond = Acb_GiaDeriveBranchConditionMiter( p, nMiterOuts, fUseOneBranch ); + nAndCond = Gia_ManAndNum( pCond ); + pOpt = nAndCond > 1000 ? Gia_ManCompress2( pCond, 1, 0 ) : NULL; + pCnfGia = pOpt ? pOpt : pCond; + pSweep = Acb_NtkBranchSweepReduce( pCnfGia, fVerbose, pLabel ); + if ( pSweep ) + pCnfGia = pSweep; + pMan = Gia_ManToAig( pCnfGia, 0 ); + pCnf = pMan ? Cnf_Derive( pMan, Aig_ManCoNum(pMan) ) : NULL; + if ( pCnf == NULL ) + { + Status = 0; + nUndec++; + goto cleanup; + } + if ( fVerbose ) + printf( "%s conditioned grouped CaDiCaL sweep: selector %d branch %d. outputs = %d. And = %d -> %d. CNF var = %d. cla = %d.\n", + pLabel, iSelId, fUseOneBranch, nMiterOuts, nAndCond, Gia_ManAndNum(pCnfGia), pCnf->nVars, pCnf->nClauses ); + pOrder = ABC_ALLOC( Acb_SplitPoOrder_t, nMiterOuts ); + Acb_NtkSortSplitOutputsLimit( pCnfGia, pOrder, nMiterOuts ); + pPoStatus = ABC_ALLOC( int, nMiterOuts ); + pPoCone = ABC_ALLOC( int, nMiterOuts ); + pPoConf = ABC_ALLOC( int, nMiterOuts ); + pPoLearn = ABC_ALLOC( int, nMiterOuts ); + pPoTime = ABC_ALLOC( int, nMiterOuts ); + pPoSlot = ABC_ALLOC( int, nMiterOuts ); + pPoConfTotal = ABC_ALLOC( int, nMiterOuts ); + pPoLearnTotal = ABC_ALLOC( int, nMiterOuts ); + for ( i = 0; i < nMiterOuts; i++ ) + { + pPoStatus[i] = 2; + pPoCone[i] = 0; + pPoConf[i] = 0; + pPoLearn[i] = 0; + pPoTime[i] = 0; + pPoSlot[i] = 0; + pPoConfTotal[i] = 0; + pPoLearnTotal[i] = 0; + } + for ( i = 0; i < nMiterOuts; i++ ) + { + pPoCone[pOrder[i].iPo] = pOrder[i].nAnds; + pPoSlot[pOrder[i].iPo] = i + 1; + } + if ( fVerbose ) + printf( "%s output order: smallest cone %d ANDs, largest cone %d ANDs.\n", + pLabel, pOrder[0].nAnds, pOrder[nMiterOuts-1].nAnds ); + pSched = ABC_ALLOC( int, nMiterOuts ); + pGroupStart = ABC_ALLOC( int, nMiterOuts ); + nGroups = Acb_GiaBuildOverlapSchedule( pCnfGia, pOrder, nMiterOuts, pSched, pGroupStart, fVerbose, pLabel, pCtx ); + nSched = nMiterOuts; + if ( fVerbose ) + { + int nPrint = Abc_MinInt( nSched, pCtx->Pars.nBranchSchedulePrintMax ); + printf( "%s support-overlap schedule: selector %d branch %d outputs = %d. order =", pLabel, iSelId, fUseOneBranch, nSched ); + for ( i = 0; i < nPrint; i++ ) + { + if ( pGroupStart[i] ) + printf( " |" ); + printf( " %d", pOrder[pSched[i]].iPo ); + } + if ( nPrint < nSched ) + printf( " ..." ); + printf( ".\n" ); + } + for ( i = 0; i < nSched; i++ ) + { + abctime clkOut = Abc_Clock(); + int iPos = pSched[i]; + int iPo = pOrder[iPos].iPo; + int nConflictsBeg, nLearnedBeg; + int fTryLocalOpt = iPos >= nMiterOuts/2 && iPos <= nMiterOuts - 4 && pOrder[iPos].nAnds >= pCtx->Pars.nBranchLocalOptAndMin; + if ( pGroupStart[i] || pSat == NULL ) + { + if ( pSat ) + cadical_solver_delete( pSat ); + pSat = cadical_solver_new(); + if ( pSat == NULL || !Acb_CnfWriteIntoCadical( pSat, pCnf ) ) + { + Status = 0; + nUndec++; + break; + } + if ( fVerbose ) + printf( "%s grouped CaDiCaL: starting overlap cluster at output %d.\n", + pLabel, iPo ); + } + nConflictsBeg = cadical_solver_nconflicts(pSat); + nLearnedBeg = cadical_solver_nlearned(pSat); + if ( clkLimit && Abc_Clock() >= clkLimit ) + { + Status = 0; + nUndec++; + break; + } + if ( clkLimit && clkLimit - Abc_Clock() < nMinOutTime * CLOCKS_PER_SEC ) + { + Status = 0; + nUndec++; + if ( fVerbose ) + printf( "%s grouped CaDiCaL: skipping output %d because remaining branch budget is below %d sec.\n", + pLabel, iPo, nMinOutTime ); + break; + } + if ( fTryLocalOpt && clkLimit ) + { + int StatusLocal = -1; + int nRemain = (int)((clkLimit - Abc_Clock()) / CLOCKS_PER_SEC); + int nLocalLimit = Abc_MinInt( pCtx->Pars.nBranchLocalOptSec, nRemain - nMinOutTime ); + int nAbsLimit = Abc_MinInt( pCtx->Pars.nBranchFrontierAbsSec, nRemain - nMinOutTime ); + if ( nAbsLimit >= 15 && Acb_NtkTryFrontierAbstractPo( pCnfGia, iPo, fVerbose, nAbsLimit, iSelId, fUseOneBranch, pLabel ) == 1 ) + { + pPoStatus[iPo] = -1; + pPoConf[iPo] = 0; + pPoLearn[iPo] = 0; + pPoConfTotal[iPo] = nConflictsBeg; + pPoLearnTotal[iPo] = nLearnedBeg; + pPoTime[iPo] = (int)((Abc_Clock() - clkOut + CLOCKS_PER_SEC/2) / CLOCKS_PER_SEC); + Status = -1; + nUnsat++; + if ( fVerbose ) + printf( "%s grouped CaDiCaL: output %d UNSAT by frontier abstraction; skipping grouped assumption.\n", pLabel, iPo ); + continue; + } + if ( nLocalLimit >= nMinOutTime ) + { + int * pLocalModel = Acb_NtkSolveCadicalLocalOptPo( pCnfGia, iPo, fVerbose, &StatusLocal, nLocalLimit, iSelId, fUseOneBranch, pLabel ); + pPoStatus[iPo] = StatusLocal == 0 ? 1 : (StatusLocal == 1 ? -1 : 0); + pPoConf[iPo] = 0; + pPoLearn[iPo] = 0; + pPoConfTotal[iPo] = nConflictsBeg; + pPoLearnTotal[iPo] = nLearnedBeg; + pPoTime[iPo] = (int)((Abc_Clock() - clkOut + CLOCKS_PER_SEC/2) / CLOCKS_PER_SEC); + if ( StatusLocal == 0 ) + { + pModel = pLocalModel; + Status = 1; + break; + } + if ( pLocalModel ) + ABC_FREE( pLocalModel ); + if ( StatusLocal == 1 ) + { + Status = -1; + nUnsat++; + if ( fVerbose ) + printf( "%s grouped CaDiCaL: output %d UNSAT by local optimized cone; skipping grouped assumption.\n", pLabel, iPo ); + continue; + } + } + } + { + int RetLit = Acb_CnfCoDriverLit( pCnf, iPo, &Lit ); + if ( RetLit == -2 ) + { + Status = 0; + nUndec++; + pPoStatus[iPo] = Status; + pPoConf[iPo] = 0; + pPoLearn[iPo] = 0; + pPoConfTotal[iPo] = cadical_solver_nconflicts(pSat); + pPoLearnTotal[iPo] = cadical_solver_nlearned(pSat); + pPoTime[iPo] = (int)((Abc_Clock() - clkOut + CLOCKS_PER_SEC/2) / CLOCKS_PER_SEC); + if ( fVerbose ) + printf( "%s grouped CaDiCaL: output %d UNDECIDED because its CNF driver is unmapped.\n", pLabel, iPo ); + break; + } + if ( RetLit == -1 ) + { + Status = -1; + nUnsat++; + pPoStatus[iPo] = Status; + pPoConf[iPo] = 0; + pPoLearn[iPo] = 0; + pPoConfTotal[iPo] = cadical_solver_nconflicts(pSat); + pPoLearnTotal[iPo] = cadical_solver_nlearned(pSat); + pPoTime[iPo] = (int)((Abc_Clock() - clkOut + CLOCKS_PER_SEC/2) / CLOCKS_PER_SEC); + if ( fVerbose ) + printf( "%s grouped CaDiCaL: output %d UNSAT because it is constant 0.\n", pLabel, iPo ); + continue; + } + if ( RetLit == 0 ) + { + Status = 1; + Lit = -1; + } + } + if ( fVerbose ) + printf( "%s grouped CaDiCaL: selector %d branch %d output %d (%d/%d), cone = %d ANDs.\n", + pLabel, iSelId, fUseOneBranch, iPo, iPos + 1, nMiterOuts, pOrder[iPos].nAnds ); + if ( Status != 1 ) + Status = cadical_solver_solve( pSat, &Lit, &Lit + 1, 0, 0, 0, 0 ); + nConflicts = cadical_solver_nconflicts(pSat); + nLearned = cadical_solver_nlearned(pSat); + pPoStatus[iPo] = Status; + pPoConf[iPo] = nConflicts - nConflictsBeg; + pPoLearn[iPo] = nLearned - nLearnedBeg; + pPoConfTotal[iPo] = nConflicts; + pPoLearnTotal[iPo] = nLearned; + pPoTime[iPo] = (int)((Abc_Clock() - clkOut + CLOCKS_PER_SEC/2) / CLOCKS_PER_SEC); + if ( Status == 1 ) + { + Aig_Obj_t * pObj; + pModel = ABC_ALLOC( int, Aig_ManCiNum(pMan) ); + Aig_ManForEachCi( pMan, pObj, iPo ) + pModel[iPo] = cadical_solver_get_var_value( pSat, pCnf->pVarNums[pObj->Id] ); + break; + } + if ( Status == -1 ) + { + nUnsat++; + if ( fVerbose ) + { + printf( "%s grouped CaDiCaL: output %d UNSAT. delta conflicts = %d. delta learned = %d. total conflicts = %d. total learned = %d. ", + pLabel, iPo, pPoConf[iPo], pPoLearn[iPo], nConflicts, nLearned ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clkOut ); + } + } + else + { + nUndec++; + if ( fVerbose ) + { + printf( "%s grouped CaDiCaL: output %d UNDECIDED. delta conflicts = %d. delta learned = %d. total conflicts = %d. total learned = %d. ", + pLabel, iPo, pPoConf[iPo], pPoLearn[iPo], nConflicts, nLearned ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clkOut ); + } + if ( fStopOnUndec ) + break; + } + if ( Status != -1 || pPoConf[iPo] >= pCtx->Pars.nBranchHardConflictMin || pPoTime[iPo] >= pCtx->Pars.nBranchHardTimeMin ) + { + if ( Status == -1 ) + nIsolations++; + if ( fVerbose && Status == -1 ) + printf( "%s hard-output isolation: resetting solver after output %d. delta conflicts = %d, time = %d sec.\n", + pLabel, iPo, pPoConf[iPo], pPoTime[iPo] ); + cadical_solver_delete( pSat ); + pSat = NULL; + } + } + if ( pStatus ) + *pStatus = Status == 1 ? 0 : (nUndec ? -1 : 1); + printf( "The selector %d branch %d is %s by %s CaDiCaL. ", + iSelId, fUseOneBranch, Status == 1 ? "SAT" : (nUndec ? "UNDECIDED" : "UNSAT"), pLabel ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + for ( i = 0; i < nMiterOuts; i++ ) + if ( pPoStatus && (pPoStatus[i] == 2 || pPoStatus[i] != -1 || pPoConf[i] >= pCtx->Pars.nBranchHardConflictMin || pPoTime[i] >= pCtx->Pars.nBranchHardTimeMin) ) + Vec_IntPushUnique( pCtx->vLastBranchHardPos, i ); + if ( fVerbose ) + { + int nHard = 0; + printf( "%s grouped CaDiCaL stats: overlap clusters = %d. hard isolations = %d. SAT = %d. UNSAT = %d. UNDEC = %d. last-group conflicts = %d. last-group learned = %d.\n", + pLabel, nGroups, nIsolations, Status == 1, nUnsat, nUndec, nConflicts, nLearned ); + for ( i = 0; i < nMiterOuts; i++ ) + if ( pPoStatus[i] != 2 && (pPoStatus[i] != -1 || pPoConf[i] >= pCtx->Pars.nBranchHardConflictMin || pPoTime[i] >= pCtx->Pars.nBranchHardTimeMin) ) + nHard++; + if ( nHard ) + { + printf( "%s hard-output summary: selector %d branch %d. thresholds: delta conflicts >= %d OR time >= %d sec OR non-UNSAT.\n", + pLabel, iSelId, fUseOneBranch, pCtx->Pars.nBranchHardConflictMin, pCtx->Pars.nBranchHardTimeMin ); + for ( i = 0; i < nMiterOuts; i++ ) + if ( pPoStatus[i] != 2 && (pPoStatus[i] != -1 || pPoConf[i] >= pCtx->Pars.nBranchHardConflictMin || pPoTime[i] >= pCtx->Pars.nBranchHardTimeMin) ) + { + printf( " output %d: status = %s, sorted slot = %d/%d, cone = %d ANDs, delta conflicts = %d, delta learned = %d, total conflicts = %d, total learned = %d, time = %d sec.\n", + i, pPoStatus[i] == 1 ? "SAT" : (pPoStatus[i] == -1 ? "UNSAT" : "UNDECIDED"), + pPoSlot[i], nMiterOuts, pPoCone[i], pPoConf[i], pPoLearn[i], pPoConfTotal[i], pPoLearnTotal[i], pPoTime[i] ); + Acb_GiaPrintHardPoFrontier( pCnfGia, i, fVerbose ); + } + } + } +cleanup: + if ( pPoStatus ) + ABC_FREE( pPoStatus ); + if ( pPoCone ) + ABC_FREE( pPoCone ); + if ( pPoConf ) + ABC_FREE( pPoConf ); + if ( pPoLearn ) + ABC_FREE( pPoLearn ); + if ( pPoTime ) + ABC_FREE( pPoTime ); + if ( pPoSlot ) + ABC_FREE( pPoSlot ); + if ( pPoConfTotal ) + ABC_FREE( pPoConfTotal ); + if ( pPoLearnTotal ) + ABC_FREE( pPoLearnTotal ); + if ( pSched ) + ABC_FREE( pSched ); + if ( pGroupStart ) + ABC_FREE( pGroupStart ); + if ( pOrder ) + ABC_FREE( pOrder ); + if ( pSat ) + cadical_solver_delete( pSat ); + if ( pCnf ) + Cnf_DataFree( pCnf ); + if ( pMan ) + Aig_ManStop( pMan ); + if ( pOpt ) + Gia_ManStop( pOpt ); + if ( pSweep ) + Gia_ManStop( pSweep ); + if ( pCond ) + Gia_ManStop( pCond ); + return pModel; +} +int * Acb_NtkSolveHmuxBranches( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, Vec_Int_t * vCutObjsG, Vec_Int_t * vMuxSelectorsG, Vec_Int_t * vMuxPoSelIdsG, Vec_Int_t * vIntDcObjsG, Vec_Int_t * vIntDcCtrlsG, Vec_Int_t * vIntDcCtrlIdsG, int fVerbose, int * pStatus, Acb_XecCtx_t * pCtx ) +{ + int iSel, fOne, Status, fUndec = 0; + int * pModel = NULL; + abctime clk = Abc_Clock(); + if ( fVerbose ) + printf( "Trying HMUX branch-level proving: selectors = %d.\n", Vec_IntSize(vMuxSelectorsG) ); + for ( iSel = 0; iSel < Vec_IntSize(vMuxSelectorsG); iSel++ ) + { + for ( fOne = 0; fOne <= 1; fOne++ ) + { + Vec_Int_t * vFTargets = Acb_NtkCollectCoDriversForSelector( pNtkF, vMuxPoSelIdsG, iSel ); + Vec_Int_t * vGTargets = Acb_NtkCollectPoMuxBranchTargets( pNtkG, vCutObjsG, vMuxPoSelIdsG, iSel, fOne ); + Gia_Man_t * pGiaFBranch = NULL, * pGiaGBranch = NULL, * pGiaBranch = NULL, * pGiaCond = NULL; + int fTriedBranchWhole = 0; + assert( Vec_IntSize(vFTargets) == Vec_IntSize(vGTargets) ); + Vec_IntPush( vGTargets, Vec_IntEntry(vMuxSelectorsG, iSel) ); + pGiaFBranch = Acb_NtkGiaDeriveDualTargets( pNtkF, vFTargets ); + pGiaGBranch = Acb_NtkGiaDeriveDualTargets( pNtkG, vGTargets ); + pGiaBranch = Acb_NtkGiaDeriveMiterWithSecondExtras( pGiaFBranch, pGiaGBranch, 1 ); + if ( fVerbose ) + printf( "HMUX branch miter: selector %d branch %d. And = %d. PO = %d.\n", + iSel, fOne, Gia_ManAndNum(pGiaBranch), Gia_ManPoNum(pGiaBranch) ); + Status = -1; + if ( Vec_IntSize(vFTargets) >= 16 && Gia_ManAndNum(pGiaBranch) <= 30000 ) + { + int nWholeLimit = Vec_IntSize(vFTargets) >= 24 ? 60 : 450; + pGiaCond = Acb_GiaDeriveBranchConditionMiter( pGiaBranch, Vec_IntSize(vFTargets), fOne ); + if ( fVerbose ) + printf( "HMUX branch whole-miter try: selector %d branch %d. And = %d. PO = %d. limit = %d sec.\n", + iSel, fOne, Gia_ManAndNum(pGiaCond), Gia_ManPoNum(pGiaCond), nWholeLimit ); + fTriedBranchWhole = 1; + pModel = Acb_NtkSolveCadicalLimit( pGiaCond, 0, fVerbose, &Status, nWholeLimit, "HMUX branch whole-miter CaDiCaL", 0 ); + Gia_ManStop( pGiaCond ); + pGiaCond = NULL; + if ( Status == -1 && fVerbose ) + printf( "HMUX branch whole-miter CaDiCaL was UNDECIDED; skipping duplicate grouped branch sweep.\n" ); + } + if ( Status == -1 && !fTriedBranchWhole ) + pModel = Acb_NtkSolveCadicalSelectorBranch( pGiaBranch, Vec_IntSize(vFTargets), fOne, fVerbose, &Status, 1200, iSel, 0, "HMUX branch", pCtx ); + Gia_ManStop( pGiaBranch ); + Gia_ManStop( pGiaGBranch ); + Gia_ManStop( pGiaFBranch ); + Vec_IntFree( vGTargets ); + Vec_IntFree( vFTargets ); + if ( Status == 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + if ( Status == -1 ) + { + int StatusDc = -1; + if ( vIntDcObjsG && vIntDcCtrlsG && vIntDcCtrlIdsG && + Vec_IntSize(vIntDcObjsG) > 0 && Vec_IntSize(vIntDcCtrlsG) > 4 && + pCtx->vLastBranchHardPos && Vec_IntSize(pCtx->vLastBranchHardPos) > 0 ) + { + Vec_Int_t * vPoIds = Acb_NtkCollectPoIdsForSelector( pNtkF, vMuxPoSelIdsG, iSel ); + Vec_Int_t * vHardOrig = Vec_IntAlloc( Vec_IntSize(pCtx->vLastBranchHardPos) ); + int iHardLocal, k; + Vec_IntForEachEntry( pCtx->vLastBranchHardPos, iHardLocal, k ) + if ( iHardLocal >= 0 && iHardLocal < Vec_IntSize(vPoIds) ) + Vec_IntPushUnique( vHardOrig, Vec_IntEntry(vPoIds, iHardLocal) ); + if ( fVerbose ) + printf( "HMUX branch selector %d branch %d collected %d hard/unvisited local outputs -> %d original outputs for targeted DC-control proof.\n", + iSel, fOne, Vec_IntSize(pCtx->vLastBranchHardPos), Vec_IntSize(vHardOrig) ); + if ( Vec_IntSize(vHardOrig) <= 4 && Vec_IntSize(vIntDcCtrlsG) <= 8 ) + pModel = Acb_NtkSolveMuxDcControlTargetList( pNtkF, pNtkG, vHardOrig, + vCutObjsG, vMuxSelectorsG, vMuxPoSelIdsG, fOne, + vIntDcObjsG, vIntDcCtrlsG, vIntDcCtrlIdsG, fVerbose, &StatusDc, 90 ); + else if ( fVerbose ) + printf( "Skipping HMUX+DC targeted recursive proof: hard outputs = %d, DC controls = %d; recursion is too broad for this branch.\n", + Vec_IntSize(vHardOrig), Vec_IntSize(vIntDcCtrlsG) ); + Vec_IntFree( vHardOrig ); + Vec_IntFree( vPoIds ); + if ( StatusDc == 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + if ( StatusDc == 1 ) + Status = 1; + } + if ( Status == -1 ) + fUndec = 1; + } + } + } + if ( pStatus ) + *pStatus = fUndec ? -1 : 1; + printf( "The networks are %s by HMUX branch-level proving. ", fUndec ? "UNDECIDED" : "equivalent" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return NULL; +} +int * Acb_NtkSolveHmuxCompleteCubes( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, Vec_Int_t * vCutObjsG, Vec_Int_t * vMuxSelectorsG, Vec_Int_t * vMuxPoSelIdsG, int fVerbose, int * pStatus, int nTotalLimit, int nCubeLimit ) +{ + Vec_Int_t * vFTargets = NULL, * vGTargets = NULL, * vCubeVals = NULL; + Gia_Man_t * pGiaF = NULL, * pGiaG = NULL, * pGiaMiter = NULL, * pGiaCond = NULL, * pTemp = NULL; + int nSels, nCubes, iCube, iSel, Status = ACB_XEC_UNDEC, StatusAll = ACB_XEC_EQ, * pModel = NULL; + abctime clk = Abc_Clock(); + abctime clkLimit = nTotalLimit > 0 ? clk + nTotalLimit * CLOCKS_PER_SEC : 0; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( vCutObjsG == NULL || vMuxSelectorsG == NULL || vMuxPoSelIdsG == NULL ) + return NULL; + nSels = Vec_IntSize( vMuxSelectorsG ); + if ( nSels <= 0 || nSels > 4 || Vec_IntSize(vCutObjsG) != Acb_NtkCoNum(pNtkG) ) + return NULL; + nCubes = 1 << nSels; + vFTargets = Acb_NtkCollectCoDrivers( pNtkF ); + pGiaF = Acb_NtkGiaDeriveDualTargets( pNtkF, vFTargets ); + vCubeVals = Vec_IntAlloc( nSels ); + if ( fVerbose ) + printf( "Trying complete HMUX selector-cube proof: selectors = %d. cubes = %d. outputs = %d. total limit = %d sec.\n", + nSels, nCubes, Acb_NtkCoNum(pNtkF), nTotalLimit ); + for ( iCube = 0; iCube < nCubes; iCube++ ) + { + int nThisLimit = nCubeLimit; + if ( clkLimit ) + { + int nRemain = (int)((clkLimit - Abc_Clock()) / CLOCKS_PER_SEC); + if ( nRemain <= 0 ) + { + StatusAll = ACB_XEC_UNDEC; + break; + } + nThisLimit = nCubeLimit > 0 ? Abc_MinInt( nCubeLimit, nRemain ) : nRemain; + } + Vec_IntClear( vCubeVals ); + for ( iSel = 0; iSel < nSels; iSel++ ) + Vec_IntPush( vCubeVals, (iCube >> iSel) & 1 ); + vGTargets = Acb_NtkCollectPoMuxCubeTargets( pNtkG, vCutObjsG, vMuxPoSelIdsG, vCubeVals ); + Vec_IntAppend( vGTargets, vMuxSelectorsG ); + pGiaG = Acb_NtkGiaDeriveDualTargets( pNtkG, vGTargets ); + pGiaMiter = Acb_NtkGiaDeriveMiterWithSecondExtras( pGiaF, pGiaG, nSels ); + pGiaCond = Acb_GiaDeriveCubeConditionMiter( pGiaMiter, Acb_NtkCoNum(pNtkF), vCubeVals ); + if ( Gia_ManAndNum(pGiaCond) > 5000 ) + { + pTemp = Gia_ManCompress2( pGiaCond, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pGiaCond ); + pGiaCond = pTemp; + pTemp = NULL; + } + } + if ( Gia_ManAndNum(pGiaCond) > 8000 && Gia_ManAndNum(pGiaCond) < 70000 ) + { + pTemp = Acb_NtkFraigEquivReduce( pGiaCond, fVerbose, "Complete HMUX selector cube", "conditioned cube", 32, 300, 12000, Abc_MaxInt( 50, Gia_ManAndNum(pGiaCond) / 200 ) ); + if ( pTemp ) + { + Gia_ManStop( pGiaCond ); + pGiaCond = pTemp; + pTemp = NULL; + } + } + if ( fVerbose ) + { + printf( "Complete HMUX selector cube %d/%d: values =", iCube + 1, nCubes ); + for ( iSel = 0; iSel < nSels; iSel++ ) + printf( " s%d=%d", iSel, Vec_IntEntry(vCubeVals, iSel) ); + printf( ". And = %d. PO = %d. limit = %d sec.\n", Gia_ManAndNum(pGiaCond), Gia_ManCoNum(pGiaCond), nThisLimit ); + } + pModel = Acb_NtkSolveCadicalPoSweepLabel( pGiaCond, fVerbose, &Status, nThisLimit, 500000, "complete HMUX selector-cube PO sweep", 0 ); + if ( Status == ACB_XEC_UNDEC ) + { + if ( fVerbose ) + printf( "Complete HMUX selector cube %d/%d PO sweep was inconclusive; skipping duplicate whole-cube CaDiCaL.\n", + iCube + 1, nCubes ); + } + Gia_ManStop( pGiaCond ); pGiaCond = NULL; + Gia_ManStop( pGiaMiter ); pGiaMiter = NULL; + Gia_ManStop( pGiaG ); pGiaG = NULL; + Vec_IntFreeP( &vGTargets ); + if ( Status == ACB_XEC_NEQ ) + { + StatusAll = ACB_XEC_NEQ; + break; + } + if ( Status != ACB_XEC_EQ ) + { + StatusAll = ACB_XEC_UNDEC; + break; + } + } + if ( pStatus ) + *pStatus = StatusAll; + if ( StatusAll == ACB_XEC_EQ ) + { + printf( "The networks are equivalent by complete HMUX selector-cube proof. " ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + else if ( fVerbose ) + { + printf( "The networks are %s by complete HMUX selector-cube proof. ", + StatusAll == ACB_XEC_NEQ ? "NOT equivalent" : "UNDECIDED" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + Vec_IntFreeP( &vFTargets ); + Vec_IntFreeP( &vGTargets ); + Vec_IntFreeP( &vCubeVals ); + if ( pGiaF ) Gia_ManStop( pGiaF ); + if ( pGiaG ) Gia_ManStop( pGiaG ); + if ( pGiaMiter ) Gia_ManStop( pGiaMiter ); + if ( pGiaCond ) Gia_ManStop( pGiaCond ); + if ( pTemp ) Gia_ManStop( pTemp ); + return pModel; +} +int * Acb_NtkSolveMuxTargetBranches( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, int iPo, Vec_Int_t * vCutObjsG, Vec_Int_t * vMuxSelectorsG, Vec_Int_t * vMuxPoSelIdsG, int fVerbose, int * pStatus, int nBranchLimit, int nMaxBranchAnd ) +{ + Vec_Int_t * vFTargets = Vec_IntAlloc( 1 ); + Acb_XecCtx_t BranchCtx; + int iSel, fOne, Status, fUndec = 0; + int * pModel = NULL; + abctime clk = Abc_Clock(); + Acb_XecCtxInit( &BranchCtx ); + assert( iPo >= 0 && iPo < Acb_NtkCoNum(pNtkF) && iPo < Acb_NtkCoNum(pNtkG) ); + assert( vCutObjsG && vMuxSelectorsG && vMuxPoSelIdsG ); + assert( Vec_IntSize(vCutObjsG) == Acb_NtkCoNum(pNtkG) ); + assert( Vec_IntSize(vMuxPoSelIdsG) == Acb_NtkCoNum(pNtkG) ); + iSel = Vec_IntEntry( vMuxPoSelIdsG, iPo ); + if ( iSel < 0 || iSel >= Vec_IntSize(vMuxSelectorsG) ) + { + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + Vec_IntFree( vFTargets ); + Acb_XecCtxFree( &BranchCtx ); + return NULL; + } + Vec_IntPush( vFTargets, Acb_ObjFanin(pNtkF, Acb_NtkCo(pNtkF, iPo), 0) ); + if ( fVerbose ) + printf( "Trying MUX target branch proving: output = %d. selector = %d/%d.\n", + iPo, iSel, Vec_IntSize(vMuxSelectorsG) ); + for ( fOne = 0; fOne <= 1; fOne++ ) + { + Vec_Int_t * vGTargets = Vec_IntAlloc( 2 ); + Gia_Man_t * pGiaFBranch = NULL, * pGiaGBranch = NULL, * pGiaBranch = NULL; + int iMux = Vec_IntEntry( vCutObjsG, iPo ); + assert( !Acb_ObjIsCio(pNtkG, iMux) && Acb_ObjType(pNtkG, iMux) == ABC_OPER_BIT_MUX ); + Vec_IntPush( vGTargets, Acb_ObjFanin(pNtkG, iMux, fOne ? 1 : 0) ); + Vec_IntPush( vGTargets, Vec_IntEntry(vMuxSelectorsG, iSel) ); + pGiaFBranch = Acb_NtkGiaDeriveDualTargets( pNtkF, vFTargets ); + pGiaGBranch = Acb_NtkGiaDeriveDualTargets( pNtkG, vGTargets ); + pGiaBranch = Acb_NtkGiaDeriveMiterWithSecondExtras( pGiaFBranch, pGiaGBranch, 1 ); + if ( fVerbose ) + printf( "MUX target branch miter: output %d selector %d branch %d. And = %d. PO = %d.\n", + iPo, iSel, fOne, Gia_ManAndNum(pGiaBranch), Gia_ManPoNum(pGiaBranch) ); + if ( nMaxBranchAnd > 0 && Gia_ManAndNum(pGiaBranch) >= nMaxBranchAnd ) + { + if ( fVerbose ) + printf( "Skipping MUX target branch output %d selector %d/%d because branch miter is not smaller than current hard cone: branch And = %d, current And = %d.\n", + iPo, iSel, fOne, Gia_ManAndNum(pGiaBranch), nMaxBranchAnd ); + Status = -1; + fUndec = 1; + } + else + pModel = Acb_NtkSolveCadicalSelectorBranch( pGiaBranch, 1, fOne, fVerbose, &Status, nBranchLimit, iSel, 0, "MUX target branch", &BranchCtx ); + Gia_ManStop( pGiaBranch ); + Gia_ManStop( pGiaGBranch ); + Gia_ManStop( pGiaFBranch ); + Vec_IntFree( vGTargets ); + if ( Status == 0 ) + { + Vec_IntFree( vFTargets ); + Acb_XecCtxFree( &BranchCtx ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + if ( Status == -1 ) + fUndec = 1; + } + Vec_IntFree( vFTargets ); + if ( pStatus ) + *pStatus = fUndec ? ACB_XEC_UNDEC : ACB_XEC_EQ; + printf( "The hard output %d is %s by MUX target branch proving. ", + iPo, fUndec ? "UNDECIDED" : "UNSAT" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + Acb_XecCtxFree( &BranchCtx ); + return NULL; +} +int * Acb_NtkSolveDcControlBranchesLimit( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, Vec_Int_t * vDcObjsG, Vec_Int_t * vDcCtrlsG, Vec_Int_t * vDcCtrlIdsG, int fVerbose, int * pStatus, int nBranchLimit, int fStopOnUndec, int nMaxBranchAnd ) +{ + Vec_Int_t * vFTargets = Acb_NtkCollectCoDrivers( pNtkF ); + Acb_XecCtx_t BranchCtx; + int iCtrl, fOne, Status, fUndec = 0; + int * pModel = NULL; + abctime clk = Abc_Clock(); + Acb_XecCtxInit( &BranchCtx ); + if ( fVerbose ) + printf( "Trying DC-control branch proving: DC nodes = %d. controls = %d.\n", + Vec_IntSize(vDcObjsG), Vec_IntSize(vDcCtrlsG) ); + for ( iCtrl = 0; iCtrl < Vec_IntSize(vDcCtrlsG); iCtrl++ ) + { + Vec_Int_t * vDcObjsOne = Acb_NtkCollectDcObjsForControl( vDcObjsG, vDcCtrlIdsG, iCtrl ); + int fCtrlUndec = 0; + for ( fOne = 0; fOne <= 1; fOne++ ) + { + Vec_Int_t * vGTargets = Vec_IntAlloc( Acb_NtkCoNum(pNtkG) + 1 ); + Gia_Man_t * pGiaFBranch = NULL, * pGiaGBranch = NULL, * pGiaBranch = NULL; + int i, iObj; + Acb_NtkForEachCo( pNtkG, iObj, i ) + Vec_IntPush( vGTargets, Acb_ObjFanin(pNtkG, iObj, 0) ); + Vec_IntPush( vGTargets, Vec_IntEntry(vDcCtrlsG, iCtrl) ); + pGiaFBranch = Acb_NtkGiaDeriveDualTargets( pNtkF, vFTargets ); + pGiaGBranch = Acb_NtkGiaDeriveDualTargetsBranch( pNtkG, vGTargets, vDcObjsOne, fOne ); + pGiaBranch = Acb_NtkGiaDeriveMiterWithSecondExtras( pGiaFBranch, pGiaGBranch, 1 ); + if ( fVerbose ) + printf( "DC-control branch miter: control %d branch %d. DC nodes = %d. And = %d. PO = %d.\n", + iCtrl, fOne, Vec_IntSize(vDcObjsOne), Gia_ManAndNum(pGiaBranch), Gia_ManPoNum(pGiaBranch) ); + if ( nMaxBranchAnd > 0 && Gia_ManAndNum(pGiaBranch) >= nMaxBranchAnd ) + { + if ( fVerbose ) + printf( "Skipping DC-control branch %d/%d because branch miter is not smaller than current miter: branch And = %d, current And = %d.\n", + iCtrl, fOne, Gia_ManAndNum(pGiaBranch), nMaxBranchAnd ); + Status = -1; + fUndec = 1; + } + else + { + pModel = Acb_NtkSolveCadicalSelectorBranch( pGiaBranch, Vec_IntSize(vFTargets), fOne, fVerbose, &Status, nBranchLimit, iCtrl, fStopOnUndec, "DC-control branch", &BranchCtx ); + } + Gia_ManStop( pGiaBranch ); + Gia_ManStop( pGiaGBranch ); + Gia_ManStop( pGiaFBranch ); + Vec_IntFree( vGTargets ); + if ( Status == 0 ) + { + Vec_IntFree( vDcObjsOne ); + Vec_IntFree( vFTargets ); + Acb_XecCtxFree( &BranchCtx ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + if ( Status == -1 ) + { + fUndec = 1; + fCtrlUndec = 1; + } + } + Vec_IntFree( vDcObjsOne ); + if ( !fCtrlUndec ) + { + Vec_IntFree( vFTargets ); + Acb_XecCtxFree( &BranchCtx ); + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + printf( "The networks are equivalent by DC-control branch proving on control %d. ", iCtrl ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return NULL; + } + } + Vec_IntFree( vFTargets ); + if ( pStatus ) + *pStatus = fUndec ? -1 : 1; + printf( "The networks are %s by DC-control branch proving. ", fUndec ? "UNDECIDED" : "equivalent" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + Acb_XecCtxFree( &BranchCtx ); + return NULL; +} +int * Acb_NtkSolveDcControlTargetBranches( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, int iPo, Vec_Int_t * vDcObjsG, Vec_Int_t * vDcCtrlsG, Vec_Int_t * vDcCtrlIdsG, int fVerbose, int * pStatus, int nBranchLimit, int nMaxBranchAnd ) +{ + Vec_Int_t * vFTargets = Vec_IntAlloc( 1 ); + Acb_XecCtx_t BranchCtx; + int iCtrl, fOne, Status, fUndec = 0; + int * pModel = NULL; + abctime clk = Abc_Clock(); + Acb_XecCtxInit( &BranchCtx ); + assert( iPo >= 0 && iPo < Acb_NtkCoNum(pNtkF) && iPo < Acb_NtkCoNum(pNtkG) ); + Vec_IntPush( vFTargets, Acb_ObjFanin(pNtkF, Acb_NtkCo(pNtkF, iPo), 0) ); + if ( fVerbose ) + printf( "Trying DC-control target branch proving: output = %d. DC nodes = %d. controls = %d.\n", + iPo, Vec_IntSize(vDcObjsG), Vec_IntSize(vDcCtrlsG) ); + for ( iCtrl = 0; iCtrl < Vec_IntSize(vDcCtrlsG); iCtrl++ ) + { + Vec_Int_t * vDcObjsOne = Acb_NtkCollectDcObjsForControl( vDcObjsG, vDcCtrlIdsG, iCtrl ); + int fCtrlUndec = 0; + for ( fOne = 0; fOne <= 1; fOne++ ) + { + Vec_Int_t * vGTargets = Vec_IntAlloc( 2 ); + Gia_Man_t * pGiaFBranch = NULL, * pGiaGBranch = NULL, * pGiaBranch = NULL; + Vec_IntPush( vGTargets, Acb_ObjFanin(pNtkG, Acb_NtkCo(pNtkG, iPo), 0) ); + Vec_IntPush( vGTargets, Vec_IntEntry(vDcCtrlsG, iCtrl) ); + pGiaFBranch = Acb_NtkGiaDeriveDualTargets( pNtkF, vFTargets ); + pGiaGBranch = Acb_NtkGiaDeriveDualTargetsBranch( pNtkG, vGTargets, vDcObjsOne, fOne ); + pGiaBranch = Acb_NtkGiaDeriveMiterWithSecondExtras( pGiaFBranch, pGiaGBranch, 1 ); + if ( fVerbose ) + printf( "DC-control target branch miter: output %d control %d branch %d. DC nodes = %d. And = %d. PO = %d.\n", + iPo, iCtrl, fOne, Vec_IntSize(vDcObjsOne), Gia_ManAndNum(pGiaBranch), Gia_ManPoNum(pGiaBranch) ); + if ( nMaxBranchAnd > 0 && Gia_ManAndNum(pGiaBranch) >= nMaxBranchAnd ) + { + if ( fVerbose ) + printf( "Skipping DC-control target branch output %d control %d/%d because branch miter is not smaller than current hard cone: branch And = %d, current And = %d.\n", + iPo, iCtrl, fOne, Gia_ManAndNum(pGiaBranch), nMaxBranchAnd ); + Status = -1; + fUndec = 1; + } + else + pModel = Acb_NtkSolveCadicalSelectorBranch( pGiaBranch, 1, fOne, fVerbose, &Status, nBranchLimit, iCtrl, 0, "DC-control target branch", &BranchCtx ); + Gia_ManStop( pGiaBranch ); + Gia_ManStop( pGiaGBranch ); + Gia_ManStop( pGiaFBranch ); + Vec_IntFree( vGTargets ); + if ( Status == 0 ) + { + Vec_IntFree( vDcObjsOne ); + Vec_IntFree( vFTargets ); + Acb_XecCtxFree( &BranchCtx ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + if ( Status == -1 ) + { + fUndec = 1; + fCtrlUndec = 1; + } + } + Vec_IntFree( vDcObjsOne ); + if ( !fCtrlUndec ) + { + Vec_IntFree( vFTargets ); + Acb_XecCtxFree( &BranchCtx ); + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + printf( "The hard output %d is UNSAT by DC-control target branch proving on control %d. ", iPo, iCtrl ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return NULL; + } + } + Vec_IntFree( vFTargets ); + if ( pStatus ) + *pStatus = fUndec ? ACB_XEC_UNDEC : ACB_XEC_EQ; + printf( "The hard output %d is %s by DC-control target branch proving. ", + iPo, fUndec ? "UNDECIDED" : "UNSAT" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + Acb_XecCtxFree( &BranchCtx ); + return NULL; +} +void Acb_NtkCollectDcObjsForCube( Vec_Int_t * vDcObjs, Vec_Int_t * vDcCtrlIds, Vec_Int_t * vCubeCtrls, Vec_Int_t * vCubeVals, Vec_Int_t ** pvObjs, Vec_Int_t ** pvVals ) +{ + Vec_Int_t * vObjs = Vec_IntAlloc( Vec_IntSize(vDcObjs) ); + Vec_Int_t * vVals = Vec_IntAlloc( Vec_IntSize(vDcObjs) ); + int i, k, iObj, iCtrlId, iCubeCtrl; + Vec_IntForEachEntry( vDcObjs, iObj, i ) + { + iCtrlId = Vec_IntEntry( vDcCtrlIds, i ); + Vec_IntForEachEntry( vCubeCtrls, iCubeCtrl, k ) + if ( iCubeCtrl == iCtrlId ) + { + Vec_IntPush( vObjs, iObj ); + Vec_IntPush( vVals, Vec_IntEntry(vCubeVals, k) ); + break; + } + } + *pvObjs = vObjs; + *pvVals = vVals; +} +int * Acb_NtkSolveDcControlTargetCube( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, int iPo, Vec_Int_t * vDcObjsG, Vec_Int_t * vDcCtrlsG, Vec_Int_t * vDcCtrlIdsG, Vec_Int_t * vCubeCtrls, Vec_Int_t * vCubeVals, int fVerbose, int * pStatus, int nBranchLimit, int nDepthLeft ) +{ + Vec_Int_t * vFTargets = Vec_IntAlloc( 1 ); + Vec_Int_t * vGTargets = Vec_IntAlloc( 1 + Vec_IntSize(vCubeCtrls) ); + Vec_Int_t * vDcObjsCube = NULL, * vDcValsCube = NULL; + Gia_Man_t * pGiaFBranch = NULL, * pGiaGBranch = NULL, * pGiaBranch = NULL, * pGiaCond = NULL, * pTemp = NULL; + int i, k, iCtrl, Status = -1, * pModel = NULL; + int nProbeLimit = nDepthLeft > 0 ? Abc_MinInt( nBranchLimit, 30 ) : nBranchLimit; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + Vec_IntPush( vFTargets, Acb_ObjFanin(pNtkF, Acb_NtkCo(pNtkF, iPo), 0) ); + Vec_IntPush( vGTargets, Acb_ObjFanin(pNtkG, Acb_NtkCo(pNtkG, iPo), 0) ); + Vec_IntForEachEntry( vCubeCtrls, iCtrl, i ) + Vec_IntPush( vGTargets, Vec_IntEntry(vDcCtrlsG, iCtrl) ); + Acb_NtkCollectDcObjsForCube( vDcObjsG, vDcCtrlIdsG, vCubeCtrls, vCubeVals, &vDcObjsCube, &vDcValsCube ); + pGiaFBranch = Acb_NtkGiaDeriveDualTargets( pNtkF, vFTargets ); + pGiaGBranch = Acb_NtkGiaDeriveDualTargetsBranchValues( pNtkG, vGTargets, vDcObjsCube, vDcValsCube, 0 ); + pGiaBranch = Acb_NtkGiaDeriveMiterWithSecondExtras( pGiaFBranch, pGiaGBranch, Vec_IntSize(vCubeCtrls) ); + pGiaCond = Acb_GiaDeriveCubeConditionMiter( pGiaBranch, 1, vCubeVals ); + if ( Gia_ManAndNum(pGiaCond) > 5000 ) + { + pTemp = Gia_ManCompress2( pGiaCond, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pGiaCond ); + pGiaCond = pTemp; + pTemp = NULL; + } + } + if ( fVerbose ) + { + printf( "DC-control recursive target: output %d cube =", iPo ); + Vec_IntForEachEntry( vCubeCtrls, iCtrl, i ) + printf( " c%d=%d", iCtrl, Vec_IntEntry(vCubeVals, i) ); + printf( ". And = %d. limit = %d sec%s.\n", Gia_ManAndNum(pGiaCond), nProbeLimit, + nDepthLeft > 0 ? " before split" : "" ); + } + pModel = Acb_NtkSolveCadicalLimit( pGiaCond, 0, 0, &Status, nProbeLimit, NULL, 0 ); + Gia_ManStop( pGiaCond ); + Gia_ManStop( pGiaBranch ); + Gia_ManStop( pGiaGBranch ); + Gia_ManStop( pGiaFBranch ); + Vec_IntFree( vDcObjsCube ); + Vec_IntFree( vDcValsCube ); + Vec_IntFree( vGTargets ); + Vec_IntFree( vFTargets ); + if ( Status == 1 ) + { + if ( fVerbose ) + printf( "DC-control recursive target: output %d cube UNSAT.\n", iPo ); + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + return NULL; + } + if ( Status == 0 ) + { + if ( fVerbose ) + printf( "DC-control recursive target: output %d cube SAT.\n", iPo ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + ABC_FREE( pModel ); + if ( nDepthLeft <= 0 ) + { + if ( fVerbose ) + printf( "DC-control recursive target: output %d cube UNDECIDED at depth limit.\n", iPo ); + return NULL; + } + { + Vec_Int_t * vTriedCtrls = Vec_IntAlloc( Vec_IntSize(vDcCtrlsG) ); + for ( k = 0; k < Vec_IntSize(vDcCtrlsG); k++ ) + { + int fOne, fCtrlUndec = 0, nBestCount = -1; + iCtrl = -1; + for ( i = 0; i < Vec_IntSize(vDcCtrlsG); i++ ) + { + int j, iCtrlId, nCount = 0; + if ( Vec_IntFind(vCubeCtrls, i) >= 0 || Vec_IntFind(vTriedCtrls, i) >= 0 ) + continue; + Vec_IntForEachEntry( vDcCtrlIdsG, iCtrlId, j ) + nCount += (iCtrlId == i); + if ( nCount > nBestCount ) + { + nBestCount = nCount; + iCtrl = i; + } + } + if ( iCtrl < 0 ) + break; + Vec_IntPush( vTriedCtrls, iCtrl ); + for ( fOne = 1; fOne >= 0; fOne-- ) + { + int StatusSub = -1; + Vec_IntPush( vCubeCtrls, iCtrl ); + Vec_IntPush( vCubeVals, fOne ); + pModel = Acb_NtkSolveDcControlTargetCube( pNtkF, pNtkG, iPo, vDcObjsG, vDcCtrlsG, vDcCtrlIdsG, + vCubeCtrls, vCubeVals, fVerbose, &StatusSub, nBranchLimit, nDepthLeft - 1 ); + Vec_IntPop( vCubeCtrls ); + Vec_IntPop( vCubeVals ); + if ( StatusSub == 0 ) + { + Vec_IntFree( vTriedCtrls ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + if ( StatusSub != 1 ) + fCtrlUndec = 1; + } + if ( !fCtrlUndec ) + { + if ( fVerbose ) + printf( "DC-control recursive target: output %d proven by splitting control %d.\n", iPo, iCtrl ); + Vec_IntFree( vTriedCtrls ); + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + return NULL; + } + } + Vec_IntFree( vTriedCtrls ); + } + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + return NULL; +} +int * Acb_NtkSolveDcControlTargetList( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, Vec_Int_t * vHardPos, Vec_Int_t * vDcObjsG, Vec_Int_t * vDcCtrlsG, Vec_Int_t * vDcCtrlIdsG, int fVerbose, int * pStatus, int nBranchLimit ) +{ + int i, iPo, StatusOne = -1, fUndec = 0; + int * pModel = NULL; + abctime clk = Abc_Clock(); + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( vHardPos == NULL || Vec_IntSize(vHardPos) == 0 ) + return NULL; + if ( fVerbose ) + printf( "Trying DC-control target proving for %d hard outputs.\n", Vec_IntSize(vHardPos) ); + Vec_IntForEachEntry( vHardPos, iPo, i ) + { + Vec_Int_t * vCubeCtrls = Vec_IntAlloc( Vec_IntSize(vDcCtrlsG) ); + Vec_Int_t * vCubeVals = Vec_IntAlloc( Vec_IntSize(vDcCtrlsG) ); + int nDepth = Vec_IntSize(vDcCtrlsG) <= 3 ? Vec_IntSize(vDcCtrlsG) : Abc_MinInt( 2, Vec_IntSize(vDcCtrlsG) ); + pModel = Acb_NtkSolveDcControlTargetCube( pNtkF, pNtkG, iPo, vDcObjsG, vDcCtrlsG, vDcCtrlIdsG, + vCubeCtrls, vCubeVals, fVerbose, &StatusOne, nBranchLimit, nDepth ); + Vec_IntFree( vCubeCtrls ); + Vec_IntFree( vCubeVals ); + if ( StatusOne == 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + if ( StatusOne != 1 ) + { + fUndec = 1; + if ( fVerbose ) + printf( "DC-control target list: output %d remains UNDECIDED; stopping target-list proof.\n", iPo ); + break; + } + } + if ( pStatus ) + *pStatus = fUndec ? -1 : 1; + printf( "The hard outputs are %s by DC-control target-list proving. ", fUndec ? "UNDECIDED" : "UNSAT" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return NULL; +} +int * Acb_NtkSolveMuxDcControlTargetCube( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, int iPo, Vec_Int_t * vCutObjsG, Vec_Int_t * vMuxSelectorsG, Vec_Int_t * vMuxPoSelIdsG, int fSelBranch, Vec_Int_t * vDcObjsG, Vec_Int_t * vDcCtrlsG, Vec_Int_t * vDcCtrlIdsG, Vec_Int_t * vCubeCtrls, Vec_Int_t * vCubeVals, int fVerbose, int * pStatus, int nBranchLimit, int nDepthLeft ) +{ + Vec_Int_t * vFTargets = Vec_IntAlloc( 1 ); + Vec_Int_t * vGTargets = Vec_IntAlloc( 2 + Vec_IntSize(vCubeCtrls) ); + Vec_Int_t * vDcObjsCube = NULL, * vDcValsCube = NULL, * vCondVals = Vec_IntAlloc( 1 + Vec_IntSize(vCubeCtrls) ); + Gia_Man_t * pGiaFBranch = NULL, * pGiaGBranch = NULL, * pGiaBranch = NULL, * pGiaCond = NULL, * pTemp = NULL; + int i, k, iCtrl, iMux, Status = -1, * pModel = NULL; + int nProbeLimit = nDepthLeft > 0 ? Abc_MinInt( nBranchLimit, 30 ) : nBranchLimit; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + assert( iPo >= 0 && iPo < Acb_NtkCoNum(pNtkF) && iPo < Acb_NtkCoNum(pNtkG) ); + assert( vCutObjsG && vMuxSelectorsG && vMuxPoSelIdsG ); + iMux = Vec_IntEntry( vCutObjsG, iPo ); + assert( !Acb_ObjIsCio(pNtkG, iMux) && Acb_ObjType(pNtkG, iMux) == ABC_OPER_BIT_MUX ); + Vec_IntPush( vFTargets, Acb_ObjFanin(pNtkF, Acb_NtkCo(pNtkF, iPo), 0) ); + Vec_IntPush( vGTargets, Acb_ObjFanin(pNtkG, iMux, fSelBranch ? 1 : 0) ); + Vec_IntPush( vGTargets, Vec_IntEntry(vMuxSelectorsG, Vec_IntEntry(vMuxPoSelIdsG, iPo)) ); + Vec_IntForEachEntry( vCubeCtrls, iCtrl, i ) + Vec_IntPush( vGTargets, Vec_IntEntry(vDcCtrlsG, iCtrl) ); + Acb_NtkCollectDcObjsForCube( vDcObjsG, vDcCtrlIdsG, vCubeCtrls, vCubeVals, &vDcObjsCube, &vDcValsCube ); + pGiaFBranch = Acb_NtkGiaDeriveDualTargets( pNtkF, vFTargets ); + pGiaGBranch = Acb_NtkGiaDeriveDualTargetsBranchValues( pNtkG, vGTargets, vDcObjsCube, vDcValsCube, 0 ); + pGiaBranch = Acb_NtkGiaDeriveMiterWithSecondExtras( pGiaFBranch, pGiaGBranch, 1 + Vec_IntSize(vCubeCtrls) ); + Vec_IntPush( vCondVals, fSelBranch ); + Vec_IntForEachEntry( vCubeVals, iCtrl, i ) + Vec_IntPush( vCondVals, iCtrl ); + pGiaCond = Acb_GiaDeriveCubeConditionMiter( pGiaBranch, 1, vCondVals ); + if ( Gia_ManAndNum(pGiaCond) > 5000 ) + { + pTemp = Gia_ManCompress2( pGiaCond, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pGiaCond ); + pGiaCond = pTemp; + pTemp = NULL; + } + } + if ( fVerbose ) + { + printf( "HMUX+DC recursive target: output %d selector %d branch %d cube =", + iPo, Vec_IntEntry(vMuxPoSelIdsG, iPo), fSelBranch ); + Vec_IntForEachEntry( vCubeCtrls, iCtrl, i ) + printf( " c%d=%d", iCtrl, Vec_IntEntry(vCubeVals, i) ); + printf( ". And = %d. limit = %d sec%s.\n", Gia_ManAndNum(pGiaCond), nProbeLimit, + nDepthLeft > 0 ? " before split" : "" ); + } + pModel = Acb_NtkSolveCadicalLimit( pGiaCond, 0, 0, &Status, nProbeLimit, NULL, 0 ); + Gia_ManStop( pGiaCond ); + Gia_ManStop( pGiaBranch ); + Gia_ManStop( pGiaGBranch ); + Gia_ManStop( pGiaFBranch ); + Vec_IntFree( vDcObjsCube ); + Vec_IntFree( vDcValsCube ); + Vec_IntFree( vCondVals ); + Vec_IntFree( vGTargets ); + Vec_IntFree( vFTargets ); + if ( Status == 1 ) + { + if ( fVerbose ) + printf( "HMUX+DC recursive target: output %d cube UNSAT.\n", iPo ); + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + return NULL; + } + if ( Status == 0 ) + { + if ( fVerbose ) + printf( "HMUX+DC recursive target: output %d cube SAT.\n", iPo ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + ABC_FREE( pModel ); + if ( nDepthLeft <= 0 ) + { + if ( fVerbose ) + printf( "HMUX+DC recursive target: output %d cube UNDECIDED at depth limit.\n", iPo ); + return NULL; + } + { + Vec_Int_t * vTriedCtrls = Vec_IntAlloc( Vec_IntSize(vDcCtrlsG) ); + for ( k = 0; k < Vec_IntSize(vDcCtrlsG); k++ ) + { + int fOne, fCtrlUndec = 0, nBestCount = -1; + iCtrl = -1; + for ( i = 0; i < Vec_IntSize(vDcCtrlsG); i++ ) + { + int j, iCtrlId, nCount = 0; + if ( Vec_IntFind(vCubeCtrls, i) >= 0 || Vec_IntFind(vTriedCtrls, i) >= 0 ) + continue; + Vec_IntForEachEntry( vDcCtrlIdsG, iCtrlId, j ) + nCount += (iCtrlId == i); + if ( nCount > nBestCount ) + { + nBestCount = nCount; + iCtrl = i; + } + } + if ( iCtrl < 0 ) + break; + Vec_IntPush( vTriedCtrls, iCtrl ); + for ( fOne = 1; fOne >= 0; fOne-- ) + { + int StatusSub = -1; + Vec_IntPush( vCubeCtrls, iCtrl ); + Vec_IntPush( vCubeVals, fOne ); + pModel = Acb_NtkSolveMuxDcControlTargetCube( pNtkF, pNtkG, iPo, vCutObjsG, vMuxSelectorsG, vMuxPoSelIdsG, fSelBranch, + vDcObjsG, vDcCtrlsG, vDcCtrlIdsG, vCubeCtrls, vCubeVals, fVerbose, &StatusSub, nBranchLimit, nDepthLeft - 1 ); + Vec_IntPop( vCubeCtrls ); + Vec_IntPop( vCubeVals ); + if ( StatusSub == 0 ) + { + Vec_IntFree( vTriedCtrls ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + if ( StatusSub != 1 ) + fCtrlUndec = 1; + } + if ( !fCtrlUndec ) + { + if ( fVerbose ) + printf( "HMUX+DC recursive target: output %d proven by splitting control %d.\n", iPo, iCtrl ); + Vec_IntFree( vTriedCtrls ); + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + return NULL; + } + } + Vec_IntFree( vTriedCtrls ); + } + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + return NULL; +} + +int * Acb_NtkSolveMuxDcControlTargetList( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, Vec_Int_t * vHardPos, Vec_Int_t * vCutObjsG, Vec_Int_t * vMuxSelectorsG, Vec_Int_t * vMuxPoSelIdsG, int fSelBranch, Vec_Int_t * vDcObjsG, Vec_Int_t * vDcCtrlsG, Vec_Int_t * vDcCtrlIdsG, int fVerbose, int * pStatus, int nBranchLimit ) +{ + int i, iPo, StatusOne = -1, fUndec = 0; + int * pModel = NULL; + Vec_Int_t * vCubeCtrls = NULL, * vCubeVals = NULL; + abctime clk = Abc_Clock(); + int nCtrls = vDcCtrlsG ? Vec_IntSize(vDcCtrlsG) : 0; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( vHardPos == NULL || Vec_IntSize(vHardPos) == 0 ) + return NULL; + if ( Vec_IntSize(vHardPos) > 4 || nCtrls > 8 ) + { + if ( fVerbose ) + printf( "Skipping HMUX+DC target proving because recursive search is too broad: hard outputs = %d, controls = %d.\n", + Vec_IntSize(vHardPos), nCtrls ); + return NULL; + } + if ( fVerbose ) + printf( "Trying HMUX+DC target proving for %d hard outputs. selector branch = %d. controls = %d.\n", + Vec_IntSize(vHardPos), fSelBranch, nCtrls ); + vCubeCtrls = Vec_IntAlloc( nCtrls ); + vCubeVals = Vec_IntAlloc( nCtrls ); + Vec_IntForEachEntry( vHardPos, iPo, i ) + { + int nDepth = nCtrls <= 3 ? nCtrls : 2; + Vec_IntClear( vCubeCtrls ); + Vec_IntClear( vCubeVals ); + pModel = Acb_NtkSolveMuxDcControlTargetCube( pNtkF, pNtkG, iPo, vCutObjsG, vMuxSelectorsG, vMuxPoSelIdsG, fSelBranch, + vDcObjsG, vDcCtrlsG, vDcCtrlIdsG, vCubeCtrls, vCubeVals, fVerbose, &StatusOne, nBranchLimit, nDepth ); + if ( StatusOne == 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + Vec_IntFree( vCubeCtrls ); + Vec_IntFree( vCubeVals ); + return pModel; + } + if ( StatusOne != 1 ) + { + fUndec = 1; + if ( fVerbose ) + printf( "HMUX+DC target list: output %d remains UNDECIDED; stopping target-list proof.\n", iPo ); + break; + } + } + if ( pStatus ) + *pStatus = fUndec ? -1 : 1; + printf( "The HMUX hard outputs are %s by targeted DC-control proving. ", fUndec ? "UNDECIDED" : "UNSAT" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + Vec_IntFree( vCubeCtrls ); + Vec_IntFree( vCubeVals ); + return NULL; +} +int * Acb_NtkSolveDcControlWholeCubes( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, Vec_Int_t * vDcObjsG, Vec_Int_t * vDcCtrlsG, Vec_Int_t * vDcCtrlIdsG, int fVerbose, int * pStatus, int nTotalLimit, int nCubeLimit ) +{ + Vec_Int_t * vFTargets = NULL, * vGTargets = NULL, * vCubeCtrls = NULL, * vCubeVals = NULL; + Vec_Int_t * vDcObjsCube = NULL, * vDcValsCube = NULL; + Gia_Man_t * pGiaF = NULL, * pGiaG = NULL, * pGiaMiter = NULL, * pGiaCond = NULL, * pTemp = NULL; + int nCtrls = vDcCtrlsG ? Vec_IntSize(vDcCtrlsG) : 0; + int nCubes, i, iObj, iCube, StatusCube = -1, StatusAll = 1, * pModel = NULL; + abctime clk = Abc_Clock(); + abctime clkLimit = nTotalLimit > 0 ? clk + nTotalLimit * CLOCKS_PER_SEC : 0; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( nCtrls <= 0 || nCtrls > 4 || vDcObjsG == NULL || Vec_IntSize(vDcObjsG) == 0 ) + return NULL; + nCubes = 1 << nCtrls; + if ( fVerbose ) + printf( "Trying few-control DC whole-cube proof: outputs = %d. DC nodes = %d. controls = %d. cubes = %d. total limit = %d sec.\n", + Acb_NtkCoNum(pNtkF), Vec_IntSize(vDcObjsG), nCtrls, nCubes, nTotalLimit ); + vFTargets = Vec_IntAlloc( Acb_NtkCoNum(pNtkF) ); + vGTargets = Vec_IntAlloc( Acb_NtkCoNum(pNtkG) + nCtrls ); + vCubeCtrls = Vec_IntAlloc( nCtrls ); + vCubeVals = Vec_IntAlloc( nCtrls ); + Acb_NtkForEachCo( pNtkF, iObj, i ) + Vec_IntPush( vFTargets, Acb_ObjFanin(pNtkF, iObj, 0) ); + Acb_NtkForEachCo( pNtkG, iObj, i ) + Vec_IntPush( vGTargets, Acb_ObjFanin(pNtkG, iObj, 0) ); + for ( i = 0; i < nCtrls; i++ ) + { + Vec_IntPush( vCubeCtrls, i ); + Vec_IntPush( vGTargets, Vec_IntEntry(vDcCtrlsG, i) ); + } + pGiaF = Acb_NtkGiaDeriveDualTargets( pNtkF, vFTargets ); + for ( iCube = 0; iCube < nCubes; iCube++ ) + { + int nThisLimit, nRemain; + Vec_IntClear( vCubeVals ); + for ( i = 0; i < nCtrls; i++ ) + Vec_IntPush( vCubeVals, (iCube >> i) & 1 ); + if ( clkLimit ) + { + nRemain = (int)((clkLimit - Abc_Clock()) / CLOCKS_PER_SEC); + if ( nRemain <= 0 ) + { + StatusAll = -1; + break; + } + nThisLimit = nCubeLimit > 0 ? Abc_MinInt( nCubeLimit, nRemain ) : nRemain; + } + else + nThisLimit = nCubeLimit; + Acb_NtkCollectDcObjsForCube( vDcObjsG, vDcCtrlIdsG, vCubeCtrls, vCubeVals, &vDcObjsCube, &vDcValsCube ); + pGiaG = Acb_NtkGiaDeriveDualTargetsBranchValues( pNtkG, vGTargets, vDcObjsCube, vDcValsCube, 0 ); + pGiaMiter = Acb_NtkGiaDeriveMiterWithSecondExtras( pGiaF, pGiaG, nCtrls ); + pGiaCond = Acb_GiaDeriveCubeConditionMiter( pGiaMiter, Acb_NtkCoNum(pNtkF), vCubeVals ); + if ( Gia_ManAndNum(pGiaCond) > 5000 ) + { + pTemp = Gia_ManCompress2( pGiaCond, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pGiaCond ); + pGiaCond = pTemp; + pTemp = NULL; + } + } + if ( fVerbose ) + { + printf( "Few-control DC whole cube %d/%d:", iCube + 1, nCubes ); + for ( i = 0; i < nCtrls; i++ ) + printf( " c%d=%d", i, Vec_IntEntry(vCubeVals, i) ); + printf( ". And = %d. PO = %d. limit = %d sec.\n", Gia_ManAndNum(pGiaCond), Gia_ManCoNum(pGiaCond), nThisLimit ); + } + pModel = Acb_NtkSolveCadicalLimit( pGiaCond, 0, fVerbose, &StatusCube, nThisLimit, "few-control DC whole-cube CaDiCaL", 0 ); + Gia_ManStop( pGiaCond ); pGiaCond = NULL; + Gia_ManStop( pGiaMiter ); pGiaMiter = NULL; + Gia_ManStop( pGiaG ); pGiaG = NULL; + Vec_IntFreeP( &vDcObjsCube ); + Vec_IntFreeP( &vDcValsCube ); + if ( StatusCube == 0 ) + { + StatusAll = 0; + break; + } + if ( StatusCube != 1 ) + { + StatusAll = -1; + break; + } + } + if ( pStatus ) + *pStatus = StatusAll; + if ( StatusAll == 1 ) + { + printf( "The networks are equivalent by few-control DC whole-cube proof. " ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + else if ( fVerbose ) + { + printf( "The networks are %s by few-control DC whole-cube proof. ", + StatusAll == 0 ? "NOT equivalent" : "UNDECIDED" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + Vec_IntFreeP( &vFTargets ); + Vec_IntFreeP( &vGTargets ); + Vec_IntFreeP( &vCubeCtrls ); + Vec_IntFreeP( &vCubeVals ); + Vec_IntFreeP( &vDcObjsCube ); + Vec_IntFreeP( &vDcValsCube ); + if ( pGiaF ) + Gia_ManStop( pGiaF ); + if ( pGiaG ) + Gia_ManStop( pGiaG ); + if ( pGiaMiter ) + Gia_ManStop( pGiaMiter ); + if ( pGiaCond ) + Gia_ManStop( pGiaCond ); + if ( pTemp ) + Gia_ManStop( pTemp ); + return pModel; +} +int Acb_NtkObjIsCutCandBasic( Acb_Ntk_t * p, int iObj ); +int Acb_NtkObjIsCutCand( Acb_Ntk_t * p, int iObj ) +{ + if ( !Acb_NtkObjIsCutCandBasic(p, iObj) ) + return 0; + if ( Acb_ObjName(p, iObj) <= 0 ) + return 0; + return 1; +} +int Acb_NtkObjIsCutCandBasic( Acb_Ntk_t * p, int iObj ) +{ + Acb_ObjType_t Type; + if ( iObj <= 0 || Acb_ObjIsCio(p, iObj) ) + return 0; + Type = Acb_ObjType( p, iObj ); + if ( Type == ABC_OPER_NONE || Type == ABC_OPER_CONST_F || Type == ABC_OPER_CONST_T || Type == ABC_OPER_CONST_X ) + return 0; + if ( Type == ABC_OPER_TRI || Type == ABC_OPER_BIT_MUX ) + return 0; + return 1; +} +void Acb_NtkMarkCone_rec( Acb_Ntk_t * p, int iObj, Vec_Int_t * vMarks ) +{ + int iFanin, k; + if ( iObj <= 0 || Vec_IntEntry(vMarks, iObj) ) + return; + Vec_IntWriteEntry( vMarks, iObj, 1 ); + if ( Acb_ObjIsCio(p, iObj) ) + return; + Acb_ObjForEachFanin( p, iObj, iFanin, k ) + Acb_NtkMarkCone_rec( p, iFanin, vMarks ); +} +int Acb_NtkConeLevel_rec( Acb_Ntk_t * p, int iObj, Vec_Int_t * vMarks, Vec_Int_t * vLevels ) +{ + int iFanin, k, Level, LevelMax = 0; + if ( iObj <= 0 || !Vec_IntEntry(vMarks, iObj) || Acb_ObjIsCio(p, iObj) ) + return 0; + Level = Vec_IntEntry(vLevels, iObj); + if ( Level >= 0 ) + return Level; + Acb_ObjForEachFanin( p, iObj, iFanin, k ) + LevelMax = Abc_MaxInt( LevelMax, Acb_NtkConeLevel_rec(p, iFanin, vMarks, vLevels) ); + Vec_IntWriteEntry( vLevels, iObj, LevelMax + 1 ); + return LevelMax + 1; +} +void Acb_NtkCollectTargetCutCandidates( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, int iPo, Vec_Int_t ** pvCutsF, Vec_Int_t ** pvCutsG, int nLimit ) +{ + Vec_Int_t * vNamesInvF = Vec_IntInvert( &pNtkF->vObjName, 0 ); + Vec_Int_t * vMarksF = Vec_IntStart( Acb_NtkObjNumMax(pNtkF) ); + Vec_Int_t * vMarksG = Vec_IntStart( Acb_NtkObjNumMax(pNtkG) ); + Vec_Int_t * vLevelsG = Vec_IntStartFull( Acb_NtkObjNumMax(pNtkG) ); + Vec_Int_t * vCutsF = Vec_IntAlloc( nLimit ); + Vec_Int_t * vCutsG = Vec_IntAlloc( nLimit ); + int iRootF, iRootG, iObjG, iObjF, NameIdF, Level, LevelMax; + assert( iPo >= 0 && iPo < Acb_NtkCoNum(pNtkF) && iPo < Acb_NtkCoNum(pNtkG) ); + iRootF = Acb_ObjFanin( pNtkF, Acb_NtkCo(pNtkF, iPo), 0 ); + iRootG = Acb_ObjFanin( pNtkG, Acb_NtkCo(pNtkG, iPo), 0 ); + Acb_NtkMarkCone_rec( pNtkF, iRootF, vMarksF ); + Acb_NtkMarkCone_rec( pNtkG, iRootG, vMarksG ); + LevelMax = Acb_NtkConeLevel_rec( pNtkG, iRootG, vMarksG, vLevelsG ); + Acb_NtkForEachNodeReverse( pNtkG, iObjG ) + { + if ( Vec_IntSize(vCutsG) >= nLimit ) + break; + if ( !Vec_IntEntry(vMarksG, iObjG) || !Acb_NtkObjIsCutCand(pNtkG, iObjG) ) + continue; + Level = Vec_IntEntry(vLevelsG, iObjG); + if ( Level < Abc_MaxInt(2, LevelMax/4) || Level > Abc_MaxInt(3, 3*LevelMax/4) ) + continue; + NameIdF = Acb_NtkStrId( pNtkF, Acb_ObjNameStr(pNtkG, iObjG) ); + if ( NameIdF <= 0 || NameIdF >= Vec_IntSize(vNamesInvF) ) + continue; + iObjF = Vec_IntEntry( vNamesInvF, NameIdF ); + if ( iObjF <= 0 || iObjF >= Vec_IntSize(vMarksF) || !Vec_IntEntry(vMarksF, iObjF) ) + continue; + if ( !Acb_NtkObjIsCutCand(pNtkF, iObjF) ) + continue; + if ( Acb_ObjType(pNtkF, iObjF) != Acb_ObjType(pNtkG, iObjG) ) + continue; + if ( Acb_ObjFaninNum(pNtkF, iObjF) != Acb_ObjFaninNum(pNtkG, iObjG) ) + continue; + Vec_IntPush( vCutsF, iObjF ); + Vec_IntPush( vCutsG, iObjG ); + } + Vec_IntFree( vNamesInvF ); + Vec_IntFree( vMarksF ); + Vec_IntFree( vMarksG ); + Vec_IntFree( vLevelsG ); + *pvCutsF = vCutsF; + *pvCutsG = vCutsG; +} +Vec_Int_t * Acb_NtkCollectTargetCutPool( Acb_Ntk_t * p, int iPo, int nLimit ) +{ + Vec_Int_t * vMarks = Vec_IntStart( Acb_NtkObjNumMax(p) ); + Vec_Int_t * vLevels = Vec_IntStartFull( Acb_NtkObjNumMax(p) ); + Vec_Int_t * vPool = Vec_IntAlloc( nLimit ); + int iRoot, iObj, Level, LevelMax; + assert( iPo >= 0 && iPo < Acb_NtkCoNum(p) ); + iRoot = Acb_ObjFanin( p, Acb_NtkCo(p, iPo), 0 ); + Acb_NtkMarkCone_rec( p, iRoot, vMarks ); + LevelMax = Acb_NtkConeLevel_rec( p, iRoot, vMarks, vLevels ); + Acb_NtkForEachNodeReverse( p, iObj ) + { + if ( Vec_IntSize(vPool) >= nLimit ) + break; + if ( !Vec_IntEntry(vMarks, iObj) || !Acb_NtkObjIsCutCandBasic(p, iObj) ) + continue; + Level = Vec_IntEntry(vLevels, iObj); + if ( Level < Abc_MaxInt(2, LevelMax/5) || Level > Abc_MaxInt(3, 4*LevelMax/5) ) + continue; + Vec_IntPush( vPool, iObj ); + } + Vec_IntFree( vMarks ); + Vec_IntFree( vLevels ); + return vPool; +} +int Acb_NtkSimSignaturesEqual( Vec_Wrd_t * vSimsF, Vec_Wrd_t * vSimsG, int nWords, int iCandF, int iCandG ) +{ + word * pF0 = Vec_WrdEntryP( vSimsF, (2*iCandF + 0) * nWords ); + word * pF1 = Vec_WrdEntryP( vSimsF, (2*iCandF + 1) * nWords ); + word * pG0 = Vec_WrdEntryP( vSimsG, (2*iCandG + 0) * nWords ); + word * pG1 = Vec_WrdEntryP( vSimsG, (2*iCandG + 1) * nWords ); + int w; + for ( w = 0; w < nWords; w++ ) + if ( pF0[w] != pG0[w] || pF1[w] != pG1[w] ) + return 0; + return 1; +} +void Acb_NtkCollectTargetCutCandidatesSim( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, int iPo, Vec_Int_t ** pvCutsF, Vec_Int_t ** pvCutsG, int nLimit, int fVerbose ) +{ + Vec_Int_t * vPoolF = Acb_NtkCollectTargetCutPool( pNtkF, iPo, 192 ); + Vec_Int_t * vPoolG = Acb_NtkCollectTargetCutPool( pNtkG, iPo, 384 ); + Vec_Int_t * vCutsF = Vec_IntAlloc( nLimit ); + Vec_Int_t * vCutsG = Vec_IntAlloc( nLimit ); + Vec_Int_t * vUsedF = Vec_IntStart( Vec_IntSize(vPoolF) ); + Gia_Man_t * pGiaF = NULL, * pGiaG = NULL; + Vec_Wrd_t * vSimsF = NULL, * vSimsG = NULL; + int i, k, iObjF, iObjG, nWords = 16; + if ( Vec_IntSize(vPoolF) == 0 || Vec_IntSize(vPoolG) == 0 ) + goto finish; + pGiaF = Acb_NtkGiaDeriveDualTargets( pNtkF, vPoolF ); + pGiaG = Acb_NtkGiaDeriveDualTargets( pNtkG, vPoolG ); + if ( Gia_ManCiNum(pGiaF) != Gia_ManCiNum(pGiaG) ) + goto finish; + Abc_Random(1); + Vec_WrdFreeP( &pGiaF->vSimsPi ); + Vec_WrdFreeP( &pGiaG->vSimsPi ); + pGiaF->vSimsPi = Vec_WrdStartRandom( Gia_ManCiNum(pGiaF) * nWords ); + pGiaG->vSimsPi = Vec_WrdDup( pGiaF->vSimsPi ); + vSimsF = Gia_ManSimPatSim( pGiaF ); + vSimsG = Gia_ManSimPatSim( pGiaG ); + Vec_IntForEachEntry( vPoolG, iObjG, i ) + { + if ( Vec_IntSize(vCutsG) >= nLimit ) + break; + Vec_IntForEachEntry( vPoolF, iObjF, k ) + { + if ( Vec_IntEntry(vUsedF, k) ) + continue; + if ( Acb_ObjType(pNtkF, iObjF) != Acb_ObjType(pNtkG, iObjG) ) + continue; + if ( Acb_ObjFaninNum(pNtkF, iObjF) != Acb_ObjFaninNum(pNtkG, iObjG) ) + continue; + if ( !Acb_NtkSimSignaturesEqual(vSimsF, vSimsG, nWords, k, i) ) + continue; + Vec_IntPush( vCutsF, iObjF ); + Vec_IntPush( vCutsG, iObjG ); + Vec_IntWriteEntry( vUsedF, k, 1 ); + break; + } + } +finish: + if ( fVerbose ) + printf( "Hard-output simulation cutpoint candidates: F pool = %d. G pool = %d. matched = %d.\n", + Vec_IntSize(vPoolF), Vec_IntSize(vPoolG), Vec_IntSize(vCutsF) ); + Vec_IntFree( vPoolF ); + Vec_IntFree( vPoolG ); + Vec_IntFree( vUsedF ); + Vec_WrdFreeP( &vSimsF ); + Vec_WrdFreeP( &vSimsG ); + if ( pGiaF ) + { + Vec_WrdFreeP( &pGiaF->vSimsPi ); + Gia_ManStop( pGiaF ); + } + if ( pGiaG ) + { + Vec_WrdFreeP( &pGiaG->vSimsPi ); + Gia_ManStop( pGiaG ); + } + *pvCutsF = vCutsF; + *pvCutsG = vCutsG; +} +int * Acb_NtkSolveTargetCutpoints( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, int iPo, int fVerbose, int * pStatus, int nSatTimeLimit ) +{ + Vec_Int_t * vCandF = NULL, * vCandG = NULL, * vProofF = Vec_IntAlloc( 64 ), * vProofG = Vec_IntAlloc( 64 ); + Vec_Int_t * vOneF = Vec_IntAlloc( 1 ), * vOneG = Vec_IntAlloc( 1 ), * vTargetF = Vec_IntAlloc( 1 ), * vTargetG = Vec_IntAlloc( 1 ); + Gia_Man_t * pGiaF = NULL, * pGiaG = NULL, * pGiaMiter = NULL, * pTemp = NULL; + int i, iObjF, iObjG, StatusOne = -1, StatusTop = -1, nTried = 0, nSat = 0, nUndec = 0; + int * pModel = NULL; + abctime clk = Abc_Clock(), clkLimit = nSatTimeLimit > 0 ? Abc_Clock() + nSatTimeLimit * CLOCKS_PER_SEC : 0; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( iPo < 0 || iPo >= Acb_NtkCoNum(pNtkF) || iPo >= Acb_NtkCoNum(pNtkG) ) + goto cleanup; + Acb_NtkCollectTargetCutCandidates( pNtkF, pNtkG, iPo, &vCandF, &vCandG, 96 ); + if ( Vec_IntSize(vCandF) == 0 ) + { + Vec_IntFreeP( &vCandF ); + Vec_IntFreeP( &vCandG ); + Acb_NtkCollectTargetCutCandidatesSim( pNtkF, pNtkG, iPo, &vCandF, &vCandG, 96, fVerbose ); + } + if ( fVerbose ) + printf( "Trying hard-output internal cutpoints: output = %d. candidates = %d.\n", iPo, Vec_IntSize(vCandF) ); + Vec_IntForEachEntryTwo( vCandF, vCandG, iObjF, iObjG, i ) + { + if ( clkLimit && Abc_Clock() > clkLimit ) + break; + Vec_IntClear( vOneF ); + Vec_IntClear( vOneG ); + Vec_IntPush( vOneF, iObjF ); + Vec_IntPush( vOneG, iObjG ); + pGiaF = Acb_NtkGiaDeriveDualTargets( pNtkF, vOneF ); + pGiaG = Acb_NtkGiaDeriveDualTargets( pNtkG, vOneG ); + pGiaMiter = Acb_NtkGiaDeriveMiter( pGiaF, pGiaG, 2 ); + if ( Gia_ManAndNum(pGiaMiter) > 5000 ) + { + pTemp = Gia_ManCompress2( pGiaMiter, 0, 0 ); + if ( pTemp ) + { + Gia_ManStop( pGiaMiter ); + pGiaMiter = pTemp; + pTemp = NULL; + } + } + nTried++; + StatusOne = -1; + pModel = Acb_NtkSolveCadicalPoSweepLabel( pGiaMiter, fVerbose && nTried <= 8, &StatusOne, 8, 100000, "Hard-output cutpoint proof", 0 ); + if ( pModel ) + { + ABC_FREE( pModel ); + pModel = NULL; + } + if ( StatusOne == 1 ) + { + Vec_IntPush( vProofF, iObjF ); + Vec_IntPush( vProofG, iObjG ); + } + else if ( StatusOne == 0 ) + nSat++; + else + nUndec++; + Gia_ManStop( pGiaF ); + Gia_ManStop( pGiaG ); + Gia_ManStop( pGiaMiter ); + pGiaF = pGiaG = pGiaMiter = NULL; + if ( Vec_IntSize(vProofF) >= 48 ) + break; + } + if ( fVerbose ) + printf( "Hard-output cutpoints: tried = %d. proven = %d. bad = %d. undecided = %d.\n", + nTried, Vec_IntSize(vProofF), nSat, nUndec ); + if ( Vec_IntSize(vProofF) == 0 ) + goto cleanup; + Vec_IntPush( vTargetF, Acb_ObjFanin(pNtkF, Acb_NtkCo(pNtkF, iPo), 0) ); + Vec_IntPush( vTargetG, Acb_ObjFanin(pNtkG, Acb_NtkCo(pNtkG, iPo), 0) ); + pGiaF = Acb_NtkGiaDeriveDualTargetsCutLeaves( pNtkF, vTargetF, vProofF ); + pGiaG = Acb_NtkGiaDeriveDualTargetsCutLeaves( pNtkG, vTargetG, vProofG ); + pGiaMiter = Acb_NtkGiaDeriveMiter( pGiaF, pGiaG, 2 ); + if ( Gia_ManAndNum(pGiaMiter) > 5000 ) + { + pTemp = Gia_ManCompress2( pGiaMiter, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pGiaMiter ); + pGiaMiter = pTemp; + pTemp = NULL; + } + } + if ( fVerbose ) + printf( "Hard-output top cutpoint miter: output = %d. cutpoints = %d. And = %d. PO = %d.\n", + iPo, Vec_IntSize(vProofF), Gia_ManAndNum(pGiaMiter), Gia_ManCoNum(pGiaMiter) ); + pModel = Acb_NtkSolveCadicalPoSweepLabel( pGiaMiter, fVerbose, &StatusTop, nSatTimeLimit, 250000, "Hard-output top cutpoint miter", 0 ); + if ( StatusTop == 1 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + printf( "The hard output %d is UNSAT by internal cutpoint abstraction. ", iPo ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + else + { + if ( pModel ) + { + ABC_FREE( pModel ); + pModel = NULL; + } + if ( fVerbose ) + printf( "Hard-output cutpoint abstraction did not prove output %d; SAT on abstraction may be spurious.\n", iPo ); + } +cleanup: + Vec_IntFreeP( &vCandF ); + Vec_IntFreeP( &vCandG ); + Vec_IntFree( vProofF ); + Vec_IntFree( vProofG ); + Vec_IntFree( vOneF ); + Vec_IntFree( vOneG ); + Vec_IntFree( vTargetF ); + Vec_IntFree( vTargetG ); + if ( pGiaF ) + Gia_ManStop( pGiaF ); + if ( pGiaG ) + Gia_ManStop( pGiaG ); + if ( pGiaMiter ) + Gia_ManStop( pGiaMiter ); + if ( pTemp ) + Gia_ManStop( pTemp ); + return pModel; +} +int * Acb_NtkSolveTargetCutpointList( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, Vec_Int_t * vHardPos, int fVerbose, int * pStatus, int nTotalLimit, int nPoLimit ) +{ + int i, iPo, StatusOne = -1, fUndec = 0; + int * pModel = NULL; + abctime clk = Abc_Clock(); + abctime clkLimit = nTotalLimit > 0 ? clk + nTotalLimit * CLOCKS_PER_SEC : 0; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( vHardPos == NULL || Vec_IntSize(vHardPos) == 0 ) + return NULL; + if ( fVerbose ) + printf( "Trying hard-output cutpoint abstraction for %d collected outputs.\n", Vec_IntSize(vHardPos) ); + Vec_IntForEachEntry( vHardPos, iPo, i ) + { + int nLimit = nPoLimit; + if ( clkLimit ) + { + int nRemain = (int)((clkLimit - Abc_Clock()) / CLOCKS_PER_SEC); + if ( nRemain <= 0 ) + { + fUndec = 1; + break; + } + nLimit = nPoLimit > 0 ? Abc_MinInt( nPoLimit, nRemain ) : nRemain; + } + StatusOne = -1; + pModel = Acb_NtkSolveTargetCutpoints( pNtkF, pNtkG, iPo, fVerbose, &StatusOne, nLimit ); + if ( StatusOne == 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + return pModel; + } + if ( pModel ) + { + ABC_FREE( pModel ); + pModel = NULL; + } + if ( StatusOne != 1 ) + { + fUndec = 1; + if ( fVerbose ) + printf( "Hard-output cutpoint abstraction: output %d remains UNDECIDED; stopping list proof.\n", iPo ); + break; + } + } + if ( pStatus ) + *pStatus = fUndec ? -1 : 1; + printf( "The collected hard outputs are %s by cutpoint abstraction. ", fUndec ? "UNDECIDED" : "UNSAT" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return NULL; +} +int * Acb_NtkSolveCadicalPoSweepLabel( Gia_Man_t * p, int fVerbose, int * pStatus, int nSatTimeLimit, int nPoConfLimit, char * pLabel, int fStopOnUndec ) +{ + Gia_Man_t * pGiaCnf = p; + Aig_Man_t * pMan = NULL; + Cnf_Dat_t * pCnf = NULL; + cadical_solver * pSat = NULL; + Acb_SplitPoOrder_t * pOrder = NULL; + int i, k, Lit, Ret, Status, * pModel = NULL; + int nSat = 0, nUnsat = 0, nUndec = 0; + int fManyOutputs = Gia_ManCoNum(p) > 64; + int nMaxManyUndec = fManyOutputs ? 12 : Gia_ManCoNum(p); + abctime clk = Abc_Clock(); + (void)nSatTimeLimit; + if ( Gia_ManCoNum(p) == 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + return NULL; + } + pMan = Gia_ManToAig( pGiaCnf, 0 ); + pCnf = pMan ? Cnf_Derive( pMan, Aig_ManCoNum(pMan) ) : NULL; + pSat = pCnf ? cadical_solver_new() : NULL; + if ( pCnf == NULL || pSat == NULL ) + goto cleanup; + if ( !Acb_CnfWriteIntoCadical( pSat, pCnf ) ) + goto cleanup; + pOrder = ABC_ALLOC( Acb_SplitPoOrder_t, Gia_ManCoNum(p) ); + Acb_NtkSortSplitOutputs( p, pOrder ); + if ( fVerbose ) + printf( "%s: smallest cone %d ANDs, largest cone %d ANDs.\n", + pLabel, pOrder[0].nAnds, pOrder[Gia_ManCoNum(p)-1].nAnds ); + for ( k = 0; k < Gia_ManCoNum(p); k++ ) + { + i = pOrder[k].iPo; + Ret = Acb_CnfCoDriverLit( pCnf, i, &Lit ); + if ( fVerbose ) + printf( "%s: trying output %d (%d/%d), cone = %d ANDs.\n", + pLabel, i, k + 1, Gia_ManCoNum(p), pOrder[k].nAnds ); + if ( Ret == -2 ) + { + nUndec++; + if ( fVerbose ) + printf( "%s: output %d UNDECIDED because its CNF driver is unmapped.\n", pLabel, i ); + if ( fStopOnUndec ) + break; + continue; + } + if ( Ret == -1 ) + { + nUnsat++; + if ( fVerbose ) + printf( "%s: output %d UNSAT because it is constant 0.\n", pLabel, i ); + continue; + } + if ( Ret == 0 ) + { + nSat++; + pModel = ABC_CALLOC( int, Aig_ManCiNum(pMan) ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + printf( "%s found SAT on output %d. ", pLabel, pOrder[k].iPo ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + goto cleanup; + } + Status = cadical_solver_solve( pSat, &Lit, &Lit + 1, (ABC_INT64_T)nPoConfLimit, 0, 0, 0 ); + if ( Status == 1 ) + { + Aig_Obj_t * pObjCi; + nSat++; + pModel = ABC_ALLOC( int, Aig_ManCiNum(pMan) ); + Aig_ManForEachCi( pMan, pObjCi, i ) + pModel[i] = cadical_solver_get_var_value( pSat, pCnf->pVarNums[pObjCi->Id] ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + printf( "%s found SAT on output %d. ", pLabel, pOrder[k].iPo ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + goto cleanup; + } + if ( Status == -1 ) + { + nUnsat++; + if ( fVerbose ) + printf( "%s: output %d UNSAT. conflicts = %d. learned = %d.\n", + pLabel, i, cadical_solver_nconflicts(pSat), cadical_solver_nlearned(pSat) ); + } + else + { + nUndec++; + if ( fVerbose ) + printf( "%s: output %d UNDECIDED. conflicts = %d. learned = %d.\n", + pLabel, i, cadical_solver_nconflicts(pSat), cadical_solver_nlearned(pSat) ); + if ( fStopOnUndec ) + { + if ( fVerbose ) + printf( "%s: stopping after first UNDECIDED output because this proof needs every output UNSAT.\n", pLabel ); + break; + } + if ( (fManyOutputs || Gia_ManCoNum(p) > 16) && k + 1 < Gia_ManCoNum(p) ) + { + cadical_solver_delete( pSat ); + pSat = cadical_solver_new(); + if ( pSat == NULL || !Acb_CnfWriteIntoCadical( pSat, pCnf ) ) + goto cleanup; + if ( fVerbose ) + printf( "%s: reset solver after undecided output %d to avoid carrying unrelated learned clauses.\n", + pLabel, i ); + } + if ( fManyOutputs && nUndec >= nMaxManyUndec && nUnsat == 0 ) + { + if ( fVerbose ) + printf( "%s: stopping early after %d many-output UNDECIDED probes; moving to next XEC method.\n", + pLabel, nUndec ); + break; + } + } + } + if ( pStatus ) + *pStatus = nUndec ? -1 : 1; + printf( "%s is %s. ", pLabel, nUndec ? "UNDECIDED" : "UNSAT" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); +cleanup: + if ( fVerbose && pSat ) + printf( "%s stats: SAT = %d. UNSAT = %d. UNDEC = %d. conflicts = %d. learned = %d.\n", + pLabel, nSat, nUnsat, nUndec, cadical_solver_nconflicts(pSat), cadical_solver_nlearned(pSat) ); + if ( pOrder ) + ABC_FREE( pOrder ); + if ( pSat ) + cadical_solver_delete( pSat ); + if ( pCnf ) + Cnf_DataFree( pCnf ); + if ( pMan ) + Aig_ManStop( pMan ); + return pModel; +} +void Acb_NtkSortSplitOutputsLimit( Gia_Man_t * p, Acb_SplitPoOrder_t * pOrder, int nPos ) +{ + Gia_Obj_t * pObj; + Acb_SplitPoOrder_t Temp; + int i, k, iObj; + assert( nPos > 0 && nPos <= Gia_ManCoNum(p) ); + for ( i = 0; i < nPos; i++ ) + { + pObj = Gia_ManCo( p, i ); + iObj = Gia_ObjId( p, pObj ); + pOrder[i].iPo = i; + pOrder[i].nAnds = Gia_ManConeSize( p, &iObj, 1 ); + } + for ( i = 1; i < nPos; i++ ) + { + Temp = pOrder[i]; + for ( k = i; k > 0 && pOrder[k-1].nAnds > Temp.nAnds; k-- ) + pOrder[k] = pOrder[k-1]; + pOrder[k] = Temp; + } +} +void Acb_NtkSortSplitOutputs( Gia_Man_t * p, Acb_SplitPoOrder_t * pOrder ) +{ + Acb_NtkSortSplitOutputsLimit( p, pOrder, Gia_ManCoNum(p) ); +} +int Acb_GiaRequiredLiteralContradiction( Gia_Man_t * p, int iPo, int fVerbose ) +{ + Vec_Int_t * vStack = Vec_IntAlloc( 1024 ); + Vec_Int_t * vAssign = Vec_IntStartFull( Gia_ManObjNum(p) ); + Gia_Obj_t * pObj; + int Lit, Var, Sign, Val, nSeen = 0, Ret = ACB_XEC_UNDEC; + if ( iPo < 0 || iPo >= Gia_ManCoNum(p) ) + { + Vec_IntFree( vStack ); + Vec_IntFree( vAssign ); + return Ret; + } + Vec_IntPush( vStack, Gia_ObjFaninLit0p(p, Gia_ManCo(p, iPo)) ); + while ( Vec_IntSize(vStack) > 0 ) + { + Lit = Vec_IntPop( vStack ); + Var = Abc_Lit2Var(Lit); + Sign = Abc_LitIsCompl(Lit); + if ( Var == 0 ) + { + if ( Sign == 0 ) + { + Ret = ACB_XEC_EQ; + break; + } + continue; + } + Val = Vec_IntEntry( vAssign, Var ); + if ( Val >= 0 ) + { + if ( Val != (Sign ? 0 : 1) ) + { + Ret = ACB_XEC_EQ; + break; + } + continue; + } + Vec_IntWriteEntry( vAssign, Var, Sign ? 0 : 1 ); + nSeen++; + pObj = Gia_ManObj( p, Var ); + if ( !Sign && Gia_ObjIsAnd(pObj) ) + { + Vec_IntPush( vStack, Gia_ObjFaninLit0(pObj, Var) ); + Vec_IntPush( vStack, Gia_ObjFaninLit1(pObj, Var) ); + } + if ( nSeen > 200000 ) + break; + } + if ( fVerbose ) + printf( "Required-literal structural proof: output %d %s. required literals = %d.\n", + iPo, Ret == ACB_XEC_EQ ? "UNSAT" : "inconclusive", nSeen ); + Vec_IntFree( vStack ); + Vec_IntFree( vAssign ); + return Ret; +} +Vec_Int_t * Acb_GiaCollectRequiredLiteralAssigns( Gia_Man_t * p, int iPo, int fVerbose, int * pStatus ); +int Acb_GiaRequiredLiteralUnitProof( Gia_Man_t * p, int iPo, int fVerbose, int nSatTimeLimit ) +{ + Vec_Int_t * vAssign = NULL, * vReq = NULL; + Gia_Obj_t * pObj; + int i, Val, Status = ACB_XEC_UNDEC, nReq = 0; + if ( p == NULL || iPo < 0 || iPo >= Gia_ManCoNum(p) || Gia_ManAndNum(p) > 30000 ) + return ACB_XEC_UNDEC; + vAssign = Acb_GiaCollectRequiredLiteralAssigns( p, iPo, 0, &Status ); + if ( Status == ACB_XEC_EQ ) + { + Vec_IntFreeP( &vAssign ); + return Status; + } + if ( vAssign == NULL ) + return ACB_XEC_UNDEC; + vReq = Vec_IntAlloc( 100 ); + Gia_ManForEachObj1( p, pObj, i ) + { + Val = Vec_IntEntry( vAssign, i ); + if ( Val < 0 ) + continue; + Vec_IntPush( vReq, Abc_Var2Lit(i, Val ? 0 : 1) ); + nReq++; + } + if ( nReq < 2 || nReq > 512 ) + Status = ACB_XEC_UNDEC; + else + { + Status = Acb_GiaSolveObligationListUnit( p, vReq, 0, nSatTimeLimit, NULL ); + if ( fVerbose ) + printf( "Required-literal unit proof: output %d %s. required literals = %d.\n", + iPo, Status == ACB_XEC_EQ ? "UNSAT" : "inconclusive", nReq ); + } + Vec_IntFreeP( &vAssign ); + Vec_IntFreeP( &vReq ); + return Status; +} +Vec_Int_t * Acb_GiaCollectRequiredLiteralAssigns( Gia_Man_t * p, int iPo, int fVerbose, int * pStatus ) +{ + Vec_Int_t * vStack = Vec_IntAlloc( 1024 ); + Vec_Int_t * vAssign = Vec_IntStartFull( Gia_ManObjNum(p) ); + Gia_Obj_t * pObj; + int Lit, Var, Sign, Val, nSeen = 0; + *pStatus = ACB_XEC_UNDEC; + if ( iPo < 0 || iPo >= Gia_ManCoNum(p) ) + { + Vec_IntFree( vStack ); + return vAssign; + } + Vec_IntPush( vStack, Gia_ObjFaninLit0p(p, Gia_ManCo(p, iPo)) ); + while ( Vec_IntSize(vStack) > 0 ) + { + Lit = Vec_IntPop( vStack ); + Var = Abc_Lit2Var(Lit); + Sign = Abc_LitIsCompl(Lit); + if ( Var == 0 ) + { + if ( Sign == 0 ) + { + *pStatus = ACB_XEC_EQ; + break; + } + continue; + } + Val = Vec_IntEntry( vAssign, Var ); + if ( Val >= 0 ) + { + if ( Val != (Sign ? 0 : 1) ) + { + *pStatus = ACB_XEC_EQ; + break; + } + continue; + } + Vec_IntWriteEntry( vAssign, Var, Sign ? 0 : 1 ); + nSeen++; + pObj = Gia_ManObj( p, Var ); + if ( !Sign && Gia_ObjIsAnd(pObj) ) + { + Vec_IntPush( vStack, Gia_ObjFaninLit0(pObj, Var) ); + Vec_IntPush( vStack, Gia_ObjFaninLit1(pObj, Var) ); + } + if ( nSeen > 200000 ) + break; + } + if ( fVerbose && *pStatus == ACB_XEC_EQ ) + printf( "Required-literal cofactor: output %d is UNSAT before cofactoring. required literals = %d.\n", iPo, nSeen ); + Vec_IntFree( vStack ); + return vAssign; +} +static int Acb_XecRemainingTimeLimit( abctime clkLimit, int nCap ) +{ + if ( clkLimit == 0 ) + return nCap; + if ( Abc_Clock() >= clkLimit ) + return 0; + return Abc_MinInt( nCap, (int)((clkLimit - Abc_Clock()) / CLOCKS_PER_SEC) ); +} +static int Acb_XecLocalConeStatus( int nSat, int nUnsat, int nSkipUnsat, int nUndec, int nOuts ) +{ + if ( nSat ) + return ACB_XEC_NEQ; + if ( nUndec == 0 ) + return nUnsat + nSkipUnsat == nOuts ? ACB_XEC_EQ : ACB_XEC_UNDEC; + if ( nUndec == 1 && nUnsat + nSkipUnsat == nOuts - 1 ) + return ACB_XEC_ONE_HARD; + if ( nUnsat + nSkipUnsat + nUndec == nOuts && nUndec > 1 ) + return ACB_XEC_MANY_HARD; + return ACB_XEC_UNDEC; +} +static int Acb_XecLocalConeKeepSweeping( int fQuickMany, int fMediumSweep, int fResumeSweep, int nUndec, int nMaxUndec ) +{ + if ( nUndec >= nMaxUndec ) + return 0; + return fQuickMany || fMediumSweep || fResumeSweep; +} +static Gia_Man_t * Acb_XecLocalConePrepare( Gia_Man_t * p, int iPo, Vec_Int_t ** pvSuppMap, Acb_XecCtx_t * pCtx, int fVerbose ) +{ + Gia_Man_t * pOne, * pTemp; + Vec_Int_t * vSuppMap = Vec_IntAlloc( 1000 ); + pOne = Acb_GiaDupOnePoTrimmed( p, iPo, vSuppMap ); + if ( pOne == NULL ) + { + Vec_IntFree( vSuppMap ); + *pvSuppMap = NULL; + return NULL; + } + if ( Gia_ManAndNum(pOne) > pCtx->Pars.nLocalConeCompressAndMin ) + { + pTemp = Gia_ManCompress2( pOne, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pOne ); + pOne = pTemp; + } + } + if ( Vec_IntSize(vSuppMap) <= 64 && Gia_ManAndNum(pOne) <= 8000 ) + { + pTemp = Acb_XecGiaSmallConeXorRewrite( pOne, fVerbose ); + if ( pTemp ) + { + Gia_ManStop( pOne ); + pOne = pTemp; + } + } + *pvSuppMap = vSuppMap; + return pOne; +} +static int * Acb_XecLocalConeExpandModel( Gia_Man_t * p, Gia_Man_t * pOne, Vec_Int_t * vSuppMap, int * pModel ) +{ + int * pModelFull = NULL; + int i, iObj; + if ( pModel == NULL ) + return NULL; + if ( Vec_IntSize(vSuppMap) != Gia_ManCiNum(pOne) ) + { + ABC_FREE( pModel ); + return NULL; + } + pModelFull = ABC_CALLOC( int, Gia_ManCiNum(p) ); + Vec_IntForEachEntry( vSuppMap, iObj, i ) + if ( Gia_ObjIsCi(Gia_ManObj(p, iObj)) ) + pModelFull[Gia_ObjCioId(Gia_ManObj(p, iObj))] = pModel[i]; + ABC_FREE( pModel ); + return pModelFull; +} +static int Acb_XecLocalConeProof( Gia_Man_t * pOne, int nSuppSize, int nLimit, abctime clkLimit, int fVerbose, int ** ppModel ) +{ + int Status = ACB_XEC_UNDEC; + int nExhLimit, nFrontLimit; + *ppModel = NULL; + if ( Gia_ManCoNum(pOne) == 1 && Gia_ManAndNum(pOne) > 0 && + (Gia_ManAndNum(pOne) >= 1000 || nSuppSize >= 32) ) + { + int nSimWords = Gia_ManAndNum(pOne) <= 5000 ? 256 : 64; + *ppModel = Acb_GiaFindOnePoSimCex( pOne, nSimWords, fVerbose, "Local-cone hard-output" ); + if ( *ppModel ) + return ACB_XEC_NEQ; + } + if ( Gia_ManAndNum(pOne) <= 8000 ) + Status = Acb_GiaRequiredLiteralContradiction( pOne, 0, fVerbose ); + if ( Status == ACB_XEC_UNDEC && Gia_ManAndNum(pOne) <= 8000 && nSuppSize >= 40 && nSuppSize <= 64 ) + Status = Acb_GiaRequiredLiteralUnitProof( pOne, 0, fVerbose, Abc_MinInt(nLimit, 120) ); + if ( Status == ACB_XEC_UNDEC && Gia_ManAndNum(pOne) < 5000 && nSuppSize >= 32 && nSuppSize <= 64 ) + { + nFrontLimit = Acb_XecRemainingTimeLimit( clkLimit, nLimit > 120 ? 180 : 90 ); + if ( nFrontLimit >= 20 ) + Status = Acb_GiaSolveSmallConeInternalFrontier( pOne, fVerbose, nFrontLimit ); + } + if ( Status == ACB_XEC_UNDEC && Gia_ManAndNum(pOne) <= 5000 ) + { + nExhLimit = Acb_XecRemainingTimeLimit( clkLimit, 600 ); + if ( nExhLimit >= 30 ) + Status = Acb_XecGiaSolveSmallConeExhaustive( pOne, fVerbose, nExhLimit ); + } + if ( Status == ACB_XEC_UNDEC ) + *ppModel = Acb_NtkSolveCadicalLimit( pOne, 0, 0, &Status, nLimit, NULL, 0 ); + return Status; +} +int * Acb_NtkSolveCadicalLocalConeSweepSkipCtx( Gia_Man_t * p, int fVerbose, int * pStatus, int nSatTimeLimit, int nPoTimeLimit, Vec_Int_t * vSkipUnsat, Acb_XecCtx_t * pCtx ) +{ + Acb_SplitPoOrder_t * pOrder; + int i, k, StatusOne, StatusFinal, nSat = 0, nUnsat = 0, nUndec = 0, iLastUndec = -1; + int nSkipUnsat = vSkipUnsat ? Vec_IntSize(vSkipUnsat) : 0; + int fDisableQuickMany = nPoTimeLimit < 0; + int fResumeSweep = vSkipUnsat != NULL; + int fStopAfterFirstHard, fMediumSweep, fQuickMany, nProbeLimit, nHardProbeLimit, nMaxUndec; + int * pModel = NULL; + abctime clk = Abc_Clock(); + abctime clkLimit = nSatTimeLimit > 0 ? clk + nSatTimeLimit * CLOCKS_PER_SEC : 0; + assert( pCtx != NULL ); + Acb_XecCtxResetLocalSweep( pCtx ); + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( Gia_ManCoNum(p) == 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + return NULL; + } + if ( fDisableQuickMany ) + nPoTimeLimit = -nPoTimeLimit; + fStopAfterFirstHard = pCtx->Pars.nLocalMediumPoMin == ABC_INFINITY; + fMediumSweep = !fStopAfterFirstHard && Gia_ManCoNum(p) > pCtx->Pars.nLocalMediumPoMin && Gia_ManCoNum(p) <= pCtx->Pars.nLocalMediumPoMax; + fQuickMany = !fStopAfterFirstHard && !fDisableQuickMany && Gia_ManCoNum(p) > pCtx->Pars.nLocalManyPoThreshold; + nMaxUndec = fStopAfterFirstHard ? 1 : (fResumeSweep ? Gia_ManCoNum(p) : (fQuickMany ? pCtx->Pars.nLocalQuickMaxUndec : (fMediumSweep ? Gia_ManCoNum(p) : 1))); + nProbeLimit = nPoTimeLimit; + if ( fQuickMany ) + nProbeLimit = nPoTimeLimit > 0 ? Abc_MinInt( nPoTimeLimit, pCtx->Pars.nLocalQuickPoSec ) : pCtx->Pars.nLocalQuickPoSec; + else if ( fMediumSweep ) + nProbeLimit = nPoTimeLimit > 0 ? Abc_MinInt( nPoTimeLimit, pCtx->Pars.nLocalMediumPoSec ) : pCtx->Pars.nLocalMediumPoSec; + nHardProbeLimit = fResumeSweep ? nPoTimeLimit : (fQuickMany ? pCtx->Pars.nLocalQuickPoSec : (fMediumSweep ? pCtx->Pars.nLocalMediumHardPoSec : nPoTimeLimit)); + pOrder = ABC_ALLOC( Acb_SplitPoOrder_t, Gia_ManCoNum(p) ); + Acb_NtkSortSplitOutputs( p, pOrder ); + if ( fVerbose ) + printf( "Local-cone CaDiCaL sweep: outputs = %d. skip = %d. smallest cone %d ANDs, largest cone %d ANDs. total limit = %d sec, per-output limit = %d sec%s.\n", + Gia_ManCoNum(p), nSkipUnsat, pOrder[0].nAnds, pOrder[Gia_ManCoNum(p)-1].nAnds, nSatTimeLimit, nProbeLimit, + fQuickMany ? " (SAT-hunting quick probe for many-output miter)" : "" ); + for ( k = 0; k < Gia_ManCoNum(p); k++ ) + { + Gia_Man_t * pOne; + Vec_Int_t * vSuppMap = NULL; + abctime clkOut = Abc_Clock(); + int nBaseLimit = nUndec > 0 ? nHardProbeLimit : nProbeLimit; + int nLimit = Acb_XecRemainingTimeLimit( clkLimit, nBaseLimit ); + int nSuppSize; + i = pOrder[k].iPo; + if ( vSkipUnsat && Vec_IntFind(vSkipUnsat, i) >= 0 ) + { + if ( fVerbose ) + printf( "Local-cone CaDiCaL: skipping output %d because it is already proven UNSAT.\n", i ); + continue; + } + if ( clkLimit && nLimit <= 0 ) + { + nUndec++; + iLastUndec = i; + Vec_IntPushUnique( pCtx->vLastHardPos, i ); + break; + } + pOne = Acb_XecLocalConePrepare( p, i, &vSuppMap, pCtx, fVerbose ); + if ( pOne == NULL ) + { + nUndec++; + iLastUndec = i; + Vec_IntPushUnique( pCtx->vLastHardPos, i ); + break; + } + nSuppSize = Vec_IntSize( vSuppMap ); + if ( Gia_ManCiNum(p) <= 64 && Gia_ManCoNum(p) >= 16 && Gia_ManCoNum(p) <= 64 && + nUnsat >= 3 && nSuppSize >= 40 && nSuppSize <= 56 && + Gia_ManAndNum(pOne) <= 5000 && nLimit <= 120 ) + nLimit = Abc_MaxInt( nLimit, Acb_XecRemainingTimeLimit( clkLimit, nUnsat >= 4 ? 420 : 180 ) ); + if ( fVerbose ) + printf( "Local-cone CaDiCaL: output %d (%d/%d), cone = %d ANDs, support = %d/%d PIs, limit = %d sec.\n", + i, k + 1, Gia_ManCoNum(p), Gia_ManAndNum(pOne), nSuppSize, Gia_ManCiNum(p), nLimit ); + StatusOne = Acb_XecLocalConeProof( pOne, nSuppSize, nLimit, clkLimit, fVerbose && (nUndec > 0 || nSuppSize >= 40), &pModel ); + if ( StatusOne == ACB_XEC_NEQ ) + { + nSat++; + pModel = Acb_XecLocalConeExpandModel( p, pOne, vSuppMap, pModel ); + if ( fVerbose ) + { + printf( "Local-cone CaDiCaL: output %d SAT. ", i ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clkOut ); + } + Gia_ManStop( pOne ); + Vec_IntFree( vSuppMap ); + break; + } + if ( pModel ) + { + ABC_FREE( pModel ); + pModel = NULL; + } + if ( StatusOne == ACB_XEC_EQ ) + { + nUnsat++; + Vec_IntPushUnique( pCtx->vLastProvenPos, i ); + if ( fVerbose ) + { + printf( "Local-cone CaDiCaL: output %d UNSAT. ", i ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clkOut ); + } + Gia_ManStop( pOne ); + Vec_IntFree( vSuppMap ); + continue; + } + nUndec++; + iLastUndec = i; + Vec_IntPushUnique( pCtx->vLastHardPos, i ); + if ( fVerbose ) + { + printf( "Local-cone CaDiCaL: output %d UNDECIDED. ", i ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clkOut ); + } + Gia_ManStop( pOne ); + Vec_IntFree( vSuppMap ); + if ( Acb_XecLocalConeKeepSweeping( fQuickMany, fMediumSweep, fResumeSweep, nUndec, nMaxUndec ) ) + { + if ( fVerbose ) + printf( "Local-cone CaDiCaL: continuing after hard output %d; undecided probes = %d/%d.\n", + i, nUndec, nMaxUndec ); + continue; + } + break; + } + StatusFinal = Acb_XecLocalConeStatus( nSat, nUnsat, nSkipUnsat, nUndec, Gia_ManCoNum(p) ); + if ( StatusFinal == ACB_XEC_ONE_HARD || StatusFinal == ACB_XEC_MANY_HARD ) + pCtx->LastHardPo = iLastUndec; + if ( pStatus ) + *pStatus = StatusFinal; + printf( "The networks are %s by local-cone CaDiCaL sweep. ", + nSat ? "NOT equivalent" : (nUndec ? "UNDECIDED" : "equivalent") ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + if ( fVerbose ) + { + printf( "Local-cone CaDiCaL stats: SAT = %d. UNSAT = %d. SKIP = %d. UNDEC = %d.\n", nSat, nUnsat, nSkipUnsat, nUndec ); + if ( StatusFinal == ACB_XEC_ONE_HARD ) + printf( "Local-cone CaDiCaL: only output %d remains hard; whole-miter CaDiCaL would duplicate this cone.\n", iLastUndec ); + else if ( StatusFinal == ACB_XEC_MANY_HARD ) + printf( "Local-cone CaDiCaL: %d outputs remain hard after complete short-probe sweep.\n", nUndec ); + } + ABC_FREE( pOrder ); + return pModel; +} +int * Acb_NtkSolveCadicalOdc( Gia_Man_t * p, int fVerbose, int * pStatus ) +{ + Gia_Man_t * pOne; + Acb_SplitPoOrder_t * pOrder; + int i, k, Status = -1, fOneUndef = 0, * pModel = NULL; + int nConeTimeLimit = 5; + int nTotalTimeLimit = 60; + abctime clk = Abc_Clock(); + if ( Gia_ManCoNum(p) == 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + printf( "The networks are equivalent by ODC CaDiCaL. " ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return NULL; + } + pOrder = ABC_ALLOC( Acb_SplitPoOrder_t, Gia_ManCoNum(p) ); + Acb_NtkSortSplitOutputs( p, pOrder ); + if ( fVerbose ) + printf( "ODC CaDiCaL: solving one observable output cone at a time; cone limit = %d sec, total limit = %d sec.\n", + nConeTimeLimit, nTotalTimeLimit ); + for ( k = 0; k < Gia_ManCoNum(p); k++ ) + { + if ( (Abc_Clock() - clk) / CLOCKS_PER_SEC >= nTotalTimeLimit ) + { + fOneUndef = 1; + break; + } + i = pOrder[k].iPo; + if ( fVerbose ) + printf( "ODC CaDiCaL output %d: cone ANDs = %d.\n", i, pOrder[k].nAnds ); + pOne = Gia_ManDupCones( p, &i, 1, 0 ); + pModel = Acb_NtkSolveCadicalLimit( pOne, 0, fVerbose, &Status, nConeTimeLimit, NULL, 0 ); + Gia_ManStop( pOne ); + if ( Status == 0 ) + { + ABC_FREE( pOrder ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + printf( "The networks are NOT equivalent by ODC CaDiCaL on output %d. ", i ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return pModel; + } + if ( Status == -1 ) + fOneUndef = 1; + } + ABC_FREE( pOrder ); + if ( pStatus ) + *pStatus = fOneUndef ? -1 : 1; + printf( "The networks are %s by ODC CaDiCaL. ", fOneUndef ? "UNDECIDED" : "equivalent" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return NULL; +} + +int * Acb_NtkSolveSplit( Gia_Man_t * p, int fVerbose, int * pStatus ) +{ + Gia_Man_t * pOne; + Acb_SplitPoOrder_t * pOrder; + int i, k, Status, fOneUndef = 0, * pModel = NULL; + abctime clk = Abc_Clock(); + Abc_CexFreeP( &p->pCexComb ); + if ( Gia_ManCoNum(p) == 0 ) + { + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + printf( "The networks are equivalent by split SAT. " ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return NULL; + } + pOrder = ABC_ALLOC( Acb_SplitPoOrder_t, Gia_ManCoNum(p) ); + Acb_NtkSortSplitOutputs( p, pOrder ); + if ( fVerbose && Gia_ManCoNum(p) > 1 ) + printf( "Split SAT output order: smallest cone %d ANDs, largest cone %d ANDs.\n", + pOrder[0].nAnds, pOrder[Gia_ManCoNum(p)-1].nAnds ); + for ( k = 0; k < Gia_ManCoNum(p); k++ ) + { + i = pOrder[k].iPo; + pOne = Gia_ManDupCones( p, &i, 1, 0 ); + pModel = Acb_NtkSolveCadicalLimit( pOne, 0, fVerbose, &Status, 0, NULL, 0 ); + if ( Status == 0 ) + { + Gia_ManStop( pOne ); + ABC_FREE( pOrder ); + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + printf( "The networks are NOT equivalent by split SAT on output %d. ", i ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return pModel; + } + ABC_FREE( pModel ); + pModel = NULL; + if ( Status == ACB_XEC_UNDEC ) + fOneUndef = 1; + Gia_ManStop( pOne ); + } + ABC_FREE( pOrder ); + if ( pStatus ) + *pStatus = fOneUndef ? -1 : 1; + printf( "The networks are %s by split SAT. ", fOneUndef ? "UNDECIDED" : "equivalent" ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); return NULL; } @@ -521,7 +4574,7 @@ int * Acb_NtkSolve( Gia_Man_t * p ) Synopsis [Various statistics.] Description [] - + SideEffects [] SeeAlso [] @@ -544,12 +4597,105 @@ void Acb_NtkPrintCecStats( Acb_Ntk_t * pNtk ) printf( "\n" ); } +void Acb_NtkCountXConstructs( Acb_Ntk_t * pNtk, int * pnDcs, int * pnMuxes, int * pnConstXs ) +{ + int iObj; + *pnDcs = *pnMuxes = *pnConstXs = 0; + Acb_NtkForEachNode( pNtk, iObj ) + if ( Acb_ObjType( pNtk, iObj ) == ABC_OPER_TRI ) + (*pnDcs)++; + else if ( Acb_ObjType( pNtk, iObj ) == ABC_OPER_BIT_MUX ) + (*pnMuxes)++; + else if ( Acb_ObjType( pNtk, iObj ) == ABC_OPER_CONST_X ) + (*pnConstXs)++; +} + +int * Acb_NtkSolveBinaryCec( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG, int fVerbose, int * pStatus, int nTimeLimit ) +{ + extern Vec_Int_t * Acb_NtkFindNodes( Acb_Ntk_t * p, Vec_Int_t * vRoots, Vec_Int_t * vDivs ); + extern Gia_Man_t * Acb_NtkToGia( Acb_Ntk_t * p, Vec_Int_t * vSupp, Vec_Int_t * vNodes, Vec_Int_t * vRoots, Vec_Int_t * vDivs, Vec_Int_t * vTargets ); + Vec_Int_t * vRoots = Vec_IntAlloc( Acb_NtkCoNum(pNtkF) ); + Vec_Int_t * vSupp = Vec_IntAlloc( Acb_NtkCiNum(pNtkF) ); + Vec_Int_t * vNodesF = NULL, * vNodesG = NULL; + Gia_Man_t * pGiaF = NULL, * pGiaG = NULL, * pMiter = NULL; + int i, RetValue = ACB_XEC_UNDEC, * pModel = NULL; + abctime clk = Abc_Clock(); + for ( i = 0; i < Acb_NtkCoNum(pNtkF); i++ ) + Vec_IntPush( vRoots, i ); + for ( i = 0; i < Acb_NtkCiNum(pNtkF); i++ ) + Vec_IntPush( vSupp, i ); + vNodesF = Acb_NtkFindNodes( pNtkF, vRoots, NULL ); + vNodesG = Acb_NtkFindNodes( pNtkG, vRoots, NULL ); + pGiaF = Acb_NtkToGia( pNtkF, vSupp, vNodesF, vRoots, NULL, NULL ); + pGiaG = Acb_NtkToGia( pNtkG, vSupp, vNodesG, vRoots, NULL, NULL ); + pMiter = Gia_ManMiter( pGiaF, pGiaG, 0, 0, 0, 0, fVerbose ); + if ( pMiter == NULL ) + { + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + Gia_ManStop( pGiaF ); + Gia_ManStop( pGiaG ); + Vec_IntFree( vNodesF ); + Vec_IntFree( vNodesG ); + Vec_IntFree( vRoots ); + Vec_IntFree( vSupp ); + return NULL; + } + if ( Gia_ManAndNum(pMiter) > 5000 ) + { + Gia_Man_t * pTemp; + int nAndBefore = Gia_ManAndNum(pMiter); + int nLevBefore = Gia_ManLevelNum(pMiter); + pTemp = Gia_ManCompress2( pMiter, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pMiter ); + pMiter = pTemp; + if ( fVerbose ) + printf( "Conventional binary XOR-miter compression: And = %d -> %d. Lev = %d -> %d. PO = %d.\n", + nAndBefore, Gia_ManAndNum(pMiter), nLevBefore, Gia_ManLevelNum(pMiter), Gia_ManCoNum(pMiter) ); + } + } + if ( fVerbose ) + { + printf( "Trying conventional binary XOR-miter CaDiCaL for no-X design: PI = %d. PO = %d. And = %d. limit = %d sec.\n", + Gia_ManCiNum(pMiter), Gia_ManCoNum(pMiter), Gia_ManAndNum(pMiter), nTimeLimit ); + Gia_ManPrintStats( pMiter, NULL ); + } + pModel = Acb_NtkSolveCadicalLimit( pMiter, 0, fVerbose, &RetValue, nTimeLimit, NULL, 0 ); + if ( pStatus ) + *pStatus = RetValue; + if ( RetValue == 0 && pModel ) + { + if ( !Acb_NtkCheckModelCexAcbBool( pNtkF, pNtkG, pModel, fVerbose ) ) + { + ABC_FREE( pModel ); + pModel = NULL; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + RetValue = ACB_XEC_UNDEC; + printf( "The binary XOR-miter CaDiCaL SAT model is not a valid original Boolean counterexample; treating it as UNDECIDED.\n" ); + } + } + printf( "The networks are %s by conventional binary XOR-miter CaDiCaL. ", + RetValue == 1 ? "equivalent" : (RetValue == 0 ? "NOT equivalent" : "UNDECIDED") ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + Gia_ManStop( pMiter ); + Gia_ManStop( pGiaF ); + Gia_ManStop( pGiaG ); + Vec_IntFree( vNodesF ); + Vec_IntFree( vNodesG ); + Vec_IntFree( vRoots ); + Vec_IntFree( vSupp ); + return pModel; +} + /**Function************************************************************* Synopsis [Changing the PI order.] Description [] - + SideEffects [] SeeAlso [] @@ -601,27 +4747,54 @@ int Acb_NtkCheckPiOrder( Acb_Ntk_t * pNtkF, Acb_Ntk_t * pNtkG ) Synopsis [] Description [] - + SideEffects [] SeeAlso [] ***********************************************************************/ -void Acb_NtkRunTest( char * pFileNames[4], int fFancy, int fVerbose ) +void Acb_NtkRunTest( char * pFileNames[4], int fFancy, int fVerbose, int fUseCadical ) { - extern Acb_Ntk_t * Acb_VerilogSimpleRead( char * pFileName, char * pFileNameW ); - extern void Gia_AigerWrite( Gia_Man_t * p, char * pFileName, int fWriteSymbols, int fCompact, int fWriteNewLine ); - - int fSolve = 1; + int Status = -1; int * pModel = NULL; Gia_Man_t * pGiaF = NULL; Gia_Man_t * pGiaG = NULL; Gia_Man_t * pGia = NULL; + Gia_Man_t * pGiaFCut = NULL; + Gia_Man_t * pGiaGCut = NULL; + Gia_Man_t * pGiaGCtrl = NULL; + Gia_Man_t * pGiaCut = NULL; + Gia_Man_t * pGiaX = NULL; + Gia_Man_t * pTemp = NULL; + Vec_Int_t * vCutObjsF = NULL; + Vec_Int_t * vCutObjsG = NULL; + Vec_Int_t * vMuxSelectorsG = NULL; + Vec_Int_t * vMuxPoSelIdsG = NULL; + Vec_Int_t * vSymCutObjsF = NULL; + Vec_Int_t * vSymMuxSelectorsF = NULL; + Vec_Int_t * vSymIntDcObjsF = NULL; + Vec_Int_t * vSymIntDcCtrlsF = NULL; + Vec_Int_t * vSymIntDcCtrlIdsF = NULL; + Vec_Int_t * vIntDcObjsG = NULL; + Vec_Int_t * vIntDcCtrlsG = NULL; + Vec_Int_t * vIntDcCtrlIdsG = NULL; + Vec_Int_t * vDcDataObjsG = NULL; + Vec_Int_t * vDcCtrlObjsG = NULL; + Acb_XecCtx_t XecCtx; + int fSymmetricMuxDc = 0; + int nDcsF = 0, nMuxesF = 0, nConstXsF = 0, nDcsG = 0, nMuxesG = 0, nConstXsG = 0; Acb_Ntk_t * pNtkF = Acb_VerilogSimpleRead( pFileNames[0], NULL ); Acb_Ntk_t * pNtkG = Acb_VerilogSimpleRead( pFileNames[1], NULL ); if ( !pNtkF || !pNtkG ) + { + if ( pNtkF ) + Acb_ManFree( pNtkF->pDesign ); + if ( pNtkG ) + Acb_ManFree( pNtkG->pDesign ); return; - + } + Acb_XecCtxInit( &XecCtx ); + assert( Acb_NtkCiNum(pNtkF) == Acb_NtkCiNum(pNtkG) ); assert( Acb_NtkCoNum(pNtkF) == Acb_NtkCoNum(pNtkG) ); @@ -629,28 +4802,526 @@ void Acb_NtkRunTest( char * pFileNames[4], int fFancy, int fVerbose ) //Acb_NtkCheckPiOrder( pNtkG, pNtkF ); Acb_NtkPrintCecStats( pNtkF ); Acb_NtkPrintCecStats( pNtkG ); + Acb_NtkCountXConstructs( pNtkF, &nDcsF, &nMuxesF, &nConstXsF ); + Acb_NtkCountXConstructs( pNtkG, &nDcsG, &nMuxesG, &nConstXsG ); + + if ( fUseCadical && nDcsF == 0 && nMuxesF == 0 && nConstXsF == 0 && nDcsG == 0 && nMuxesG == 0 && nConstXsG == 0 ) + { + if ( fVerbose ) + printf( "No X/DC/MUX constructs found; using conventional binary CaDiCaL instead of X-aware dual-rail proving.\n" ); + pModel = Acb_NtkSolveBinaryCec( pNtkF, pNtkG, fVerbose, &Status, 1200 ); + Acb_OutputFile( pFileNames[2], pNtkF, pModel, Status ); + ABC_FREE( pModel ); + Acb_XecCtxFree( &XecCtx ); + Acb_ManFree( pNtkF->pDesign ); + Acb_ManFree( pNtkG->pDesign ); + return; + } pGiaF = Acb_NtkGiaDeriveDual( pNtkF ); pGiaG = Acb_NtkGiaDeriveDual( pNtkG ); - pGia = Acb_NtkGiaDeriveMiter( pGiaF, pGiaG, 2 ); - //Gia_AigerWrite( pGiaF, Extra_FileNameGenericAppend(pFileNames[1], "_f2.aig"), 0, 0, 0 ); - //Gia_AigerWrite( pGiaG, Extra_FileNameGenericAppend(pFileNames[1], "_g2.aig"), 0, 0, 0 ); - //Gia_AigerWrite( pGia, Extra_FileNameGenericAppend(pFileNames[1], "_miter_0.aig"), 0, 0, 0 ); - //printf( "Written the miter info file \"%s\".\n", Extra_FileNameGenericAppend(pFileNames[1], "_miter_0.aig") ); - - //Gia_ManPrintStats( pGia, NULL ); - //Gia_ManSimTry( pGiaF, pGiaG ); - - if ( fSolve ) + if ( pGiaF == NULL || pGiaG == NULL ) { - pModel = Acb_NtkSolve( pGia ); - Acb_OutputFile( pFileNames[2], pNtkF, pModel ); + printf( "XEC dual-rail translation failed; see unsupported ACB object diagnostic above.\n" ); + Status = ACB_XEC_UNDEC; + Acb_OutputFile( pFileNames[2], pNtkF, NULL, Status ); + Gia_ManStopP( &pGiaF ); + Gia_ManStopP( &pGiaG ); + Acb_XecCtxFree( &XecCtx ); + Acb_ManFree( pNtkF->pDesign ); + Acb_ManFree( pNtkG->pDesign ); + return; + } + pGia = Acb_NtkGiaDeriveMiter( pGiaF, pGiaG, 2 ); + if ( fUseCadical ) + { + if ( Acb_NtkCollectPoDcCutpoints( pNtkG, &vDcDataObjsG, &vDcCtrlObjsG ) ) + { + vCutObjsF = Acb_NtkCollectCoDrivers( pNtkF ); + if ( fVerbose ) + printf( "Found %d output DC cutpoints in implementation network.\n", Vec_IntSize(vDcDataObjsG) ); + } + vCutObjsG = Acb_NtkCollectPoMuxCutpoints( pNtkG ); + if ( vCutObjsF == NULL && Vec_IntSize(vCutObjsG) == Acb_NtkCoNum(pNtkG) ) + { + vCutObjsF = Acb_NtkCollectCoDrivers( pNtkF ); + vMuxSelectorsG = Acb_NtkCollectPoMuxSelectors( pNtkG, vCutObjsG ); + vMuxPoSelIdsG = Acb_NtkCollectPoMuxSelectorIds( pNtkG, vCutObjsG, vMuxSelectorsG ); + if ( fVerbose ) + printf( "Found %d output partition-candidate cutpoints using %d unique selectors in implementation network.\n", + Vec_IntSize(vCutObjsG), Vec_IntSize(vMuxSelectorsG) ); + } + else if ( fVerbose && vCutObjsF == NULL && Vec_IntSize(vCutObjsG) > 0 ) + printf( "No complete output partition-candidate cutpoint set found.\n" ); + if ( Acb_NtkCollectInternalDcControls( pNtkG, &vIntDcObjsG, &vIntDcCtrlsG, &vIntDcCtrlIdsG ) && fVerbose ) + printf( "Found %d internal DC nodes using %d unique controls in implementation network.\n", + Vec_IntSize(vIntDcObjsG), Vec_IntSize(vIntDcCtrlsG) ); + if ( vIntDcObjsG && Vec_IntSize(vIntDcObjsG) > 0 && Acb_NtkCoNum(pNtkG) > 512 ) + Acb_NtkCollectInternalDcControls( pNtkF, &vSymIntDcObjsF, &vSymIntDcCtrlsF, &vSymIntDcCtrlIdsF ); + if ( vCutObjsG && vMuxSelectorsG && Vec_IntSize(vCutObjsG) == Acb_NtkCoNum(pNtkG) && + vIntDcObjsG && vIntDcCtrlsG && Vec_IntSize(vIntDcObjsG) > 0 && Vec_IntSize(vIntDcCtrlsG) > 0 ) + { + vSymCutObjsF = Acb_NtkCollectPoMuxCutpoints( pNtkF ); + if ( vSymCutObjsF && Vec_IntSize(vSymCutObjsF) == Acb_NtkCoNum(pNtkF) ) + vSymMuxSelectorsF = Acb_NtkCollectPoMuxSelectors( pNtkF, vSymCutObjsF ); + if ( vSymIntDcObjsF == NULL ) + Acb_NtkCollectInternalDcControls( pNtkF, &vSymIntDcObjsF, &vSymIntDcCtrlsF, &vSymIntDcCtrlIdsF ); + fSymmetricMuxDc = + vSymMuxSelectorsF && vSymIntDcObjsF && vSymIntDcCtrlsF && + Vec_IntSize(vSymMuxSelectorsF) == Vec_IntSize(vMuxSelectorsG) && + Vec_IntSize(vSymIntDcObjsF) == Vec_IntSize(vIntDcObjsG) && + Vec_IntSize(vSymIntDcCtrlsF) == Vec_IntSize(vIntDcCtrlsG); + if ( fVerbose && fSymmetricMuxDc ) + printf( "Detected symmetric output-MUX/internal-DC structure in both networks.\n" ); + } + } + if ( Gia_ManAndNum(pGia) > 5000 ) + { + int nAndBefore = Gia_ManAndNum(pGia); + pTemp = Gia_ManCompress2( pGia, 1, fVerbose ); + if ( pTemp ) + { + if ( fVerbose ) + printf( "XEC miter compression: And = %d -> %d. PO = %d.\n", nAndBefore, Gia_ManAndNum(pTemp), Gia_ManPoNum(pTemp) ); + Gia_ManStop( pGia ); + pGia = pTemp; + } + } + { + int nSimWords = Gia_ManAndNum(pGia) > XecCtx.Pars.nSimLargeAndMin ? XecCtx.Pars.nSimLargeWords : XecCtx.Pars.nSimSmallWords; + int fCheckModel = 0; + int fSkipWholeMiter = 0; + int fTriedWholeMiterEarly = 0; + int fSkipAsymHmuxBranch = 0; + int fSkipHighPiDcFallbacks = 0; + int fHighPiTwoCtrlDc = !fFancy && + Acb_XecIsSharedDcWholeMiterShape( pGia, vMuxSelectorsG, vIntDcObjsG, vIntDcCtrlsG, &XecCtx ); + int fConstXSeedDc = !fFancy && + nDcsF == 0 && nMuxesF == 0 && nConstXsF == 0 && + (vMuxSelectorsG == NULL || Vec_IntSize(vMuxSelectorsG) == 0) && + vIntDcObjsG && Vec_IntSize(vIntDcObjsG) > 0 && Vec_IntSize(vIntDcObjsG) <= 8 && + Acb_NtkAllDcObjsAreConstXSeeds( pNtkG, vIntDcObjsG ) && + Gia_ManCiNum(pGia) <= 512 && Gia_ManCoNum(pGia) >= 64; + pModel = Acb_NtkFindSimCex( pGiaF, pGiaG, nSimWords, fVerbose ); + if ( pModel ) + { + Status = 0; + printf( "The networks are NOT equivalent by random simulation.\n" ); + } + else + { + if ( fUseCadical ) + { + if ( Gia_ManAndNum(pGia) > XecCtx.Pars.nMainLargeAndMin || Gia_ManCiNum(pGia) > XecCtx.Pars.nMainLargePiMin || Gia_ManCoNum(pGia) > XecCtx.Pars.nMainLargePoMin ) + { + if ( Status == -1 && !fFancy && vDcDataObjsG && vDcCtrlObjsG && + vIntDcObjsG && Vec_IntSize(vIntDcObjsG) > 0 && + vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) == 1 && + (vMuxSelectorsG == NULL || Vec_IntSize(vMuxSelectorsG) == 0) && + Vec_IntSize(vDcDataObjsG) == Acb_NtkCoNum(pNtkG) && + Gia_ManCiNum(pGia) <= 512 && Gia_ManCoNum(pGia) > 32 ) + { + int StatusPre = -1; + pModel = Acb_NtkSolveNormalPrecheck( pGia, fVerbose, &StatusPre, 750000 ); + Acb_XecMergeTargetStatus( StatusPre, pModel != NULL, &Status, &fCheckModel ); + } + if ( !fFancy && !fSkipHighPiDcFallbacks && Status == -1 && vCutObjsF && vDcDataObjsG && vDcCtrlObjsG ) + { + pGiaFCut = Acb_NtkGiaDeriveDualTargets( pNtkF, vCutObjsF ); + pGiaGCut = Acb_NtkGiaDeriveDualTargets( pNtkG, vDcDataObjsG ); + pGiaGCtrl = Acb_NtkGiaDeriveDualTargets( pNtkG, vDcCtrlObjsG ); + pGiaCut = Acb_NtkGiaDeriveMiterDcGuard( pGiaFCut, pGiaGCut, pGiaGCtrl ); + if ( Gia_ManAndNum(pGiaCut) >= Gia_ManAndNum(pGia) ) + { + if ( fVerbose ) + printf( "Skipping output-DC guarded cutpoint miter because it is not smaller: And = %d, current = %d.\n", + Gia_ManAndNum(pGiaCut), Gia_ManAndNum(pGia) ); + } + else + { + if ( fVerbose ) + printf( "Trying output-DC guarded cutpoint CaDiCaL sweep before whole-miter CaDiCaL: And = %d. PO = %d.\n", + Gia_ManAndNum(pGiaCut), Gia_ManPoNum(pGiaCut) ); + pModel = Acb_NtkSolveCadicalLimit( pGiaCut, 0, fVerbose, &Status, 900, "output-DC guarded cutpoint CaDiCaL", 0 ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + } + } + if ( Status == -1 && !fFancy && + (vMuxSelectorsG == NULL || Vec_IntSize(vMuxSelectorsG) == 0) && + vIntDcObjsG && Vec_IntSize(vIntDcObjsG) > 0 && Vec_IntSize(vIntDcObjsG) <= 8 && + vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) > 1 && Vec_IntSize(vIntDcCtrlsG) <= 2 && + Gia_ManCiNum(pGia) <= 512 && Gia_ManCoNum(pGia) > 64 ) + { + int StatusTarget = -1; + pModel = Acb_NtkSolveDcControlWholeCubes( pNtkF, pNtkG, + vIntDcObjsG, vIntDcCtrlsG, vIntDcCtrlIdsG, fVerbose, &StatusTarget, 900, 300 ); + Acb_XecMergeTargetStatus( StatusTarget, pModel != NULL, &Status, &fCheckModel ); + } + if ( Status == -1 && fConstXSeedDc ) + { + int StatusConstX = -1; + pModel = Acb_NtkSolveConstXSeedCanonical( pNtkF, pNtkG, vIntDcObjsG, + fVerbose, &StatusConstX, 1200 ); + Acb_XecMergeTargetStatus( StatusConstX, pModel != NULL, &Status, &fCheckModel ); + } + if ( Status == -1 && !fFancy && + (vMuxSelectorsG == NULL || Vec_IntSize(vMuxSelectorsG) == 0) && + vIntDcObjsG && Vec_IntSize(vIntDcObjsG) > 0 && + vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) > 0 && Vec_IntSize(vIntDcCtrlsG) <= 2 && + (fHighPiTwoCtrlDc || (Gia_ManCiNum(pGia) >= XecCtx.Pars.nSharedDcPiMin && + Gia_ManCoNum(pGia) > 64 && Gia_ManAndNum(pGia) > XecCtx.Pars.nSharedDcAndMin)) ) + { + int nWholeDcLimit = fHighPiTwoCtrlDc ? XecCtx.Pars.nSharedDcWholeSec : 1200; + if ( fVerbose ) + printf( "Trying whole-miter CaDiCaL before local sweep for %slarge high-PI DC design: And = %d. PO = %d. DC controls = %d.\n", + fHighPiTwoCtrlDc ? "case8-style " : "", Gia_ManAndNum(pGia), Gia_ManCoNum(pGia), Vec_IntSize(vIntDcCtrlsG) ); + fTriedWholeMiterEarly = 1; + pModel = Acb_NtkSolveCadicalLimit( pGia, 0, fVerbose, &Status, nWholeDcLimit, "large high-PI DC whole-miter CaDiCaL", 0 ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + if ( Status == -1 && fVerbose ) + printf( "Large high-PI DC whole-miter CaDiCaL was UNDECIDED%s.\n", + fHighPiTwoCtrlDc ? "; skipping local/cube detours for this case8-style shape" : "; continuing with local structural attempts" ); + if ( Status == -1 && fHighPiTwoCtrlDc ) + { + fSkipHighPiDcFallbacks = 1; + fSkipWholeMiter = 1; + } + } + if ( Status == -1 && !fFancy && fSymmetricMuxDc && + vMuxSelectorsG && Vec_IntSize(vMuxSelectorsG) > 0 && Vec_IntSize(vMuxSelectorsG) <= 4 && + vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) > 4 && + Gia_ManCoNum(pGia) >= 32 && Gia_ManCoNum(pGia) <= 128 && + Gia_ManCiNum(pGia) <= 1024 && + Gia_ManAndNum(pGia) >= 30000 && Gia_ManAndNum(pGia) <= 70000 ) + { + if ( fVerbose ) + printf( "Trying whole-miter CaDiCaL before local sweep for symmetric MUX/DC design: And = %d. PO = %d. selectors = %d. DC controls = %d.\n", + Gia_ManAndNum(pGia), Gia_ManCoNum(pGia), Vec_IntSize(vMuxSelectorsG), Vec_IntSize(vIntDcCtrlsG) ); + fTriedWholeMiterEarly = 1; + pModel = Acb_NtkSolveCadicalLimit( pGia, 0, fVerbose, &Status, 1200, "symmetric MUX/DC whole-miter CaDiCaL", 0 ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + if ( Status == -1 && fVerbose ) + printf( "Symmetric MUX/DC whole-miter CaDiCaL was UNDECIDED; continuing with local structural attempts.\n" ); + } + if ( Status == -1 && !fFancy && !fSymmetricMuxDc && !fSkipAsymHmuxBranch && + vCutObjsG && vMuxSelectorsG && vMuxPoSelIdsG && + Vec_IntSize(vCutObjsG) == Acb_NtkCoNum(pNtkG) && + Vec_IntSize(vMuxSelectorsG) > 0 && Vec_IntSize(vMuxSelectorsG) <= 4 && + vIntDcObjsG && vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) > 4 && + Gia_ManCiNum(pGia) <= 1024 && Gia_ManCoNum(pGia) >= 32 && Gia_ManCoNum(pGia) <= 96 ) + { + int StatusCube = -1; + pModel = Acb_NtkSolveHmuxCompleteCubes( pNtkF, pNtkG, + vCutObjsG, vMuxSelectorsG, vMuxPoSelIdsG, + fVerbose, &StatusCube, 1200, 300 ); + Acb_XecMergeTargetStatus( StatusCube, pModel != NULL, &Status, &fCheckModel ); + if ( StatusCube == ACB_XEC_UNDEC ) + { + fSkipAsymHmuxBranch = 1; + if ( fVerbose ) + printf( "Complete HMUX selector-cube proof was inconclusive; skipping CEPR and partial-selector HMUX detours for this broad asymmetric shape.\n" ); + } + } + if ( Status == -1 && !fFancy && !fSymmetricMuxDc && !fSkipAsymHmuxBranch && + vCutObjsG && vMuxSelectorsG && vMuxPoSelIdsG && + Vec_IntSize(vCutObjsG) == Acb_NtkCoNum(pNtkG) && + Vec_IntSize(vMuxSelectorsG) > 0 && Vec_IntSize(vMuxSelectorsG) <= 4 && + vIntDcObjsG && Vec_IntSize(vIntDcObjsG) >= 128 && + vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) >= 8 && + Gia_ManCiNum(pGia) <= 1024 && Gia_ManCoNum(pGia) >= 32 && Gia_ManCoNum(pGia) <= 64 && + Gia_ManAndNum(pGia) <= 35000 ) + { + if ( fVerbose ) + printf( "Trying compact HMUX/DC whole-miter CaDiCaL before asymmetric branch proof: And = %d. PO = %d. selectors = %d. DC controls = %d.\n", + Gia_ManAndNum(pGia), Gia_ManCoNum(pGia), Vec_IntSize(vMuxSelectorsG), Vec_IntSize(vIntDcCtrlsG) ); + fTriedWholeMiterEarly = 1; + fSkipAsymHmuxBranch = 1; + pModel = Acb_NtkSolveCadicalLimit( pGia, 0, fVerbose, &Status, 1200, "compact HMUX/DC whole-miter CaDiCaL", 0 ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + if ( Status == -1 && fVerbose ) + printf( "Compact HMUX/DC whole-miter CaDiCaL was UNDECIDED; skipping the expensive asymmetric HMUX branch detour.\n" ); + } + + if ( Status == -1 && !fFancy && !fSymmetricMuxDc && !fSkipAsymHmuxBranch && + vCutObjsG && vMuxSelectorsG && vMuxPoSelIdsG && + Vec_IntSize(vCutObjsG) == Acb_NtkCoNum(pNtkG) && + Vec_IntSize(vMuxSelectorsG) > 0 && Vec_IntSize(vMuxSelectorsG) <= 4 && + vIntDcObjsG && vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) > 4 && + Gia_ManCoNum(pGia) >= 16 && Gia_ManCoNum(pGia) <= 128 ) + { + int StatusHmux = -1; + if ( fVerbose ) + printf( "Trying asymmetric HMUX branch proof with targeted DC fallback before local sweep.\n" ); + pModel = Acb_NtkSolveHmuxBranches( pNtkF, pNtkG, + vCutObjsG, vMuxSelectorsG, vMuxPoSelIdsG, + vIntDcObjsG, vIntDcCtrlsG, vIntDcCtrlIdsG, + fVerbose, &StatusHmux, &XecCtx ); + Acb_XecMergeTargetStatus( StatusHmux, pModel != NULL, &Status, &fCheckModel ); + } + if ( Status == -1 && !fFancy && !fSkipHighPiDcFallbacks ) + { + pModel = Acb_NtkSolveCadicalLocalConeSweepSkipCtx( pGia, fVerbose, &Status, 900, 120, NULL, &XecCtx ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + } + if ( Status == -1 && !fSkipHighPiDcFallbacks ) + { + int StatusTarget = -1; + if ( !fFancy && vIntDcObjsG && Vec_IntSize(vIntDcObjsG) > 0 && + vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) > 0 && Vec_IntSize(vIntDcCtrlsG) <= 4 && + (vMuxSelectorsG == NULL || Vec_IntSize(vMuxSelectorsG) == 0) && + XecCtx.vLastHardPos && Vec_IntSize(XecCtx.vLastHardPos) > 0 ) + { + int fTriedSmallDcCutpoints = 0; + if ( Vec_IntSize(vIntDcCtrlsG) == 1 && Vec_IntSize(vIntDcObjsG) <= 2 && + Gia_ManCiNum(pGia) <= 256 && Gia_ManCoNum(pGia) > 64 && + Vec_IntSize(XecCtx.vLastHardPos) <= 16 ) + { + fTriedSmallDcCutpoints = 1; + if ( fVerbose ) + printf( "Trying collected hard-output cutpoint abstraction for small single-control DC design: hard outputs = %d. DC nodes = %d.\n", + Vec_IntSize(XecCtx.vLastHardPos), Vec_IntSize(vIntDcObjsG) ); + pModel = Acb_NtkSolveTargetCutpointList( pNtkF, pNtkG, XecCtx.vLastHardPos, + fVerbose, &StatusTarget, 900, 120 ); + } + if ( StatusTarget == -1 && !fTriedSmallDcCutpoints && !fHighPiTwoCtrlDc ) + { + if ( fVerbose ) + printf( "Trying DC-control target recursion after local-cone sweep: hard outputs = %d. controls = %d.\n", + Vec_IntSize(XecCtx.vLastHardPos), Vec_IntSize(vIntDcCtrlsG) ); + pModel = Acb_NtkSolveDcControlTargetList( pNtkF, pNtkG, XecCtx.vLastHardPos, + vIntDcObjsG, vIntDcCtrlsG, vIntDcCtrlIdsG, fVerbose, &StatusTarget, 180 ); + } + Acb_XecMergeTargetStatus( StatusTarget, pModel != NULL, &Status, &fCheckModel ); + if ( StatusTarget == 1 ) + { + if ( Vec_IntSize(XecCtx.vLastHardPos) == Gia_ManCoNum(pGia) ) + Status = 1; + else + { + Vec_Int_t * vSkipUnsat = Vec_IntDup( XecCtx.vLastHardPos ); + int iPoSkip, iSkip, StatusResume = -1; + if ( XecCtx.vLastProvenPos ) + Vec_IntForEachEntry( XecCtx.vLastProvenPos, iPoSkip, iSkip ) + Vec_IntPushUnique( vSkipUnsat, iPoSkip ); + if ( fVerbose ) + printf( "DC-control target recursion proved %d collected hard outputs; resuming local-cone sweep for %d remaining outputs.\n", + Vec_IntSize(XecCtx.vLastHardPos), Gia_ManCoNum(pGia) - Vec_IntSize(vSkipUnsat) ); + pModel = Acb_NtkSolveCadicalLocalConeSweepSkipCtx( pGia, fVerbose, &StatusResume, 900, -120, vSkipUnsat, &XecCtx ); + Vec_IntFree( vSkipUnsat ); + Acb_XecMergeTargetStatus( StatusResume, pModel != NULL, &Status, &fCheckModel ); + if ( StatusResume != ACB_XEC_EQ && StatusResume != ACB_XEC_NEQ ) + Status = StatusResume; + } + } + else if ( fHighPiTwoCtrlDc ) + { + fSkipWholeMiter = 1; + if ( fVerbose ) + printf( "Skipping expensive high-PI two-control DC fallbacks after quick local sweep; remaining outputs need a specialized proof.\n" ); + } + } + } + if ( Status == ACB_XEC_ONE_HARD ) + { + int StatusTarget = -1; + if ( !fFancy && vCutObjsG && vMuxSelectorsG && vMuxPoSelIdsG && + Vec_IntSize(vCutObjsG) == Acb_NtkCoNum(pNtkG) && + Vec_IntSize(vMuxSelectorsG) > 0 && Vec_IntSize(vMuxSelectorsG) <= 16 ) + { + pModel = Acb_NtkSolveMuxTargetBranches( pNtkF, pNtkG, XecCtx.LastHardPo, + vCutObjsG, vMuxSelectorsG, vMuxPoSelIdsG, fVerbose, &StatusTarget, 450, 0 ); + Acb_XecMergeTargetStatus( StatusTarget, pModel != NULL, &Status, &fCheckModel ); + } + } + if ( Status == ACB_XEC_MANY_HARD ) + { + int StatusTarget = -1; + if ( !fFancy && vIntDcObjsG && Vec_IntSize(vIntDcObjsG) > 0 && + vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) > 0 && Vec_IntSize(vIntDcCtrlsG) <= 4 && + (vMuxSelectorsG == NULL || Vec_IntSize(vMuxSelectorsG) == 0) && + XecCtx.vLastHardPos && Vec_IntSize(XecCtx.vLastHardPos) > 0 ) + { + pModel = Acb_NtkSolveDcControlTargetList( pNtkF, pNtkG, XecCtx.vLastHardPos, + vIntDcObjsG, vIntDcCtrlsG, vIntDcCtrlIdsG, fVerbose, &StatusTarget, 180 ); + Acb_XecMergeTargetStatus( StatusTarget, pModel != NULL, &Status, &fCheckModel ); + } + } + if ( Status == ACB_XEC_ONE_HARD ) + { + int StatusTarget = -1; + if ( !fFancy && vIntDcObjsG && Vec_IntSize(vIntDcObjsG) > 0 && + vIntDcCtrlsG && Vec_IntSize(vIntDcCtrlsG) > 0 && Vec_IntSize(vIntDcCtrlsG) <= 4 && + (vMuxSelectorsG == NULL || Vec_IntSize(vMuxSelectorsG) == 0) ) + { + pModel = Acb_NtkSolveDcControlTargetBranches( pNtkF, pNtkG, XecCtx.LastHardPo, + vIntDcObjsG, vIntDcCtrlsG, vIntDcCtrlIdsG, fVerbose, &StatusTarget, 450, 0 ); + Acb_XecMergeTargetStatus( StatusTarget, pModel != NULL, &Status, &fCheckModel ); + } + } + if ( Status == ACB_XEC_ONE_HARD ) + { + int StatusTarget = -1; + pModel = Acb_NtkSolveTargetCutpoints( pNtkF, pNtkG, XecCtx.LastHardPo, fVerbose, &StatusTarget, 900 ); + Acb_XecMergeTargetStatus( StatusTarget, pModel != NULL, &Status, &fCheckModel ); + } + if ( Status == ACB_XEC_ONE_HARD ) + { + if ( XecCtx.LastHardDirectTried ) + { + if ( fVerbose ) + printf( "Skipping whole-miter CaDiCaL because the isolated hard output already had a rejected direct-clause SAT model.\n" ); + Status = -1; + fSkipWholeMiter = 1; + } + else + { + if ( fVerbose ) + printf( "Skipping whole-miter CaDiCaL because it duplicates the isolated hard-output cone.\n" ); + Status = -1; + fSkipWholeMiter = 1; + } + } + if ( Status == ACB_XEC_MANY_HARD ) + { + if ( fVerbose ) + printf( "Skipping whole-miter CaDiCaL because the remaining hard-output proof already isolated the unresolved outputs.\n" ); + Status = -1; + fSkipWholeMiter = 1; + } + if ( Status == -1 && !fFancy && !fSkipWholeMiter && !fTriedWholeMiterEarly ) + pModel = Acb_NtkSolveCadicalLimit( pGia, 0, fVerbose, &Status, 1200, "CaDiCaL SAT-only", 0 ); + else if ( Status == -1 && !fSkipWholeMiter ) + pModel = Acb_NtkSolveCadicalLimit( pGia, fFancy, fVerbose, &Status, 1200, fFancy ? "X-aware CaDiCaL SAT-only" : "CaDiCaL SAT-only", fFancy ); + if ( Status == -1 && fFancy && vCutObjsF && vDcDataObjsG && vDcCtrlObjsG ) + { + pGiaFCut = Acb_NtkGiaDeriveDualTargets( pNtkF, vCutObjsF ); + pGiaGCut = Acb_NtkGiaDeriveDualTargets( pNtkG, vDcDataObjsG ); + pGiaGCtrl = Acb_NtkGiaDeriveDualTargets( pNtkG, vDcCtrlObjsG ); + pGiaCut = Acb_NtkGiaDeriveMiterDcGuard( pGiaFCut, pGiaGCut, pGiaGCtrl ); + if ( fVerbose ) + printf( "Trying exact split SAT on output-DC cutpoint miter: And = %d. PO = %d.\n", Gia_ManAndNum(pGiaCut), Gia_ManPoNum(pGiaCut) ); + pModel = Acb_NtkSolveSplit( pGiaCut, fVerbose, &Status ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + } + if ( Status == -1 && fFancy && vCutObjsF && vCutObjsG && Vec_IntSize(vCutObjsG) == Acb_NtkCoNum(pNtkG) ) + { + if ( pGiaCut ) + { + Gia_ManStop( pGiaCut ); + pGiaCut = NULL; + } + if ( pGiaFCut ) + { + Gia_ManStop( pGiaFCut ); + pGiaFCut = NULL; + } + if ( pGiaGCut ) + { + Gia_ManStop( pGiaGCut ); + pGiaGCut = NULL; + } + pGiaFCut = Acb_NtkGiaDeriveDualTargets( pNtkF, vCutObjsF ); + pGiaGCut = Acb_NtkGiaDeriveDualTargets( pNtkG, vCutObjsG ); + pGiaCut = Acb_NtkGiaDeriveMiter( pGiaFCut, pGiaGCut, 2 ); + if ( Gia_ManAndNum(pGiaCut) >= Gia_ManAndNum(pGia) ) + { + if ( fVerbose ) + printf( "Skipping partition-candidate cutpoint miter because it is not smaller: And = %d, current = %d.\n", + Gia_ManAndNum(pGiaCut), Gia_ManAndNum(pGia) ); + } + else + { + if ( fVerbose ) + printf( "Trying exact split SAT on partition-candidate cutpoint miter: And = %d. PO = %d.\n", Gia_ManAndNum(pGiaCut), Gia_ManPoNum(pGiaCut) ); + pModel = Acb_NtkSolveSplit( pGiaCut, fVerbose, &Status ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + } + } + if ( Status == -1 && fFancy ) + pModel = Acb_NtkSolveCadicalOdc( pGia, fVerbose, &Status ); + if ( Status == -1 && fFancy ) + { + pModel = Acb_NtkSolveSplit( pGia, fVerbose, &Status ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + } + if ( Status == -1 && fFancy ) + { + printf( "Trying X-aware whole-miter CaDiCaL after ODC/split SAT was undecided.\n" ); + if ( pGiaX == NULL ) + pGiaX = Acb_NtkGiaDeriveMiter( pGiaF, pGiaG, 3 ); + pModel = Acb_NtkSolveCadicalLimit( pGiaX, fFancy, fVerbose, &Status, 1200, "X-aware CaDiCaL SAT-only", 1 ); + fCheckModel = 1; + } + } + else + { + if ( !fFancy && Status == -1 && Gia_ManCoNum(pGia) == 1 && Gia_ManAndNum(pGia) <= 30000 && + (vMuxSelectorsG == NULL || Vec_IntSize(vMuxSelectorsG) == 0) ) + { + pModel = Acb_NtkSolveIvyPrecheck( pGia, fVerbose, &Status ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + } + if ( !fFancy && Status == -1 && vIntDcObjsG && Vec_IntSize(vIntDcObjsG) > 0 && Vec_IntSize(vIntDcCtrlsG) > 0 && Vec_IntSize(vIntDcCtrlsG) <= 4 ) + { + int fSmallMultiOutput = Gia_ManAndNum(pGia) <= 5000 && Gia_ManCoNum(pGia) > 1; + if ( fSmallMultiOutput && Vec_IntSize(vIntDcCtrlsG) == 1 && Vec_IntSize(vIntDcObjsG) <= 16 && + Gia_ManCoNum(pGia) >= 8 && Gia_ManCoNum(pGia) <= 32 ) + { + pModel = Acb_NtkSolveCadicalLimit( pGia, fFancy, fVerbose, &Status, 1700, + "small single-control DC whole-miter CaDiCaL", 0 ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + } + else + { + pModel = Acb_NtkSolveDcControlBranchesLimit( pNtkF, pNtkG, vIntDcObjsG, vIntDcCtrlsG, vIntDcCtrlIdsG, + fVerbose, &Status, fSmallMultiOutput ? 60 : 5, !fSmallMultiOutput, 0 ); + Acb_XecMergeTargetStatus( Status, pModel != NULL, &Status, &fCheckModel ); + } + } + + if ( Status == -1 ) + pModel = Acb_NtkSolveCadicalLimit( pGia, fFancy, fVerbose, &Status, 1200, fFancy ? "X-aware CaDiCaL SAT-only" : "CaDiCaL SAT-only", fFancy ); + } + } + else + { + pModel = Acb_NtkSolve( pGia, fVerbose, &Status ); + } + if ( fCheckModel && pModel && !Acb_NtkCheckModelCex( pGiaF, pGiaG, pModel, fVerbose ) ) + { + ABC_FREE( pModel ); + pModel = NULL; + Status = -1; + printf( "The SAT model is not a valid XEC counterexample; treating the result as UNDECIDED.\n" ); + } + } + Acb_OutputFile( pFileNames[2], pNtkF, pModel, Status ); ABC_FREE( pModel ); } - Gia_ManStop( pGia ); - Gia_ManStop( pGiaF ); - Gia_ManStop( pGiaG ); + Gia_ManStopP( &pGiaX ); + Gia_ManStopP( &pGiaCut ); + Gia_ManStopP( &pGiaFCut ); + Gia_ManStopP( &pGiaGCut ); + Gia_ManStopP( &pGiaGCtrl ); + Gia_ManStopP( &pGia ); + Gia_ManStopP( &pGiaF ); + Gia_ManStopP( &pGiaG ); + Vec_IntFreeP( &vCutObjsF ); + Vec_IntFreeP( &vCutObjsG ); + Vec_IntFreeP( &vMuxSelectorsG ); + Vec_IntFreeP( &vMuxPoSelIdsG ); + Vec_IntFreeP( &vSymCutObjsF ); + Vec_IntFreeP( &vSymMuxSelectorsF ); + Vec_IntFreeP( &vSymIntDcObjsF ); + Vec_IntFreeP( &vSymIntDcCtrlsF ); + Vec_IntFreeP( &vSymIntDcCtrlIdsF ); + Vec_IntFreeP( &vIntDcObjsG ); + Vec_IntFreeP( &vIntDcCtrlsG ); + Vec_IntFreeP( &vIntDcCtrlIdsG ); + Vec_IntFreeP( &vDcDataObjsG ); + Vec_IntFreeP( &vDcCtrlObjsG ); + Acb_XecCtxFree( &XecCtx ); Acb_ManFree( pNtkF->pDesign ); Acb_ManFree( pNtkG->pDesign ); @@ -663,4 +5334,3 @@ void Acb_NtkRunTest( char * pFileNames[4], int fFancy, int fVerbose ) ABC_NAMESPACE_IMPL_END - diff --git a/src/base/acb/acbXec.c b/src/base/acb/acbXec.c new file mode 100644 index 000000000..c37a5fe60 --- /dev/null +++ b/src/base/acb/acbXec.c @@ -0,0 +1,368 @@ +/**CFile**************************************************************** + + FileName [acbXec.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Hierarchical word-level netlist.] + + Synopsis [Reusable XEC proof helpers.] + +***********************************************************************/ + +#include "acbXec.h" +#include "aig/gia/giaAig.h" +#include "base/abc/abc.h" +#include "opt/dar/dar.h" +#include "sat/cadical/cadicalSolver.h" + +ABC_NAMESPACE_IMPL_START + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +typedef enum Acb_SatStatus_t_ +{ + ACB_SAT_UNSAT = -1, + ACB_SAT_UNDEC = 0, + ACB_SAT_SAT = 1 +} Acb_SatStatus_t; + +int Acb_CnfCoDriverLit( Cnf_Dat_t * pCnf, int iCo, int * pLit ) +{ + Aig_Obj_t * pCo = Aig_ManCo( pCnf->pMan, iCo ); + Aig_Obj_t * pFan = Aig_ObjFanin0( pCo ); + int fCompl = Aig_ObjFaninC0( pCo ); + int Var; + if ( Aig_ObjIsConst1(pFan) ) + return fCompl ? -1 : 0; + Var = pCnf->pVarNums[pFan->Id]; + if ( Var < 0 ) + return -2; + *pLit = Abc_Var2Lit( Var, fCompl ); + return 1; +} + +static int Acb_GiaPoIsConst0( Gia_Man_t * p, int iPo ) +{ + Gia_Obj_t * pObj; + if ( iPo < 0 || iPo >= Gia_ManCoNum(p) ) + return 0; + pObj = Gia_ManCo( p, iPo ); + return Gia_ObjFanin0(pObj) == Gia_ManConst0(p) && !Gia_ObjFaninC0(pObj); +} + +int Acb_GiaAllPosConst0( Gia_Man_t * p ) +{ + int i; + for ( i = 0; i < Gia_ManCoNum(p); i++ ) + if ( !Acb_GiaPoIsConst0(p, i) ) + return 0; + return 1; +} + +static word Acb_XecGiaVarWord( int iVar, ABC_UINT64_T iWord ) +{ + static word Truth6[6] = { + ABC_CONST(0xAAAAAAAAAAAAAAAA), + ABC_CONST(0xCCCCCCCCCCCCCCCC), + ABC_CONST(0xF0F0F0F0F0F0F0F0), + ABC_CONST(0xFF00FF00FF00FF00), + ABC_CONST(0xFFFF0000FFFF0000), + ABC_CONST(0xFFFFFFFF00000000) + }; + if ( iVar < 6 ) + return Truth6[iVar]; + return ((iWord >> (iVar - 6)) & 1) ? ~(word)0 : 0; +} +static inline word Acb_XecGiaLitWord( Vec_Wrd_t * vSims, int nWords, int Lit, int w ) +{ + word Res = Vec_WrdEntry( vSims, Abc_Lit2Var(Lit) * nWords + w ); + return Abc_LitIsCompl(Lit) ? ~Res : Res; +} +int * Acb_NtkSolveCadicalLimit( Gia_Man_t * p, int fUseHeavyOpt, int fVerbose, int * pStatus, int nSatTimeLimit, const char * pLabel, int fUseXecOutputClauses ) +{ + Aig_Man_t * pMan = NULL; + Cnf_Dat_t * pCnf = NULL; + cadical_solver * pSat = NULL; + Vec_Int_t * vPoLits = NULL; + Gia_Man_t * pGiaOpt = NULL, * pGiaTemp = NULL; + Gia_Man_t * pGia = p; + Aig_Obj_t * pObj; + int i, Ret, Lit, Status = ACB_SAT_UNDEC, * pBeg, * pEnd, * pModel = NULL; + int fRunSolve = 0, fSolvedSat = 0; + abctime clk = Abc_Clock(); + (void)fUseXecOutputClauses; + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( p == NULL ) + return NULL; + if ( Gia_ManCoNum(p) == 0 || Acb_GiaAllPosConst0(p) ) + { + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + if ( pLabel ) + { + printf( "The networks are equivalent by %s. ", pLabel ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + return NULL; + } + if ( fUseHeavyOpt && Gia_ManAndNum(p) > 0 ) + { + pGiaTemp = Gia_ManCompress2( p, 1, 0 ); + if ( pGiaTemp ) + { + pGiaOpt = pGiaTemp; + pGiaTemp = NULL; + pGia = pGiaOpt; + assert( Gia_ManCiNum(pGia) == Gia_ManCiNum(p) ); + } + } + pMan = Gia_ManToAig( pGia, 0 ); + pCnf = pMan ? Cnf_Derive( pMan, Aig_ManCoNum(pMan) ) : NULL; + pSat = pCnf ? cadical_solver_new() : NULL; + if ( pCnf && pSat ) + { + fRunSolve = 1; + cadical_solver_setnvars( pSat, pCnf->nVars ); + Cnf_CnfForClause( pCnf, pBeg, pEnd, i ) + { + if ( !cadical_solver_addclause( pSat, pBeg, pEnd ) ) + { + Status = ACB_SAT_UNSAT; + fRunSolve = 0; + break; + } + } + if ( fRunSolve ) + { + vPoLits = Vec_IntAlloc( Gia_ManCoNum(pGia) ); + for ( i = 0; i < Gia_ManCoNum(pGia); i++ ) + { + Ret = Acb_CnfCoDriverLit( pCnf, i, &Lit ); + if ( Ret == -2 ) + { + Status = ACB_SAT_UNDEC; + fRunSolve = 0; + break; + } + if ( Ret == -1 ) + continue; + if ( Ret == 0 ) + { + Status = ACB_SAT_SAT; + fRunSolve = 0; + break; + } + Vec_IntPush( vPoLits, Lit ); + } + } + if ( fRunSolve && Vec_IntSize(vPoLits) == 0 ) + { + Status = ACB_SAT_UNSAT; + fRunSolve = 0; + } + if ( fRunSolve && !cadical_solver_addclause( pSat, Vec_IntArray(vPoLits), Vec_IntArray(vPoLits) + Vec_IntSize(vPoLits) ) ) + { + Status = ACB_SAT_UNSAT; + fRunSolve = 0; + } + if ( fRunSolve && fVerbose ) + { + printf( "CaDiCaL CNF: Var = %d. Cla = %d. PO = %d.\n", + pCnf->nVars, pCnf->nClauses + 1, Gia_ManCoNum(pGia) ); + if ( nSatTimeLimit > 0 ) + printf( "CaDiCaL SAT runtime limit: %d sec.\n", nSatTimeLimit ); + } + if ( fRunSolve ) + { + Status = cadical_solver_solve( pSat, NULL, NULL, 0, 0, 0, 0 ); + fSolvedSat = Status == ACB_SAT_SAT; + } + if ( fVerbose ) + printf( "CaDiCaL stats: conflicts = %d. learned = %d.\n", + cadical_solver_nconflicts(pSat), cadical_solver_nlearned(pSat) ); + } + if ( Status == ACB_SAT_UNSAT ) + { + if ( pStatus ) + *pStatus = ACB_XEC_EQ; + if ( pLabel ) + { + printf( "The networks are equivalent by %s. ", pLabel ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + } + else if ( Status == ACB_SAT_SAT ) + { + if ( pStatus ) + *pStatus = ACB_XEC_NEQ; + if ( fSolvedSat ) + pModel = ABC_CALLOC( int, Gia_ManCiNum(pGia) ); + if ( pModel && pSat && pCnf && pMan ) + Aig_ManForEachCi( pMan, pObj, i ) + { + int Var = pCnf->pVarNums[pObj->Id]; + pModel[i] = Var >= 0 ? cadical_solver_get_var_value( pSat, Var ) : 0; + } + if ( pLabel ) + { + printf( "The networks are NOT equivalent by %s. ", pLabel ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + } + else + { + if ( pStatus ) + *pStatus = ACB_XEC_UNDEC; + if ( fVerbose && pLabel ) + { + printf( "The networks are UNDECIDED by %s. ", pLabel ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + } + if ( pSat ) + cadical_solver_delete( pSat ); + if ( pCnf ) + Cnf_DataFree( pCnf ); + if ( pMan ) + Aig_ManStop( pMan ); + if ( pGiaOpt ) + Gia_ManStop( pGiaOpt ); + Vec_IntFreeP( &vPoLits ); + return pModel; +} +int Acb_XecGiaSolveSmallConeExhaustive( Gia_Man_t * p, int fVerbose, int nTotalLimit ) +{ + Vec_Wrd_t * vSims = NULL; + Gia_Obj_t * pObj; + ABC_UINT64_T nWordsTotal, nWordBudget, iWordBase, nWordsDone = 0; + int i, w, nWords, nWordsChunk, nObjs, nCis, nHiVars, Status = ACB_XEC_EQ, fDone = 0; + abctime clk = Abc_Clock(); + abctime clkLimit = nTotalLimit > 0 ? clk + nTotalLimit * CLOCKS_PER_SEC : 0; + if ( Gia_ManCoNum(p) != 1 || Gia_ManAndNum(p) > 5000 ) + return ACB_XEC_UNDEC; + nCis = Gia_ManCiNum(p); + nHiVars = Abc_MaxInt( 0, nCis - 6 ); + if ( nHiVars >= 63 ) + { + if ( fVerbose ) + printf( "Skipping small-cone exhaustive word proof: CI = %d needs more than 2^63 simulation words.\n", nCis ); + return ACB_XEC_UNDEC; + } + nWordsChunk = nCis >= 31 ? 4096 : (nCis >= 28 ? 8192 : 16384); + nWordsTotal = nHiVars ? ((ABC_UINT64_T)1 << nHiVars) : 1; + nWordBudget = nTotalLimit > 0 ? (ABC_UINT64_T)200000 * nTotalLimit : (ABC_UINT64_T)60000000; + if ( nWordBudget < (ABC_UINT64_T)8000000 ) + nWordBudget = (ABC_UINT64_T)8000000; + if ( nWordsTotal > nWordBudget ) + { + if ( fVerbose ) + printf( "Skipping small-cone exhaustive word proof: CI = %d needs %llu words, budget = %llu words.\n", + nCis, (unsigned long long)nWordsTotal, (unsigned long long)nWordBudget ); + return ACB_XEC_UNDEC; + } + nObjs = Gia_ManObjNum(p); + vSims = Vec_WrdStart( nObjs * nWordsChunk ); + if ( fVerbose ) + printf( "Trying small-cone exhaustive word proof: CI = %d. AND = %d. chunks = %llu x %d words. limit = %d sec.\n", + nCis, Gia_ManAndNum(p), (unsigned long long)((nWordsTotal + nWordsChunk - 1) / nWordsChunk), nWordsChunk, nTotalLimit ); + for ( iWordBase = 0; iWordBase < nWordsTotal && !fDone; iWordBase += nWordsChunk ) + { + ABC_UINT64_T nWordsLeft = nWordsTotal - iWordBase; + nWords = nWordsLeft < (ABC_UINT64_T)nWordsChunk ? (int)nWordsLeft : nWordsChunk; + if ( clkLimit && Abc_Clock() >= clkLimit ) + { + Status = ACB_XEC_UNDEC; + break; + } + /* Only the active words [0..nWords) are consumed in this chunk; other words may retain previous data. */ + for ( w = 0; w < nWords; w++ ) + Vec_WrdWriteEntry( vSims, w, 0 ); + Gia_ManForEachCi( p, pObj, i ) + for ( w = 0; w < nWords; w++ ) + Vec_WrdWriteEntry( vSims, Gia_ObjId(p, pObj) * nWordsChunk + w, Acb_XecGiaVarWord(i, iWordBase + w) ); + Gia_ManForEachAnd( p, pObj, i ) + for ( w = 0; w < nWords; w++ ) + Vec_WrdWriteEntry( vSims, Gia_ObjId(p, pObj) * nWordsChunk + w, + Acb_XecGiaLitWord(vSims, nWordsChunk, Gia_ObjFaninLit0p(p, pObj), w) & + Acb_XecGiaLitWord(vSims, nWordsChunk, Gia_ObjFaninLit1p(p, pObj), w) ); + pObj = Gia_ManCo( p, 0 ); + for ( w = 0; w < nWords; w++ ) + { + word Res = Acb_XecGiaLitWord(vSims, nWordsChunk, Gia_ObjFaninLit0p(p, pObj), w); + if ( iWordBase + w + 1 == nWordsTotal && nCis < 6 ) + Res &= (((word)1) << (1 << nCis)) - 1; + if ( Res ) + { + Status = ACB_XEC_UNDEC; + fDone = 1; + break; + } + } + nWordsDone += nWords; + } + if ( fVerbose ) + { + printf( "Small-cone exhaustive word proof: %s. checked words = %llu/%llu. ", + Status == ACB_XEC_EQ ? "UNSAT" : "UNDECIDED", + (unsigned long long)nWordsDone, (unsigned long long)nWordsTotal ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + Vec_WrdFree( vSims ); + return Status; +} +Gia_Man_t * Acb_XecGiaSmallConeXorRewrite( Gia_Man_t * p, int fVerbose ) +{ + Aig_Man_t * pAig = NULL, * pAigTemp = NULL; + Gia_Man_t * pGia = NULL, * pTemp = NULL; + int nAndStart = Gia_ManAndNum(p); + abctime clk = Abc_Clock(); + if ( Gia_ManCoNum(p) != 1 || Gia_ManCiNum(p) > 64 || nAndStart > 8000 ) + return NULL; + if ( fVerbose ) + printf( "Small-cone XOR structural rewrite: CI = %d. AND = %d.\n", + Gia_ManCiNum(p), nAndStart ); + pAig = Gia_ManToAig( p, 0 ); + if ( pAig == NULL ) + return NULL; + pAig = Dar_ManBalanceXor( pAigTemp = pAig, 1, 1, 0 ); + Aig_ManStop( pAigTemp ); + if ( pAig == NULL ) + return NULL; + pAig = Dar_ManRwsat( pAigTemp = pAig, 1, 0 ); + Aig_ManStop( pAigTemp ); + if ( pAig == NULL ) + return NULL; + pGia = Gia_ManFromAig( pAig ); + Aig_ManStop( pAig ); + if ( pGia == NULL ) + return NULL; + pTemp = Gia_ManCompress2( pGia, 1, 0 ); + if ( pTemp ) + { + Gia_ManStop( pGia ); + pGia = pTemp; + } + if ( fVerbose ) + { + printf( "Small-cone XOR structural rewrite: AND = %d -> %d. Lev = %d -> %d. ", + nAndStart, Gia_ManAndNum(pGia), Gia_ManLevelNum(p), Gia_ManLevelNum(pGia) ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } + if ( Gia_ManCoNum(pGia) != Gia_ManCoNum(p) || + (!Acb_GiaAllPosConst0(pGia) && Gia_ManAndNum(pGia) >= nAndStart) ) + { + Gia_ManStop( pGia ); + return NULL; + } + return pGia; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_IMPL_END diff --git a/src/base/acb/acbXec.h b/src/base/acb/acbXec.h new file mode 100644 index 000000000..a85c80f5b --- /dev/null +++ b/src/base/acb/acbXec.h @@ -0,0 +1,70 @@ +/**CFile**************************************************************** + + FileName [acbXec.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Hierarchical word-level netlist.] + + Synopsis [Shared XEC proof helper API.] + +***********************************************************************/ + +#ifndef ABC__base__acb__acbXec_h +#define ABC__base__acb__acbXec_h + +#include "acb.h" +#include "sat/cnf/cnf.h" + +ABC_NAMESPACE_HEADER_START + +/* + * XEC/network-level proof status: + * ACB_XEC_EQ : networks/output are proven equivalent/UNSAT miter + * ACB_XEC_NEQ : networks/output are proven different/SAT miter + * ACB_XEC_UNDEC : proof was inconclusive + * ACB_XEC_ONE_HARD : local sweep proved all but one output + * ACB_XEC_MANY_HARD : local sweep left multiple hard outputs + * + */ +typedef enum Acb_XecStatus_t_ +{ + ACB_XEC_MANY_HARD = -3, + ACB_XEC_ONE_HARD = -2, + ACB_XEC_UNDEC = -1, + ACB_XEC_NEQ = 0, + ACB_XEC_EQ = 1 +} Acb_XecStatus_t; + +static inline void Acb_NtkPrintUnsupportedObj( Acb_Ntk_t * p, int iObj, const char * pWhere, int ExpectedFans, int ActualFans ) +{ + printf( "%s unsupported ACB object: obj = %d", pWhere ? pWhere : "XEC" , iObj ); + if ( p && iObj >= 0 && iObj < Acb_NtkObjNumMax(p) ) + printf( ", type = %d", Acb_ObjType(p, iObj) ); + if ( ExpectedFans >= 0 || ActualFans >= 0 ) + printf( ", fanins = %d, expected = %d", ActualFans, ExpectedFans ); + printf( ".\n" ); +} + +static inline void Acb_XecMergeTargetStatus( int StatusTarget, int fHasModel, int * pStatus, int * pCheckModel ) +{ + if ( fHasModel && pCheckModel ) + *pCheckModel = 1; + if ( pStatus == NULL ) + return; + if ( StatusTarget == ACB_XEC_EQ ) + *pStatus = ACB_XEC_EQ; + else if ( StatusTarget == ACB_XEC_NEQ ) + *pStatus = ACB_XEC_NEQ; +} + +extern int * Acb_NtkSolveCadicalLimit( Gia_Man_t * p, int fUseHeavyOpt, int fVerbose, int * pStatus, int nSatTimeLimit, const char * pLabel, int fUseXecOutputClauses ); +extern int Acb_CnfCoDriverLit( Cnf_Dat_t * pCnf, int iCo, int * pLit ); +extern int Acb_GiaAllPosConst0( Gia_Man_t * p ); + +extern int Acb_XecGiaSolveSmallConeExhaustive( Gia_Man_t * p, int fVerbose, int nTotalLimit ); +extern Gia_Man_t *Acb_XecGiaSmallConeXorRewrite( Gia_Man_t * p, int fVerbose ); + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/acb/module.make b/src/base/acb/module.make index ec78b3b0c..914193a93 100644 --- a/src/base/acb/module.make +++ b/src/base/acb/module.make @@ -1,9 +1,9 @@ SRC += src/base/acb/acbAbc.c \ src/base/acb/acbAig.c \ - src/base/acb/acbCom.c \ src/base/acb/acbFunc.c \ src/base/acb/acbMfs.c \ src/base/acb/acbPush.c \ src/base/acb/acbSets.c \ src/base/acb/acbTest.c \ - src/base/acb/acbUtil.c + src/base/acb/acbUtil.c \ + src/base/acb/acbXec.c diff --git a/src/base/main/mainInit.c b/src/base/main/mainInit.c index 98b374e15..ad9eb6e1b 100644 --- a/src/base/main/mainInit.c +++ b/src/base/main/mainInit.c @@ -53,6 +53,8 @@ extern void Wlc_Init( Abc_Frame_t * pAbc ); extern void Wlc_End( Abc_Frame_t * pAbc ); extern void Wln_Init( Abc_Frame_t * pAbc ); extern void Wln_End( Abc_Frame_t * pAbc ); +extern void Sn_Init( Abc_Frame_t * pAbc ); +extern void Sn_End( Abc_Frame_t * pAbc ); extern void Pla_Init( Abc_Frame_t * pAbc ); extern void Pla_End( Abc_Frame_t * pAbc ); extern void Sim_Init( Abc_Frame_t * pAbc ); @@ -119,6 +121,7 @@ void Abc_FrameInit( Abc_Frame_t * pAbc ) Emap_Init( pAbc ); Wlc_Init( pAbc ); Wln_Init( pAbc ); + Sn_Init( pAbc ); Pla_Init( pAbc ); Test_Init( pAbc ); Ufar_Init( pAbc ); @@ -160,6 +163,7 @@ void Abc_FrameEnd( Abc_Frame_t * pAbc ) Scl_End( pAbc ); Wlc_End( pAbc ); Wln_End( pAbc ); + Sn_End( pAbc ); Pla_End( pAbc ); Test_End( pAbc ); Glucose_End( pAbc ); diff --git a/src/base/main/mainInt.h b/src/base/main/mainInt.h index 3525ed015..9da2deb3b 100644 --- a/src/base/main/mainInt.h +++ b/src/base/main/mainInt.h @@ -149,6 +149,7 @@ struct Abc_Frame_t_ void * pAbcWlc; Vec_Int_t * pAbcWlcInv; void * pAbcRtl; + void * pAbcSn; void * pAbcPla; Abc_Nam_t * pJsonStrs; Vec_Wec_t * vJsonObjs; diff --git a/src/base/sn/module.make b/src/base/sn/module.make new file mode 100644 index 000000000..f1f801724 --- /dev/null +++ b/src/base/sn/module.make @@ -0,0 +1 @@ +SRC += src/base/sn/snCom.c diff --git a/src/base/sn/readme.md b/src/base/sn/readme.md new file mode 100644 index 000000000..824d666d2 --- /dev/null +++ b/src/base/sn/readme.md @@ -0,0 +1,253 @@ +# Simple Netlist in ABC + +This directory contains the Slang-independent Simple Netlist (SN) representation and algorithms. + +The external `sn_slang` executable parses and elaborates Verilog/SystemVerilog using Mike Popoloski's excellent +[slang SystemVerilog compiler](https://github.com/MikePopoloski/slang) and writes a binary `.sn` design. ABC does +not link slang or require its C++20 dependencies. + +The frontend architecture benefited from [yosys-slang](https://github.com/povik/yosys-slang), developed by +Martin PoviÅ¡er. It has been both an inspiration and a helpful practical guideline for working from slang's +elaborated model, particularly for lvalue analysis, procedural state, timing patterns, memory eligibility, resolved +nets, and diagnostics. The SN representation and lowering are independently developed, with warm thanks to Martin +for his work and advice. + +ABC holds the SN design and the `&`-space GIA as independent representations. Commands move data between them only +when explicitly requested: + +| Command | Reads | Writes | +| --- | --- | --- | +| `@slang`, `@read` | HDL or `.sn` | Current SN design | +| `@map_*`, `@opt_mux`, `@collapse` | SN | New SN design revision | +| `@blast` | SN | Current `&`-space GIA plus a saved boundary | +| `&...` commands | GIA | GIA | +| `@put` | GIA plus saved boundary | Module selected by the preceding `@blast` | +| `@write` | SN | `.sn` or Verilog | + +`@status` reports both representations, the monotonically increasing SN revision, and whether the saved boundary is +compatible with the current SN design and GIA. In particular, reading or transforming SN does not clear or update an +old `&`-space network; it makes that network unavailable for `@put` until another combinational `@blast` records a +matching boundary. + +## Commands + +The commands appear under `New word level commands` in ABC's `help` output. + +```text +set snslang /path/to/sn_slang +@slang -M top rtl1.sv rtl2.sv +@status +@check +@ps -v +@map_mem -v +@check +@map_dsp -v +@check +@map_add -v +@check +@opt_mux -v +@check +@blast -M top -c -v +&resyn3 +&if -m -K 6 +&ps +@status +@put -v +@check +@collapse -v +@check +@write mapped_logic.v +@write mapped_logic.sn +``` + +`@slang` uses `sn_slang` from `PATH` unless the `snslang` setting overrides it. It accepts `-M` for the top module, +repeatable `-D NAME` or `-D NAME=value` preprocessor definitions, `-F` for one additional source file, and any number +of positional source files. For example, `-D WIDTH=8 -D SIGNED=1` defines two macros. `-T` is not used because ABC +conventionally reserves it for a time limit. `-v` prints the external command and frontend timing. A module declared +inside SystemVerilog `` `celldefine`` / `` `endcelldefine``, or marked by a nonzero `black_box` or `syn_black_box` +module attribute, is imported as an opaque technology primitive with its elaborated PI/PO interface; its simulation +body is not lowered. For example, both `` `celldefine`` around a module definition and +`(* syn_black_box = 1 *) module macro (...);` create an opaque leaf. An explicit zero or false attribute does not. +The declaration is still required: slang must know every port's name, direction, width, and signedness, so an +undefined-module inst remains an error. Undefined-module patterns and include-directory options remain unsupported. + +`@read` and `@write` provide binary persistence. `@write` selects SN or Verilog output from the `.sn`, `.v`, or +`.sv` extension. `@read -M module` selects the top stored in a multi-top design; otherwise the last top is used. +Before installing external binary data, `@read` validates the encoding and runs the same non-aborting structural and +semantic checks as `@check`. A failed `@write` removes its incomplete output file. Every design installed in ABC is +topologically ordered. The current writer emits binary format version 6; the reader also accepts version 5 and treats +its modules as ordinary non-black-box modules because that format predates module flags. + +`@status` prints the current design and top names, SN revision, selected technology, hierarchy form, last extraction +mode/module/revision, saved boundary hash, current GIA dimensions, and `@put` compatibility. A new `@read` or `@slang` +design starts at revision 1. Each transformation that installs a replacement SN design, and each successful `@put`, +advances the revision; `&` commands do not. An optimization that finds no profitable rewrite leaves the design and its +revision unchanged. + +`@blast` gives every GIA input and output a unique ordered name containing the retained SN signal name, bit index, and +interface index. It also hashes the selected module identity and all saved boundary occurrences, primitives, registers, +loops, and input/output endpoint records. Before insertion, `@put` verifies the SN revision, module ID and name, +boundary hash, GIA dimensions, and ordered GIA-name signature. It rejects a GIA whose interface was reordered, renamed, +or stripped of names, even if its input and output counts still match. The GIA must also remain combinational, with zero +registers. Normal interface-preserving `&` synthesis commands retain the names and remain compatible. + +MiniAIG has only an edge-triggered register convention. Therefore `@blast` and `@map_lut` explicitly reject any +level-sensitive `SN_REG_LATCH` reachable from the selected module until a semantics-preserving latch flow is available. + +`@check` performs a non-aborting consistency audit of the complete SN design. It validates core and type-specific +attribute vectors, fanin storage, object IDs, widths, names, constants, topology, state pairing, memory-port ownership, +instance/FAN ordering, hierarchy recursion, LUTs, gates, and mapped primitive interfaces. `@check -v` adds one summary +line per module. Memory, DSP, and carry mapping commands run the same checker transactionally before and after each +transformation, so an invalid result is diagnosed and rejected without replacing the current design. + +`@ps` prints compact statistics for every module definition by default. `@ps -M module` prints the selected module +instead and uses it as the root for optional hierarchy and detailed reports. `@ps -v` adds the selected hierarchy and +keeps opaque definitions annotated with `[blackbox]`. Like `%ps -d`, `@ps -d` prints occurrences by object type and +output/input width signature. It also reports every reachable black-box type, its instance-occurrence multiplicity, +PI/PO port and bit counts, and totals for abstract AIG inputs and outputs. Counts cover the elaborated hierarchy rooted +at the selected module (or the current design top when `-M` is absent), including repeated insts. Hierarchical totals +are accumulated over the module DAG rather than by recursively revisiting every inst, so statistics remain practical +for deeply repeated hierarchy. Memory is reported as used/allocated storage with rounded K, M, or G suffixes. + +`@map_mem`, `@map_dsp`, and `@map_add` map into the initial AMD/Xilinx UltraScale+ technology description. +Transformations are transactional and keep the original user-visible top-module name. `@map_add` replaces word-level +addition and subtraction of at least three bits by chains of behavioral `__sn_CARRY4` primitive insts. Propagate, +operand inversion, extension, and final slicing remain ordinary SN logic for subsequent LUT mapping. Run DSP mapping +before carry mapping so future DSP preadder and postadder recognition is not hidden. `@collapse` flattens user hierarchy +while retaining mapped hard-block leaf instances. + +Opaque `SN_MODULE_BLACKBOX` insts are preserved by hierarchy collapse even when ordinary user hierarchy is flattened. +During `@blast`, each opaque output is an additional GIA input and each opaque input is an additional GIA output, in +natural port and LSB-first bit order. A black-box `SN_PO` has `SN_INVALID_ID` as its sole fanin, explicitly recording +that its value has no SN implementation; no zero-valued placeholder is created. `@write` emits the preserved interface +as a port-only `(* blackbox *)` module. Internally an opaque module contains only its declared `SN_PI` and `SN_PO` +objects; an `inout` is a same-named PI/PO pair. Its body and descendants are absent from SN. `@check` permits the +invalid PO fanin only for this boundary representation, and `@ps -v` / `@ps -d` expose the retained black boxes and +their reachable occurrence counts. + +`SN_CAST` is a one-fanin operator whose object width and signedness define the result type. It does not permute bits. +An equal-width cast only changes the signedness annotation; widening sign-extends a signed result and zero-extends an +unsigned result; narrowing discards high bits and retains the LSB-first low-order portion. `sn_slang` adds casts for +explicit and implicit slang conversions, `$signed` / `$unsigned`, dynamic selected-value normalization, packed-value +updates, and final normalization of `SN_MUX` data branches to the mux result width. Memory, DSP, and carry mapping may +also introduce casts while adapting word-level values to primitive interfaces. The Verilog writer uses `$signed` or +`$unsigned` on a result-width wire, and the bit-blaster implements the same extension or truncation directly. + +`@opt_mux` restructures register mux cones by collecting root-to-terminal paths, grouping structurally identical +LSB-first word values, and ORing the corresponding path conditions. A register-output terminal is converted into an +explicit enable when the path controls are provably exclusive. The pass currently recognizes ordinary `SN_MUX` +trees and packed `SN_PMUX` alternatives; separately created casts, slices, repetitions, concatenations, and constants +are compared structurally. Rewritten modules are restored at their stable hierarchy IDs and retain every register +pair so that the canonical transition interface remains unchanged. The default profitability filter requires at +least 4-bit data, six paths, two eliminated paths, and a path-to-distinct-terminal ratio of at least 2:1. This avoids +increasing logic for narrow control muxes while retaining the intended wide datapath transformations. + +`@blast` traverses hierarchy directly without first allocating a flat SN module. Sequential extraction is the +default; `-c` selects combinational extraction. `-t` emits the same effective next-state functions as a purely +combinational transition AIG for equivalence checking. +`-M module` selects the module to +extract; the default is the current SN top. ABC records the selected module and the exact LSB-first boundary mapping, +then installs the resulting GIA as the current `&` network. The user may apply any `&`-space combinational synthesis +and mapping commands that preserve the number and order of combinational inputs and outputs. Nothing requires the +logic to be put back into SN: omitting `@put` leaves the SN design unchanged. + +Adders use a Brent-Kung parallel-prefix network by default. `@blast -r` selects ripple-carry adders instead. This +choice also applies to adder networks used while blasting subtraction and other arithmetic operators; `-b` separately +selects Booth rather than the direct-unsigned/Baugh-Wooley multiplier. Signed and unsigned relational operators use +a balanced, delay-oriented comparator by default; `@blast -d` toggles to the minimum-node topology implemented by ABC's +`&gencomp`. Equality comparison remains balanced in both modes. Ripple adders and multiplier compressor trees share +the seven-node full-adder construction from `Wlc_BlastFullAdder()`. Direct unsigned, signed Baugh-Wooley, and radix-4 +Booth partial products use the delay-aware, level-ordered matrix reduction adapted from `Wlc_BlastReduceMatrix()`, +followed by the selected Brent-Kung or ripple final adder. The radix-4 Booth recoding, signed correction, rectangular +operand handling, and unsigned zero extension follow `Wlc_BlastBooth()`. Binary mux trees use `Mini_AigMuxMulti()`, +while AND/OR reductions and equality aggregation use balanced `Mini_AigAndMulti()` trees over copied temporary +literals. One-hot priority muxes use a balanced sum-of-products tree; their result for a multi-hot select remains +intentionally undefined. Variable shifts instantiate only the useful barrel stages and combine all higher shift bits +into one balanced overshift condition. + +Unnamed constants are interned by width, signedness, and packed value within each module. Concatenations whose inputs +are all constant are folded into one packed `SN_CONST`, including tables wider than the per-object fanin-count limit. +When such a constant drives an `SN_BMUX`, blasting reads one output-bit column at a time and simplifies constant and +equal mux branches before creating MiniAIG nodes; it never materializes the complete packed table as an integer-literal +array. The Verilog writer splits very large constants into bounded-size hexadecimal concatenation chunks. + +In combinational mode (`@blast -c`), flop outputs become additional inputs, while raw data and synchronous control +inputs become separate outputs for later stitching; clock and asynchronous controls remain outside this boundary. +Mapped RAM/DSP and CARRY4 outputs and inputs are likewise exposed as additional cloud endpoints. `@put` checks the saved +interface and reconnects registers and mapped primitive instances. With the default sequential `@blast`, the AIG +transition functions elaborate synchronous reset, set, and enable controls in SN priority order; clock and asynchronous +controls remain outside the transition relation. Sequential-AIG insertion is deliberately rejected for now. + +`@map_lut` applies this combinational extraction and reconstruction module by module while preserving the natural SN +hierarchy. Child instances, registers, and mapped RAM/DSP/CARRY4 instances are partition boundaries, matching the broad +structure of Yosys's per-module ABC flow. `@map_lut -S "&resyn3; &if -m -K 6"` supplies an inline per-partition ABC +script; `-F script.abc` sources it from a file. The default is the same `&resyn3; &if -m -K 6` sequence. Every script +must preserve CI/CO order and leave a LUT-mapped GIA. Generic-memory modules left unsupported by `@map_mem` remain +unchanged and are reported as skipped partitions. Mapped nodes wider than the physical SN LUT6 primitive are +decomposed deterministically by Shannon expansion. The pass maps a duplicate design and commits it only after every +reachable non-primitive module succeeds. `-P num` runs the independent partition jobs concurrently using `num - 1` +pthread workers and one coordinating process. `-P 1` uses the current ABC process directly, so its last partition +becomes the current `&`-space GIA; use `-P 2` or more when the preexisting `&`-space network must remain untouched. +SN pthread support is compiled out on Windows, where `-P 1` remains fully supported and larger values are rejected. +`@map_lut -E prefix` stops at the same partition boundary, writes each nontrivial job as +`prefix__.aig` with a `.txt` interface-statistics sidecar, and does not run synthesis or modify +the SN design. This mode cannot be combined with `-S` or `-F`, currently requires `-P 1`, and is intended for +developing or benchmarking an external per-partition synthesis flow. +Generated clock and asynchronous-control cones remain outside the mapped cloud and are copied with per-occurrence +memoization when registers are reconnected. + +The transition AIG orders state bits canonically by depth-first natural instance type ID, natural `SN_REG_OUT` type +ID within each occurrence, and LSB-first bit index. Both hierarchy duplication and mux sharing preserve these IDs. +Consequently, the transition AIGs made before and after `@opt_mux` have identical CI/CO order and can be compared +directly with `&cec before.aig after.aig`. For large, structurally different cones, explicitly constructing the miter +is often much faster: `&r before.aig; &miter after.aig; &cec -m`. Transition-AIG insertion through `@put` is +deliberately rejected. + +`@put` replaces only the module selected by the preceding `@blast`. Its module ID, name, and port interface remain +stable, so parent instances and every other module in an uncollapsed hierarchical design are preserved. The current +GIA determines the reconstructed representation: + +- An unmapped GIA becomes explicit one-bit `SN_BIT_AND` and `SN_BIT_NOT` objects. +- A LUT-mapped GIA becomes `SN_LUT` objects with truth tables transferred through MiniLUT. +- A cell-mapped GIA becomes `SN_GATE` objects annotated with current genlib gate IDs and cell names through ABC's + mini-mapping format. Insertion requires the current genlib to contain every referenced gate. + +For example, `@blast -c; &resyn3; &if -m -K 6; @put` implements the former canned LUT-mapping flow without hiding +the ABC script. `@blast -c; &dc2; @put` reinserts an optimized unmapped AIG, while +`read_genlib library.genlib; @blast -c; &nf; @put` reinserts standard cells. The Verilog writer emits LUT and gate +instances as well as ordinary SN logic. + +Mapped RAM/DSP/CARRY4 instances are reconstructed as technology leaf instances. SN loop-breaker pairs connect their +output ports while the new flat module is built and are placed into a legal order by the final topological reorder. +Temporary primitive-output loop pairs are pruned after reconnection unless an actual feedback dependency remains, so +acyclic datapaths do not gain artificial loop-breakers. Explicit loop boundaries extracted from the original SN module +are reconstructed unchanged; they are not currently re-proved unnecessary after `&`-space optimization. Generic +unmapped memory endpoints are recorded and abstracted by `@blast`, but `@put` currently rejects them because the +boundary does not yet retain enough per-memory-port ownership data. This check prevents silent loss or misconnection +of stateful memories. + +## Source files + +The package uses ABC-style filenames: + +```text +sn.h core representation, hierarchy, serialization, and Verilog writer +snCheck.h non-aborting design, module, hierarchy, and technology-interface consistency checker +snTech.h target technology descriptions +snMapMem.h memory mapping support +snMapDsp.h DSP mapping support +snMapAdd.h CARRY4 mapping support +snMapTech.h combined hierarchy mapping +snMapLut.h natural-hierarchy LUT-mapping harness +snPth.h bounded pthread worker harness +snBlast.h direct hierarchical MiniAIG construction +snMux.h register mux-path sharing and restructuring +snBoundary.h saved boundary and combinational register reconnection +snMiniAig.h unmapped MiniAIG reconstruction +snMiniLut.h MiniLUT analysis and SN_LUT reconstruction +snMiniGate.h mini-mapping and SN_GATE reconstruction +snCom.c ABC manager ownership and command handlers +``` + +The external frontend must compile against this directory through a configured include path. Representation changes +are made here first and must update the binary-format version when serialization compatibility changes. diff --git a/src/base/sn/sn.h b/src/base/sn/sn.h new file mode 100644 index 000000000..415953433 --- /dev/null +++ b/src/base/sn/sn.h @@ -0,0 +1,5411 @@ +/**CFile**************************************************************** + + FileName [sn.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Simple hierarchical word-level netlist data structures and core APIs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: sn.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_H +#define SN_H + +#include +#include +#include +#include +#include +#include +#include + +#include "misc/util/abc_namespaces.h" + +ABC_NAMESPACE_HEADER_START + +// Simple Netlist (sn) +// ------------------- +// +// A design owns a dense array of modules. A module owns a dense array of +// objects. There are no separate pin or net records: an object is identified +// by its module-local integer ID, and its fanins are object IDs in the same +// module. Fanouts are not stored by default; they can be derived and cached on +// demand. +// +// Core object properties use structure-of-arrays storage. Therefore an object +// ID is also the index of that object's type, width / signedness, fanin count, +// fanin offset, type ID, and name ID. A type ID indexes the dense array of +// objects of that type and any type-specific metadata. +// +// Ordered bit collections use LSB-first significance order throughout SN. +// Index zero denotes the least-significant bit, word, or concatenation operand; +// increasing indices denote increasingly significant data. This is an IR +// convention, independent of how SystemVerilog writes ranges and concatenations. +// Importers and writers must translate between the two conventions. +// +// All normal operators have one output, represented by the operator object +// itself. A single-output module inst is represented the same way. A +// multi-output module inst is immediately followed by one SN_FAN object +// per output in the referenced module's natural SN_PO order. Every SN_FAN has +// the inst as its sole fanin and stores only that inst ID as type- +// specific data. The invariant fan_id == inst_id + 1 + output_index makes +// the output index derived rather than stored. +// +// UINT32_MAX denotes an illegal, unused, or not-yet-connected fanin. This lets +// builders allocate objects before all dependencies have been translated. +// +// Registers, memories, and loop breakers are state / ordering boundaries made +// from adjacent OUT and IN objects. REG_OUT has fixed fanin slots for clock, +// data (= its paired REG_IN), enable, set, reset, packed initial data, an +// equally wide per-bit initialization-validity mask, and an optional nonzero/ +// non-one reset value. REG_IN has one initially unresolved fanin for +// next-state data. MEM_OUT has fixed fanins for its paired MEM_IN, packed +// initialization data, and an equally wide per-bit validity mask. MEM_IN has +// zero or more MEM_WRITE fanins. MEM_READ objects consume MEM_OUT and +// represent individual read ports. +// Topological algorithms must not traverse OUT-to-IN structural edges as +// combinational dependencies. +// +// Each REG_OUT/REG_IN, MEM_OUT/MEM_IN, and LOOP_OUT/LOOP_IN pair shares one +// type ID. For pair index k, type_objects[OUT_TYPE][k] and +// type_objects[IN_TYPE][k] are the corresponding objects. This provides +// bidirectional lookup without storing another object ID. Constructors create +// adjacent OUT/IN objects, but topologically reordered modules need not retain +// that adjacency; the shared type ID is the authoritative pairing invariant. +// +// A module marked SN_MODULE_BLACKBOX retains only its declared PI/PO interface. +// Its implementation is intentionally opaque: hierarchy collapse preserves its +// insts, AIG construction abstracts their outputs as CIs and inputs as COs, and +// the Verilog writer emits a black-box module declaration without a body. Each +// black-box PO has SN_INVALID_ID as its sole fanin, denoting an unimplemented +// boundary value rather than an ordinary undriven net or a zero constant. An +// inout port is represented by same-named PI and PO objects. + +#define SN_INVALID_ID UINT32_MAX + +typedef uint32_t sn_obj_id_t; +typedef uint32_t sn_module_id_t; +typedef uint32_t sn_name_id_t; +typedef uint32_t sn_type_id_t; +typedef uint16_t sn_fanin_count_t; + +typedef enum sn_module_flag_t +{ + SN_MODULE_NO_FLAGS = 0, + SN_MODULE_BLACKBOX = 1u << 0 +} sn_module_flag_t; + +#define SN_MODULE_ALL_FLAGS ((uint32_t)SN_MODULE_BLACKBOX) + +// A C-style generic vector. Cap and size are measured in elements. +// Access macros take the element type explicitly, for example: +// +// sn_vec_t values; +// sn_vec_init(&values); +// *sn_vec_push(int, &values) = 42; +// int value = sn_vec_at(int, &values, 0); +// +// The same API supports pointer element types: +// +// *sn_vec_push(void *, &values) = pointer; +typedef struct sn_vec_t +{ + void* data; + size_t cap; + size_t size; +} sn_vec_t; + +static inline void sn_vec_init(sn_vec_t* vec) +{ + assert(vec); + vec->data = NULL; + vec->cap = 0; + vec->size = 0; +} + +static inline void sn_vec_destroy(sn_vec_t* vec) +{ + assert(vec); + free(vec->data); + sn_vec_init(vec); +} + +static inline void sn_vec_reserve_raw(sn_vec_t* vec, size_t size, size_t cap) +{ + assert(vec); + assert(size); + if (cap <= vec->cap) + return; + + size_t new_cap = vec->cap ? vec->cap : 8; + while (new_cap < cap) + { + assert(new_cap <= SIZE_MAX / 2); + new_cap *= 2; + } + assert(new_cap <= SIZE_MAX / size); + + void* data = realloc(vec->data, new_cap * size); + assert(data); + vec->data = data; + vec->cap = new_cap; +} + +static inline void sn_vec_resize_raw(sn_vec_t* vec, size_t size, size_t count) +{ + assert(vec); + size_t old_size = vec->size; + sn_vec_reserve_raw(vec, size, count); + if (count > old_size) + memset((char*)vec->data + old_size * size, 0, (count - old_size) * size); + vec->size = count; +} + +static inline void* sn_vec_push_raw(sn_vec_t* vec, size_t size) +{ + assert(vec); + assert(vec->size < SIZE_MAX); + sn_vec_reserve_raw(vec, size, vec->size + 1); + void* slot = (char*)vec->data + vec->size * size; + memset(slot, 0, size); + vec->size++; + return slot; +} + +static inline void sn_vec_dup_raw(sn_vec_t* target, const sn_vec_t* source, size_t element_size) +{ + assert(target && source && element_size && target->data == NULL && target->size == 0 && target->cap == 0); + sn_vec_resize_raw(target, element_size, source->size); + if (source->size) + memcpy(target->data, source->data, source->size * element_size); +} + +#define sn_vec_data(type, vec) ((type*)((vec)->data)) +#define sn_vec_at(type, vec, index) (sn_vec_data(type, vec)[(index)]) +#define sn_vec_reserve(type, vec, cap) sn_vec_reserve_raw((vec), sizeof(type), (cap)) +#define sn_vec_resize(type, vec, count) sn_vec_resize_raw((vec), sizeof(type), (count)) +#define sn_vec_push(type, vec) ((type*)sn_vec_push_raw((vec), sizeof(type))) +#define sn_vec_dup(type, target, source) sn_vec_dup_raw((target), (source), sizeof(type)) + +typedef uint8_t sn_obj_type_t; + +enum sn_obj_type_enum +{ + SN_NONE = 0, + + // Design interface and structural objects. + SN_PI, + SN_PO, + SN_CONST0, + SN_CONST1, + SN_CONST, + SN_BUF, + SN_FAN, + SN_INST, + + // State and ordering boundaries. + SN_REG_OUT, + SN_REG_IN, + SN_MEM_OUT, + SN_MEM_IN, + SN_MEM_READ, + SN_MEM_WRITE, + SN_LOOP_OUT, + SN_LOOP_IN, + + // Unary operators. + SN_POS, + SN_NEG, + SN_BIT_NOT, + SN_LOG_NOT, + SN_REDUCE_AND, + SN_REDUCE_NAND, + SN_REDUCE_OR, + SN_REDUCE_NOR, + SN_REDUCE_XOR, + SN_REDUCE_XNOR, + + // Arithmetic operators. + SN_ADD, + SN_SUB, + // Multiplication fanins retain their independent native widths and signedness. The SN_MUL width is the required + // result width; blasting and mapping resize the product at its output rather than widening both operands first. + SN_MUL, + SN_DIV, + SN_MOD, + SN_POW, + + // Bitwise and logical operators. + SN_BIT_AND, + SN_BIT_OR, + SN_BIT_XOR, + SN_BIT_XNOR, + SN_LOG_AND, + SN_LOG_OR, + + // Comparison operators. + SN_EQ, + SN_NE, + SN_CASE_EQ, + SN_CASE_NE, + SN_WILDCARD_EQ, + SN_WILDCARD_NE, + SN_LT, + SN_LE, + SN_GT, + SN_GE, + + // Shift and word-construction operators. + SN_SHL, + SN_SHR, + SN_ASHL, + SN_ASHR, + + // Mux fanins always put the control first and any default value last. + // + // SN_MUX: [select, selected_when_one, default_when_zero] + // SN_BMUX: [binary_select, packed_alternatives] + // SN_PMUX: [one_hot_select, packed_alternatives, default_when_zero] + // + // Packed alternatives use LSB-first significance order. Alternative i + // occupies bits [i * output_width +: output_width]. SN_PMUX produces its + // default when the select is zero and is undefined for a multi-hot select. + SN_MUX, + SN_BMUX, + SN_PMUX, + + // Concatenation uses LSB-first significance order: fanin zero supplies the + // least-significant result bits, and later fanins supply successively more- + // significant bits. Repetition has one fanin and a type-indexed repeat count. + // Slice has one fanin and type-indexed left, right, and direction data. + // Cast also has one fanin. Its object width and signedness define the result: + // widening sign-extends a signed result and zero-extends an unsigned result; + // narrowing retains the LSB-first low-order bits; equal-width conversion + // changes only the signedness annotation. + SN_CONCAT, + SN_REPLICATE, + SN_SLICE, + SN_CAST, + + // One-bit FPGA lookup table. Fanin zero is truth-table variable I0. + // The low 2^fanin_count bits of the type-indexed uint64_t are significant. + SN_LUT, + + // One-bit technology-mapped gate. Its uint32_t gate ID identifies the gate + // in the technology library; fanins follow the library cell's input order. + SN_GATE, + + SN_OBJ_TYPE_COUNT +}; + +// Use a negative-size array as a compile-time check because ABC is also built +// as C by MSVC, whose C frontend does not accept the C11 _Static_assert keyword. +typedef char sn_obj_type_count_must_fit_in_uint8_t[(SN_OBJ_TYPE_COUNT <= UINT8_MAX) ? 1 : -1]; + +typedef enum sn_mux_fanin_t +{ + SN_MUX_SELECT = 0, + SN_MUX_SELECTED, + SN_MUX_DEFAULT, + SN_MUX_FANIN_COUNT +} sn_mux_fanin_t; + +typedef enum sn_bmux_fanin_t +{ + SN_BMUX_SELECT = 0, + SN_BMUX_ALTERNATIVES, + SN_BMUX_FANIN_COUNT +} sn_bmux_fanin_t; + +typedef enum sn_pmux_fanin_t +{ + SN_PMUX_SELECT = 0, + SN_PMUX_ALTERNATIVES, + SN_PMUX_DEFAULT, + SN_PMUX_FANIN_COUNT +} sn_pmux_fanin_t; + +enum +{ + SN_REG_CLOCK_NEGEDGE = 1u << 0, + SN_REG_RESET_NEGEDGE = 1u << 1, + SN_REG_RESET_ASYNC = 1u << 2, + SN_REG_SET_NEGEDGE = 1u << 3, + SN_REG_SET_ASYNC = 1u << 4, + // A level-sensitive latch uses SN_REG_ENABLE as its gate and has no clock fanin. + SN_REG_LATCH = 1u << 5, + SN_REG_FLAGS_ALL = + SN_REG_CLOCK_NEGEDGE | SN_REG_RESET_NEGEDGE | SN_REG_RESET_ASYNC | SN_REG_SET_NEGEDGE | SN_REG_SET_ASYNC | + SN_REG_LATCH +}; + +typedef enum sn_reg_fanin_t +{ + SN_REG_CLOCK = 0, + SN_REG_DATA, + SN_REG_ENABLE, + SN_REG_SET, + SN_REG_RESET, + SN_REG_INIT_DATA, + SN_REG_INIT = SN_REG_INIT_DATA, + SN_REG_INIT_MASK, + // UINT32_MAX means the reset value is the implicit all-zero constant. + SN_REG_RESET_VALUE, + SN_REG_FANIN_COUNT +} sn_reg_fanin_t; + +typedef struct sn_slice_info_t +{ + int32_t left_index; + int32_t right_index; + uint32_t flags; +} sn_slice_info_t; + +enum +{ + SN_SLICE_DESCENDING = 1u << 0 +}; + +// UINT32_MAX in the clock slot denotes an asynchronous read. UINT32_MAX in +// an enable slot denotes an always-enabled port. +typedef enum sn_mem_read_fanin_t +{ + SN_MEM_READ_MEMORY = 0, + SN_MEM_READ_CLOCK, + SN_MEM_READ_ENABLE, + SN_MEM_READ_ADDRESS, + SN_MEM_READ_FANIN_COUNT +} sn_mem_read_fanin_t; + +typedef enum sn_mem_write_fanin_t +{ + SN_MEM_WRITE_CLOCK = 0, + SN_MEM_WRITE_ENABLE, + SN_MEM_WRITE_DATA, + SN_MEM_WRITE_ADDRESS, + SN_MEM_WRITE_FANIN_COUNT +} sn_mem_write_fanin_t; + +// Memory initialization uses the same LSB-first packed layout for data and +// validity: entry zero occupies the least-significant word-width bits. A mask +// bit of one means that the corresponding data bit has a specified initial +// value. UINT32_MAX in either init slot means that the slot is absent; an +// absent mask with present data means that every data bit is valid. +typedef enum sn_mem_out_fanin_t +{ + SN_MEM_STATE = 0, + SN_MEM_INIT_DATA, + SN_MEM_INIT_MASK, + SN_MEM_OUT_FANIN_COUNT +} sn_mem_out_fanin_t; + +typedef struct sn_obj_pair_t +{ + sn_obj_id_t out; + sn_obj_id_t in; +} sn_obj_pair_t; + +typedef struct sn_name_mgr_t +{ + // char* entries owned by this manager and indexed by sn_name_id_t. + sn_vec_t names; + + // Chained hash table. Buckets and links contain name IDs. + sn_vec_t buckets; + sn_vec_t links; +} sn_name_mgr_t; + +typedef struct sn_design_t sn_design_t; + +typedef struct sn_const_hash_entry_t +{ + uint64_t hash; + sn_obj_id_t object; + uint32_t next; +} sn_const_hash_entry_t; + +typedef struct sn_module_t +{ + sn_design_t* design; + sn_module_id_t id; + sn_name_id_t name; + uint32_t flags; + + // Core object attributes, all indexed by sn_obj_id_t. + sn_vec_t obj_types; + sn_vec_t width_signed; + sn_vec_t fanin_counts; + sn_vec_t fanin_offsets; + sn_vec_t type_ids; + sn_vec_t name_ids; + + // Concatenated fanin spans for all objects. + sn_vec_t fanins; + + // For each object type, type_objects[type][type_id] is an object ID. + sn_vec_t type_objects[SN_OBJ_TYPE_COUNT]; + + // Type-specific metadata indexed by the corresponding OUT / object type ID. + // uint32_t flags indexed by SN_REG_OUT type ID. + sn_vec_t reg_flags; + // uint32_t entry count indexed by SN_MEM_OUT type ID. + sn_vec_t mem_depths; + // Referenced sn_module_id_t indexed by SN_INST type ID. + sn_vec_t inst_modules; + // Owning sn_obj_id_t indexed by SN_FAN type ID. + sn_vec_t fan_insts; + sn_vec_t slice_infos; + // uint32_t repetition count indexed by SN_REPLICATE type ID. + sn_vec_t repeat_counts; + // uint32_t offset into design->constant_words indexed by SN_CONST type ID. + sn_vec_t const_word_offsets; + // Derived chained hash table for unnamed constant interning. It is rebuilt lazily after binary loading. + sn_vec_t const_hash_buckets; + sn_vec_t const_hash_entries; + // uint64_t truth table indexed by SN_LUT type ID. + sn_vec_t lut_truths; + // uint32_t technology-library gate ID indexed by SN_GATE type ID. + sn_vec_t gate_ids; + + // Optional derived fanout cache, indexed like the fanin representation. + sn_vec_t fanout_counts; + sn_vec_t fanout_offsets; + sn_vec_t fanouts; + bool fanouts_valid; + + // PI and PO lists become immutable once this module is instantiated. + bool interface_locked; + + // Most recent duplication map: old object ID -> object ID in copy_module. + // It belongs to this source module and is released with the module. + sn_vec_t copy_ids; + sn_module_id_t copy_module; +} sn_module_t; + +struct sn_design_t +{ + // sn_module_t* entries indexed by sn_module_id_t. + sn_vec_t modules; + sn_name_mgr_t names; + + // SN_CONST values other than zero and one, packed as 32-bit words in + // LSB-first significance order. The module's const_word_offsets array + // stores each constant's starting word offset. + sn_vec_t constant_words; +}; + +// Memory accounting distinguishes populated payload bytes from reserved heap +// cap. Allocated bytes are the bytes requested from malloc/realloc; they +// do not include allocator headers or size-class rounding. +typedef struct sn_mem_size_t +{ + size_t used_bytes; + size_t allocated_bytes; +} sn_mem_size_t; + +// A module owns its struct and the payload allocations listed below. Vector +// headers are embedded in module_struct and are therefore not counted again. +typedef struct sn_module_mem_usage_t +{ + sn_mem_size_t module_struct; + sn_mem_size_t obj_types; + sn_mem_size_t width_signed; + sn_mem_size_t fanin_counts; + sn_mem_size_t fanin_offsets; + sn_mem_size_t type_ids; + sn_mem_size_t name_ids; + sn_mem_size_t fanins; + sn_mem_size_t type_objects[SN_OBJ_TYPE_COUNT]; + sn_mem_size_t reg_flags; + sn_mem_size_t mem_depths; + sn_mem_size_t inst_modules; + sn_mem_size_t fan_insts; + sn_mem_size_t slice_infos; + sn_mem_size_t repeat_counts; + sn_mem_size_t const_word_offsets; + sn_mem_size_t const_hash_buckets; + sn_mem_size_t const_hash_entries; + sn_mem_size_t lut_truths; + sn_mem_size_t gate_ids; + sn_mem_size_t fanout_counts; + sn_mem_size_t fanout_offsets; + sn_mem_size_t fanouts; + sn_mem_size_t copy_ids; + sn_mem_size_t total; +} sn_module_mem_usage_t; + +// modules is the sum of all module structs and their payloads. names is the +// sum of the name pointer/index/hash arrays and the separately allocated +// zero-terminated strings. constant_words is reported separately as requested. +typedef struct sn_design_mem_usage_t +{ + sn_mem_size_t design_struct; + sn_mem_size_t module_table; + sn_mem_size_t modules; + sn_module_mem_usage_t module_attributes; + sn_mem_size_t name_pointers; + sn_mem_size_t name_buckets; + sn_mem_size_t name_links; + sn_mem_size_t name_strings; + sn_mem_size_t names; + sn_mem_size_t constant_words; + sn_mem_size_t total; +} sn_design_mem_usage_t; + +static inline sn_mem_size_t sn_mem_size_make(size_t used_bytes, size_t allocated_bytes) +{ + sn_mem_size_t usage = {used_bytes, allocated_bytes}; + return usage; +} + +static inline sn_mem_size_t sn_vec_mem_usage(const sn_vec_t* vec, size_t size) +{ + assert(vec); + assert(size); + assert(vec->size <= vec->cap); + assert(vec->cap <= SIZE_MAX / size); + sn_mem_size_t usage = {vec->size * size, vec->cap * size}; + return usage; +} + +static inline void sn_mem_size_add(sn_mem_size_t* total, sn_mem_size_t usage) +{ + assert(total); + assert(total->used_bytes <= SIZE_MAX - usage.used_bytes); + assert(total->allocated_bytes <= SIZE_MAX - usage.allocated_bytes); + total->used_bytes += usage.used_bytes; + total->allocated_bytes += usage.allocated_bytes; +} + +static inline void sn_module_get_mem_usage(const sn_module_t* module, sn_module_mem_usage_t* usage) +{ + assert(module); + assert(usage); + memset(usage, 0, sizeof(*usage)); + usage->module_struct = sn_mem_size_make(sizeof(*module), sizeof(*module)); + usage->obj_types = sn_vec_mem_usage(&module->obj_types, sizeof(sn_obj_type_t)); + usage->width_signed = sn_vec_mem_usage(&module->width_signed, sizeof(uint32_t)); + usage->fanin_counts = sn_vec_mem_usage(&module->fanin_counts, sizeof(sn_fanin_count_t)); + usage->fanin_offsets = sn_vec_mem_usage(&module->fanin_offsets, sizeof(uint32_t)); + usage->type_ids = sn_vec_mem_usage(&module->type_ids, sizeof(uint32_t)); + usage->name_ids = sn_vec_mem_usage(&module->name_ids, sizeof(uint32_t)); + usage->fanins = sn_vec_mem_usage(&module->fanins, sizeof(sn_obj_id_t)); + for (size_t i = 0; i < SN_OBJ_TYPE_COUNT; i++) + usage->type_objects[i] = sn_vec_mem_usage(&module->type_objects[i], sizeof(sn_obj_id_t)); + usage->reg_flags = sn_vec_mem_usage(&module->reg_flags, sizeof(uint32_t)); + usage->mem_depths = sn_vec_mem_usage(&module->mem_depths, sizeof(uint32_t)); + usage->inst_modules = sn_vec_mem_usage(&module->inst_modules, sizeof(sn_module_id_t)); + usage->fan_insts = sn_vec_mem_usage(&module->fan_insts, sizeof(sn_obj_id_t)); + usage->slice_infos = sn_vec_mem_usage(&module->slice_infos, sizeof(sn_slice_info_t)); + usage->repeat_counts = sn_vec_mem_usage(&module->repeat_counts, sizeof(uint32_t)); + usage->const_word_offsets = sn_vec_mem_usage(&module->const_word_offsets, sizeof(uint32_t)); + usage->const_hash_buckets = sn_vec_mem_usage(&module->const_hash_buckets, sizeof(uint32_t)); + usage->const_hash_entries = sn_vec_mem_usage(&module->const_hash_entries, sizeof(sn_const_hash_entry_t)); + usage->lut_truths = sn_vec_mem_usage(&module->lut_truths, sizeof(uint64_t)); + usage->gate_ids = sn_vec_mem_usage(&module->gate_ids, sizeof(uint32_t)); + usage->fanout_counts = sn_vec_mem_usage(&module->fanout_counts, sizeof(uint32_t)); + usage->fanout_offsets = sn_vec_mem_usage(&module->fanout_offsets, sizeof(uint32_t)); + usage->fanouts = sn_vec_mem_usage(&module->fanouts, sizeof(sn_obj_id_t)); + usage->copy_ids = sn_vec_mem_usage(&module->copy_ids, sizeof(sn_obj_id_t)); + + usage->total = usage->module_struct; +#define SN_MEM_ADD_FIELD(field) sn_mem_size_add(&usage->total, usage->field) + SN_MEM_ADD_FIELD(obj_types); + SN_MEM_ADD_FIELD(width_signed); + SN_MEM_ADD_FIELD(fanin_counts); + SN_MEM_ADD_FIELD(fanin_offsets); + SN_MEM_ADD_FIELD(type_ids); + SN_MEM_ADD_FIELD(name_ids); + SN_MEM_ADD_FIELD(fanins); + for (size_t i = 0; i < SN_OBJ_TYPE_COUNT; i++) + sn_mem_size_add(&usage->total, usage->type_objects[i]); + SN_MEM_ADD_FIELD(reg_flags); + SN_MEM_ADD_FIELD(mem_depths); + SN_MEM_ADD_FIELD(inst_modules); + SN_MEM_ADD_FIELD(fan_insts); + SN_MEM_ADD_FIELD(slice_infos); + SN_MEM_ADD_FIELD(repeat_counts); + SN_MEM_ADD_FIELD(const_word_offsets); + SN_MEM_ADD_FIELD(const_hash_buckets); + SN_MEM_ADD_FIELD(const_hash_entries); + SN_MEM_ADD_FIELD(lut_truths); + SN_MEM_ADD_FIELD(gate_ids); + SN_MEM_ADD_FIELD(fanout_counts); + SN_MEM_ADD_FIELD(fanout_offsets); + SN_MEM_ADD_FIELD(fanouts); + SN_MEM_ADD_FIELD(copy_ids); +#undef SN_MEM_ADD_FIELD +} + +static inline void sn_module_mem_usage_add(sn_module_mem_usage_t* total, const sn_module_mem_usage_t* usage) +{ + assert(total); + assert(usage); +#define SN_MEM_ADD_MODULE_FIELD(field) sn_mem_size_add(&total->field, usage->field) + SN_MEM_ADD_MODULE_FIELD(module_struct); + SN_MEM_ADD_MODULE_FIELD(obj_types); + SN_MEM_ADD_MODULE_FIELD(width_signed); + SN_MEM_ADD_MODULE_FIELD(fanin_counts); + SN_MEM_ADD_MODULE_FIELD(fanin_offsets); + SN_MEM_ADD_MODULE_FIELD(type_ids); + SN_MEM_ADD_MODULE_FIELD(name_ids); + SN_MEM_ADD_MODULE_FIELD(fanins); + for (size_t i = 0; i < SN_OBJ_TYPE_COUNT; i++) + sn_mem_size_add(&total->type_objects[i], usage->type_objects[i]); + SN_MEM_ADD_MODULE_FIELD(reg_flags); + SN_MEM_ADD_MODULE_FIELD(mem_depths); + SN_MEM_ADD_MODULE_FIELD(inst_modules); + SN_MEM_ADD_MODULE_FIELD(fan_insts); + SN_MEM_ADD_MODULE_FIELD(slice_infos); + SN_MEM_ADD_MODULE_FIELD(repeat_counts); + SN_MEM_ADD_MODULE_FIELD(const_word_offsets); + SN_MEM_ADD_MODULE_FIELD(const_hash_buckets); + SN_MEM_ADD_MODULE_FIELD(const_hash_entries); + SN_MEM_ADD_MODULE_FIELD(lut_truths); + SN_MEM_ADD_MODULE_FIELD(gate_ids); + SN_MEM_ADD_MODULE_FIELD(fanout_counts); + SN_MEM_ADD_MODULE_FIELD(fanout_offsets); + SN_MEM_ADD_MODULE_FIELD(fanouts); + SN_MEM_ADD_MODULE_FIELD(copy_ids); + SN_MEM_ADD_MODULE_FIELD(total); +#undef SN_MEM_ADD_MODULE_FIELD +} + +static inline void sn_design_get_mem_usage(const sn_design_t* design, sn_design_mem_usage_t* usage) +{ + assert(design); + assert(usage); + memset(usage, 0, sizeof(*usage)); + usage->design_struct = sn_mem_size_make(sizeof(*design), sizeof(*design)); + usage->module_table = sn_vec_mem_usage(&design->modules, sizeof(sn_module_t*)); + usage->name_pointers = sn_vec_mem_usage(&design->names.names, sizeof(char*)); + usage->name_buckets = sn_vec_mem_usage(&design->names.buckets, sizeof(uint32_t)); + usage->name_links = sn_vec_mem_usage(&design->names.links, sizeof(uint32_t)); + for (size_t i = 0; i < design->names.names.size; i++) + { + size_t string_bytes = strlen(sn_vec_at(char*, &design->names.names, i)) + 1; + sn_mem_size_add(&usage->name_strings, sn_mem_size_make(string_bytes, string_bytes)); + } + usage->names = usage->name_pointers; + sn_mem_size_add(&usage->names, usage->name_buckets); + sn_mem_size_add(&usage->names, usage->name_links); + sn_mem_size_add(&usage->names, usage->name_strings); + usage->constant_words = sn_vec_mem_usage(&design->constant_words, sizeof(uint32_t)); + for (size_t i = 0; i < design->modules.size; i++) + { + sn_module_mem_usage_t module_usage; + sn_module_get_mem_usage(sn_vec_at(sn_module_t*, &design->modules, i), &module_usage); + sn_module_mem_usage_add(&usage->module_attributes, &module_usage); + } + usage->modules = usage->module_attributes.total; + usage->total = usage->design_struct; + sn_mem_size_add(&usage->total, usage->module_table); + sn_mem_size_add(&usage->total, usage->modules); + sn_mem_size_add(&usage->total, usage->names); + sn_mem_size_add(&usage->total, usage->constant_words); +} + +static inline uint64_t sn_name_hash(const char* text) +{ + assert(text); + uint64_t hash = UINT64_C(1469598103934665603); + while (*text) + { + hash ^= (unsigned char)*text++; + hash *= UINT64_C(1099511628211); + } + return hash; +} + +static inline char* sn_string_dup(const char* text) +{ + assert(text); + size_t size = strlen(text) + 1; + char* copy = (char*)malloc(size); + assert(copy); + memcpy(copy, text, size); + return copy; +} + +static inline void sn_name_mgr_rehash(sn_name_mgr_t* mgr, size_t bucket_count) +{ + assert(mgr); + assert(bucket_count >= 8); + assert((bucket_count & (bucket_count - 1)) == 0); + + sn_vec_t buckets; + sn_vec_init(&buckets); + sn_vec_resize(uint32_t, &buckets, bucket_count); + for (size_t i = 0; i < bucket_count; i++) + sn_vec_at(uint32_t, &buckets, i) = SN_INVALID_ID; + + assert(mgr->links.size == mgr->names.size); + for (size_t i = 0; i < mgr->names.size; i++) + { + const char* name = sn_vec_at(char*, &mgr->names, i); + size_t bucket = (size_t)sn_name_hash(name) & (bucket_count - 1); + sn_vec_at(uint32_t, &mgr->links, i) = sn_vec_at(uint32_t, &buckets, bucket); + sn_vec_at(uint32_t, &buckets, bucket) = (uint32_t)i; + } + + sn_vec_destroy(&mgr->buckets); + mgr->buckets = buckets; +} + +static inline void sn_name_mgr_init(sn_name_mgr_t* mgr) +{ + assert(mgr); + sn_vec_init(&mgr->names); + sn_vec_init(&mgr->buckets); + sn_vec_init(&mgr->links); + sn_name_mgr_rehash(mgr, 64); +} + +static inline void sn_name_mgr_destroy(sn_name_mgr_t* mgr) +{ + assert(mgr); + for (size_t i = 0; i < mgr->names.size; i++) + free(sn_vec_at(char*, &mgr->names, i)); + sn_vec_destroy(&mgr->names); + sn_vec_destroy(&mgr->buckets); + sn_vec_destroy(&mgr->links); +} + +static inline sn_name_id_t sn_name_find(const sn_name_mgr_t* mgr, const char* text) +{ + assert(mgr); + assert(text); + assert(mgr->buckets.size); + + size_t bucket = (size_t)sn_name_hash(text) & (mgr->buckets.size - 1); + uint32_t id = sn_vec_at(uint32_t, &mgr->buckets, bucket); + while (id != SN_INVALID_ID) + { + assert(id < mgr->names.size); + if (strcmp(sn_vec_at(char*, &mgr->names, id), text) == 0) + return id; + id = sn_vec_at(uint32_t, &mgr->links, id); + } + return SN_INVALID_ID; +} + +static inline sn_name_id_t sn_name_intern(sn_name_mgr_t* mgr, const char* text) +{ + assert(mgr); + assert(text); + + sn_name_id_t id = sn_name_find(mgr, text); + if (id != SN_INVALID_ID) + return id; + + assert(mgr->names.size < SN_INVALID_ID); + if ((mgr->names.size + 1) * 4 >= mgr->buckets.size * 3) + sn_name_mgr_rehash(mgr, mgr->buckets.size * 2); + + id = (sn_name_id_t)mgr->names.size; + size_t bucket = (size_t)sn_name_hash(text) & (mgr->buckets.size - 1); + *sn_vec_push(char*, &mgr->names) = sn_string_dup(text); + *sn_vec_push(uint32_t, &mgr->links) = sn_vec_at(uint32_t, &mgr->buckets, bucket); + sn_vec_at(uint32_t, &mgr->buckets, bucket) = id; + return id; +} + +// Removes a temporary name that was the most recently interned entry. Reordering helpers use this after replacing a +// provisional module, preventing internal __sn_* names from accumulating in serialized designs. +static inline void sn_name_remove_last(sn_name_mgr_t* mgr, sn_name_id_t id) +{ + assert(mgr && mgr->names.size && id + 1 == mgr->names.size && mgr->links.size == mgr->names.size); + const char* name = sn_vec_at(char*, &mgr->names, id); + size_t bucket = (size_t)sn_name_hash(name) & (mgr->buckets.size - 1); + uint32_t current = sn_vec_at(uint32_t, &mgr->buckets, bucket); + uint32_t previous = SN_INVALID_ID; + while (current != id) + { + assert(current != SN_INVALID_ID && current < id); + previous = current; + current = sn_vec_at(uint32_t, &mgr->links, current); + } + uint32_t next = sn_vec_at(uint32_t, &mgr->links, id); + if (previous == SN_INVALID_ID) + sn_vec_at(uint32_t, &mgr->buckets, bucket) = next; + else + sn_vec_at(uint32_t, &mgr->links, previous) = next; + free(sn_vec_at(char*, &mgr->names, id)); + mgr->names.size--; + mgr->links.size--; +} + +static inline const char* sn_name_get(const sn_name_mgr_t* mgr, sn_name_id_t id) +{ + assert(mgr); + assert(id < mgr->names.size); + return sn_vec_at(char*, &mgr->names, id); +} + +static inline void sn_module_init(sn_module_t* module, sn_design_t* design, sn_module_id_t id, sn_name_id_t name) +{ + assert(module); + assert(design); + module->design = design; + module->id = id; + module->name = name; + module->flags = SN_MODULE_NO_FLAGS; + + sn_vec_init(&module->obj_types); + sn_vec_init(&module->width_signed); + sn_vec_init(&module->fanin_counts); + sn_vec_init(&module->fanin_offsets); + sn_vec_init(&module->type_ids); + sn_vec_init(&module->name_ids); + sn_vec_init(&module->fanins); + for (size_t i = 0; i < SN_OBJ_TYPE_COUNT; i++) + sn_vec_init(&module->type_objects[i]); + sn_vec_init(&module->reg_flags); + sn_vec_init(&module->mem_depths); + sn_vec_init(&module->inst_modules); + sn_vec_init(&module->fan_insts); + sn_vec_init(&module->slice_infos); + sn_vec_init(&module->repeat_counts); + sn_vec_init(&module->const_word_offsets); + sn_vec_init(&module->const_hash_buckets); + sn_vec_init(&module->const_hash_entries); + sn_vec_init(&module->lut_truths); + sn_vec_init(&module->gate_ids); + sn_vec_init(&module->fanout_counts); + sn_vec_init(&module->fanout_offsets); + sn_vec_init(&module->fanouts); + module->fanouts_valid = false; + module->interface_locked = false; + sn_vec_init(&module->copy_ids); + module->copy_module = SN_INVALID_ID; +} + +static inline void sn_module_destroy(sn_module_t* module) +{ + assert(module); + sn_vec_destroy(&module->obj_types); + sn_vec_destroy(&module->width_signed); + sn_vec_destroy(&module->fanin_counts); + sn_vec_destroy(&module->fanin_offsets); + sn_vec_destroy(&module->type_ids); + sn_vec_destroy(&module->name_ids); + sn_vec_destroy(&module->fanins); + for (size_t i = 0; i < SN_OBJ_TYPE_COUNT; i++) + sn_vec_destroy(&module->type_objects[i]); + sn_vec_destroy(&module->reg_flags); + sn_vec_destroy(&module->mem_depths); + sn_vec_destroy(&module->inst_modules); + sn_vec_destroy(&module->fan_insts); + sn_vec_destroy(&module->slice_infos); + sn_vec_destroy(&module->repeat_counts); + sn_vec_destroy(&module->const_word_offsets); + sn_vec_destroy(&module->const_hash_buckets); + sn_vec_destroy(&module->const_hash_entries); + sn_vec_destroy(&module->lut_truths); + sn_vec_destroy(&module->gate_ids); + sn_vec_destroy(&module->fanout_counts); + sn_vec_destroy(&module->fanout_offsets); + sn_vec_destroy(&module->fanouts); + sn_vec_destroy(&module->copy_ids); +} + +static inline sn_design_t* sn_design_create(void) +{ + sn_design_t* design = (sn_design_t*)calloc(1, sizeof(sn_design_t)); + assert(design); + sn_vec_init(&design->modules); + sn_name_mgr_init(&design->names); + sn_vec_init(&design->constant_words); + return design; +} + +static inline void sn_design_destroy(sn_design_t* design) +{ + if (!design) + return; + for (size_t i = 0; i < design->modules.size; i++) + { + sn_module_t* module = sn_vec_at(sn_module_t*, &design->modules, i); + sn_module_destroy(module); + free(module); + } + sn_vec_destroy(&design->modules); + sn_name_mgr_destroy(&design->names); + sn_vec_destroy(&design->constant_words); + free(design); +} + +static inline sn_module_id_t sn_design_add_module_name_id(sn_design_t* design, sn_name_id_t name) +{ + assert(design); + assert(name < design->names.names.size); + assert(design->modules.size < SN_INVALID_ID); + sn_module_id_t id = (sn_module_id_t)design->modules.size; + sn_module_t* module = (sn_module_t*)calloc(1, sizeof(sn_module_t)); + assert(module); + sn_module_init(module, design, id, name); + *sn_vec_push(sn_module_t*, &design->modules) = module; + return id; +} + +static inline sn_module_id_t sn_design_add_module(sn_design_t* design, const char* name) +{ + assert(design); + assert(name); + sn_name_id_t name_id = sn_name_intern(&design->names, name); + for (size_t i = 0; i < design->modules.size; i++) + assert(sn_vec_at(sn_module_t*, &design->modules, i)->name != name_id); + return sn_design_add_module_name_id(design, name_id); +} + +static inline sn_module_t* sn_design_get_module(sn_design_t* design, sn_module_id_t id) +{ + assert(design); + assert(id < design->modules.size); + return sn_vec_at(sn_module_t*, &design->modules, id); +} + +static inline const sn_module_t* sn_design_get_module_const(const sn_design_t* design, sn_module_id_t id) +{ + assert(design); + assert(id < design->modules.size); + return sn_vec_at(sn_module_t*, &design->modules, id); +} + +static inline sn_module_id_t sn_design_find_module(const sn_design_t* design, const char* name) +{ + assert(design); + assert(name); + sn_name_id_t name_id = sn_name_find(&design->names, name); + if (name_id == SN_INVALID_ID) + return SN_INVALID_ID; + for (sn_module_id_t id = 0; id < design->modules.size; id++) + if (sn_design_get_module_const(design, id)->name == name_id) + return id; + return SN_INVALID_ID; +} + +static inline bool sn_module_is_blackbox(const sn_module_t* module) +{ + assert(module); + return (module->flags & SN_MODULE_BLACKBOX) != 0; +} + +static inline void sn_module_set_blackbox(sn_module_t* module, bool blackbox) +{ + assert(module); + if (blackbox) + module->flags |= SN_MODULE_BLACKBOX; + else + module->flags &= ~((uint32_t)SN_MODULE_BLACKBOX); +} + +// Deep-copy the semantic design state directly in memory. Derived constant-interner tables are intentionally left +// empty and rebuilt lazily, matching binary roundtrip behavior. Fanout caches and optional object-copy maps are +// preserved because callers may intentionally retain them between transformations. +static inline sn_design_t* sn_design_dup(const sn_design_t* source) +{ + assert(source); + sn_design_t* target = sn_design_create(); + for (size_t i = 0; i < source->names.names.size; i++) + { + sn_name_id_t name = sn_name_intern(&target->names, sn_name_get(&source->names, (sn_name_id_t)i)); + assert(name == i); + } + sn_vec_dup(uint32_t, &target->constant_words, &source->constant_words); + for (sn_module_id_t module_id = 0; module_id < source->modules.size; module_id++) + { + const sn_module_t* old_module = sn_design_get_module_const(source, module_id); + sn_module_id_t new_id = sn_design_add_module(target, sn_name_get(&source->names, old_module->name)); + assert(new_id == module_id); + sn_module_t* new_module = sn_design_get_module(target, new_id); + new_module->flags = old_module->flags; + new_module->fanouts_valid = old_module->fanouts_valid; + new_module->interface_locked = old_module->interface_locked; + new_module->copy_module = old_module->copy_module; +#define SN_DUP_MODULE_VECTOR(type, field) sn_vec_dup(type, &new_module->field, &old_module->field) + SN_DUP_MODULE_VECTOR(sn_obj_type_t, obj_types); + SN_DUP_MODULE_VECTOR(uint32_t, width_signed); + SN_DUP_MODULE_VECTOR(sn_fanin_count_t, fanin_counts); + SN_DUP_MODULE_VECTOR(uint32_t, fanin_offsets); + SN_DUP_MODULE_VECTOR(uint32_t, type_ids); + SN_DUP_MODULE_VECTOR(uint32_t, name_ids); + SN_DUP_MODULE_VECTOR(sn_obj_id_t, fanins); + for (uint32_t type = 0; type < SN_OBJ_TYPE_COUNT; type++) + sn_vec_dup(sn_obj_id_t, &new_module->type_objects[type], &old_module->type_objects[type]); + SN_DUP_MODULE_VECTOR(uint32_t, reg_flags); + SN_DUP_MODULE_VECTOR(uint32_t, mem_depths); + SN_DUP_MODULE_VECTOR(sn_module_id_t, inst_modules); + SN_DUP_MODULE_VECTOR(sn_obj_id_t, fan_insts); + SN_DUP_MODULE_VECTOR(sn_slice_info_t, slice_infos); + SN_DUP_MODULE_VECTOR(uint32_t, repeat_counts); + SN_DUP_MODULE_VECTOR(uint32_t, const_word_offsets); + SN_DUP_MODULE_VECTOR(uint64_t, lut_truths); + SN_DUP_MODULE_VECTOR(uint32_t, gate_ids); + SN_DUP_MODULE_VECTOR(uint32_t, fanout_counts); + SN_DUP_MODULE_VECTOR(uint32_t, fanout_offsets); + SN_DUP_MODULE_VECTOR(sn_obj_id_t, fanouts); + SN_DUP_MODULE_VECTOR(sn_obj_id_t, copy_ids); +#undef SN_DUP_MODULE_VECTOR + } + return target; +} + +static inline uint32_t sn_design_module_output_count(const sn_design_t* design, sn_module_id_t module_id) +{ + const sn_module_t* module = sn_design_get_module_const(design, module_id); + assert(module->type_objects[SN_PO].size <= UINT32_MAX); + return (uint32_t)module->type_objects[SN_PO].size; +} + +static inline uint32_t sn_pack_width_signed(uint32_t width, bool is_signed) +{ + assert(width <= UINT32_MAX >> 1); + return (width << 1) | (is_signed ? 1u : 0u); +} + +static inline uint32_t sn_obj_width(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(object < module->width_signed.size); + return sn_vec_at(uint32_t, &module->width_signed, object) >> 1; +} + +static inline bool sn_obj_is_signed(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(object < module->width_signed.size); + return (sn_vec_at(uint32_t, &module->width_signed, object) & 1u) != 0; +} + +static inline sn_obj_type_t sn_obj_type(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(object < module->obj_types.size); + return sn_vec_at(sn_obj_type_t, &module->obj_types, object); +} + +static inline sn_type_id_t sn_obj_type_id(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(object < module->type_ids.size); + return sn_vec_at(uint32_t, &module->type_ids, object); +} + +static inline sn_name_id_t sn_obj_name_id(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(object < module->name_ids.size); + return sn_vec_at(uint32_t, &module->name_ids, object); +} + +static inline const char* sn_obj_name(const sn_module_t* module, sn_obj_id_t object) +{ + sn_name_id_t name = sn_obj_name_id(module, object); + assert(name != SN_INVALID_ID); + return sn_name_get(&module->design->names, name); +} + +static inline void sn_module_invalidate_fanouts(sn_module_t* module) +{ + assert(module); + module->fanouts_valid = false; + module->fanout_counts.size = 0; + module->fanout_offsets.size = 0; + module->fanouts.size = 0; +} + +static inline sn_obj_id_t sn_module_add_obj(sn_module_t* module, sn_obj_type_t type, uint32_t width, bool is_signed, + uint32_t fanin_count, sn_name_id_t name) +{ + assert(module); + assert(type > SN_NONE && type < SN_OBJ_TYPE_COUNT); + assert(module->obj_types.size < SN_INVALID_ID); + assert(fanin_count <= UINT16_MAX); + assert(module->fanins.size + fanin_count <= UINT32_MAX); + assert(name == SN_INVALID_ID || name < module->design->names.names.size); + + size_t object_count = module->obj_types.size + 1; + sn_vec_reserve(sn_obj_type_t, &module->obj_types, object_count); + sn_vec_reserve(uint32_t, &module->width_signed, object_count); + sn_vec_reserve(sn_fanin_count_t, &module->fanin_counts, object_count); + sn_vec_reserve(uint32_t, &module->fanin_offsets, object_count); + sn_vec_reserve(uint32_t, &module->type_ids, object_count); + sn_vec_reserve(uint32_t, &module->name_ids, object_count); + sn_vec_reserve(sn_obj_id_t, &module->fanins, module->fanins.size + fanin_count); + + sn_vec_t* objects_of_type = &module->type_objects[type]; + assert(objects_of_type->size < SN_INVALID_ID); + sn_vec_reserve(sn_obj_id_t, objects_of_type, objects_of_type->size + 1); + + if (type == SN_REG_OUT) + sn_vec_reserve(uint32_t, &module->reg_flags, module->reg_flags.size + 1); + else if (type == SN_MEM_OUT) + sn_vec_reserve(uint32_t, &module->mem_depths, module->mem_depths.size + 1); + else if (type == SN_INST) + sn_vec_reserve(sn_module_id_t, &module->inst_modules, module->inst_modules.size + 1); + else if (type == SN_FAN) + sn_vec_reserve(sn_obj_id_t, &module->fan_insts, module->fan_insts.size + 1); + else if (type == SN_SLICE) + sn_vec_reserve(sn_slice_info_t, &module->slice_infos, module->slice_infos.size + 1); + else if (type == SN_REPLICATE) + sn_vec_reserve(uint32_t, &module->repeat_counts, module->repeat_counts.size + 1); + else if (type == SN_CONST) + sn_vec_reserve(uint32_t, &module->const_word_offsets, module->const_word_offsets.size + 1); + else if (type == SN_LUT) + sn_vec_reserve(uint64_t, &module->lut_truths, module->lut_truths.size + 1); + else if (type == SN_GATE) + sn_vec_reserve(uint32_t, &module->gate_ids, module->gate_ids.size + 1); + + sn_obj_id_t object = (sn_obj_id_t)module->obj_types.size; + sn_type_id_t type_id = (sn_type_id_t)objects_of_type->size; + uint32_t fanin_offset = (uint32_t)module->fanins.size; + + *sn_vec_push(sn_obj_type_t, &module->obj_types) = type; + *sn_vec_push(uint32_t, &module->width_signed) = sn_pack_width_signed(width, is_signed); + *sn_vec_push(sn_fanin_count_t, &module->fanin_counts) = (sn_fanin_count_t)fanin_count; + *sn_vec_push(uint32_t, &module->fanin_offsets) = fanin_offset; + *sn_vec_push(uint32_t, &module->type_ids) = type_id; + *sn_vec_push(uint32_t, &module->name_ids) = name; + *sn_vec_push(sn_obj_id_t, objects_of_type) = object; + + for (uint32_t i = 0; i < fanin_count; i++) + *sn_vec_push(sn_obj_id_t, &module->fanins) = SN_INVALID_ID; + + if (type == SN_REG_OUT) + { + assert(type_id == module->reg_flags.size); + sn_vec_push(uint32_t, &module->reg_flags); + } + else if (type == SN_MEM_OUT) + { + assert(type_id == module->mem_depths.size); + sn_vec_push(uint32_t, &module->mem_depths); + } + else if (type == SN_INST) + { + assert(type_id == module->inst_modules.size); + *sn_vec_push(sn_module_id_t, &module->inst_modules) = SN_INVALID_ID; + } + else if (type == SN_FAN) + { + assert(type_id == module->fan_insts.size); + *sn_vec_push(sn_obj_id_t, &module->fan_insts) = SN_INVALID_ID; + } + else if (type == SN_SLICE) + { + assert(type_id == module->slice_infos.size); + sn_vec_push(sn_slice_info_t, &module->slice_infos); + } + else if (type == SN_REPLICATE) + { + assert(type_id == module->repeat_counts.size); + sn_vec_push(uint32_t, &module->repeat_counts); + } + else if (type == SN_CONST) + { + assert(type_id == module->const_word_offsets.size); + sn_vec_push(uint32_t, &module->const_word_offsets); + } + else if (type == SN_LUT) + { + assert(type_id == module->lut_truths.size); + sn_vec_push(uint64_t, &module->lut_truths); + } + else if (type == SN_GATE) + { + assert(type_id == module->gate_ids.size); + sn_vec_push(uint32_t, &module->gate_ids); + } + + sn_module_invalidate_fanouts(module); + return object; +} + +static inline sn_obj_id_t sn_module_add_named_obj(sn_module_t* module, sn_obj_type_t type, uint32_t width, + bool is_signed, uint32_t fanin_count, const char* name) +{ + assert(module); + sn_name_id_t name_id = name ? sn_name_intern(&module->design->names, name) : SN_INVALID_ID; + return sn_module_add_obj(module, type, width, is_signed, fanin_count, name_id); +} + +static inline uint32_t sn_obj_fanin_count(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(object < module->fanin_counts.size); + return sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object); +} + +static inline sn_obj_id_t sn_obj_fanin(const sn_module_t* module, sn_obj_id_t object, uint32_t input_index) +{ + assert(module); + assert(object < module->fanin_counts.size); + assert(input_index < sn_obj_fanin_count(module, object)); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + assert((size_t)offset + input_index < module->fanins.size); + return sn_vec_at(sn_obj_id_t, &module->fanins, offset + input_index); +} + +static inline void sn_obj_connect(sn_module_t* module, sn_obj_id_t object, uint32_t input_index, sn_obj_id_t fanin) +{ + assert(module); + assert(object < module->obj_types.size); + assert(fanin == SN_INVALID_ID || fanin < module->obj_types.size); + assert(input_index < sn_obj_fanin_count(module, object)); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + sn_vec_at(sn_obj_id_t, &module->fanins, offset + input_index) = fanin; + sn_module_invalidate_fanouts(module); +} + +static inline void sn_obj_add_fanins(sn_module_t* module, sn_obj_id_t object, uint32_t added_count, + const sn_obj_id_t* added_fanins) +{ + assert(module); + assert(object < module->obj_types.size); + assert(added_count == 0 || added_fanins); + assert(module->fanins.size + added_count <= UINT32_MAX); + for (uint32_t i = 0; i < added_count; i++) + assert(added_fanins[i] < module->obj_types.size); + if (!added_count) + return; + + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + uint32_t count = sn_obj_fanin_count(module, object); + assert(added_count <= UINT16_MAX - count); + size_t insertion = (size_t)offset + count; + assert(insertion <= module->fanins.size); + + size_t old_size = module->fanins.size; + sn_vec_resize(sn_obj_id_t, &module->fanins, old_size + added_count); + sn_obj_id_t* fanins = sn_vec_data(sn_obj_id_t, &module->fanins); + memmove(fanins + insertion + added_count, fanins + insertion, (old_size - insertion) * sizeof(*fanins)); + memcpy(fanins + insertion, added_fanins, (size_t)added_count * sizeof(*fanins)); + sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object) = (sn_fanin_count_t)(count + added_count); + + // Preserve fanin-span order for every object after the modified object. + for (sn_obj_id_t other = object + 1; other < module->obj_types.size; other++) + { + uint32_t other_offset = sn_vec_at(uint32_t, &module->fanin_offsets, other); + if (other_offset >= insertion) + sn_vec_at(uint32_t, &module->fanin_offsets, other) = other_offset + added_count; + } + sn_module_invalidate_fanouts(module); +} + +static inline void sn_obj_add_fanin(sn_module_t* module, sn_obj_id_t object, sn_obj_id_t fanin) +{ + sn_obj_add_fanins(module, object, 1, &fanin); +} + +static inline sn_obj_id_t sn_module_add_pi(sn_module_t* module, uint32_t width, bool is_signed, const char* name) +{ + assert(module); + assert(!module->interface_locked); + return sn_module_add_named_obj(module, SN_PI, width, is_signed, 0, name); +} + +static inline sn_obj_id_t sn_module_add_po(sn_module_t* module, uint32_t width, bool is_signed, const char* name, + sn_obj_id_t driver) +{ + assert(module); + assert(!sn_module_is_blackbox(module)); + assert(!module->interface_locked); + assert(driver < module->obj_types.size); + assert(sn_obj_width(module, driver) == width); + sn_obj_id_t output = sn_module_add_named_obj(module, SN_PO, width, is_signed, 1, name); + sn_obj_connect(module, output, 0, driver); + return output; +} + +// Adds an output port to an opaque module. Unlike an ordinary SN_PO, whose +// only fanin is its RTL driver, a black-box output deliberately has no driver +// inside SN. Parent insts expose it through their normal SN_INST/SN_FAN +// boundary objects; collapse preserves that boundary and blasting abstracts +// the value as a new combinational input. +static inline sn_obj_id_t sn_module_add_blackbox_po(sn_module_t* module, uint32_t width, bool is_signed, + const char* name) +{ + assert(module); + assert(sn_module_is_blackbox(module)); + assert(!module->interface_locked); + return sn_module_add_named_obj(module, SN_PO, width, is_signed, 1, name); +} + +static inline bool sn_obj_fanin_may_be_invalid(const sn_module_t* module, sn_obj_type_t type, uint32_t index) +{ + assert(module); + if (type == SN_PO) + return sn_module_is_blackbox(module) && index == 0; + if (type == SN_REG_OUT) + return index != SN_REG_DATA; + if (type == SN_MEM_OUT) + return index == SN_MEM_INIT_DATA || index == SN_MEM_INIT_MASK; + if (type == SN_MEM_READ) + return index == SN_MEM_READ_CLOCK || index == SN_MEM_READ_ENABLE; + if (type == SN_MEM_WRITE) + return index == SN_MEM_WRITE_ENABLE; + return false; +} + +static inline bool sn_obj_type_is_operator(sn_obj_type_t type) +{ + return type == SN_BUF || (type >= SN_POS && type < SN_OBJ_TYPE_COUNT); +} + +static inline sn_obj_id_t sn_module_add_operator(sn_module_t* module, sn_obj_type_t type, uint32_t width, + bool is_signed, uint32_t fanin_count, const sn_obj_id_t* fanins, + const char* name) +{ + assert(sn_obj_type_is_operator(type)); + assert(fanin_count == 0 || fanins); + sn_obj_id_t object = sn_module_add_named_obj(module, type, width, is_signed, fanin_count, name); + for (uint32_t i = 0; i < fanin_count; i++) + sn_obj_connect(module, object, i, fanins[i]); + return object; +} + +static inline sn_obj_id_t sn_module_add_lut(sn_module_t* module, uint32_t fanin_count, + const sn_obj_id_t* fanins, uint64_t truth, const char* name) +{ + assert(module && fanin_count <= 6); + assert(fanin_count == 0 || fanins); + for (uint32_t i = 0; i < fanin_count; i++) + assert(fanins[i] < module->obj_types.size && sn_obj_width(module, fanins[i]) == 1); + if (fanin_count < 6) + truth &= (UINT64_C(1) << (UINT32_C(1) << fanin_count)) - 1; + sn_obj_id_t object = sn_module_add_operator(module, SN_LUT, 1, false, fanin_count, fanins, name); + sn_vec_at(uint64_t, &module->lut_truths, sn_obj_type_id(module, object)) = truth; + return object; +} + +static inline uint64_t sn_obj_lut_truth(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module && sn_obj_type(module, object) == SN_LUT); + return sn_vec_at(uint64_t, &module->lut_truths, sn_obj_type_id(module, object)); +} + +static inline sn_obj_id_t sn_module_add_gate(sn_module_t* module, uint32_t fanin_count, + const sn_obj_id_t* fanins, uint32_t gate_id, const char* name) +{ + assert(module); + assert(gate_id != SN_INVALID_ID); + assert(fanin_count == 0 || fanins); + for (uint32_t i = 0; i < fanin_count; i++) + assert(fanins[i] < module->obj_types.size && sn_obj_width(module, fanins[i]) == 1); + sn_obj_id_t object = sn_module_add_operator(module, SN_GATE, 1, false, fanin_count, fanins, name); + sn_vec_at(uint32_t, &module->gate_ids, sn_obj_type_id(module, object)) = gate_id; + return object; +} + +static inline uint32_t sn_obj_gate_id(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module && sn_obj_type(module, object) == SN_GATE); + return sn_vec_at(uint32_t, &module->gate_ids, sn_obj_type_id(module, object)); +} + +static inline sn_obj_id_t sn_module_add_mux(sn_module_t* module, sn_obj_id_t select, sn_obj_id_t selected, + sn_obj_id_t default_value, const char* name) +{ + assert(module); + assert(select < module->obj_types.size); + assert(selected < module->obj_types.size); + assert(default_value < module->obj_types.size); + assert(sn_obj_width(module, select) == 1); + assert(sn_obj_width(module, selected) == sn_obj_width(module, default_value)); + sn_obj_id_t fanins[SN_MUX_FANIN_COUNT] = {select, selected, default_value}; + return sn_module_add_operator(module, SN_MUX, sn_obj_width(module, default_value), + sn_obj_is_signed(module, default_value), SN_MUX_FANIN_COUNT, fanins, name); +} + +static inline sn_obj_id_t sn_module_add_bmux(sn_module_t* module, sn_obj_id_t select, sn_obj_id_t packed_alternatives, + uint32_t output_width, bool is_signed, const char* name) +{ + assert(module); + assert(select < module->obj_types.size); + assert(packed_alternatives < module->obj_types.size); + assert(output_width); + uint32_t select_width = sn_obj_width(module, select); + assert(select_width < 31); + uint64_t packed_width = (uint64_t)output_width << select_width; + assert(packed_width <= UINT32_MAX >> 1); + assert(sn_obj_width(module, packed_alternatives) == packed_width); + (void)packed_width; + sn_obj_id_t fanins[SN_BMUX_FANIN_COUNT] = {select, packed_alternatives}; + return sn_module_add_operator(module, SN_BMUX, output_width, is_signed, SN_BMUX_FANIN_COUNT, fanins, name); +} + +static inline sn_obj_id_t sn_module_add_pmux(sn_module_t* module, sn_obj_id_t select, sn_obj_id_t packed_alternatives, + sn_obj_id_t default_value, const char* name) +{ + assert(module); + assert(select < module->obj_types.size); + assert(packed_alternatives < module->obj_types.size); + assert(default_value < module->obj_types.size); + uint32_t select_width = sn_obj_width(module, select); + uint32_t output_width = sn_obj_width(module, default_value); + assert(select_width); + assert(output_width); + uint64_t packed_width = (uint64_t)output_width * select_width; + assert(packed_width <= UINT32_MAX >> 1); + assert(sn_obj_width(module, packed_alternatives) == packed_width); + (void)packed_width; + sn_obj_id_t fanins[SN_PMUX_FANIN_COUNT] = {select, packed_alternatives, default_value}; + return sn_module_add_operator(module, SN_PMUX, output_width, sn_obj_is_signed(module, default_value), + SN_PMUX_FANIN_COUNT, fanins, name); +} + +static inline uint32_t sn_const_word_count(uint32_t width) +{ + assert(width); + return (width + 31u) / 32u; +} + +static inline uint64_t sn_const_hash_words(uint32_t width, bool is_signed, const uint32_t* words) +{ + uint64_t hash = UINT64_C(1469598103934665603); + hash = (hash ^ width) * UINT64_C(1099511628211); + hash = (hash ^ (is_signed ? 1u : 0u)) * UINT64_C(1099511628211); + for (uint32_t i = 0; i < sn_const_word_count(width); i++) + hash = (hash ^ words[i]) * UINT64_C(1099511628211); + return hash; +} + +static inline uint64_t sn_const_object_hash(const sn_module_t* module, sn_obj_id_t object) +{ + sn_obj_type_t type = sn_obj_type(module, object); + uint32_t width = sn_obj_width(module, object); + uint64_t hash = UINT64_C(1469598103934665603); + hash = (hash ^ width) * UINT64_C(1099511628211); + hash = (hash ^ (sn_obj_is_signed(module, object) ? 1u : 0u)) * UINT64_C(1099511628211); + const uint32_t* words = NULL; + if (type == SN_CONST) + { + uint32_t type_id = sn_obj_type_id(module, object); + uint32_t offset = sn_vec_at(uint32_t, &module->const_word_offsets, type_id); + words = &sn_vec_at(uint32_t, &module->design->constant_words, offset); + } + for (uint32_t i = 0; i < sn_const_word_count(width); i++) + { + uint32_t word = type == SN_CONST0 ? 0 : type == SN_CONST1 ? (i == 0 ? 1u : 0u) : words[i]; + hash = (hash ^ word) * UINT64_C(1099511628211); + } + return hash; +} + +static inline void sn_const_cache_rehash(sn_module_t* module, size_t bucket_count) +{ + assert(module && bucket_count >= 64 && (bucket_count & (bucket_count - 1)) == 0); + sn_vec_t buckets; + sn_vec_init(&buckets); + sn_vec_resize(uint32_t, &buckets, bucket_count); + for (size_t i = 0; i < bucket_count; i++) + sn_vec_at(uint32_t, &buckets, i) = SN_INVALID_ID; + for (size_t i = 0; i < module->const_hash_entries.size; i++) + { + sn_const_hash_entry_t* entry = &sn_vec_at(sn_const_hash_entry_t, &module->const_hash_entries, i); + size_t bucket = (size_t)entry->hash & (bucket_count - 1); + entry->next = sn_vec_at(uint32_t, &buckets, bucket); + sn_vec_at(uint32_t, &buckets, bucket) = (uint32_t)i; + } + sn_vec_destroy(&module->const_hash_buckets); + module->const_hash_buckets = buckets; +} + +static inline void sn_const_cache_insert(sn_module_t* module, sn_obj_id_t object, uint64_t hash) +{ + if ((module->const_hash_entries.size + 1) * 4 >= module->const_hash_buckets.size * 3) + sn_const_cache_rehash(module, module->const_hash_buckets.size * 2); + assert(module->const_hash_entries.size < SN_INVALID_ID); + size_t bucket = (size_t)hash & (module->const_hash_buckets.size - 1); + sn_const_hash_entry_t* entry = sn_vec_push(sn_const_hash_entry_t, &module->const_hash_entries); + entry->hash = hash; + entry->object = object; + entry->next = sn_vec_at(uint32_t, &module->const_hash_buckets, bucket); + sn_vec_at(uint32_t, &module->const_hash_buckets, bucket) = (uint32_t)(module->const_hash_entries.size - 1); +} + +static inline void sn_const_cache_ensure(sn_module_t* module) +{ + if (module->const_hash_buckets.size) + return; + sn_const_cache_rehash(module, 64); + for (sn_obj_id_t object = 0; object < module->obj_types.size; object++) + { + sn_obj_type_t type = sn_obj_type(module, object); + if ((type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST) && + sn_obj_name_id(module, object) == SN_INVALID_ID) + sn_const_cache_insert(module, object, sn_const_object_hash(module, object)); + } +} + +static inline bool sn_const_object_equal(const sn_module_t* module, sn_obj_id_t object, sn_obj_type_t type, + uint32_t width, bool is_signed, const uint32_t* words) +{ + if (sn_obj_type(module, object) != type || sn_obj_width(module, object) != width || + sn_obj_is_signed(module, object) != is_signed) + return false; + if (type != SN_CONST) + return true; + uint32_t type_id = sn_obj_type_id(module, object); + uint32_t offset = sn_vec_at(uint32_t, &module->const_word_offsets, type_id); + const uint32_t* existing = &sn_vec_at(uint32_t, &module->design->constant_words, offset); + return memcmp(existing, words, (size_t)sn_const_word_count(width) * sizeof(uint32_t)) == 0; +} + +static inline sn_obj_id_t sn_module_add_const(sn_module_t* module, uint32_t width, bool is_signed, + const uint32_t* words, const char* name) +{ + assert(module); + assert(width); + assert(words); + uint32_t word_count = sn_const_word_count(width); + uint32_t final_bits = width & 31u; + if (final_bits) + assert((words[word_count - 1] >> final_bits) == 0); + + bool is_zero = true; + bool is_one = words[0] == 1; + for (uint32_t i = 0; i < word_count; i++) + { + is_zero = is_zero && words[i] == 0; + if (i != 0) + is_one = is_one && words[i] == 0; + } + + sn_obj_type_t type = is_zero ? SN_CONST0 : (is_one ? SN_CONST1 : SN_CONST); + uint64_t hash = sn_const_hash_words(width, is_signed, words); + if (!name) + { + sn_const_cache_ensure(module); + size_t bucket = (size_t)hash & (module->const_hash_buckets.size - 1); + uint32_t entry_id = sn_vec_at(uint32_t, &module->const_hash_buckets, bucket); + while (entry_id != SN_INVALID_ID) + { + const sn_const_hash_entry_t* entry = + &sn_vec_at(sn_const_hash_entry_t, &module->const_hash_entries, entry_id); + if (entry->hash == hash && + sn_const_object_equal(module, entry->object, type, width, is_signed, words)) + return entry->object; + entry_id = entry->next; + } + } + sn_obj_id_t object = sn_module_add_named_obj(module, type, width, is_signed, 0, name); + if (type == SN_CONST) + { + assert(module->design->constant_words.size + word_count <= UINT32_MAX); + uint32_t offset = (uint32_t)module->design->constant_words.size; + for (uint32_t i = 0; i < word_count; i++) + *sn_vec_push(uint32_t, &module->design->constant_words) = words[i]; + sn_vec_at(uint32_t, &module->const_word_offsets, sn_obj_type_id(module, object)) = offset; + } + if (!name) + sn_const_cache_insert(module, object, hash); + return object; +} + +static inline const uint32_t* sn_const_words(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(sn_obj_type(module, object) == SN_CONST); + uint32_t word_offset = sn_vec_at(uint32_t, &module->const_word_offsets, sn_obj_type_id(module, object)); + assert(word_offset + sn_const_word_count(sn_obj_width(module, object)) <= module->design->constant_words.size); + return &sn_vec_at(uint32_t, &module->design->constant_words, word_offset); +} + +static inline sn_obj_id_t sn_module_add_concat(sn_module_t* module, uint32_t fanin_count, const sn_obj_id_t* fanins, + const char* name) +{ + // fanins[0] contributes the least-significant result bits. + assert(fanin_count); + assert(fanins); + uint64_t width = 0; + bool all_constant = true; + for (uint32_t i = 0; i < fanin_count; i++) + { + assert(fanins[i] < module->obj_types.size); + width += sn_obj_width(module, fanins[i]); + sn_obj_type_t type = sn_obj_type(module, fanins[i]); + all_constant = all_constant && (type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST); + } + assert(width <= UINT32_MAX >> 1); + if (all_constant) + { + uint32_t result_width = (uint32_t)width; + uint32_t* words = (uint32_t*)calloc(sn_const_word_count(result_width), sizeof(uint32_t)); + assert(words); + uint32_t offset = 0; + for (uint32_t i = 0; i < fanin_count; i++) + { + sn_obj_id_t fanin = fanins[i]; + sn_obj_type_t type = sn_obj_type(module, fanin); + uint32_t fanin_width = sn_obj_width(module, fanin); + const uint32_t* source = type == SN_CONST ? sn_const_words(module, fanin) : NULL; + for (uint32_t bit = 0; bit < fanin_width; bit++) + { + bool value = type == SN_CONST1 ? bit == 0 + : type == SN_CONST && ((source[bit >> 5] >> (bit & 31)) & 1u); + if (value) + words[(offset + bit) >> 5] |= 1u << ((offset + bit) & 31); + } + offset += fanin_width; + } + assert(offset == result_width); + sn_obj_id_t result = sn_module_add_const(module, result_width, false, words, name); + free(words); + return result; + } + if (fanin_count > UINT16_MAX) + { + uint32_t chunk_count = 1 + (fanin_count - 1) / UINT16_MAX; + sn_obj_id_t* chunks = (sn_obj_id_t*)malloc((size_t)chunk_count * sizeof(sn_obj_id_t)); + assert(chunks); + for (uint32_t chunk = 0; chunk < chunk_count; chunk++) + { + uint32_t offset = chunk * UINT16_MAX; + uint32_t count = fanin_count - offset < UINT16_MAX ? fanin_count - offset : UINT16_MAX; + chunks[chunk] = sn_module_add_concat(module, count, fanins + offset, NULL); + } + sn_obj_id_t result = sn_module_add_concat(module, chunk_count, chunks, name); + free(chunks); + return result; + } + return sn_module_add_operator(module, SN_CONCAT, (uint32_t)width, false, fanin_count, fanins, name); +} + +static inline sn_obj_id_t sn_module_add_repeat(sn_module_t* module, sn_obj_id_t value, uint32_t count, const char* name) +{ + assert(module); + assert(value < module->obj_types.size); + assert(count); + uint64_t width = (uint64_t)sn_obj_width(module, value) * count; + assert(width <= UINT32_MAX >> 1); + sn_obj_id_t object = sn_module_add_named_obj(module, SN_REPLICATE, (uint32_t)width, false, 1, name); + sn_obj_connect(module, object, 0, value); + sn_vec_at(uint32_t, &module->repeat_counts, sn_obj_type_id(module, object)) = count; + return object; +} + +static inline uint32_t sn_obj_repeat_count(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(sn_obj_type(module, object) == SN_REPLICATE); + return sn_vec_at(uint32_t, &module->repeat_counts, sn_obj_type_id(module, object)); +} + +static inline sn_obj_id_t sn_module_add_slice(sn_module_t* module, sn_obj_id_t value, int32_t left_index, + int32_t right_index, const char* name) +{ + assert(module); + assert(value < module->obj_types.size); + assert(left_index >= 0 && (uint32_t)left_index < sn_obj_width(module, value)); + assert(right_index >= 0 && (uint32_t)right_index < sn_obj_width(module, value)); + int64_t difference = (int64_t)left_index - (int64_t)right_index; + uint64_t width = (uint64_t)(difference < 0 ? -difference : difference) + 1; + assert(width <= UINT32_MAX >> 1); + sn_obj_id_t object = sn_module_add_named_obj(module, SN_SLICE, (uint32_t)width, false, 1, name); + sn_obj_connect(module, object, 0, value); + sn_slice_info_t* info = &sn_vec_at(sn_slice_info_t, &module->slice_infos, sn_obj_type_id(module, object)); + info->left_index = left_index; + info->right_index = right_index; + info->flags = left_index >= right_index ? SN_SLICE_DESCENDING : 0; + return object; +} + +static inline const sn_slice_info_t* sn_obj_slice_info(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(sn_obj_type(module, object) == SN_SLICE); + return &sn_vec_at(sn_slice_info_t, &module->slice_infos, sn_obj_type_id(module, object)); +} + +static inline sn_obj_id_t sn_obj_pair_in(const sn_module_t* module, sn_obj_id_t out) +{ + assert(module); + sn_obj_type_t out_type = sn_obj_type(module, out); + sn_obj_type_t in_type = SN_NONE; + if (out_type == SN_REG_OUT) + in_type = SN_REG_IN; + else if (out_type == SN_MEM_OUT) + in_type = SN_MEM_IN; + else if (out_type == SN_LOOP_OUT) + in_type = SN_LOOP_IN; + else + assert(false); + + sn_type_id_t pair_id = sn_obj_type_id(module, out); + assert(pair_id < module->type_objects[in_type].size); + sn_obj_id_t in = sn_vec_at(sn_obj_id_t, &module->type_objects[in_type], pair_id); + assert(sn_obj_type_id(module, in) == pair_id); + return in; +} + +static inline sn_obj_id_t sn_obj_pair_out(const sn_module_t* module, sn_obj_id_t in) +{ + assert(module); + sn_obj_type_t in_type = sn_obj_type(module, in); + sn_obj_type_t out_type = SN_NONE; + if (in_type == SN_REG_IN) + out_type = SN_REG_OUT; + else if (in_type == SN_MEM_IN) + out_type = SN_MEM_OUT; + else if (in_type == SN_LOOP_IN) + out_type = SN_LOOP_OUT; + else + assert(false); + + sn_type_id_t pair_id = sn_obj_type_id(module, in); + assert(pair_id < module->type_objects[out_type].size); + sn_obj_id_t out = sn_vec_at(sn_obj_id_t, &module->type_objects[out_type], pair_id); + assert(sn_obj_type_id(module, out) == pair_id); + return out; +} + +static inline sn_obj_pair_t sn_module_add_pair(sn_module_t* module, sn_obj_type_t out_type, sn_obj_type_t in_type, + uint32_t width, bool is_signed, const char* out_name, + const char* in_name) +{ + assert(module); + assert((out_type == SN_REG_OUT && in_type == SN_REG_IN) || (out_type == SN_MEM_OUT && in_type == SN_MEM_IN) || + (out_type == SN_LOOP_OUT && in_type == SN_LOOP_IN)); + assert(module->type_objects[out_type].size == module->type_objects[in_type].size); + + uint32_t out_fanin_count = out_type == SN_REG_OUT ? SN_REG_FANIN_COUNT + : out_type == SN_MEM_OUT ? SN_MEM_OUT_FANIN_COUNT + : 1; + uint32_t in_fanin_count = in_type == SN_MEM_IN ? 0 : 1; + + sn_obj_pair_t pair; + pair.out = sn_module_add_named_obj(module, out_type, width, is_signed, out_fanin_count, out_name); + pair.in = sn_module_add_named_obj(module, in_type, width, is_signed, in_fanin_count, in_name); + assert(sn_obj_type_id(module, pair.out) == sn_obj_type_id(module, pair.in)); + assert(pair.in == pair.out + 1); + assert(sn_obj_pair_in(module, pair.out) == pair.in); + assert(sn_obj_pair_out(module, pair.in) == pair.out); + uint32_t pair_slot = out_type == SN_REG_OUT ? SN_REG_DATA + : out_type == SN_MEM_OUT ? SN_MEM_STATE + : 0; + sn_obj_connect(module, pair.out, pair_slot, pair.in); + return pair; +} + +static inline sn_obj_pair_t sn_module_add_reg_pair(sn_module_t* module, uint32_t width, bool is_signed, + const char* out_name, const char* in_name, sn_obj_id_t clock) +{ + assert(clock == SN_INVALID_ID || clock < module->obj_types.size); + sn_obj_pair_t pair = sn_module_add_pair(module, SN_REG_OUT, SN_REG_IN, width, is_signed, out_name, in_name); + sn_obj_connect(module, pair.out, SN_REG_CLOCK, clock); + return pair; +} + +static inline uint32_t sn_obj_reg_flags(const sn_module_t* module, sn_obj_id_t reg_out) +{ + assert(module); + assert(sn_obj_type(module, reg_out) == SN_REG_OUT); + return sn_vec_at(uint32_t, &module->reg_flags, sn_obj_type_id(module, reg_out)); +} + +static inline void sn_reg_set_flags(sn_module_t* module, sn_obj_id_t reg_out, uint32_t flags) +{ + assert(module); + assert(sn_obj_type(module, reg_out) == SN_REG_OUT); + assert((flags & ~SN_REG_FLAGS_ALL) == 0); + sn_vec_at(uint32_t, &module->reg_flags, sn_obj_type_id(module, reg_out)) = flags; +} + +static inline void sn_reg_set_fanin(sn_module_t* module, sn_obj_id_t reg_out, sn_reg_fanin_t slot, sn_obj_id_t fanin) +{ + assert(module); + assert(sn_obj_type(module, reg_out) == SN_REG_OUT); + assert(slot < SN_REG_FANIN_COUNT); + assert(slot != SN_REG_DATA); + if ((slot == SN_REG_INIT_DATA || slot == SN_REG_INIT_MASK) && fanin != SN_INVALID_ID) + { + sn_obj_type_t type = sn_obj_type(module, fanin); + assert(type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST); + assert(sn_obj_width(module, fanin) == sn_obj_width(module, reg_out)); + (void)type; + } + sn_obj_connect(module, reg_out, (uint32_t)slot, fanin); +} + +// Register initialization uses the same aligned representation as memory +// initialization: mask bit i describes data bit i, and a set mask bit means +// that the corresponding data bit is initialized. No data means no init; data +// with no mask means all bits valid. Builders normally provide both constants. +static inline void sn_reg_set_init(sn_module_t* module, sn_obj_id_t reg_out, sn_obj_id_t data, sn_obj_id_t mask) +{ + assert(data != SN_INVALID_ID || mask == SN_INVALID_ID); + sn_reg_set_fanin(module, reg_out, SN_REG_INIT_DATA, data); + sn_reg_set_fanin(module, reg_out, SN_REG_INIT_MASK, mask); +} + +static inline sn_obj_id_t sn_obj_reg_init_data(const sn_module_t* module, sn_obj_id_t reg_out) +{ + assert(module && sn_obj_type(module, reg_out) == SN_REG_OUT); + return sn_obj_fanin(module, reg_out, SN_REG_INIT_DATA); +} + +static inline sn_obj_id_t sn_obj_reg_init_mask(const sn_module_t* module, sn_obj_id_t reg_out) +{ + assert(module && sn_obj_type(module, reg_out) == SN_REG_OUT); + return sn_obj_fanin(module, reg_out, SN_REG_INIT_MASK); +} + +static inline sn_obj_pair_t sn_module_add_mem_pair(sn_module_t* module, uint32_t width, bool is_signed, uint32_t depth, + const char* out_name, const char* in_name) +{ + assert(depth); + sn_obj_pair_t pair = sn_module_add_pair(module, SN_MEM_OUT, SN_MEM_IN, width, is_signed, out_name, in_name); + sn_vec_at(uint32_t, &module->mem_depths, sn_obj_type_id(module, pair.out)) = depth; + return pair; +} + +static inline uint32_t sn_obj_mem_depth(const sn_module_t* module, sn_obj_id_t mem_out) +{ + assert(module); + assert(sn_obj_type(module, mem_out) == SN_MEM_OUT); + uint32_t depth = sn_vec_at(uint32_t, &module->mem_depths, sn_obj_type_id(module, mem_out)); + assert(depth); + return depth; +} + +static inline uint32_t sn_obj_mem_init_width(const sn_module_t* module, sn_obj_id_t mem_out) +{ + assert(module); + assert(sn_obj_type(module, mem_out) == SN_MEM_OUT); + uint64_t width = (uint64_t)sn_obj_width(module, mem_out) * sn_obj_mem_depth(module, mem_out); + assert(width <= UINT32_MAX); + return (uint32_t)width; +} + +static inline void sn_mem_set_init(sn_module_t* module, sn_obj_id_t mem_out, sn_obj_id_t data, sn_obj_id_t mask) +{ + assert(module); + assert(sn_obj_type(module, mem_out) == SN_MEM_OUT); + assert(data != SN_INVALID_ID || mask == SN_INVALID_ID); + uint32_t init_width = sn_obj_mem_init_width(module, mem_out); + if (data != SN_INVALID_ID) + { + sn_obj_type_t type = sn_obj_type(module, data); + assert(type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST); + assert(sn_obj_width(module, data) == init_width); + } + if (mask != SN_INVALID_ID) + { + sn_obj_type_t type = sn_obj_type(module, mask); + assert(type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST); + assert(sn_obj_width(module, mask) == init_width); + } + sn_obj_connect(module, mem_out, SN_MEM_INIT_DATA, data); + sn_obj_connect(module, mem_out, SN_MEM_INIT_MASK, mask); +} + +static inline sn_obj_id_t sn_obj_mem_init_data(const sn_module_t* module, sn_obj_id_t mem_out) +{ + assert(module); + assert(sn_obj_type(module, mem_out) == SN_MEM_OUT); + return sn_obj_fanin(module, mem_out, SN_MEM_INIT_DATA); +} + +static inline sn_obj_id_t sn_obj_mem_init_mask(const sn_module_t* module, sn_obj_id_t mem_out) +{ + assert(module); + assert(sn_obj_type(module, mem_out) == SN_MEM_OUT); + return sn_obj_fanin(module, mem_out, SN_MEM_INIT_MASK); +} + +static inline sn_obj_id_t sn_module_add_mem_read(sn_module_t* module, sn_obj_id_t mem_out, sn_obj_id_t clock, + sn_obj_id_t enable, sn_obj_id_t address, const char* name) +{ + assert(module); + assert(sn_obj_type(module, mem_out) == SN_MEM_OUT); + assert(clock == SN_INVALID_ID || clock < module->obj_types.size); + assert(enable == SN_INVALID_ID || enable < module->obj_types.size); + assert(clock != SN_INVALID_ID || enable == SN_INVALID_ID); + assert(address < module->obj_types.size); + sn_obj_id_t read = sn_module_add_named_obj(module, SN_MEM_READ, sn_obj_width(module, mem_out), + sn_obj_is_signed(module, mem_out), SN_MEM_READ_FANIN_COUNT, name); + sn_obj_connect(module, read, SN_MEM_READ_MEMORY, mem_out); + sn_obj_connect(module, read, SN_MEM_READ_CLOCK, clock); + sn_obj_connect(module, read, SN_MEM_READ_ENABLE, enable); + sn_obj_connect(module, read, SN_MEM_READ_ADDRESS, address); + return read; +} + +static inline sn_obj_id_t sn_module_add_mem_write_unlinked(sn_module_t* module, sn_obj_id_t mem_in, + sn_obj_id_t clock, sn_obj_id_t enable, + sn_obj_id_t data, sn_obj_id_t address, + const char* name) +{ + assert(module); + assert(sn_obj_type(module, mem_in) == SN_MEM_IN); + assert(clock < module->obj_types.size); + assert(enable == SN_INVALID_ID || enable < module->obj_types.size); + assert(data < module->obj_types.size); + assert(address < module->obj_types.size); + assert(sn_obj_width(module, data) == sn_obj_width(module, mem_in)); + sn_obj_id_t write = sn_module_add_named_obj(module, SN_MEM_WRITE, sn_obj_width(module, mem_in), + sn_obj_is_signed(module, mem_in), SN_MEM_WRITE_FANIN_COUNT, name); + sn_obj_connect(module, write, SN_MEM_WRITE_CLOCK, clock); + sn_obj_connect(module, write, SN_MEM_WRITE_ENABLE, enable); + sn_obj_connect(module, write, SN_MEM_WRITE_DATA, data); + sn_obj_connect(module, write, SN_MEM_WRITE_ADDRESS, address); + return write; +} + +static inline sn_obj_id_t sn_module_add_mem_write(sn_module_t* module, sn_obj_id_t mem_in, sn_obj_id_t clock, + sn_obj_id_t enable, sn_obj_id_t data, sn_obj_id_t address, + const char* name) +{ + sn_obj_id_t write = sn_module_add_mem_write_unlinked(module, mem_in, clock, enable, data, address, name); + sn_obj_add_fanin(module, mem_in, write); + return write; +} + +static inline sn_obj_pair_t sn_module_add_loop_pair(sn_module_t* module, uint32_t width, bool is_signed, + const char* out_name, const char* in_name) +{ + return sn_module_add_pair(module, SN_LOOP_OUT, SN_LOOP_IN, width, is_signed, out_name, in_name); +} + +static inline sn_obj_id_t sn_module_add_inst(sn_module_t* module, sn_module_id_t referenced_module, + uint32_t input_count, const sn_obj_id_t* inputs, const char* name, + const char* const* output_names) +{ + assert(module); + assert(referenced_module < module->design->modules.size); + assert(input_count == 0 || inputs); + sn_module_t* child = sn_design_get_module(module->design, referenced_module); + assert(child->type_objects[SN_PI].size == input_count); + uint32_t output_count = sn_design_module_output_count(module->design, referenced_module); + assert(output_count != 0); + child->interface_locked = true; + + sn_obj_id_t first_output = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PO], 0); + uint32_t inst_width = output_count == 1 ? sn_obj_width(child, first_output) : 0; + bool inst_signedness = output_count == 1 ? sn_obj_is_signed(child, first_output) : false; + sn_obj_id_t inst = + sn_module_add_named_obj(module, SN_INST, inst_width, inst_signedness, input_count, name); + for (uint32_t i = 0; i < input_count; i++) + sn_obj_connect(module, inst, i, inputs[i]); + + sn_vec_at(sn_module_id_t, &module->inst_modules, sn_obj_type_id(module, inst)) = referenced_module; + + // A one-output inst is itself the output value. Multi-output insts + // are followed immediately by one SN_FAN per output. + if (output_count > 1) + { + for (uint32_t i = 0; i < output_count; i++) + { + sn_obj_id_t child_output = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PO], i); + const char* output_name = output_names ? output_names[i] : NULL; + sn_obj_id_t fan = sn_module_add_named_obj(module, SN_FAN, sn_obj_width(child, child_output), + sn_obj_is_signed(child, child_output), 1, output_name); + assert(fan == inst + 1 + i); + sn_obj_connect(module, fan, 0, inst); + sn_vec_at(sn_obj_id_t, &module->fan_insts, sn_obj_type_id(module, fan)) = inst; + } + } + return inst; +} + +static inline sn_module_id_t sn_inst_module_id(const sn_module_t* module, sn_obj_id_t inst) +{ + assert(module); + assert(sn_obj_type(module, inst) == SN_INST); + sn_module_id_t module_id = sn_vec_at(sn_module_id_t, &module->inst_modules, sn_obj_type_id(module, inst)); + assert(module_id < module->design->modules.size); + return module_id; +} + +static inline sn_obj_id_t sn_fan_inst_id(const sn_module_t* module, sn_obj_id_t fan) +{ + assert(module); + assert(sn_obj_type(module, fan) == SN_FAN); + sn_obj_id_t inst = sn_vec_at(sn_obj_id_t, &module->fan_insts, sn_obj_type_id(module, fan)); + assert(inst < fan); + assert(sn_obj_type(module, inst) == SN_INST); + assert(sn_obj_fanin_count(module, fan) == 1); + assert(sn_obj_fanin(module, fan, 0) == inst); + return inst; +} + +static inline uint32_t sn_fan_output_index(const sn_module_t* module, sn_obj_id_t fan) +{ + sn_obj_id_t inst = sn_fan_inst_id(module, fan); + uint32_t output_index = fan - inst - 1; + sn_module_id_t child_id = sn_inst_module_id(module, inst); + assert(output_index < sn_design_module_output_count(module->design, child_id)); + (void)child_id; + return output_index; +} + +static inline sn_obj_id_t sn_inst_output(const sn_module_t* module, sn_obj_id_t inst, uint32_t output_index) +{ + assert(module); + assert(sn_obj_type(module, inst) == SN_INST); + sn_module_id_t child_id = sn_inst_module_id(module, inst); + uint32_t output_count = sn_design_module_output_count(module->design, child_id); + assert(output_index < output_count); + if (output_count == 1) + return inst; + + sn_obj_id_t fan = inst + 1 + output_index; + assert(fan < module->obj_types.size); + assert(sn_obj_type(module, fan) == SN_FAN); + assert(sn_obj_fanin(module, fan, 0) == inst); + assert(sn_fan_inst_id(module, fan) == inst); + assert(sn_fan_output_index(module, fan) == output_index); + return fan; +} + +// Print the elaborated module-inst hierarchy rooted at module_id. Children +// follow the natural SN_INST order of their parent module. A module is +// printed once per inst, so repeated insts remain visible. Recursive +// instantiation is invalid for synthesis, but is marked and stopped rather +// than causing unbounded recursion in this diagnostic routine. +static inline void sn_design_print_hierarchy_rec(FILE* out, const sn_design_t* design, sn_module_id_t module_id, + size_t depth, bool is_last, bool* ancestor_has_next, + bool* active_modules) +{ + assert(out); + assert(design); + assert(module_id < design->modules.size); + assert(ancestor_has_next); + assert(active_modules); + + if (depth) + { + for (size_t level = 0; level + 1 < depth; level++) + fputs(ancestor_has_next[level] ? "│ " : " ", out); + fputs(is_last ? "└─ " : "├─ ", out); + } + + const sn_module_t* module = sn_design_get_module_const(design, module_id); + fputs(sn_name_get(&design->names, module->name), out); + if (active_modules[module_id]) + { + fputs(" [recursive]\n", out); + return; + } + if (sn_module_is_blackbox(module)) + { + fputs(" [blackbox]\n", out); + return; + } + fputc('\n', out); + + active_modules[module_id] = true; + size_t inst_count = module->type_objects[SN_INST].size; + for (size_t i = 0; i < inst_count; i++) + { + sn_obj_id_t inst = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_INST], i); + ancestor_has_next[depth] = i + 1 < inst_count; + sn_design_print_hierarchy_rec(out, design, sn_inst_module_id(module, inst), depth + 1, + i + 1 == inst_count, ancestor_has_next, active_modules); + } + active_modules[module_id] = false; +} + +static inline void sn_design_print_hierarchy(FILE* out, const sn_design_t* design, sn_module_id_t top_module_id) +{ + assert(out); + assert(design); + assert(top_module_id < design->modules.size); + + bool* ancestor_has_next = (bool*)calloc(design->modules.size, sizeof(bool)); + bool* active_modules = (bool*)calloc(design->modules.size, sizeof(bool)); + assert(ancestor_has_next); + assert(active_modules); + sn_design_print_hierarchy_rec(out, design, top_module_id, 0, true, ancestor_has_next, active_modules); + free(active_modules); + free(ancestor_has_next); +} + +static inline void sn_module_build_fanouts(sn_module_t* module) +{ + assert(module); + size_t object_count = module->obj_types.size; + sn_vec_resize(uint32_t, &module->fanout_counts, object_count); + sn_vec_resize(uint32_t, &module->fanout_offsets, object_count); + memset(module->fanout_counts.data, 0, object_count * sizeof(uint32_t)); + + for (sn_obj_id_t object = 0; object < object_count; object++) + { + uint32_t count = sn_obj_fanin_count(module, object); + for (uint32_t i = 0; i < count; i++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, object, i); + if (fanin == SN_INVALID_ID) + continue; + assert(fanin < object_count); + uint32_t* fanout_count = &sn_vec_at(uint32_t, &module->fanout_counts, fanin); + assert(*fanout_count < UINT32_MAX); + (*fanout_count)++; + } + } + + uint32_t total = 0; + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_vec_at(uint32_t, &module->fanout_offsets, object) = total; + uint32_t count = sn_vec_at(uint32_t, &module->fanout_counts, object); + assert(total <= UINT32_MAX - count); + total += count; + } + sn_vec_resize(sn_obj_id_t, &module->fanouts, total); + + uint32_t* cursor = (uint32_t*)calloc(object_count ? object_count : 1, sizeof(uint32_t)); + assert(cursor); + for (sn_obj_id_t object = 0; object < object_count; object++) + { + uint32_t count = sn_obj_fanin_count(module, object); + for (uint32_t i = 0; i < count; i++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, object, i); + if (fanin == SN_INVALID_ID) + continue; + uint32_t offset = sn_vec_at(uint32_t, &module->fanout_offsets, fanin); + assert(cursor[fanin] < sn_vec_at(uint32_t, &module->fanout_counts, fanin)); + sn_vec_at(sn_obj_id_t, &module->fanouts, offset + cursor[fanin]++) = object; + } + } + free(cursor); + module->fanouts_valid = true; +} + +static inline uint32_t sn_obj_fanout_count(const sn_module_t* module, sn_obj_id_t object) +{ + assert(module); + assert(module->fanouts_valid); + assert(object < module->fanout_counts.size); + return sn_vec_at(uint32_t, &module->fanout_counts, object); +} + +static inline sn_obj_id_t sn_obj_fanout(const sn_module_t* module, sn_obj_id_t object, uint32_t output_index) +{ + assert(module); + assert(module->fanouts_valid); + assert(output_index < sn_obj_fanout_count(module, object)); + uint32_t offset = sn_vec_at(uint32_t, &module->fanout_offsets, object); + return sn_vec_at(sn_obj_id_t, &module->fanouts, offset + output_index); +} + +// Sequential topological traversal treats state OUT objects as sources and +// emits all of them immediately after the PIs. Their paired IN objects remain +// ordinary sinks: each IN follows the OUT, the OUT's non-pair control fanins, +// and its own functional fanins. Pre-emitting every OUT is important when an +// OUT is first discovered from inside its own next-state cone. +enum +{ + SN_TOPO_UNSEEN = 0, + SN_TOPO_VISITING, + SN_TOPO_DONE +}; + +typedef struct sn_topo_context_t +{ + const sn_module_t* module; + sn_vec_t* order; + uint8_t* marks; +} sn_topo_context_t; + +typedef struct sn_topo_frame_t +{ + sn_obj_id_t object; + uint32_t index; + uint8_t phase; +} sn_topo_frame_t; + +static inline bool sn_obj_type_is_pair_out(sn_obj_type_t type) +{ + return type == SN_REG_OUT || type == SN_MEM_OUT || type == SN_LOOP_OUT; +} + +static inline bool sn_obj_type_is_pair_in(sn_obj_type_t type) +{ + return type == SN_REG_IN || type == SN_MEM_IN || type == SN_LOOP_IN; +} + +static inline void sn_module_topo_push(sn_topo_context_t* context, sn_vec_t* stack, sn_obj_id_t object) +{ + const sn_module_t* module = context->module; + assert(object < module->obj_types.size); + if (context->marks[object] == SN_TOPO_DONE) + return; + assert(context->marks[object] == SN_TOPO_UNSEEN); + sn_obj_type_t type = sn_obj_type(module, object); + assert(type != SN_PI && type != SN_PO); + context->marks[object] = SN_TOPO_VISITING; + sn_topo_frame_t* frame = sn_vec_push(sn_topo_frame_t, stack); + frame->object = object; + frame->index = 0; + frame->phase = 0; +} + +static inline void sn_module_topo_visit(sn_topo_context_t* context, sn_obj_id_t object) +{ + assert(context); + const sn_module_t* module = context->module; + sn_vec_t stack; + sn_vec_init(&stack); + sn_module_topo_push(context, &stack, object); + while (stack.size) + { + sn_topo_frame_t* frame = &sn_vec_at(sn_topo_frame_t, &stack, stack.size - 1); + object = frame->object; + if (context->marks[object] == SN_TOPO_DONE) + { + stack.size--; + continue; + } + sn_obj_type_t type = sn_obj_type(module, object); + + if (sn_obj_type_is_pair_out(type)) + { + *sn_vec_push(sn_obj_id_t, context->order) = object; + context->marks[object] = SN_TOPO_DONE; + stack.size--; + continue; + } + + if (frame->phase == 0) + { + frame->phase = 1; + frame->index = 0; + if (sn_obj_type_is_pair_in(type)) + { + sn_obj_id_t pair_out = sn_obj_pair_out(module, object); + assert(context->marks[pair_out] != SN_TOPO_VISITING); + if (context->marks[pair_out] == SN_TOPO_UNSEEN) + sn_module_topo_push(context, &stack, pair_out); + continue; + } + } + + if (frame->phase == 1) + { + if (sn_obj_type_is_pair_in(type)) + { + sn_obj_id_t pair_out = sn_obj_pair_out(module, object); + bool pushed = false; + while (frame->index < sn_obj_fanin_count(module, pair_out)) + { + sn_obj_id_t fanin = sn_obj_fanin(module, pair_out, frame->index++); + if (fanin == SN_INVALID_ID || fanin == object) + continue; + assert(context->marks[fanin] != SN_TOPO_VISITING); + if (context->marks[fanin] == SN_TOPO_UNSEEN) + { + sn_module_topo_push(context, &stack, fanin); + pushed = true; + break; + } + } + if (pushed) + continue; + } + frame = &sn_vec_at(sn_topo_frame_t, &stack, stack.size - 1); + frame->phase = 2; + frame->index = 0; + } + + bool pushed = false; + while (frame->index < sn_obj_fanin_count(module, object)) + { + sn_obj_id_t fanin = sn_obj_fanin(module, object, frame->index++); + if (fanin == SN_INVALID_ID) + continue; + assert(context->marks[fanin] != SN_TOPO_VISITING); + if (context->marks[fanin] == SN_TOPO_UNSEEN) + { + sn_module_topo_push(context, &stack, fanin); + pushed = true; + break; + } + } + if (pushed) + continue; + + if (type == SN_INST) + { + *sn_vec_push(sn_obj_id_t, context->order) = object; + context->marks[object] = SN_TOPO_DONE; + uint32_t output_count = sn_design_module_output_count(module->design, sn_inst_module_id(module, object)); + if (output_count > 1) + for (uint32_t i = 0; i < output_count; i++) + { + sn_obj_id_t fan = sn_inst_output(module, object, i); + assert(context->marks[fan] != SN_TOPO_DONE); + *sn_vec_push(sn_obj_id_t, context->order) = fan; + context->marks[fan] = SN_TOPO_DONE; + } + } + else if (context->marks[object] != SN_TOPO_DONE) + { + *sn_vec_push(sn_obj_id_t, context->order) = object; + context->marks[object] = SN_TOPO_DONE; + } + stack.size--; + } + + sn_vec_destroy(&stack); +} + +// Returns all module object IDs exactly once. The caller owns the returned +// vector and must call sn_vec_destroy(). PIs occupy the prefix in natural port +// order, POs occupy the suffix in natural port order, and every state OUT +// precedes its paired IN. A normalized combinational cycle triggers assert(). +static inline sn_vec_t sn_module_topo_order(const sn_module_t* module) +{ + assert(module); + sn_vec_t order; + sn_vec_init(&order); + size_t object_count = module->obj_types.size; + sn_vec_reserve(sn_obj_id_t, &order, object_count); + + uint8_t* marks = NULL; + if (object_count) + { + marks = (uint8_t*)calloc(object_count, sizeof(uint8_t)); + assert(marks); + } + + sn_topo_context_t context; + context.module = module; + context.order = ℴ + context.marks = marks; + + // Place every PI first, including unused inputs. + for (size_t i = 0; i < module->type_objects[SN_PI].size; i++) + { + sn_obj_id_t input = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], i); + assert(marks[input] == SN_TOPO_UNSEEN); + marks[input] = SN_TOPO_DONE; + *sn_vec_push(sn_obj_id_t, &order) = input; + } + + // State values are combinational sources. Emit every OUT before exploring + // any next-state cone so feedback through the corresponding IN cannot + // encounter an in-progress combinational node. + for (sn_obj_id_t object = 0; object < object_count; object++) + { + if (!sn_obj_type_is_pair_out(sn_obj_type(module, object))) + continue; + assert(marks[object] == SN_TOPO_UNSEEN); + marks[object] = SN_TOPO_DONE; + *sn_vec_push(sn_obj_id_t, &order) = object; + } + + // Traverse PO cones first. POs themselves are deliberately delayed. + for (size_t i = 0; i < module->type_objects[SN_PO].size; i++) + { + sn_obj_id_t output = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], i); + for (uint32_t j = 0; j < sn_obj_fanin_count(module, output); j++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, output, j); + if (fanin != SN_INVALID_ID) + sn_module_topo_visit(&context, fanin); + } + } + + // Include disconnected and otherwise unreachable internal objects. + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_obj_type_t type = sn_obj_type(module, object); + if (type != SN_PI && type != SN_PO && marks[object] == SN_TOPO_UNSEEN) + sn_module_topo_visit(&context, object); + } + + // Place every PO last in natural port order. + for (size_t i = 0; i < module->type_objects[SN_PO].size; i++) + { + sn_obj_id_t output = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], i); + assert(marks[output] == SN_TOPO_UNSEEN); + marks[output] = SN_TOPO_DONE; + *sn_vec_push(sn_obj_id_t, &order) = output; + } + + assert(order.size == object_count); + free(marks); + return order; +} + +// Accepts any legal SN topological order, not only the particular depth-first +// order returned by sn_module_topo_order(). PIs and POs must form their natural- +// order prefix and suffix. Ordinary fanins precede their users. State OUT may +// precede its structural paired-IN fanin, and its controls may occur after OUT, +// but every such dependency must precede the paired IN. Instance FANs retain +// their immediate natural-order block. +static inline bool sn_module_is_topo(const sn_module_t* module) +{ + assert(module); + size_t object_count = module->obj_types.size; + size_t input_count = module->type_objects[SN_PI].size; + size_t output_count = module->type_objects[SN_PO].size; + if (input_count + output_count > object_count) + return false; + + for (size_t i = 0; i < input_count; i++) + if (sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], i) != i) + return false; + size_t output_begin = object_count - output_count; + for (size_t i = 0; i < output_count; i++) + if (sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], i) != output_begin + i) + return false; + + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_obj_type_t type = sn_obj_type(module, object); + if (object < input_count && type != SN_PI) + return false; + if (object >= output_begin && type != SN_PO) + return false; + + sn_obj_id_t pair_in = SN_INVALID_ID; + if (sn_obj_type_is_pair_out(type)) + { + pair_in = sn_obj_pair_in(module, object); + if (pair_in <= object) + return false; + } + for (uint32_t i = 0; i < sn_obj_fanin_count(module, object); i++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, object, i); + if (fanin == SN_INVALID_ID || fanin == pair_in) + continue; + if (pair_in != SN_INVALID_ID) + { + if (fanin >= pair_in) + return false; + } + else if (fanin >= object) + return false; + } + + if (type == SN_INST) + { + sn_module_id_t child_id = sn_inst_module_id(module, object); + uint32_t child_outputs = sn_design_module_output_count(module->design, child_id); + if (child_outputs > 1) + { + for (uint32_t i = 0; i < child_outputs; i++) + if (sn_inst_output(module, object, i) != object + 1 + i) + return false; + } + } + else if (type == SN_FAN) + { + sn_obj_id_t inst = sn_fan_inst_id(module, object); + if (object != inst + 1 + sn_fan_output_index(module, object)) + return false; + } + } + return true; +} + +static inline bool sn_design_is_topo(const sn_design_t* design) +{ + assert(design); + for (size_t i = 0; i < design->modules.size; i++) + if (!sn_module_is_topo(sn_design_get_module_const(design, (sn_module_id_t)i))) + return false; + return true; +} + +// Returns the target module and object mapping from the source module's most +// recent duplication. A new duplication replaces the previous mapping. The +// mapping remains owned by the source module until another duplication or +// until that module is destroyed. +static inline sn_module_id_t sn_module_dup_target(const sn_module_t* source) +{ + assert(source); + assert(source->copy_module < source->design->modules.size); + return source->copy_module; +} + +static inline sn_obj_id_t sn_obj_dup(const sn_module_t* source, sn_obj_id_t old_object) +{ + assert(source); + assert(source->copy_module < source->design->modules.size); + assert(source->copy_ids.size == source->obj_types.size); + assert(old_object < source->copy_ids.size); + sn_obj_id_t new_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + const sn_module_t* target = sn_design_get_module_const(source->design, source->copy_module); + assert(new_object < target->obj_types.size); + (void)target; + return new_object; +} + +static inline void sn_design_invalidate_copies_to_module_except(sn_design_t* design, sn_module_id_t module_id, + const sn_module_t* preserve) +{ + for (size_t i = 0; i < design->modules.size; i++) + { + sn_module_t* module = sn_vec_at(sn_module_t*, &design->modules, i); + if (!module || module == preserve || module->copy_module != module_id) + continue; + sn_vec_destroy(&module->copy_ids); + sn_vec_init(&module->copy_ids); + module->copy_module = SN_INVALID_ID; + } +} + +static inline void sn_design_invalidate_copies_to_module(sn_design_t* design, sn_module_id_t module_id) +{ + sn_design_invalidate_copies_to_module_except(design, module_id, NULL); +} + +// Duplicates a module in topological order within the same design. Object +// names and design-wide constant words are shared by ID; all module-local +// object references and type-specific object IDs are rebuilt. The old-to-new +// object map is retained in the source module as its most recent copy map. +static inline sn_obj_id_t sn_module_dup_obj_skeleton(sn_module_t* target, const sn_module_t* source, + sn_obj_id_t old_object) +{ + assert(target); + assert(source); + return sn_module_add_obj(target, sn_obj_type(source, old_object), sn_obj_width(source, old_object), + sn_obj_is_signed(source, old_object), sn_obj_fanin_count(source, old_object), + sn_obj_name_id(source, old_object)); +} + +// Copies metadata whose value does not contain a module-local object ID. +// SN_FAN ownership is remapped separately, while collapsing omits insts +// and fans entirely. +static inline void sn_module_dup_obj_metadata(sn_module_t* target, sn_type_id_t target_type_id, + const sn_module_t* source, sn_obj_id_t old_object) +{ + assert(target); + assert(source); + sn_obj_type_t type = sn_obj_type(source, old_object); + sn_type_id_t source_type_id = sn_obj_type_id(source, old_object); + if (type == SN_REG_OUT) + sn_vec_at(uint32_t, &target->reg_flags, target_type_id) = + sn_vec_at(uint32_t, &source->reg_flags, source_type_id); + else if (type == SN_MEM_OUT) + sn_vec_at(uint32_t, &target->mem_depths, target_type_id) = + sn_vec_at(uint32_t, &source->mem_depths, source_type_id); + else if (type == SN_INST) + { + sn_module_id_t child_id = sn_vec_at(sn_module_id_t, &source->inst_modules, source_type_id); + assert(child_id < target->design->modules.size); + sn_vec_at(sn_module_id_t, &target->inst_modules, target_type_id) = child_id; + sn_design_get_module(target->design, child_id)->interface_locked = true; + } + else if (type == SN_SLICE) + sn_vec_at(sn_slice_info_t, &target->slice_infos, target_type_id) = + sn_vec_at(sn_slice_info_t, &source->slice_infos, source_type_id); + else if (type == SN_REPLICATE) + sn_vec_at(uint32_t, &target->repeat_counts, target_type_id) = + sn_vec_at(uint32_t, &source->repeat_counts, source_type_id); + else if (type == SN_CONST) + sn_vec_at(uint32_t, &target->const_word_offsets, target_type_id) = + sn_vec_at(uint32_t, &source->const_word_offsets, source_type_id); + else if (type == SN_LUT) + sn_vec_at(uint64_t, &target->lut_truths, target_type_id) = + sn_vec_at(uint64_t, &source->lut_truths, source_type_id); + else if (type == SN_GATE) + sn_vec_at(uint32_t, &target->gate_ids, target_type_id) = + sn_vec_at(uint32_t, &source->gate_ids, source_type_id); +} + +static inline sn_module_id_t sn_design_dup_module_topo(sn_design_t* design, sn_module_id_t source_module_id, + const char* new_name) +{ + assert(design); + assert(source_module_id < design->modules.size); + assert(new_name); + + sn_module_t* source = sn_design_get_module(design, source_module_id); + sn_vec_t order = sn_module_topo_order(source); + sn_module_id_t target_module_id = sn_design_add_module(design, new_name); + sn_module_t* target = sn_design_get_module(design, target_module_id); + target->flags = source->flags; + + sn_vec_resize(sn_obj_id_t, &source->copy_ids, source->obj_types.size); + for (size_t i = 0; i < source->copy_ids.size; i++) + sn_vec_at(sn_obj_id_t, &source->copy_ids, i) = SN_INVALID_ID; + + // Create all objects first so every old fanin has a known new object ID. + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_obj_id_t new_object = sn_module_dup_obj_skeleton(target, source, old_object); + assert(new_object == i); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = new_object; + } + + // Type IDs are semantically significant (notably for OUT/IN pairing), so + // preserve them and rebuild each inverse type_objects mapping. + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_obj_id_t new_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + sn_obj_type_t type = sn_obj_type(source, old_object); + sn_type_id_t type_id = sn_obj_type_id(source, old_object); + assert(type_id < target->type_objects[type].size); + sn_vec_at(uint32_t, &target->type_ids, new_object) = type_id; + sn_vec_at(sn_obj_id_t, &target->type_objects[type], type_id) = new_object; + } + + // Copy the compact type-specific arrays. Entries in these arrays use the + // preserved source type IDs, except fan owner IDs which are module-local. + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_obj_id_t new_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + sn_obj_type_t type = sn_obj_type(source, old_object); + sn_type_id_t type_id = sn_obj_type_id(source, old_object); + (void)new_object; + + sn_module_dup_obj_metadata(target, type_id, source, old_object); + if (type == SN_FAN) + { + sn_obj_id_t old_inst = sn_vec_at(sn_obj_id_t, &source->fan_insts, type_id); + assert(old_inst < source->copy_ids.size); + sn_obj_id_t new_inst = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_inst); + assert(new_inst != SN_INVALID_ID); + sn_vec_at(sn_obj_id_t, &target->fan_insts, type_id) = new_inst; + } + } + + // Reconnect all module-local fanins through the persistent copy map. + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_obj_id_t new_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + for (uint32_t j = 0; j < sn_obj_fanin_count(source, old_object); j++) + { + sn_obj_id_t old_fanin = sn_obj_fanin(source, old_object, j); + sn_obj_id_t new_fanin = + old_fanin == SN_INVALID_ID ? SN_INVALID_ID : sn_vec_at(sn_obj_id_t, &source->copy_ids, old_fanin); + assert(new_fanin == SN_INVALID_ID || new_fanin < target->obj_types.size); + sn_obj_connect(target, new_object, j, new_fanin); + } + } + + // Validate the invariants that are sensitive to physical reordering. + for (sn_obj_id_t object = 0; object < target->obj_types.size; object++) + { + sn_obj_type_t type = sn_obj_type(target, object); + if (sn_obj_type_is_pair_out(type)) + { + sn_obj_id_t pair_in = sn_obj_pair_in(target, object); + assert(object < pair_in); + assert(sn_obj_pair_out(target, pair_in) == object); + (void)pair_in; + } + else if (type == SN_FAN) + { + sn_obj_id_t inst = sn_fan_inst_id(target, object); + uint32_t output_index = sn_fan_output_index(target, object); + assert(object == inst + 1 + output_index); + (void)inst; + (void)output_index; + } + } + + source->copy_module = target_module_id; + sn_vec_destroy(&order); + return target_module_id; +} + +// Reorders one module in place using the same dependency-aware duplication as +// sn_design_dup_module_topo(). Its module ID and name are preserved, so existing +// insts remain valid. Pointers and object IDs into the old module become +// invalid; callers must reacquire the module and rebuild any object mappings. +static inline void sn_design_reorder_module_topo(sn_design_t* design, sn_module_id_t module_id) +{ + assert(design); + assert(module_id < design->modules.size); + if (sn_module_is_topo(sn_design_get_module_const(design, module_id))) + return; + + char temporary_name[96]; + uint32_t suffix = 0; + do + { + int length = snprintf(temporary_name, sizeof(temporary_name), "__sn_topo_%u_%u", module_id, suffix++); + assert(length >= 0 && (size_t)length < sizeof(temporary_name)); + assert(suffix != 0); + } while (sn_name_find(&design->names, temporary_name) != SN_INVALID_ID); + + size_t old_module_count = design->modules.size; + sn_module_t* source = sn_design_get_module(design, module_id); + sn_name_id_t source_name = source->name; + bool interface_locked = source->interface_locked; + sn_module_id_t reordered_id = sn_design_dup_module_topo(design, module_id, temporary_name); + assert(reordered_id == old_module_count); + sn_module_t* reordered = sn_design_get_module(design, reordered_id); + sn_name_id_t temporary_name_id = reordered->name; + + sn_design_invalidate_copies_to_module(design, module_id); + sn_module_destroy(source); + free(source); + reordered->id = module_id; + reordered->name = source_name; + reordered->interface_locked = interface_locked; + sn_vec_at(sn_module_t*, &design->modules, module_id) = reordered; + design->modules.size--; + sn_name_remove_last(&design->names, temporary_name_id); + assert(sn_module_is_topo(reordered)); +} + +typedef struct sn_const_zero_frame_t +{ + sn_obj_id_t object; + uint32_t next_fanin; +} sn_const_zero_frame_t; + +static inline uint32_t sn_const_zero_dependency_count(const sn_module_t* module, sn_obj_id_t object) +{ + sn_obj_type_t type = sn_obj_type(module, object); + if (type == SN_CAST || type == SN_SLICE || type == SN_REPLICATE) + return 1; + if (type == SN_CONCAT || type == SN_BIT_AND || type == SN_LOG_AND || type == SN_BIT_OR || + type == SN_BIT_XOR || type == SN_LOG_OR) + return sn_obj_fanin_count(module, object); + if (type == SN_MUX || type == SN_PMUX) + return 3; + return 0; +} + +static inline bool sn_const_zero_cached(const uint8_t* cache, sn_obj_id_t object) +{ + return object != SN_INVALID_ID && cache[object] == 1; +} + +static inline bool sn_const_zero_evaluate(const sn_module_t* module, sn_obj_id_t object, const uint8_t* cache) +{ + sn_obj_type_t type = sn_obj_type(module, object); + if (type == SN_CONST0) + return true; + if (type == SN_CONST) + { + const uint32_t* words = sn_const_words(module, object); + for (uint32_t i = 0; i < sn_const_word_count(sn_obj_width(module, object)); i++) + if (words[i]) + return false; + return true; + } + if (type == SN_CAST || type == SN_SLICE || type == SN_REPLICATE) + return sn_const_zero_cached(cache, sn_obj_fanin(module, object, 0)); + if (type == SN_CONCAT || type == SN_BIT_OR || type == SN_BIT_XOR || type == SN_LOG_OR) + { + for (uint32_t i = 0; i < sn_obj_fanin_count(module, object); i++) + if (!sn_const_zero_cached(cache, sn_obj_fanin(module, object, i))) + return false; + return true; + } + if (type == SN_BIT_AND || type == SN_LOG_AND) + { + for (uint32_t i = 0; i < sn_obj_fanin_count(module, object); i++) + if (sn_const_zero_cached(cache, sn_obj_fanin(module, object, i))) + return true; + return false; + } + if (type == SN_MUX) + { + sn_obj_id_t select = sn_obj_fanin(module, object, SN_MUX_SELECT); + sn_obj_id_t selected = sn_obj_fanin(module, object, SN_MUX_SELECTED); + sn_obj_id_t default_value = sn_obj_fanin(module, object, SN_MUX_DEFAULT); + return sn_const_zero_cached(cache, select) + ? sn_const_zero_cached(cache, default_value) + : sn_const_zero_cached(cache, selected) && sn_const_zero_cached(cache, default_value); + } + if (type == SN_PMUX) + { + sn_obj_id_t select = sn_obj_fanin(module, object, SN_PMUX_SELECT); + sn_obj_id_t alternatives = sn_obj_fanin(module, object, SN_PMUX_ALTERNATIVES); + sn_obj_id_t default_value = sn_obj_fanin(module, object, SN_PMUX_DEFAULT); + return sn_const_zero_cached(cache, select) + ? sn_const_zero_cached(cache, default_value) + : sn_const_zero_cached(cache, alternatives) && sn_const_zero_cached(cache, default_value); + } + return false; +} + +// Returns true for the constant-zero forms needed by sequential cleanup. The explicit stack bounds native call-stack +// use even for very deep expression chains; ordinary combinational constant propagation remains a separate pass. +static inline bool sn_obj_is_const_zero_rec(const sn_module_t* module, sn_obj_id_t object, uint8_t* cache) +{ + assert(module); + if (object == SN_INVALID_ID) + return false; + bool owns_cache = cache == NULL; + if (owns_cache) + { + cache = (uint8_t*)calloc(module->obj_types.size, 1); + assert(cache); + } + if (!cache[object]) + { + sn_vec_t stack; + sn_vec_init(&stack); + cache[object] = 3; + sn_const_zero_frame_t* first = sn_vec_push(sn_const_zero_frame_t, &stack); + first->object = object; + first->next_fanin = 0; + while (stack.size) + { + sn_const_zero_frame_t* frame = + &sn_vec_at(sn_const_zero_frame_t, &stack, stack.size - 1); + uint32_t dependency_count = sn_const_zero_dependency_count(module, frame->object); + if (frame->next_fanin < dependency_count) + { + sn_obj_id_t dependency = sn_obj_fanin(module, frame->object, frame->next_fanin++); + if (dependency != SN_INVALID_ID && cache[dependency] == 0) + { + cache[dependency] = 3; + sn_const_zero_frame_t* child = sn_vec_push(sn_const_zero_frame_t, &stack); + child->object = dependency; + child->next_fanin = 0; + } + continue; + } + cache[frame->object] = sn_const_zero_evaluate(module, frame->object, cache) ? 1 : 2; + stack.size--; + } + sn_vec_destroy(&stack); + } + bool result = cache[object] == 1; + if (owns_cache) + free(cache); + return result; +} + +static inline bool sn_obj_is_const_zero(const sn_module_t* module, sn_obj_id_t object) +{ + return sn_obj_is_const_zero_rec(module, object, NULL); +} + +// Under SN's two-state sequential convention, an unspecified or unknown +// initial state starts at zero. A register therefore remains zero when it has +// no active set, has no nonzero explicit initialization/reset value, and its +// data is zero (or its enable is permanently disabled). +static inline bool sn_reg_is_const_zero(const sn_module_t* module, sn_obj_id_t reg_out, uint8_t* cache) +{ + assert(module); + assert(sn_obj_type(module, reg_out) == SN_REG_OUT); + sn_obj_id_t set = sn_obj_fanin(module, reg_out, SN_REG_SET); + sn_obj_id_t init = sn_obj_fanin(module, reg_out, SN_REG_INIT_DATA); + sn_obj_id_t reset_value = sn_obj_fanin(module, reg_out, SN_REG_RESET_VALUE); + if ((set != SN_INVALID_ID && !sn_obj_is_const_zero_rec(module, set, cache)) || + (init != SN_INVALID_ID && !sn_obj_is_const_zero_rec(module, init, cache)) || + (reset_value != SN_INVALID_ID && !sn_obj_is_const_zero_rec(module, reset_value, cache))) + return false; + + sn_obj_id_t enable = sn_obj_fanin(module, reg_out, SN_REG_ENABLE); + if (enable != SN_INVALID_ID && sn_obj_is_const_zero_rec(module, enable, cache)) + return true; + sn_obj_id_t reg_in = sn_obj_pair_in(module, reg_out); + sn_obj_id_t data = sn_obj_fanin(module, reg_in, 0); + return data == reg_out || sn_obj_is_const_zero_rec(module, data, cache); +} + +typedef struct sn_clean_topo_context_t +{ + const sn_module_t* module; + sn_vec_t* order; + uint8_t* marks; + uint8_t* const_zero_objects; + uint8_t* const_zero_cache; +} sn_clean_topo_context_t; + +static inline void sn_module_clean_topo_push(sn_clean_topo_context_t* context, sn_vec_t* stack, + sn_obj_id_t object) +{ + const sn_module_t* module = context->module; + assert(object < module->obj_types.size); + if (context->marks[object] == SN_TOPO_DONE) + return; + assert(context->marks[object] == SN_TOPO_UNSEEN); + sn_obj_type_t type = sn_obj_type(module, object); + assert(type != SN_PI && type != SN_PO); + if (context->const_zero_objects[object] || sn_obj_is_const_zero_rec(module, object, context->const_zero_cache)) + { + context->const_zero_objects[object] = 1; + context->marks[object] = SN_TOPO_DONE; + return; + } + context->marks[object] = SN_TOPO_VISITING; + sn_topo_frame_t* frame = sn_vec_push(sn_topo_frame_t, stack); + frame->object = object; + frame->index = 0; + frame->phase = 0; +} + +static inline void sn_module_clean_topo_visit(sn_clean_topo_context_t* context, sn_obj_id_t object) +{ + assert(context); + const sn_module_t* module = context->module; + sn_vec_t stack; + sn_vec_init(&stack); + sn_module_clean_topo_push(context, &stack, object); + while (stack.size) + { + sn_topo_frame_t* frame = &sn_vec_at(sn_topo_frame_t, &stack, stack.size - 1); + object = frame->object; + if (context->marks[object] == SN_TOPO_DONE) + { + stack.size--; + continue; + } + sn_obj_type_t type = sn_obj_type(module, object); + if (sn_obj_type_is_pair_out(type)) + { + *sn_vec_push(sn_obj_id_t, context->order) = object; + context->marks[object] = SN_TOPO_DONE; + stack.size--; + continue; + } + if (frame->phase == 0) + { + frame->phase = 1; + frame->index = 0; + if (sn_obj_type_is_pair_in(type)) + { + sn_obj_id_t pair_out = sn_obj_pair_out(module, object); + assert(context->marks[pair_out] != SN_TOPO_VISITING); + if (context->marks[pair_out] == SN_TOPO_UNSEEN) + sn_module_clean_topo_push(context, &stack, pair_out); + continue; + } + } + if (frame->phase == 1) + { + if (sn_obj_type_is_pair_in(type)) + { + sn_obj_id_t pair_out = sn_obj_pair_out(module, object); + bool pushed = false; + while (frame->index < sn_obj_fanin_count(module, pair_out)) + { + sn_obj_id_t fanin = sn_obj_fanin(module, pair_out, frame->index++); + if (fanin == SN_INVALID_ID || fanin == object) + continue; + assert(context->marks[fanin] != SN_TOPO_VISITING); + if (context->marks[fanin] == SN_TOPO_UNSEEN) + { + sn_module_clean_topo_push(context, &stack, fanin); + pushed = true; + break; + } + } + if (pushed) + continue; + } + frame = &sn_vec_at(sn_topo_frame_t, &stack, stack.size - 1); + frame->phase = 2; + frame->index = 0; + } + bool pushed = false; + while (frame->index < sn_obj_fanin_count(module, object)) + { + sn_obj_id_t fanin = sn_obj_fanin(module, object, frame->index++); + if (fanin == SN_INVALID_ID) + continue; + assert(context->marks[fanin] != SN_TOPO_VISITING); + if (context->marks[fanin] == SN_TOPO_UNSEEN) + { + sn_module_clean_topo_push(context, &stack, fanin); + pushed = true; + break; + } + } + if (pushed) + continue; + if (type == SN_INST) + { + *sn_vec_push(sn_obj_id_t, context->order) = object; + context->marks[object] = SN_TOPO_DONE; + uint32_t output_count = sn_design_module_output_count(module->design, sn_inst_module_id(module, object)); + if (output_count > 1) + for (uint32_t i = 0; i < output_count; i++) + { + sn_obj_id_t fan = sn_inst_output(module, object, i); + assert(context->marks[fan] != SN_TOPO_DONE); + *sn_vec_push(sn_obj_id_t, context->order) = fan; + context->marks[fan] = SN_TOPO_DONE; + } + } + else if (context->marks[object] != SN_TOPO_DONE) + { + *sn_vec_push(sn_obj_id_t, context->order) = object; + context->marks[object] = SN_TOPO_DONE; + } + stack.size--; + } + sn_vec_destroy(&stack); +} + +static inline void sn_module_clean_rebuild_pair_ids(sn_module_t* target, const sn_module_t* source, + sn_obj_type_t out_type, sn_obj_type_t in_type) +{ + assert(target); + assert(source); + size_t next_type_id = 0; + for (size_t i = 0; i < source->type_objects[out_type].size; i++) + { + sn_obj_id_t old_out = sn_vec_at(sn_obj_id_t, &source->type_objects[out_type], i); + sn_obj_id_t new_out = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_out); + if (new_out == SN_INVALID_ID || sn_obj_type(target, new_out) != out_type) + continue; + sn_obj_id_t old_in = sn_obj_pair_in(source, old_out); + sn_obj_id_t new_in = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_in); + assert(new_in != SN_INVALID_ID && sn_obj_type(target, new_in) == in_type); + assert(next_type_id < target->type_objects[out_type].size); + assert(next_type_id < target->type_objects[in_type].size); + sn_vec_at(uint32_t, &target->type_ids, new_out) = (uint32_t)next_type_id; + sn_vec_at(uint32_t, &target->type_ids, new_in) = (uint32_t)next_type_id; + sn_vec_at(sn_obj_id_t, &target->type_objects[out_type], next_type_id) = new_out; + sn_vec_at(sn_obj_id_t, &target->type_objects[in_type], next_type_id) = new_in; + next_type_id++; + } + assert(next_type_id == target->type_objects[out_type].size); + assert(next_type_id == target->type_objects[in_type].size); +} + +// Duplicates only the sequential transitive fanin cone of the module outputs. +// A reached state OUT makes its paired IN, next-state logic, and controls +// reachable. Unreached state and logic are omitted. Registers proven to remain +// zero are replaced by constants, so their pairs are never constructed. PIs +// and POs are always preserved in natural interface order. +static inline sn_module_id_t sn_design_dup_module_clean_topo(sn_design_t* design, + sn_module_id_t source_module_id, + const char* new_name) +{ + assert(design); + assert(source_module_id < design->modules.size); + assert(new_name); + sn_module_t* source = sn_design_get_module(design, source_module_id); + size_t object_count = source->obj_types.size; + uint8_t* marks = object_count ? (uint8_t*)calloc(object_count, sizeof(uint8_t)) : NULL; + uint8_t* const_zero_objects = object_count ? (uint8_t*)calloc(object_count, sizeof(uint8_t)) : NULL; + uint8_t* const_zero_cache = object_count ? (uint8_t*)calloc(object_count, sizeof(uint8_t)) : NULL; + assert(!object_count || (marks && const_zero_objects && const_zero_cache)); + + for (size_t i = 0; i < source->type_objects[SN_REG_OUT].size; i++) + { + sn_obj_id_t reg_out = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_REG_OUT], i); + if (!sn_reg_is_const_zero(source, reg_out, const_zero_cache)) + continue; + const_zero_objects[reg_out] = 1; + marks[sn_obj_pair_in(source, reg_out)] = SN_TOPO_DONE; + } + + sn_vec_t order; + sn_vec_init(&order); + sn_vec_reserve(sn_obj_id_t, &order, object_count); + for (size_t i = 0; i < source->type_objects[SN_PI].size; i++) + { + sn_obj_id_t input = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PI], i); + marks[input] = SN_TOPO_DONE; + *sn_vec_push(sn_obj_id_t, &order) = input; + } + sn_clean_topo_context_t context = {source, &order, marks, const_zero_objects, const_zero_cache}; + for (size_t i = 0; i < source->type_objects[SN_PO].size; i++) + { + sn_obj_id_t output = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PO], i); + for (uint32_t j = 0; j < sn_obj_fanin_count(source, output); j++) + { + sn_obj_id_t fanin = sn_obj_fanin(source, output, j); + if (fanin != SN_INVALID_ID) + sn_module_clean_topo_visit(&context, fanin); + } + } + // State OUTs break sequential cycles. After discovering them from the PO + // cones, traverse their paired IN cones separately. Those cones can expose + // more live state, so iterate to a fixed point. + bool added_state; + do + { + added_state = false; + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_obj_type_t type = sn_obj_type(source, object); + if (!sn_obj_type_is_pair_out(type) || const_zero_objects[object] || + marks[object] != SN_TOPO_DONE) + continue; + sn_obj_id_t pair_in = sn_obj_pair_in(source, object); + if (marks[pair_in] == SN_TOPO_DONE) + continue; + sn_module_clean_topo_visit(&context, pair_in); + added_state = true; + } + } while (added_state); + for (size_t i = 0; i < source->type_objects[SN_PO].size; i++) + { + sn_obj_id_t output = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PO], i); + marks[output] = SN_TOPO_DONE; + *sn_vec_push(sn_obj_id_t, &order) = output; + } + + sn_module_id_t target_id = sn_design_add_module(design, new_name); + sn_module_t* target = sn_design_get_module(design, target_id); + target->flags = source->flags; + sn_vec_resize(sn_obj_id_t, &source->copy_ids, object_count); + for (size_t i = 0; i < object_count; i++) + sn_vec_at(sn_obj_id_t, &source->copy_ids, i) = SN_INVALID_ID; + + size_t input_count = source->type_objects[SN_PI].size; + for (size_t i = 0; i < input_count; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = + sn_module_dup_obj_skeleton(target, source, old_object); + } + for (sn_obj_id_t old_object = 0; old_object < object_count; old_object++) + { + if (!const_zero_objects[old_object] || marks[old_object] != SN_TOPO_DONE) + continue; + uint32_t bits = sn_obj_width(source, old_object); + uint32_t* words = (uint32_t*)calloc(sn_const_word_count(bits), sizeof(uint32_t)); + sn_name_id_t source_name_id = sn_obj_name_id(source, old_object); + const char* source_name = source_name_id == SN_INVALID_ID ? NULL : + sn_name_get(&source->design->names, source_name_id); + char* name = source_name ? (char*)malloc(strlen(source_name) + 1) : NULL; + assert(words); + if (source_name) + { + assert(name); + memcpy(name, source_name, strlen(source_name) + 1); + } + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = + sn_module_add_const(target, bits, sn_obj_is_signed(source, old_object), words, name); + free(name); + free(words); + } + for (size_t i = input_count; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = + sn_module_dup_obj_skeleton(target, source, old_object); + } + + sn_module_clean_rebuild_pair_ids(target, source, SN_REG_OUT, SN_REG_IN); + sn_module_clean_rebuild_pair_ids(target, source, SN_MEM_OUT, SN_MEM_IN); + sn_module_clean_rebuild_pair_ids(target, source, SN_LOOP_OUT, SN_LOOP_IN); + + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_obj_id_t new_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + sn_obj_type_t type = sn_obj_type(source, old_object); + sn_type_id_t target_type_id = sn_obj_type_id(target, new_object); + sn_module_dup_obj_metadata(target, target_type_id, source, old_object); + if (type == SN_FAN) + { + sn_obj_id_t old_inst = sn_fan_inst_id(source, old_object); + sn_vec_at(sn_obj_id_t, &target->fan_insts, target_type_id) = + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_inst); + } + } + + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_obj_id_t new_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + for (uint32_t j = 0; j < sn_obj_fanin_count(source, old_object); j++) + { + sn_obj_id_t old_fanin = sn_obj_fanin(source, old_object, j); + sn_obj_id_t new_fanin = old_fanin == SN_INVALID_ID + ? SN_INVALID_ID + : sn_vec_at(sn_obj_id_t, &source->copy_ids, old_fanin); + assert(new_fanin != SN_INVALID_ID || old_fanin == SN_INVALID_ID); + sn_obj_connect(target, new_object, j, new_fanin); + } + } + + source->copy_module = target_id; + sn_vec_destroy(&order); + free(marks); + free(const_zero_objects); + free(const_zero_cache); + assert(sn_module_is_topo(target)); + return target_id; +} + +// Reconstructs one module in place using the observable-cone cleanup above. +// Existing module IDs and interface names remain stable for hierarchical insts. +static inline void sn_design_cleanup_module_topo(sn_design_t* design, sn_module_id_t module_id) +{ + assert(design); + assert(module_id < design->modules.size); + // A loop-breaker can separate output-specific dependencies of a + // multi-output child instance. This local traversal conservatively treats + // every instance output as depending on every input, which can recreate an + // artificial cycle. Preserve such modules with the ordinary topo rebuild. + if (sn_design_get_module_const(design, module_id)->type_objects[SN_LOOP_OUT].size) + { + sn_design_reorder_module_topo(design, module_id); + return; + } + char temporary_name[96]; + uint32_t suffix = 0; + do + { + int length = snprintf(temporary_name, sizeof(temporary_name), "__sn_clean_%u_%u", module_id, suffix++); + assert(length >= 0 && (size_t)length < sizeof(temporary_name)); + assert(suffix != 0); + } while (sn_name_find(&design->names, temporary_name) != SN_INVALID_ID); + + sn_module_t* source = sn_design_get_module(design, module_id); + sn_name_id_t source_name = source->name; + bool interface_locked = source->interface_locked; + sn_module_id_t clean_id = sn_design_dup_module_clean_topo(design, module_id, temporary_name); + sn_module_t* clean = sn_design_get_module(design, clean_id); + sn_name_id_t temporary_name_id = clean->name; + sn_design_invalidate_copies_to_module(design, module_id); + sn_module_destroy(source); + free(source); + clean->id = module_id; + clean->name = source_name; + clean->interface_locked = interface_locked; + sn_vec_at(sn_module_t*, &design->modules, module_id) = clean; + design->modules.size--; + sn_name_remove_last(&design->names, temporary_name_id); + assert(sn_module_is_topo(clean)); +} + +typedef struct sn_collapse_context_t +{ + sn_design_t* design; + sn_module_t* target; + uint8_t* active_modules; + bool preserve_technology_primitives; +} sn_collapse_context_t; + +static inline bool sn_module_is_technology_primitive(const sn_module_t* module) +{ + assert(module); + if (sn_module_is_blackbox(module)) + return true; + const char* name = sn_name_get(&module->design->names, module->name); + return strncmp(name, "__sn_RAM", 8) == 0 || strncmp(name, "__sn_URAM", 9) == 0 || + strncmp(name, "__sn_DSP", 8) == 0 || strncmp(name, "__sn_CARRY", 10) == 0; +} + +static inline bool sn_collapse_preserves_object(const sn_collapse_context_t* context, + const sn_module_t* source, sn_obj_id_t object) +{ + sn_obj_type_t type = sn_obj_type(source, object); + sn_obj_id_t inst = type == SN_INST ? object : type == SN_FAN ? sn_fan_inst_id(source, object) + : SN_INVALID_ID; + if (inst == SN_INVALID_ID) + return false; + const sn_module_t* child = sn_design_get_module_const(context->design, sn_inst_module_id(source, inst)); + if (sn_module_is_blackbox(child)) + return true; + return context->preserve_technology_primitives && sn_module_is_technology_primitive(child); +} + +static inline bool sn_collapse_obj_is_copied(sn_obj_type_t type, bool is_top) +{ + if (type == SN_INST || type == SN_FAN) + return false; + if (!is_top && (type == SN_PI || type == SN_PO)) + return false; + return true; +} + +static inline void sn_module_collapse_into(sn_collapse_context_t* context, sn_module_id_t source_module_id, + const sn_obj_id_t* input_bindings, uint32_t input_count, bool is_top, + sn_vec_t* output_bindings) +{ + assert(context); + assert(source_module_id < context->design->modules.size); + assert(!context->active_modules[source_module_id]); + sn_module_t* source = sn_design_get_module(context->design, source_module_id); + assert(sn_module_is_topo(source)); + assert(is_top || input_count == source->type_objects[SN_PI].size); + assert(is_top || input_count == 0 || input_bindings); + (void)input_count; + context->active_modules[source_module_id] = 1; + + sn_vec_resize(sn_obj_id_t, &source->copy_ids, source->obj_types.size); + for (size_t i = 0; i < source->copy_ids.size; i++) + sn_vec_at(sn_obj_id_t, &source->copy_ids, i) = SN_INVALID_ID; + source->copy_module = context->target->id; + + for (sn_obj_id_t old_object = 0; old_object < source->obj_types.size; old_object++) + { + sn_obj_type_t type = sn_obj_type(source, old_object); + if (type == SN_PI && !is_top) + { + sn_type_id_t port_index = sn_obj_type_id(source, old_object); + assert(port_index < input_count); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = input_bindings[port_index]; + continue; + } + if (type == SN_PO && !is_top) + { + assert(sn_obj_fanin_count(source, old_object) == 1); + sn_obj_id_t driver = sn_obj_fanin(source, old_object, 0); + assert(driver < source->copy_ids.size); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = sn_vec_at(sn_obj_id_t, &source->copy_ids, driver); + continue; + } + if (type == SN_FAN) + { + if (sn_collapse_preserves_object(context, source, old_object)) + { + sn_obj_id_t new_object = sn_module_dup_obj_skeleton(context->target, source, old_object); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = new_object; + sn_obj_id_t old_inst = sn_fan_inst_id(source, old_object); + sn_vec_at(sn_obj_id_t, &context->target->fan_insts, + sn_obj_type_id(context->target, new_object)) = + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_inst); + } + else + assert(sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) != SN_INVALID_ID); + continue; + } + if (type == SN_INST) + { + if (sn_collapse_preserves_object(context, source, old_object)) + { + sn_obj_id_t new_object = sn_module_dup_obj_skeleton(context->target, source, old_object); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = new_object; + sn_module_dup_obj_metadata(context->target, sn_obj_type_id(context->target, new_object), + source, old_object); + continue; + } + sn_module_id_t child_id = sn_inst_module_id(source, old_object); + const sn_module_t* child = sn_design_get_module_const(context->design, child_id); + uint32_t child_input_count = (uint32_t)child->type_objects[SN_PI].size; + assert(child_input_count == sn_obj_fanin_count(source, old_object)); + + sn_vec_t child_inputs; + sn_vec_t child_outputs; + sn_vec_init(&child_inputs); + sn_vec_init(&child_outputs); + sn_vec_resize(sn_obj_id_t, &child_inputs, child_input_count); + for (uint32_t i = 0; i < child_input_count; i++) + { + sn_obj_id_t old_fanin = sn_obj_fanin(source, old_object, i); + assert(old_fanin < source->copy_ids.size); + sn_obj_id_t new_fanin = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_fanin); + assert(new_fanin < context->target->obj_types.size); + sn_vec_at(sn_obj_id_t, &child_inputs, i) = new_fanin; + } + + sn_module_collapse_into(context, child_id, sn_vec_data(sn_obj_id_t, &child_inputs), child_input_count, + false, &child_outputs); + uint32_t output_count = sn_design_module_output_count(context->design, child_id); + assert(child_outputs.size == output_count); + if (output_count == 1) + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = sn_vec_at(sn_obj_id_t, &child_outputs, 0); + else + { + for (uint32_t i = 0; i < output_count; i++) + { + sn_obj_id_t fan = sn_inst_output(source, old_object, i); + sn_vec_at(sn_obj_id_t, &source->copy_ids, fan) = sn_vec_at(sn_obj_id_t, &child_outputs, i); + } + } + sn_vec_destroy(&child_outputs); + sn_vec_destroy(&child_inputs); + continue; + } + + sn_obj_id_t new_object = sn_module_dup_obj_skeleton(context->target, source, old_object); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = new_object; + sn_module_dup_obj_metadata(context->target, sn_obj_type_id(context->target, new_object), source, old_object); + } + + // Patch fanins after every source object has a mapping. This is needed for + // the deliberate forward structural edge from a state OUT to its IN and + // for REG_OUT control fanins which can appear between the pair endpoints. + for (sn_obj_id_t old_object = 0; old_object < source->obj_types.size; old_object++) + { + sn_obj_type_t type = sn_obj_type(source, old_object); + if (!sn_collapse_obj_is_copied(type, is_top) && + !sn_collapse_preserves_object(context, source, old_object)) + continue; + sn_obj_id_t new_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + assert(new_object < context->target->obj_types.size); + for (uint32_t i = 0; i < sn_obj_fanin_count(source, old_object); i++) + { + sn_obj_id_t old_fanin = sn_obj_fanin(source, old_object, i); + sn_obj_id_t new_fanin = + old_fanin == SN_INVALID_ID ? SN_INVALID_ID : sn_vec_at(sn_obj_id_t, &source->copy_ids, old_fanin); + assert(new_fanin == SN_INVALID_ID || new_fanin < context->target->obj_types.size); + sn_obj_connect(context->target, new_object, i, new_fanin); + } + } + + if (output_bindings) + { + size_t output_count = source->type_objects[SN_PO].size; + sn_vec_resize(sn_obj_id_t, output_bindings, output_count); + for (size_t i = 0; i < output_count; i++) + { + sn_obj_id_t old_output = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PO], i); + sn_obj_id_t new_output = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_output); + if (is_top) + new_output = sn_obj_fanin(context->target, new_output, 0); + assert(new_output < context->target->obj_types.size); + sn_vec_at(sn_obj_id_t, output_bindings, i) = new_output; + } + } + context->active_modules[source_module_id] = 0; +} + +static inline void sn_module_rebuild_pair_type_ids(sn_module_t* module, sn_obj_type_t out_type, sn_obj_type_t in_type, + uint32_t pair_fanin_slot) +{ + assert(module); + assert(module->type_objects[out_type].size == module->type_objects[in_type].size); + for (size_t i = 0; i < module->type_objects[out_type].size; i++) + { + sn_obj_id_t out = sn_vec_at(sn_obj_id_t, &module->type_objects[out_type], i); + sn_obj_id_t in = sn_obj_fanin(module, out, pair_fanin_slot); + assert(sn_obj_type(module, in) == in_type); + sn_vec_at(uint32_t, &module->type_ids, out) = (sn_type_id_t)i; + sn_vec_at(uint32_t, &module->type_ids, in) = (sn_type_id_t)i; + sn_vec_at(sn_obj_id_t, &module->type_objects[in_type], i) = in; + } +} + +static inline char* sn_design_flat_module_name(const sn_design_t* design, sn_module_id_t source_module_id) +{ + const sn_module_t* source = sn_design_get_module_const(design, source_module_id); + const char* source_name = sn_name_get(&design->names, source->name); + size_t cap = strlen(source_name) + 32; + char* candidate = (char*)malloc(cap); + assert(candidate); + for (uint32_t suffix = 0;; suffix++) + { + int length = suffix == 0 ? snprintf(candidate, cap, "%s_flat", source_name) + : snprintf(candidate, cap, "%s_flat_%u", source_name, suffix); + assert(length >= 0 && (size_t)length < cap); + (void)length; + bool found = false; + for (size_t i = 0; i < design->modules.size; i++) + { + const sn_module_t* module = sn_design_get_module_const(design, (sn_module_id_t)i); + found = found || strcmp(candidate, sn_name_get(&design->names, module->name)) == 0; + } + if (!found) + return candidate; + assert(suffix != UINT32_MAX); + } +} + +// Flattens the selected top module into a newly created module. Every existing +// design module must already satisfy sn_module_is_topo(). The generated name is +// _flat, with a numeric suffix when needed. +static inline sn_module_id_t sn_design_collapse_module_internal(sn_design_t* design, sn_module_id_t top_module_id, + bool preserve_technology_primitives) +{ + assert(design); + assert(top_module_id < design->modules.size); + bool design_is_topo = sn_design_is_topo(design); + assert(design_is_topo); + (void)design_is_topo; + + size_t source_module_count = design->modules.size; + char* flat_name = sn_design_flat_module_name(design, top_module_id); + sn_module_id_t flat_module_id = sn_design_add_module(design, flat_name); + free(flat_name); + sn_module_t* flat = sn_design_get_module(design, flat_module_id); + + uint8_t* active_modules = NULL; + if (source_module_count) + { + active_modules = (uint8_t*)calloc(source_module_count, sizeof(uint8_t)); + assert(active_modules); + } + sn_collapse_context_t context; + context.design = design; + context.target = flat; + context.active_modules = active_modules; + context.preserve_technology_primitives = preserve_technology_primitives; + sn_module_collapse_into(&context, top_module_id, NULL, 0, true, NULL); + free(active_modules); + + sn_module_rebuild_pair_type_ids(flat, SN_REG_OUT, SN_REG_IN, SN_REG_DATA); + sn_module_rebuild_pair_type_ids(flat, SN_MEM_OUT, SN_MEM_IN, SN_MEM_STATE); + sn_module_rebuild_pair_type_ids(flat, SN_LOOP_OUT, SN_LOOP_IN, 0); + + for (size_t i = 0; i < flat->inst_modules.size; i++) + assert(sn_module_is_technology_primitive( + sn_design_get_module_const(design, sn_vec_at(sn_module_id_t, &flat->inst_modules, i)))); + assert(sn_module_is_topo(flat)); + return flat_module_id; +} + +static inline sn_module_id_t sn_design_collapse_module(sn_design_t* design, sn_module_id_t top_module_id) +{ + return sn_design_collapse_module_internal(design, top_module_id, false); +} + +// Flattens user hierarchy while retaining technology primitive leaf insts. +static inline sn_module_id_t sn_design_collapse_module_tech(sn_design_t* design, sn_module_id_t top_module_id) +{ + return sn_design_collapse_module_internal(design, top_module_id, true); +} + +// The initial Verilog writer is a structural debugging aid. It preserves +// hierarchy and supports ports, insts, constants, common combinational +// operators, slices, and concatenations. It asserts on state and memory +// objects until their exact behavioral Verilog policy is finalized. + +static inline const char* sn_verilog_unary_token(sn_obj_type_t type) +{ + switch (type) + { + case SN_POS: + return "+"; + case SN_NEG: + return "-"; + case SN_BIT_NOT: + return "~"; + case SN_LOG_NOT: + return "!"; + case SN_REDUCE_AND: + return "&"; + case SN_REDUCE_NAND: + return "~&"; + case SN_REDUCE_OR: + return "|"; + case SN_REDUCE_NOR: + return "~|"; + case SN_REDUCE_XOR: + return "^"; + case SN_REDUCE_XNOR: + return "~^"; + default: + return NULL; + } +} + +static inline const char* sn_verilog_binary_token(sn_obj_type_t type) +{ + switch (type) + { + case SN_ADD: + return "+"; + case SN_SUB: + return "-"; + case SN_MUL: + return "*"; + case SN_DIV: + return "/"; + case SN_MOD: + return "%"; + case SN_POW: + return "**"; + case SN_BIT_AND: + return "&"; + case SN_BIT_OR: + return "|"; + case SN_BIT_XOR: + return "^"; + case SN_BIT_XNOR: + return "~^"; + case SN_LOG_AND: + return "&&"; + case SN_LOG_OR: + return "||"; + case SN_EQ: + return "=="; + case SN_NE: + return "!="; + case SN_CASE_EQ: + return "==="; + case SN_CASE_NE: + return "!=="; + case SN_WILDCARD_EQ: + return "==?"; + case SN_WILDCARD_NE: + return "!=?"; + case SN_LT: + return "<"; + case SN_LE: + return "<="; + case SN_GT: + return ">"; + case SN_GE: + return ">="; + case SN_SHL: + return "<<"; + case SN_SHR: + return ">>"; + case SN_ASHL: + return "<<<"; + case SN_ASHR: + return ">>>"; + default: + return NULL; + } +} + +static inline void sn_write_verilog_range(FILE* out, const sn_module_t* module, sn_obj_id_t object) +{ + uint32_t width = sn_obj_width(module, object); + assert(width); + if (sn_obj_is_signed(module, object)) + fputs("signed ", out); + if (width > 1) + fprintf(out, "[%u:0] ", width - 1); +} + +static inline bool sn_verilog_is_keyword(const char* text) +{ + static const char* const keywords[] = { + "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", + "assign", "assume", "automatic", "before", "begin", "bind", "bins", "binsof", "bit", "break", + "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", "checker", "class", + "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", + "coverpoint", "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", + "else", "end", "endcase", "endchecker", "endclass", "endclocking", "endconfig", "endfunction", + "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", "endprimitive", "endprogram", + "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", + "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", + "fork", "forkjoin", "function", "generate", "genvar", "global", "highz0", "highz1", "if", "iff", + "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", "import", "incdir", "include", + "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", + "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", + "logic", "longint", "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", + "new", "nmos", "nor", "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", + "packed", "parameter", "pmos", "posedge", "primitive", "priority", "program", "property", "protected", + "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", "pure", "rand", + "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", + "repeat", "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", + "s_eventually", "s_nexttime", "s_until", "s_until_with", "scalared", "sequence", "shortint", "shortreal", + "showcancelled", "signed", "small", "soft", "solve", "specify", "specparam", "static", "string", "strong", + "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", "sync_reject_on", + "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", + "tranif1", "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", + "unique0", "unsigned", "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", + "wand", "weak", "weak0", "weak1", "while", "wildcard", "wire", "with", "within", "wor", "xnor", "xor" + }; + for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) + if (strcmp(text, keywords[i]) == 0) + return true; + return false; +} + +static inline bool sn_verilog_is_simple_identifier(const char* text) +{ + assert(text && text[0]); + unsigned char first = (unsigned char)text[0]; + if (!((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z') || first == '_')) + return false; + for (size_t i = 1; text[i]; i++) + { + unsigned char c = (unsigned char)text[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || + c == '$')) + return false; + } + return !sn_verilog_is_keyword(text); +} + +// Escaped Verilog identifiers terminate at whitespace. SN names containing whitespace or a backslash cannot be emitted +// losslessly and are rejected as construction errors; all other non-simple names and language keywords are escaped. +static inline void sn_write_verilog_identifier(FILE* out, const char* text) +{ + assert(out && text && text[0]); + if (sn_verilog_is_simple_identifier(text)) + { + fputs(text, out); + return; + } + for (size_t i = 0; text[i]; i++) + assert((unsigned char)text[i] > 32 && (unsigned char)text[i] < 127 && text[i] != '\\'); + fputc('\\', out); + fputs(text, out); + fputc(' ', out); +} + +// Internal names retain the compact historical spelling unless a user name collides with it. The fallback includes the +// module, object, and object role and is checked against the global name manager as well. +static inline void sn_write_verilog_generated_name(FILE* out, const sn_module_t* module, const char* role, + sn_obj_id_t object) +{ + assert(out && module && role); + char name[160]; + if (strcmp(role, "obj") == 0) + snprintf(name, sizeof(name), "_sn_%u", object); + else + snprintf(name, sizeof(name), "_sn_%s_%u", role, object); + if (sn_name_find(&module->design->names, name) == SN_INVALID_ID) + { + fputs(name, out); + return; + } + uint32_t suffix = 0; + do + { + int count = snprintf(name, sizeof(name), "__sn_generated_%u_%s_%u_%u", module->id, role, object, suffix++); + assert(count > 0 && (size_t)count < sizeof(name)); + (void)count; + } while (sn_name_find(&module->design->names, name) != SN_INVALID_ID); + fputs(name, out); +} + +static inline bool sn_const_bit(const sn_module_t* module, sn_obj_id_t object, uint32_t bit) +{ + assert(bit < sn_obj_width(module, object)); + sn_obj_type_t type = sn_obj_type(module, object); + if (type == SN_CONST0) + return false; + if (type == SN_CONST1) + return bit == 0; + assert(type == SN_CONST); + return (sn_const_words(module, object)[bit / 32] >> (bit % 32)) & 1u; +} + +static inline void sn_write_verilog_constant_chunk(FILE* out, const sn_module_t* module, sn_obj_id_t object, + uint32_t offset, uint32_t width) +{ + fprintf(out, "%u'h", width); + uint32_t nibble_count = (width + 3) / 4; + for (uint32_t nibble = nibble_count; nibble > 0; nibble--) + { + uint32_t digit = 0; + for (uint32_t bit = 0; bit < 4; bit++) + { + uint32_t index = (nibble - 1) * 4 + bit; + if (index < width && sn_const_bit(module, object, offset + index)) + digit |= 1u << bit; + } + fputc("0123456789abcdef"[digit], out); + } +} + +static inline void sn_write_verilog_constant(FILE* out, const sn_module_t* module, sn_obj_id_t object) +{ + uint32_t width = sn_obj_width(module, object); + sn_obj_type_t type = sn_obj_type(module, object); + if (type == SN_CONST0 || type == SN_CONST1) + { + fprintf(out, "%u", width); + fputc(39, out); + if (sn_obj_is_signed(module, object)) + fputc(115, out); + fprintf(out, "d%u", type == SN_CONST1 ? 1u : 0u); + return; + } + assert(type == SN_CONST); + if (width <= 4096) + { + fprintf(out, "%u", width); + fputc(39, out); + if (sn_obj_is_signed(module, object)) + fputc(115, out); + fputc(104, out); + const uint32_t* words = sn_const_words(module, object); + uint32_t count = sn_const_word_count(width); + fprintf(out, "%x", words[count - 1]); + while (--count) + fprintf(out, "%08x", words[count - 1]); + return; + } + + if (sn_obj_is_signed(module, object)) + fputs("$signed(", out); + fputs("{\n", out); + uint32_t remaining = width; + while (remaining) + { + uint32_t chunk_width = remaining > 1024 ? 1024 : remaining; + uint32_t offset = remaining - chunk_width; + fputs(" ", out); + sn_write_verilog_constant_chunk(out, module, object, offset, chunk_width); + remaining = offset; + fputs(remaining ? ",\n" : "\n", out); + } + fputc(125, out); + if (sn_obj_is_signed(module, object)) + fputc(41, out); +} + +static inline void sn_write_verilog_ref(FILE* out, const sn_module_t* module, sn_obj_id_t object) +{ + assert(object < module->obj_types.size); + sn_obj_type_t type = sn_obj_type(module, object); + if (type == SN_PI || type == SN_PO) + sn_write_verilog_identifier(out, sn_obj_name(module, object)); + else if (type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST) + sn_write_verilog_constant(out, module, object); + else + sn_write_verilog_generated_name(out, module, "obj", object); +} + +static inline void sn_write_verilog_constant_slice(FILE* out, const sn_module_t* module, sn_obj_id_t object, + const sn_slice_info_t* info, uint32_t width) +{ + assert(info); + assert(info->left_index >= 0 && info->right_index >= 0); + assert((uint32_t)info->left_index < sn_obj_width(module, object)); + assert((uint32_t)info->right_index < sn_obj_width(module, object)); + fprintf(out, "%u'h", width); + uint32_t nibble_count = (width + 3) / 4; + for (uint32_t nibble = nibble_count; nibble > 0; nibble--) + { + uint32_t digit = 0; + for (uint32_t offset = 0; offset < 4; offset++) + { + uint32_t result_bit = (nibble - 1) * 4 + offset; + if (result_bit >= width) + continue; + int64_t source_bit = info->left_index >= info->right_index ? (int64_t)info->right_index + result_bit + : (int64_t)info->right_index - result_bit; + assert(source_bit >= 0 && (uint64_t)source_bit < sn_obj_width(module, object)); + digit |= (uint32_t)sn_const_bit(module, object, (uint32_t)source_bit) << offset; + } + fputc("0123456789abcdef"[digit], out); + } +} + +static inline void sn_write_verilog_expression(FILE* out, const sn_module_t* module, sn_obj_id_t object) +{ + sn_obj_type_t type = sn_obj_type(module, object); + const char* unary_token = sn_verilog_unary_token(type); + if (unary_token) + { + assert(sn_obj_fanin_count(module, object) == 1); + fputc(40, out); + fputs(unary_token, out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, 0)); + fputc(41, out); + return; + } + const char* token = sn_verilog_binary_token(type); + if (token) + { + assert(sn_obj_fanin_count(module, object) == 2); + fputc(40, out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, 0)); + fprintf(out, " %s ", token); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, 1)); + fputc(41, out); + return; + } + if (type == SN_BUF) + { + assert(sn_obj_fanin_count(module, object) == 1); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, 0)); + return; + } + if (type == SN_CAST) + { + assert(sn_obj_fanin_count(module, object) == 1); + fputs(sn_obj_is_signed(module, object) ? "$signed(" : "$unsigned(", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, 0)); + fputc(41, out); + return; + } + if (type == SN_MUX) + { + assert(sn_obj_fanin_count(module, object) == SN_MUX_FANIN_COUNT); + fputc(40, out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, SN_MUX_SELECT)); + fputs(" ? ", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, SN_MUX_SELECTED)); + fputs(" : ", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, SN_MUX_DEFAULT)); + fputc(41, out); + return; + } + if (type == SN_BMUX) + { + assert(sn_obj_fanin_count(module, object) == SN_BMUX_FANIN_COUNT); + fputc(40, out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, SN_BMUX_ALTERNATIVES)); + fputs(" >> (", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, SN_BMUX_SELECT)); + fprintf(out, " * %u))", sn_obj_width(module, object)); + return; + } + if (type == SN_PMUX) + { + assert(sn_obj_fanin_count(module, object) == SN_PMUX_FANIN_COUNT); + sn_obj_id_t select = sn_obj_fanin(module, object, SN_PMUX_SELECT); + sn_obj_id_t alternatives = sn_obj_fanin(module, object, SN_PMUX_ALTERNATIVES); + uint32_t select_width = sn_obj_width(module, select); + uint32_t output_width = sn_obj_width(module, object); + for (uint32_t i = 0; i < select_width; i++) + { + fputs("(((", out); + sn_write_verilog_ref(out, module, select); + fprintf(out, " >> %u) & 1'd1) ? (", i); + sn_write_verilog_ref(out, module, alternatives); + fprintf(out, " >> %u) : ", i * output_width); + } + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, SN_PMUX_DEFAULT)); + for (uint32_t i = 0; i < select_width; i++) + fputc(41, out); + return; + } + if (type == SN_CONCAT) + { + fputc(123, out); + uint32_t count = sn_obj_fanin_count(module, object); + for (uint32_t i = count; i > 0; i--) + { + if (i != count) + fputs(", ", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, i - 1)); + } + fputc(125, out); + return; + } + if (type == SN_REPLICATE) + { + assert(sn_obj_fanin_count(module, object) == 1); + fputc(123, out); + fprintf(out, "%u", sn_obj_repeat_count(module, object)); + fputc(123, out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, 0)); + fputc(125, out); + fputc(125, out); + return; + } + if (type == SN_SLICE) + { + sn_obj_id_t value = sn_obj_fanin(module, object, 0); + const sn_slice_info_t* info = sn_obj_slice_info(module, object); + sn_obj_type_t value_type = sn_obj_type(module, value); + if (value_type == SN_CONST0 || value_type == SN_CONST1 || value_type == SN_CONST) + { + sn_write_verilog_constant_slice(out, module, value, info, sn_obj_width(module, object)); + return; + } + if (sn_obj_width(module, value) == 1 && info->left_index == 0 && info->right_index == 0) + { + sn_write_verilog_ref(out, module, value); + return; + } + if (info->left_index >= info->right_index) + { + sn_write_verilog_ref(out, module, value); + fprintf(out, "[%d:%d]", info->left_index, info->right_index); + } + else + { + // SN values use LSB-first significance order. For an ascending slice, result bit 0 is value[right], + // which cannot be expressed as an ascending part-select of SN's normalized [width-1:0] wires. + fputc('{', out); + for (int64_t index = info->left_index; index <= info->right_index; index++) + { + if (index != info->left_index) + fputs(", ", out); + sn_write_verilog_ref(out, module, value); + fprintf(out, "[%lld]", (long long)index); + } + fputc('}', out); + } + return; + } + assert(false); +} + +static inline sn_obj_id_t sn_module_find_named_type_object(const sn_module_t* module, sn_obj_type_t type, + sn_name_id_t name) +{ + if (name == SN_INVALID_ID) + return SN_INVALID_ID; + for (size_t i = 0; i < module->type_objects[type].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[type], i); + if (sn_obj_name_id(module, object) == name) + return object; + } + return SN_INVALID_ID; +} + +static inline void sn_write_verilog_inst(FILE* out, const sn_module_t* module, sn_obj_id_t inst) +{ + sn_module_id_t child_id = sn_inst_module_id(module, inst); + const sn_module_t* child = sn_design_get_module_const(module->design, child_id); + assert(sn_obj_fanin_count(module, inst) == child->type_objects[SN_PI].size); + fputs(" ", out); + sn_write_verilog_identifier(out, sn_name_get(&module->design->names, child->name)); + fputc(' ', out); + sn_write_verilog_generated_name(out, module, "inst", inst); + fputs(" (", out); + fputc(10, out); + size_t connection = 0; + size_t inout_count = 0; + for (size_t i = 0; i < child->type_objects[SN_PI].size; i++) + { + sn_obj_id_t port = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PI], i); + inout_count += sn_module_find_named_type_object(child, SN_PO, sn_obj_name_id(child, port)) != SN_INVALID_ID; + } + size_t connection_count = child->type_objects[SN_PI].size + child->type_objects[SN_PO].size - inout_count; + for (size_t i = 0; i < child->type_objects[SN_PI].size; i++, connection++) + { + sn_obj_id_t port = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PI], i); + sn_obj_id_t output_port = sn_module_find_named_type_object(child, SN_PO, sn_obj_name_id(child, port)); + fputs(" .", out); + sn_write_verilog_identifier(out, sn_obj_name(child, port)); + fputc('(', out); + if (output_port == SN_INVALID_ID) + sn_write_verilog_ref(out, module, sn_obj_fanin(module, inst, (uint32_t)i)); + else + sn_write_verilog_ref(out, module, + sn_inst_output(module, inst, sn_obj_type_id(child, output_port))); + fprintf(out, ")%s", connection + 1 == connection_count ? "" : ","); + fputc(10, out); + } + for (size_t i = 0; i < child->type_objects[SN_PO].size; i++) + { + sn_obj_id_t port = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PO], i); + if (sn_module_find_named_type_object(child, SN_PI, sn_obj_name_id(child, port)) != SN_INVALID_ID) + continue; + fputs(" .", out); + sn_write_verilog_identifier(out, sn_obj_name(child, port)); + fputc('(', out); + sn_write_verilog_ref(out, module, sn_inst_output(module, inst, (uint32_t)i)); + fprintf(out, ")%s", connection + 1 == connection_count ? "" : ","); + fputc(10, out); + connection++; + } + fputs(" );", out); + fputc(10, out); + for (size_t i = 0; i < child->type_objects[SN_PI].size; i++) + { + sn_obj_id_t port = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PI], i); + sn_obj_id_t output_port = sn_module_find_named_type_object(child, SN_PO, sn_obj_name_id(child, port)); + if (output_port == SN_INVALID_ID) + continue; + fputs(" assign ", out); + sn_write_verilog_ref(out, module, + sn_inst_output(module, inst, sn_obj_type_id(child, output_port))); + fputs(" = ", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, inst, (uint32_t)i)); + fputs(";\n", out); + } +} + +static inline void sn_write_verilog_lut(FILE* out, const sn_module_t* module, sn_obj_id_t object) +{ + assert(sn_obj_type(module, object) == SN_LUT); + uint32_t count = sn_obj_fanin_count(module, object); + uint64_t truth = sn_obj_lut_truth(module, object); + if (count == 0) + { + fputs(" assign ", out); + sn_write_verilog_generated_name(out, module, "obj", object); + fprintf(out, " = 1'b%u;\n", (unsigned)(truth & 1)); + return; + } + uint32_t truth_bits = UINT32_C(1) << count; + uint32_t hex_digits = (truth_bits + 3) / 4; + // A variable bit-select is portable synthesizable Verilog and does not require vendor LUT simulation models. + // Fanin 0 is the least-significant truth-table index bit, matching the SN_LUT convention. + fputs(" assign ", out); + sn_write_verilog_generated_name(out, module, "obj", object); + fprintf(out, " = %u'h%0*llx >> {", truth_bits, (int)hex_digits, (unsigned long long)truth); + for (uint32_t i = count; i-- > 0; ) + { + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, i)); + if (i) + fputs(", ", out); + } + fputs("};\n", out); +} + +// SN_GATE names created by ABC reconstruction are the current library cell names. Mini-mapping orders each cell's +// fanins in library pin order. Structural Verilog uses the conventional output-first positional cell interface so it +// remains independent of ABC's in-memory Mio pin objects after the SN design has been serialized. +static inline void sn_write_verilog_gate(FILE* out, const sn_module_t* module, sn_obj_id_t object) +{ + assert(sn_obj_type(module, object) == SN_GATE); + const char* gate_name = sn_obj_name(module, object); + assert(gate_name); + fputs(" ", out); + sn_write_verilog_identifier(out, gate_name); + fputc(' ', out); + sn_write_verilog_generated_name(out, module, "gate", object); + fputs(" (", out); + sn_write_verilog_generated_name(out, module, "obj", object); + for (uint32_t i = 0; i < sn_obj_fanin_count(module, object); i++) + { + fputs(", ", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, i)); + } + fputs(");\n", out); +} + +static inline void sn_write_verilog_active_control(FILE* out, const sn_module_t* module, sn_obj_id_t control, + bool active_low) +{ + if (active_low) + fputc('!', out); + sn_write_verilog_ref(out, module, control); +} + +static inline void sn_write_verilog_register(FILE* out, const sn_module_t* module, sn_obj_id_t reg_out) +{ + assert(sn_obj_type(module, reg_out) == SN_REG_OUT); + uint32_t flags = sn_obj_reg_flags(module, reg_out); + sn_obj_id_t clock = sn_obj_fanin(module, reg_out, SN_REG_CLOCK); + sn_obj_id_t enable = sn_obj_fanin(module, reg_out, SN_REG_ENABLE); + sn_obj_id_t set = sn_obj_fanin(module, reg_out, SN_REG_SET); + sn_obj_id_t reset = sn_obj_fanin(module, reg_out, SN_REG_RESET); + sn_obj_id_t initial_value = sn_obj_reg_init_data(module, reg_out); + sn_obj_id_t initial_mask = sn_obj_reg_init_mask(module, reg_out); + sn_obj_id_t reset_value = sn_obj_fanin(module, reg_out, SN_REG_RESET_VALUE); + sn_obj_id_t reg_in = sn_obj_pair_in(module, reg_out); + sn_obj_id_t data = sn_obj_fanin(module, reg_in, 0); + assert(data != SN_INVALID_ID); + assert((flags & SN_REG_LATCH) ? clock == SN_INVALID_ID : clock != SN_INVALID_ID); + + if (initial_value != SN_INVALID_ID) + { + fputs(" initial begin\n ", out); + sn_write_verilog_ref(out, module, reg_out); + fputs(" = ", out); + if (initial_mask == SN_INVALID_ID) + sn_write_verilog_ref(out, module, initial_value); + else + { + uint32_t width = sn_obj_width(module, reg_out); + fprintf(out, "%u'b", width); + for (uint32_t bit = width; bit-- > 0;) + fputc(!sn_const_bit(module, initial_mask, bit) ? 'x' + : sn_const_bit(module, initial_value, bit) ? '1' + : '0', + out); + } + fputs(";\n end\n", out); + } + + if (flags & SN_REG_LATCH) + { + assert(enable != SN_INVALID_ID && set == SN_INVALID_ID && reset == SN_INVALID_ID); + fputs(" always @* begin\n if (", out); + sn_write_verilog_ref(out, module, enable); + fputs(") ", out); + sn_write_verilog_ref(out, module, reg_out); + fputs(" <= ", out); + sn_write_verilog_ref(out, module, data); + fputs(";\n end\n", out); + return; + } + + fputs(" always @(", out); + fputs(flags & SN_REG_CLOCK_NEGEDGE ? "negedge " : "posedge ", out); + sn_write_verilog_ref(out, module, clock); + if (reset != SN_INVALID_ID && (flags & SN_REG_RESET_ASYNC)) + { + fputs(flags & SN_REG_RESET_NEGEDGE ? " or negedge " : " or posedge ", out); + sn_write_verilog_ref(out, module, reset); + } + if (set != SN_INVALID_ID && (flags & SN_REG_SET_ASYNC)) + { + fputs(flags & SN_REG_SET_NEGEDGE ? " or negedge " : " or posedge ", out); + sn_write_verilog_ref(out, module, set); + } + fputs(") begin\n", out); + + bool has_condition = false; + if (reset != SN_INVALID_ID) + { + fputs(" if (", out); + sn_write_verilog_active_control(out, module, reset, flags & SN_REG_RESET_NEGEDGE); + fputs(") ", out); + sn_write_verilog_ref(out, module, reg_out); + fputs(" <= ", out); + if (reset_value == SN_INVALID_ID) + fprintf(out, "%u'd0", sn_obj_width(module, reg_out)); + else + sn_write_verilog_ref(out, module, reset_value); + fputs(";\n", out); + has_condition = true; + } + if (set != SN_INVALID_ID) + { + fputs(has_condition ? " else if (" : " if (", out); + sn_write_verilog_active_control(out, module, set, flags & SN_REG_SET_NEGEDGE); + fputs(") ", out); + sn_write_verilog_ref(out, module, reg_out); + fprintf(out, " <= {%u{1'b1}};\n", sn_obj_width(module, reg_out)); + has_condition = true; + } + if (enable != SN_INVALID_ID) + { + fputs(has_condition ? " else if (" : " if (", out); + sn_write_verilog_ref(out, module, enable); + fputs(") ", out); + sn_write_verilog_ref(out, module, reg_out); + fputs(" <= ", out); + sn_write_verilog_ref(out, module, data); + fputs(";\n", out); + } + else + { + fputs(has_condition ? " else " : " ", out); + sn_write_verilog_ref(out, module, reg_out); + fputs(" <= ", out); + sn_write_verilog_ref(out, module, data); + fputs(";\n", out); + } + fputs(" end\n", out); +} + +static inline void sn_write_verilog_memory_read(FILE* out, const sn_module_t* module, sn_obj_id_t read) +{ + assert(sn_obj_type(module, read) == SN_MEM_READ); + sn_obj_id_t memory = sn_obj_fanin(module, read, SN_MEM_READ_MEMORY); + sn_obj_id_t clock = sn_obj_fanin(module, read, SN_MEM_READ_CLOCK); + sn_obj_id_t enable = sn_obj_fanin(module, read, SN_MEM_READ_ENABLE); + sn_obj_id_t address = sn_obj_fanin(module, read, SN_MEM_READ_ADDRESS); + assert(sn_obj_type(module, memory) == SN_MEM_OUT); + assert(clock != SN_INVALID_ID || enable == SN_INVALID_ID); + if (clock == SN_INVALID_ID) + fputs(" assign ", out); + else + { + fputs(" always @(posedge ", out); + sn_write_verilog_ref(out, module, clock); + fputs(") begin\n ", out); + if (enable != SN_INVALID_ID) + { + fputs("if (", out); + sn_write_verilog_ref(out, module, enable); + fputs(") ", out); + } + } + sn_write_verilog_ref(out, module, read); + fputs(clock == SN_INVALID_ID ? " = " : " <= ", out); + sn_write_verilog_generated_name(out, module, "mem", memory); + fputc('[', out); + sn_write_verilog_ref(out, module, address); + fputs(clock == SN_INVALID_ID ? "];\n" : "];\n end\n", out); +} + +static inline void sn_write_verilog_memory_init(FILE* out, const sn_module_t* module, sn_obj_id_t memory) +{ + assert(sn_obj_type(module, memory) == SN_MEM_OUT); + sn_obj_id_t data = sn_obj_mem_init_data(module, memory); + sn_obj_id_t mask = sn_obj_mem_init_mask(module, memory); + if (data == SN_INVALID_ID) + return; + + uint32_t width = sn_obj_width(module, memory); + uint32_t depth = sn_obj_mem_depth(module, memory); + fputs(" initial begin\n", out); + for (uint32_t entry = 0; entry < depth; entry++) + { + uint64_t offset = (uint64_t)entry * width; + bool has_valid_bit = mask == SN_INVALID_ID; + if (mask != SN_INVALID_ID) + for (uint32_t bit = 0; bit < width; bit++) + has_valid_bit = has_valid_bit || sn_const_bit(module, mask, (uint32_t)offset + bit); + if (!has_valid_bit) + continue; + + fputs(" ", out); + sn_write_verilog_generated_name(out, module, "mem", memory); + fprintf(out, "[%u] = %u'b", entry, width); + for (uint32_t bit = width; bit-- > 0;) + { + uint32_t init_bit = (uint32_t)offset + bit; + if (mask != SN_INVALID_ID && !sn_const_bit(module, mask, init_bit)) + fputc('x', out); + else + fputc(sn_const_bit(module, data, init_bit) ? '1' : '0', out); + } + fputs(";\n", out); + } + fputs(" end\n", out); +} + +static inline void sn_write_verilog_memory_write(FILE* out, const sn_module_t* module, sn_obj_id_t write, + sn_obj_id_t memory) +{ + assert(sn_obj_type(module, write) == SN_MEM_WRITE); + assert(sn_obj_type(module, memory) == SN_MEM_OUT); + sn_obj_id_t clock = sn_obj_fanin(module, write, SN_MEM_WRITE_CLOCK); + sn_obj_id_t enable = sn_obj_fanin(module, write, SN_MEM_WRITE_ENABLE); + sn_obj_id_t data = sn_obj_fanin(module, write, SN_MEM_WRITE_DATA); + sn_obj_id_t address = sn_obj_fanin(module, write, SN_MEM_WRITE_ADDRESS); + assert(clock != SN_INVALID_ID && data != SN_INVALID_ID && address != SN_INVALID_ID); + fputs(" always @(posedge ", out); + sn_write_verilog_ref(out, module, clock); + fputs(") begin\n", out); + if (enable != SN_INVALID_ID) + { + fputs(" if (", out); + sn_write_verilog_ref(out, module, enable); + fputs(")\n ", out); + } + else + fputs(" ", out); + sn_write_verilog_generated_name(out, module, "mem", memory); + fputc('[', out); + sn_write_verilog_ref(out, module, address); + fputs("] <= ", out); + sn_write_verilog_ref(out, module, data); + fputs(";\n end\n", out); +} + +static inline void sn_module_write_verilog_as(FILE* out, const sn_module_t* module, const char* emitted_name) +{ + assert(out); + assert(module); + assert(emitted_name); + size_t write_count = module->type_objects[SN_MEM_WRITE].size; + sn_obj_id_t* write_memories = write_count ? (sn_obj_id_t*)malloc(write_count * sizeof(sn_obj_id_t)) : NULL; + assert(write_memories || !write_count); + for (size_t i = 0; i < write_count; i++) + write_memories[i] = SN_INVALID_ID; + for (size_t i = 0; i < module->type_objects[SN_MEM_IN].size; i++) + { + sn_obj_id_t mem_in = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_IN], i); + sn_obj_id_t memory = sn_obj_pair_out(module, mem_in); + for (uint32_t j = 0; j < sn_obj_fanin_count(module, mem_in); j++) + { + sn_obj_id_t write = sn_obj_fanin(module, mem_in, j); + assert(sn_obj_type(module, write) == SN_MEM_WRITE); + uint32_t write_id = sn_obj_type_id(module, write); + assert(write_id < write_count && write_memories[write_id] == SN_INVALID_ID); + write_memories[write_id] = memory; + } + } + if (sn_module_is_blackbox(module)) + fputs("(* blackbox *) ", out); + fputs("module ", out); + sn_write_verilog_identifier(out, emitted_name); + fputs(" (", out); + fputc(10, out); + size_t inout_count = 0; + for (size_t i = 0; i < module->type_objects[SN_PI].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], i); + inout_count += + sn_module_find_named_type_object(module, SN_PO, sn_obj_name_id(module, object)) != SN_INVALID_ID; + } + size_t port_count = module->type_objects[SN_PI].size + module->type_objects[SN_PO].size - inout_count; + size_t port_index = 0; + for (uint32_t type = SN_PI; type <= SN_PO; type++) + for (size_t i = 0; i < module->type_objects[type].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[type], i); + if (type == SN_PO && + sn_module_find_named_type_object(module, SN_PI, sn_obj_name_id(module, object)) != SN_INVALID_ID) + continue; + fputs(" ", out); + sn_write_verilog_identifier(out, sn_obj_name(module, object)); + fputs(port_index + 1 == port_count ? "" : ",", out); + fputc(10, out); + port_index++; + } + fputs(");", out); + fputc(10, out); + + for (uint32_t type = SN_PI; type <= SN_PO; type++) + for (size_t i = 0; i < module->type_objects[type].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[type], i); + sn_obj_id_t matching = sn_module_find_named_type_object( + module, type == SN_PI ? SN_PO : SN_PI, sn_obj_name_id(module, object)); + if (type == SN_PO && matching != SN_INVALID_ID) + continue; + fprintf(out, " %s wire ", matching != SN_INVALID_ID ? "inout" : type == SN_PI ? "input" : "output"); + sn_write_verilog_range(out, module, object); + sn_write_verilog_identifier(out, sn_obj_name(module, object)); + fputc(';', out); + fputc(10, out); + } + + if (sn_module_is_blackbox(module)) + { + fputs("endmodule\n\n", out); + free(write_memories); + return; + } + + for (sn_obj_id_t object = 0; object < module->obj_types.size; object++) + { + sn_obj_type_t type = sn_obj_type(module, object); + if (type == SN_PI || type == SN_PO || type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST) + continue; + if (type == SN_INST) + { + sn_module_id_t child_id = sn_inst_module_id(module, object); + if (sn_design_module_output_count(module->design, child_id) > 1) + continue; + } + if (type == SN_MEM_OUT) + { + fputs(" reg ", out); + sn_write_verilog_range(out, module, object); + sn_write_verilog_generated_name(out, module, "mem", object); + fprintf(out, " [0:%u];", sn_obj_mem_depth(module, object) - 1); + fputc(10, out); + continue; + } + if (type == SN_MEM_IN || type == SN_MEM_WRITE) + continue; + bool procedural = type == SN_REG_OUT || + (type == SN_MEM_READ && sn_obj_fanin(module, object, SN_MEM_READ_CLOCK) != SN_INVALID_ID); + fputs(procedural ? " reg " : " wire ", out); + sn_write_verilog_range(out, module, object); + sn_write_verilog_generated_name(out, module, "obj", object); + fputc(';', out); + sn_name_id_t name = sn_obj_name_id(module, object); + if (name != SN_INVALID_ID) + fprintf(out, " // %s", sn_name_get(&module->design->names, name)); + fputc(10, out); + } + fputc(10, out); + + for (size_t i = 0; i < module->type_objects[SN_MEM_OUT].size; i++) + { + sn_obj_id_t memory = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_OUT], i); + sn_write_verilog_memory_init(out, module, memory); + } + + for (sn_obj_id_t object = 0; object < module->obj_types.size; object++) + { + sn_obj_type_t type = sn_obj_type(module, object); + if (type == SN_PO) + { + fputs(" assign ", out); + sn_write_verilog_ref(out, module, object); + fputs(" = ", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, 0)); + fputs(";", out); + fputc(10, out); + } + else if (type == SN_INST) + sn_write_verilog_inst(out, module, object); + else if (type == SN_REG_OUT) + sn_write_verilog_register(out, module, object); + else if (type == SN_REG_IN) + { + fputs(" assign ", out); + sn_write_verilog_ref(out, module, object); + fputs(" = ", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, 0)); + fputs(";\n", out); + } + else if (type == SN_LOOP_OUT || type == SN_LOOP_IN) + { + // Loop pairs are transparent buffers. Their OUT half is treated as a source only by graph traversal. + fputs(" assign ", out); + sn_write_verilog_ref(out, module, object); + fputs(" = ", out); + sn_write_verilog_ref(out, module, sn_obj_fanin(module, object, 0)); + fputs(";\n", out); + } + else if (type == SN_MEM_READ) + sn_write_verilog_memory_read(out, module, object); + else if (type == SN_MEM_WRITE) + { + sn_obj_id_t memory = write_memories[sn_obj_type_id(module, object)]; + assert(memory != SN_INVALID_ID); + sn_write_verilog_memory_write(out, module, object, memory); + } + else if (type == SN_LUT) + sn_write_verilog_lut(out, module, object); + else if (type == SN_GATE) + sn_write_verilog_gate(out, module, object); + else if (sn_obj_type_is_operator(type)) + { + fputs(" assign ", out); + sn_write_verilog_ref(out, module, object); + fputs(" = ", out); + sn_write_verilog_expression(out, module, object); + fputs(";", out); + fputc(10, out); + } + } + fputs("endmodule", out); + fputc(10, out); + fputc(10, out); + free(write_memories); +} + +static inline void sn_module_write_verilog(FILE* out, const sn_module_t* module) +{ + assert(module); + sn_module_write_verilog_as(out, module, sn_name_get(&module->design->names, module->name)); +} + +static inline void sn_design_write_module_verilog(FILE* out, const sn_design_t* design, sn_module_id_t module_id, + const char* emitted_name) +{ + assert(out); + assert(design); + assert(module_id < design->modules.size); + const sn_module_t* module = sn_design_get_module_const(design, module_id); + sn_module_write_verilog_as(out, module, + emitted_name ? emitted_name : sn_name_get(&design->names, module->name)); +} + +static inline void sn_design_write_module_verilog_file(const sn_design_t* design, sn_module_id_t module_id, + const char* emitted_name, const char* path) +{ + assert(path); + FILE* out = fopen(path, "w"); + assert(out); + sn_design_write_module_verilog(out, design, module_id, emitted_name); + fclose(out); +} + +static inline void sn_design_write_module_verilog_deps_rec(FILE* out, const sn_design_t* design, + sn_module_id_t module_id, sn_module_id_t root, + bool* active, bool* written) +{ + assert(out && design && module_id < design->modules.size && active && written); + if (written[module_id]) + return; + assert(!active[module_id]); + active[module_id] = true; + const sn_module_t* module = sn_design_get_module_const(design, module_id); + for (size_t i = 0; i < module->inst_modules.size; i++) + sn_design_write_module_verilog_deps_rec( + out, design, sn_vec_at(sn_module_id_t, &module->inst_modules, i), root, active, written); + active[module_id] = false; + written[module_id] = true; + if (module_id != root) + sn_module_write_verilog(out, module); +} + +// Writes one selected module plus only the module definitions reachable from +// its insts. Dependencies precede the selected module, whose emitted name +// may differ from its internal SN name. This is useful for a collapsed design +// that intentionally retains technology primitive insts. +static inline void sn_design_write_module_verilog_with_deps(FILE* out, const sn_design_t* design, + sn_module_id_t module_id, const char* emitted_name) +{ + assert(out && design && module_id < design->modules.size); + bool* active = (bool*)calloc(design->modules.size, sizeof(bool)); + bool* written = (bool*)calloc(design->modules.size, sizeof(bool)); + assert(active && written); + sn_design_write_module_verilog_deps_rec(out, design, module_id, module_id, active, written); + sn_design_write_module_verilog(out, design, module_id, emitted_name); + free(active); + free(written); +} + +static inline void sn_design_write_module_verilog_with_deps_file(const sn_design_t* design, + sn_module_id_t module_id, + const char* emitted_name, const char* path) +{ + assert(path); + FILE* out = fopen(path, "w"); + assert(out); + sn_design_write_module_verilog_with_deps(out, design, module_id, emitted_name); + fclose(out); +} + +static inline void sn_design_write_verilog(FILE* out, const sn_design_t* design) +{ + assert(out); + assert(design); + for (size_t i = 0; i < design->modules.size; i++) + sn_module_write_verilog(out, sn_design_get_module_const(design, (sn_module_id_t)i)); +} + +static inline void sn_design_write_verilog_file(const sn_design_t* design, const char* path) +{ + assert(design); + assert(path); + FILE* out = fopen(path, "w"); + assert(out); + sn_design_write_verilog(out, design); + fclose(out); +} + +// SN binary format +// ---------------- +// +// The binary representation is a versioned, little-endian semantic dump, not +// a native-memory image. Vector capacities, pointers, and name hash buckets are +// process-local details and are reconstructed when reading. IDs, vector sizes, +// names, constants, object attributes, type-specific attributes, optional +// fanout caches, and duplication maps are preserved exactly. +// +// File order is: +// header; names; constant words; module count; module records. +// A module record follows sn_module_t's semantic field order: +// module flags; core object vectors; fanins; type-object vectors; +// type-specific vectors; fanout vectors; copy map. +// +// Size fields are unsigned 64-bit values. IDs, flags, enum values, and stored +// data words are unsigned 32-bit values. A format change must increment the +// version below. + +#define SN_BINARY_FORMAT_VERSION 6u +#define SN_BINARY_MIN_READ_VERSION 5u + +// The format version covers field-layout changes. This signature additionally binds every serialized object type to +// its numeric value, so reordering the enum cannot silently reinterpret an otherwise same-sized binary design. +static inline uint32_t sn_binary_layout_signature(void) +{ + static const sn_obj_type_t types[] = { + SN_NONE, SN_PI, SN_PO, SN_CONST0, SN_CONST1, SN_CONST, + SN_BUF, SN_FAN, SN_INST, SN_REG_OUT, SN_REG_IN, SN_MEM_OUT, + SN_MEM_IN, SN_MEM_READ, SN_MEM_WRITE, SN_LOOP_OUT, SN_LOOP_IN, SN_POS, + SN_NEG, SN_BIT_NOT, SN_LOG_NOT, SN_REDUCE_AND, SN_REDUCE_NAND, SN_REDUCE_OR, + SN_REDUCE_NOR, SN_REDUCE_XOR, SN_REDUCE_XNOR, SN_ADD, SN_SUB, SN_MUL, + SN_DIV, SN_MOD, SN_POW, SN_BIT_AND, SN_BIT_OR, SN_BIT_XOR, + SN_BIT_XNOR, SN_LOG_AND, SN_LOG_OR, SN_EQ, SN_NE, SN_CASE_EQ, + SN_CASE_NE, SN_WILDCARD_EQ, SN_WILDCARD_NE, SN_LT, SN_LE, SN_GT, + SN_GE, SN_SHL, SN_SHR, SN_ASHL, SN_ASHR, SN_MUX, + SN_BMUX, SN_PMUX, SN_CONCAT, SN_REPLICATE, SN_SLICE, SN_CAST, + SN_LUT, SN_GATE}; + uint32_t hash = UINT32_C(2166136261); + for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); i++) + { + hash ^= types[i]; + hash *= UINT32_C(16777619); + hash ^= (uint32_t)i; + hash *= UINT32_C(16777619); + } + return hash; +} + +typedef struct sn_binary_writer_t +{ + FILE* out; + bool valid; +} sn_binary_writer_t; + +static inline sn_binary_writer_t sn_binary_writer_start(FILE* out) +{ + sn_binary_writer_t writer = {out, out != NULL}; + return writer; +} + +static inline bool sn_binary_write_bytes(sn_binary_writer_t* writer, const void* data, size_t size) +{ + assert(data || !size); + if (!writer || !writer->valid) + return false; + if (size && fwrite(data, 1, size, writer->out) != size) + writer->valid = false; + return writer->valid; +} + +typedef struct sn_binary_reader_t +{ + FILE* in; + uint64_t remaining; + bool valid; +} sn_binary_reader_t; + +static inline sn_binary_reader_t sn_binary_reader_start(FILE* in) +{ + sn_binary_reader_t reader = {in, UINT64_MAX, in != NULL}; +#if defined(_WIN32) + __int64 position; +#else + long position; +#endif + if (!in) + return reader; +#if defined(_WIN32) + position = _ftelli64(in); + if (position >= 0 && _fseeki64(in, 0, SEEK_END) == 0) + { + __int64 end = _ftelli64(in); + if (end >= position && _fseeki64(in, position, SEEK_SET) == 0) +#else + position = ftell(in); + if (position >= 0 && fseek(in, 0, SEEK_END) == 0) + { + long end = ftell(in); + if (end >= position && fseek(in, position, SEEK_SET) == 0) +#endif + reader.remaining = (uint64_t)(end - position); + else + reader.valid = false; + } + else + clearerr(in); + return reader; +} + +static inline bool sn_binary_read_bytes(sn_binary_reader_t* reader, void* data, size_t size) +{ + assert(data || !size); + if (!reader || !reader->valid || (uint64_t)size > reader->remaining) + { + if (reader) + reader->valid = false; + return false; + } + if (size && fread(data, 1, size, reader->in) != size) + { + reader->valid = false; + return false; + } + if (reader->remaining != UINT64_MAX) + reader->remaining -= size; + return true; +} + +static inline void sn_binary_write_u32(sn_binary_writer_t* writer, uint32_t value) +{ + uint8_t bytes[4]; + for (uint32_t i = 0; i < 4; i++) + bytes[i] = (uint8_t)(value >> (8 * i)); + sn_binary_write_bytes(writer, bytes, sizeof(bytes)); +} + +static inline uint32_t sn_binary_read_u32(sn_binary_reader_t* reader) +{ + uint8_t bytes[4] = {0}; + sn_binary_read_bytes(reader, bytes, sizeof(bytes)); + uint32_t value = 0; + for (uint32_t i = 0; i < 4; i++) + value |= (uint32_t)bytes[i] << (8 * i); + return value; +} + +static inline void sn_binary_write_u64(sn_binary_writer_t* writer, uint64_t value) +{ + uint8_t bytes[8]; + for (uint32_t i = 0; i < 8; i++) + bytes[i] = (uint8_t)(value >> (8 * i)); + sn_binary_write_bytes(writer, bytes, sizeof(bytes)); +} + +static inline uint64_t sn_binary_read_u64(sn_binary_reader_t* reader) +{ + uint8_t bytes[8] = {0}; + sn_binary_read_bytes(reader, bytes, sizeof(bytes)); + uint64_t value = 0; + for (uint32_t i = 0; i < 8; i++) + value |= (uint64_t)bytes[i] << (8 * i); + return value; +} + +static inline size_t sn_binary_read_size(sn_binary_reader_t* reader) +{ + uint64_t size = sn_binary_read_u64(reader); + if (size > SIZE_MAX) + { + reader->valid = false; + return 0; + } + return (size_t)size; +} + +static inline bool sn_binary_read_vec_size(sn_binary_reader_t* reader, size_t element_bytes, size_t* size) +{ + *size = sn_binary_read_size(reader); + if (!reader->valid || *size >= SN_INVALID_ID || (element_bytes && *size > reader->remaining / element_bytes)) + { + reader->valid = false; + return false; + } + return true; +} + +static inline void sn_binary_write_u32_vec(sn_binary_writer_t* writer, const sn_vec_t* vec) +{ + assert(vec); + sn_binary_write_u64(writer, vec->size); +#if defined(_WIN32) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + sn_binary_write_bytes(writer, vec->data, vec->size * sizeof(uint32_t)); +#else + for (size_t i = 0; i < vec->size; i++) + sn_binary_write_u32(writer, sn_vec_at(uint32_t, vec, i)); +#endif +} + +static inline void sn_binary_read_u32_vec(sn_binary_reader_t* reader, sn_vec_t* vec) +{ + assert(vec); + size_t size; + if (!sn_binary_read_vec_size(reader, 4, &size)) + return; + sn_vec_resize(uint32_t, vec, size); +#if defined(_WIN32) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + sn_binary_read_bytes(reader, vec->data, size * sizeof(uint32_t)); +#else + for (size_t i = 0; i < size; i++) + sn_vec_at(uint32_t, vec, i) = sn_binary_read_u32(reader); +#endif +} + +// Fanin counts use 16 bits in memory but retain their historical 32-bit binary representation. +static inline void sn_binary_write_fanin_count_vec(sn_binary_writer_t* writer, const sn_vec_t* vec) +{ + assert(vec); + sn_binary_write_u64(writer, vec->size); + for (size_t i = 0; i < vec->size; i++) + sn_binary_write_u32(writer, sn_vec_at(sn_fanin_count_t, vec, i)); +} + +static inline void sn_binary_read_fanin_count_vec(sn_binary_reader_t* reader, sn_vec_t* vec) +{ + assert(vec); + size_t size; + if (!sn_binary_read_vec_size(reader, 4, &size)) + return; + sn_vec_resize(sn_fanin_count_t, vec, size); + for (size_t i = 0; i < size; i++) + { + uint32_t count = sn_binary_read_u32(reader); + if (count > UINT16_MAX) + { + reader->valid = false; + return; + } + sn_vec_at(sn_fanin_count_t, vec, i) = (sn_fanin_count_t)count; + } +} + +static inline void sn_binary_write_u64_vec(sn_binary_writer_t* writer, const sn_vec_t* vec) +{ + assert(vec); + sn_binary_write_u64(writer, vec->size); +#if defined(_WIN32) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + sn_binary_write_bytes(writer, vec->data, vec->size * sizeof(uint64_t)); +#else + for (size_t i = 0; i < vec->size; i++) + sn_binary_write_u64(writer, sn_vec_at(uint64_t, vec, i)); +#endif +} + +static inline void sn_binary_read_u64_vec(sn_binary_reader_t* reader, sn_vec_t* vec) +{ + assert(vec); + size_t size; + if (!sn_binary_read_vec_size(reader, 8, &size)) + return; + sn_vec_resize(uint64_t, vec, size); +#if defined(_WIN32) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + sn_binary_read_bytes(reader, vec->data, size * sizeof(uint64_t)); +#else + for (size_t i = 0; i < size; i++) + sn_vec_at(uint64_t, vec, i) = sn_binary_read_u64(reader); +#endif +} + +static inline void sn_binary_write_type_vec(sn_binary_writer_t* writer, const sn_vec_t* vec) +{ + assert(vec); + sn_binary_write_u64(writer, vec->size); + for (size_t i = 0; i < vec->size; i++) + sn_binary_write_u32(writer, (uint32_t)sn_vec_at(sn_obj_type_t, vec, i)); +} + +static inline void sn_binary_read_type_vec(sn_binary_reader_t* reader, sn_vec_t* vec) +{ + assert(vec); + size_t size; + if (!sn_binary_read_vec_size(reader, 4, &size)) + return; + sn_vec_resize(sn_obj_type_t, vec, size); + for (size_t i = 0; i < size; i++) + { + uint32_t type = sn_binary_read_u32(reader); + if (type >= SN_OBJ_TYPE_COUNT) + { + reader->valid = false; + return; + } + sn_vec_at(sn_obj_type_t, vec, i) = (sn_obj_type_t)type; + } +} + +static inline void sn_binary_write_slice_vec(sn_binary_writer_t* writer, const sn_vec_t* vec) +{ + assert(vec); + sn_binary_write_u64(writer, vec->size); + for (size_t i = 0; i < vec->size; i++) + { + const sn_slice_info_t* info = &sn_vec_at(sn_slice_info_t, vec, i); + sn_binary_write_u32(writer, (uint32_t)info->left_index); + sn_binary_write_u32(writer, (uint32_t)info->right_index); + sn_binary_write_u32(writer, info->flags); + } +} + +static inline void sn_binary_read_slice_vec(sn_binary_reader_t* reader, sn_vec_t* vec) +{ + assert(vec); + size_t size; + if (!sn_binary_read_vec_size(reader, 12, &size)) + return; + sn_vec_resize(sn_slice_info_t, vec, size); + for (size_t i = 0; i < size; i++) + { + sn_slice_info_t* info = &sn_vec_at(sn_slice_info_t, vec, i); + info->left_index = (int32_t)sn_binary_read_u32(reader); + info->right_index = (int32_t)sn_binary_read_u32(reader); + info->flags = sn_binary_read_u32(reader); + } +} + +static inline void sn_module_assert_valid(const sn_module_t* module) +{ + assert(module); + assert(module->design); + assert(module->id < module->design->modules.size); + assert(sn_design_get_module_const(module->design, module->id) == module); + assert(module->name < module->design->names.names.size); + assert((module->flags & ~SN_MODULE_ALL_FLAGS) == 0); + + size_t object_count = module->obj_types.size; + assert(object_count < SN_INVALID_ID); + assert(module->width_signed.size == object_count); + assert(module->fanin_counts.size == object_count); + assert(module->fanin_offsets.size == object_count); + assert(module->type_ids.size == object_count); + assert(module->name_ids.size == object_count); + + size_t expected_fanin_offset = 0; + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_obj_type_t type = sn_vec_at(sn_obj_type_t, &module->obj_types, object); + uint32_t type_id = sn_vec_at(uint32_t, &module->type_ids, object); + uint32_t fanin_count = sn_obj_fanin_count(module, object); + uint32_t fanin_offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + uint32_t name = sn_vec_at(uint32_t, &module->name_ids, object); + assert(type > SN_NONE && type < SN_OBJ_TYPE_COUNT); + assert(type_id < module->type_objects[type].size); + assert(sn_vec_at(sn_obj_id_t, &module->type_objects[type], type_id) == object); + assert(fanin_offset == expected_fanin_offset); + assert(expected_fanin_offset + fanin_count <= module->fanins.size); + assert(name == SN_INVALID_ID || name < module->design->names.names.size); + for (uint32_t i = 0; i < fanin_count; i++) + { + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, fanin_offset + i); + assert(fanin < object_count || + (fanin == SN_INVALID_ID && sn_obj_fanin_may_be_invalid(module, type, i))); + } + expected_fanin_offset += fanin_count; + } + assert(expected_fanin_offset == module->fanins.size); + + if (sn_module_is_blackbox(module)) + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_obj_type_t type = sn_obj_type(module, object); + assert(type == SN_PI || type == SN_PO); + if (type == SN_PO) + assert(sn_obj_fanin_count(module, object) == 1 && + sn_obj_fanin(module, object, 0) == SN_INVALID_ID); + } + + for (uint32_t type = 0; type < SN_OBJ_TYPE_COUNT; type++) + for (uint32_t type_id = 0; type_id < module->type_objects[type].size; type_id++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[type], type_id); + assert(object < object_count); + assert(sn_vec_at(sn_obj_type_t, &module->obj_types, object) == (sn_obj_type_t)type); + assert(sn_vec_at(uint32_t, &module->type_ids, object) == type_id); + } + + assert(module->reg_flags.size == module->type_objects[SN_REG_OUT].size); + assert(module->mem_depths.size == module->type_objects[SN_MEM_OUT].size); + assert(module->inst_modules.size == module->type_objects[SN_INST].size); + assert(module->fan_insts.size == module->type_objects[SN_FAN].size); + assert(module->slice_infos.size == module->type_objects[SN_SLICE].size); + assert(module->repeat_counts.size == module->type_objects[SN_REPLICATE].size); + assert(module->const_word_offsets.size == module->type_objects[SN_CONST].size); + assert(module->lut_truths.size == module->type_objects[SN_LUT].size); + assert(module->gate_ids.size == module->type_objects[SN_GATE].size); + assert(module->type_objects[SN_REG_OUT].size == module->type_objects[SN_REG_IN].size); + assert(module->type_objects[SN_MEM_OUT].size == module->type_objects[SN_MEM_IN].size); + assert(module->type_objects[SN_LOOP_OUT].size == module->type_objects[SN_LOOP_IN].size); + + for (size_t i = 0; i < module->reg_flags.size; i++) + { + assert((sn_vec_at(uint32_t, &module->reg_flags, i) & ~SN_REG_FLAGS_ALL) == 0); + sn_obj_id_t reg = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_REG_OUT], i); + assert(sn_obj_fanin_count(module, reg) == SN_REG_FANIN_COUNT); + sn_obj_id_t data = sn_obj_reg_init_data(module, reg); + sn_obj_id_t mask = sn_obj_reg_init_mask(module, reg); + assert(data != SN_INVALID_ID || mask == SN_INVALID_ID); + if (data != SN_INVALID_ID) + { + sn_obj_type_t type = sn_obj_type(module, data); + assert(type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST); + assert(sn_obj_width(module, data) == sn_obj_width(module, reg)); + } + if (mask != SN_INVALID_ID) + { + sn_obj_type_t type = sn_obj_type(module, mask); + assert(type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST); + assert(sn_obj_width(module, mask) == sn_obj_width(module, reg)); + } + } + for (size_t i = 0; i < module->mem_depths.size; i++) + { + assert(sn_vec_at(uint32_t, &module->mem_depths, i)); + sn_obj_id_t memory = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_OUT], i); + assert(sn_obj_fanin_count(module, memory) == SN_MEM_OUT_FANIN_COUNT); + assert(sn_obj_fanin(module, memory, SN_MEM_STATE) == sn_obj_pair_in(module, memory)); + sn_obj_id_t data = sn_obj_mem_init_data(module, memory); + sn_obj_id_t mask = sn_obj_mem_init_mask(module, memory); + assert(data != SN_INVALID_ID || mask == SN_INVALID_ID); + uint32_t init_width = sn_obj_mem_init_width(module, memory); + if (data != SN_INVALID_ID) + { + sn_obj_type_t type = sn_obj_type(module, data); + assert(type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST); + assert(sn_obj_width(module, data) == init_width); + } + if (mask != SN_INVALID_ID) + { + sn_obj_type_t type = sn_obj_type(module, mask); + assert(type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST); + assert(sn_obj_width(module, mask) == init_width); + } + } + for (size_t i = 0; i < module->inst_modules.size; i++) + assert(sn_vec_at(sn_module_id_t, &module->inst_modules, i) < module->design->modules.size); + for (size_t i = 0; i < module->slice_infos.size; i++) + assert((sn_vec_at(sn_slice_info_t, &module->slice_infos, i).flags & ~SN_SLICE_DESCENDING) == 0); + for (size_t i = 0; i < module->repeat_counts.size; i++) + assert(sn_vec_at(uint32_t, &module->repeat_counts, i)); + + for (size_t i = 0; i < module->const_word_offsets.size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_CONST], i); + uint32_t offset = sn_vec_at(uint32_t, &module->const_word_offsets, i); + assert((size_t)offset + sn_const_word_count(sn_obj_width(module, object)) <= + module->design->constant_words.size); + } + for (size_t i = 0; i < module->lut_truths.size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_LUT], i); + uint32_t fanin_count = sn_obj_fanin_count(module, object); + assert(sn_obj_width(module, object) == 1 && !sn_obj_is_signed(module, object) && fanin_count <= 6); + for (uint32_t k = 0; k < fanin_count; k++) + assert(sn_obj_width(module, sn_obj_fanin(module, object, k)) == 1); + if (fanin_count < 6) + assert((sn_obj_lut_truth(module, object) >> (UINT32_C(1) << fanin_count)) == 0); + } + for (size_t i = 0; i < module->gate_ids.size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_GATE], i); + assert(sn_obj_width(module, object) == 1); + assert(sn_obj_gate_id(module, object) != SN_INVALID_ID); + for (uint32_t j = 0; j < sn_obj_fanin_count(module, object); j++) + assert(sn_obj_width(module, sn_obj_fanin(module, object, j)) == 1); + } + + for (size_t i = 0; i < module->fan_insts.size; i++) + { + sn_obj_id_t fan = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_FAN], i); + sn_obj_id_t inst = sn_vec_at(sn_obj_id_t, &module->fan_insts, i); + assert(inst < object_count); + assert(sn_obj_type(module, inst) == SN_INST); + assert(sn_obj_fanin_count(module, fan) == 1); + assert(sn_obj_fanin(module, fan, 0) == inst); + assert(fan > inst); + uint32_t output_index = fan - inst - 1; + sn_module_id_t child = sn_vec_at(sn_module_id_t, &module->inst_modules, + sn_obj_type_id(module, inst)); + assert(output_index < sn_design_module_output_count(module->design, child)); + } + + if (module->fanouts_valid) + { + assert(module->fanout_counts.size == object_count); + assert(module->fanout_offsets.size == object_count); + size_t offset = 0; + for (sn_obj_id_t object = 0; object < object_count; object++) + { + assert(sn_vec_at(uint32_t, &module->fanout_offsets, object) == offset); + offset += sn_vec_at(uint32_t, &module->fanout_counts, object); + assert(offset <= module->fanouts.size); + } + assert(offset == module->fanouts.size); + for (size_t i = 0; i < module->fanouts.size; i++) + assert(sn_vec_at(sn_obj_id_t, &module->fanouts, i) < object_count); + } + else + { + assert(module->fanout_counts.size == 0); + assert(module->fanout_offsets.size == 0); + assert(module->fanouts.size == 0); + } + + if (module->copy_ids.size) + { + assert(module->copy_ids.size == object_count); + assert(module->copy_module < module->design->modules.size); + size_t copy_count = sn_design_get_module_const(module->design, module->copy_module)->obj_types.size; + for (size_t i = 0; i < module->copy_ids.size; i++) + { + sn_obj_id_t copy = sn_vec_at(sn_obj_id_t, &module->copy_ids, i); + assert(copy == SN_INVALID_ID || copy < copy_count); + } + } + else + assert(module->copy_module == SN_INVALID_ID); +} + +static inline void sn_design_assert_valid(const sn_design_t* design) +{ + assert(design); + assert(design->modules.size < SN_INVALID_ID); + assert(design->names.names.size < SN_INVALID_ID); + assert(design->names.links.size == design->names.names.size); + assert(design->names.buckets.size); + for (size_t i = 0; i < design->names.names.size; i++) + { + const char* name = sn_vec_at(char*, &design->names.names, i); + assert(name); + assert(sn_name_find(&design->names, name) == i); + } + for (sn_module_id_t i = 0; i < design->modules.size; i++) + { + const sn_module_t* module = sn_design_get_module_const(design, i); + assert(module->id == i); + for (sn_module_id_t previous = 0; previous < i; previous++) + assert(module->name != sn_design_get_module_const(design, previous)->name); + sn_module_assert_valid(module); + } +} + +static inline void sn_binary_write_module(sn_binary_writer_t* writer, const sn_module_t* module) +{ + sn_binary_write_u32(writer, module->name); + sn_binary_write_u32(writer, module->flags); + sn_binary_write_u32(writer, module->fanouts_valid ? 1u : 0u); + sn_binary_write_u32(writer, module->interface_locked ? 1u : 0u); + sn_binary_write_u32(writer, module->copy_module); + sn_binary_write_type_vec(writer, &module->obj_types); + sn_binary_write_u32_vec(writer, &module->width_signed); + sn_binary_write_fanin_count_vec(writer, &module->fanin_counts); + sn_binary_write_u32_vec(writer, &module->fanin_offsets); + sn_binary_write_u32_vec(writer, &module->type_ids); + sn_binary_write_u32_vec(writer, &module->name_ids); + sn_binary_write_u32_vec(writer, &module->fanins); + for (uint32_t type = 0; type < SN_OBJ_TYPE_COUNT; type++) + sn_binary_write_u32_vec(writer, &module->type_objects[type]); + sn_binary_write_u32_vec(writer, &module->reg_flags); + sn_binary_write_u32_vec(writer, &module->mem_depths); + sn_binary_write_u32_vec(writer, &module->inst_modules); + sn_binary_write_u32_vec(writer, &module->fan_insts); + sn_binary_write_slice_vec(writer, &module->slice_infos); + sn_binary_write_u32_vec(writer, &module->repeat_counts); + sn_binary_write_u32_vec(writer, &module->const_word_offsets); + sn_binary_write_u64_vec(writer, &module->lut_truths); + sn_binary_write_u32_vec(writer, &module->gate_ids); + sn_binary_write_u32_vec(writer, &module->fanout_counts); + sn_binary_write_u32_vec(writer, &module->fanout_offsets); + sn_binary_write_u32_vec(writer, &module->fanouts); + sn_binary_write_u32_vec(writer, &module->copy_ids); +} + +static inline bool sn_binary_read_module(sn_binary_reader_t* reader, sn_design_t* design, sn_module_id_t expected_id, + uint32_t version, uint8_t* module_name_seen) +{ + sn_name_id_t name = sn_binary_read_u32(reader); + uint32_t flags = version >= 6 ? sn_binary_read_u32(reader) : SN_MODULE_NO_FLAGS; + uint32_t fanouts_valid = sn_binary_read_u32(reader); + uint32_t interface_locked = sn_binary_read_u32(reader); + sn_module_id_t copy_module = sn_binary_read_u32(reader); + if (!reader->valid || name >= design->names.names.size || module_name_seen[name] || + (flags & ~SN_MODULE_ALL_FLAGS) != 0 || fanouts_valid > 1 || interface_locked > 1) + { + reader->valid = false; + return false; + } + + module_name_seen[name] = 1; + sn_module_id_t id = sn_design_add_module_name_id(design, name); + assert(id == expected_id); + sn_module_t* module = sn_design_get_module(design, id); + assert(module->name == name); + module->flags = flags; + module->fanouts_valid = fanouts_valid != 0; + module->interface_locked = interface_locked != 0; + module->copy_module = copy_module; + + sn_binary_read_type_vec(reader, &module->obj_types); + sn_binary_read_u32_vec(reader, &module->width_signed); + sn_binary_read_fanin_count_vec(reader, &module->fanin_counts); + sn_binary_read_u32_vec(reader, &module->fanin_offsets); + sn_binary_read_u32_vec(reader, &module->type_ids); + sn_binary_read_u32_vec(reader, &module->name_ids); + sn_binary_read_u32_vec(reader, &module->fanins); + for (uint32_t type = 0; type < SN_OBJ_TYPE_COUNT; type++) + sn_binary_read_u32_vec(reader, &module->type_objects[type]); + sn_binary_read_u32_vec(reader, &module->reg_flags); + sn_binary_read_u32_vec(reader, &module->mem_depths); + sn_binary_read_u32_vec(reader, &module->inst_modules); + sn_binary_read_u32_vec(reader, &module->fan_insts); + sn_binary_read_slice_vec(reader, &module->slice_infos); + sn_binary_read_u32_vec(reader, &module->repeat_counts); + sn_binary_read_u32_vec(reader, &module->const_word_offsets); + sn_binary_read_u64_vec(reader, &module->lut_truths); + sn_binary_read_u32_vec(reader, &module->gate_ids); + sn_binary_read_u32_vec(reader, &module->fanout_counts); + sn_binary_read_u32_vec(reader, &module->fanout_offsets); + sn_binary_read_u32_vec(reader, &module->fanouts); + sn_binary_read_u32_vec(reader, &module->copy_ids); + return reader->valid; +} + +static inline bool sn_design_write_binary(FILE* out, const sn_design_t* design) +{ + static const uint8_t magic[8] = {'S', 'N', 'B', 'I', 'N', '\r', '\n', 0x1a}; + if (!out || !design) + return false; + sn_design_assert_valid(design); + sn_binary_writer_t writer = sn_binary_writer_start(out); + sn_binary_write_bytes(&writer, magic, sizeof(magic)); + sn_binary_write_u32(&writer, SN_BINARY_FORMAT_VERSION); + sn_binary_write_u32(&writer, sn_binary_layout_signature()); + sn_binary_write_u32(&writer, SN_OBJ_TYPE_COUNT); + sn_binary_write_u32(&writer, SN_REG_FANIN_COUNT); + sn_binary_write_u32(&writer, SN_MEM_OUT_FANIN_COUNT); + + sn_binary_write_u64(&writer, design->names.names.size); + for (size_t i = 0; i < design->names.names.size; i++) + { + const char* name = sn_vec_at(char*, &design->names.names, i); + size_t length = strlen(name); + sn_binary_write_u64(&writer, length); + sn_binary_write_bytes(&writer, name, length); + } + sn_binary_write_u32_vec(&writer, &design->constant_words); + sn_binary_write_u64(&writer, design->modules.size); + for (size_t i = 0; i < design->modules.size; i++) + sn_binary_write_module(&writer, sn_design_get_module_const(design, (sn_module_id_t)i)); + return writer.valid && ferror(out) == 0; +} + +static inline bool sn_design_write_binary_file(const sn_design_t* design, const char* path) +{ + if (!design || !path) + return false; + FILE* out = fopen(path, "wb"); + if (!out) + return false; + bool success = sn_design_write_binary(out, design); + if (fclose(out) != 0) + success = false; + return success; +} + +// This routine validates the binary encoding while reconstructing its vectors, but intentionally does not dereference +// structural IDs or offsets. Callers must pass the result through sn_design_check() before installing or using it. +typedef enum sn_binary_read_status_t +{ + SN_BINARY_READ_OK, + SN_BINARY_READ_IO, + SN_BINARY_READ_MAGIC, + SN_BINARY_READ_VERSION, + SN_BINARY_READ_LAYOUT, + SN_BINARY_READ_MALFORMED +} sn_binary_read_status_t; + +static inline sn_design_t* sn_design_read_binary_raw_status(FILE* in, sn_binary_read_status_t* returned_status, + uint32_t* returned_version) +{ + static const uint8_t expected_magic[8] = {'S', 'N', 'B', 'I', 'N', '\r', '\n', 0x1a}; + sn_binary_read_status_t status = SN_BINARY_READ_OK; + sn_binary_reader_t reader = sn_binary_reader_start(in); + uint8_t magic[8] = {0}; + sn_binary_read_bytes(&reader, magic, sizeof(magic)); + uint32_t version = sn_binary_read_u32(&reader); + uint32_t layout_signature = sn_binary_read_u32(&reader); + uint32_t object_type_count = sn_binary_read_u32(&reader); + uint32_t register_fanin_count = sn_binary_read_u32(&reader); + uint32_t memory_fanin_count = sn_binary_read_u32(&reader); + if (returned_version) + *returned_version = version; + if (!reader.valid) + status = SN_BINARY_READ_IO; + else if (memcmp(magic, expected_magic, sizeof(magic)) != 0) + status = SN_BINARY_READ_MAGIC; + else if (version < SN_BINARY_MIN_READ_VERSION || version > SN_BINARY_FORMAT_VERSION) + status = SN_BINARY_READ_VERSION; + else if (layout_signature != sn_binary_layout_signature() || object_type_count != SN_OBJ_TYPE_COUNT || + register_fanin_count != SN_REG_FANIN_COUNT || + memory_fanin_count != SN_MEM_OUT_FANIN_COUNT) + status = SN_BINARY_READ_LAYOUT; + if (status != SN_BINARY_READ_OK) + { + if (returned_status) + *returned_status = status; + return NULL; + } + + sn_design_t* design = sn_design_create(); + size_t name_count = sn_binary_read_size(&reader); + if (!reader.valid || name_count >= SN_INVALID_ID || name_count > reader.remaining / 8) + reader.valid = false; + for (size_t i = 0; i < name_count; i++) + { + size_t length = sn_binary_read_size(&reader); + if (!reader.valid || length == SIZE_MAX || length > reader.remaining) + { + reader.valid = false; + break; + } + char* name = (char*)malloc(length + 1); + if (!name) + { + reader.valid = false; + break; + } + sn_binary_read_bytes(&reader, name, length); + name[length] = 0; + if (memchr(name, 0, length) != NULL) + { + free(name); + reader.valid = false; + break; + } + sn_name_id_t id = sn_name_intern(&design->names, name); + free(name); + if (id != i) + { + reader.valid = false; + break; + } + } + sn_binary_read_u32_vec(&reader, &design->constant_words); + size_t module_count = sn_binary_read_size(&reader); + if (!reader.valid || module_count >= SN_INVALID_ID || module_count > reader.remaining / 16) + reader.valid = false; + uint8_t* module_name_seen = name_count ? (uint8_t*)calloc(name_count, 1) : NULL; + if (reader.valid && module_count && !module_name_seen) + reader.valid = false; + for (sn_module_id_t i = 0; reader.valid && i < module_count; i++) + sn_binary_read_module(&reader, design, i, version, module_name_seen); + free(module_name_seen); + if (!reader.valid) + { + sn_design_destroy(design); + if (returned_status) + *returned_status = SN_BINARY_READ_MALFORMED; + return NULL; + } + if (returned_status) + *returned_status = SN_BINARY_READ_OK; + return design; +} + +static inline sn_design_t* sn_design_read_binary_raw(FILE* in) +{ + return sn_design_read_binary_raw_status(in, NULL, NULL); +} + +ABC_NAMESPACE_HEADER_END + +#endif // SN_H diff --git a/src/base/sn/snBlast.h b/src/base/sn/snBlast.h new file mode 100644 index 000000000..561defa2a --- /dev/null +++ b/src/base/sn/snBlast.h @@ -0,0 +1,2259 @@ +/**CFile**************************************************************** + + FileName [snBlast.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Bit-blasting flat or hierarchical SN designs into MiniAIG networks.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snBlast.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_BLAST_H +#define SN_BLAST_H + +// Flat combinational SN-to-MiniAIG bit blaster. MiniAIG is intentionally not +// hashed or constant propagated; Mini_AigerWrite() emits the resulting AIGER. + +#include "sn.h" +#include "aig/miniaig/miniaig.h" + +#include +#include +#include +#include +#include + +ABC_NAMESPACE_HEADER_START + +typedef enum sn_blast_mul_mode_t +{ + SN_BLAST_MUL_BAUGH_WOOLEY = 0, + SN_BLAST_MUL_BOOTH = 1 +} sn_blast_mul_mode_t; + +typedef enum sn_blast_mode_t +{ + // State elements are exposed as extra PI/PO pairs and are not marked as latches. + SN_BLAST_COMB = 0, + // State outputs are extra CIs, state inputs are the final COs, and nRegs is set. + SN_BLAST_SEQ = 1, + // Emit the sequential transition relation as a combinational AIG. Synchronous controls are folded into each + // next-state function exactly as in sequential mode, but nRegs remains zero. This is intended for combinational + // equivalence checking of state logic before and after word-level transformations. + SN_BLAST_TRANSITION = 2 +} sn_blast_mode_t; + +static inline bool sn_blast_mode_has_transition(sn_blast_mode_t mode) +{ + return mode == SN_BLAST_SEQ || mode == SN_BLAST_TRANSITION; +} + +typedef struct sn_blast_options_t +{ + sn_blast_mul_mode_t mul_mode; + bool ripple_adders; + bool delay_comparators; + bool abstract_memories; + bool abstract_multipliers; + // Treat every child instance as a combinational boundary. This derives one natural module partition while + // preserving the hierarchy and is used by module-by-module logic mapping. + bool abstract_instances; + bool expose_register_controls; + sn_blast_mode_t mode; +} sn_blast_options_t; + +static inline sn_blast_options_t sn_blast_default_options(void) +{ + sn_blast_options_t options = {SN_BLAST_MUL_BAUGH_WOOLEY, false, true, true, true, false, true, SN_BLAST_COMB}; + return options; +} + +// Latches cannot be represented by MiniAIG's edge-triggered register convention. Command-level clients use this +// query to reject a reachable latch before constructing an AIG or modifying any saved extraction state. +static inline sn_module_id_t sn_design_find_reachable_latch(const sn_design_t* design, sn_module_id_t root, + sn_obj_id_t* returned_latch) +{ + assert(design && root < design->modules.size); + uint8_t* seen = (uint8_t*)calloc(design->modules.size, 1); + sn_module_id_t* pending = (sn_module_id_t*)malloc(sizeof(sn_module_id_t) * design->modules.size); + size_t pending_count = 0; + assert(seen && pending); + seen[root] = 1; + pending[pending_count++] = root; + while (pending_count) + { + sn_module_id_t module_id = pending[--pending_count]; + const sn_module_t* module = sn_design_get_module_const(design, module_id); + if (!sn_module_is_technology_primitive(module)) + for (size_t i = 0; i < module->reg_flags.size; i++) + if (sn_vec_at(uint32_t, &module->reg_flags, i) & SN_REG_LATCH) + { + if (returned_latch) + *returned_latch = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_REG_OUT], i); + free(pending); + free(seen); + return module_id; + } + for (size_t i = 0; i < module->inst_modules.size; i++) + { + sn_module_id_t child = sn_vec_at(sn_module_id_t, &module->inst_modules, i); + assert(child < design->modules.size); + if (!seen[child]) + { + seen[child] = 1; + pending[pending_count++] = child; + } + } + } + free(pending); + free(seen); + if (returned_latch) + *returned_latch = SN_INVALID_ID; + return SN_INVALID_ID; +} + +typedef struct sn_blast_ctx_t sn_blast_ctx_t; +typedef int* (*sn_blast_special_eval_fn)(sn_blast_ctx_t* ctx, sn_obj_id_t object); + +struct sn_blast_ctx_t +{ + const sn_module_t* module; + Mini_Aig_t* aig; + sn_blast_options_t options; + int** bits; + uint8_t* state; + sn_blast_special_eval_fn special_eval; + void* special_data; +}; + +static inline int* sn_blast_eval(sn_blast_ctx_t* ctx, sn_obj_id_t object); + +static inline bool sn_blast_reg_init_bit(const sn_module_t* module, sn_obj_id_t reg_out, uint32_t bit) +{ + sn_obj_id_t data = sn_obj_reg_init_data(module, reg_out); + sn_obj_id_t mask = sn_obj_reg_init_mask(module, reg_out); + assert(bit < sn_obj_width(module, reg_out)); + if (data == SN_INVALID_ID) + return false; + return (mask == SN_INVALID_ID || sn_const_bit(module, mask, bit)) && sn_const_bit(module, data, bit); +} + +static inline int* sn_blast_alloc_bits(uint32_t width) +{ + assert(width); + int* bits = (int*)malloc(sizeof(int) * width); + assert(bits); + return bits; +} + +static inline void sn_blast_copy(int* dst, const int* src, uint32_t width) +{ + for (uint32_t i = 0; i < width; i++) + dst[i] = src[i]; +} + +static inline int sn_blast_bit(const sn_blast_ctx_t* ctx, sn_obj_id_t object, uint32_t bit) +{ + const sn_module_t* module = ctx->module; + assert(object < module->obj_types.size); + assert(bit < sn_obj_width(module, object)); + return ctx->bits[object][bit]; +} + +static inline int sn_blast_fill_bit(const sn_blast_ctx_t* ctx, sn_obj_id_t object, uint32_t bit, bool sign) +{ + uint32_t width = sn_obj_width(ctx->module, object); + if (bit < width) + return sn_blast_bit(ctx, object, bit); + return sign ? sn_blast_bit(ctx, object, width - 1) : Mini_AigLitConst0(); +} + +static inline int* sn_blast_extend(sn_blast_ctx_t* ctx, sn_obj_id_t object, uint32_t width, bool sign) +{ + sn_blast_eval(ctx, object); + int* result = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + result[i] = sn_blast_fill_bit(ctx, object, i, sign); + return result; +} + +static inline int sn_blast_or(Mini_Aig_t* aig, const int* bits, uint32_t width) +{ + assert(width); + assert(width <= INT_MAX); + int* temp = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + temp[i] = Mini_AigLitNot(bits[i]); + int result = Mini_AigLitNot(Mini_AigAndMulti(aig, temp, (int)width)); + free(temp); + return result; +} + +static inline int sn_blast_and(Mini_Aig_t* aig, const int* bits, uint32_t width) +{ + assert(width); + assert(width <= INT_MAX); + int* temp = sn_blast_alloc_bits(width); + sn_blast_copy(temp, bits, width); + int result = Mini_AigAndMulti(aig, temp, (int)width); + free(temp); + return result; +} + +static inline int sn_blast_xor(Mini_Aig_t* aig, const int* bits, uint32_t width) +{ + assert(width); + assert(width <= INT_MAX); + int* temp = sn_blast_alloc_bits(width); + sn_blast_copy(temp, bits, width); + int result = Mini_AigXorMulti(aig, temp, (int)width); + free(temp); + return result; +} + +static inline int sn_blast_lut_rec(Mini_Aig_t* aig, const int* inputs, uint32_t count, uint64_t truth) +{ + if (count == 0) + return (truth & 1) ? Mini_AigLitConst1() : Mini_AigLitConst0(); + uint32_t half = UINT32_C(1) << (count - 1); + uint64_t mask = half == 32 ? UINT32_MAX : (UINT64_C(1) << half) - 1; + int zero = sn_blast_lut_rec(aig, inputs, count - 1, truth & mask); + int one = sn_blast_lut_rec(aig, inputs, count - 1, truth >> half); + return Mini_AigMux(aig, inputs[count - 1], one, zero); +} + +// Seven-node full adder from Wlc_BlastFullAdder(). Complement propagation handles a constant-one input without +// introducing avoidable logic. The generic construction creates exactly seven unstrashed MiniAIG AND nodes. +static inline void sn_blast_full_adder(Mini_Aig_t* aig, int a, int b, int c, int* carry, int* sum) +{ + bool complement = a == Mini_AigLitConst1() || b == Mini_AigLitConst1() || c == Mini_AigLitConst1(); + if (complement) + { + a = Mini_AigLitNot(a); + b = Mini_AigLitNot(b); + c = Mini_AigLitNot(c); + } + int and1 = Mini_AigAnd(aig, a, b); + int and1n = Mini_AigAnd(aig, Mini_AigLitNot(a), Mini_AigLitNot(b)); + int x_ab = Mini_AigAnd(aig, Mini_AigLitNot(and1), Mini_AigLitNot(and1n)); + int and2 = Mini_AigAnd(aig, c, x_ab); + int and2n = Mini_AigAnd(aig, Mini_AigLitNot(c), Mini_AigLitNot(x_ab)); + *sum = Mini_AigAnd(aig, Mini_AigLitNot(and2), Mini_AigLitNot(and2n)); + *carry = Mini_AigOr(aig, and1, and2); + if (complement) + { + *sum = Mini_AigLitNot(*sum); + *carry = Mini_AigLitNot(*carry); + } +} + +static inline void sn_blast_add_inplace_ripple(Mini_Aig_t* aig, int* dst, const int* add, uint32_t width, + bool subtract) +{ + int carry = subtract ? Mini_AigLitConst1() : Mini_AigLitConst0(); + for (uint32_t i = 0; i < width; i++) + { + int value = subtract ? Mini_AigLitNot(add[i]) : add[i]; + sn_blast_full_adder(aig, dst[i], value, carry, &carry, &dst[i]); + } +} + +// XOR using an existing a & b node. This is the polarity used by Wlc_BlastFullAdder() and saves one AIG node +// whenever an adder generate or propagate-carry term is already available. +static inline int sn_blast_xor_with_and(Mini_Aig_t* aig, int a, int b, int and_ab) +{ + int and_neither = Mini_AigAnd(aig, Mini_AigLitNot(a), Mini_AigLitNot(b)); + return Mini_AigAnd(aig, Mini_AigLitNot(and_ab), Mini_AigLitNot(and_neither)); +} + +// Brent-Kung parallel-prefix addition. The prefix pairs are (propagate, generate), and the two sweeps follow the +// topology used by ABC's &genadder -b. Inputs and results remain in SN's LSB-first significance order. +static inline void sn_blast_add_inplace_brent_kung(Mini_Aig_t* aig, int* dst, const int* add, uint32_t width, + bool subtract) +{ + // A - B is A + ~B + 1: complement the second operand and use a constant-one carry-in. Folding this carry + // into the bit-0 generate before the prefix sweeps avoids adding it separately to every group carry. + bool carry_in = subtract; + uint32_t prefix_width = width - 1; + int* props = sn_blast_alloc_bits(width); + int* group_props = prefix_width ? sn_blast_alloc_bits(prefix_width) : NULL; + int* group_gens = prefix_width ? sn_blast_alloc_bits(prefix_width) : NULL; + int* local_terms = prefix_width ? sn_blast_alloc_bits(prefix_width) : NULL; + for (uint32_t i = 0; i < width; i++) + { + int value = carry_in ? Mini_AigLitNot(add[i]) : add[i]; + if (i < prefix_width) + { + int generate = Mini_AigAnd(aig, dst[i], value); + props[i] = sn_blast_xor_with_and(aig, dst[i], value, generate); + group_props[i] = props[i]; + group_gens[i] = i == 0 && carry_in ? Mini_AigOr(aig, dst[i], value) : generate; + local_terms[i] = -1; + } + else + props[i] = Mini_AigXor(aig, dst[i], value); + } + + // The carry leaving the most-significant result bit is discarded, so construct prefixes only through bit + // width - 2. Record the complete Brent-Kung schedule first. A group-propagate output is useful only when a + // later operation updates the same target; omitting all other propagate outputs removes dead prefix logic. + uint32_t* targets = prefix_width ? (uint32_t*)malloc(sizeof(uint32_t) * 2 * prefix_width) : NULL; + uint32_t* lowers = prefix_width ? (uint32_t*)malloc(sizeof(uint32_t) * 2 * prefix_width) : NULL; + uint32_t* last_target = prefix_width ? (uint32_t*)malloc(sizeof(uint32_t) * prefix_width) : NULL; + assert(!prefix_width || (targets && lowers && last_target)); + for (uint32_t i = 0; i < prefix_width; i++) + last_target[i] = UINT32_MAX; + + uint32_t operation_count = 0; + uint64_t step; + for (step = 2; step / 2 < prefix_width; step <<= 1) + for (uint64_t i = step - 1; i < prefix_width; i += step) + { + assert(operation_count < 2 * prefix_width); + targets[operation_count] = (uint32_t)i; + lowers[operation_count++] = (uint32_t)(i - step / 2); + } + for (step >>= 1; step >= 2; step >>= 1) + for (uint64_t i = 3 * step / 2 - 1; i < prefix_width; i += step) + { + assert(operation_count < 2 * prefix_width); + targets[operation_count] = (uint32_t)i; + lowers[operation_count++] = (uint32_t)(i - step / 2); + } + for (uint32_t i = 0; i < operation_count; i++) + last_target[targets[i]] = i; + for (uint32_t i = 0; i < operation_count; i++) + { + uint32_t target = targets[i], lower = lowers[i]; + int term = Mini_AigAnd(aig, group_props[target], group_gens[lower]); + if (target == lower + 1 && (last_target[lower] == UINT32_MAX || last_target[lower] < i)) + local_terms[target] = term; + group_gens[target] = Mini_AigOr(aig, group_gens[target], term); + if (last_target[target] != i) + group_props[target] = Mini_AigAnd(aig, group_props[target], group_props[lower]); + } + + for (uint32_t i = 0; i < width; i++) + if (i == 0) + dst[i] = carry_in ? Mini_AigLitNot(props[i]) : props[i]; + else if (i < prefix_width && local_terms[i] >= 0) + dst[i] = sn_blast_xor_with_and(aig, props[i], group_gens[i - 1], local_terms[i]); + else + dst[i] = Mini_AigXor(aig, props[i], group_gens[i - 1]); + free(last_target); + free(lowers); + free(targets); + free(local_terms); + free(group_gens); + free(group_props); + free(props); +} + +static inline void sn_blast_add_inplace(Mini_Aig_t* aig, int* dst, const int* add, uint32_t width, bool subtract, + bool ripple) +{ + if (ripple) + sn_blast_add_inplace_ripple(aig, dst, add, width, subtract); + else + sn_blast_add_inplace_brent_kung(aig, dst, add, width, subtract); +} + +// Minimum-node comparator topology used by ABC's &gencomp. The construction computes a > b and consumes vectors in +// SN's LSB-first significance order. Signed comparison removes the sign bits, then selects b's sign when they differ. +static inline int sn_blast_gt(Mini_Aig_t* aig, const int* a, const int* b, uint32_t width, bool signed_compare) +{ + assert(width > 0); + uint32_t compare_width = width; + int signs_differ = Mini_AigLitConst0(); + int b_sign = Mini_AigLitConst0(); + if (signed_compare) + { + compare_width--; + signs_differ = Mini_AigXor(aig, a[compare_width], b[compare_width]); + b_sign = b[compare_width]; + } + + int result = compare_width ? Mini_AigLitConst1() : Mini_AigLitConst0(); + for (uint32_t i = 0; i < compare_width; i++) + { + int bit_a0 = a[i], bit_b0 = b[i]; + int bit_a1 = i + 1 < compare_width ? a[i + 1] : Mini_AigLitConst0(); + int bit_b1 = i + 1 < compare_width ? b[i + 1] : Mini_AigLitConst0(); + bool odd = (i & 1) != 0; + int term0 = i == 0 + ? Mini_AigOr(aig, odd ? bit_a0 : Mini_AigLitNot(bit_a0), + odd ? Mini_AigLitNot(bit_b0) : bit_b0) + : Mini_AigAnd(aig, odd ? bit_a0 : Mini_AigLitNot(bit_a0), + odd ? Mini_AigLitNot(bit_b0) : bit_b0); + int term1 = Mini_AigAnd(aig, odd ? bit_a1 : Mini_AigLitNot(bit_a1), + odd ? Mini_AigLitNot(bit_b1) : bit_b1); + result = Mini_AigOr(aig, Mini_AigLitNot(result), Mini_AigOr(aig, term0, term1)); + } + result = (compare_width & 1) ? Mini_AigLitNot(result) : result; + return signed_compare ? Mini_AigMux(aig, signs_differ, b_sign, result) : result; +} + +// Delay-oriented comparator. Each bit produces a greater-than generate and an equality propagate. Adjacent ranges +// are combined from least to most significant in a balanced tree: G = G_high | (E_high & G_low), +// E = E_high & E_low. Complementing both sign bits converts signed ordering into unsigned ordering. +static inline int sn_blast_gt_delay(Mini_Aig_t* aig, const int* a, const int* b, uint32_t width, bool signed_compare) +{ + assert(width > 0); + int* generates = sn_blast_alloc_bits(width); + int* equals = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + { + int bit_a = signed_compare && i + 1 == width ? Mini_AigLitNot(a[i]) : a[i]; + int bit_b = signed_compare && i + 1 == width ? Mini_AigLitNot(b[i]) : b[i]; + generates[i] = Mini_AigAnd(aig, bit_a, Mini_AigLitNot(bit_b)); + equals[i] = Mini_AigLitNot(Mini_AigXor(aig, bit_a, bit_b)); + } + for (uint32_t count = width; count > 1; count = (count + 1) / 2) + { + uint32_t output = 0; + for (uint32_t i = 0; i < count; i += 2, output++) + { + if (i + 1 == count) + { + generates[output] = generates[i]; + equals[output] = equals[i]; + continue; + } + int low_generate = generates[i]; + int high_generate = generates[i + 1]; + int high_equal = equals[i + 1]; + generates[output] = Mini_AigOr(aig, high_generate, Mini_AigAnd(aig, high_equal, low_generate)); + equals[output] = Mini_AigAnd(aig, high_equal, equals[i]); + } + } + int result = generates[0]; + free(generates); + free(equals); + return result; +} + +static inline int sn_blast_eq_bits(Mini_Aig_t* aig, const int* a, const int* b, uint32_t width) +{ + assert(width && width <= INT_MAX); + int* temp = sn_blast_alloc_bits(width); + uint32_t count = 0; + for (uint32_t i = 0; i < width; i++) + { + int equal; + if (a[i] == Mini_AigLitConst0()) + equal = Mini_AigLitNot(b[i]); + else if (a[i] == Mini_AigLitConst1()) + equal = b[i]; + else if (b[i] == Mini_AigLitConst0()) + equal = Mini_AigLitNot(a[i]); + else if (b[i] == Mini_AigLitConst1()) + equal = a[i]; + else if (a[i] == b[i]) + equal = Mini_AigLitConst1(); + else if (a[i] == Mini_AigLitNot(b[i])) + equal = Mini_AigLitConst0(); + else + equal = Mini_AigLitNot(Mini_AigXor(aig, a[i], b[i])); + if (equal == Mini_AigLitConst0()) + { + free(temp); + return Mini_AigLitConst0(); + } + if (equal != Mini_AigLitConst1()) + temp[count++] = equal; + } + int result = count ? Mini_AigAndMulti(aig, temp, (int)count) : Mini_AigLitConst1(); + free(temp); + return result; +} + +static inline int* sn_blast_add_vectors(Mini_Aig_t* aig, const int* a, const int* b, uint32_t width, bool subtract, + bool ripple) +{ + int* result = sn_blast_alloc_bits(width); + sn_blast_copy(result, a, width); + sn_blast_add_inplace(aig, result, b, width, subtract, ripple); + return result; +} + +static inline int* sn_blast_negate_vector(Mini_Aig_t* aig, const int* value, uint32_t width, bool ripple) +{ + if (!ripple) + { + int* result = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + result[i] = Mini_AigLitConst0(); + sn_blast_add_inplace_brent_kung(aig, result, value, width, true); + return result; + } + int* result = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + result[i] = Mini_AigLitNot(value[i]); + int carry = Mini_AigLitConst1(); + for (uint32_t i = 0; i < width; i++) + { + int old = result[i]; + result[i] = Mini_AigXor(aig, old, carry); + carry = Mini_AigAnd(aig, old, carry); + } + return result; +} + +static inline int* sn_blast_mux_tree(Mini_Aig_t* aig, int* select, uint32_t select_width, const int* alternatives, + uint32_t output_width) +{ + assert(select_width < 31); + int* result = sn_blast_alloc_bits(output_width); + uint32_t count = 1u << select_width; + for (uint32_t bit = 0; bit < output_width; bit++) + { + int* values = sn_blast_alloc_bits(count); + for (uint32_t i = 0; i < count; i++) + values[i] = alternatives[i * output_width + bit]; + result[bit] = Mini_AigMuxMulti(aig, select, (int)select_width, values, (int)count); + free(values); + } + return result; +} + +static inline int sn_blast_mux_simplified(Mini_Aig_t* aig, int select, int one, int zero) +{ + if (one == zero) + return one; + if (select == Mini_AigLitConst0()) + return zero; + if (select == Mini_AigLitConst1()) + return one; + if (one == Mini_AigLitConst1() && zero == Mini_AigLitConst0()) + return select; + if (one == Mini_AigLitConst0() && zero == Mini_AigLitConst1()) + return Mini_AigLitNot(select); + if (zero == Mini_AigLitConst0()) + return Mini_AigAnd(aig, select, one); + if (one == Mini_AigLitConst0()) + return Mini_AigAnd(aig, Mini_AigLitNot(select), zero); + if (one == Mini_AigLitConst1()) + return Mini_AigOr(aig, select, zero); + if (zero == Mini_AigLitConst1()) + return Mini_AigOr(aig, Mini_AigLitNot(select), one); + return Mini_AigMux(aig, select, one, zero); +} + +// A constant table is read one output bit at a time. This bounds temporary storage by the table entry count rather +// than its full packed bit count and removes constant/equal mux branches before they enter the unhashed MiniAIG. +static inline int* sn_blast_const_mux_tree(Mini_Aig_t* aig, const sn_module_t* module, sn_obj_id_t table, + int* select, uint32_t select_width, uint32_t output_width) +{ + assert(select_width < 31); + uint32_t count = 1u << select_width; + int* values = sn_blast_alloc_bits(count); + int* result = sn_blast_alloc_bits(output_width); + for (uint32_t bit = 0; bit < output_width; bit++) + { + for (uint32_t i = 0; i < count; i++) + values[i] = sn_const_bit(module, table, i * output_width + bit) ? Mini_AigLitConst1() + : Mini_AigLitConst0(); + uint32_t value_count = count; + for (uint32_t stage = 0; stage < select_width; stage++) + { + for (uint32_t i = 0; i < value_count / 2; i++) + values[i] = sn_blast_mux_simplified(aig, select[stage], values[2 * i + 1], values[2 * i]); + value_count /= 2; + } + assert(value_count == 1); + result[bit] = values[0]; + } + free(values); + return result; +} + +typedef struct sn_blast_column_t +{ + int* values; + uint32_t* levels; + uint32_t size; + uint32_t cap; +} sn_blast_column_t; + +static inline void sn_blast_column_push(sn_blast_column_t* column, int literal, uint32_t level) +{ + if (column->size == column->cap) + { + column->cap = column->cap ? 2 * column->cap : 8; + column->values = (int*)realloc(column->values, sizeof(int) * column->cap); + column->levels = (uint32_t*)realloc(column->levels, sizeof(uint32_t) * column->cap); + assert(column->values); + assert(column->levels); + } + uint32_t i = column->size++; + while (i && column->levels[i - 1] < level) + { + column->values[i] = column->values[i - 1]; + column->levels[i] = column->levels[i - 1]; + i--; + } + column->values[i] = literal; + column->levels[i] = level; +} + +static inline sn_blast_column_t* sn_blast_columns_alloc(uint32_t count, uint32_t cap) +{ + sn_blast_column_t* columns = (sn_blast_column_t*)calloc(count, sizeof(*columns)); + assert(columns); + for (uint32_t i = 0; i < count; i++) + { + columns[i].cap = cap; + columns[i].values = (int*)malloc(sizeof(int) * cap); + columns[i].levels = (uint32_t*)malloc(sizeof(uint32_t) * cap); + assert(columns[i].values); + assert(columns[i].levels); + } + return columns; +} + +static inline void sn_blast_columns_free(sn_blast_column_t* columns, uint32_t count) +{ + for (uint32_t i = 0; i < count; i++) + { + free(columns[i].values); + free(columns[i].levels); + } + free(columns); +} + +// Reduces a partial-product matrix by always combining the three least-deep signals in each column. This is the +// Mini_Aig counterpart of Wlc_BlastReduceMatrix(): sum and carry depths are tracked explicitly and inserted back in +// level order, preventing carries from creating a serial diagonal chain through the matrix. +static inline int* sn_blast_reduce_columns(Mini_Aig_t* aig, sn_blast_column_t* columns, uint32_t width, bool ripple) +{ + int* row0 = sn_blast_alloc_bits(width); + int* row1 = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + { + sn_blast_column_t* column = &columns[i]; + while (column->size > 2) + { + uint32_t level0 = column->levels[--column->size]; + int value0 = column->values[column->size]; + uint32_t level1 = column->levels[--column->size]; + int value1 = column->values[column->size]; + uint32_t level2 = column->levels[--column->size]; + int value2 = column->values[column->size]; + uint32_t level = level0 > level1 ? level0 : level1; + level = level > level2 ? level : level2; + int carry, sum; + sn_blast_full_adder(aig, value0, value1, value2, &carry, &sum); + sn_blast_column_push(column, sum, level + 2); + sn_blast_column_push(&columns[i + 1], carry, level + 1); + } + row0[i] = column->size ? column->values[0] : Mini_AigLitConst0(); + row1[i] = column->size == 2 ? column->values[1] : Mini_AigLitConst0(); + } + sn_blast_add_inplace(aig, row0, row1, width, false, ripple); + free(row1); + return row0; +} + +static inline int* sn_blast_mul_operand(const int* value, bool is_signed, uint32_t width, uint32_t work) +{ + int* result = sn_blast_alloc_bits(work); + for (uint32_t i = 0; i < work; i++) + result[i] = i < width ? value[i] : (is_signed ? value[width - 1] : Mini_AigLitConst0()); + return result; +} + +static inline int* sn_blast_mul_baugh_wooley(Mini_Aig_t* aig, const int* a, bool a_signed, uint32_t a_width, + const int* b, bool b_signed, uint32_t b_width, uint32_t result_width, + bool ripple) +{ + // Unsigned and mixed multiplication use the partial-product matrix directly. Baugh-Wooley operates on two signed + // operands at their original widths, without sign-extending either input. + bool signed_matrix = a_signed && b_signed; + uint32_t a_work = a_width; + uint32_t b_work = b_width; + uint32_t product_width = a_work + b_work; + uint32_t column_count = product_width + 1; + uint32_t cap = (a_work < b_work ? a_work : b_work) + 8; + int* aa = sn_blast_mul_operand(a, a_signed, a_width, a_work); + int* bb = sn_blast_mul_operand(b, b_signed, b_width, b_work); + sn_blast_column_t* columns = sn_blast_columns_alloc(column_count, cap); + for (uint32_t i = 0; i < a_work; i++) + for (uint32_t j = 0; j < b_work; j++) + { + bool complement = signed_matrix && ((i + 1 == a_work) != (j + 1 == b_work)); + int product = Mini_AigAnd(aig, aa[i], bb[j]); + sn_blast_column_push(&columns[i + j], complement ? Mini_AigLitNot(product) : product, 0); + } + if (signed_matrix) + { + sn_blast_column_push(&columns[a_work - 1], Mini_AigLitConst1(), 0); + sn_blast_column_push(&columns[b_work - 1], Mini_AigLitConst1(), 0); + sn_blast_column_push(&columns[product_width - 1], Mini_AigLitConst1(), 0); + } + int* product = sn_blast_reduce_columns(aig, columns, product_width, ripple); + int* result = sn_blast_alloc_bits(result_width); + for (uint32_t i = 0; i < result_width; i++) + result[i] = i < product_width ? product[i] + : (signed_matrix ? product[product_width - 1] : Mini_AigLitConst0()); + free(product); + free(aa); + free(bb); + sn_blast_columns_free(columns, column_count); + return result; +} + +static inline int* sn_blast_mul_booth(Mini_Aig_t* aig, const int* a, bool a_signed, uint32_t a_width, + const int* b, bool b_signed, uint32_t b_width, uint32_t result_width, + bool ripple) +{ + bool signed_multiply = a_signed && b_signed; + uint32_t common_width = a_width > b_width ? a_width : b_width; + uint32_t a_constants = 0, b_constants = 0; + for (uint32_t i = 0; i < common_width; i++) + { + int a_bit = i < a_width ? a[i] : (signed_multiply ? a[a_width - 1] : Mini_AigLitConst0()); + int b_bit = i < b_width ? b[i] : (signed_multiply ? b[b_width - 1] : Mini_AigLitConst0()); + a_constants += Mini_AigLitIsConst(a_bit); + b_constants += Mini_AigLitIsConst(b_bit); + } + if (a_constants < b_constants) + { + const int* temp_value = a; + uint32_t temp_width = a_width; + a = b; + a_width = b_width; + b = temp_value; + b_width = temp_width; + } + + uint32_t product_width = a_width + b_width; + uint32_t matrix_width = product_width + 3; + uint32_t column_count = matrix_width + 1; + uint32_t cap = a_width + 8; + sn_blast_column_t* columns = sn_blast_columns_alloc(column_count, cap); + int fill_a = signed_multiply ? a[a_width - 1] : Mini_AigLitConst0(); + int fill_b = signed_multiply ? b[b_width - 1] : Mini_AigLitConst0(); + int* extended_b = sn_blast_alloc_bits(b_width + 4); + uint32_t extended_size = 0; + extended_b[extended_size++] = Mini_AigLitConst0(); + for (uint32_t i = 0; i < b_width; i++) + extended_b[extended_size++] = b[i]; + if (!signed_multiply) + { + extended_b[extended_size++] = fill_b; + extended_b[extended_size++] = fill_b; + } + if ((extended_size & 1) == 0) + extended_b[extended_size++] = fill_b; + assert(extended_size & 1); + for (uint32_t k = 0; k + 2 < extended_size; k += 2) + { + int q_minus = extended_b[k]; + int q = extended_b[k + 1]; + int q_plus = extended_b[k + 2]; + int negative = q_plus; + int one = Mini_AigXor(aig, q, q_minus); + int two = Mini_AigMux(aig, negative, + Mini_AigAnd(aig, Mini_AigLitNot(q), Mini_AigLitNot(q_minus)), + Mini_AigAnd(aig, q, q_minus)); + int partial = Mini_AigLitConst0(); + uint32_t i; + for (i = 0; i <= a_width; i++) + { + int current = i == a_width ? fill_a : a[i]; + int previous = i ? a[i - 1] : Mini_AigLitConst0(); + int part = Mini_AigOr(aig, Mini_AigAnd(aig, one, current), Mini_AigAnd(aig, two, previous)); + partial = Mini_AigXor(aig, part, negative); + if (partial != Mini_AigLitConst0() && !(signed_multiply && i == a_width)) + sn_blast_column_push(&columns[k + i], partial, 0); + } + if (signed_multiply) + i--; + int sign = signed_multiply ? partial : negative; + if (k == 0) + { + sn_blast_column_push(&columns[k + i], sign, 0); + sn_blast_column_push(&columns[k + i + 1], sign, 0); + if (sign != Mini_AigLitConst1()) + sn_blast_column_push(&columns[k + i + 2], Mini_AigLitNot(sign), 0); + } + else + { + if (sign != Mini_AigLitConst1()) + sn_blast_column_push(&columns[k + i], Mini_AigLitNot(sign), 0); + sn_blast_column_push(&columns[k + i + 1], Mini_AigLitConst1(), 0); + } + if (negative != Mini_AigLitConst0()) + sn_blast_column_push(&columns[k], negative, 0); + } + int* product = sn_blast_reduce_columns(aig, columns, matrix_width, ripple); + int* result = sn_blast_alloc_bits(result_width); + for (uint32_t i = 0; i < result_width; i++) + result[i] = i < product_width ? product[i] + : (signed_multiply ? product[product_width - 1] : Mini_AigLitConst0()); + free(extended_b); + free(product); + sn_blast_columns_free(columns, column_count); + return result; +} + +static inline int* sn_blast_div_vectors(Mini_Aig_t* aig, const int* dividend, const int* divisor, uint32_t width, + bool signed_operands, bool remainder_result, bool ripple, + bool delay_comparators) +{ + int* a = sn_blast_alloc_bits(width); + int* b = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + { + a[i] = dividend[i]; + b[i] = divisor[i]; + } + int sign_a = signed_operands ? a[width - 1] : 0; + int sign_b = signed_operands ? b[width - 1] : 0; + if (signed_operands) + { + int* neg_a = sn_blast_negate_vector(aig, a, width, ripple); + int* neg_b = sn_blast_negate_vector(aig, b, width, ripple); + for (uint32_t i = 0; i < width; i++) + { + a[i] = Mini_AigMux(aig, sign_a, neg_a[i], a[i]); + b[i] = Mini_AigMux(aig, sign_b, neg_b[i], b[i]); + } + free(neg_a); + free(neg_b); + } + int* rem = sn_blast_alloc_bits(width + 1); + int* div = sn_blast_alloc_bits(width + 1); + int* quotient = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i <= width; i++) + { + rem[i] = 0; + div[i] = i < width ? b[i] : 0; + } + for (uint32_t i = 0; i < width; i++) + quotient[i] = 0; + int divisor_zero = Mini_AigLitNot(sn_blast_or(aig, b, width)); + for (uint32_t i = width; i-- > 0;) + { + int* shifted = sn_blast_alloc_bits(width + 1); + shifted[0] = a[i]; + for (uint32_t k = 1; k <= width; k++) + shifted[k] = rem[k - 1]; + int less = delay_comparators ? sn_blast_gt_delay(aig, div, shifted, width + 1, false) + : sn_blast_gt(aig, div, shifted, width + 1, false); + int ge = Mini_AigLitNot(less); + int* difference = sn_blast_alloc_bits(width + 1); + sn_blast_copy(difference, shifted, width + 1); + sn_blast_add_inplace(aig, difference, div, width + 1, true, ripple); + for (uint32_t k = 0; k <= width; k++) + rem[k] = Mini_AigMux(aig, ge, difference[k], shifted[k]); + quotient[i] = Mini_AigMux(aig, divisor_zero, Mini_AigLitConst1(), ge); + free(shifted); + free(difference); + } + int* result = sn_blast_alloc_bits(width); + if (remainder_result) + for (uint32_t i = 0; i < width; i++) + result[i] = Mini_AigMux(aig, divisor_zero, dividend[i], rem[i]); + else + for (uint32_t i = 0; i < width; i++) + result[i] = quotient[i]; + if (signed_operands) + { + int result_sign = remainder_result ? sign_a : Mini_AigXor(aig, sign_a, sign_b); + result_sign = Mini_AigMux(aig, divisor_zero, Mini_AigLitConst0(), result_sign); + int* neg_result = sn_blast_negate_vector(aig, result, width, ripple); + for (uint32_t i = 0; i < width; i++) + result[i] = Mini_AigMux(aig, result_sign, neg_result[i], result[i]); + free(neg_result); + } + free(a); + free(b); + free(rem); + free(div); + free(quotient); + return result; +} + +static inline int* sn_blast_power(Mini_Aig_t* aig, const int* base, uint32_t base_width, bool base_signed, + const int* exponent, uint32_t exponent_width, bool exponent_signed, + uint32_t result_width, sn_blast_mul_mode_t mode, bool ripple) +{ + int* result = sn_blast_alloc_bits(result_width); + int* power = sn_blast_alloc_bits(result_width); + for (uint32_t i = 0; i < result_width; i++) + { + result[i] = i == 0 ? Mini_AigLitConst1() : Mini_AigLitConst0(); + power[i] = i < base_width ? base[i] + : (base_signed ? base[base_width - 1] : Mini_AigLitConst0()); + } + for (uint32_t i = 0; i < exponent_width; i++) + { + int* selected = mode == SN_BLAST_MUL_BOOTH + ? sn_blast_mul_booth(aig, result, base_signed, result_width, power, base_signed, + result_width, + result_width, ripple) + : sn_blast_mul_baugh_wooley(aig, result, base_signed, result_width, power, base_signed, + result_width, + result_width, ripple); + for (uint32_t bit = 0; bit < result_width; bit++) + result[bit] = Mini_AigMux(aig, exponent[i], selected[bit], result[bit]); + free(selected); + if (i + 1 < exponent_width) + { + int* squared = mode == SN_BLAST_MUL_BOOTH + ? sn_blast_mul_booth(aig, power, base_signed, result_width, power, base_signed, + result_width, result_width, ripple) + : sn_blast_mul_baugh_wooley(aig, power, base_signed, result_width, power, base_signed, + result_width, result_width, ripple); + free(power); + power = squared; + } + } + if (exponent_signed) + { + int is_zero = Mini_AigLitConst1(); + int is_one = Mini_AigLitConst1(); + int is_minus_one = Mini_AigLitConst1(); + for (uint32_t i = 0; i < base_width; i++) + { + is_zero = Mini_AigAnd(aig, is_zero, Mini_AigLitNot(base[i])); + is_one = Mini_AigAnd(aig, is_one, i == 0 ? base[i] : Mini_AigLitNot(base[i])); + if (base_signed) + is_minus_one = Mini_AigAnd(aig, is_minus_one, base[i]); + } + int unit = Mini_AigOr(aig, is_zero, is_one); + if (base_signed) + unit = Mini_AigOr(aig, unit, is_minus_one); + int force_zero = Mini_AigAnd(aig, exponent[exponent_width - 1], Mini_AigLitNot(unit)); + for (uint32_t i = 0; i < result_width; i++) + result[i] = Mini_AigAnd(aig, result[i], Mini_AigLitNot(force_zero)); + } + free(power); + return result; +} + +static inline int* sn_blast_shift(sn_blast_ctx_t* ctx, sn_obj_id_t object, bool left, bool arithmetic) +{ + const sn_module_t* m = ctx->module; + sn_obj_id_t value_id = sn_obj_fanin(m, object, 0); + sn_obj_id_t amount_id = sn_obj_fanin(m, object, 1); + uint32_t width = sn_obj_width(m, object); + uint32_t value_width = sn_obj_width(m, value_id); + uint32_t amount_width = sn_obj_width(m, amount_id); + uint32_t work_width = width > value_width ? width : value_width; + int* current = sn_blast_extend(ctx, value_id, work_width, sn_obj_is_signed(m, value_id)); + const int* amount = sn_blast_eval(ctx, amount_id); + int fill = arithmetic && !left && sn_obj_is_signed(m, value_id) ? current[work_width - 1] + : Mini_AigLitConst0(); + uint32_t useful_stages = 0; + // SN widths are capped below 2^31, so this unsigned shift never reaches 32. + while ((UINT32_C(1) << useful_stages) < work_width) + useful_stages++; + uint32_t stage_count = amount_width < useful_stages ? amount_width : useful_stages; + for (uint32_t stage = 0; stage < stage_count; stage++) + { + uint64_t distance = UINT64_C(1) << stage; + int* next = sn_blast_alloc_bits(work_width); + for (uint32_t bit = 0; bit < work_width; bit++) + { + int shifted = fill; + if (left) + shifted = bit >= distance ? current[bit - distance] : Mini_AigLitConst0(); + else + shifted = bit + distance < work_width ? current[bit + distance] : fill; + next[bit] = Mini_AigMux(ctx->aig, amount[stage], shifted, current[bit]); + } + free(current); + current = next; + } + if (amount_width > useful_stages) + { + int overshift = sn_blast_or(ctx->aig, amount + useful_stages, amount_width - useful_stages); + for (uint32_t bit = 0; bit < work_width; bit++) + current[bit] = Mini_AigMux(ctx->aig, overshift, fill, current[bit]); + } + int* result = sn_blast_alloc_bits(width); + for (uint32_t bit = 0; bit < width; bit++) + result[bit] = current[bit]; + free(current); + return result; +} + +static inline int* sn_blast_eval(sn_blast_ctx_t* ctx, sn_obj_id_t object) +{ + const sn_module_t* m = ctx->module; + assert(object < m->obj_types.size); + if (ctx->bits[object]) + return ctx->bits[object]; + assert(ctx->state[object] == 0); + ctx->state[object] = 1; + sn_obj_type_t type = sn_obj_type(m, object); + uint32_t width = sn_obj_width(m, object); + int* result = NULL; + if ((type == SN_PI || type == SN_INST || type == SN_FAN) && ctx->special_eval) + result = ctx->special_eval(ctx, object); + else if (type == SN_PO || type == SN_LOOP_OUT || type == SN_LOOP_IN) + { + sn_obj_id_t fanin = sn_obj_fanin(m, object, 0); + assert(fanin != SN_INVALID_ID); + int* source = sn_blast_eval(ctx, fanin); + result = sn_blast_alloc_bits(width); + sn_blast_copy(result, source, width); + } + else if (type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST) + { + result = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + result[i] = sn_const_bit(m, object, i) ? Mini_AigLitConst1() : Mini_AigLitConst0(); + } + else if (type == SN_BUF || type == SN_POS || type == SN_CAST) + { + sn_obj_id_t fanin = sn_obj_fanin(m, object, 0); + result = sn_blast_extend(ctx, fanin, width, type == SN_CAST ? sn_obj_is_signed(m, object) + : sn_obj_is_signed(m, fanin)); + } + else if (type == SN_CONCAT) + { + result = sn_blast_alloc_bits(width); + uint32_t offset = 0; + for (uint32_t i = 0; i < sn_obj_fanin_count(m, object); i++) + { + sn_obj_id_t fanin = sn_obj_fanin(m, object, i); + int* source = sn_blast_eval(ctx, fanin); + uint32_t source_width = sn_obj_width(m, fanin); + sn_blast_copy(result + offset, source, source_width); + offset += source_width; + } + assert(offset == width); + } + else if (type == SN_REPLICATE) + { + sn_obj_id_t fanin = sn_obj_fanin(m, object, 0); + int* source = sn_blast_eval(ctx, fanin); + uint32_t source_width = sn_obj_width(m, fanin); + uint32_t count = sn_obj_repeat_count(m, object); + result = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < count; i++) + sn_blast_copy(result + i * source_width, source, source_width); + } + else if (type == SN_SLICE) + { + sn_obj_id_t fanin = sn_obj_fanin(m, object, 0); + int* source = sn_blast_eval(ctx, fanin); + const sn_slice_info_t* info = sn_obj_slice_info(m, object); + result = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + { + int64_t index = info->left_index >= info->right_index ? (int64_t)info->right_index + i + : (int64_t)info->right_index - i; + assert(index >= 0 && (uint64_t)index < sn_obj_width(m, fanin)); + result[i] = source[index]; + } + } + else if (type == SN_BIT_NOT || type == SN_NEG || type == SN_LOG_NOT || + type == SN_REDUCE_AND || type == SN_REDUCE_NAND || type == SN_REDUCE_OR || + type == SN_REDUCE_NOR || type == SN_REDUCE_XOR || type == SN_REDUCE_XNOR) + { + sn_obj_id_t fanin = sn_obj_fanin(m, object, 0); + int* source = sn_blast_eval(ctx, fanin); + if (type == SN_BIT_NOT) + { + int* extended = sn_blast_extend(ctx, fanin, width, sn_obj_is_signed(m, fanin)); + result = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + result[i] = Mini_AigLitNot(extended[i]); + free(extended); + } + else if (type == SN_NEG) + { + int* extended = sn_blast_extend(ctx, fanin, width, sn_obj_is_signed(m, fanin)); + result = sn_blast_negate_vector(ctx->aig, extended, width, ctx->options.ripple_adders); + free(extended); + } + else + { + int reduced = type == SN_LOG_NOT + ? Mini_AigLitNot(sn_blast_or(ctx->aig, source, sn_obj_width(m, fanin))) + : type == SN_REDUCE_AND || type == SN_REDUCE_NAND + ? sn_blast_and(ctx->aig, source, sn_obj_width(m, fanin)) + : type == SN_REDUCE_OR || type == SN_REDUCE_NOR + ? sn_blast_or(ctx->aig, source, sn_obj_width(m, fanin)) + : sn_blast_xor(ctx->aig, source, sn_obj_width(m, fanin)); + if (type == SN_REDUCE_NAND || type == SN_REDUCE_NOR || type == SN_REDUCE_XNOR) + reduced = Mini_AigLitNot(reduced); + result = sn_blast_alloc_bits(width); + result[0] = reduced; + for (uint32_t i = 1; i < width; i++) + result[i] = 0; + } + } + else if (type == SN_BIT_AND || type == SN_BIT_OR || type == SN_BIT_XOR || type == SN_BIT_XNOR || + type == SN_LOG_AND || type == SN_LOG_OR) + { + sn_obj_id_t a_id = sn_obj_fanin(m, object, 0), b_id = sn_obj_fanin(m, object, 1); + bool logical = type == SN_LOG_AND || type == SN_LOG_OR; + uint32_t a_width = logical ? sn_obj_width(m, a_id) : width; + uint32_t b_width = logical ? sn_obj_width(m, b_id) : width; + int* a = sn_blast_extend(ctx, a_id, a_width, sn_obj_is_signed(m, a_id) && sn_obj_is_signed(m, b_id)); + int* b = sn_blast_extend(ctx, b_id, b_width, sn_obj_is_signed(m, a_id) && sn_obj_is_signed(m, b_id)); + result = sn_blast_alloc_bits(width); + if (logical) + { + int av = sn_blast_or(ctx->aig, a, a_width); + int bv = sn_blast_or(ctx->aig, b, b_width); + result[0] = type == SN_LOG_AND ? Mini_AigAnd(ctx->aig, av, bv) : Mini_AigOr(ctx->aig, av, bv); + for (uint32_t i = 1; i < width; i++) + result[i] = 0; + } + else + for (uint32_t i = 0; i < width; i++) + result[i] = type == SN_BIT_AND ? Mini_AigAnd(ctx->aig, a[i], b[i]) + : type == SN_BIT_OR ? Mini_AigOr(ctx->aig, a[i], b[i]) + : type == SN_BIT_XOR ? Mini_AigXor(ctx->aig, a[i], b[i]) + : Mini_AigLitNot(Mini_AigXor(ctx->aig, a[i], b[i])); + free(a); + free(b); + } + else if (type == SN_EQ || type == SN_NE || type == SN_CASE_EQ || type == SN_CASE_NE || + type == SN_WILDCARD_EQ || type == SN_WILDCARD_NE || type == SN_LT || type == SN_LE || + type == SN_GT || type == SN_GE) + { + sn_obj_id_t a_id = sn_obj_fanin(m, object, 0), b_id = sn_obj_fanin(m, object, 1); + uint32_t compare_width = sn_obj_width(m, a_id) > sn_obj_width(m, b_id) ? sn_obj_width(m, a_id) + : sn_obj_width(m, b_id); + bool signed_compare = sn_obj_is_signed(m, a_id) && sn_obj_is_signed(m, b_id); + int* a = sn_blast_extend(ctx, a_id, compare_width, signed_compare); + int* b = sn_blast_extend(ctx, b_id, compare_width, signed_compare); + int value; + if (type == SN_EQ || type == SN_CASE_EQ || type == SN_WILDCARD_EQ || type == SN_NE || + type == SN_CASE_NE || type == SN_WILDCARD_NE) + value = sn_blast_eq_bits(ctx->aig, a, b, compare_width); + else + { + int (*blast_gt)(Mini_Aig_t*, const int*, const int*, uint32_t, bool) = + ctx->options.delay_comparators ? sn_blast_gt_delay : sn_blast_gt; + if (type == SN_GT) + value = blast_gt(ctx->aig, a, b, compare_width, signed_compare); + else if (type == SN_LT) + value = blast_gt(ctx->aig, b, a, compare_width, signed_compare); + else if (type == SN_GE) + value = Mini_AigLitNot(blast_gt(ctx->aig, b, a, compare_width, signed_compare)); + else + value = Mini_AigLitNot(blast_gt(ctx->aig, a, b, compare_width, signed_compare)); + } + if (type == SN_NE || type == SN_CASE_NE || type == SN_WILDCARD_NE) + value = Mini_AigLitNot(value); + result = sn_blast_alloc_bits(width); + result[0] = value; + for (uint32_t i = 1; i < width; i++) + result[i] = 0; + free(a); + free(b); + } + else if (type == SN_ADD || type == SN_SUB) + { + sn_obj_id_t a_id = sn_obj_fanin(m, object, 0), b_id = sn_obj_fanin(m, object, 1); + bool signed_operands = sn_obj_is_signed(m, a_id) && sn_obj_is_signed(m, b_id); + int* a = sn_blast_extend(ctx, a_id, width, signed_operands); + int* b = sn_blast_extend(ctx, b_id, width, signed_operands); + result = sn_blast_add_vectors(ctx->aig, a, b, width, type == SN_SUB, ctx->options.ripple_adders); + free(a); + free(b); + } + else if (type == SN_MUL) + { + sn_obj_id_t a_id = sn_obj_fanin(m, object, 0), b_id = sn_obj_fanin(m, object, 1); + int* a = sn_blast_eval(ctx, a_id), *b = sn_blast_eval(ctx, b_id); + bool signed_operands = sn_obj_is_signed(m, a_id) && sn_obj_is_signed(m, b_id); + uint32_t a_width = sn_obj_width(m, a_id), b_width = sn_obj_width(m, b_id); + result = ctx->options.mul_mode == SN_BLAST_MUL_BOOTH + ? sn_blast_mul_booth(ctx->aig, a, signed_operands, a_width, b, signed_operands, b_width, width, + ctx->options.ripple_adders) + : sn_blast_mul_baugh_wooley(ctx->aig, a, signed_operands, a_width, b, signed_operands, b_width, + width, ctx->options.ripple_adders); + } + else if (type == SN_LUT) + { + uint32_t count = sn_obj_fanin_count(m, object); + int inputs[6]; + assert(count <= sizeof(inputs) / sizeof(inputs[0])); + for (uint32_t i = 0; i < count; i++) + inputs[i] = sn_blast_eval(ctx, sn_obj_fanin(m, object, i))[0]; + result = sn_blast_alloc_bits(1); + result[0] = sn_blast_lut_rec(ctx->aig, inputs, count, sn_obj_lut_truth(m, object)); + } + else if (type == SN_DIV || type == SN_MOD) + { + sn_obj_id_t a_id = sn_obj_fanin(m, object, 0), b_id = sn_obj_fanin(m, object, 1); + uint32_t work_width = sn_obj_width(m, a_id) > sn_obj_width(m, b_id) ? sn_obj_width(m, a_id) + : sn_obj_width(m, b_id); + if (work_width < width) + work_width = width; + bool signed_operands = sn_obj_is_signed(m, a_id) && sn_obj_is_signed(m, b_id); + int* a = sn_blast_extend(ctx, a_id, work_width, signed_operands); + int* b = sn_blast_extend(ctx, b_id, work_width, signed_operands); + int* quotient_or_remainder = sn_blast_div_vectors(ctx->aig, a, b, work_width, signed_operands, + type == SN_MOD, ctx->options.ripple_adders, + ctx->options.delay_comparators); + result = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + result[i] = quotient_or_remainder[i]; + free(a); + free(b); + free(quotient_or_remainder); + } + else if (type == SN_POW) + { + sn_obj_id_t base_id = sn_obj_fanin(m, object, 0), exponent_id = sn_obj_fanin(m, object, 1); + int* base = sn_blast_eval(ctx, base_id); + int* exponent = sn_blast_eval(ctx, exponent_id); + result = sn_blast_power(ctx->aig, base, sn_obj_width(m, base_id), sn_obj_is_signed(m, base_id), exponent, + sn_obj_width(m, exponent_id), sn_obj_is_signed(m, exponent_id), width, + ctx->options.mul_mode, ctx->options.ripple_adders); + } + else if (type == SN_SHL || type == SN_SHR || type == SN_ASHL || type == SN_ASHR) + result = sn_blast_shift(ctx, object, type == SN_SHL || type == SN_ASHL, type == SN_ASHR); + else if (type == SN_MUX) + { + int* select = sn_blast_eval(ctx, sn_obj_fanin(m, object, SN_MUX_SELECT)); + int* one = sn_blast_eval(ctx, sn_obj_fanin(m, object, SN_MUX_SELECTED)); + int* zero = sn_blast_eval(ctx, sn_obj_fanin(m, object, SN_MUX_DEFAULT)); + result = sn_blast_alloc_bits(width); + for (uint32_t i = 0; i < width; i++) + result[i] = Mini_AigMux(ctx->aig, select[0], one[i], zero[i]); + } + else if (type == SN_BMUX) + { + int* select = sn_blast_eval(ctx, sn_obj_fanin(m, object, 0)); + sn_obj_id_t alternatives_id = sn_obj_fanin(m, object, 1); + sn_obj_type_t alternatives_type = sn_obj_type(m, alternatives_id); + uint32_t select_width = sn_obj_width(m, sn_obj_fanin(m, object, 0)); + if (alternatives_type == SN_CONST0 || alternatives_type == SN_CONST1 || alternatives_type == SN_CONST) + result = sn_blast_const_mux_tree(ctx->aig, m, alternatives_id, select, select_width, width); + else + { + int* alternatives = sn_blast_eval(ctx, alternatives_id); + result = sn_blast_mux_tree(ctx->aig, select, select_width, alternatives, width); + } + } + else if (type == SN_PMUX) + { + int* select = sn_blast_eval(ctx, sn_obj_fanin(m, object, 0)); + int* alternatives = sn_blast_eval(ctx, sn_obj_fanin(m, object, 1)); + int* default_value = sn_blast_eval(ctx, sn_obj_fanin(m, object, 2)); + uint32_t select_width = sn_obj_width(m, sn_obj_fanin(m, object, 0)); + int any_select = sn_blast_or(ctx->aig, select, select_width); + result = sn_blast_alloc_bits(width); + int* terms = sn_blast_alloc_bits(select_width + 1); + for (uint32_t bit = 0; bit < width; bit++) + { + for (uint32_t i = 0; i < select_width; i++) + terms[i] = Mini_AigAnd(ctx->aig, select[i], alternatives[i * width + bit]); + terms[select_width] = Mini_AigAnd(ctx->aig, Mini_AigLitNot(any_select), default_value[bit]); + result[bit] = sn_blast_or(ctx->aig, terms, select_width + 1); + } + free(terms); + } + else + assert(false); + assert(result); + ctx->bits[object] = result; + ctx->state[object] = 2; + return result; +} + +static inline void sn_blast_check_module(const sn_module_t* module, sn_blast_options_t options) +{ + assert(module); + for (sn_obj_id_t object = 0; object < module->obj_types.size; object++) + { + sn_obj_type_t type = sn_obj_type(module, object); + assert(type != SN_INST && type != SN_FAN); + if (type == SN_LUT) + assert(sn_obj_fanin_count(module, object) <= 6); + if (type == SN_REG_OUT) + assert(!(sn_vec_at(uint32_t, &module->reg_flags, sn_obj_type_id(module, object)) & SN_REG_LATCH)); + if (!options.abstract_memories) + assert(type != SN_MEM_OUT && type != SN_MEM_IN && type != SN_MEM_READ && type != SN_MEM_WRITE); + } + assert(sn_module_is_topo(module)); +} + +static inline Mini_Aig_t* sn_module_blast_comb_options(const sn_module_t* module, + sn_blast_options_t options); + +static inline Mini_Aig_t* sn_module_blast_comb(const sn_module_t* module) +{ + return sn_module_blast_comb_options(module, sn_blast_default_options()); +} + +static inline Mini_Aig_t* sn_module_blast_seq_options(const sn_module_t* module, sn_blast_options_t options) +{ + options.mode = SN_BLAST_SEQ; + return sn_module_blast_comb_options(module, options); +} + +static inline Mini_Aig_t* sn_module_blast_seq(const sn_module_t* module) +{ + return sn_module_blast_seq_options(module, sn_blast_default_options()); +} + +typedef struct sn_blast_hier_stats_t +{ + uint64_t primary_input_bits; + uint64_t primary_output_bits; + uint64_t flop_bits; + uint64_t register_control_bits; + uint64_t memory_count; + uint64_t multiplier_count; + uint64_t abstraction_input_bits; + uint64_t abstraction_output_bits; +} sn_blast_hier_stats_t; + +typedef enum sn_blast_boundary_kind_t +{ + SN_BLAST_BOUNDARY_TOP_PI, + SN_BLAST_BOUNDARY_MEMORY_OUTPUT, + SN_BLAST_BOUNDARY_PRIMITIVE_OUTPUT, + SN_BLAST_BOUNDARY_REG_OUTPUT, + SN_BLAST_BOUNDARY_LOOP_OUTPUT, + SN_BLAST_BOUNDARY_TOP_PO, + SN_BLAST_BOUNDARY_REG_CONTROL, + SN_BLAST_BOUNDARY_MEMORY_INPUT, + SN_BLAST_BOUNDARY_PRIMITIVE_INPUT, + SN_BLAST_BOUNDARY_REG_INPUT, + SN_BLAST_BOUNDARY_LOOP_INPUT +} sn_blast_boundary_kind_t; + +typedef struct sn_blast_hier_ref_t +{ + uint32_t occurrence; + sn_obj_id_t object; + uint32_t bit; +} sn_blast_hier_ref_t; + +typedef struct sn_blast_boundary_bit_t +{ + sn_blast_boundary_kind_t kind; + sn_blast_hier_ref_t signal; + uint32_t owner; + uint32_t port; +} sn_blast_boundary_bit_t; + +typedef struct sn_blast_occurrence_t +{ + sn_module_id_t module; + uint32_t parent_occurrence; + sn_obj_id_t parent_inst; +} sn_blast_occurrence_t; + +typedef struct sn_blast_primitive_t +{ + uint32_t occurrence; + sn_obj_id_t inst; + sn_module_id_t module; + uint32_t ci_begin; + uint32_t ci_count; + uint32_t co_begin; + uint32_t co_count; +} sn_blast_primitive_t; + +typedef struct sn_blast_register_t +{ + uint32_t occurrence; + sn_obj_id_t reg_out; + uint32_t ci_begin; + uint32_t co_begin; + uint32_t control_co_begin[SN_REG_FANIN_COUNT]; + uint32_t width; +} sn_blast_register_t; + +typedef struct sn_blast_loop_t +{ + uint32_t occurrence; + sn_obj_id_t loop_out; + uint32_t co_begin; + uint32_t width; +} sn_blast_loop_t; + +typedef struct sn_blast_boundary_t +{ + uint32_t register_bits; + sn_vec_t occurrences; // sn_blast_occurrence_t + sn_vec_t primitives; // sn_blast_primitive_t + sn_vec_t registers; // sn_blast_register_t + sn_vec_t loops; // sn_blast_loop_t + sn_vec_t cis; // sn_blast_boundary_bit_t in MiniAIG PI order + sn_vec_t cos; // sn_blast_boundary_bit_t in MiniAIG PO order +} sn_blast_boundary_t; + +static inline void sn_blast_boundary_init(sn_blast_boundary_t* boundary) +{ + assert(boundary); + boundary->register_bits = 0; + sn_vec_init(&boundary->occurrences); + sn_vec_init(&boundary->primitives); + sn_vec_init(&boundary->registers); + sn_vec_init(&boundary->loops); + sn_vec_init(&boundary->cis); + sn_vec_init(&boundary->cos); +} + +static inline void sn_blast_boundary_destroy(sn_blast_boundary_t* boundary) +{ + assert(boundary); + boundary->register_bits = 0; + sn_vec_destroy(&boundary->occurrences); + sn_vec_destroy(&boundary->primitives); + sn_vec_destroy(&boundary->registers); + sn_vec_destroy(&boundary->loops); + sn_vec_destroy(&boundary->cis); + sn_vec_destroy(&boundary->cos); +} + +typedef struct sn_blast_hier_t sn_blast_hier_t; + +typedef struct sn_blast_hier_frame_t +{ + sn_blast_hier_t* hierarchy; + sn_blast_ctx_t blast; + struct sn_blast_hier_frame_t** children; + uint32_t occurrence; + struct sn_blast_hier_frame_t* parent; + sn_obj_id_t parent_inst; +} sn_blast_hier_frame_t; + +typedef struct sn_blast_hier_object_t +{ + sn_blast_hier_frame_t* frame; + sn_obj_id_t object; + uint32_t boundary_owner; +} sn_blast_hier_object_t; + +struct sn_blast_hier_t +{ + const sn_design_t* design; + Mini_Aig_t* aig; + sn_blast_options_t options; + sn_blast_hier_stats_t stats; + sn_blast_boundary_t* boundary; + uint8_t* active_modules; + sn_vec_t memory_reads; + sn_vec_t memory_writes; + sn_vec_t registers; + sn_vec_t loops; + sn_vec_t abstract_insts; +}; + +static inline bool sn_blast_reg_control_is_comb_output(const sn_module_t* module, sn_obj_id_t reg_out, + uint32_t slot) +{ + uint32_t flags = sn_obj_reg_flags(module, reg_out); + if (slot == SN_REG_ENABLE) + return true; + if (slot == SN_REG_SET) + return !(flags & SN_REG_SET_ASYNC); + if (slot == SN_REG_RESET) + return !(flags & SN_REG_RESET_ASYNC); + if (slot == SN_REG_RESET_VALUE) + return sn_obj_fanin(module, reg_out, SN_REG_RESET) != SN_INVALID_ID && !(flags & SN_REG_RESET_ASYNC); + return false; +} + +static inline bool sn_blast_hier_is_abstract_inst(const sn_blast_hier_t* hierarchy, + const sn_module_t* module, sn_obj_id_t inst) +{ + const sn_module_t* child = sn_design_get_module_const(hierarchy->design, + sn_inst_module_id(module, inst)); + if (sn_module_is_blackbox(child)) + return true; + const char* name = sn_name_get(&hierarchy->design->names, child->name); + bool memory = strncmp(name, "__sn_RAM", 8) == 0 || strncmp(name, "__sn_URAM", 9) == 0; + bool multiplier = strncmp(name, "__sn_DSP", 8) == 0; + bool carry = strncmp(name, "__sn_CARRY", 10) == 0; + return hierarchy->options.abstract_instances || (memory && hierarchy->options.abstract_memories) || + (multiplier && hierarchy->options.abstract_multipliers) || carry; +} + +static inline sn_blast_hier_object_t* sn_blast_hier_add_object(sn_vec_t* objects, sn_blast_hier_frame_t* frame, + sn_obj_id_t object) +{ + sn_blast_hier_object_t* entry = sn_vec_push(sn_blast_hier_object_t, objects); + entry->frame = frame; + entry->object = object; + entry->boundary_owner = SN_INVALID_ID; + return entry; +} + +static inline uint64_t sn_blast_hier_memory_input_bits(const sn_module_t* module, sn_obj_id_t object) +{ + sn_obj_type_t type = sn_obj_type(module, object); + uint32_t first = type == SN_MEM_READ ? (uint32_t)SN_MEM_READ_CLOCK : (uint32_t)SN_MEM_WRITE_CLOCK; + uint32_t count = type == SN_MEM_READ ? (uint32_t)SN_MEM_READ_FANIN_COUNT + : (uint32_t)SN_MEM_WRITE_FANIN_COUNT; + uint64_t bits = 0; + for (uint32_t slot = first; slot < count; slot++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, object, slot); + if (fanin != SN_INVALID_ID) + bits += sn_obj_width(module, fanin); + } + return bits; +} + +static inline sn_blast_hier_frame_t* sn_blast_hier_build_frame(sn_blast_hier_t* hierarchy, sn_module_id_t module_id, + uint32_t parent_occurrence, + sn_obj_id_t parent_inst, + sn_blast_hier_frame_t* parent_frame) +{ + assert(module_id < hierarchy->design->modules.size); + assert(!hierarchy->active_modules[module_id]); + hierarchy->active_modules[module_id] = 1; + const sn_module_t* module = sn_design_get_module_const(hierarchy->design, module_id); + assert(sn_module_is_topo(module)); + for (size_t i = 0; i < module->reg_flags.size; i++) + assert(!(sn_vec_at(uint32_t, &module->reg_flags, i) & SN_REG_LATCH)); + for (size_t i = 0; i < module->type_objects[SN_LUT].size; i++) + assert(sn_obj_fanin_count(module, sn_vec_at(sn_obj_id_t, &module->type_objects[SN_LUT], i)) <= 6); + + sn_blast_hier_frame_t* frame = (sn_blast_hier_frame_t*)calloc(1, sizeof(sn_blast_hier_frame_t)); + assert(frame); + size_t object_count = module->obj_types.size; + frame->hierarchy = hierarchy; + frame->parent = parent_frame; + frame->parent_inst = parent_inst; + frame->occurrence = SN_INVALID_ID; + if (hierarchy->boundary) + { + assert(hierarchy->boundary->occurrences.size < UINT32_MAX); + frame->occurrence = (uint32_t)hierarchy->boundary->occurrences.size; + sn_blast_occurrence_t* occurrence = sn_vec_push(sn_blast_occurrence_t, &hierarchy->boundary->occurrences); + occurrence->module = module_id; + occurrence->parent_occurrence = parent_occurrence; + occurrence->parent_inst = parent_inst; + } + frame->blast.module = module; + frame->blast.aig = NULL; + frame->blast.options = hierarchy->options; + frame->blast.bits = (int**)calloc(object_count, sizeof(int*)); + frame->blast.state = (uint8_t*)calloc(object_count, sizeof(uint8_t)); + frame->children = (sn_blast_hier_frame_t**)calloc(object_count, sizeof(sn_blast_hier_frame_t*)); + assert(frame->blast.bits && frame->blast.state && frame->children); + + // Boundary order must not depend on physical/topological object order, because a word-level transform can + // legitimately rebuild that order. Type IDs are the stable natural order preserved by SN duplication. Collect + // each class in type-ID order and recurse through child occurrences in natural instance order. Register bits then + // have the canonical key (depth-first instance path, register type ID, LSB-first bit index). + hierarchy->stats.memory_count += module->type_objects[SN_MEM_OUT].size; + if (hierarchy->options.abstract_memories) + { + for (size_t i = 0; i < module->type_objects[SN_MEM_READ].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_READ], i); + sn_blast_hier_add_object(&hierarchy->memory_reads, frame, object); + hierarchy->stats.abstraction_output_bits += sn_obj_width(module, object); + hierarchy->stats.abstraction_input_bits += sn_blast_hier_memory_input_bits(module, object); + } + } + if (hierarchy->options.abstract_memories) + { + for (size_t i = 0; i < module->type_objects[SN_MEM_WRITE].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_WRITE], i); + sn_blast_hier_add_object(&hierarchy->memory_writes, frame, object); + hierarchy->stats.abstraction_input_bits += sn_blast_hier_memory_input_bits(module, object); + } + } + for (size_t i = 0; i < module->type_objects[SN_REG_OUT].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_REG_OUT], i); + sn_blast_hier_object_t* entry = sn_blast_hier_add_object(&hierarchy->registers, frame, object); + if (hierarchy->boundary) + { + assert(hierarchy->boundary->registers.size < UINT32_MAX); + entry->boundary_owner = (uint32_t)hierarchy->boundary->registers.size; + sn_blast_register_t* reg = sn_vec_push(sn_blast_register_t, &hierarchy->boundary->registers); + reg->occurrence = frame->occurrence; + reg->reg_out = object; + reg->ci_begin = SN_INVALID_ID; + reg->co_begin = SN_INVALID_ID; + for (uint32_t slot = 0; slot < SN_REG_FANIN_COUNT; slot++) + reg->control_co_begin[slot] = SN_INVALID_ID; + reg->width = sn_obj_width(module, object); + } + hierarchy->stats.flop_bits += sn_obj_width(module, object); + if (hierarchy->options.expose_register_controls) + { + const uint32_t slots[] = {SN_REG_ENABLE, SN_REG_SET, SN_REG_RESET, SN_REG_RESET_VALUE}; + for (size_t j = 0; j < sizeof(slots) / sizeof(slots[0]); j++) + { + if (hierarchy->options.mode != SN_BLAST_COMB || + !sn_blast_reg_control_is_comb_output(module, object, slots[j])) + continue; + sn_obj_id_t fanin = sn_obj_fanin(module, object, slots[j]); + if (fanin != SN_INVALID_ID) + hierarchy->stats.register_control_bits += sn_obj_width(module, fanin); + } + } + } + for (size_t i = 0; i < module->type_objects[SN_LOOP_OUT].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_LOOP_OUT], i); + sn_blast_hier_object_t* entry = sn_blast_hier_add_object(&hierarchy->loops, frame, object); + if (hierarchy->boundary) + { + assert(hierarchy->boundary->loops.size < UINT32_MAX); + entry->boundary_owner = (uint32_t)hierarchy->boundary->loops.size; + sn_blast_loop_t* loop = sn_vec_push(sn_blast_loop_t, &hierarchy->boundary->loops); + loop->occurrence = frame->occurrence; + loop->loop_out = object; + loop->co_begin = SN_INVALID_ID; + loop->width = sn_obj_width(module, object); + } + hierarchy->stats.abstraction_output_bits += sn_obj_width(module, object); + hierarchy->stats.abstraction_input_bits += sn_obj_width(module, object); + } + for (size_t i = 0; i < module->type_objects[SN_INST].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_INST], i); + const sn_module_t* child = sn_design_get_module_const(hierarchy->design, + sn_inst_module_id(module, object)); + if (sn_blast_hier_is_abstract_inst(hierarchy, module, object)) + { + sn_blast_hier_object_t* entry = sn_blast_hier_add_object(&hierarchy->abstract_insts, frame, object); + if (hierarchy->boundary) + { + assert(hierarchy->boundary->primitives.size < UINT32_MAX); + entry->boundary_owner = (uint32_t)hierarchy->boundary->primitives.size; + sn_blast_primitive_t* primitive = + sn_vec_push(sn_blast_primitive_t, &hierarchy->boundary->primitives); + primitive->occurrence = frame->occurrence; + primitive->inst = object; + primitive->module = child->id; + primitive->ci_begin = SN_INVALID_ID; + primitive->ci_count = 0; + primitive->co_begin = SN_INVALID_ID; + primitive->co_count = 0; + } + const char* name = sn_name_get(&hierarchy->design->names, child->name); + if (strncmp(name, "__sn_DSP", 8) == 0) + hierarchy->stats.multiplier_count++; + else if (strncmp(name, "__sn_RAM", 8) == 0 || strncmp(name, "__sn_URAM", 9) == 0) + hierarchy->stats.memory_count++; + for (size_t j = 0; j < child->type_objects[SN_PO].size; j++) + { + sn_obj_id_t output = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PO], j); + hierarchy->stats.abstraction_output_bits += sn_obj_width(child, output); + } + for (uint32_t j = 0; j < sn_obj_fanin_count(module, object); j++) + hierarchy->stats.abstraction_input_bits += sn_obj_width(module, sn_obj_fanin(module, object, j)); + } + else + frame->children[object] = + sn_blast_hier_build_frame(hierarchy, child->id, frame->occurrence, object, frame); + } + hierarchy->active_modules[module_id] = 0; + return frame; +} + +static inline int* sn_blast_hier_eval_inst(sn_blast_ctx_t* context, sn_obj_id_t object) +{ + // sn_blast_eval() caches this result on the parent SN_INST or SN_FAN object, while the child PO evaluation below + // is cached in the child occurrence frame. Thus each used output cone is built once per instance occurrence; + // subsequent fanouts neither re-enter the child nor rebuild its logic. Unused outputs remain unexpanded. + sn_blast_hier_frame_t* frame = (sn_blast_hier_frame_t*)context->special_data; + const sn_module_t* module = context->module; + sn_obj_type_t type = sn_obj_type(module, object); + if (type == SN_PI) + { + assert(frame->parent && frame->parent_inst != SN_INVALID_ID); + uint32_t port = sn_obj_type_id(module, object); + sn_obj_id_t parent_fanin = sn_obj_fanin(frame->parent->blast.module, frame->parent_inst, port); + assert(sn_obj_width(module, object) == sn_obj_width(frame->parent->blast.module, parent_fanin)); + int* source = sn_blast_eval(&frame->parent->blast, parent_fanin); + int* result = sn_blast_alloc_bits(sn_obj_width(module, object)); + sn_blast_copy(result, source, sn_obj_width(module, object)); + return result; + } + sn_obj_id_t inst = type == SN_INST ? object : sn_fan_inst_id(module, object); + uint32_t output_index = type == SN_INST ? 0 : sn_fan_output_index(module, object); + sn_blast_hier_frame_t* child = frame->children[inst]; + assert(child); // Abstract insts have their output bits pre-seeded as CIs. + const sn_module_t* child_module = child->blast.module; + assert(output_index < child_module->type_objects[SN_PO].size); + sn_obj_id_t child_po = sn_vec_at(sn_obj_id_t, &child_module->type_objects[SN_PO], output_index); + int* source = sn_blast_eval(&child->blast, child_po); + uint32_t width = sn_obj_width(module, object); + assert(width == sn_obj_width(child_module, child_po)); + int* result = sn_blast_alloc_bits(width); + sn_blast_copy(result, source, width); + return result; +} + +static inline void sn_blast_hier_prepare_frame(sn_blast_hier_frame_t* frame, Mini_Aig_t* aig, + sn_blast_options_t options) +{ + frame->blast.aig = aig; + frame->blast.options = options; + frame->blast.special_eval = sn_blast_hier_eval_inst; + frame->blast.special_data = frame; + for (sn_obj_id_t object = 0; object < frame->blast.module->obj_types.size; object++) + if (frame->children[object]) + sn_blast_hier_prepare_frame(frame->children[object], aig, options); +} + +static inline void sn_blast_hier_seed_object(sn_blast_hier_frame_t* frame, sn_obj_id_t object, Mini_Aig_t* aig, + bool invert) +{ + const sn_module_t* module = frame->blast.module; + uint32_t width = sn_obj_width(module, object); + assert(!frame->blast.bits[object]); + frame->blast.bits[object] = sn_blast_alloc_bits(width); + for (uint32_t bit = 0; bit < width; bit++) + { + int input = Mini_AigCreatePi(aig); + frame->blast.bits[object][bit] = invert ? Mini_AigLitNot(input) : input; + } + frame->blast.state[object] = 2; +} + +static inline void sn_blast_boundary_add_bit(sn_blast_hier_t* hierarchy, bool is_ci, + sn_blast_boundary_kind_t kind, sn_blast_hier_frame_t* frame, + sn_obj_id_t object, uint32_t bit, uint32_t owner, uint32_t port) +{ + if (!hierarchy->boundary) + return; + sn_vec_t* bits = is_ci ? &hierarchy->boundary->cis : &hierarchy->boundary->cos; + sn_blast_boundary_bit_t* entry = sn_vec_push(sn_blast_boundary_bit_t, bits); + entry->kind = kind; + entry->signal.occurrence = frame->occurrence; + entry->signal.object = object; + entry->signal.bit = bit; + entry->owner = owner; + entry->port = port; +} + +static inline void sn_blast_hier_seed_abstract_inst(sn_blast_hier_object_t occurrence, Mini_Aig_t* aig) +{ + const sn_module_t* module = occurrence.frame->blast.module; + sn_obj_id_t inst = occurrence.object; + const sn_module_t* child = sn_design_get_module_const(occurrence.frame->hierarchy->design, + sn_inst_module_id(module, inst)); + uint32_t output_count = (uint32_t)child->type_objects[SN_PO].size; + sn_blast_primitive_t* primitive = NULL; + if (occurrence.frame->hierarchy->boundary) + { + primitive = &sn_vec_at(sn_blast_primitive_t, &occurrence.frame->hierarchy->boundary->primitives, + occurrence.boundary_owner); + // The boundary vector is the running CI count. Mini_AigPiNum() scans the whole manager and must not be used + // here because this routine is called once per primitive occurrence. + primitive->ci_begin = (uint32_t)occurrence.frame->hierarchy->boundary->cis.size; + } + for (uint32_t i = 0; i < output_count; i++) + { + sn_obj_id_t output = output_count == 1 ? inst : sn_inst_output(module, inst, i); + sn_blast_hier_seed_object(occurrence.frame, output, aig, false); + for (uint32_t bit = 0; bit < sn_obj_width(module, output); bit++) + sn_blast_boundary_add_bit(occurrence.frame->hierarchy, true, + SN_BLAST_BOUNDARY_PRIMITIVE_OUTPUT, occurrence.frame, output, bit, + occurrence.boundary_owner, i); + } + if (primitive) + primitive->ci_count = (uint32_t)occurrence.frame->hierarchy->boundary->cis.size - primitive->ci_begin; +} + +static inline void sn_blast_hier_emit_memory_inputs(sn_blast_hier_object_t occurrence, Mini_Aig_t* aig) +{ + const sn_module_t* module = occurrence.frame->blast.module; + sn_obj_type_t type = sn_obj_type(module, occurrence.object); + uint32_t first = type == SN_MEM_READ ? (uint32_t)SN_MEM_READ_CLOCK : (uint32_t)SN_MEM_WRITE_CLOCK; + uint32_t count = type == SN_MEM_READ ? (uint32_t)SN_MEM_READ_FANIN_COUNT + : (uint32_t)SN_MEM_WRITE_FANIN_COUNT; + for (uint32_t slot = first; slot < count; slot++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, occurrence.object, slot); + if (fanin == SN_INVALID_ID) + continue; + int* bits = sn_blast_eval(&occurrence.frame->blast, fanin); + for (uint32_t bit = 0; bit < sn_obj_width(module, fanin); bit++) + { + Mini_AigCreatePo(aig, bits[bit]); + sn_blast_boundary_add_bit(occurrence.frame->hierarchy, false, SN_BLAST_BOUNDARY_MEMORY_INPUT, + occurrence.frame, fanin, bit, SN_INVALID_ID, slot); + } + } +} + +static inline void sn_blast_hier_emit_register_controls(sn_blast_hier_object_t occurrence, Mini_Aig_t* aig) +{ + sn_blast_hier_t* hierarchy = occurrence.frame->hierarchy; + if (!hierarchy->options.expose_register_controls || hierarchy->options.mode != SN_BLAST_COMB) + return; + const sn_module_t* module = occurrence.frame->blast.module; + const uint32_t slots[] = {SN_REG_ENABLE, SN_REG_SET, SN_REG_RESET, SN_REG_RESET_VALUE}; + for (size_t i = 0; i < sizeof(slots) / sizeof(slots[0]); i++) + { + uint32_t slot = slots[i]; + if (!sn_blast_reg_control_is_comb_output(module, occurrence.object, slot)) + continue; + sn_obj_id_t fanin = sn_obj_fanin(module, occurrence.object, slot); + if (fanin == SN_INVALID_ID) + continue; + int* bits = sn_blast_eval(&occurrence.frame->blast, fanin); + if (hierarchy->boundary) + sn_vec_at(sn_blast_register_t, &hierarchy->boundary->registers, + occurrence.boundary_owner).control_co_begin[slot] = + (uint32_t)hierarchy->boundary->cos.size; + for (uint32_t bit = 0; bit < sn_obj_width(module, fanin); bit++) + { + Mini_AigCreatePo(aig, bits[bit]); + sn_blast_boundary_add_bit(hierarchy, false, SN_BLAST_BOUNDARY_REG_CONTROL, occurrence.frame, + fanin, bit, occurrence.boundary_owner, slot); + } + } +} + +// Constructs the edge-triggered next-state function. Synchronous reset has +// highest priority, followed by synchronous set, enable, and the raw data input, +// matching the SN Verilog writer. Clock and asynchronous controls deliberately +// remain outside the sequential AIG transition relation. +static inline int* sn_blast_hier_reg_next(sn_blast_hier_object_t occurrence) +{ + sn_blast_ctx_t* context = &occurrence.frame->blast; + const sn_module_t* module = context->module; + sn_obj_id_t reg_out = occurrence.object; + sn_obj_id_t reg_in = sn_obj_fanin(module, reg_out, SN_REG_DATA); + sn_obj_id_t data = sn_obj_fanin(module, reg_in, 0); + uint32_t width = sn_obj_width(module, reg_out); + uint32_t flags = sn_obj_reg_flags(module, reg_out); + int* source = sn_blast_eval(context, data); + int* result = sn_blast_alloc_bits(width); + sn_blast_copy(result, source, width); + + sn_obj_id_t enable = sn_obj_fanin(module, reg_out, SN_REG_ENABLE); + if (enable != SN_INVALID_ID) + { + int control = sn_blast_eval(context, enable)[0]; + int* state = sn_blast_eval(context, reg_out); + for (uint32_t bit = 0; bit < width; bit++) + result[bit] = Mini_AigMux(context->aig, control, result[bit], state[bit]); + } + + sn_obj_id_t set = sn_obj_fanin(module, reg_out, SN_REG_SET); + if (set != SN_INVALID_ID && !(flags & SN_REG_SET_ASYNC)) + { + int control = sn_blast_eval(context, set)[0]; + if (flags & SN_REG_SET_NEGEDGE) + control = Mini_AigLitNot(control); + for (uint32_t bit = 0; bit < width; bit++) + result[bit] = Mini_AigMux(context->aig, control, Mini_AigLitConst1(), result[bit]); + } + + sn_obj_id_t reset = sn_obj_fanin(module, reg_out, SN_REG_RESET); + if (reset != SN_INVALID_ID && !(flags & SN_REG_RESET_ASYNC)) + { + int control = sn_blast_eval(context, reset)[0]; + if (flags & SN_REG_RESET_NEGEDGE) + control = Mini_AigLitNot(control); + sn_obj_id_t value = sn_obj_fanin(module, reg_out, SN_REG_RESET_VALUE); + int* reset_bits = value == SN_INVALID_ID ? NULL : sn_blast_eval(context, value); + for (uint32_t bit = 0; bit < width; bit++) + result[bit] = Mini_AigMux(context->aig, control, + reset_bits ? reset_bits[bit] : Mini_AigLitConst0(), result[bit]); + } + return result; +} + +static inline void sn_blast_hier_destroy_frame(sn_blast_hier_frame_t* frame) +{ + for (sn_obj_id_t object = 0; object < frame->blast.module->obj_types.size; object++) + { + if (frame->children[object]) + sn_blast_hier_destroy_frame(frame->children[object]); + free(frame->blast.bits[object]); + } + free(frame->blast.bits); + free(frame->blast.state); + free(frame->children); + free(frame); +} + +// Derives one flat MiniAIG directly from a hierarchical SN design without first +// materializing a flat SN module. Every reachable module must be in SN +// topological order. A preliminary depth-first walk builds one lightweight +// object-to-literal frame per inst occurrence and counts top-level ports, +// flop bits, generic memories, and mapped RAM/DSP leaf insts. This permits +// all MiniAIG CIs to be created before the first AND: top PIs first, abstracted +// memory/DSP outputs next, and flop outputs last. The second depth-first walk +// binds each child PI to its inst fanin and bit-blasts child outputs in +// place. It emits top POs first, combinational register-control side outputs and +// abstract-box inputs next, and flop inputs last; the flop CIs and COs therefore +// occupy MiniAIG's required final positions. +// Every final MiniAIG node is created directly in this one manager: there is no +// temporary AIG, AIG duplication, or AIG-literal remapping pass. Consequently +// the AIG is flat while the usually much larger, attribute-rich collapsed SN +// module is never allocated. Generic memories and mapped RAM/DSP insts are +// black-boxed as extra CI/CO bundles according to the options. +// +// The boundary variant fills an initialized, empty descriptor whose stable +// occurrence/object/bit references survive destruction of the temporary frames. +// In combinational mode, synchronous enable/set/reset inputs become side COs +// before the raw register-data COs; clock and asynchronous controls are omitted. +// In sequential mode these synchronous controls are folded into the effective D +// function instead. The caller releases the descriptor with +// sn_blast_boundary_destroy(). +static inline Mini_Aig_t* sn_design_blast_hier_boundary_options(const sn_design_t* design, + sn_module_id_t top_module_id, + sn_blast_options_t options, + sn_blast_hier_stats_t* returned_stats, + sn_blast_boundary_t* boundary) +{ + assert(design && top_module_id < design->modules.size); + sn_blast_hier_t hierarchy; + memset(&hierarchy, 0, sizeof(hierarchy)); + hierarchy.design = design; + hierarchy.options = options; + hierarchy.boundary = boundary; + if (boundary) + assert(boundary->occurrences.size == 0 && boundary->primitives.size == 0 && + boundary->registers.size == 0 && boundary->loops.size == 0 && + boundary->cis.size == 0 && boundary->cos.size == 0); + hierarchy.active_modules = (uint8_t*)calloc(design->modules.size, sizeof(uint8_t)); + assert(hierarchy.active_modules); + sn_vec_init(&hierarchy.memory_reads); + sn_vec_init(&hierarchy.memory_writes); + sn_vec_init(&hierarchy.registers); + sn_vec_init(&hierarchy.loops); + sn_vec_init(&hierarchy.abstract_insts); + + sn_blast_hier_frame_t* root = + sn_blast_hier_build_frame(&hierarchy, top_module_id, SN_INVALID_ID, SN_INVALID_ID, NULL); + const sn_module_t* top = root->blast.module; + for (size_t i = 0; i < top->type_objects[SN_PI].size; i++) + hierarchy.stats.primary_input_bits += + sn_obj_width(top, sn_vec_at(sn_obj_id_t, &top->type_objects[SN_PI], i)); + for (size_t i = 0; i < top->type_objects[SN_PO].size; i++) + hierarchy.stats.primary_output_bits += + sn_obj_width(top, sn_vec_at(sn_obj_id_t, &top->type_objects[SN_PO], i)); + + hierarchy.aig = Mini_AigStart(); + sn_blast_hier_prepare_frame(root, hierarchy.aig, options); + for (size_t i = 0; i < top->type_objects[SN_PI].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &top->type_objects[SN_PI], i); + sn_blast_hier_seed_object(root, object, hierarchy.aig, false); + for (uint32_t bit = 0; bit < sn_obj_width(top, object); bit++) + sn_blast_boundary_add_bit(&hierarchy, true, SN_BLAST_BOUNDARY_TOP_PI, root, object, bit, + SN_INVALID_ID, (uint32_t)i); + } + for (size_t i = 0; i < hierarchy.memory_reads.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.memory_reads, i); + sn_blast_hier_seed_object(occurrence.frame, occurrence.object, hierarchy.aig, false); + for (uint32_t bit = 0; bit < sn_obj_width(occurrence.frame->blast.module, occurrence.object); bit++) + sn_blast_boundary_add_bit(&hierarchy, true, SN_BLAST_BOUNDARY_MEMORY_OUTPUT, occurrence.frame, + occurrence.object, bit, SN_INVALID_ID, (uint32_t)i); + } + for (size_t i = 0; i < hierarchy.abstract_insts.size; i++) + sn_blast_hier_seed_abstract_inst( + sn_vec_at(sn_blast_hier_object_t, &hierarchy.abstract_insts, i), hierarchy.aig); + for (size_t i = 0; i < hierarchy.loops.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.loops, i); + sn_blast_hier_seed_object(occurrence.frame, occurrence.object, hierarchy.aig, false); + for (uint32_t bit = 0; bit < sn_obj_width(occurrence.frame->blast.module, occurrence.object); bit++) + sn_blast_boundary_add_bit(&hierarchy, true, SN_BLAST_BOUNDARY_LOOP_OUTPUT, occurrence.frame, + occurrence.object, bit, occurrence.boundary_owner, bit); + } + for (size_t i = 0; i < hierarchy.registers.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.registers, i); + if (boundary) + sn_vec_at(sn_blast_register_t, &boundary->registers, occurrence.boundary_owner).ci_begin = + (uint32_t)boundary->cis.size; + sn_blast_hier_seed_object(occurrence.frame, occurrence.object, hierarchy.aig, false); + if (sn_blast_mode_has_transition(options.mode)) + for (uint32_t bit = 0; + bit < sn_obj_width(occurrence.frame->blast.module, occurrence.object); bit++) + if (sn_blast_reg_init_bit(occurrence.frame->blast.module, occurrence.object, bit)) + occurrence.frame->blast.bits[occurrence.object][bit] = + Mini_AigLitNot(occurrence.frame->blast.bits[occurrence.object][bit]); + for (uint32_t bit = 0; bit < sn_obj_width(occurrence.frame->blast.module, occurrence.object); bit++) + sn_blast_boundary_add_bit(&hierarchy, true, SN_BLAST_BOUNDARY_REG_OUTPUT, occurrence.frame, + occurrence.object, bit, occurrence.boundary_owner, bit); + } + assert((uint64_t)Mini_AigPiNum(hierarchy.aig) == hierarchy.stats.primary_input_bits + + hierarchy.stats.abstraction_output_bits + + hierarchy.stats.flop_bits); + + // Evaluate every emitted CO cone before creating the first PO. MiniAIG's + // normalized form requires all AND nodes to precede all POs. This selective + // preparation also avoids elaborating clock and asynchronous-control cones. + int** reg_next = (int**)calloc(hierarchy.registers.size, sizeof(int*)); + assert(reg_next || hierarchy.registers.size == 0); + for (size_t i = 0; i < top->type_objects[SN_PO].size; i++) + sn_blast_eval(&root->blast, sn_vec_at(sn_obj_id_t, &top->type_objects[SN_PO], i)); + for (size_t i = 0; i < hierarchy.registers.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.registers, i); + const sn_module_t* module = occurrence.frame->blast.module; + if (sn_blast_mode_has_transition(options.mode)) + reg_next[i] = sn_blast_hier_reg_next(occurrence); + else + { + sn_obj_id_t reg_in = sn_obj_fanin(module, occurrence.object, SN_REG_DATA); + sn_blast_eval(&occurrence.frame->blast, sn_obj_fanin(module, reg_in, 0)); + const uint32_t slots[] = {SN_REG_ENABLE, SN_REG_SET, SN_REG_RESET, SN_REG_RESET_VALUE}; + for (size_t k = 0; k < sizeof(slots) / sizeof(slots[0]); k++) + if (sn_blast_reg_control_is_comb_output(module, occurrence.object, slots[k])) + { + sn_obj_id_t fanin = sn_obj_fanin(module, occurrence.object, slots[k]); + if (fanin != SN_INVALID_ID) + sn_blast_eval(&occurrence.frame->blast, fanin); + } + } + } + for (size_t i = 0; i < hierarchy.memory_reads.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.memory_reads, i); + const sn_module_t* module = occurrence.frame->blast.module; + for (uint32_t slot = SN_MEM_READ_CLOCK; slot < SN_MEM_READ_FANIN_COUNT; slot++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, occurrence.object, slot); + if (fanin != SN_INVALID_ID) + sn_blast_eval(&occurrence.frame->blast, fanin); + } + } + for (size_t i = 0; i < hierarchy.memory_writes.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.memory_writes, i); + const sn_module_t* module = occurrence.frame->blast.module; + for (uint32_t slot = SN_MEM_WRITE_CLOCK; slot < SN_MEM_WRITE_FANIN_COUNT; slot++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, occurrence.object, slot); + if (fanin != SN_INVALID_ID) + sn_blast_eval(&occurrence.frame->blast, fanin); + } + } + for (size_t i = 0; i < hierarchy.abstract_insts.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.abstract_insts, i); + const sn_module_t* module = occurrence.frame->blast.module; + for (uint32_t slot = 0; slot < sn_obj_fanin_count(module, occurrence.object); slot++) + sn_blast_eval(&occurrence.frame->blast, sn_obj_fanin(module, occurrence.object, slot)); + } + for (size_t i = 0; i < hierarchy.loops.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.loops, i); + const sn_module_t* module = occurrence.frame->blast.module; + sn_obj_id_t loop_in = sn_obj_pair_in(module, occurrence.object); + sn_blast_eval(&occurrence.frame->blast, sn_obj_fanin(module, loop_in, 0)); + } + + for (size_t i = 0; i < top->type_objects[SN_PO].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &top->type_objects[SN_PO], i); + int* bits = sn_blast_eval(&root->blast, object); + for (uint32_t bit = 0; bit < sn_obj_width(top, object); bit++) + { + Mini_AigCreatePo(hierarchy.aig, bits[bit]); + sn_blast_boundary_add_bit(&hierarchy, false, SN_BLAST_BOUNDARY_TOP_PO, root, object, bit, + SN_INVALID_ID, (uint32_t)i); + } + } + for (size_t i = 0; i < hierarchy.registers.size; i++) + sn_blast_hier_emit_register_controls( + sn_vec_at(sn_blast_hier_object_t, &hierarchy.registers, i), hierarchy.aig); + for (size_t i = 0; i < hierarchy.memory_reads.size; i++) + sn_blast_hier_emit_memory_inputs(sn_vec_at(sn_blast_hier_object_t, &hierarchy.memory_reads, i), + hierarchy.aig); + for (size_t i = 0; i < hierarchy.memory_writes.size; i++) + sn_blast_hier_emit_memory_inputs(sn_vec_at(sn_blast_hier_object_t, &hierarchy.memory_writes, i), + hierarchy.aig); + for (size_t i = 0; i < hierarchy.abstract_insts.size; i++) + { + sn_blast_hier_object_t occurrence = + sn_vec_at(sn_blast_hier_object_t, &hierarchy.abstract_insts, i); + const sn_module_t* module = occurrence.frame->blast.module; + sn_blast_primitive_t* primitive = NULL; + if (boundary) + { + primitive = &sn_vec_at(sn_blast_primitive_t, &boundary->primitives, occurrence.boundary_owner); + // As above, the boundary vector provides a constant-time running count; Mini_AigPoNum() is linear. + primitive->co_begin = (uint32_t)boundary->cos.size; + } + for (uint32_t slot = 0; slot < sn_obj_fanin_count(module, occurrence.object); slot++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, occurrence.object, slot); + int* bits = sn_blast_eval(&occurrence.frame->blast, fanin); + for (uint32_t bit = 0; bit < sn_obj_width(module, fanin); bit++) + { + Mini_AigCreatePo(hierarchy.aig, bits[bit]); + sn_blast_boundary_add_bit(&hierarchy, false, SN_BLAST_BOUNDARY_PRIMITIVE_INPUT, + occurrence.frame, fanin, bit, occurrence.boundary_owner, slot); + } + } + if (primitive) + primitive->co_count = (uint32_t)boundary->cos.size - primitive->co_begin; + } + for (size_t i = 0; i < hierarchy.loops.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.loops, i); + const sn_module_t* module = occurrence.frame->blast.module; + sn_obj_id_t loop_in = sn_obj_pair_in(module, occurrence.object); + sn_obj_id_t data = sn_obj_fanin(module, loop_in, 0); + int* bits = sn_blast_eval(&occurrence.frame->blast, data); + if (boundary) + sn_vec_at(sn_blast_loop_t, &boundary->loops, occurrence.boundary_owner).co_begin = + (uint32_t)boundary->cos.size; + for (uint32_t bit = 0; bit < sn_obj_width(module, occurrence.object); bit++) + { + Mini_AigCreatePo(hierarchy.aig, bits[bit]); + sn_blast_boundary_add_bit(&hierarchy, false, SN_BLAST_BOUNDARY_LOOP_INPUT, occurrence.frame, + data, bit, occurrence.boundary_owner, bit); + } + } + + // Register inputs are deliberately emitted last; nRegs pairs the final CIs and final COs. + for (size_t i = 0; i < hierarchy.registers.size; i++) + { + sn_blast_hier_object_t occurrence = sn_vec_at(sn_blast_hier_object_t, &hierarchy.registers, i); + const sn_module_t* module = occurrence.frame->blast.module; + sn_obj_id_t reg_in = sn_obj_fanin(module, occurrence.object, SN_REG_DATA); + sn_obj_id_t data = sn_obj_fanin(module, reg_in, 0); + int* bits = sn_blast_mode_has_transition(options.mode) ? reg_next[i] + : sn_blast_eval(&occurrence.frame->blast, data); + if (boundary) + sn_vec_at(sn_blast_register_t, &boundary->registers, occurrence.boundary_owner).co_begin = + (uint32_t)boundary->cos.size; + for (uint32_t bit = 0; bit < sn_obj_width(module, occurrence.object); bit++) + { + bool invert = sn_blast_mode_has_transition(options.mode) && + sn_blast_reg_init_bit(module, occurrence.object, bit); + Mini_AigCreatePo(hierarchy.aig, invert ? Mini_AigLitNot(bits[bit]) : bits[bit]); + sn_blast_boundary_add_bit(&hierarchy, false, SN_BLAST_BOUNDARY_REG_INPUT, occurrence.frame, + data, bit, occurrence.boundary_owner, bit); + } + if (sn_blast_mode_has_transition(options.mode)) + free(reg_next[i]); + } + free(reg_next); + + Mini_AigSetRegNum(hierarchy.aig, options.mode == SN_BLAST_SEQ ? (int)hierarchy.stats.flop_bits : 0); + assert(Mini_AigIsNormalized(hierarchy.aig)); + if (boundary) + { + boundary->register_bits = (uint32_t)Mini_AigRegNum(hierarchy.aig); + assert(boundary->cis.size == (size_t)Mini_AigPiNum(hierarchy.aig)); + assert(boundary->cos.size == (size_t)Mini_AigPoNum(hierarchy.aig)); + } + if (returned_stats) + *returned_stats = hierarchy.stats; + Mini_Aig_t* result = hierarchy.aig; + sn_blast_hier_destroy_frame(root); + sn_vec_destroy(&hierarchy.memory_reads); + sn_vec_destroy(&hierarchy.memory_writes); + sn_vec_destroy(&hierarchy.registers); + sn_vec_destroy(&hierarchy.loops); + sn_vec_destroy(&hierarchy.abstract_insts); + free(hierarchy.active_modules); + return result; +} + +static inline Mini_Aig_t* sn_design_blast_hier_options(const sn_design_t* design, sn_module_id_t top_module_id, + sn_blast_options_t options, + sn_blast_hier_stats_t* returned_stats) +{ + return sn_design_blast_hier_boundary_options(design, top_module_id, options, returned_stats, NULL); +} + +static inline Mini_Aig_t* sn_design_blast_hier(const sn_design_t* design, sn_module_id_t top_module_id) +{ + return sn_design_blast_hier_options(design, top_module_id, sn_blast_default_options(), NULL); +} + +static inline Mini_Aig_t* sn_design_blast_hier_seq_options(const sn_design_t* design, sn_module_id_t top_module_id, + sn_blast_options_t options, + sn_blast_hier_stats_t* returned_stats) +{ + options.mode = SN_BLAST_SEQ; + return sn_design_blast_hier_options(design, top_module_id, options, returned_stats); +} + +static inline Mini_Aig_t* sn_design_blast_hier_seq(const sn_design_t* design, sn_module_id_t top_module_id) +{ + return sn_design_blast_hier_seq_options(design, top_module_id, sn_blast_default_options(), NULL); +} + +static inline Mini_Aig_t* sn_design_blast_hier_transition(const sn_design_t* design, + sn_module_id_t top_module_id) +{ + sn_blast_options_t options = sn_blast_default_options(); + options.mode = SN_BLAST_TRANSITION; + return sn_design_blast_hier_options(design, top_module_id, options, NULL); +} + +// Keep the standalone-module API as a thin adapter around the hierarchical +// driver so register controls, loop boundaries, and initialization semantics +// cannot drift between the two exported blasting paths. +static inline Mini_Aig_t* sn_module_blast_comb_options(const sn_module_t* module, + sn_blast_options_t options) +{ + sn_blast_check_module(module, options); + sn_design_t wrapper; + memset(&wrapper, 0, sizeof(wrapper)); + sn_vec_init(&wrapper.modules); + *sn_vec_push(sn_module_t*, &wrapper.modules) = (sn_module_t*)module; + Mini_Aig_t* aig = sn_design_blast_hier_options(&wrapper, 0, options, NULL); + sn_vec_destroy(&wrapper.modules); + return aig; +} + +static inline Mini_Aig_t* sn_design_blast_comb_options(sn_design_t* design, sn_module_id_t module_id, + sn_blast_options_t options) +{ + assert(design); + assert(module_id < design->modules.size); + return sn_design_blast_hier_options(design, module_id, options, NULL); +} + +static inline Mini_Aig_t* sn_design_blast_comb(sn_design_t* design, sn_module_id_t module_id) +{ + return sn_design_blast_comb_options(design, module_id, sn_blast_default_options()); +} + +static inline Mini_Aig_t* sn_design_blast_seq_options(sn_design_t* design, sn_module_id_t module_id, + sn_blast_options_t options) +{ + options.mode = SN_BLAST_SEQ; + return sn_design_blast_comb_options(design, module_id, options); +} + +static inline Mini_Aig_t* sn_design_blast_seq(sn_design_t* design, sn_module_id_t module_id) +{ + return sn_design_blast_seq_options(design, module_id, sn_blast_default_options()); +} + +static inline void sn_module_write_aiger(const sn_module_t* module, const char* file_name, + sn_blast_options_t options) +{ + assert(module && file_name); + Mini_Aig_t* aig = sn_module_blast_comb_options(module, options); + Mini_AigerWrite((char*)file_name, aig, 0); + Mini_AigStop(aig); +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snBoundary.h b/src/base/sn/snBoundary.h new file mode 100644 index 000000000..d955a7281 --- /dev/null +++ b/src/base/sn/snBoundary.h @@ -0,0 +1,874 @@ +/**CFile**************************************************************** + + FileName [snBoundary.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Reconstruction and reconnection of extracted combinational boundaries.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snBoundary.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__base__sn__snBoundary_h +#define ABC__base__sn__snBoundary_h + +#include "snBlast.h" + +ABC_NAMESPACE_HEADER_START + +typedef struct sn_boundary_regs_t +{ + sn_design_t* design; + const sn_blast_boundary_t* boundary; + sn_module_t* result; + sn_obj_id_t* top_inputs; + sn_obj_pair_t* pairs; + sn_obj_pair_t* loops; + sn_obj_pair_t* primitive_pairs; + uint32_t* primitive_offsets; + sn_obj_id_t** external_copies; + struct sn_boundary_link_t* links; + size_t link_cap; +} sn_boundary_regs_t; + +typedef struct sn_boundary_link_t +{ + uint64_t key; + uint32_t primitive; + uint32_t child; + uint32_t reg; + uint32_t loop; +} sn_boundary_link_t; + +typedef struct sn_boundary_external_frame_t +{ + sn_blast_hier_ref_t ref; + sn_blast_hier_ref_t dependency; + sn_obj_id_t result; + uint32_t next_fanin; + uint8_t phase; +} sn_boundary_external_frame_t; + +enum +{ + SN_BOUNDARY_EXTERNAL_START, + SN_BOUNDARY_EXTERNAL_ALIAS, + SN_BOUNDARY_EXTERNAL_OPERATOR +}; + +static inline uint64_t sn_boundary_link_key(uint32_t occurrence, sn_obj_id_t object) +{ + return ((uint64_t)occurrence << 32) | object; +} + +static inline size_t sn_boundary_link_hash(uint64_t key, size_t mask) +{ + key ^= key >> 33; + key *= UINT64_C(0xff51afd7ed558ccd); + key ^= key >> 33; + return (size_t)key & mask; +} + +static inline sn_boundary_link_t* sn_boundary_link_find(sn_boundary_regs_t* regs, uint32_t occurrence, + sn_obj_id_t object, bool create) +{ + if (!regs->link_cap) + return NULL; + uint64_t key = sn_boundary_link_key(occurrence, object); + size_t slot = sn_boundary_link_hash(key, regs->link_cap - 1); + while (regs->links[slot].key != UINT64_MAX && regs->links[slot].key != key) + slot = (slot + 1) & (regs->link_cap - 1); + if (regs->links[slot].key == UINT64_MAX) + { + if (!create) + return NULL; + regs->links[slot].key = key; + } + return ®s->links[slot]; +} + +static inline sn_obj_id_t* sn_boundary_external_copies(sn_boundary_regs_t* regs, sn_blast_hier_ref_t ref, + const sn_module_t** returned_module) +{ + const sn_blast_occurrence_t* occurrence = + &sn_vec_at(sn_blast_occurrence_t, ®s->boundary->occurrences, ref.occurrence); + const sn_module_t* module = sn_design_get_module_const(regs->design, occurrence->module); + sn_obj_id_t* copies = regs->external_copies[ref.occurrence]; + assert(ref.object < module->obj_types.size); + if (!copies) + { + copies = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * module->obj_types.size); + assert(copies); + for (size_t i = 0; i < module->obj_types.size; i++) + copies[i] = SN_INVALID_ID; + regs->external_copies[ref.occurrence] = copies; + } + if (returned_module) + *returned_module = module; + return copies; +} + +static inline sn_obj_id_t sn_boundary_pack_bits(sn_module_t* module, const sn_obj_id_t* bits, uint32_t width, + const char* name) +{ + assert(module && bits && width); + return width == 1 ? bits[0] : sn_module_add_operator(module, SN_CONCAT, width, false, width, bits, name); +} + +static inline sn_blast_hier_ref_t sn_boundary_parent_ref(const sn_design_t* design, + const sn_blast_boundary_t* boundary, + sn_blast_hier_ref_t ref) +{ + const sn_blast_occurrence_t* occurrence; + const sn_module_t* module; + const sn_module_t* parent; + sn_obj_id_t parent_fanin; + assert(ref.occurrence < boundary->occurrences.size); + occurrence = &sn_vec_at(sn_blast_occurrence_t, &boundary->occurrences, ref.occurrence); + module = sn_design_get_module_const(design, occurrence->module); + assert(sn_obj_type(module, ref.object) == SN_PI && occurrence->parent_occurrence != SN_INVALID_ID); + parent = sn_design_get_module_const( + design, sn_vec_at(sn_blast_occurrence_t, &boundary->occurrences, occurrence->parent_occurrence).module); + parent_fanin = sn_obj_fanin(parent, occurrence->parent_inst, sn_obj_type_id(module, ref.object)); + ref.occurrence = occurrence->parent_occurrence; + ref.object = parent_fanin; + return ref; +} + +// Resolves clocks, asynchronous controls, and initialization constants that @blast intentionally leaves outside the +// combinational cloud. Hierarchical PI bindings are followed to the root. Generated combinational control cones are +// copied and memoized per hierarchy occurrence; an explicit DFS stack avoids overflowing the C stack on deep control +// cones. The link table resolves inst, primitive, register, and loop endpoints in expected constant time. +static inline sn_obj_id_t sn_boundary_resolve_external(sn_boundary_regs_t* regs, sn_blast_hier_ref_t ref) +{ + sn_blast_hier_ref_t root = ref; + sn_vec_t stack; + sn_vec_init(&stack); + sn_boundary_external_frame_t* initial = sn_vec_push(sn_boundary_external_frame_t, &stack); + memset(initial, 0, sizeof(*initial)); + initial->ref = ref; + while (stack.size) + { + sn_boundary_external_frame_t* frame = + &sn_vec_at(sn_boundary_external_frame_t, &stack, stack.size - 1); + ref = frame->ref; + const sn_blast_occurrence_t* occurrence = + &sn_vec_at(sn_blast_occurrence_t, ®s->boundary->occurrences, ref.occurrence); + const sn_module_t* module; + sn_obj_id_t* copies = sn_boundary_external_copies(regs, ref, &module); + if (frame->phase == SN_BOUNDARY_EXTERNAL_ALIAS) + { + const sn_module_t* dependency_module; + sn_obj_id_t* dependency_copies = + sn_boundary_external_copies(regs, frame->dependency, &dependency_module); + (void)dependency_module; + assert(dependency_copies[frame->dependency.object] != SN_INVALID_ID); + copies[ref.object] = dependency_copies[frame->dependency.object]; + stack.size--; + continue; + } + if (frame->phase == SN_BOUNDARY_EXTERNAL_OPERATOR) + { + uint32_t count = sn_obj_fanin_count(module, ref.object); + if (frame->next_fanin == count) + { + stack.size--; + continue; + } + uint32_t index = frame->next_fanin; + sn_obj_id_t old_fanin = sn_obj_fanin(module, ref.object, index); + if (old_fanin == SN_INVALID_ID) + { + sn_obj_connect(regs->result, frame->result, index, SN_INVALID_ID); + frame->next_fanin++; + continue; + } + sn_blast_hier_ref_t dependency = {ref.occurrence, old_fanin, 0}; + sn_obj_id_t* dependency_copies = sn_boundary_external_copies(regs, dependency, NULL); + if (dependency_copies[old_fanin] != SN_INVALID_ID) + { + sn_obj_connect(regs->result, frame->result, index, dependency_copies[old_fanin]); + frame->next_fanin++; + continue; + } + sn_boundary_external_frame_t* child = sn_vec_push(sn_boundary_external_frame_t, &stack); + memset(child, 0, sizeof(*child)); + child->ref = dependency; + continue; + } + if (copies[ref.object] != SN_INVALID_ID) + { + stack.size--; + continue; + } + sn_obj_type_t type = sn_obj_type(module, ref.object); + if (type == SN_PI && occurrence->parent_occurrence != SN_INVALID_ID) + { + frame->phase = SN_BOUNDARY_EXTERNAL_ALIAS; + frame->dependency = sn_boundary_parent_ref(regs->design, regs->boundary, ref); + sn_obj_id_t* dependency_copies = sn_boundary_external_copies(regs, frame->dependency, NULL); + if (dependency_copies[frame->dependency.object] == SN_INVALID_ID) + { + sn_boundary_external_frame_t* child = sn_vec_push(sn_boundary_external_frame_t, &stack); + memset(child, 0, sizeof(*child)); + child->ref = frame->dependency; + } + continue; + } + if (type == SN_PI) + { + assert(ref.occurrence == 0 && regs->top_inputs[ref.object] != SN_INVALID_ID); + copies[ref.object] = regs->top_inputs[ref.object]; + stack.size--; + continue; + } + if (type == SN_INST || type == SN_FAN) + { + sn_obj_id_t inst = type == SN_INST ? ref.object : sn_fan_inst_id(module, ref.object); + uint32_t output = type == SN_INST ? 0 : sn_fan_output_index(module, ref.object); + sn_boundary_link_t* link = sn_boundary_link_find(regs, ref.occurrence, inst, false); + assert(link); + if (link->primitive != SN_INVALID_ID) + { + const sn_blast_primitive_t* primitive = + &sn_vec_at(sn_blast_primitive_t, ®s->boundary->primitives, link->primitive); + assert(output < sn_design_module_output_count(regs->design, primitive->module)); + (void)primitive; + copies[ref.object] = regs->primitive_pairs[regs->primitive_offsets[link->primitive] + output].out; + stack.size--; + continue; + } + if (link->child != SN_INVALID_ID) + { + const sn_blast_occurrence_t* child_occurrence = + &sn_vec_at(sn_blast_occurrence_t, ®s->boundary->occurrences, link->child); + const sn_module_t* child = sn_design_get_module_const(regs->design, child_occurrence->module); + assert(output < child->type_objects[SN_PO].size); + sn_obj_id_t child_po = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PO], output); + sn_obj_id_t child_fanin = sn_obj_fanin(child, child_po, 0); + assert(child_fanin != SN_INVALID_ID); + frame->phase = SN_BOUNDARY_EXTERNAL_ALIAS; + frame->dependency.occurrence = link->child; + frame->dependency.object = child_fanin; + frame->dependency.bit = 0; + sn_obj_id_t* dependency_copies = sn_boundary_external_copies(regs, frame->dependency, NULL); + if (dependency_copies[child_fanin] == SN_INVALID_ID) + { + sn_boundary_external_frame_t* child_frame = + sn_vec_push(sn_boundary_external_frame_t, &stack); + memset(child_frame, 0, sizeof(*child_frame)); + child_frame->ref = frame->dependency; + } + continue; + } + assert(false); + } + if (type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST) + { + uint32_t width = sn_obj_width(module, ref.object); + uint32_t* words = (uint32_t*)calloc(sn_const_word_count(width), sizeof(uint32_t)); + assert(words); + for (uint32_t bit = 0; bit < width; bit++) + words[bit >> 5] |= (uint32_t)sn_const_bit(module, ref.object, bit) << (bit & 31); + const char* name = sn_obj_name_id(module, ref.object) == SN_INVALID_ID + ? NULL + : sn_obj_name(module, ref.object); + sn_obj_id_t result = sn_module_add_const(regs->result, width, sn_obj_is_signed(module, ref.object), words, + name); + free(words); + copies[ref.object] = result; + stack.size--; + continue; + } + if (type == SN_REG_OUT) + { + sn_boundary_link_t* link = sn_boundary_link_find(regs, ref.occurrence, ref.object, false); + assert(link && link->reg != SN_INVALID_ID); + copies[ref.object] = regs->pairs[link->reg].out; + stack.size--; + continue; + } + if (type == SN_LOOP_OUT) + { + sn_boundary_link_t* link = sn_boundary_link_find(regs, ref.occurrence, ref.object, false); + assert(link && link->loop != SN_INVALID_ID); + copies[ref.object] = regs->loops[link->loop].out; + stack.size--; + continue; + } + assert(type == SN_BUF || (type >= SN_POS && type <= SN_GATE)); + frame->result = sn_module_dup_obj_skeleton(regs->result, module, ref.object); + copies[ref.object] = frame->result; + sn_module_dup_obj_metadata(regs->result, sn_obj_type_id(regs->result, frame->result), module, ref.object); + frame->phase = SN_BOUNDARY_EXTERNAL_OPERATOR; + frame->next_fanin = 0; + } + sn_obj_id_t* root_copies = sn_boundary_external_copies(regs, root, NULL); + assert(root_copies[root.object] != SN_INVALID_ID); + sn_obj_id_t result = root_copies[root.object]; + sn_vec_destroy(&stack); + return result; +} + +static inline void sn_boundary_regs_init(sn_boundary_regs_t* regs, sn_design_t* design, + const sn_blast_boundary_t* boundary, sn_module_t* result, + sn_obj_id_t* top_inputs) +{ + assert(regs && design && boundary && result && top_inputs); + regs->design = design; + regs->boundary = boundary; + regs->result = result; + regs->top_inputs = top_inputs; + regs->external_copies = boundary->occurrences.size + ? (sn_obj_id_t**)calloc(boundary->occurrences.size, sizeof(sn_obj_id_t*)) + : NULL; + assert(regs->external_copies || boundary->occurrences.size == 0); + regs->links = NULL; + regs->link_cap = 0; + regs->primitive_offsets = boundary->primitives.size + ? (uint32_t*)malloc(sizeof(uint32_t) * (boundary->primitives.size + 1)) + : NULL; + assert(regs->primitive_offsets || boundary->primitives.size == 0); + uint32_t primitive_output_count = 0; + for (size_t i = 0; i < boundary->primitives.size; i++) + { + const sn_blast_primitive_t* entry = &sn_vec_at(sn_blast_primitive_t, &boundary->primitives, i); + regs->primitive_offsets[i] = primitive_output_count; + primitive_output_count += sn_design_module_output_count(design, entry->module); + } + if (boundary->primitives.size) + regs->primitive_offsets[boundary->primitives.size] = primitive_output_count; + regs->primitive_pairs = primitive_output_count + ? (sn_obj_pair_t*)malloc(sizeof(sn_obj_pair_t) * primitive_output_count) + : NULL; + assert(regs->primitive_pairs || primitive_output_count == 0); + for (size_t i = 0; i < boundary->primitives.size; i++) + { + const sn_blast_primitive_t* entry = &sn_vec_at(sn_blast_primitive_t, &boundary->primitives, i); + const sn_blast_occurrence_t* occurrence = + &sn_vec_at(sn_blast_occurrence_t, &boundary->occurrences, entry->occurrence); + const sn_module_t* module = sn_design_get_module_const(design, occurrence->module); + const sn_module_t* child = sn_design_get_module_const(design, entry->module); + for (size_t output = 0; output < child->type_objects[SN_PO].size; output++) + { + sn_obj_id_t old_output = child->type_objects[SN_PO].size == 1 + ? entry->inst + : sn_inst_output(module, entry->inst, (uint32_t)output); + const char* output_name = sn_obj_name_id(module, old_output) == SN_INVALID_ID + ? NULL + : sn_obj_name(module, old_output); + regs->primitive_pairs[regs->primitive_offsets[i] + output] = + sn_module_add_loop_pair(result, sn_obj_width(module, old_output), + sn_obj_is_signed(module, old_output), output_name, "primitive_boundary_input"); + } + } + regs->pairs = boundary->registers.size + ? (sn_obj_pair_t*)malloc(sizeof(sn_obj_pair_t) * boundary->registers.size) + : NULL; + assert(regs->pairs || boundary->registers.size == 0); + for (size_t i = 0; i < boundary->registers.size; i++) + { + const sn_blast_register_t* entry = &sn_vec_at(sn_blast_register_t, &boundary->registers, i); + const sn_blast_occurrence_t* occurrence = + &sn_vec_at(sn_blast_occurrence_t, &boundary->occurrences, entry->occurrence); + const sn_module_t* module = sn_design_get_module_const(design, occurrence->module); + sn_obj_id_t old_out = entry->reg_out; + sn_obj_id_t old_in = sn_obj_pair_in(module, old_out); + const char* out_name = sn_obj_name_id(module, old_out) == SN_INVALID_ID ? NULL : sn_obj_name(module, old_out); + const char* in_name = sn_obj_name_id(module, old_in) == SN_INVALID_ID ? NULL : sn_obj_name(module, old_in); + regs->pairs[i] = sn_module_add_reg_pair(result, entry->width, sn_obj_is_signed(module, old_out), + out_name, in_name, SN_INVALID_ID); + sn_reg_set_flags(result, regs->pairs[i].out, sn_obj_reg_flags(module, old_out)); + } + regs->loops = boundary->loops.size ? (sn_obj_pair_t*)malloc(sizeof(sn_obj_pair_t) * boundary->loops.size) : NULL; + assert(regs->loops || boundary->loops.size == 0); + for (size_t i = 0; i < boundary->loops.size; i++) + { + const sn_blast_loop_t* entry = &sn_vec_at(sn_blast_loop_t, &boundary->loops, i); + const sn_blast_occurrence_t* occurrence = + &sn_vec_at(sn_blast_occurrence_t, &boundary->occurrences, entry->occurrence); + const sn_module_t* module = sn_design_get_module_const(design, occurrence->module); + sn_obj_id_t old_in = sn_obj_pair_in(module, entry->loop_out); + const char* out_name = sn_obj_name_id(module, entry->loop_out) == SN_INVALID_ID + ? NULL + : sn_obj_name(module, entry->loop_out); + const char* in_name = sn_obj_name_id(module, old_in) == SN_INVALID_ID ? NULL : sn_obj_name(module, old_in); + regs->loops[i] = sn_module_add_loop_pair(result, entry->width, + sn_obj_is_signed(module, entry->loop_out), out_name, in_name); + } + + size_t link_count = boundary->primitives.size + boundary->registers.size + boundary->loops.size; + if (boundary->occurrences.size) + link_count += boundary->occurrences.size - 1; + if (link_count) + { + regs->link_cap = 2; + while (regs->link_cap < 2 * link_count) + regs->link_cap <<= 1; + regs->links = (sn_boundary_link_t*)malloc(regs->link_cap * sizeof(sn_boundary_link_t)); + assert(regs->links); + for (size_t i = 0; i < regs->link_cap; i++) + { + regs->links[i].key = UINT64_MAX; + regs->links[i].primitive = SN_INVALID_ID; + regs->links[i].child = SN_INVALID_ID; + regs->links[i].reg = SN_INVALID_ID; + regs->links[i].loop = SN_INVALID_ID; + } + for (size_t i = 0; i < boundary->primitives.size; i++) + { + const sn_blast_primitive_t* entry = &sn_vec_at(sn_blast_primitive_t, &boundary->primitives, i); + sn_boundary_link_find(regs, entry->occurrence, entry->inst, true)->primitive = (uint32_t)i; + } + for (size_t i = 1; i < boundary->occurrences.size; i++) + { + const sn_blast_occurrence_t* entry = &sn_vec_at(sn_blast_occurrence_t, &boundary->occurrences, i); + sn_boundary_link_find(regs, entry->parent_occurrence, entry->parent_inst, true)->child = (uint32_t)i; + } + for (size_t i = 0; i < boundary->registers.size; i++) + { + const sn_blast_register_t* entry = &sn_vec_at(sn_blast_register_t, &boundary->registers, i); + sn_boundary_link_find(regs, entry->occurrence, entry->reg_out, true)->reg = (uint32_t)i; + } + for (size_t i = 0; i < boundary->loops.size; i++) + { + const sn_blast_loop_t* entry = &sn_vec_at(sn_blast_loop_t, &boundary->loops, i); + sn_boundary_link_find(regs, entry->occurrence, entry->loop_out, true)->loop = (uint32_t)i; + } + } +} + +static inline sn_obj_id_t sn_boundary_primitive_output_bit(sn_boundary_regs_t* regs, uint32_t owner, + uint32_t port, uint32_t bit) +{ + assert(owner < regs->boundary->primitives.size); + const sn_blast_primitive_t* entry = &sn_vec_at(sn_blast_primitive_t, ®s->boundary->primitives, owner); + assert(port < sn_design_module_output_count(regs->design, entry->module)); + (void)entry; + sn_obj_id_t output = regs->primitive_pairs[regs->primitive_offsets[owner] + port].out; + assert(bit < sn_obj_width(regs->result, output)); + return sn_module_add_slice(regs->result, output, (int32_t)bit, (int32_t)bit, "primitive_output_bit"); +} + +static inline sn_obj_id_t sn_boundary_reg_output_bit(sn_boundary_regs_t* regs, uint32_t owner, uint32_t bit) +{ + assert(owner < regs->boundary->registers.size); + assert(bit < sn_obj_width(regs->result, regs->pairs[owner].out)); + return sn_module_add_slice(regs->result, regs->pairs[owner].out, (int32_t)bit, (int32_t)bit, "reg_q_bit"); +} + +static inline sn_obj_id_t sn_boundary_loop_output_bit(sn_boundary_regs_t* regs, uint32_t owner, uint32_t bit) +{ + assert(owner < regs->boundary->loops.size); + assert(bit < sn_obj_width(regs->result, regs->loops[owner].out)); + return sn_module_add_slice(regs->result, regs->loops[owner].out, (int32_t)bit, (int32_t)bit, "loop_q_bit"); +} + +static inline sn_obj_id_t sn_boundary_co_word(sn_boundary_regs_t* regs, const sn_obj_id_t* co_drivers, + uint32_t begin, sn_blast_boundary_kind_t kind, uint32_t owner, + uint32_t port, uint32_t width) +{ + assert(begin <= regs->boundary->cos.size && width <= regs->boundary->cos.size - begin); + for (uint32_t bit = 0; bit < width; bit++) + { + sn_blast_boundary_bit_t endpoint = + sn_vec_at(sn_blast_boundary_bit_t, ®s->boundary->cos, begin + bit); + assert(endpoint.kind == kind && endpoint.owner == owner && + (port == SN_INVALID_ID || endpoint.port == port) && endpoint.signal.bit == bit); + } + return sn_boundary_pack_bits(regs->result, co_drivers + begin, width, "boundary_word"); +} + +typedef struct sn_boundary_dfs_frame_t +{ + sn_obj_id_t object; + uint32_t next_fanout; +} sn_boundary_dfs_frame_t; + +// Marks tentative primitive-output substitutions that create combinational feedback. All temporary pair outputs +// have already been replaced by the corresponding primitive outputs. One iterative Kosaraju traversal identifies +// the resulting strongly connected components; a substituted edge whose endpoints share a component must retain +// its loop pair. This replaces one complete cone walk per primitive output by linear whole-module graph work. +static inline void sn_boundary_mark_feedback_pairs(sn_module_t* module, const sn_obj_id_t* actual_to_pair, + uint8_t* keep) +{ + size_t object_count = module->obj_types.size; + uint8_t* visited = (uint8_t*)calloc(object_count, sizeof(uint8_t)); + uint32_t* components = object_count ? (uint32_t*)malloc(object_count * sizeof(uint32_t)) : NULL; + sn_vec_t order, stack; + assert(visited && (components || object_count == 0)); + sn_vec_init(&order); + sn_vec_init(&stack); + sn_vec_reserve(sn_obj_id_t, &order, object_count); + sn_module_build_fanouts(module); + + for (sn_obj_id_t start = 0; start < object_count; start++) + { + if (visited[start]) + continue; + visited[start] = 1; + sn_boundary_dfs_frame_t* first = sn_vec_push(sn_boundary_dfs_frame_t, &stack); + first->object = start; + first->next_fanout = 0; + while (stack.size) + { + sn_boundary_dfs_frame_t* frame = + &sn_vec_at(sn_boundary_dfs_frame_t, &stack, stack.size - 1); + uint32_t count = sn_obj_fanout_count(module, frame->object); + if (frame->next_fanout < count) + { + sn_obj_id_t fanout = sn_obj_fanout(module, frame->object, frame->next_fanout++); + if (!visited[fanout]) + { + visited[fanout] = 1; + sn_boundary_dfs_frame_t* child = sn_vec_push(sn_boundary_dfs_frame_t, &stack); + child->object = fanout; + child->next_fanout = 0; + } + continue; + } + *sn_vec_push(sn_obj_id_t, &order) = frame->object; + stack.size--; + } + } + + for (sn_obj_id_t object = 0; object < object_count; object++) + components[object] = UINT32_MAX; + uint32_t component_count = 0; + for (size_t i = order.size; i-- > 0;) + { + sn_obj_id_t start = sn_vec_at(sn_obj_id_t, &order, i); + if (components[start] != UINT32_MAX) + continue; + components[start] = component_count; + *sn_vec_push(sn_obj_id_t, &stack) = start; + while (stack.size) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &stack, --stack.size); + for (uint32_t k = 0; k < sn_obj_fanin_count(module, object); k++) + { + sn_obj_id_t fanin = sn_obj_fanin(module, object, k); + if (fanin != SN_INVALID_ID && components[fanin] == UINT32_MAX) + { + components[fanin] = component_count; + *sn_vec_push(sn_obj_id_t, &stack) = fanin; + } + } + } + component_count++; + } + + for (sn_obj_id_t object = 0; object < object_count; object++) + for (uint32_t i = 0; i < sn_obj_fanin_count(module, object); i++) + { + sn_obj_id_t actual = sn_obj_fanin(module, object, i); + sn_obj_id_t pair_out = actual == SN_INVALID_ID ? SN_INVALID_ID : actual_to_pair[actual]; + if (pair_out != SN_INVALID_ID && components[actual] == components[object]) + keep[pair_out] = 1; + } + + sn_module_invalidate_fanouts(module); + sn_vec_destroy(&order); + sn_vec_destroy(&stack); + free(components); + free(visited); +} + +// Duplicates a module in topological order while omitting an explicitly unreferenced set of objects. This is used +// to remove temporary primitive-output loop pairs after their consumers have been redirected to the real outputs. +static inline sn_module_id_t sn_boundary_dup_filtered_topo(sn_design_t* design, sn_module_id_t source_id, + const uint8_t* remove, const char* name) +{ + sn_module_t* source = sn_design_get_module(design, source_id); + size_t object_count = source->obj_types.size; + uint8_t* marks = (uint8_t*)calloc(object_count, sizeof(uint8_t)); + sn_vec_t order; + assert(marks); + sn_vec_init(&order); + sn_vec_reserve(sn_obj_id_t, &order, object_count); + for (sn_obj_id_t object = 0; object < object_count; object++) + if (remove[object]) + marks[object] = SN_TOPO_DONE; + for (size_t i = 0; i < source->type_objects[SN_PI].size; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PI], i); + assert(!remove[object]); + marks[object] = SN_TOPO_DONE; + *sn_vec_push(sn_obj_id_t, &order) = object; + } + for (sn_obj_id_t object = 0; object < object_count; object++) + if (!remove[object] && sn_obj_type_is_pair_out(sn_obj_type(source, object))) + { + marks[object] = SN_TOPO_DONE; + *sn_vec_push(sn_obj_id_t, &order) = object; + } + sn_topo_context_t context = {source, &order, marks}; + for (size_t i = 0; i < source->type_objects[SN_PO].size; i++) + { + sn_obj_id_t output = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PO], i); + for (uint32_t j = 0; j < sn_obj_fanin_count(source, output); j++) + { + sn_obj_id_t fanin = sn_obj_fanin(source, output, j); + if (fanin != SN_INVALID_ID) + sn_module_topo_visit(&context, fanin); + } + } + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_obj_type_t type = sn_obj_type(source, object); + if (type != SN_PI && type != SN_PO && marks[object] == SN_TOPO_UNSEEN) + sn_module_topo_visit(&context, object); + } + for (size_t i = 0; i < source->type_objects[SN_PO].size; i++) + { + sn_obj_id_t output = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PO], i); + marks[output] = SN_TOPO_DONE; + *sn_vec_push(sn_obj_id_t, &order) = output; + } + + sn_module_id_t target_id = sn_design_add_module(design, name); + sn_module_t* target = sn_design_get_module(design, target_id); + sn_vec_resize(sn_obj_id_t, &source->copy_ids, object_count); + for (sn_obj_id_t object = 0; object < object_count; object++) + sn_vec_at(sn_obj_id_t, &source->copy_ids, object) = SN_INVALID_ID; + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = + sn_module_dup_obj_skeleton(target, source, old_object); + } + sn_module_clean_rebuild_pair_ids(target, source, SN_REG_OUT, SN_REG_IN); + sn_module_clean_rebuild_pair_ids(target, source, SN_MEM_OUT, SN_MEM_IN); + sn_module_clean_rebuild_pair_ids(target, source, SN_LOOP_OUT, SN_LOOP_IN); + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_obj_id_t new_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + sn_obj_type_t type = sn_obj_type(source, old_object); + sn_module_dup_obj_metadata(target, sn_obj_type_id(target, new_object), source, old_object); + if (type == SN_FAN) + sn_vec_at(sn_obj_id_t, &target->fan_insts, sn_obj_type_id(target, new_object)) = + sn_vec_at(sn_obj_id_t, &source->copy_ids, sn_fan_inst_id(source, old_object)); + for (uint32_t j = 0; j < sn_obj_fanin_count(source, old_object); j++) + { + sn_obj_id_t old_fanin = sn_obj_fanin(source, old_object, j); + sn_obj_id_t new_fanin = old_fanin == SN_INVALID_ID + ? SN_INVALID_ID + : sn_vec_at(sn_obj_id_t, &source->copy_ids, old_fanin); + assert(new_fanin != SN_INVALID_ID || old_fanin == SN_INVALID_ID); + sn_obj_connect(target, new_object, j, new_fanin); + } + } + source->copy_module = target_id; + sn_vec_destroy(&order); + free(marks); + assert(sn_module_is_topo(target)); + return target_id; +} + +static inline void sn_boundary_prune_primitive_pairs(sn_boundary_regs_t* regs) +{ + sn_module_t* source = regs->result; + size_t object_count = source->obj_types.size; + uint8_t* remove = (uint8_t*)calloc(object_count, sizeof(uint8_t)); + uint8_t* keep = (uint8_t*)calloc(object_count, sizeof(uint8_t)); + sn_obj_id_t* replacement = object_count ? (sn_obj_id_t*)malloc(object_count * sizeof(sn_obj_id_t)) : NULL; + sn_obj_id_t* actual_to_pair = object_count ? (sn_obj_id_t*)malloc(object_count * sizeof(sn_obj_id_t)) : NULL; + size_t remove_count = 0; + assert(remove && keep && (replacement || object_count == 0) && (actual_to_pair || object_count == 0)); + for (sn_obj_id_t object = 0; object < object_count; object++) + replacement[object] = actual_to_pair[object] = SN_INVALID_ID; + for (size_t i = 0; i < regs->boundary->primitives.size; i++) + { + const sn_blast_primitive_t* entry = &sn_vec_at(sn_blast_primitive_t, ®s->boundary->primitives, i); + uint32_t output_count = sn_design_module_output_count(regs->design, entry->module); + for (uint32_t output = 0; output < output_count; output++) + { + sn_obj_pair_t pair = regs->primitive_pairs[regs->primitive_offsets[i] + output]; + sn_obj_id_t actual = sn_obj_fanin(source, pair.in, 0); + assert(replacement[pair.out] == SN_INVALID_ID && actual_to_pair[actual] == SN_INVALID_ID); + replacement[pair.out] = actual; + actual_to_pair[actual] = pair.out; + } + } + for (size_t i = 0; i < source->fanins.size; i++) + { + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &source->fanins, i); + if (fanin != SN_INVALID_ID && replacement[fanin] != SN_INVALID_ID) + sn_vec_at(sn_obj_id_t, &source->fanins, i) = replacement[fanin]; + } + sn_module_invalidate_fanouts(source); + sn_boundary_mark_feedback_pairs(source, actual_to_pair, keep); + for (sn_obj_id_t object = 0; object < object_count; object++) + for (uint32_t i = 0; i < sn_obj_fanin_count(source, object); i++) + { + sn_obj_id_t actual = sn_obj_fanin(source, object, i); + sn_obj_id_t pair_out = actual == SN_INVALID_ID ? SN_INVALID_ID : actual_to_pair[actual]; + if (pair_out != SN_INVALID_ID && keep[pair_out] && object != sn_obj_pair_in(source, pair_out)) + sn_obj_connect(source, object, i, pair_out); + } + for (sn_obj_id_t pair_out = 0; pair_out < object_count; pair_out++) + if (replacement[pair_out] != SN_INVALID_ID && !keep[pair_out]) + { + sn_obj_id_t actual = replacement[pair_out]; + sn_obj_id_t pair_in = sn_obj_pair_in(source, pair_out); + if (sn_obj_type(source, actual) == SN_FAN && sn_obj_name_id(source, actual) == SN_INVALID_ID && + sn_obj_name_id(source, pair_out) != SN_INVALID_ID) + sn_vec_at(sn_name_id_t, &source->name_ids, actual) = sn_obj_name_id(source, pair_out); + remove[pair_out] = remove[pair_in] = 1; + remove_count += 2; + } + sn_module_invalidate_fanouts(source); + if (remove_count) + { + char name[96]; + uint32_t suffix = 0; + do + { + int length = snprintf(name, sizeof(name), "__sn_boundary_%u_%u", source->id, suffix++); + assert(length >= 0 && (size_t)length < sizeof(name) && suffix != 0); + } while (sn_name_find(®s->design->names, name) != SN_INVALID_ID); + sn_module_id_t source_id = source->id; + sn_name_id_t source_name = source->name; + bool interface_locked = source->interface_locked; + sn_module_id_t filtered_id = sn_boundary_dup_filtered_topo(regs->design, source_id, remove, name); + sn_module_t* filtered = sn_design_get_module(regs->design, filtered_id); + sn_name_id_t temporary_name = filtered->name; + sn_design_invalidate_copies_to_module(regs->design, source_id); + sn_module_destroy(source); + free(source); + filtered->id = source_id; + filtered->name = source_name; + filtered->interface_locked = interface_locked; + sn_vec_at(sn_module_t*, ®s->design->modules, source_id) = filtered; + regs->design->modules.size--; + sn_name_remove_last(®s->design->names, temporary_name); + regs->result = filtered; + } + free(actual_to_pair); + free(replacement); + free(keep); + free(remove); +} + +static inline void sn_boundary_regs_finish(sn_boundary_regs_t* regs, const sn_obj_id_t* co_drivers) +{ + for (size_t i = 0; i < regs->boundary->primitives.size; i++) + { + const sn_blast_primitive_t* entry = &sn_vec_at(sn_blast_primitive_t, ®s->boundary->primitives, i); + const sn_blast_occurrence_t* occurrence = + &sn_vec_at(sn_blast_occurrence_t, ®s->boundary->occurrences, entry->occurrence); + const sn_module_t* module = sn_design_get_module_const(regs->design, occurrence->module); + const sn_module_t* child = sn_design_get_module_const(regs->design, entry->module); + uint32_t input_count = (uint32_t)child->type_objects[SN_PI].size; + sn_obj_id_t* inputs = input_count ? (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * input_count) : NULL; + assert(inputs || input_count == 0); + uint32_t co_begin = entry->co_begin; + for (uint32_t input = 0; input < input_count; input++) + { + sn_obj_id_t port = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PI], input); + uint32_t width = sn_obj_width(child, port); + inputs[input] = sn_boundary_co_word(regs, co_drivers, co_begin, SN_BLAST_BOUNDARY_PRIMITIVE_INPUT, + (uint32_t)i, input, width); + co_begin += width; + } + assert(co_begin == entry->co_begin + entry->co_count); + const char* inst_name = sn_obj_name_id(module, entry->inst) == SN_INVALID_ID + ? NULL + : sn_obj_name(module, entry->inst); + sn_obj_id_t inst = sn_module_add_inst(regs->result, entry->module, input_count, inputs, inst_name, NULL); + free(inputs); + for (uint32_t output = 0; output < child->type_objects[SN_PO].size; output++) + { + sn_obj_pair_t pair = regs->primitive_pairs[regs->primitive_offsets[i] + output]; + sn_obj_connect(regs->result, pair.in, 0, sn_inst_output(regs->result, inst, output)); + } + } + for (size_t i = 0; i < regs->boundary->loops.size; i++) + { + const sn_blast_loop_t* entry = &sn_vec_at(sn_blast_loop_t, ®s->boundary->loops, i); + sn_obj_id_t data = sn_boundary_co_word(regs, co_drivers, entry->co_begin, SN_BLAST_BOUNDARY_LOOP_INPUT, + (uint32_t)i, SN_INVALID_ID, entry->width); + sn_obj_connect(regs->result, regs->loops[i].in, 0, data); + } + for (size_t i = 0; i < regs->boundary->registers.size; i++) + { + const sn_blast_register_t* entry = &sn_vec_at(sn_blast_register_t, ®s->boundary->registers, i); + const sn_blast_occurrence_t* occurrence = + &sn_vec_at(sn_blast_occurrence_t, ®s->boundary->occurrences, entry->occurrence); + const sn_module_t* module = sn_design_get_module_const(regs->design, occurrence->module); + sn_obj_id_t old_out = entry->reg_out; + sn_obj_pair_t pair = regs->pairs[i]; + sn_obj_id_t old_clock = sn_obj_fanin(module, old_out, SN_REG_CLOCK); + if (old_clock != SN_INVALID_ID) + { + sn_blast_hier_ref_t ref = {entry->occurrence, old_clock, 0}; + sn_reg_set_fanin(regs->result, pair.out, SN_REG_CLOCK, sn_boundary_resolve_external(regs, ref)); + } + const uint32_t slots[] = {SN_REG_ENABLE, SN_REG_SET, SN_REG_RESET, SN_REG_RESET_VALUE}; + for (size_t k = 0; k < sizeof(slots) / sizeof(slots[0]); k++) + { + uint32_t slot = slots[k]; + sn_obj_id_t old_fanin = sn_obj_fanin(module, old_out, slot); + if (old_fanin == SN_INVALID_ID) + continue; + bool in_cloud = sn_blast_reg_control_is_comb_output(module, old_out, slot); + sn_obj_id_t fanin; + if (in_cloud) + { + assert(entry->control_co_begin[slot] != SN_INVALID_ID); + fanin = sn_boundary_co_word(regs, co_drivers, entry->control_co_begin[slot], + SN_BLAST_BOUNDARY_REG_CONTROL, (uint32_t)i, slot, + sn_obj_width(module, old_fanin)); + } + else + { + sn_blast_hier_ref_t ref = {entry->occurrence, old_fanin, 0}; + fanin = sn_boundary_resolve_external(regs, ref); + } + sn_reg_set_fanin(regs->result, pair.out, (sn_reg_fanin_t)slot, fanin); + } + for (uint32_t slot = SN_REG_INIT_DATA; slot <= SN_REG_INIT_MASK; slot++) + { + sn_obj_id_t old_fanin = sn_obj_fanin(module, old_out, slot); + if (old_fanin != SN_INVALID_ID) + { + sn_blast_hier_ref_t ref = {entry->occurrence, old_fanin, 0}; + sn_reg_set_fanin(regs->result, pair.out, (sn_reg_fanin_t)slot, + sn_boundary_resolve_external(regs, ref)); + } + } + sn_obj_id_t data = sn_boundary_co_word(regs, co_drivers, entry->co_begin, SN_BLAST_BOUNDARY_REG_INPUT, + (uint32_t)i, SN_INVALID_ID, entry->width); + sn_obj_connect(regs->result, pair.in, 0, data); + } + sn_boundary_prune_primitive_pairs(regs); + free(regs->pairs); + free(regs->loops); + free(regs->primitive_pairs); + free(regs->primitive_offsets); + free(regs->links); + for (size_t i = 0; i < regs->boundary->occurrences.size; i++) + free(regs->external_copies[i]); + free(regs->external_copies); + regs->pairs = NULL; + regs->loops = NULL; + regs->primitive_pairs = NULL; + regs->primitive_offsets = NULL; + regs->external_copies = NULL; + regs->links = NULL; + regs->link_cap = 0; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snCheck.h b/src/base/sn/snCheck.h new file mode 100644 index 000000000..c559419de --- /dev/null +++ b/src/base/sn/snCheck.h @@ -0,0 +1,1426 @@ +/**CFile**************************************************************** + + FileName [snCheck.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Structural and semantic consistency checking for SN designs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snCheck.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_CHECK_H +#define SN_CHECK_H + +#include "sn.h" + +#include + +ABC_NAMESPACE_HEADER_START + +typedef struct sn_check_ctx_t +{ + FILE* out; + size_t errors; + size_t modules; + size_t objects; + bool verbose; +} sn_check_ctx_t; + +static inline const char* sn_check_module_name(const sn_module_t* module) +{ + if (!module || !module->design || module->name >= module->design->names.names.size) + return ""; + return sn_vec_at(char*, &module->design->names.names, module->name); +} + +static inline void sn_check_error(sn_check_ctx_t* ctx, const sn_module_t* module, sn_obj_id_t object, + const char* format, ...) +{ + va_list args; + ctx->errors++; + fprintf(ctx->out, "SN check failed"); + if (module) + fprintf(ctx->out, " in module \"%s\"", sn_check_module_name(module)); + if (object != SN_INVALID_ID) + fprintf(ctx->out, ", object %u", object); + fputs(": ", ctx->out); + va_start(args, format); + vfprintf(ctx->out, format, args); + va_end(args); + fputc('\n', ctx->out); +} + +#define SN_CHECK(ctx, module, object, condition, ...) \ + do { \ + if (!(condition)) \ + sn_check_error((ctx), (module), (object), __VA_ARGS__); \ + } while (false) + +static inline bool sn_check_vec(sn_check_ctx_t* ctx, const sn_module_t* module, const sn_vec_t* vec, + const char* name) +{ + bool valid = vec->size <= vec->cap && (vec->cap == 0 || vec->data != NULL); + SN_CHECK(ctx, module, SN_INVALID_ID, valid, "vector %s has size %zu, capacity %zu, and data %p", name, + vec->size, vec->cap, vec->data); + return valid; +} + +static inline bool sn_check_const_type(sn_obj_type_t type) +{ + return type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST; +} + +static inline bool sn_check_name_is_emittable(const char* name) +{ + size_t i; + if (!name || !name[0]) + return false; + for (i = 0; name[i]; i++) + { + unsigned char c = (unsigned char)name[i]; + if (c <= 32 || c >= 127 || c == '\\') + return false; + } + return true; +} + +static inline int sn_check_fixed_fanin_count(sn_obj_type_t type) +{ + if (type == SN_PI || type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST) + return 0; + if (type == SN_PO || type == SN_BUF || type == SN_FAN || type == SN_REG_IN || type == SN_LOOP_OUT || + type == SN_LOOP_IN || (type >= SN_POS && type <= SN_REDUCE_XNOR) || type == SN_REPLICATE || + type == SN_SLICE || type == SN_CAST) + return 1; + if ((type >= SN_ADD && type <= SN_LOG_OR) || (type >= SN_EQ && type <= SN_GE) || + (type >= SN_SHL && type <= SN_ASHR)) + return 2; + if (type == SN_REG_OUT) + return SN_REG_FANIN_COUNT; + if (type == SN_MEM_OUT) + return SN_MEM_OUT_FANIN_COUNT; + if (type == SN_MEM_READ) + return SN_MEM_READ_FANIN_COUNT; + if (type == SN_MEM_WRITE) + return SN_MEM_WRITE_FANIN_COUNT; + if (type == SN_MUX) + return SN_MUX_FANIN_COUNT; + if (type == SN_BMUX) + return SN_BMUX_FANIN_COUNT; + if (type == SN_PMUX) + return SN_PMUX_FANIN_COUNT; + return -1; +} + +static inline bool sn_check_module_core(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + const sn_design_t* design = module ? module->design : NULL; + size_t object_count; + size_t offset = 0; + bool safe = module && design; + SN_CHECK(ctx, module, SN_INVALID_ID, module != NULL, "null module pointer"); + if (!safe) + return false; + SN_CHECK(ctx, module, SN_INVALID_ID, module->id < design->modules.size, "module ID %u is out of range", module->id); + if (module->id < design->modules.size) + SN_CHECK(ctx, module, SN_INVALID_ID, sn_vec_at(sn_module_t*, &design->modules, module->id) == module, + "module table does not point back to this module"); + SN_CHECK(ctx, module, SN_INVALID_ID, module->name < design->names.names.size, "module name ID %u is out of range", + module->name); + SN_CHECK(ctx, module, SN_INVALID_ID, (module->flags & ~SN_MODULE_ALL_FLAGS) == 0, + "module flags 0x%x contain unsupported bits", module->flags); + if (module->name < design->names.names.size) + { + const char* module_name = sn_name_get(&design->names, module->name); + SN_CHECK(ctx, module, SN_INVALID_ID, module_name != NULL && module_name[0] != '\0', + "module name is null or empty"); + if (module_name && strncmp(module_name, "__sn_", 5) == 0) + SN_CHECK(ctx, module, SN_INVALID_ID, sn_module_is_technology_primitive(module), + "module uses the reserved internal prefix __sn_"); + } + + safe &= sn_check_vec(ctx, module, &module->obj_types, "obj_types"); + safe &= sn_check_vec(ctx, module, &module->width_signed, "width_signed"); + safe &= sn_check_vec(ctx, module, &module->fanin_counts, "fanin_counts"); + safe &= sn_check_vec(ctx, module, &module->fanin_offsets, "fanin_offsets"); + safe &= sn_check_vec(ctx, module, &module->type_ids, "type_ids"); + safe &= sn_check_vec(ctx, module, &module->name_ids, "name_ids"); + safe &= sn_check_vec(ctx, module, &module->fanins, "fanins"); + if (!safe) + return false; + object_count = module->obj_types.size; + SN_CHECK(ctx, module, SN_INVALID_ID, object_count < SN_INVALID_ID, "object count %zu is too large", object_count); +#define SN_CHECK_OBJECT_VECTOR(field) \ + SN_CHECK(ctx, module, SN_INVALID_ID, module->field.size == object_count, \ + #field " size %zu differs from object count %zu", module->field.size, object_count) + SN_CHECK_OBJECT_VECTOR(width_signed); + SN_CHECK_OBJECT_VECTOR(fanin_counts); + SN_CHECK_OBJECT_VECTOR(fanin_offsets); + SN_CHECK_OBJECT_VECTOR(type_ids); + SN_CHECK_OBJECT_VECTOR(name_ids); +#undef SN_CHECK_OBJECT_VECTOR + if (module->width_signed.size != object_count || module->fanin_counts.size != object_count || + module->fanin_offsets.size != object_count || module->type_ids.size != object_count || + module->name_ids.size != object_count) + return false; + + for (uint32_t type = 0; type < SN_OBJ_TYPE_COUNT; type++) + safe &= sn_check_vec(ctx, module, &module->type_objects[type], "type_objects"); + if (!safe) + return false; + + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_obj_type_t type = sn_vec_at(sn_obj_type_t, &module->obj_types, object); + uint32_t packed_width = sn_vec_at(uint32_t, &module->width_signed, object); + uint32_t count = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object); + uint32_t stored_offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + uint32_t type_id = sn_vec_at(uint32_t, &module->type_ids, object); + uint32_t name_id = sn_vec_at(uint32_t, &module->name_ids, object); + bool type_valid = type > SN_NONE && type < SN_OBJ_TYPE_COUNT; + SN_CHECK(ctx, module, object, type_valid, "object type %u is invalid", (unsigned)type); + SN_CHECK(ctx, module, object, (packed_width >> 1) != 0 || type == SN_INST, + "object width is zero (only structural multi-output insts may have zero width)"); + SN_CHECK(ctx, module, object, stored_offset == offset, "fanin offset %u should be %zu", stored_offset, offset); + SN_CHECK(ctx, module, object, offset <= module->fanins.size && count <= module->fanins.size - offset, + "fanin span [%zu, %zu) exceeds fanin storage size %zu", offset, offset + count, module->fanins.size); + SN_CHECK(ctx, module, object, name_id == SN_INVALID_ID || name_id < design->names.names.size, + "name ID %u is out of range", name_id); + if (type_valid && (type == SN_PI || type == SN_PO || type == SN_GATE)) + { + const char* object_name = name_id < design->names.names.size + ? sn_name_get(&design->names, name_id) + : NULL; + SN_CHECK(ctx, module, object, + object_name != NULL && object_name[0] != '\0', + "type %u requires a nonempty Verilog name", (unsigned)type); + } + if (type_valid) + { + int expected = sn_check_fixed_fanin_count(type); + SN_CHECK(ctx, module, object, expected < 0 || count == (uint32_t)expected, + "type %u has %u fanins; expected %d", (unsigned)type, count, expected); + SN_CHECK(ctx, module, object, type_id < module->type_objects[type].size, + "type ID %u is out of range for type %u", type_id, (unsigned)type); + if (type_id < module->type_objects[type].size) + SN_CHECK(ctx, module, object, sn_vec_at(sn_obj_id_t, &module->type_objects[type], type_id) == object, + "reverse type-object entry does not point back to this object"); + } + if (offset <= module->fanins.size && count <= module->fanins.size - offset) + for (uint32_t i = 0; i < count; i++) + { + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, offset + i); + SN_CHECK(ctx, module, object, fanin < object_count || + (fanin == SN_INVALID_ID && type_valid && + sn_obj_fanin_may_be_invalid(module, type, i)), + "fanin %u has invalid object ID %u", i, fanin); + } + offset += count; + } + SN_CHECK(ctx, module, SN_INVALID_ID, offset == module->fanins.size, + "fanin spans use %zu entries but storage contains %zu", offset, module->fanins.size); + + if (sn_module_is_blackbox(module)) + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_obj_type_t type = sn_vec_at(sn_obj_type_t, &module->obj_types, object); + SN_CHECK(ctx, module, object, type == SN_PI || type == SN_PO, + "black-box module contains non-port object of type %u", (unsigned)type); + if (type == SN_PO) + { + uint32_t count = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object); + uint32_t po_offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + SN_CHECK(ctx, module, object, + count == 1 && po_offset < module->fanins.size && + sn_vec_at(sn_obj_id_t, &module->fanins, po_offset) == SN_INVALID_ID, + "black-box output must have one intentionally undriven fanin"); + } + } + + for (uint32_t type = 0; type < SN_OBJ_TYPE_COUNT; type++) + for (size_t type_id = 0; type_id < module->type_objects[type].size; type_id++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[type], type_id); + SN_CHECK(ctx, module, object, object < object_count, "reverse type-object ID is out of range"); + if (object < object_count) + { + SN_CHECK(ctx, module, object, sn_vec_at(sn_obj_type_t, &module->obj_types, object) == type, + "reverse type-object entry has the wrong type"); + SN_CHECK(ctx, module, object, sn_vec_at(uint32_t, &module->type_ids, object) == type_id, + "reverse type-object entry has the wrong type ID"); + } + } + return true; +} + +static inline bool sn_check_type_metadata(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + bool safe = true; +#define SN_CHECK_TYPE_VECTOR(field, type) \ + do { \ + safe &= sn_check_vec(ctx, module, &module->field, #field); \ + SN_CHECK(ctx, module, SN_INVALID_ID, module->field.size == module->type_objects[type].size, \ + #field " size %zu differs from type %u count %zu", module->field.size, (unsigned)(type), \ + module->type_objects[type].size); \ + safe &= module->field.size == module->type_objects[type].size; \ + } while (false) + SN_CHECK_TYPE_VECTOR(reg_flags, SN_REG_OUT); + SN_CHECK_TYPE_VECTOR(mem_depths, SN_MEM_OUT); + SN_CHECK_TYPE_VECTOR(inst_modules, SN_INST); + SN_CHECK_TYPE_VECTOR(fan_insts, SN_FAN); + SN_CHECK_TYPE_VECTOR(slice_infos, SN_SLICE); + SN_CHECK_TYPE_VECTOR(repeat_counts, SN_REPLICATE); + SN_CHECK_TYPE_VECTOR(const_word_offsets, SN_CONST); + SN_CHECK_TYPE_VECTOR(lut_truths, SN_LUT); + SN_CHECK_TYPE_VECTOR(gate_ids, SN_GATE); +#undef SN_CHECK_TYPE_VECTOR + return safe; +} + +static inline void sn_check_pairs(sn_check_ctx_t* ctx, const sn_module_t* module, sn_obj_type_t out_type, + sn_obj_type_t in_type, uint32_t pair_slot) +{ + size_t out_count = module->type_objects[out_type].size; + size_t in_count = module->type_objects[in_type].size; + SN_CHECK(ctx, module, SN_INVALID_ID, out_count == in_count, "pair types %u/%u have %zu/%zu objects", + (unsigned)out_type, (unsigned)in_type, out_count, in_count); + for (size_t i = 0; i < out_count && i < in_count; i++) + { + sn_obj_id_t out = sn_vec_at(sn_obj_id_t, &module->type_objects[out_type], i); + sn_obj_id_t in = sn_vec_at(sn_obj_id_t, &module->type_objects[in_type], i); + if (out >= module->obj_types.size || in >= module->obj_types.size) + continue; + SN_CHECK(ctx, module, out, sn_vec_at(uint32_t, &module->type_ids, out) == i && + sn_vec_at(uint32_t, &module->type_ids, in) == i, "paired objects do not share type ID %zu", i); + SN_CHECK(ctx, module, out, sn_vec_at(uint32_t, &module->width_signed, out) == + sn_vec_at(uint32_t, &module->width_signed, in), "paired objects differ in width or signedness"); + uint32_t count = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, out); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, out); + if (pair_slot < count && offset + pair_slot < module->fanins.size) + SN_CHECK(ctx, module, out, sn_vec_at(sn_obj_id_t, &module->fanins, offset + pair_slot) == in, + "OUT object does not reference its paired IN object"); + } +} + +static inline void sn_check_memories(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + size_t object_count = module->obj_types.size; + uint32_t* owners = object_count ? (uint32_t*)calloc(object_count, sizeof(uint32_t)) : NULL; + sn_obj_id_t* owner_memories = object_count ? (sn_obj_id_t*)malloc(object_count * sizeof(sn_obj_id_t)) : NULL; + SN_CHECK(ctx, module, SN_INVALID_ID, object_count == 0 || (owners != NULL && owner_memories != NULL), + "cannot allocate memory ownership map"); + if (object_count && (!owners || !owner_memories)) + { + free(owners); + free(owner_memories); + return; + } + for (size_t i = 0; i < object_count; i++) + owner_memories[i] = SN_INVALID_ID; + for (size_t i = 0; i < module->type_objects[SN_MEM_OUT].size && i < module->mem_depths.size; i++) + { + sn_obj_id_t memory = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_OUT], i); + SN_CHECK(ctx, module, memory, sn_vec_at(uint32_t, &module->mem_depths, i) != 0, "memory depth is zero"); + if (memory >= object_count) + continue; + uint64_t init_width = (uint64_t)(sn_vec_at(uint32_t, &module->width_signed, memory) >> 1) * + sn_vec_at(uint32_t, &module->mem_depths, i); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, memory); + for (uint32_t slot = SN_MEM_INIT_DATA; slot <= SN_MEM_INIT_MASK; slot++) + { + sn_obj_id_t value = sn_vec_at(sn_obj_id_t, &module->fanins, offset + slot); + if (value == SN_INVALID_ID || value >= object_count) + continue; + SN_CHECK(ctx, module, memory, sn_check_const_type(sn_vec_at(sn_obj_type_t, &module->obj_types, value)), + "memory initialization slot %u is not driven by a constant", slot); + SN_CHECK(ctx, module, memory, init_width <= UINT32_MAX && + (sn_vec_at(uint32_t, &module->width_signed, value) >> 1) == init_width, + "memory initialization slot %u has the wrong width", slot); + } + } + for (size_t i = 0; i < module->type_objects[SN_MEM_IN].size; i++) + { + sn_obj_id_t memory_in = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_IN], i); + if (memory_in >= object_count) + continue; + uint32_t count = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, memory_in); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, memory_in); + for (uint32_t j = 0; j < count; j++) + { + sn_obj_id_t write = sn_vec_at(sn_obj_id_t, &module->fanins, offset + j); + SN_CHECK(ctx, module, memory_in, write < object_count && + sn_vec_at(sn_obj_type_t, &module->obj_types, write) == SN_MEM_WRITE, + "memory input fanin %u is not a memory-write object", j); + if (write < object_count && sn_vec_at(sn_obj_type_t, &module->obj_types, write) == SN_MEM_WRITE) + { + owners[write]++; + if (owner_memories[write] == SN_INVALID_ID) + owner_memories[write] = memory_in; + } + } + } + for (size_t i = 0; i < module->type_objects[SN_MEM_WRITE].size; i++) + { + sn_obj_id_t write = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_WRITE], i); + if (write >= object_count) + continue; + SN_CHECK(ctx, module, write, owners[write] == 1, "memory-write object has %u owners", owners[write]); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, write); + sn_obj_id_t clock = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MEM_WRITE_CLOCK); + sn_obj_id_t enable = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MEM_WRITE_ENABLE); + sn_obj_id_t data = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MEM_WRITE_DATA); + if (clock < object_count) + SN_CHECK(ctx, module, write, (sn_vec_at(uint32_t, &module->width_signed, clock) >> 1) == 1, + "memory-write clock is not one bit"); + if (enable < object_count) + SN_CHECK(ctx, module, write, (sn_vec_at(uint32_t, &module->width_signed, enable) >> 1) == 1, + "memory-write enable is not one bit"); + if (data < object_count && owners[write] == 1) + { + sn_obj_id_t memory_in = owner_memories[write]; + SN_CHECK(ctx, module, write, memory_in < object_count && + (sn_vec_at(uint32_t, &module->width_signed, data) >> 1) == + (sn_vec_at(uint32_t, &module->width_signed, memory_in) >> 1), + "memory-write data width differs from its memory"); + } + } + for (size_t i = 0; i < module->type_objects[SN_MEM_READ].size; i++) + { + sn_obj_id_t read = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_READ], i); + if (read >= object_count) + continue; + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, read); + sn_obj_id_t memory = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MEM_READ_MEMORY); + sn_obj_id_t clock = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MEM_READ_CLOCK); + sn_obj_id_t enable = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MEM_READ_ENABLE); + SN_CHECK(ctx, module, read, memory < object_count && + sn_vec_at(sn_obj_type_t, &module->obj_types, memory) == SN_MEM_OUT, + "memory-read object is not owned by a memory output"); + if (memory < object_count && sn_vec_at(sn_obj_type_t, &module->obj_types, memory) == SN_MEM_OUT) + SN_CHECK(ctx, module, read, sn_vec_at(uint32_t, &module->width_signed, read) == + sn_vec_at(uint32_t, &module->width_signed, memory), + "memory-read result differs from its memory width or signedness"); + if (clock < object_count) + SN_CHECK(ctx, module, read, (sn_vec_at(uint32_t, &module->width_signed, clock) >> 1) == 1, + "memory-read clock is not one bit"); + if (enable < object_count) + SN_CHECK(ctx, module, read, (sn_vec_at(uint32_t, &module->width_signed, enable) >> 1) == 1, + "memory-read enable is not one bit"); + } + free(owner_memories); + free(owners); +} + +static inline void sn_check_instances(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + const sn_design_t* design = module->design; + size_t object_count = module->obj_types.size; + for (size_t i = 0; i < module->type_objects[SN_INST].size && i < module->inst_modules.size; i++) + { + sn_obj_id_t inst = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_INST], i); + sn_module_id_t child_id = sn_vec_at(sn_module_id_t, &module->inst_modules, i); + SN_CHECK(ctx, module, inst, child_id < design->modules.size, "referenced module ID %u is out of range", + child_id); + if (inst >= object_count || child_id >= design->modules.size) + continue; + const sn_module_t* child = sn_vec_at(sn_module_t*, &design->modules, child_id); + SN_CHECK(ctx, module, inst, child != NULL, "referenced module pointer is null"); + if (!child) + continue; + uint32_t inputs = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, inst); + uint32_t input_offset = sn_vec_at(uint32_t, &module->fanin_offsets, inst); + uint32_t outputs = (uint32_t)child->type_objects[SN_PO].size; + SN_CHECK(ctx, module, inst, inputs == child->type_objects[SN_PI].size, + "inst has %u inputs but child module has %zu", inputs, child->type_objects[SN_PI].size); + for (uint32_t input_index = 0; input_index < inputs && input_index < child->type_objects[SN_PI].size; + input_index++) + { + sn_obj_id_t input = sn_vec_at(sn_obj_id_t, &module->fanins, input_offset + input_index); + sn_obj_id_t port = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PI], input_index); + if (input < object_count && port < child->width_signed.size) + SN_CHECK(ctx, module, inst, (sn_vec_at(uint32_t, &module->width_signed, input) >> 1) == + (sn_vec_at(uint32_t, &child->width_signed, port) >> 1), + "inst input %u width differs from the child port", input_index); + } + SN_CHECK(ctx, module, inst, outputs != 0, "instantiated module has no outputs"); + if (outputs == 1) + { + sn_obj_id_t output = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PO], 0); + SN_CHECK(ctx, module, inst, sn_vec_at(uint32_t, &module->width_signed, inst) == + sn_vec_at(uint32_t, &child->width_signed, output), + "single-output inst differs from its child output width or signedness"); + } + else + { + SN_CHECK(ctx, module, inst, (sn_vec_at(uint32_t, &module->width_signed, inst) >> 1) == 0, + "multi-output inst must have zero structural width"); + for (uint32_t output_index = 0; output_index < outputs; output_index++) + { + sn_obj_id_t fan = inst + 1 + output_index; + SN_CHECK(ctx, module, inst, fan < object_count && + sn_vec_at(sn_obj_type_t, &module->obj_types, fan) == SN_FAN, + "output %u is not represented by the adjacent FAN object %u", output_index, fan); + if (fan >= object_count || sn_vec_at(sn_obj_type_t, &module->obj_types, fan) != SN_FAN) + continue; + uint32_t fan_id = sn_vec_at(uint32_t, &module->type_ids, fan); + sn_obj_id_t child_output = sn_vec_at(sn_obj_id_t, &child->type_objects[SN_PO], output_index); + SN_CHECK(ctx, module, fan, fan_id < module->fan_insts.size && + sn_vec_at(sn_obj_id_t, &module->fan_insts, fan_id) == inst, + "FAN ownership does not reference its adjacent inst"); + SN_CHECK(ctx, module, fan, sn_vec_at(uint32_t, &module->width_signed, fan) == + sn_vec_at(uint32_t, &child->width_signed, child_output), + "FAN differs from its child output width or signedness"); + } + } + } + for (size_t i = 0; i < module->type_objects[SN_FAN].size && i < module->fan_insts.size; i++) + { + sn_obj_id_t fan = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_FAN], i); + sn_obj_id_t inst = sn_vec_at(sn_obj_id_t, &module->fan_insts, i); + SN_CHECK(ctx, module, fan, inst < fan && inst < object_count && + sn_vec_at(sn_obj_type_t, &module->obj_types, inst) == SN_INST, + "FAN owner %u is not an earlier inst", inst); + if (fan < object_count) + { + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, fan); + SN_CHECK(ctx, module, fan, offset < module->fanins.size && + sn_vec_at(sn_obj_id_t, &module->fanins, offset) == inst, + "FAN data fanin does not reference its owning inst"); + } + if (inst < object_count && sn_vec_at(sn_obj_type_t, &module->obj_types, inst) == SN_INST) + { + uint32_t inst_id = sn_vec_at(uint32_t, &module->type_ids, inst); + if (inst_id < module->inst_modules.size) + { + sn_module_id_t child_id = sn_vec_at(sn_module_id_t, &module->inst_modules, inst_id); + if (child_id < design->modules.size && sn_vec_at(sn_module_t*, &design->modules, child_id)) + { + const sn_module_t* child = sn_vec_at(sn_module_t*, &design->modules, child_id); + uint32_t output_index = fan - inst - 1; + SN_CHECK(ctx, module, fan, output_index < child->type_objects[SN_PO].size, + "FAN is outside its inst's natural adjacent output block"); + } + } + } + } +} + +static inline void sn_check_special_objects(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + size_t object_count = module->obj_types.size; + size_t reg_count = module->reg_flags.size < module->type_objects[SN_REG_OUT].size + ? module->reg_flags.size + : module->type_objects[SN_REG_OUT].size; + for (size_t i = 0; i < reg_count; i++) + { + sn_obj_id_t reg = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_REG_OUT], i); + uint32_t flags = sn_vec_at(uint32_t, &module->reg_flags, i); + SN_CHECK(ctx, module, reg, (flags & ~SN_REG_FLAGS_ALL) == 0, "register flags 0x%x are invalid", flags); + if (reg >= object_count) + continue; + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, reg); + sn_obj_id_t data = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_REG_INIT_DATA); + sn_obj_id_t mask = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_REG_INIT_MASK); + SN_CHECK(ctx, module, reg, data != SN_INVALID_ID || mask == SN_INVALID_ID, + "register init mask is present without init data"); + for (uint32_t slot = SN_REG_INIT_DATA; slot <= SN_REG_INIT_MASK; slot++) + { + sn_obj_id_t value = sn_vec_at(sn_obj_id_t, &module->fanins, offset + slot); + if (value == SN_INVALID_ID || value >= object_count) + continue; + SN_CHECK(ctx, module, reg, sn_check_const_type(sn_vec_at(sn_obj_type_t, &module->obj_types, value)), + "register initialization slot %u is not a constant", slot); + SN_CHECK(ctx, module, reg, (sn_vec_at(uint32_t, &module->width_signed, value) >> 1) == + (sn_vec_at(uint32_t, &module->width_signed, reg) >> 1), + "register initialization slot %u has the wrong width", slot); + } + } + size_t repeat_count = module->repeat_counts.size < module->type_objects[SN_REPLICATE].size + ? module->repeat_counts.size + : module->type_objects[SN_REPLICATE].size; + for (size_t i = 0; i < repeat_count; i++) + SN_CHECK(ctx, module, sn_vec_at(sn_obj_id_t, &module->type_objects[SN_REPLICATE], i), + sn_vec_at(uint32_t, &module->repeat_counts, i) != 0, "repetition count is zero"); + size_t slice_count = module->slice_infos.size < module->type_objects[SN_SLICE].size + ? module->slice_infos.size + : module->type_objects[SN_SLICE].size; + for (size_t i = 0; i < slice_count; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_SLICE], i); + const sn_slice_info_t* info = &sn_vec_at(sn_slice_info_t, &module->slice_infos, i); + SN_CHECK(ctx, module, object, (info->flags & ~SN_SLICE_DESCENDING) == 0, "slice flags are invalid"); + SN_CHECK(ctx, module, object, ((info->flags & SN_SLICE_DESCENDING) != 0) == + (info->left_index >= info->right_index), "slice direction flag disagrees with its indices"); + if (object < object_count) + { + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + sn_obj_id_t value = sn_vec_at(sn_obj_id_t, &module->fanins, offset); + if (value < object_count) + { + uint32_t source_width = sn_vec_at(uint32_t, &module->width_signed, value) >> 1; + uint64_t slice_width = info->left_index >= info->right_index + ? (uint64_t)(int64_t)info->left_index - info->right_index + 1 + : (uint64_t)(int64_t)info->right_index - info->left_index + 1; + SN_CHECK(ctx, module, object, info->left_index >= 0 && (uint32_t)info->left_index < source_width, + "slice left index %d is outside source width %u", info->left_index, source_width); + SN_CHECK(ctx, module, object, info->right_index >= 0 && (uint32_t)info->right_index < source_width, + "slice right index %d is outside source width %u", info->right_index, source_width); + SN_CHECK(ctx, module, object, slice_width == + (sn_vec_at(uint32_t, &module->width_signed, object) >> 1), + "slice width does not match its index range"); + } + } + } + size_t const_count = module->const_word_offsets.size < module->type_objects[SN_CONST].size + ? module->const_word_offsets.size + : module->type_objects[SN_CONST].size; + for (size_t i = 0; i < const_count; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_CONST], i); + if (object >= object_count) + continue; + uint32_t words = ((sn_vec_at(uint32_t, &module->width_signed, object) >> 1) + 31) / 32; + uint32_t offset = sn_vec_at(uint32_t, &module->const_word_offsets, i); + SN_CHECK(ctx, module, object, offset <= module->design->constant_words.size && + words <= module->design->constant_words.size - offset, + "constant word span [%u, %u) exceeds storage size %zu", offset, offset + words, + module->design->constant_words.size); + } + size_t lut_count = module->lut_truths.size < module->type_objects[SN_LUT].size + ? module->lut_truths.size + : module->type_objects[SN_LUT].size; + for (size_t i = 0; i < lut_count; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_LUT], i); + if (object >= object_count) + continue; + uint32_t count = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + uint64_t truth = sn_vec_at(uint64_t, &module->lut_truths, i); + SN_CHECK(ctx, module, object, (sn_vec_at(uint32_t, &module->width_signed, object) >> 1) == 1 && + !(sn_vec_at(uint32_t, &module->width_signed, object) & 1), + "LUT output must be one-bit unsigned"); + SN_CHECK(ctx, module, object, count <= 6, "LUT has %u inputs; at most 6 are supported", count); + if (count < 6) + SN_CHECK(ctx, module, object, (truth >> (1u << count)) == 0, + "LUT truth table has nonzero unused high bits"); + for (uint32_t j = 0; j < count; j++) + { + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, offset + j); + if (fanin < object_count) + SN_CHECK(ctx, module, object, (sn_vec_at(uint32_t, &module->width_signed, fanin) >> 1) == 1, + "LUT input %u is not one bit", j); + } + } + size_t gate_count = module->gate_ids.size < module->type_objects[SN_GATE].size + ? module->gate_ids.size + : module->type_objects[SN_GATE].size; + for (size_t i = 0; i < gate_count; i++) + { + sn_obj_id_t object = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_GATE], i); + if (object >= object_count) + continue; + SN_CHECK(ctx, module, object, sn_vec_at(uint32_t, &module->gate_ids, i) != SN_INVALID_ID, + "gate ID is invalid"); + SN_CHECK(ctx, module, object, (sn_vec_at(uint32_t, &module->width_signed, object) >> 1) == 1, + "gate output is not one bit"); + } +} + +static inline void sn_check_operator_shapes(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + size_t object_count = module->obj_types.size; + for (sn_obj_id_t object = 0; object < object_count; object++) + { + sn_obj_type_t type = sn_vec_at(sn_obj_type_t, &module->obj_types, object); + uint32_t width = sn_vec_at(uint32_t, &module->width_signed, object) >> 1; + uint32_t count = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + if (type == SN_CONCAT) + { + uint64_t packed_width = 0; + for (uint32_t i = 0; i < count; i++) + { + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, offset + i); + if (fanin < object_count) + packed_width += sn_vec_at(uint32_t, &module->width_signed, fanin) >> 1; + } + SN_CHECK(ctx, module, object, packed_width == width, + "concatenation fanins contain %llu bits but output width is %u", + (unsigned long long)packed_width, width); + } + else if (type == SN_REPLICATE && count == 1) + { + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, offset); + uint32_t type_id = sn_vec_at(uint32_t, &module->type_ids, object); + if (fanin < object_count && type_id < module->repeat_counts.size) + { + uint64_t packed_width = (uint64_t)(sn_vec_at(uint32_t, &module->width_signed, fanin) >> 1) * + sn_vec_at(uint32_t, &module->repeat_counts, type_id); + SN_CHECK(ctx, module, object, packed_width == width, + "repetition produces %llu bits but output width is %u", + (unsigned long long)packed_width, width); + } + } + else if (type == SN_MUX && count == SN_MUX_FANIN_COUNT) + { + sn_obj_id_t select = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MUX_SELECT); + sn_obj_id_t selected = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MUX_SELECTED); + sn_obj_id_t default_value = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MUX_DEFAULT); + if (select < object_count) + SN_CHECK(ctx, module, object, + (sn_vec_at(uint32_t, &module->width_signed, select) >> 1) == 1, + "mux select is not one bit"); + if (selected < object_count) + SN_CHECK(ctx, module, object, + (sn_vec_at(uint32_t, &module->width_signed, selected) >> 1) == width, + "mux selected branch width differs from output width"); + if (default_value < object_count) + SN_CHECK(ctx, module, object, + (sn_vec_at(uint32_t, &module->width_signed, default_value) >> 1) == width, + "mux default branch width differs from output width"); + } + else if (type == SN_BMUX && count == SN_BMUX_FANIN_COUNT) + { + sn_obj_id_t select = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_BMUX_SELECT); + sn_obj_id_t alternatives = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_BMUX_ALTERNATIVES); + if (select < object_count && alternatives < object_count) + { + uint32_t select_width = sn_vec_at(uint32_t, &module->width_signed, select) >> 1; + uint32_t alternatives_width = sn_vec_at(uint32_t, &module->width_signed, alternatives) >> 1; + bool valid = select_width < 31 && ((uint64_t)width << select_width) == alternatives_width; + SN_CHECK(ctx, module, object, valid, + "binary mux alternatives width %u does not equal %u * 2^%u", + alternatives_width, width, select_width); + } + } + else if (type == SN_PMUX && count == SN_PMUX_FANIN_COUNT) + { + sn_obj_id_t select = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_PMUX_SELECT); + sn_obj_id_t alternatives = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_PMUX_ALTERNATIVES); + sn_obj_id_t default_value = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_PMUX_DEFAULT); + if (select < object_count && alternatives < object_count) + { + uint32_t select_width = sn_vec_at(uint32_t, &module->width_signed, select) >> 1; + uint32_t alternatives_width = sn_vec_at(uint32_t, &module->width_signed, alternatives) >> 1; + SN_CHECK(ctx, module, object, (uint64_t)width * select_width == alternatives_width, + "priority mux alternatives width %u does not equal %u * %u", + alternatives_width, width, select_width); + } + if (default_value < object_count) + SN_CHECK(ctx, module, object, + (sn_vec_at(uint32_t, &module->width_signed, default_value) >> 1) == width, + "priority mux default width differs from output width"); + } + else if (type == SN_REG_IN || type == SN_LOOP_IN) + { + if (count == 1) + { + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, offset); + if (fanin < object_count) + SN_CHECK(ctx, module, object, + (sn_vec_at(uint32_t, &module->width_signed, fanin) >> 1) == width, + "state input data width differs from state width"); + } + } + else if (type == SN_PO && count == 1) + { + sn_obj_id_t driver = sn_vec_at(sn_obj_id_t, &module->fanins, offset); + if (driver < object_count) + SN_CHECK(ctx, module, object, + (sn_vec_at(uint32_t, &module->width_signed, driver) >> 1) == width, + "primary-output driver width differs from output width"); + } + else if (type == SN_MEM_READ && count == SN_MEM_READ_FANIN_COUNT) + { + sn_obj_id_t clock = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MEM_READ_CLOCK); + sn_obj_id_t enable = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_MEM_READ_ENABLE); + SN_CHECK(ctx, module, object, clock != SN_INVALID_ID || enable == SN_INVALID_ID, + "memory read enable is present without a clock"); + if (clock < object_count) + SN_CHECK(ctx, module, object, + (sn_vec_at(uint32_t, &module->width_signed, clock) >> 1) == 1, + "memory read clock is not one bit"); + if (enable < object_count) + SN_CHECK(ctx, module, object, + (sn_vec_at(uint32_t, &module->width_signed, enable) >> 1) == 1, + "memory read enable is not one bit"); + } + } + + for (size_t i = 0; i < module->type_objects[SN_REG_OUT].size; i++) + { + sn_obj_id_t reg = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_REG_OUT], i); + if (reg >= object_count) + continue; + uint32_t width = sn_vec_at(uint32_t, &module->width_signed, reg) >> 1; + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, reg); + uint32_t flags = sn_vec_at(uint32_t, &module->reg_flags, i); + sn_obj_id_t clock = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_REG_CLOCK); + sn_obj_id_t enable = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_REG_ENABLE); + sn_obj_id_t set = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_REG_SET); + sn_obj_id_t reset = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_REG_RESET); + if (flags & SN_REG_LATCH) + { + SN_CHECK(ctx, module, reg, clock == SN_INVALID_ID, "latch has a clock fanin"); + SN_CHECK(ctx, module, reg, enable < object_count, "latch has no enable fanin"); + SN_CHECK(ctx, module, reg, set == SN_INVALID_ID && reset == SN_INVALID_ID, + "latch has unsupported set or reset controls"); + SN_CHECK(ctx, module, reg, (flags & ~SN_REG_LATCH) == 0, + "latch has edge-triggered register flags 0x%x", flags & ~SN_REG_LATCH); + } + else + SN_CHECK(ctx, module, reg, clock < object_count, "edge-triggered register has no clock fanin"); + const uint32_t one_bit_slots[] = {SN_REG_CLOCK, SN_REG_ENABLE, SN_REG_SET, SN_REG_RESET}; + for (size_t slot_index = 0; slot_index < sizeof(one_bit_slots) / sizeof(one_bit_slots[0]); slot_index++) + { + uint32_t slot = one_bit_slots[slot_index]; + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, offset + slot); + if (fanin < object_count) + SN_CHECK(ctx, module, reg, + (sn_vec_at(uint32_t, &module->width_signed, fanin) >> 1) == 1, + "register control slot %u is not one bit", slot); + } + sn_obj_id_t reset_value = sn_vec_at(sn_obj_id_t, &module->fanins, offset + SN_REG_RESET_VALUE); + if (reset_value < object_count) + SN_CHECK(ctx, module, reg, + (sn_vec_at(uint32_t, &module->width_signed, reset_value) >> 1) == width, + "register reset value width differs from register width"); + } + + for (size_t i = 0; i < module->type_objects[SN_GATE].size; i++) + { + sn_obj_id_t gate = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_GATE], i); + if (gate >= object_count) + continue; + uint32_t count = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, gate); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, gate); + for (uint32_t j = 0; j < count; j++) + { + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, offset + j); + if (fanin < object_count) + SN_CHECK(ctx, module, gate, + (sn_vec_at(uint32_t, &module->width_signed, fanin) >> 1) == 1, + "gate input %u is not one bit", j); + } + } +} + +static inline void sn_check_auxiliary_storage(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + size_t object_count = module->obj_types.size; + bool hash_safe = sn_check_vec(ctx, module, &module->const_hash_buckets, "constant hash buckets"); + hash_safe &= sn_check_vec(ctx, module, &module->const_hash_entries, "constant hash entries"); + if (hash_safe && !module->const_hash_buckets.size) + SN_CHECK(ctx, module, SN_INVALID_ID, module->const_hash_entries.size == 0, + "constant hash entries exist without buckets"); + else if (hash_safe) + { + size_t bucket_count = module->const_hash_buckets.size; + SN_CHECK(ctx, module, SN_INVALID_ID, (bucket_count & (bucket_count - 1)) == 0, + "constant hash bucket count %zu is not a power of two", bucket_count); + uint8_t* seen = module->const_hash_entries.size + ? (uint8_t*)calloc(module->const_hash_entries.size, 1) + : NULL; + SN_CHECK(ctx, module, SN_INVALID_ID, module->const_hash_entries.size == 0 || seen != NULL, + "cannot allocate constant-hash validation state"); + if (seen || !module->const_hash_entries.size) + for (size_t bucket = 0; bucket < bucket_count; bucket++) + { + uint32_t entry_id = sn_vec_at(uint32_t, &module->const_hash_buckets, bucket); + size_t steps = 0; + while (entry_id != SN_INVALID_ID && entry_id < module->const_hash_entries.size && + steps++ <= module->const_hash_entries.size) + { + const sn_const_hash_entry_t* entry = + &sn_vec_at(sn_const_hash_entry_t, &module->const_hash_entries, entry_id); + SN_CHECK(ctx, module, entry->object, !seen[entry_id], + "constant hash entry %u appears more than once", entry_id); + seen[entry_id] = 1; + SN_CHECK(ctx, module, entry->object, (entry->hash & (bucket_count - 1)) == bucket, + "constant hash entry %u is in the wrong bucket", entry_id); + SN_CHECK(ctx, module, entry->object, entry->object < object_count, + "constant hash entry %u has an invalid object", entry_id); + if (entry->object < object_count) + { + sn_obj_type_t type = sn_vec_at(sn_obj_type_t, &module->obj_types, entry->object); + SN_CHECK(ctx, module, entry->object, sn_check_const_type(type), + "constant hash entry refers to a nonconstant object"); + SN_CHECK(ctx, module, entry->object, + sn_vec_at(uint32_t, &module->name_ids, entry->object) == SN_INVALID_ID, + "named constants must not appear in the interning cache"); + } + entry_id = entry->next; + } + SN_CHECK(ctx, module, SN_INVALID_ID, entry_id == SN_INVALID_ID, + "constant hash bucket %zu has an invalid or cyclic chain", bucket); + } + if (seen) + for (size_t i = 0; i < module->const_hash_entries.size; i++) + SN_CHECK(ctx, module, SN_INVALID_ID, seen[i], "constant hash entry %zu is unreachable", i); + free(seen); + } + + bool fanout_safe = sn_check_vec(ctx, module, &module->fanout_counts, "fanout_counts"); + fanout_safe &= sn_check_vec(ctx, module, &module->fanout_offsets, "fanout_offsets"); + fanout_safe &= sn_check_vec(ctx, module, &module->fanouts, "fanouts"); + if (fanout_safe && !module->fanouts_valid) + SN_CHECK(ctx, module, SN_INVALID_ID, module->fanout_counts.size == 0 && + module->fanout_offsets.size == 0 && module->fanouts.size == 0, + "invalidated fanout cache is not empty"); + else if (fanout_safe) + { + SN_CHECK(ctx, module, SN_INVALID_ID, module->fanout_counts.size == object_count && + module->fanout_offsets.size == object_count, + "valid fanout cache does not have one count and offset per object"); + if (module->fanout_counts.size == object_count && module->fanout_offsets.size == object_count) + { + size_t offset = 0; + for (sn_obj_id_t object = 0; object < object_count; object++) + { + uint32_t stored = sn_vec_at(uint32_t, &module->fanout_offsets, object); + uint32_t count = sn_vec_at(uint32_t, &module->fanout_counts, object); + SN_CHECK(ctx, module, object, stored == offset, "fanout offset %u should be %zu", stored, offset); + SN_CHECK(ctx, module, object, offset <= module->fanouts.size && count <= module->fanouts.size - offset, + "fanout span exceeds fanout storage"); + offset += count; + } + SN_CHECK(ctx, module, SN_INVALID_ID, offset == module->fanouts.size, + "fanout spans use %zu entries but storage contains %zu", offset, module->fanouts.size); + for (size_t i = 0; i < module->fanouts.size; i++) + SN_CHECK(ctx, module, SN_INVALID_ID, sn_vec_at(sn_obj_id_t, &module->fanouts, i) < object_count, + "fanout entry %zu has an invalid object ID", i); + } + } + + bool copy_safe = sn_check_vec(ctx, module, &module->copy_ids, "copy_ids"); + if (copy_safe && !module->copy_ids.size) + SN_CHECK(ctx, module, SN_INVALID_ID, module->copy_module == SN_INVALID_ID, + "copy target exists without a copy map"); + else if (copy_safe) + { + SN_CHECK(ctx, module, SN_INVALID_ID, module->copy_ids.size == object_count, + "copy map size %zu differs from object count %zu", module->copy_ids.size, object_count); + SN_CHECK(ctx, module, SN_INVALID_ID, module->copy_module < module->design->modules.size, + "copy target module ID %u is out of range", module->copy_module); + if (module->copy_module < module->design->modules.size) + { + const sn_module_t* target = sn_vec_at(sn_module_t*, &module->design->modules, module->copy_module); + if (target) + for (size_t i = 0; i < module->copy_ids.size; i++) + { + sn_obj_id_t copy = sn_vec_at(sn_obj_id_t, &module->copy_ids, i); + SN_CHECK(ctx, module, (sn_obj_id_t)i, copy == SN_INVALID_ID || copy < target->obj_types.size, + "copy object ID %u is out of range", copy); + } + } + } +} + +static inline void sn_check_topology(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + size_t object_count = module->obj_types.size; + size_t pi_count = module->type_objects[SN_PI].size; + size_t po_count = module->type_objects[SN_PO].size; + bool valid = pi_count + po_count <= object_count; + if (valid) + for (size_t i = 0; i < pi_count; i++) + valid &= sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], i) == i; + if (valid) + for (size_t i = 0; i < po_count; i++) + valid &= sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], i) == object_count - po_count + i; + for (sn_obj_id_t object = 0; valid && object < object_count; object++) + { + sn_obj_type_t type = sn_vec_at(sn_obj_type_t, &module->obj_types, object); + sn_obj_id_t pair_in = SN_INVALID_ID; + if (type == SN_REG_OUT || type == SN_MEM_OUT || type == SN_LOOP_OUT) + { + sn_obj_type_t in_type = type == SN_REG_OUT ? SN_REG_IN : type == SN_MEM_OUT ? SN_MEM_IN : SN_LOOP_IN; + uint32_t type_id = sn_vec_at(uint32_t, &module->type_ids, object); + if (type_id >= module->type_objects[in_type].size) + valid = false; + else + pair_in = sn_vec_at(sn_obj_id_t, &module->type_objects[in_type], type_id); + valid &= pair_in > object; + } + uint32_t count = sn_vec_at(sn_fanin_count_t, &module->fanin_counts, object); + uint32_t offset = sn_vec_at(uint32_t, &module->fanin_offsets, object); + for (uint32_t i = 0; valid && i < count; i++) + { + sn_obj_id_t fanin = sn_vec_at(sn_obj_id_t, &module->fanins, offset + i); + if (fanin == SN_INVALID_ID || fanin == pair_in) + continue; + valid &= pair_in != SN_INVALID_ID ? fanin < pair_in : fanin < object; + } + } + SN_CHECK(ctx, module, SN_INVALID_ID, valid, "objects are not in legal SN topological order"); +} + +static inline bool sn_check_parse_u32(const char** cursor, uint32_t* value, char delimiter) +{ + char* end; + unsigned long parsed; + if (!cursor || !*cursor || !value || **cursor < '0' || **cursor > '9') + return false; + parsed = strtoul(*cursor, &end, 10); + if (end == *cursor || parsed > UINT32_MAX || *end != delimiter) + return false; + *value = (uint32_t)parsed; + *cursor = delimiter ? end + 1 : end; + return true; +} + +static inline bool sn_check_slice_bit(const sn_module_t* module, sn_obj_id_t object, sn_obj_id_t source, + uint32_t bit) +{ + if (object == SN_INVALID_ID || object >= module->obj_types.size || sn_obj_type(module, object) != SN_SLICE || + sn_obj_fanin(module, object, 0) != source) + return false; + const sn_slice_info_t* info = sn_obj_slice_info(module, object); + return info->left_index == (int32_t)bit && info->right_index == (int32_t)bit; +} + +static inline void sn_check_carry_primitive(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + size_t pi_count = module->type_objects[SN_PI].size; + size_t po_count = module->type_objects[SN_PO].size; + const uint32_t pi_widths[] = {1, 1, 4, 4}; + SN_CHECK(ctx, module, SN_INVALID_ID, pi_count == 4 && po_count == 2, + "carry primitive interface must have 4 inputs and 2 outputs"); + for (size_t i = 0; i < pi_count && i < 4; i++) + { + sn_obj_id_t pi = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], i); + SN_CHECK(ctx, module, pi, sn_obj_width(module, pi) == pi_widths[i], + "carry primitive input %zu has the wrong width", i); + } + for (size_t i = 0; i < po_count; i++) + { + sn_obj_id_t po = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], i); + SN_CHECK(ctx, module, po, sn_obj_width(module, po) == 4, + "carry primitive output %zu has the wrong width", i); + } + if (pi_count != 4 || po_count != 2) + return; + sn_obj_id_t ci = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 0); + sn_obj_id_t cyinit = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 1); + sn_obj_id_t di = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 2); + sn_obj_id_t s = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 3); + sn_obj_id_t o = sn_obj_fanin(module, sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], 0), 0); + sn_obj_id_t co = sn_obj_fanin(module, sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], 1), 0); + bool packed = sn_obj_type(module, o) == SN_CONCAT && sn_obj_fanin_count(module, o) == 4 && + sn_obj_type(module, co) == SN_CONCAT && sn_obj_fanin_count(module, co) == 4; + SN_CHECK(ctx, module, SN_INVALID_ID, packed, + "carry primitive outputs must be four-bit concatenations"); + if (!packed) + return; + sn_obj_id_t first_o = sn_obj_fanin(module, o, 0); + sn_obj_id_t carry = sn_obj_type(module, first_o) == SN_BIT_XOR && sn_obj_fanin_count(module, first_o) == 2 + ? sn_obj_fanin(module, first_o, 1) + : SN_INVALID_ID; + bool initial = carry != SN_INVALID_ID && sn_obj_type(module, carry) == SN_BIT_OR && + sn_obj_fanin_count(module, carry) == 2 && + ((sn_obj_fanin(module, carry, 0) == ci && sn_obj_fanin(module, carry, 1) == cyinit) || + (sn_obj_fanin(module, carry, 0) == cyinit && sn_obj_fanin(module, carry, 1) == ci)); + SN_CHECK(ctx, module, carry, initial, "carry primitive has an invalid initial carry expression"); + for (uint32_t bit = 0; bit < 4; bit++) + { + sn_obj_id_t o_bit = sn_obj_fanin(module, o, bit); + sn_obj_id_t co_bit = sn_obj_fanin(module, co, bit); + bool o_valid = sn_obj_type(module, o_bit) == SN_BIT_XOR && sn_obj_fanin_count(module, o_bit) == 2; + bool co_valid = sn_obj_type(module, co_bit) == SN_MUX && sn_obj_fanin_count(module, co_bit) == 3; + sn_obj_id_t s_bit = o_valid ? sn_obj_fanin(module, o_bit, 0) : SN_INVALID_ID; + sn_obj_id_t di_bit = co_valid ? sn_obj_fanin(module, co_bit, SN_MUX_DEFAULT) : SN_INVALID_ID; + o_valid &= s_bit != SN_INVALID_ID && sn_obj_fanin(module, o_bit, 1) == carry && + sn_check_slice_bit(module, s_bit, s, bit); + co_valid &= s_bit != SN_INVALID_ID && di_bit != SN_INVALID_ID && + sn_obj_fanin(module, co_bit, SN_MUX_SELECT) == s_bit && + sn_obj_fanin(module, co_bit, SN_MUX_SELECTED) == carry && + sn_check_slice_bit(module, di_bit, di, bit); + SN_CHECK(ctx, module, o_bit, o_valid, "carry primitive O[%u] has invalid logic", bit); + SN_CHECK(ctx, module, co_bit, co_valid, "carry primitive CO[%u] has invalid logic", bit); + carry = co_bit; + } +} + +static inline void sn_check_dsp_primitive(sn_check_ctx_t* ctx, const sn_module_t* module, const char* name) +{ + size_t pi_count = module->type_objects[SN_PI].size; + size_t po_count = module->type_objects[SN_PO].size; + size_t mul_count = module->type_objects[SN_MUL].size; + SN_CHECK(ctx, module, SN_INVALID_ID, pi_count == 2 && po_count == 1, + "DSP primitive interface must have 2 inputs and 1 output"); + SN_CHECK(ctx, module, SN_INVALID_ID, mul_count == 1, + "DSP primitive behavioral wrapper must contain one multiplier"); + const char* shape = strstr(name, "_mul_"); + uint32_t a_width = 0, b_width = 0, y_width = 0; + bool parsed = shape != NULL; + const char* cursor = parsed ? shape + 5 : NULL; + parsed &= sn_check_parse_u32(&cursor, &a_width, '_'); + parsed &= sn_check_parse_u32(&cursor, &b_width, '_'); + parsed &= sn_check_parse_u32(&cursor, &y_width, '_'); + parsed &= cursor && cursor[0] == 's' && (cursor[1] == '0' || cursor[1] == '1') && + (cursor[2] == '0' || cursor[2] == '1') && cursor[3] == '\0'; + SN_CHECK(ctx, module, SN_INVALID_ID, parsed, "DSP primitive name does not encode a valid interface"); + if (pi_count != 2 || po_count != 1 || mul_count != 1 || !parsed) + return; + bool a_signed = cursor[1] == '1'; + bool b_signed = cursor[2] == '1'; + sn_obj_id_t a = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 0); + sn_obj_id_t b = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 1); + sn_obj_id_t mul = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MUL], 0); + sn_obj_id_t po = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], 0); + bool valid = sn_obj_width(module, a) == a_width && sn_obj_is_signed(module, a) == a_signed && + sn_obj_width(module, b) == b_width && sn_obj_is_signed(module, b) == b_signed && + sn_obj_width(module, mul) == y_width && sn_obj_is_signed(module, mul) == (a_signed || b_signed) && + sn_obj_fanin_count(module, mul) == 2 && sn_obj_fanin(module, mul, 0) == a && + sn_obj_fanin(module, mul, 1) == b && sn_obj_width(module, po) == y_width && + sn_obj_is_signed(module, po) == (a_signed || b_signed) && sn_obj_fanin(module, po, 0) == mul; + SN_CHECK(ctx, module, SN_INVALID_ID, valid, + "DSP primitive behavior does not match its encoded interface"); +} + +static inline uint32_t sn_check_address_width(uint32_t depth) +{ + uint32_t width = 0; + for (uint32_t value = depth - 1; value; value >>= 1) + width++; + return width ? width : 1; +} + +static inline void sn_check_memory_primitive(sn_check_ctx_t* ctx, const sn_module_t* module, const char* name) +{ + const char* marker = strstr(name, "_tdp_tile_"); + bool tdp = marker != NULL; + bool legacy = false; + if (!marker) + marker = strstr(name, "_tile_"); + if (!marker) + { + marker = strstr(name, "_mem_"); + legacy = marker != NULL; + } + const char* cursor = marker ? marker + (tdp ? 10 : legacy ? 5 : 6) : NULL; + uint32_t width = 0, depth = 0; + bool parsed = marker && sn_check_parse_u32(&cursor, &width, '_') && + sn_check_parse_u32(&cursor, &depth, '\0') && cursor && *cursor == '\0' && width && depth; + SN_CHECK(ctx, module, SN_INVALID_ID, parsed, "memory primitive name does not encode valid dimensions"); + size_t pi_count = module->type_objects[SN_PI].size; + size_t po_count = module->type_objects[SN_PO].size; + size_t reads = module->type_objects[SN_MEM_READ].size; + size_t writes = module->type_objects[SN_MEM_WRITE].size; + SN_CHECK(ctx, module, SN_INVALID_ID, module->type_objects[SN_MEM_OUT].size == 1, + "memory primitive wrapper must contain one memory"); + SN_CHECK(ctx, module, SN_INVALID_ID, reads == (tdp ? 2u : 1u) && writes == (tdp ? 2u : 1u), + "memory primitive wrapper has the wrong number of read or write ports"); + SN_CHECK(ctx, module, SN_INVALID_ID, pi_count == (tdp ? 8u : 5u) && po_count == reads, + "memory primitive interface has the wrong number of ports"); + if (!parsed || module->type_objects[SN_MEM_OUT].size != 1 || reads != (tdp ? 2u : 1u) || + writes != (tdp ? 2u : 1u) || pi_count != (tdp ? 8u : 5u) || po_count != reads) + return; + sn_obj_id_t memory = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_OUT], 0); + uint32_t address_width = legacy ? 32 : sn_check_address_width(depth); + bool valid = sn_obj_width(module, memory) == width && sn_obj_mem_depth(module, memory) == depth; + for (uint32_t port = 0; port < reads; port++) + { + uint32_t base = tdp ? 4 * port : 0; + sn_obj_id_t clock = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], base); + sn_obj_id_t enable = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], base + 1); + sn_obj_id_t address = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], base + 2); + sn_obj_id_t data = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], base + 3); + sn_obj_id_t read_address = tdp ? address : sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PI], 4); + sn_obj_id_t write = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_WRITE], port); + sn_obj_id_t read = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_MEM_READ], port); + sn_obj_id_t po = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_PO], port); + valid &= sn_obj_width(module, clock) == 1 && sn_obj_width(module, enable) == 1 && + sn_obj_width(module, address) == address_width && sn_obj_width(module, data) == width && + sn_obj_fanin(module, write, SN_MEM_WRITE_CLOCK) == clock && + sn_obj_fanin(module, write, SN_MEM_WRITE_ENABLE) == enable && + sn_obj_fanin(module, write, SN_MEM_WRITE_DATA) == data && + sn_obj_fanin(module, write, SN_MEM_WRITE_ADDRESS) == address && + sn_obj_fanin(module, read, SN_MEM_READ_MEMORY) == memory && + sn_obj_fanin(module, read, SN_MEM_READ_CLOCK) == SN_INVALID_ID && + sn_obj_fanin(module, read, SN_MEM_READ_ENABLE) == SN_INVALID_ID && + sn_obj_width(module, read_address) == address_width && + sn_obj_fanin(module, read, SN_MEM_READ_ADDRESS) == read_address && + sn_obj_width(module, po) == width && sn_obj_fanin(module, po, 0) == read; + } + SN_CHECK(ctx, module, SN_INVALID_ID, valid, + "memory primitive behavior does not match its encoded interface"); +} + +static inline void sn_check_primitive(sn_check_ctx_t* ctx, const sn_module_t* module) +{ + const char* name = sn_check_module_name(module); + bool carry = strncmp(name, "__sn_CARRY", 10) == 0; + bool dsp = strncmp(name, "__sn_DSP", 8) == 0; + bool memory = strncmp(name, "__sn_RAM", 8) == 0 || strncmp(name, "__sn_URAM", 9) == 0; + if (!carry && !dsp && !memory) + return; + if (sn_module_is_blackbox(module)) + { + SN_CHECK(ctx, module, SN_INVALID_ID, false, + "reserved __sn_ technology primitives must have a validated behavioral body"); + return; + } + if (carry) + sn_check_carry_primitive(ctx, module); + else if (dsp) + sn_check_dsp_primitive(ctx, module, name); + else + sn_check_memory_primitive(ctx, module, name); +} + +typedef struct sn_check_hierarchy_frame_t +{ + sn_module_id_t module; + size_t next_inst; +} sn_check_hierarchy_frame_t; + +static inline void sn_check_hierarchy_visit(sn_check_ctx_t* ctx, const sn_design_t* design, sn_module_id_t root, + uint8_t* states) +{ + sn_vec_t stack; + sn_vec_init(&stack); + states[root] = 1; + sn_check_hierarchy_frame_t* first = sn_vec_push(sn_check_hierarchy_frame_t, &stack); + first->module = root; + first->next_inst = 0; + while (stack.size) + { + sn_check_hierarchy_frame_t* frame = + &sn_vec_at(sn_check_hierarchy_frame_t, &stack, stack.size - 1); + const sn_module_t* module = sn_vec_at(sn_module_t*, &design->modules, frame->module); + size_t inst_count = module ? module->inst_modules.size < module->type_objects[SN_INST].size + ? module->inst_modules.size + : module->type_objects[SN_INST].size + : 0; + if (frame->next_inst >= inst_count) + { + states[frame->module] = 2; + stack.size--; + continue; + } + size_t inst_index = frame->next_inst++; + sn_module_id_t child = sn_vec_at(sn_module_id_t, &module->inst_modules, inst_index); + if (child >= design->modules.size) + continue; + if (states[child] == 1) + sn_check_error(ctx, module, sn_vec_at(sn_obj_id_t, &module->type_objects[SN_INST], inst_index), + "recursive instantiation reaches module \"%s\"", sn_check_module_name( + sn_vec_at(sn_module_t*, &design->modules, child))); + else if (states[child] == 0) + { + states[child] = 1; + sn_check_hierarchy_frame_t* child_frame = sn_vec_push(sn_check_hierarchy_frame_t, &stack); + child_frame->module = child; + child_frame->next_inst = 0; + } + } + sn_vec_destroy(&stack); +} + +static inline bool sn_design_check(const sn_design_t* design, FILE* out, bool verbose) +{ + sn_check_ctx_t ctx = {out ? out : stderr, 0, 0, 0, verbose}; + bool safe = design != NULL; + SN_CHECK(&ctx, NULL, SN_INVALID_ID, design != NULL, "null design pointer"); + if (!safe) + return false; + safe &= sn_check_vec(&ctx, NULL, &design->modules, "modules"); + safe &= sn_check_vec(&ctx, NULL, &design->names.names, "names"); + safe &= sn_check_vec(&ctx, NULL, &design->names.links, "name links"); + safe &= sn_check_vec(&ctx, NULL, &design->names.buckets, "name buckets"); + safe &= sn_check_vec(&ctx, NULL, &design->constant_words, "constant words"); + if (!safe) + return false; + SN_CHECK(&ctx, NULL, SN_INVALID_ID, design->modules.size < SN_INVALID_ID, "module count is too large"); + SN_CHECK(&ctx, NULL, SN_INVALID_ID, design->names.names.size == design->names.links.size, + "name and link vector sizes differ"); + SN_CHECK(&ctx, NULL, SN_INVALID_ID, design->names.buckets.size != 0 && + (design->names.buckets.size & (design->names.buckets.size - 1)) == 0, + "name bucket count %zu is not a nonzero power of two", design->names.buckets.size); + if (design->names.names.size != design->names.links.size) + return false; + for (size_t i = 0; i < design->names.names.size; i++) + { + const char* name = sn_vec_at(char*, &design->names.names, i); + SN_CHECK(&ctx, NULL, SN_INVALID_ID, name != NULL, "name %zu has a null string", i); + if (name) + SN_CHECK(&ctx, NULL, SN_INVALID_ID, sn_check_name_is_emittable(name), + "name %zu cannot be emitted losslessly as a Verilog identifier", i); + } + uint8_t* name_seen = design->names.names.size ? (uint8_t*)calloc(design->names.names.size, 1) : NULL; + SN_CHECK(&ctx, NULL, SN_INVALID_ID, design->names.names.size == 0 || name_seen != NULL, + "cannot allocate name-hash validation state"); + for (size_t i = 0; i < design->names.buckets.size; i++) + { + uint32_t id = sn_vec_at(uint32_t, &design->names.buckets, i); + size_t steps = 0; + while (id != SN_INVALID_ID && id < design->names.names.size && steps++ <= design->names.names.size) + { + const char* name = sn_vec_at(char*, &design->names.names, id); + if (name_seen) + { + SN_CHECK(&ctx, NULL, SN_INVALID_ID, !name_seen[id], + "name %u appears more than once in hash chains", id); + name_seen[id] = 1; + } + if (name) + SN_CHECK(&ctx, NULL, SN_INVALID_ID, + ((size_t)sn_name_hash(name) & (design->names.buckets.size - 1)) == i, + "name %u is linked from the wrong hash bucket", id); + id = sn_vec_at(uint32_t, &design->names.links, id); + } + SN_CHECK(&ctx, NULL, SN_INVALID_ID, id == SN_INVALID_ID, "name bucket %zu has an invalid or cyclic chain", i); + } + if (name_seen) + for (size_t i = 0; i < design->names.names.size; i++) + { + SN_CHECK(&ctx, NULL, SN_INVALID_ID, name_seen[i], "name %zu is unreachable from the hash table", i); + } + free(name_seen); + sn_module_id_t* module_name_owners = design->names.names.size + ? (sn_module_id_t*)malloc(design->names.names.size * + sizeof(sn_module_id_t)) + : NULL; + SN_CHECK(&ctx, NULL, SN_INVALID_ID, design->names.names.size == 0 || module_name_owners != NULL, + "cannot allocate module-name validation state"); + for (size_t i = 0; module_name_owners && i < design->names.names.size; i++) + module_name_owners[i] = SN_INVALID_ID; + for (sn_module_id_t id = 0; id < design->modules.size; id++) + { + sn_module_t* module = sn_vec_at(sn_module_t*, &design->modules, id); + size_t before = ctx.errors; + SN_CHECK(&ctx, module, SN_INVALID_ID, module != NULL, "module table entry %u is null", id); + if (module) + { + SN_CHECK(&ctx, module, SN_INVALID_ID, module->design == design, + "module points to a different owning design"); + if (module_name_owners && module->name < design->names.names.size) + { + SN_CHECK(&ctx, module, SN_INVALID_ID, module_name_owners[module->name] == SN_INVALID_ID, + "module name duplicates module %u", module_name_owners[module->name]); + if (module_name_owners[module->name] == SN_INVALID_ID) + module_name_owners[module->name] = id; + } + } + size_t before_core = ctx.errors; + if (module && module->design == design && sn_check_module_core(&ctx, module) && ctx.errors == before_core) + { + bool metadata_safe = sn_check_type_metadata(&ctx, module); + if (metadata_safe) + { + sn_check_pairs(&ctx, module, SN_REG_OUT, SN_REG_IN, SN_REG_DATA); + sn_check_pairs(&ctx, module, SN_MEM_OUT, SN_MEM_IN, SN_MEM_STATE); + sn_check_pairs(&ctx, module, SN_LOOP_OUT, SN_LOOP_IN, 0); + sn_check_memories(&ctx, module); + sn_check_instances(&ctx, module); + sn_check_special_objects(&ctx, module); + sn_check_operator_shapes(&ctx, module); + } + sn_check_auxiliary_storage(&ctx, module); + sn_check_topology(&ctx, module); + sn_check_primitive(&ctx, module); + } + ctx.modules++; + if (module) + ctx.objects += module->obj_types.size; + if (verbose) + fprintf(ctx.out, "SN check: module \"%s\": %zu object(s), %zu error(s).\n", + sn_check_module_name(module), module ? module->obj_types.size : 0, ctx.errors - before); + } + free(module_name_owners); + if (design->modules.size) + { + uint8_t* states = (uint8_t*)calloc(design->modules.size, 1); + SN_CHECK(&ctx, NULL, SN_INVALID_ID, states != NULL, "cannot allocate hierarchy traversal state"); + if (states) + { + for (sn_module_id_t id = 0; id < design->modules.size; id++) + if (states[id] == 0) + sn_check_hierarchy_visit(&ctx, design, id, states); + free(states); + } + } + if (verbose || ctx.errors) + fprintf(ctx.out, "SN check: %zu module(s), %zu object(s), %zu error(s).\n", ctx.modules, ctx.objects, + ctx.errors); + return ctx.errors == 0; +} + +// Binary input is external data. Decode its byte-level representation first, then run the same non-aborting +// consistency checker used by @check before exposing any structural IDs or offsets to ordinary SN accessors. +static inline sn_design_t* sn_design_read_binary_checked(FILE* in, FILE* errors) +{ + sn_binary_read_status_t status = SN_BINARY_READ_OK; + uint32_t version = 0; + sn_design_t* design = sn_design_read_binary_raw_status(in, &status, &version); + if (!design) + { + FILE* out = errors ? errors : stderr; + if (status == SN_BINARY_READ_VERSION) + fprintf(out, "Cannot read SN binary format version %u; this build supports versions %u through %u.\n", + version, SN_BINARY_MIN_READ_VERSION, SN_BINARY_FORMAT_VERSION); + else if (status == SN_BINARY_READ_MAGIC) + fprintf(out, "Input is not an SN binary file.\n"); + else if (status == SN_BINARY_READ_LAYOUT) + fprintf(out, "SN binary layout does not match this build.\n"); + else if (status == SN_BINARY_READ_IO) + fprintf(out, "Cannot read the SN binary header.\n"); + else + fprintf(out, "Malformed or truncated SN binary input.\n"); + return NULL; + } + if (!sn_design_check(design, errors, false)) + { + sn_design_destroy(design); + return NULL; + } + return design; +} + +static inline sn_design_t* sn_design_read_binary(FILE* in) +{ + return sn_design_read_binary_checked(in, stderr); +} + +static inline sn_design_t* sn_design_read_binary_file(const char* path) +{ + if (!path) + return NULL; + FILE* in = fopen(path, "rb"); + if (!in) + return NULL; + sn_design_t* design = sn_design_read_binary_checked(in, stderr); + if (!design) + { + fclose(in); + return NULL; + } + int extra = fgetc(in); + int status = ferror(in); + status |= fclose(in) != 0; + bool valid = extra == EOF && !status; + if (!valid) + { + sn_design_destroy(design); + return NULL; + } + return design; +} + +#undef SN_CHECK + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snCom.c b/src/base/sn/snCom.c new file mode 100644 index 000000000..5e9ead0a7 --- /dev/null +++ b/src/base/sn/snCom.c @@ -0,0 +1,2199 @@ +/**CFile**************************************************************** + + FileName [snCom.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [ABC command handlers and manager ownership for the SN design interface.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snCom.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "sn.h" +#include "snMiniAig.h" +#include "snBlast.h" +#include "snCheck.h" +#include "snMiniGate.h" +#include "snMiniLut.h" +#include "snMapLut.h" +#include "snMapTech.h" +#include "snMux.h" +#include "base/main/mainInt.h" +#include "map/mio/mio.h" + +#include +#include + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +ABC_NAMESPACE_IMPL_START + +typedef struct Sn_Man_t_ Sn_Man_t; +struct Sn_Man_t_ +{ + sn_design_t * pDesign; + sn_module_id_t Top; + sn_name_id_t Name; + unsigned long long Revision; + int Technology; + sn_module_id_t BlastModule; + sn_name_id_t BlastName; + sn_blast_boundary_t Boundary; + int fBoundary; + int fBlasted; + int BlastMode; + int fLastBlast; + sn_module_id_t LastBlastModule; + sn_name_id_t LastBlastName; + unsigned long long LastBlastRevision; + ABC_UINT64_T BlastBoundarySignature; + ABC_UINT64_T BlastInterfaceSignature; +}; + +enum +{ + SN_COMMAND_TECH_GENERIC = 0, + SN_COMMAND_TECH_XILINX_ULTRASCALE +}; + +static int Sn_CommandRead( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandSlang( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandCollapse( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandCheck( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandMapMem( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandMapDsp( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandMapAdd( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandOptMux( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandBlast( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandPut( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandMapLut( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandWrite( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandPs( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Sn_CommandStatus( Abc_Frame_t * pAbc, int argc, char ** argv ); +static const char * Sn_ManPutStatus( const Sn_Man_t * p, Gia_Man_t * pGia ); + +extern int tmpFile( const char * pPrefix, const char * pSuffix, char ** ppFileName ); + +static int Sn_TempPrefix( char * pBuffer, size_t nBuffer, const char * pStem ) +{ + int Written; +#if defined(_MSC_VER) || defined(__MINGW32__) + const char * pDirectory = getenv( "TEMP" ); + if ( pDirectory == NULL ) + pDirectory = "."; + Written = snprintf( pBuffer, nBuffer, "%s\\%s", pDirectory, pStem ); +#else + Written = snprintf( pBuffer, nBuffer, "/tmp/%s", pStem ); +#endif + return Written >= 0 && (size_t)Written < nBuffer; +} + +static inline Sn_Man_t * Sn_AbcGetMan( Abc_Frame_t * pAbc ) +{ + return (Sn_Man_t *)pAbc->pAbcSn; +} + +static void Sn_ManFree( Sn_Man_t * p ) +{ + if ( p == NULL ) + return; + if ( p->fBoundary ) + sn_blast_boundary_destroy( &p->Boundary ); + sn_design_destroy( p->pDesign ); + ABC_FREE( p ); +} + +static void Sn_AbcUpdateMan( Abc_Frame_t * pAbc, Sn_Man_t * p ) +{ + Sn_ManFree( Sn_AbcGetMan(pAbc) ); + pAbc->pAbcSn = p; +} + +static Sn_Man_t * Sn_ManAlloc( sn_design_t * pDesign, sn_module_id_t Top ) +{ + Sn_Man_t * p; + size_t i; + assert( pDesign != NULL ); + assert( Top < pDesign->modules.size ); + p = ABC_CALLOC( Sn_Man_t, 1 ); + assert( p != NULL ); + p->pDesign = pDesign; + p->Top = Top; + p->Name = sn_design_get_module_const( pDesign, Top )->name; + p->Revision = 1; + for ( i = 0; i < pDesign->modules.size; i++ ) + if ( sn_module_is_technology_primitive(sn_design_get_module_const(pDesign, (sn_module_id_t)i)) ) + { + p->Technology = SN_COMMAND_TECH_XILINX_ULTRASCALE; + break; + } + p->BlastModule = SN_INVALID_ID; + p->BlastName = SN_INVALID_ID; + p->LastBlastModule = SN_INVALID_ID; + p->LastBlastName = SN_INVALID_ID; + sn_blast_boundary_init( &p->Boundary ); + p->fBoundary = 1; + return p; +} + +static void Sn_ManAdvanceRevision( Sn_Man_t * p ) +{ + assert( p && p->Revision != ULLONG_MAX ); + p->Revision++; + p->fBlasted = 0; + p->BlastModule = SN_INVALID_ID; + p->BlastName = SN_INVALID_ID; +} + +static int Sn_FileHasSuffix( const char * pFileName, const char * pSuffix ) +{ + size_t nFileName = strlen( pFileName ); + size_t nSuffix = strlen( pSuffix ); + return nFileName >= nSuffix && strcmp( pFileName + nFileName - nSuffix, pSuffix ) == 0; +} + +static int Sn_CommandCheckDesign( Abc_Frame_t * pAbc ) +{ + if ( Sn_AbcGetMan(pAbc) != NULL ) + return 1; + Abc_Print( -1, "There is no current SN design.\n" ); + return 0; +} + +static Sn_Man_t * Sn_ManReadBinary( const char * pFileName, const char * pTopName, FILE * pError ) +{ + sn_design_t * pDesign; + sn_module_id_t Top; + FILE * pFile = fopen( pFileName, "rb" ); + int c, Status; + if ( pFile == NULL ) + { + Abc_Print( -1, "Cannot open input file \"%s\".\n", pFileName ); + return NULL; + } + pDesign = sn_design_read_binary_checked( pFile, pError ); + if ( pDesign == NULL ) + { + fclose( pFile ); + Abc_Print( -1, "Cannot read SN design from \"%s\".\n", pFileName ); + return NULL; + } + c = fgetc( pFile ); + Status = ferror( pFile ); + Status |= fclose( pFile ) != 0; + if ( c != EOF || Status ) + { + Abc_Print( -1, "Cannot finish reading input file \"%s\".\n", pFileName ); + sn_design_destroy( pDesign ); + return NULL; + } + if ( pDesign->modules.size == 0 ) + { + Abc_Print( -1, "SN design \"%s\" contains no modules.\n", pFileName ); + sn_design_destroy( pDesign ); + return NULL; + } + Top = pTopName ? sn_design_find_module( pDesign, pTopName ) : (sn_module_id_t)(pDesign->modules.size - 1); + if ( Top == SN_INVALID_ID ) + { + Abc_Print( -1, "Cannot find top module \"%s\" in SN design \"%s\".\n", pTopName, pFileName ); + sn_design_destroy( pDesign ); + return NULL; + } + if ( !sn_design_is_topo(pDesign) ) + { + Abc_Print( -1, "SN design \"%s\" is not topologically ordered.\n", pFileName ); + sn_design_destroy( pDesign ); + return NULL; + } + return Sn_ManAlloc( pDesign, Top ); +} + +static Sn_Man_t * Sn_ManDup( const Sn_Man_t * p ) +{ + sn_design_t * pDesign; + Sn_Man_t * pNew; + if ( p == NULL ) + return NULL; + pDesign = sn_design_dup( p->pDesign ); + assert( p->Top < pDesign->modules.size ); + pNew = Sn_ManAlloc( pDesign, p->Top ); + pNew->Name = p->Name; + pNew->Revision = p->Revision; + pNew->Technology = p->Technology; + pNew->fLastBlast = p->fLastBlast; + pNew->LastBlastModule = p->LastBlastModule; + pNew->LastBlastName = p->LastBlastName; + pNew->LastBlastRevision = p->LastBlastRevision; + pNew->BlastMode = p->BlastMode; + pNew->BlastBoundarySignature = p->BlastBoundarySignature; + pNew->BlastInterfaceSignature = p->BlastInterfaceSignature; + return pNew; +} + +// Installs a freshly reconstructed module at the stable ID selected by @blast -M. Parent insts therefore continue to +// reference the same module, and modules outside the selected hierarchy remain untouched. The temporary append-only +// module is removed from the design vector after its storage is moved into the selected slot. +static void Sn_ManReplaceModule( Sn_Man_t * p, sn_module_id_t Module, sn_name_id_t Name, + sn_module_id_t Temporary ) +{ + assert( p ); + sn_design_replace_appended_module( p->pDesign, Module, Name, Temporary ); +} + +static void Sn_ManReplaceBlastedModule( Sn_Man_t * p, sn_module_id_t Temporary ) +{ + Sn_ManReplaceModule( p, p->BlastModule, p->BlastName, Temporary ); +} + +static char * Sn_SlangExecutable() +{ + char * pExecutable = Abc_FrameReadFlag( "snslang" ); + if ( pExecutable != NULL ) + return pExecutable; +#if defined(_MSC_VER) || defined(__MINGW32__) + return "sn_slang.exe"; +#else + return "sn_slang"; +#endif +} + +static int Sn_RunProcess( char ** ppArgs ) +{ +#if defined(__wasm) + (void)ppArgs; + return -1; +#elif defined(_MSC_VER) || defined(__MINGW32__) + return (int)_spawnvp( _P_WAIT, ppArgs[0], (const char * const *)ppArgs ); +#else + pid_t Child = fork(); + int Status; + if ( Child < 0 ) + return -1; + if ( Child == 0 ) + { + execvp( ppArgs[0], ppArgs ); + // execvp() returns only on failure. Release the child copy so memory checkers do not report it as leaked; + // the parent's copy is unaffected and is freed by the caller. + ABC_FREE( ppArgs ); + _exit( 127 ); + } + if ( waitpid(Child, &Status, 0) != Child ) + return -1; + return WIFEXITED(Status) ? WEXITSTATUS(Status) : -1; +#endif +} + +static int Sn_DesignHasType( const sn_design_t * pDesign, sn_obj_type_t Type ) +{ + size_t i; + for ( i = 0; i < pDesign->modules.size; i++ ) + if ( sn_design_get_module_const(pDesign, (sn_module_id_t)i)->type_objects[Type].size ) + return 1; + return 0; +} + +static int Sn_CommandRejectLatches( const sn_design_t * pDesign, sn_module_id_t Root, const char * pCommand ) +{ + sn_module_id_t LatchModule = sn_design_find_reachable_latch( pDesign, Root, NULL ); + const sn_module_t * pModule; + if ( LatchModule == SN_INVALID_ID ) + return 0; + pModule = sn_design_get_module_const( pDesign, LatchModule ); + Abc_Print( -1, "Cannot %s: reachable module \"%s\" contains a level-sensitive latch; " + "latch blasting is not supported.\n", + pCommand, sn_name_get(&pDesign->names, pModule->name) ); + return 1; +} + +static void Sn_FormatMemory( size_t Bytes, char * pBuffer, size_t BufferSize ) +{ + static const char * pUnits[] = { "", "K", "M", "G" }; + double Value = (double)Bytes; + int Unit = 0; + while ( Value >= 1000.0 && Unit < 3 ) + { + Value /= 1000.0; + Unit++; + } + if ( Unit == 0 ) + snprintf( pBuffer, BufferSize, "%zu", Bytes ); + else + snprintf( pBuffer, BufferSize, "%.1f%s", Value, pUnits[Unit] ); +} + +static void Sn_ModulePrintStats( FILE * pOut, const sn_module_t * pModule, int fMem, int fLut, int fGate ) +{ + uint64_t nRegBits = 0; + uint64_t nMemBits = 0; + size_t i; + for ( i = 0; i < pModule->type_objects[SN_REG_OUT].size; i++ ) + { + sn_obj_id_t Obj = sn_vec_at( sn_obj_id_t, &pModule->type_objects[SN_REG_OUT], i ); + nRegBits += sn_obj_width( pModule, Obj ); + } + for ( i = 0; i < pModule->type_objects[SN_MEM_OUT].size; i++ ) + { + sn_obj_id_t Obj = sn_vec_at( sn_obj_id_t, &pModule->type_objects[SN_MEM_OUT], i ); + nMemBits += (uint64_t)sn_obj_width( pModule, Obj ) * sn_obj_mem_depth( pModule, Obj ); + } + fprintf( pOut, "%-24s : obj = %8zu pi = %6zu po = %6zu reg = %6zu/%llu inst = %6zu", + sn_name_get( &pModule->design->names, pModule->name ), pModule->obj_types.size, + pModule->type_objects[SN_PI].size, pModule->type_objects[SN_PO].size, + pModule->type_objects[SN_REG_OUT].size, (unsigned long long)nRegBits, + pModule->type_objects[SN_INST].size ); + if ( fMem ) + fprintf( pOut, " mem = %5zu/%llu", pModule->type_objects[SN_MEM_OUT].size, + (unsigned long long)nMemBits ); + if ( fLut ) + fprintf( pOut, " lut = %6zu", pModule->type_objects[SN_LUT].size ); + if ( fGate ) + fprintf( pOut, " gate = %6zu", pModule->type_objects[SN_GATE].size ); + fprintf( pOut, "\n" ); +} + +typedef struct Sn_DistribEntry_t_ Sn_DistribEntry_t; +struct Sn_DistribEntry_t_ +{ + uint32_t OutWidth; + uint32_t In0Width; + uint32_t In1Width; + uint32_t FaninNum; + uint64_t Occur; + unsigned char Signs; +}; + +static const char * Sn_ObjTypeNames[SN_OBJ_TYPE_COUNT] = { + "none", "pi", "po", "const0", "const1", "const", "buf", "fan", "inst", + "reg_out", "reg_in", "mem_out", "mem_in", "mem_read", "mem_write", "loop_out", "loop_in", + "+u", "-u", "~", "!", "&r", "~&r", "|r", "~|r", "^r", "~^r", + "+", "-", "*", "/", "%", "**", "&", "|", "^", "~^", "&&", "||", + "==", "!=", "===", "!==", "==?", "!=?", "<", "<=", ">", ">=", "<<", ">>", "<<<", ">>>", + "mux", "bmux", "pmux", "{,}", "repeat", "slice", "cast", "lut", "gate" +}; + +static int Sn_DistribEntryCompare( const void * pLeft, const void * pRight ) +{ + const Sn_DistribEntry_t * pL = (const Sn_DistribEntry_t *)pLeft; + const Sn_DistribEntry_t * pR = (const Sn_DistribEntry_t *)pRight; + if ( pL->Occur != pR->Occur ) + return pL->Occur < pR->Occur ? 1 : -1; + if ( pL->OutWidth != pR->OutWidth ) + return pL->OutWidth < pR->OutWidth ? 1 : -1; + if ( pL->In0Width != pR->In0Width ) + return pL->In0Width < pR->In0Width ? 1 : -1; + if ( pL->In1Width != pR->In1Width ) + return pL->In1Width < pR->In1Width ? 1 : -1; + if ( pL->FaninNum != pR->FaninNum ) + return pL->FaninNum < pR->FaninNum ? 1 : -1; + return (int)pL->Signs - (int)pR->Signs; +} + +static int Sn_DistribAdd( Sn_DistribEntry_t ** ppEntries, size_t * pnEntries, size_t * pnCap, + const sn_module_t * pModule, sn_obj_id_t Obj, uint64_t Mult ) +{ + Sn_DistribEntry_t Entry; + uint32_t nFanins = sn_obj_fanin_count( pModule, Obj ); + uint32_t i; + memset( &Entry, 0, sizeof(Entry) ); + Entry.OutWidth = sn_obj_width( pModule, Obj ); + Entry.FaninNum = nFanins; + Entry.Signs = (unsigned char)sn_obj_is_signed( pModule, Obj ); + for ( i = 0; i < nFanins && i < 2; i++ ) + { + sn_obj_id_t Fanin = sn_obj_fanin( pModule, Obj, i ); + if ( Fanin == SN_INVALID_ID ) + continue; + if ( i == 0 ) + Entry.In0Width = sn_obj_width( pModule, Fanin ); + else + Entry.In1Width = sn_obj_width( pModule, Fanin ); + Entry.Signs |= (unsigned char)(sn_obj_is_signed( pModule, Fanin ) << (i + 1)); + } + for ( i = 0; i < *pnEntries; i++ ) + { + Sn_DistribEntry_t * pOld = *ppEntries + i; + if ( pOld->OutWidth == Entry.OutWidth && pOld->In0Width == Entry.In0Width && + pOld->In1Width == Entry.In1Width && pOld->FaninNum == Entry.FaninNum && pOld->Signs == Entry.Signs ) + { + if ( UINT64_MAX - pOld->Occur < Mult ) + return 0; + pOld->Occur += Mult; + return 1; + } + } + if ( *pnEntries == *pnCap ) + { + Sn_DistribEntry_t * pNew; + if ( *pnCap > SIZE_MAX / 2 / sizeof(Sn_DistribEntry_t) ) + return 0; + *pnCap = *pnCap ? 2 * *pnCap : 8; + pNew = ABC_REALLOC( Sn_DistribEntry_t, *ppEntries, *pnCap ); + if ( pNew == NULL ) + return 0; + *ppEntries = pNew; + } + Entry.Occur = Mult; + (*ppEntries)[(*pnEntries)++] = Entry; + return 1; +} + +static int Sn_ModuleCollectDistrib( const sn_design_t * pDesign, sn_module_id_t ModuleId, uint64_t Mult, + Sn_DistribEntry_t ** ppEntries, size_t * pnEntries, size_t * pnCaps, + uint64_t * pTypeCounts ) +{ + const sn_module_t * pModule = sn_design_get_module_const( pDesign, ModuleId ); + sn_obj_id_t Obj; + for ( Obj = 0; Obj < pModule->obj_types.size; Obj++ ) + { + sn_obj_type_t Type = sn_obj_type( pModule, Obj ); + if ( UINT64_MAX - pTypeCounts[Type] < Mult ) + return 0; + pTypeCounts[Type] += Mult; + if ( !Sn_DistribAdd(ppEntries + Type, pnEntries + Type, pnCaps + Type, pModule, Obj, Mult) ) + return 0; + } + return 1; +} + +typedef struct Sn_DistribFrame_t_ +{ + sn_module_id_t Module; + size_t NextInst; +} Sn_DistribFrame_t; + +// Returns the number of reachable occurrences of each module definition under Top. The hierarchy is a DAG, so a +// reverse-postorder propagation accounts for repeated insts without expanding every hierarchical occurrence. +static uint64_t * Sn_DesignCountModuleOccurrences( const sn_design_t * pDesign, sn_module_id_t Top ) +{ + unsigned char * pStates; + uint64_t * pMults; + sn_vec_t Stack, Postorder; + Sn_DistribFrame_t * pFrame; + size_t i; + if ( pDesign == NULL || Top >= pDesign->modules.size ) + return NULL; + pStates = ABC_CALLOC( unsigned char, pDesign->modules.size ); + pMults = ABC_CALLOC( uint64_t, pDesign->modules.size ); + if ( pStates == NULL || pMults == NULL ) + { + ABC_FREE( pStates ); + ABC_FREE( pMults ); + return NULL; + } + sn_vec_init( &Stack ); + sn_vec_init( &Postorder ); + pStates[Top] = 1; + pFrame = sn_vec_push( Sn_DistribFrame_t, &Stack ); + pFrame->Module = Top; + pFrame->NextInst = 0; + while ( Stack.size ) + { + const sn_module_t * pModule; + pFrame = &sn_vec_at( Sn_DistribFrame_t, &Stack, Stack.size - 1 ); + pModule = sn_design_get_module_const( pDesign, pFrame->Module ); + if ( pFrame->NextInst < pModule->inst_modules.size ) + { + sn_module_id_t Child = sn_vec_at( sn_module_id_t, &pModule->inst_modules, pFrame->NextInst++ ); + if ( Child >= pDesign->modules.size || pStates[Child] == 1 ) + goto fail; + if ( pStates[Child] == 0 ) + { + pStates[Child] = 1; + pFrame = sn_vec_push( Sn_DistribFrame_t, &Stack ); + pFrame->Module = Child; + pFrame->NextInst = 0; + } + continue; + } + pStates[pFrame->Module] = 2; + *sn_vec_push( sn_module_id_t, &Postorder ) = pFrame->Module; + Stack.size--; + } + pMults[Top] = 1; + for ( i = Postorder.size; i-- > 0; ) + { + sn_module_id_t Module = sn_vec_at( sn_module_id_t, &Postorder, i ); + const sn_module_t * pModule = sn_design_get_module_const( pDesign, Module ); + uint64_t Mult = pMults[Module]; + if ( Mult == 0 ) + continue; + for ( size_t k = 0; k < pModule->inst_modules.size; k++ ) + { + sn_module_id_t Child = sn_vec_at( sn_module_id_t, &pModule->inst_modules, k ); + if ( Child >= pDesign->modules.size || UINT64_MAX - pMults[Child] < Mult ) + goto fail; + pMults[Child] += Mult; + } + } + sn_vec_destroy( &Postorder ); + sn_vec_destroy( &Stack ); + ABC_FREE( pStates ); + return pMults; + +fail: + sn_vec_destroy( &Postorder ); + sn_vec_destroy( &Stack ); + ABC_FREE( pStates ); + ABC_FREE( pMults ); + return NULL; +} + +static int Sn_ModulePortBits( const sn_module_t * pModule, sn_obj_type_t Type, uint64_t * pBits ) +{ + uint64_t Bits = 0; + size_t i; + assert( Type == SN_PI || Type == SN_PO ); + for ( i = 0; i < pModule->type_objects[Type].size; i++ ) + { + uint32_t Width = sn_obj_width( pModule, sn_vec_at(sn_obj_id_t, &pModule->type_objects[Type], i) ); + if ( UINT64_MAX - Bits < Width ) + return 0; + Bits += Width; + } + *pBits = Bits; + return 1; +} + +static void Sn_DesignPrintBlackboxes( FILE * pOut, const sn_design_t * pDesign, sn_module_id_t Top ) +{ + const sn_module_t * pTop = sn_design_get_module_const( pDesign, Top ); + uint64_t * pMults = Sn_DesignCountModuleOccurrences( pDesign, Top ); + uint64_t nOccurrences = 0, nAigInputs = 0, nAigOutputs = 0; + size_t nTypes = 0, i; + if ( pMults == NULL ) + { + fprintf( pOut, "Cannot count black-box occurrences: hierarchy is invalid or the count overflows.\n" ); + return; + } + fprintf( pOut, "Black boxes reachable from \"%s\":\n", sn_name_get(&pDesign->names, pTop->name) ); + fprintf( pOut, "Module occurrences PI ports/bits PO ports/bits " + "AIG inputs AIG outputs\n" ); + for ( i = 0; i < pDesign->modules.size; i++ ) + { + const sn_module_t * pModule = sn_design_get_module_const( pDesign, (sn_module_id_t)i ); + uint64_t Mult = pMults[i], PiBits, PoBits, AigInputs, AigOutputs; + if ( Mult == 0 || !sn_module_is_blackbox(pModule) ) + continue; + if ( !Sn_ModulePortBits(pModule, SN_PI, &PiBits) || !Sn_ModulePortBits(pModule, SN_PO, &PoBits) || + (PoBits != 0 && Mult > UINT64_MAX / PoBits) || + (PiBits != 0 && Mult > UINT64_MAX / PiBits) ) + goto overflow; + AigInputs = Mult * PoBits; + AigOutputs = Mult * PiBits; + if ( UINT64_MAX - nOccurrences < Mult || UINT64_MAX - nAigInputs < AigInputs || + UINT64_MAX - nAigOutputs < AigOutputs ) + goto overflow; + nTypes++; + nOccurrences += Mult; + nAigInputs += AigInputs; + nAigOutputs += AigOutputs; + fprintf( pOut, "%-32s %10llu %6zu/%-6llu %6zu/%-6llu %10llu %11llu\n", + sn_name_get(&pDesign->names, pModule->name), (unsigned long long)Mult, + pModule->type_objects[SN_PI].size, (unsigned long long)PiBits, + pModule->type_objects[SN_PO].size, (unsigned long long)PoBits, + (unsigned long long)AigInputs, (unsigned long long)AigOutputs ); + } + fprintf( pOut, "Black-box totals: types = %zu occurrences = %llu AIG inputs = %llu AIG outputs = %llu\n", + nTypes, (unsigned long long)nOccurrences, (unsigned long long)nAigInputs, + (unsigned long long)nAigOutputs ); + ABC_FREE( pMults ); + return; + +overflow: + fprintf( pOut, "Cannot print black-box statistics: a bit or occurrence total overflows 64 bits.\n" ); + ABC_FREE( pMults ); +} + +static void Sn_DesignPrintDistrib( FILE * pOut, const sn_design_t * pDesign, sn_module_id_t Top ) +{ + Sn_DistribEntry_t * pEntries[SN_OBJ_TYPE_COUNT] = { NULL }; + size_t nEntries[SN_OBJ_TYPE_COUNT] = { 0 }; + size_t nCaps[SN_OBJ_TYPE_COUNT] = { 0 }; + uint64_t TypeCounts[SN_OBJ_TYPE_COUNT] = { 0 }; + uint64_t * pMults = Sn_DesignCountModuleOccurrences( pDesign, Top ); + size_t i; + int Type; + if ( pMults == NULL ) + { + fprintf( pOut, "Cannot print object distribution: hierarchy is invalid or the occurrence count overflows.\n" ); + return; + } + for ( i = 0; i < pDesign->modules.size; i++ ) + if ( pMults[i] && !Sn_ModuleCollectDistrib(pDesign, (sn_module_id_t)i, pMults[i], pEntries, + nEntries, nCaps, TypeCounts) ) + { + fprintf( pOut, "Cannot print object distribution: a count overflows or allocation failed.\n" ); + for ( Type = 0; Type < SN_OBJ_TYPE_COUNT; Type++ ) + ABC_FREE( pEntries[Type] ); + ABC_FREE( pMults ); + return; + } + fprintf( pOut, "ID : name occurrence (occurrence)=. ...\n" ); + for ( Type = 0; Type < SN_OBJ_TYPE_COUNT; Type++ ) + { + size_t k; + if ( TypeCounts[Type] == 0 ) + continue; + qsort( pEntries[Type], nEntries[Type], sizeof(Sn_DistribEntry_t), Sn_DistribEntryCompare ); + fprintf( pOut, "%2d : %-10s %10llu ", Type, Sn_ObjTypeNames[Type], + (unsigned long long)TypeCounts[Type] ); + for ( k = 0; k < nEntries[Type]; k++ ) + { + const Sn_DistribEntry_t * pEntry = pEntries[Type] + k; + if ( k && k % 6 == 0 ) + fprintf( pOut, "\n " ); + fprintf( pOut, "(%llu)%s%u", (unsigned long long)pEntry->Occur, + (pEntry->Signs & 1) ? "-" : "", pEntry->OutWidth ); + if ( pEntry->FaninNum ) + fprintf( pOut, "=%s%u", (pEntry->Signs & 2) ? "-" : "", pEntry->In0Width ); + if ( pEntry->FaninNum > 1 ) + fprintf( pOut, ".%s%u", (pEntry->Signs & 4) ? "-" : "", pEntry->In1Width ); + if ( pEntry->FaninNum > 2 ) + fprintf( pOut, "[%u]", pEntry->FaninNum ); + fprintf( pOut, " " ); + } + fprintf( pOut, "\n" ); + ABC_FREE( pEntries[Type] ); + } + ABC_FREE( pMults ); +} + +void Sn_Init( Abc_Frame_t * pAbc ) +{ + Cmd_CommandAdd( pAbc, "New word level", "@slang", Sn_CommandSlang, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@read", Sn_CommandRead, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@check", Sn_CommandCheck, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@collapse", Sn_CommandCollapse, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@map_mem", Sn_CommandMapMem, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@map_dsp", Sn_CommandMapDsp, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@map_add", Sn_CommandMapAdd, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@opt_mux", Sn_CommandOptMux, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@blast", Sn_CommandBlast, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@put", Sn_CommandPut, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@map_lut", Sn_CommandMapLut, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@write", Sn_CommandWrite, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@ps", Sn_CommandPs, 0 ); + Cmd_CommandAdd( pAbc, "New word level", "@status", Sn_CommandStatus, 0 ); +} + +void Sn_End( Abc_Frame_t * pAbc ) +{ + Sn_AbcUpdateMan( pAbc, NULL ); +} + +static int Sn_CommandRead( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + char * pTopName = NULL; + char * pFileName; + Sn_Man_t * p; + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "Mvh")) != EOF ) + { + switch ( c ) + { + case 'M': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-M\" should be followed by a module name.\n" ); + goto usage; + } + pTopName = argv[globalUtilOptind++]; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + default: + goto usage; + } + } + if ( argc - globalUtilOptind != 1 ) + goto usage; + pFileName = argv[globalUtilOptind]; + p = Sn_ManReadBinary( pFileName, pTopName, Abc_FrameReadErr(pAbc) ); + if ( p == NULL ) + return 1; + Sn_AbcUpdateMan( pAbc, p ); + if ( fVerbose ) + Abc_Print( 1, "Read SN design \"%s\" with %zu modules. Top module is \"%s\".\n", pFileName, + p->pDesign->modules.size, + sn_name_get(&p->pDesign->names, sn_design_get_module_const(p->pDesign, p->Top)->name) ); + return 0; + +usage: + Abc_Print( -2, "usage: @read [-Mvh] \n" ); + Abc_Print( -2, "\t reads a binary SN design\n" ); + Abc_Print( -2, "\t-M name : select the top module [default = last module]\n" ); + Abc_Print( -2, "\t-v : print verbose output\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static int Sn_CommandSlang( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + char TempPrefix[512]; + char * pTopName = NULL; + char * pExtraFile = NULL; + char * pDefine; + char * pTempName = NULL; + char ** ppArgs; + Vec_Ptr_t * vDefines = Vec_PtrAlloc( 4 ); + Sn_Man_t * p; + int c, fVerbose = 0, nFiles, nArgs, i, k, File; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "MFDBIvh")) != EOF ) + { + switch ( c ) + { + case 'M': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-%c\" should be followed by a module name.\n", c ); + goto usage; + } + pTopName = argv[globalUtilOptind++]; + break; + case 'F': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-F\" should be followed by a file name.\n" ); + goto usage; + } + pExtraFile = argv[globalUtilOptind++]; + break; + case 'D': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-D\" should be followed by definitions.\n" ); + goto usage; + } + Vec_PtrPush( vDefines, argv[globalUtilOptind++] ); + break; + case 'B': + case 'I': + Abc_Print( -1, "Command line switch \"-%c\" is not supported by the external SN frontend yet.\n", c ); + Vec_PtrFree( vDefines ); + return 1; + case 'v': + fVerbose ^= 1; + break; + case 'h': + default: + goto usage; + } + } + nFiles = argc - globalUtilOptind + (pExtraFile != NULL); + if ( nFiles == 0 ) + goto usage; + for ( i = globalUtilOptind; i < argc; i++ ) + { + FILE * pFile = fopen( argv[i], "r" ); + if ( pFile == NULL ) + { + Abc_Print( -1, "Cannot open input file \"%s\".\n", argv[i] ); + Vec_PtrFree( vDefines ); + return 1; + } + fclose( pFile ); + } + if ( pExtraFile != NULL ) + { + FILE * pFile = fopen( pExtraFile, "r" ); + if ( pFile == NULL ) + { + Abc_Print( -1, "Cannot open input file \"%s\".\n", pExtraFile ); + Vec_PtrFree( vDefines ); + return 1; + } + fclose( pFile ); + } + if ( !Sn_TempPrefix(TempPrefix, sizeof(TempPrefix), "sn_slang_") ) + { + Abc_Print( -1, "Temporary-file path is too long.\n" ); + Vec_PtrFree( vDefines ); + return 1; + } + File = tmpFile( TempPrefix, ".sn", &pTempName ); + if ( File < 0 ) + { + Abc_Print( -1, "Cannot create a temporary SN file.\n" ); + Vec_PtrFree( vDefines ); + return 1; + } +#if defined(_MSC_VER) || defined(__MINGW32__) + _close( File ); +#else + close( File ); +#endif + nArgs = 1 + (pTopName ? 2 : 0) + 2 * Vec_PtrSize(vDefines) + (fVerbose ? 1 : 0) + 2 + nFiles + 1; + ppArgs = ABC_ALLOC( char *, nArgs ); + k = 0; + ppArgs[k++] = Sn_SlangExecutable(); + if ( pTopName ) + { + ppArgs[k++] = "-M"; + ppArgs[k++] = pTopName; + } + Vec_PtrForEachEntry( char *, vDefines, pDefine, i ) + { + ppArgs[k++] = "-D"; + ppArgs[k++] = pDefine; + } + if ( fVerbose ) + ppArgs[k++] = "-t"; + ppArgs[k++] = "-o"; + ppArgs[k++] = pTempName; + for ( i = globalUtilOptind; i < argc; i++ ) + ppArgs[k++] = argv[i]; + if ( pExtraFile ) + ppArgs[k++] = pExtraFile; + ppArgs[k] = NULL; + assert( k + 1 == nArgs ); + if ( fVerbose ) + { + Abc_Print( 1, "Running:" ); + for ( i = 0; i < k; i++ ) + Abc_Print( 1, " %s", ppArgs[i] ); + Abc_Print( 1, "\n" ); + fflush( pAbc->Out ); + } + c = Sn_RunProcess( ppArgs ); + ABC_FREE( ppArgs ); + Vec_PtrFree( vDefines ); + if ( c != 0 ) + { + Abc_Print( -1, "External SN frontend failed with status %d.\n", c ); + remove( pTempName ); + ABC_FREE( pTempName ); + return 1; + } + p = Sn_ManReadBinary( pTempName, pTopName, Abc_FrameReadErr(pAbc) ); + remove( pTempName ); + ABC_FREE( pTempName ); + if ( p == NULL ) + return 1; + Sn_AbcUpdateMan( pAbc, p ); + if ( fVerbose ) + Abc_Print( 1, "Loaded SN design with %zu modules.\n", p->pDesign->modules.size ); + return 0; + +usage: + Vec_PtrFree( vDefines ); + Abc_Print( -2, "usage: @slang [-M ] [-D ] [-F ] [-vh] ...\n" ); + Abc_Print( -2, "\t reads Verilog or SystemVerilog using the external sn_slang frontend\n" ); + Abc_Print( -2, "\t based on Mike Popoloski's slang: https://github.com/MikePopoloski/slang\n" ); + Abc_Print( -2, "\t-M name : select the top module\n" ); + Abc_Print( -2, "\t-D def : define one macro as NAME or NAME=value; may be repeated\n" ); + Abc_Print( -2, "\t-F file : add another Verilog/SystemVerilog input file\n" ); + Abc_Print( -2, "\t-v : print the external command and frontend timing\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static int Sn_CommandCollapse( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Sn_Man_t * p; + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + switch ( c ) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + default: + goto usage; + } + } + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + p = Sn_ManDup( Sn_AbcGetMan(pAbc) ); + if ( p == NULL ) + { + Abc_Print( -1, "Cannot duplicate the current SN design.\n" ); + return 1; + } + p->Top = sn_design_collapse_module_tech( p->pDesign, p->Top ); + assert( sn_design_is_topo(p->pDesign) ); + Sn_ManAdvanceRevision( p ); + Sn_AbcUpdateMan( pAbc, p ); + if ( fVerbose ) + Abc_Print( 1, "Collapsed SN design into module \"%s\".\n", + sn_name_get(&p->pDesign->names, sn_design_get_module_const(p->pDesign, p->Top)->name) ); + return 0; + +usage: + Abc_Print( -2, "usage: @collapse [-vh]\n" ); + Abc_Print( -2, "\t flattens user hierarchy while preserving mapped technology primitives\n" ); + Abc_Print( -2, "\t-v : print the resulting flat module name\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static int Sn_CommandMapTech( Abc_Frame_t * pAbc, int fMapMem, int fMapDsp, int fMapAdd, int fVerbose ) +{ + Sn_Man_t * p = Sn_ManDup( Sn_AbcGetMan(pAbc) ); + sn_tech_t Tech = sn_tech_xilinx_ultrascale(); + sn_tech_map_options_t Options = sn_tech_map_default_options(); + sn_tech_map_stats_t Stats = {0}; + Options.map_memories = fMapMem != 0; + Options.map_multipliers = fMapDsp != 0; + Options.map_adders = fMapAdd != 0; + if ( p == NULL ) + { + Abc_Print( -1, "Cannot duplicate the current SN design.\n" ); + return 1; + } + if ( !sn_design_check(p->pDesign, Abc_FrameReadErr(pAbc), false) ) + { + Sn_ManFree( p ); + return 1; + } + p->Top = sn_design_map_tech_hierarchy( p->pDesign, p->Top, &Tech, &Options, &Stats ); + if ( p->Top == SN_INVALID_ID ) + { + Abc_Print( -1, "Technology mapping cannot honor the requested hard-block constraints.\n" ); + Sn_ManFree( p ); + return 1; + } + if ( !sn_design_check(p->pDesign, Abc_FrameReadErr(pAbc), false) ) + { + Sn_ManFree( p ); + return 1; + } + p->Technology = SN_COMMAND_TECH_XILINX_ULTRASCALE; + Sn_ManAdvanceRevision( p ); + Sn_AbcUpdateMan( pAbc, p ); + if ( fVerbose ) + Abc_Print( 1, "Mapped SN design: memory instances = %zu DSP instances = %zu CARRY4 instances = %zu.\n", + Stats.mem_insts, Stats.dsp_insts, Stats.carry_insts ); + return 0; +} + +static int Sn_CommandCheck( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + if ( c == 'v' ) + fVerbose ^= 1; + else + goto usage; + } + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + if ( !sn_design_check(Sn_AbcGetMan(pAbc)->pDesign, Abc_FrameReadErr(pAbc), fVerbose != 0) ) + return 1; + if ( !fVerbose ) + Abc_Print( 1, "SN design is consistent.\n" ); + return 0; + +usage: + Abc_Print( -2, "usage: @check [-vh]\n" ); + Abc_Print( -2, "\t checks the complete SN design for structural consistency\n" ); + Abc_Print( -2, "\t-v : print per-module and design summaries\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static int Sn_CommandMapMem( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + if ( c == 'v' ) + fVerbose ^= 1; + else + goto usage; + } + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + return Sn_CommandMapTech( pAbc, 1, 0, 0, fVerbose ); + +usage: + Abc_Print( -2, "usage: @map_mem [-vh]\n" ); + Abc_Print( -2, "\t maps memories into AMD/Xilinx UltraScale+ primitives\n" ); + Abc_Print( -2, "\t-v : print mapping statistics\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static int Sn_CommandMapDsp( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + if ( c == 'v' ) + fVerbose ^= 1; + else + goto usage; + } + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + return Sn_CommandMapTech( pAbc, 0, 1, 0, fVerbose ); + +usage: + Abc_Print( -2, "usage: @map_dsp [-vh]\n" ); + Abc_Print( -2, "\t maps multipliers into AMD/Xilinx UltraScale+ DSP primitives\n" ); + Abc_Print( -2, "\t-v : print mapping statistics\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static int Sn_CommandMapAdd( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + if ( c == 'v' ) + fVerbose ^= 1; + else + goto usage; + } + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + return Sn_CommandMapTech( pAbc, 0, 0, 1, fVerbose ); + +usage: + Abc_Print( -2, "usage: @map_add [-vh]\n" ); + Abc_Print( -2, "\t maps adders and subtractors into AMD/Xilinx UltraScale+ CARRY4 primitives\n" ); + Abc_Print( -2, "\t-v : print mapping statistics\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static int Sn_CommandOptMux( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Sn_Man_t * p, * pCurrent; + sn_share_options_t Options = sn_share_default_options(); + sn_share_stats_t Stats; + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + if ( c == 'v' ) + fVerbose ^= 1; + else + goto usage; + } + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + pCurrent = Sn_AbcGetMan( pAbc ); + if ( !sn_design_check(pCurrent->pDesign, Abc_FrameReadErr(pAbc), 0) ) + { + Abc_Print( -1, "Cannot @opt_mux: the current SN design is inconsistent.\n" ); + return 1; + } + p = Sn_ManDup( pCurrent ); + if ( p == NULL ) + { + Abc_Print( -1, "Cannot duplicate the current SN design.\n" ); + return 1; + } + Stats = sn_design_share( p->pDesign, Options ); + if ( !sn_design_check(p->pDesign, Abc_FrameReadErr(pAbc), 0) ) + { + Abc_Print( -1, "Cannot @opt_mux: the transformed SN design is inconsistent.\n" ); + Sn_ManFree( p ); + return 1; + } + if ( Stats.modules == 0 ) + { + Sn_ManFree( p ); + if ( fVerbose ) + Abc_Print( 1, "Optimized SN mux paths: no profitable rewrites.\n" ); + return 0; + } + Sn_ManAdvanceRevision( p ); + Sn_AbcUpdateMan( pAbc, p ); + if ( fVerbose ) + Abc_Print( 1, "Optimized SN mux paths: modules = %llu registers = %llu muxes = %llu paths = %llu -> %llu.\n", + (unsigned long long)Stats.modules, (unsigned long long)Stats.registers, + (unsigned long long)Stats.muxes, (unsigned long long)Stats.paths_before, + (unsigned long long)Stats.paths_after ); + return 0; + +usage: + Abc_Print( -2, "usage: @opt_mux [-vh]\n" ); + Abc_Print( -2, "\t shares repeated alternatives in register mux cones\n" ); + Abc_Print( -2, "\t-v : print transformation statistics\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static char * Sn_BoundaryName( const Sn_Man_t * p, const sn_blast_boundary_bit_t * pBit, + const char * pFallback, int Index ) +{ + const char * pBase = pFallback; + char * pName; + int nChars; + if ( pBit->signal.occurrence < p->Boundary.occurrences.size ) + { + sn_module_id_t Module = sn_vec_at( sn_blast_occurrence_t, &p->Boundary.occurrences, + pBit->signal.occurrence ).module; + const sn_module_t * pModule = sn_design_get_module_const( p->pDesign, Module ); + if ( pBit->signal.object < pModule->obj_types.size ) + { + sn_name_id_t Name = sn_obj_name_id( pModule, pBit->signal.object ); + if ( Name != SN_INVALID_ID ) + pBase = sn_name_get( &p->pDesign->names, Name ); + } + } + nChars = snprintf( NULL, 0, "%s[%u]_%d", pBase, pBit->signal.bit, Index ); + assert( nChars >= 0 ); + pName = ABC_ALLOC( char, nChars + 1 ); + snprintf( pName, nChars + 1, "%s[%u]_%d", pBase, pBit->signal.bit, Index ); + return pName; +} + +static ABC_UINT64_T Sn_SignatureWord( ABC_UINT64_T Signature, ABC_UINT64_T Word ) +{ + int i; + for ( i = 0; i < 8; i++ ) + { + Signature ^= (unsigned char)(Word >> (8 * i)); + Signature *= ABC_CONST(1099511628211); + } + return Signature; +} + +static ABC_UINT64_T Sn_SignatureString( ABC_UINT64_T Signature, const char * pString ) +{ + if ( pString == NULL ) + return Sn_SignatureWord( Signature, ~(ABC_UINT64_T)0 ); + while ( *pString ) + { + Signature ^= (unsigned char)*pString++; + Signature *= ABC_CONST(1099511628211); + } + return Sn_SignatureWord( Signature, 0 ); +} + +static ABC_UINT64_T Sn_BoundaryBitSignature( ABC_UINT64_T Signature, + const sn_blast_boundary_bit_t * pBit ) +{ + Signature = Sn_SignatureWord( Signature, (uint32_t)pBit->kind ); + Signature = Sn_SignatureWord( Signature, pBit->signal.occurrence ); + Signature = Sn_SignatureWord( Signature, pBit->signal.object ); + Signature = Sn_SignatureWord( Signature, pBit->signal.bit ); + Signature = Sn_SignatureWord( Signature, pBit->owner ); + return Sn_SignatureWord( Signature, pBit->port ); +} + +// This signature covers the selected module and every table that gives meaning to a boundary owner. Endpoint order, +// kind, hierarchical signal reference, owner, port, and bit are included explicitly to avoid hashing struct padding. +static ABC_UINT64_T Sn_BoundarySignature( const Sn_Man_t * p ) +{ + ABC_UINT64_T Signature = ABC_CONST(0xcbf29ce484222325); + size_t i; + int Slot; + Signature = Sn_SignatureWord( Signature, p->BlastModule ); + Signature = Sn_SignatureString( Signature, sn_name_get(&p->pDesign->names, p->BlastName) ); + Signature = Sn_SignatureWord( Signature, (uint32_t)p->BlastMode ); + Signature = Sn_SignatureWord( Signature, p->Boundary.register_bits ); + Signature = Sn_SignatureWord( Signature, p->Boundary.occurrences.size ); + for ( i = 0; i < p->Boundary.occurrences.size; i++ ) + { + const sn_blast_occurrence_t * pEntry = &sn_vec_at(sn_blast_occurrence_t, &p->Boundary.occurrences, i); + Signature = Sn_SignatureWord( Signature, pEntry->module ); + Signature = Sn_SignatureWord( Signature, pEntry->parent_occurrence ); + Signature = Sn_SignatureWord( Signature, pEntry->parent_inst ); + } + Signature = Sn_SignatureWord( Signature, p->Boundary.primitives.size ); + for ( i = 0; i < p->Boundary.primitives.size; i++ ) + { + const sn_blast_primitive_t * pEntry = &sn_vec_at(sn_blast_primitive_t, &p->Boundary.primitives, i); + Signature = Sn_SignatureWord( Signature, pEntry->occurrence ); + Signature = Sn_SignatureWord( Signature, pEntry->inst ); + Signature = Sn_SignatureWord( Signature, pEntry->module ); + Signature = Sn_SignatureWord( Signature, pEntry->ci_begin ); + Signature = Sn_SignatureWord( Signature, pEntry->ci_count ); + Signature = Sn_SignatureWord( Signature, pEntry->co_begin ); + Signature = Sn_SignatureWord( Signature, pEntry->co_count ); + } + Signature = Sn_SignatureWord( Signature, p->Boundary.registers.size ); + for ( i = 0; i < p->Boundary.registers.size; i++ ) + { + const sn_blast_register_t * pEntry = &sn_vec_at(sn_blast_register_t, &p->Boundary.registers, i); + Signature = Sn_SignatureWord( Signature, pEntry->occurrence ); + Signature = Sn_SignatureWord( Signature, pEntry->reg_out ); + Signature = Sn_SignatureWord( Signature, pEntry->ci_begin ); + Signature = Sn_SignatureWord( Signature, pEntry->co_begin ); + Signature = Sn_SignatureWord( Signature, pEntry->width ); + for ( Slot = 0; Slot < SN_REG_FANIN_COUNT; Slot++ ) + Signature = Sn_SignatureWord( Signature, pEntry->control_co_begin[Slot] ); + } + Signature = Sn_SignatureWord( Signature, p->Boundary.loops.size ); + for ( i = 0; i < p->Boundary.loops.size; i++ ) + { + const sn_blast_loop_t * pEntry = &sn_vec_at(sn_blast_loop_t, &p->Boundary.loops, i); + Signature = Sn_SignatureWord( Signature, pEntry->occurrence ); + Signature = Sn_SignatureWord( Signature, pEntry->loop_out ); + Signature = Sn_SignatureWord( Signature, pEntry->co_begin ); + Signature = Sn_SignatureWord( Signature, pEntry->width ); + } + Signature = Sn_SignatureWord( Signature, p->Boundary.cis.size ); + for ( i = 0; i < p->Boundary.cis.size; i++ ) + Signature = Sn_BoundaryBitSignature( + Signature, &sn_vec_at(sn_blast_boundary_bit_t, &p->Boundary.cis, i) ); + Signature = Sn_SignatureWord( Signature, p->Boundary.cos.size ); + for ( i = 0; i < p->Boundary.cos.size; i++ ) + Signature = Sn_BoundaryBitSignature( + Signature, &sn_vec_at(sn_blast_boundary_bit_t, &p->Boundary.cos, i) ); + return Signature; +} + +// Boundary names contain the retained signal name, bit index, and a unique interface index. ABC's normal GIA +// synthesis commands preserve these names. Hashing their ordered vectors detects interface permutations and also +// rejects a command that discarded the identity needed to prove that the boundary order is unchanged. +static ABC_UINT64_T Sn_GiaInterfaceSignature( Gia_Man_t * pGia ) +{ + ABC_UINT64_T Signature = ABC_CONST(0xcbf29ce484222325); + char * pName; + int i; + Signature = Sn_SignatureWord( Signature, Gia_ManCiNum(pGia) ); + Signature = Sn_SignatureWord( Signature, Gia_ManCoNum(pGia) ); + Signature = Sn_SignatureWord( Signature, pGia->vNamesIn ? Vec_PtrSize(pGia->vNamesIn) : ~(ABC_UINT64_T)0 ); + if ( pGia->vNamesIn ) + Vec_PtrForEachEntry( char *, pGia->vNamesIn, pName, i ) + Signature = Sn_SignatureString( Signature, pName ); + Signature = Sn_SignatureWord( Signature, pGia->vNamesOut ? Vec_PtrSize(pGia->vNamesOut) : ~(ABC_UINT64_T)0 ); + if ( pGia->vNamesOut ) + Vec_PtrForEachEntry( char *, pGia->vNamesOut, pName, i ) + Signature = Sn_SignatureString( Signature, pName ); + return Signature; +} + +static void Sn_GiaSetNames( Abc_Frame_t * pAbc, const Sn_Man_t * p ) +{ + Gia_Man_t * pGia = Abc_FrameReadGia( pAbc ); + size_t i; + assert( pGia && (size_t)Gia_ManCiNum(pGia) == p->Boundary.cis.size ); + assert( (size_t)Gia_ManCoNum(pGia) == p->Boundary.cos.size ); + if ( pGia->vNamesIn ) + Vec_PtrFreeFree( pGia->vNamesIn ); + if ( pGia->vNamesOut ) + Vec_PtrFreeFree( pGia->vNamesOut ); + pGia->vNamesIn = Vec_PtrAlloc( Gia_ManCiNum(pGia) ); + pGia->vNamesOut = Vec_PtrAlloc( Gia_ManCoNum(pGia) ); + for ( i = 0; i < p->Boundary.cis.size; i++ ) + Vec_PtrPush( pGia->vNamesIn, + Sn_BoundaryName(p, &sn_vec_at(sn_blast_boundary_bit_t, &p->Boundary.cis, i), "pi", (int)i) ); + for ( i = 0; i < p->Boundary.cos.size; i++ ) + Vec_PtrPush( pGia->vNamesOut, + Sn_BoundaryName(p, &sn_vec_at(sn_blast_boundary_bit_t, &p->Boundary.cos, i), "po", (int)i) ); + ABC_FREE( pGia->pName ); + pGia->pName = Abc_UtilStrsav( + (char *)sn_name_get(&p->pDesign->names, sn_design_get_module_const(p->pDesign, p->BlastModule)->name) ); +} + +static int Sn_CommandBlast( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Sn_Man_t * p; + Mini_Aig_t * pAig; + abctime clkBlast, clkImport, clkNames; + int nMiniAnds; + sn_blast_options_t Options = sn_blast_default_options(); + sn_blast_hier_stats_t Stats = {0}; + char * pModuleName = NULL; + sn_module_id_t BlastModule; + int c, fVerbose = 0; + Options.mode = SN_BLAST_SEQ; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "Mctdbrvh")) != EOF ) + { + switch ( c ) + { + case 'M': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-M\" should be followed by a module name.\n" ); + goto usage; + } + pModuleName = argv[globalUtilOptind++]; + break; + case 'c': + Options.mode = SN_BLAST_COMB; + break; + case 't': + Options.mode = SN_BLAST_TRANSITION; + break; + case 'b': + Options.mul_mode = Options.mul_mode == SN_BLAST_MUL_BOOTH ? SN_BLAST_MUL_BAUGH_WOOLEY : + SN_BLAST_MUL_BOOTH; + break; + case 'd': + Options.delay_comparators ^= 1; + break; + case 'r': + Options.ripple_adders ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + default: + goto usage; + } + } + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + p = Sn_AbcGetMan( pAbc ); + BlastModule = pModuleName ? sn_design_find_module( p->pDesign, pModuleName ) : p->Top; + if ( BlastModule == SN_INVALID_ID ) + { + Abc_Print( -1, "Cannot find module \"%s\" in the current SN design.\n", pModuleName ); + return 1; + } + if ( Sn_CommandRejectLatches(p->pDesign, BlastModule, "@blast") ) + return 1; + sn_blast_boundary_destroy( &p->Boundary ); + sn_blast_boundary_init( &p->Boundary ); + clkBlast = Abc_Clock(); + pAig = sn_design_blast_hier_boundary_options( p->pDesign, BlastModule, Options, &Stats, &p->Boundary ); + clkBlast = Abc_Clock() - clkBlast; + nMiniAnds = Mini_AigAndNum( pAig ); + clkImport = Abc_Clock(); + Abc_FrameGiaInputMiniAig( pAbc, pAig ); + clkImport = Abc_Clock() - clkImport; + Mini_AigStop( pAig ); + p->BlastModule = BlastModule; + p->BlastName = sn_design_get_module_const( p->pDesign, BlastModule )->name; + p->fBlasted = 1; + p->BlastMode = Options.mode; + p->fLastBlast = 1; + p->LastBlastModule = p->BlastModule; + p->LastBlastName = p->BlastName; + p->LastBlastRevision = p->Revision; + p->BlastBoundarySignature = Sn_BoundarySignature( p ); + clkNames = Abc_Clock(); + Sn_GiaSetNames( pAbc, p ); + p->BlastInterfaceSignature = Sn_GiaInterfaceSignature( Abc_FrameReadGia(pAbc) ); + clkNames = Abc_Clock() - clkNames; + if ( fVerbose ) + { + Abc_Print( 1, "Blasted SN design: PI bits = %llu PO bits = %llu flop bits = %llu " + "memories = %llu multipliers = %llu.\n", + (unsigned long long)Stats.primary_input_bits, (unsigned long long)Stats.primary_output_bits, + (unsigned long long)Stats.flop_bits, (unsigned long long)Stats.memory_count, + (unsigned long long)Stats.multiplier_count ); + Abc_Print( 1, "@blast phases: MiniAIG = %.2f s (%d ANDs) GIA import = %.2f s (%d ANDs) names = %.2f s.\n", + (double)clkBlast / CLOCKS_PER_SEC, nMiniAnds, (double)clkImport / CLOCKS_PER_SEC, + Gia_ManAndNum(Abc_FrameReadGia(pAbc)), (double)clkNames / CLOCKS_PER_SEC ); + } + return 0; + +usage: + Abc_Print( -2, "usage: @blast [-M module] [-ctdbrvh]\n" ); + Abc_Print( -2, "\t derives a flat AIG directly from the hierarchical SN design\n" ); + Abc_Print( -2, "\t-M name : select the module replaced by a later @put [default = current top]\n" ); + Abc_Print( -2, "\t-c : use combinational AIG mode\n" ); + Abc_Print( -2, "\t-t : emit the sequential transition relation as a combinational AIG\n" ); + Abc_Print( -2, "\t-b : toggle Booth multiplier blasting [default = Baugh-Wooley]\n" ); + Abc_Print( -2, "\t-d : toggle delay-oriented comparator blasting [default = enabled]\n" ); + Abc_Print( -2, "\t-r : toggle ripple-carry adders [default = Brent-Kung]\n" ); + Abc_Print( -2, "\t-v : print bit-blasting statistics\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static uint32_t Sn_GateIdResolver( void * pContext, const char * pGateName ) +{ + Mio_Library_t * pLibrary = (Mio_Library_t *)pContext; + Mio_Gate_t * pGate = Mio_LibraryReadGateByName( pLibrary, (char *)pGateName, NULL ); + int GateId; + if ( pGate == NULL ) + return SN_INVALID_ID; + GateId = Mio_GateReadCell( pGate ); + return GateId >= 0 ? (uint32_t)GateId : SN_INVALID_ID; +} + +static int Sn_BoundaryHasGenericMemories( const sn_blast_boundary_t * pBoundary ) +{ + size_t i; + for ( i = 0; i < pBoundary->cis.size; i++ ) + { + sn_blast_boundary_kind_t Kind = sn_vec_at(sn_blast_boundary_bit_t, &pBoundary->cis, i).kind; + if ( Kind == SN_BLAST_BOUNDARY_MEMORY_OUTPUT ) + return 1; + } + for ( i = 0; i < pBoundary->cos.size; i++ ) + { + sn_blast_boundary_kind_t Kind = sn_vec_at(sn_blast_boundary_bit_t, &pBoundary->cos, i).kind; + if ( Kind == SN_BLAST_BOUNDARY_MEMORY_INPUT ) + return 1; + } + return 0; +} + +static int Sn_CommandPut( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + extern Abc_Ntk_t * Abc_NtkFromCellMappedGia( Gia_Man_t * pGia, int fUseBuffs ); + extern Vec_Int_t * Abc_NtkWriteMiniMapping( Abc_Ntk_t * pNtk ); + Sn_Man_t * p; + Gia_Man_t * pGia; + sn_module_id_t Top; + const char * pPutStatus; + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "vh")) != EOF ) + { + switch ( c ) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + default: + goto usage; + } + } + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + p = Sn_AbcGetMan( pAbc ); + pGia = Abc_FrameReadGia( pAbc ); + pPutStatus = Sn_ManPutStatus( p, pGia ); + if ( strcmp(pPutStatus, "compatible") != 0 ) + { + Abc_Print( -1, "Cannot @put: %s.\n", pPutStatus ); + return 1; + } + + if ( Gia_ManHasCellMapping(pGia) ) + { + Mio_Library_t * pLibrary = (Mio_Library_t *)Abc_FrameReadLibGen(); + Abc_Ntk_t * pNtk; + Vec_Int_t * vMapping; + if ( pLibrary == NULL ) + { + Abc_Print( -1, "The cell-mapped GIA has no current genlib library.\n" ); + return 1; + } + pNtk = Abc_NtkFromCellMappedGia( pGia, 0 ); + vMapping = Abc_NtkWriteMiniMapping( pNtk ); + Top = sn_design_add_gate_module( p->pDesign, p->BlastModule, Vec_IntArray(vMapping), + (size_t)Vec_IntSize(vMapping), &p->Boundary, Sn_GateIdResolver, + pLibrary, "__sn_gate_mapped" ); + if ( Top == SN_INVALID_ID ) + { + Abc_Print( -1, "Cannot @put: the current genlib does not contain every gate used by the mapped GIA.\n" ); + Vec_IntFree( vMapping ); + Abc_NtkDelete( pNtk ); + return 1; + } + if ( fVerbose ) + Abc_Print( 1, "Inserted cell-mapped logic: gates = %d.\n", Vec_IntEntry(vMapping, 2) ); + Vec_IntFree( vMapping ); + Abc_NtkDelete( pNtk ); + } + else if ( Gia_ManHasMapping(pGia) ) + { + Mini_Lut_t * pLut = (Mini_Lut_t *)Abc_FrameGiaOutputMiniLut( pAbc ); + sn_lut_stats_t Stats; + if ( pLut == NULL ) + { + Abc_Print( -1, "Cannot extract the mapped MiniLUT network.\n" ); + return 1; + } + Stats = sn_lut_analyze( pLut, &p->Boundary ); + Top = sn_design_add_lut_module( p->pDesign, p->BlastModule, pLut, &p->Boundary, "__sn_lut_mapped" ); + Mini_LutStop( pLut ); + if ( fVerbose ) + Abc_Print( 1, "Inserted LUT-mapped logic: LUTs = %u LUT size = %u levels = %u.\n", + Stats.lut_count, Stats.lut_size, Stats.lut_levels ); + } + else + { + Mini_Aig_t * pAig = (Mini_Aig_t *)Abc_FrameGiaOutputMiniAig( pAbc ); + assert( pAig != NULL ); + Top = sn_design_add_aig_module( p->pDesign, p->BlastModule, pAig, &p->Boundary, "__sn_aig_inserted" ); + if ( fVerbose ) + Abc_Print( 1, "Inserted unmapped logic: ANDs = %d.\n", Mini_AigAndNum(pAig) ); + Mini_AigStop( pAig ); + } + Sn_ManReplaceBlastedModule( p, Top ); + Sn_ManAdvanceRevision( p ); + assert( sn_design_is_topo(p->pDesign) ); + return 0; + +usage: + Abc_Print( -2, "usage: @put [-vh]\n" ); + Abc_Print( -2, "\t inserts the current &-space GIA into the SN design after @blast\n" ); + Abc_Print( -2, "\t-v : print reconstruction statistics\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static char * Sn_SourceCommand( const char * pFileName ) +{ + Vec_Str_t * vCommand; + FILE * pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + return NULL; + if ( fclose(pFile) != 0 ) + return NULL; + vCommand = Vec_StrAlloc( (int)strlen(pFileName) + 16 ); + Vec_StrPrintStr( vCommand, "source -s " ); + Vec_StrPrintStr( vCommand, pFileName ); + Vec_StrPush( vCommand, '\0' ); + return Vec_StrReleaseArray( vCommand ); +} + +typedef struct Sn_MapLutContext_t_ +{ + Abc_Frame_t * pAbc; + const char * pScript; + const char * pExecutable; + const char * pExtractPrefix; + unsigned nProcesses; + int fVerbose; +} Sn_MapLutContext_t; + +static int Sn_MapLutExtractName( char * pFileName, size_t nFileName, const char * pPrefix, + unsigned Module, const char * pName, const char * pSuffix ) +{ + size_t Pos, NameLength, SuffixLength; + int Written = snprintf( pFileName, nFileName, "%s_%04u_", pPrefix, Module ); + if ( Written < 0 || (size_t)Written >= nFileName ) + return 0; + Pos = (size_t)Written; + NameLength = strlen( pName ); + SuffixLength = strlen( pSuffix ); + if ( NameLength > nFileName - Pos - 1 || SuffixLength > nFileName - Pos - NameLength - 1 ) + return 0; + while ( *pName ) + { + unsigned char Char = (unsigned char)*pName++; + pFileName[Pos++] = (char)(isalnum(Char) || Char == '_' || Char == '-' ? Char : '_'); + } + memcpy( pFileName + Pos, pSuffix, SuffixLength + 1 ); + return 1; +} + +static int Sn_MapLutExecutable( char * pBuffer, size_t nBuffer ) +{ +#if defined(_MSC_VER) || defined(__MINGW32__) + DWORD Length = GetModuleFileNameA( NULL, pBuffer, (DWORD)nBuffer ); + return Length > 0 && Length < nBuffer; +#else + ssize_t Length = readlink( "/proc/self/exe", pBuffer, nBuffer - 1 ); + if ( Length <= 0 || (size_t)Length >= nBuffer ) + return 0; + pBuffer[Length] = '\0'; + return 1; +#endif +} + +static int Sn_MapLutRunProcess( const char * pExecutable, const char * pCommand ) +{ +#if defined(_MSC_VER) || defined(__MINGW32__) + const char * pArgs[] = {pExecutable, "-q", pCommand, NULL}; + return (int)_spawnv( _P_WAIT, pExecutable, pArgs ); +#else + pid_t Child = fork(); + int Status; + if ( Child < 0 ) + return -1; + if ( Child == 0 ) + { + int Null = open( "/dev/null", O_WRONLY ); + if ( Null >= 0 ) + { + dup2( Null, STDOUT_FILENO ); + dup2( Null, STDERR_FILENO ); + close( Null ); + } + execl( pExecutable, pExecutable, "-q", pCommand, (char *)NULL ); + _exit( 127 ); + } + if ( waitpid(Child, &Status, 0) != Child ) + return -1; + return WIFEXITED(Status) ? WEXITSTATUS(Status) : -1; +#endif +} + +// ABC-specific callback for the reusable snMapLut.h harness. The harness owns the input MiniAIG and returned MiniLUT. +static Mini_Lut_t * Sn_MapLutPartition( void * pArg, sn_module_id_t Module, const char * pName, + Mini_Aig_t * pAig, const sn_blast_boundary_t * pBoundary ) +{ + Sn_MapLutContext_t * p = (Sn_MapLutContext_t *)pArg; + Gia_Man_t * pGia; + Mini_Lut_t * pLut; + sn_lut_stats_t Stats; + abctime clk = Abc_Clock(); + int nInputs = Mini_AigPiNum( pAig ); + int nOutputs = Mini_AigPoNum( pAig ); + int nAnds = Mini_AigAndNum( pAig ); + if ( p->pExtractPrefix ) + { + char AigFile[1024], InfoFile[1024]; + FILE * pFile; + if ( !Sn_MapLutExtractName(AigFile, sizeof(AigFile), p->pExtractPrefix, Module, pName, ".aig") || + !Sn_MapLutExtractName(InfoFile, sizeof(InfoFile), p->pExtractPrefix, Module, pName, ".txt") ) + { + Abc_Print( -1, "Cannot extract module \"%s\": the -E output name is too long.\n", pName ); + return NULL; + } + pFile = fopen( AigFile, "wb" ); + if ( pFile == NULL ) + return NULL; + fclose( pFile ); + Mini_AigerWrite( AigFile, pAig, 0 ); + pFile = fopen( InfoFile, "w" ); + if ( pFile == NULL ) + return NULL; + fprintf( pFile, "module_id\t%u\nmodule_name\t%s\ninputs\t%d\noutputs\t%d\nands\t%d\n", + Module, pName, nInputs, nOutputs, nAnds ); + fprintf( pFile, "boundary_inputs\t%zu\nboundary_outputs\t%zu\n", + pBoundary->cis.size, pBoundary->cos.size ); + fclose( pFile ); + if ( p->fVerbose ) + Abc_Print( 1, "@map_lut: extracted %-24s %7d ANDs %d inputs %d outputs time = %.2f s.\n", + pName, nAnds, nInputs, nOutputs, (double)(Abc_Clock() - clk) / CLOCKS_PER_SEC ); + return Mini_LutStart( 2 ); + } + if ( p->nProcesses > 1 ) + { + char Prefix[512], Command[4096]; + char * pAigFile = NULL, * pLutFile = NULL; + int AigFd = -1, LutFd = -1; + if ( !Sn_TempPrefix(Prefix, sizeof(Prefix), "sn_map_lut_") || + (AigFd = tmpFile(Prefix, ".aig", &pAigFile)) < 0 || + (LutFd = tmpFile(Prefix, ".lut", &pLutFile)) < 0 ) + { +#if defined(_MSC_VER) || defined(__MINGW32__) + if ( AigFd >= 0 ) + _close( AigFd ); +#else + if ( AigFd >= 0 ) + close( AigFd ); +#endif + if ( pAigFile ) + { + remove( pAigFile ); + ABC_FREE( pAigFile ); + } + return NULL; + } +#if defined(_MSC_VER) || defined(__MINGW32__) + _close( AigFd ); + _close( LutFd ); +#else + close( AigFd ); + close( LutFd ); +#endif + Mini_AigerWrite( pAigFile, pAig, 0 ); + if ( snprintf(Command, sizeof(Command), "&read \"%s\"; %s; &write -l \"%s\"", pAigFile, p->pScript, + pLutFile) >= + (int)sizeof(Command) || Sn_MapLutRunProcess(p->pExecutable, Command) != 0 ) + { + remove( pAigFile ); + remove( pLutFile ); + ABC_FREE( pAigFile ); + ABC_FREE( pLutFile ); + return NULL; + } + pLut = sn_lut_load( pLutFile ); + remove( pAigFile ); + remove( pLutFile ); + ABC_FREE( pAigFile ); + ABC_FREE( pLutFile ); + if ( pLut == NULL || !sn_lut_interface_matches(pLut, pBoundary) ) + { + if ( pLut ) + Mini_LutStop( pLut ); + return NULL; + } + Stats = sn_lut_analyze( pLut, pBoundary ); + if ( p->fVerbose ) + Abc_Print( 1, "@map_lut: %-24s %7d ANDs -> %7u LUTs level = %u time = %.2f s.\n", + pName, nAnds, Stats.lut_count, Stats.lut_levels, + (double)(Abc_Clock() - clk) / CLOCKS_PER_SEC ); + return pLut; + } + Abc_FrameGiaInputMiniAig( p->pAbc, pAig ); + if ( Cmd_CommandExecute(p->pAbc, p->pScript) ) + { + Abc_Print( -1, "ABC script failed while mapping module \"%s\".\n", pName ); + return NULL; + } + pGia = Abc_FrameReadGia( p->pAbc ); + if ( pGia == NULL || Gia_ManCiNum(pGia) != nInputs || Gia_ManCoNum(pGia) != nOutputs ) + { + Abc_Print( -1, "ABC script changed the interface of module \"%s\" (%d/%d inputs, %d/%d outputs).\n", + pName, pGia ? Gia_ManCiNum(pGia) : -1, nInputs, pGia ? Gia_ManCoNum(pGia) : -1, nOutputs ); + return NULL; + } + if ( !Gia_ManHasMapping(pGia) ) + { + Abc_Print( -1, "ABC script did not leave a LUT-mapped GIA while mapping module \"%s\".\n", pName ); + return NULL; + } + pLut = (Mini_Lut_t *)Abc_FrameGiaOutputMiniLut( p->pAbc ); + if ( pLut == NULL ) + { + Abc_Print( -1, "Cannot extract the LUT-mapped network for module \"%s\".\n", pName ); + return NULL; + } + Stats = sn_lut_analyze( pLut, pBoundary ); + if ( p->fVerbose ) + Abc_Print( 1, "@map_lut: %-24s %7d ANDs -> %7u LUTs level = %u time = %.2f s.\n", + pName, nAnds, Stats.lut_count, Stats.lut_levels, (double)(Abc_Clock() - clk) / CLOCKS_PER_SEC ); + return pLut; +} + +// Like Yosys's ABC integration, @map_lut uses natural module boundaries rather than graph partitioning. Each reachable +// non-primitive module is combinationally extracted, processed independently by the requested ABC script, and inserted +// back at the same module ID. The command works on a duplicate and commits it only after every partition succeeds. +static int Sn_CommandMapLut( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + static const char * pDefaultScript = "&resyn3; &if -m -K 6"; + Sn_Man_t * p, * pWork; + const char * pScriptArg = NULL; + const char * pScriptFile = NULL; + const char * pModuleName = NULL; + const char * pExtractPrefix = NULL; + char * pFileScript = NULL; + const char * pScript; + char Executable[1024]; + sn_module_id_t Root; + sn_map_lut_stats_t Stats; + Sn_MapLutContext_t Context; + abctime clk; + int c, fVerbose = 0, nProcesses = 1; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "MSFPEvh")) != EOF ) + { + switch ( c ) + { + case 'M': + if ( globalUtilOptind >= argc ) + goto usage; + pModuleName = argv[globalUtilOptind++]; + break; + case 'S': + if ( globalUtilOptind >= argc ) + goto usage; + pScriptArg = argv[globalUtilOptind++]; + break; + case 'F': + if ( globalUtilOptind >= argc ) + goto usage; + pScriptFile = argv[globalUtilOptind++]; + break; + case 'P': + if ( globalUtilOptind >= argc ) + goto usage; + nProcesses = atoi( argv[globalUtilOptind++] ); + if ( nProcesses < 1 || nProcesses > 100 ) + goto usage; + break; + case 'E': + if ( globalUtilOptind >= argc ) + goto usage; + pExtractPrefix = argv[globalUtilOptind++]; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + default: + goto usage; + } + } + if ( argc != globalUtilOptind || (pScriptArg && pScriptFile) || + (pExtractPrefix && (pScriptArg || pScriptFile || nProcesses != 1)) ) + goto usage; + if ( nProcesses > 1 && !sn_pth_parallel_available() ) + { + Abc_Print( -1, "Cannot use @map_lut -P %d: parallel SN mapping is unavailable in this build; use -P 1.\n", + nProcesses ); + return 1; + } + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + p = Sn_AbcGetMan( pAbc ); + Root = pModuleName ? sn_design_find_module( p->pDesign, pModuleName ) : p->Top; + if ( Root == SN_INVALID_ID ) + { + Abc_Print( -1, "Cannot find module \"%s\" in the current SN design.\n", pModuleName ); + return 1; + } + if ( Sn_CommandRejectLatches(p->pDesign, Root, "@map_lut") ) + return 1; + if ( pScriptFile ) + { + pFileScript = Sn_SourceCommand( pScriptFile ); + if ( pFileScript == NULL ) + { + Abc_Print( -1, "Cannot read ABC script file \"%s\".\n", pScriptFile ); + return 1; + } + } + pScript = pScriptArg ? pScriptArg : pFileScript ? pFileScript : pDefaultScript; + pWork = Sn_ManDup( p ); + if ( pWork == NULL ) + { + Abc_Print( -1, "Cannot duplicate the current SN design.\n" ); + ABC_FREE( pFileScript ); + return 1; + } + Context.pAbc = pAbc; + Context.pScript = pScript; + Context.pExecutable = NULL; + Context.pExtractPrefix = pExtractPrefix; + Context.nProcesses = (unsigned)nProcesses; + Context.fVerbose = fVerbose; + if ( nProcesses > 1 ) + { + if ( !Sn_MapLutExecutable(Executable, sizeof(Executable)) ) + { + Abc_Print( -1, "Cannot determine the current ABC executable path.\n" ); + ABC_FREE( pFileScript ); + Sn_ManFree( pWork ); + return 1; + } + Context.pExecutable = Executable; + } + clk = Abc_Clock(); + if ( !sn_design_map_lut_hierarchy(pWork->pDesign, Root, Sn_MapLutPartition, &Context, + (unsigned)nProcesses, pExtractPrefix != NULL, &Stats) ) + { + if ( Stats.failed_module != SN_INVALID_ID ) + { + const sn_module_t * pFailed = sn_design_get_module_const( pWork->pDesign, Stats.failed_module ); + Abc_Print( -1, "LUT mapping failed for module \"%s\".\n", + sn_name_get(&pWork->pDesign->names, pFailed->name) ); + } + ABC_FREE( pFileScript ); + Sn_ManFree( pWork ); + return 1; + } + ABC_FREE( pFileScript ); + if ( pExtractPrefix ) + { + Sn_ManFree( pWork ); + Abc_Print( 1, "Extracted %u module partitions (%llu input ANDs); skipped %u trivial, %u primitive, and %u " + "generic-memory modules. Time = %.2f s.\n", Stats.mapped_modules, + (unsigned long long)Stats.input_ands, Stats.trivial_modules, Stats.primitive_modules, + Stats.generic_memory_modules, (double)(Abc_Clock() - clk) / CLOCKS_PER_SEC ); + return 0; + } + Sn_ManAdvanceRevision( pWork ); + Sn_AbcUpdateMan( pAbc, pWork ); + Abc_Print( 1, "Mapped %u module partitions into %llu LUTs; skipped %u trivial, %u primitive, and %u generic-memory " + "modules. " + "Time = %.2f s.\n", Stats.mapped_modules, (unsigned long long)Stats.output_luts, + Stats.trivial_modules, Stats.primitive_modules, Stats.generic_memory_modules, + (double)(Abc_Clock() - clk) / CLOCKS_PER_SEC ); + return 0; + +usage: + Abc_Print( -2, "usage: @map_lut [-M module] [-S \"commands\" | -F script] [-P num] [-E prefix] [-vh]\n" ); + Abc_Print( -2, "\t maps each natural hierarchy partition independently and preserves the hierarchy\n" ); + Abc_Print( -2, "\t-M name : map modules reachable from this root [default = current top]\n" ); + Abc_Print( -2, "\t-S cmds : ABC commands applied to each partition [default = &resyn3; &if -m -K 6]\n" ); + Abc_Print( -2, "\t-F file : read the per-partition ABC commands from a file\n" ); + Abc_Print( -2, "\t-P num : use num processes; P>1 requires pthreads and a non-Windows build [default = 1]\n" ); + Abc_Print( -2, "\t P=1 leaves the last processed partition in &-space\n" ); + Abc_Print( -2, "\t-E pref : extract partition AIGs as pref__.aig and stop before synthesis\n" ); + Abc_Print( -2, "\t-v : print per-module mapping statistics\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static int Sn_CommandWrite( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Sn_Man_t * p; + char * pFileName; + FILE * pFile; + int c, Status = 0; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "h")) != EOF ) + goto usage; + if ( argc - globalUtilOptind != 1 ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + p = Sn_AbcGetMan( pAbc ); + pFileName = argv[globalUtilOptind]; + if ( Sn_FileHasSuffix(pFileName, ".sn") ) + { + pFile = fopen( pFileName, "wb" ); + if ( pFile == NULL ) + { + Abc_Print( -1, "Cannot open output file \"%s\".\n", pFileName ); + return 1; + } + Status = sn_design_write_binary( pFile, p->pDesign ) ? 0 : 1; + } + else if ( Sn_FileHasSuffix(pFileName, ".v") || Sn_FileHasSuffix(pFileName, ".sv") ) + { + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + Abc_Print( -1, "Cannot open output file \"%s\".\n", pFileName ); + return 1; + } + sn_design_write_module_verilog_with_deps( + pFile, p->pDesign, p->Top, sn_name_get(&p->pDesign->names, p->Name) ); + Status = ferror( pFile ) != 0; + } + else + { + Abc_Print( -1, "Output file \"%s\" should have extension .sn, .v, or .sv.\n", pFileName ); + return 1; + } + if ( fclose(pFile) != 0 ) + Status = 1; + if ( Status ) + { + remove( pFileName ); + Abc_Print( -1, "Cannot finish writing output file \"%s\".\n", pFileName ); + return 1; + } + return 0; + +usage: + Abc_Print( -2, "usage: @write [-h] \n" ); + Abc_Print( -2, "\t writes the current SN design according to the file extension\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static int Sn_CommandPs( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Sn_Man_t * p; + const sn_module_t * pReport; + sn_design_mem_usage_t Mem; + char * pModuleName = NULL; + sn_module_id_t Report; + size_t nObjects = 0; + size_t i; + int c, fDistrib = 0, fVerbose = 0, fMem, fLut, fGate; + char UsedMemory[32], AllocatedMemory[32]; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "Mdvh")) != EOF ) + { + switch ( c ) + { + case 'M': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-M\" should be followed by a module name.\n" ); + goto usage; + } + pModuleName = argv[globalUtilOptind++]; + break; + case 'd': + fDistrib ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + default: + goto usage; + } + } + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + p = Sn_AbcGetMan( pAbc ); + Report = pModuleName ? sn_design_find_module( p->pDesign, pModuleName ) : p->Top; + if ( Report == SN_INVALID_ID ) + { + Abc_Print( -1, "Cannot find module \"%s\" in the current SN design.\n", pModuleName ); + return 1; + } + pReport = sn_design_get_module_const( p->pDesign, Report ); + for ( i = 0; i < p->pDesign->modules.size; i++ ) + nObjects += sn_design_get_module_const( p->pDesign, (sn_module_id_t)i )->obj_types.size; + sn_design_get_mem_usage( p->pDesign, &Mem ); + fMem = Sn_DesignHasType( p->pDesign, SN_MEM_OUT ); + fLut = Sn_DesignHasType( p->pDesign, SN_LUT ); + fGate = Sn_DesignHasType( p->pDesign, SN_GATE ); + Sn_FormatMemory( Mem.total.used_bytes, UsedMemory, sizeof(UsedMemory) ); + Sn_FormatMemory( Mem.total.allocated_bytes, AllocatedMemory, sizeof(AllocatedMemory) ); + fprintf( pAbc->Out, "SN design: top = %s modules = %zu objects = %zu memory = %s/%s " + "(used/allocated)\n", + sn_name_get(&p->pDesign->names, pReport->name), p->pDesign->modules.size, nObjects, + UsedMemory, AllocatedMemory ); + if ( pModuleName ) + Sn_ModulePrintStats( pAbc->Out, pReport, fMem, fLut, fGate ); + else + { + fprintf( pAbc->Out, "Modules:\n" ); + for ( i = 0; i < p->pDesign->modules.size; i++ ) + Sn_ModulePrintStats( pAbc->Out, sn_design_get_module_const(p->pDesign, (sn_module_id_t)i), + fMem, fLut, fGate ); + } + if ( fVerbose ) + { + fprintf( pAbc->Out, "Hierarchy:\n" ); + sn_design_print_hierarchy( pAbc->Out, p->pDesign, Report ); + } + if ( fDistrib ) + { + Sn_DesignPrintBlackboxes( pAbc->Out, p->pDesign, Report ); + Sn_DesignPrintDistrib( pAbc->Out, p->pDesign, Report ); + } + return 0; + +usage: + Abc_Print( -2, "usage: @ps [-M module] [-dvh]\n" ); + Abc_Print( -2, "\t prints statistics for the current SN design\n" ); + Abc_Print( -2, "\t-M name : select one module and its hierarchy [default = print all module definitions]\n" ); + Abc_Print( -2, "\t-d : print object-type and width distribution for the elaborated hierarchy\n" ); + Abc_Print( -2, "\t-v : print the hierarchy rooted at the selected module\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +static const char * Sn_BlastModeName( int Mode ) +{ + if ( Mode == SN_BLAST_COMB ) + return "combinational"; + if ( Mode == SN_BLAST_TRANSITION ) + return "transition"; + return "sequential"; +} + +static int Sn_ManHasUserHierarchy( const Sn_Man_t * p ) +{ + const sn_module_t * pTop = sn_design_get_module_const( p->pDesign, p->Top ); + size_t i; + for ( i = 0; i < pTop->type_objects[SN_INST].size; i++ ) + { + sn_obj_id_t Inst = sn_vec_at( sn_obj_id_t, &pTop->type_objects[SN_INST], i ); + const sn_module_t * pChild = sn_design_get_module_const( p->pDesign, sn_inst_module_id(pTop, Inst) ); + if ( !sn_module_is_technology_primitive(pChild) ) + return 1; + } + return 0; +} + +static const char * Sn_ManPutStatus( const Sn_Man_t * p, Gia_Man_t * pGia ) +{ + const sn_module_t * pModule; + if ( !p->fLastBlast ) + return "unavailable (run @blast -c)"; + if ( p->LastBlastRevision != p->Revision ) + return "incompatible (SN revision changed)"; + if ( p->BlastMode != SN_BLAST_COMB ) + return "unsupported (last extraction was not combinational)"; + if ( !p->fBlasted ) + return "incompatible (saved boundary is unavailable)"; + if ( p->BlastModule != p->LastBlastModule || p->BlastName != p->LastBlastName || + p->BlastModule >= p->pDesign->modules.size ) + return "incompatible (extracted module identity changed)"; + pModule = sn_design_get_module_const( p->pDesign, p->BlastModule ); + if ( pModule->name != p->BlastName ) + return "incompatible (extracted module name changed)"; + if ( Sn_BoundarySignature(p) != p->BlastBoundarySignature ) + return "incompatible (saved boundary changed)"; + if ( pGia == NULL ) + return "incompatible (&-space GIA is unavailable)"; + if ( Gia_ManRegNum(pGia) != 0 ) + return "incompatible (GIA contains registers)"; + if ( (size_t)Gia_ManCiNum(pGia) != p->Boundary.cis.size || + (size_t)Gia_ManCoNum(pGia) != p->Boundary.cos.size ) + return "incompatible (GIA interface changed)"; + if ( Sn_GiaInterfaceSignature(pGia) != p->BlastInterfaceSignature ) + return "incompatible (GIA interface reordered or renamed)"; + if ( Sn_BoundaryHasGenericMemories(&p->Boundary) ) + return "unsupported (generic memory boundary)"; + return "compatible"; +} + +static int Sn_CommandStatus( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Sn_Man_t * p; + Gia_Man_t * pGia; + const char * pTopName; + int c; + Extra_UtilGetoptReset(); + while ( (c = Extra_UtilGetopt(argc, argv, "h")) != EOF ) + goto usage; + if ( argc != globalUtilOptind ) + goto usage; + if ( !Sn_CommandCheckDesign(pAbc) ) + return 1; + p = Sn_AbcGetMan( pAbc ); + pGia = Abc_FrameReadGia( pAbc ); + pTopName = sn_name_get( &p->pDesign->names, sn_design_get_module_const(p->pDesign, p->Top)->name ); + fprintf( pAbc->Out, "SN design : %s\n", sn_name_get(&p->pDesign->names, p->Name) ); + fprintf( pAbc->Out, "SN revision : %llu\n", p->Revision ); + fprintf( pAbc->Out, "Top module : %s\n", pTopName ); + fprintf( pAbc->Out, "Technology : %s\n", p->Technology == SN_COMMAND_TECH_XILINX_ULTRASCALE ? + "xilinx-ultrascale+" : "generic" ); + fprintf( pAbc->Out, "Hierarchy : %s\n", Sn_ManHasUserHierarchy(p) ? "hierarchical" : "flat" ); + if ( p->fLastBlast ) + { + fprintf( pAbc->Out, "Last extraction : %s, module %s, revision %llu\n", + Sn_BlastModeName(p->BlastMode), sn_name_get(&p->pDesign->names, p->LastBlastName), + p->LastBlastRevision ); + fprintf( pAbc->Out, "Boundary hash : 0x%016llx\n", + (unsigned long long)p->BlastBoundarySignature ); + } + else + fprintf( pAbc->Out, "Last extraction : none\n" ); + if ( pGia ) + fprintf( pAbc->Out, "&-space GIA : %d inputs, %d outputs, %d flops, %d ANDs\n", + Gia_ManPiNum(pGia), Gia_ManPoNum(pGia), Gia_ManRegNum(pGia), Gia_ManAndNum(pGia) ); + else + fprintf( pAbc->Out, "&-space GIA : none\n" ); + fprintf( pAbc->Out, "@put status : %s\n", Sn_ManPutStatus(p, pGia) ); + return 0; + +usage: + Abc_Print( -2, "usage: @status [-h]\n" ); + Abc_Print( -2, "\t prints SN, saved @blast boundary, and current &-space GIA state\n" ); + Abc_Print( -2, "\t-h : print the command usage\n" ); + return 1; +} + +ABC_NAMESPACE_IMPL_END diff --git a/src/base/sn/snMapAdd.h b/src/base/sn/snMapAdd.h new file mode 100644 index 000000000..26e7f831d --- /dev/null +++ b/src/base/sn/snMapAdd.h @@ -0,0 +1,167 @@ +/**CFile**************************************************************** + + FileName [snMapAdd.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Mapping word-level adders and subtractors into FPGA carry primitives.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snMapAdd.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_MAP_ADD_H +#define SN_MAP_ADD_H + +// Maps word-level addition and subtraction into preserved FPGA carry-chain +// primitive insts. The surrounding propagate/invert logic remains ordinary SN +// logic and can subsequently be mapped into LUTs. + +#include "sn.h" +#include "snTech.h" + +ABC_NAMESPACE_HEADER_START + +typedef struct sn_add_map_options_t +{ + uint32_t min_width; + bool map_add; + bool map_sub; + bool preserve_names; +} sn_add_map_options_t; + +static inline sn_add_map_options_t sn_add_map_default_options(void) +{ + sn_add_map_options_t options = {0, true, true, true}; + return options; +} + +static inline bool sn_add_tech_supports(const sn_carry_tech_t* tech, const sn_add_map_options_t* options, + sn_obj_type_t type, uint32_t width) +{ + assert(tech && options); + uint32_t min_width = options->min_width ? options->min_width : tech->min_op_width; + return width >= min_width && ((type == SN_ADD && options->map_add) || (type == SN_SUB && options->map_sub)); +} + +static inline sn_obj_id_t sn_add_slice_bit(sn_module_t* module, sn_obj_id_t value, uint32_t bit) +{ + assert(bit < sn_obj_width(module, value)); + return sn_module_add_slice(module, value, (int32_t)bit, (int32_t)bit, NULL); +} + +// The behavioral body is identical to the Xilinx CARRY4 simulation model. It +// permits standalone SN simulation and CEC while the __sn_ prefix marks the +// module as a hard primitive that hierarchy collapse and LUT mapping preserve. +static inline sn_module_id_t sn_add_carry_primitive_module(sn_design_t* design, const sn_carry_tech_t* tech) +{ + assert(design && tech && tech->width == 4); + char name[64]; + int length = snprintf(name, sizeof(name), "__sn_%s", tech->name); + assert(length >= 0 && (size_t)length < sizeof(name)); + sn_module_id_t existing = sn_design_find_module(design, name); + if (existing != SN_INVALID_ID) + return existing; + + sn_module_id_t id = sn_design_add_module(design, name); + sn_module_t* module = sn_design_get_module(design, id); + sn_obj_id_t ci = sn_module_add_pi(module, 1, false, "CI"); + sn_obj_id_t cyinit = sn_module_add_pi(module, 1, false, "CYINIT"); + sn_obj_id_t di = sn_module_add_pi(module, 4, false, "DI"); + sn_obj_id_t s = sn_module_add_pi(module, 4, false, "S"); + sn_obj_id_t init_fanins[2] = {ci, cyinit}; + sn_obj_id_t carry = sn_module_add_operator(module, SN_BIT_OR, 1, false, 2, init_fanins, NULL); + sn_obj_id_t o_bits[4], co_bits[4]; + for (uint32_t bit = 0; bit < 4; bit++) + { + sn_obj_id_t s_bit = sn_add_slice_bit(module, s, bit); + sn_obj_id_t di_bit = sn_add_slice_bit(module, di, bit); + sn_obj_id_t xor_fanins[2] = {s_bit, carry}; + o_bits[bit] = sn_module_add_operator(module, SN_BIT_XOR, 1, false, 2, xor_fanins, NULL); + co_bits[bit] = sn_module_add_mux(module, s_bit, carry, di_bit, NULL); + carry = co_bits[bit]; + } + sn_obj_id_t o = sn_module_add_concat(module, 4, o_bits, NULL); + sn_obj_id_t co = sn_module_add_concat(module, 4, co_bits, NULL); + sn_module_add_po(module, 4, false, "O", o); + sn_module_add_po(module, 4, false, "CO", co); + assert(sn_module_is_topo(module)); + return id; +} + +static inline sn_obj_id_t sn_add_resize(sn_module_t* module, sn_obj_id_t value, uint32_t width, bool is_signed) +{ + if (sn_obj_width(module, value) == width && sn_obj_is_signed(module, value) == is_signed) + return value; + return sn_module_add_operator(module, SN_CAST, width, is_signed, 1, &value, NULL); +} + +static inline sn_obj_id_t sn_add_pad_chunk(sn_module_t* module, sn_obj_id_t value, uint32_t width) +{ + assert(width && width <= 4 && sn_obj_width(module, value) == width); + if (width == 4) + return value; + sn_obj_id_t zero = sn_module_add_named_obj(module, SN_CONST0, 4 - width, false, 0, NULL); + sn_obj_id_t fanins[2] = {value, zero}; + return sn_module_add_concat(module, 2, fanins, NULL); +} + +// Implements A+B or A-B exactly as Yosys's Xilinx $alu mapping: DI=A, +// S=A^B (or A^~B), and subtraction starts the carry chain at one. +static inline sn_obj_id_t sn_add_map_carry_chain(sn_module_t* module, const sn_carry_tech_t* tech, + sn_obj_type_t type, sn_obj_id_t a, sn_obj_id_t b, + uint32_t result_width, bool result_signed, const char* name) +{ + assert(module && tech && tech->width == 4 && (type == SN_ADD || type == SN_SUB)); + assert(a < module->obj_types.size && b < module->obj_types.size && result_width); + bool signed_operands = sn_obj_is_signed(module, a) && sn_obj_is_signed(module, b); + a = sn_add_resize(module, a, result_width, signed_operands); + b = sn_add_resize(module, b, result_width, signed_operands); + if (type == SN_SUB) + b = sn_module_add_operator(module, SN_BIT_NOT, result_width, signed_operands, 1, &b, NULL); + sn_obj_id_t xor_fanins[2] = {a, b}; + sn_obj_id_t propagate = + sn_module_add_operator(module, SN_BIT_XOR, result_width, false, 2, xor_fanins, NULL); + sn_obj_id_t zero = sn_module_add_named_obj(module, SN_CONST0, 1, false, 0, NULL); + sn_obj_id_t one = sn_module_add_named_obj(module, SN_CONST1, 1, false, 0, NULL); + sn_obj_id_t carry = zero; + sn_module_id_t primitive = sn_add_carry_primitive_module(module->design, tech); + uint32_t chunk_count = (result_width + 3) / 4; + sn_obj_id_t* chunks = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * chunk_count); + assert(chunks); + for (uint32_t chunk = 0; chunk < chunk_count; chunk++) + { + uint32_t offset = chunk * 4; + uint32_t width = result_width - offset < 4 ? result_width - offset : 4; + sn_obj_id_t di = sn_module_add_slice(module, a, (int32_t)(offset + width - 1), (int32_t)offset, NULL); + sn_obj_id_t s = + sn_module_add_slice(module, propagate, (int32_t)(offset + width - 1), (int32_t)offset, NULL); + di = sn_add_pad_chunk(module, di, width); + s = sn_add_pad_chunk(module, s, width); + sn_obj_id_t inputs[4] = {carry, chunk == 0 && type == SN_SUB ? one : zero, di, s}; + const char* output_names[2] = {NULL, NULL}; + sn_obj_id_t inst = sn_module_add_inst(module, primitive, 4, inputs, NULL, output_names); + chunks[chunk] = sn_inst_output(module, inst, 0); + sn_obj_id_t co = sn_inst_output(module, inst, 1); + carry = sn_add_slice_bit(module, co, 3); + } + sn_obj_id_t result = chunk_count == 1 ? chunks[0] : sn_module_add_concat(module, chunk_count, chunks, NULL); + free(chunks); + if (sn_obj_width(module, result) != result_width) + result = sn_module_add_slice(module, result, (int32_t)result_width - 1, 0, NULL); + if (sn_obj_is_signed(module, result) != result_signed) + result = sn_module_add_operator(module, SN_CAST, result_width, result_signed, 1, &result, name); + return result; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snMapDsp.h b/src/base/sn/snMapDsp.h new file mode 100644 index 000000000..dd97d4bb1 --- /dev/null +++ b/src/base/sn/snMapDsp.h @@ -0,0 +1,85 @@ +/**CFile**************************************************************** + + FileName [snMapDsp.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Mapping word-level multipliers into FPGA DSP primitives.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snMapDsp.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_MAP_DSP_H +#define SN_MAP_DSP_H + +#include "sn.h" +#include "snTech.h" + +ABC_NAMESPACE_HEADER_START + +typedef struct sn_dsp_map_options_t +{ + bool allow_soft_fallback; + bool use_preadder; + bool use_postadder; + bool preserve_names; + bool balance_adders; + bool prune_unused_high_products; + uint32_t a_unsigned_chunk_width; + uint32_t b_unsigned_chunk_width; + uint32_t max_dsps_per_multiply; +} sn_dsp_map_options_t; + +static inline sn_dsp_map_options_t sn_dsp_map_default_options(void) +{ + sn_dsp_map_options_t options = {true, false, false, true, true, true, 0, 0, 0}; + return options; +} + +static inline bool sn_dsp_tech_supports_mul(const sn_dsp_tech_t* tech, uint32_t a_width, uint32_t b_width, + uint32_t result_width, bool a_signed, bool b_signed) +{ + assert(tech); + if (!a_width || !b_width || !result_width || a_width > tech->a_width || b_width > tech->b_width || + result_width > tech->p_width) + return false; + if (a_width < tech->min_a_width || b_width < tech->min_b_width || result_width < tech->min_p_width) + return false; + if (tech->signed_only && (!a_signed || !b_signed)) + return false; + return true; +} + +static inline sn_module_id_t sn_map_dsp_primitive_module(sn_design_t* design, const sn_dsp_tech_t* tech, + uint32_t a_width, uint32_t b_width, uint32_t y_width, + bool a_signed, bool b_signed) +{ + char name[128]; + int length = snprintf(name, sizeof(name), "__sn_%s_mul_%u_%u_%u_s%u%u", tech->name, a_width, b_width, + y_width, a_signed ? 1u : 0u, b_signed ? 1u : 0u); + assert(length >= 0 && (size_t)length < sizeof(name)); + sn_module_id_t existing = sn_design_find_module(design, name); + if (existing != SN_INVALID_ID) + return existing; + sn_module_id_t id = sn_design_add_module(design, name); + sn_module_t* module = sn_design_get_module(design, id); + sn_obj_id_t a = sn_module_add_pi(module, a_width, a_signed, "A"); + sn_obj_id_t b = sn_module_add_pi(module, b_width, b_signed, "B"); + sn_obj_id_t fanins[] = {a, b}; + sn_obj_id_t product = sn_module_add_operator(module, SN_MUL, y_width, a_signed || b_signed, 2, fanins, "P"); + sn_module_add_po(module, y_width, a_signed || b_signed, "Y", product); + return id; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snMapLut.h b/src/base/sn/snMapLut.h new file mode 100644 index 000000000..e4330253d --- /dev/null +++ b/src/base/sn/snMapLut.h @@ -0,0 +1,270 @@ +/**CFile**************************************************************** + + FileName [snMapLut.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Partitioned synthesis and LUT mapping of hierarchical SN designs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snMapLut.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_MAP_LUT_H +#define SN_MAP_LUT_H + +// Natural-hierarchy LUT-mapping harness. Each reachable user module is extracted as one combinational MiniAIG while +// child instances, registers, and mapped hard blocks remain boundary terminals. A caller-supplied callback maps this +// partition and returns a MiniLUT. The harness reconstructs the module at its stable ID, preserving parent references. + +#include "snBlast.h" +#include "snMiniLut.h" +#include "snPth.h" + +ABC_NAMESPACE_HEADER_START + +typedef Mini_Lut_t* (*sn_map_lut_partition_fn)(void* context, sn_module_id_t module, const char* name, + Mini_Aig_t* aig, const sn_blast_boundary_t* boundary); + +typedef struct sn_map_lut_stats_t +{ + uint32_t reachable_modules; + uint32_t mapped_modules; + uint32_t trivial_modules; + uint32_t primitive_modules; + uint32_t generic_memory_modules; + uint64_t input_ands; + uint64_t output_luts; + sn_module_id_t failed_module; +} sn_map_lut_stats_t; + +typedef struct sn_map_lut_job_t +{ + sn_module_id_t module; + sn_name_id_t name; + Mini_Aig_t* aig; + Mini_Lut_t* lut; + sn_blast_boundary_t boundary; +} sn_map_lut_job_t; + +typedef struct sn_map_lut_runner_t +{ + sn_design_t* design; + sn_map_lut_partition_fn function; + void* context; +} sn_map_lut_runner_t; + +static inline void sn_map_lut_run_job(void* argument, void* job_argument) +{ + sn_map_lut_runner_t* runner = (sn_map_lut_runner_t*)argument; + sn_map_lut_job_t* job = (sn_map_lut_job_t*)job_argument; + job->lut = runner->function(runner->context, job->module, + sn_name_get(&runner->design->names, job->name), job->aig, &job->boundary); +} + +static inline bool sn_map_lut_boundary_has_generic_memories(const sn_blast_boundary_t* boundary) +{ + for (size_t i = 0; i < boundary->cis.size; i++) + if (sn_vec_at(sn_blast_boundary_bit_t, &boundary->cis, i).kind == SN_BLAST_BOUNDARY_MEMORY_OUTPUT) + return true; + for (size_t i = 0; i < boundary->cos.size; i++) + if (sn_vec_at(sn_blast_boundary_bit_t, &boundary->cos, i).kind == SN_BLAST_BOUNDARY_MEMORY_INPUT) + return true; + return false; +} + +static inline void sn_design_replace_appended_module(sn_design_t* design, sn_module_id_t module, + sn_name_id_t name, sn_module_id_t temporary) +{ + sn_module_t* old_module; + sn_module_t* new_module; + bool interface_locked; + assert(design && module < design->modules.size); + old_module = sn_design_get_module(design, module); + assert(old_module->name == name); + assert(temporary + 1 == design->modules.size && temporary != module); + new_module = sn_design_get_module(design, temporary); + interface_locked = old_module->interface_locked; + sn_module_destroy(old_module); + free(old_module); + new_module->id = module; + new_module->name = name; + new_module->interface_locked = interface_locked; + sn_vec_at(sn_module_t*, &design->modules, module) = new_module; + design->modules.size--; +} + +// Maps all user modules reachable from root. The callback borrows aig and boundary for the duration of the call and +// returns a newly allocated MiniLUT owned by this harness. A NULL result aborts the pass. Modules containing generic +// memories are skipped; map their memories into primitive instances first if their surrounding logic should be mapped. +// The operation is in-place, so a transactional client should invoke it on a duplicate design and install that design +// only after this API succeeds. +static inline bool sn_design_map_lut_hierarchy(sn_design_t* design, sn_module_id_t root, + sn_map_lut_partition_fn map_partition, void* context, + unsigned processes, bool extract_only, + sn_map_lut_stats_t* returned_stats) +{ + sn_map_lut_stats_t stats = {0}; + size_t module_count; + bool* reachable; + sn_vec_t pending; + sn_vec_t jobs; + stats.failed_module = SN_INVALID_ID; + assert(design && root < design->modules.size && map_partition && processes >= 1); + module_count = design->modules.size; + // Module replacement invalidates optional duplication maps that may have been cached by earlier mapping passes. + for (sn_module_id_t module_id = 0; module_id < module_count; module_id++) + { + sn_module_t* module = sn_design_get_module(design, module_id); + sn_vec_destroy(&module->copy_ids); + sn_vec_init(&module->copy_ids); + module->copy_module = SN_INVALID_ID; + } + reachable = (bool*)calloc(module_count, sizeof(bool)); + assert(reachable); + sn_vec_init(&pending); + sn_vec_init(&jobs); + *sn_vec_push(sn_module_id_t, &pending) = root; + while (pending.size) + { + sn_module_id_t module_id = sn_vec_at(sn_module_id_t, &pending, --pending.size); + const sn_module_t* module; + if (reachable[module_id]) + continue; + reachable[module_id] = true; + stats.reachable_modules++; + module = sn_design_get_module_const(design, module_id); + for (size_t i = 0; i < module->inst_modules.size; i++) + *sn_vec_push(sn_module_id_t, &pending) = sn_vec_at(sn_module_id_t, &module->inst_modules, i); + } + for (sn_module_id_t module_id = 0; module_id < module_count; module_id++) + { + const sn_module_t* module; + sn_name_id_t name_id; + sn_blast_options_t options; + sn_blast_boundary_t boundary; + Mini_Aig_t* aig; + if (!reachable[module_id]) + continue; + module = sn_design_get_module_const(design, module_id); + if (sn_module_is_technology_primitive(module)) + { + stats.primitive_modules++; + continue; + } + name_id = module->name; + options = sn_blast_default_options(); + options.mode = SN_BLAST_COMB; + options.abstract_instances = true; + sn_blast_boundary_init(&boundary); + aig = sn_design_blast_hier_boundary_options(design, module_id, options, NULL, &boundary); + if (sn_map_lut_boundary_has_generic_memories(&boundary)) + { + stats.generic_memory_modules++; + Mini_AigStop(aig); + sn_blast_boundary_destroy(&boundary); + continue; + } + if (Mini_AigAndNum(aig) == 0) + { + stats.trivial_modules++; + Mini_AigStop(aig); + sn_blast_boundary_destroy(&boundary); + continue; + } + stats.input_ands += (uint64_t)Mini_AigAndNum(aig); + if (processes == 1) + { + Mini_Lut_t* lut = map_partition(context, module_id, sn_name_get(&design->names, name_id), aig, &boundary); + Mini_AigStop(aig); + if (!lut) + { + stats.failed_module = module_id; + sn_blast_boundary_destroy(&boundary); + sn_vec_destroy(&pending); + sn_vec_destroy(&jobs); + free(reachable); + if (returned_stats) + *returned_stats = stats; + return false; + } + if (extract_only) + { + Mini_LutStop(lut); + sn_blast_boundary_destroy(&boundary); + stats.mapped_modules++; + continue; + } + sn_lut_stats_t lut_stats = sn_lut_analyze(lut, &boundary); + sn_module_id_t temporary = sn_design_add_lut_module(design, module_id, lut, &boundary, + "__sn_lut_partition"); + Mini_LutStop(lut); + sn_blast_boundary_destroy(&boundary); + sn_design_replace_appended_module(design, module_id, name_id, temporary); + stats.mapped_modules++; + stats.output_luts += lut_stats.lut_count; + continue; + } + sn_map_lut_job_t* job = sn_vec_push(sn_map_lut_job_t, &jobs); + job->module = module_id; + job->name = name_id; + job->aig = aig; + job->lut = NULL; + job->boundary = boundary; + } + void** job_pointers = jobs.size ? (void**)malloc(sizeof(void*) * jobs.size) : NULL; + assert(job_pointers || jobs.size == 0); + for (size_t i = 0; i < jobs.size; i++) + job_pointers[i] = &sn_vec_at(sn_map_lut_job_t, &jobs, i); + sn_map_lut_runner_t runner = {design, map_partition, context}; + sn_pth_process(job_pointers, jobs.size, processes, sn_map_lut_run_job, &runner); + free(job_pointers); + bool success = true; + for (size_t i = 0; i < jobs.size; i++) + if (!sn_vec_at(sn_map_lut_job_t, &jobs, i).lut) + { + stats.failed_module = sn_vec_at(sn_map_lut_job_t, &jobs, i).module; + success = false; + break; + } + if (success && !extract_only) + for (size_t i = 0; i < jobs.size; i++) + { + sn_map_lut_job_t* job = &sn_vec_at(sn_map_lut_job_t, &jobs, i); + sn_lut_stats_t lut_stats = sn_lut_analyze(job->lut, &job->boundary); + sn_module_id_t temporary = sn_design_add_lut_module(design, job->module, job->lut, &job->boundary, + "__sn_lut_partition"); + sn_design_replace_appended_module(design, job->module, job->name, temporary); + stats.mapped_modules++; + stats.output_luts += lut_stats.lut_count; + } + else if (success) + stats.mapped_modules += (uint32_t)jobs.size; + for (size_t i = 0; i < jobs.size; i++) + { + sn_map_lut_job_t* job = &sn_vec_at(sn_map_lut_job_t, &jobs, i); + Mini_AigStop(job->aig); + if (job->lut) + Mini_LutStop(job->lut); + sn_blast_boundary_destroy(&job->boundary); + } + sn_vec_destroy(&pending); + sn_vec_destroy(&jobs); + free(reachable); + assert(!success || sn_design_is_topo(design)); + if (returned_stats) + *returned_stats = stats; + return success; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snMapMem.h b/src/base/sn/snMapMem.h new file mode 100644 index 000000000..0c8f7ac26 --- /dev/null +++ b/src/base/sn/snMapMem.h @@ -0,0 +1,101 @@ +/**CFile**************************************************************** + + FileName [snMapMem.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Mapping technology-independent memories into FPGA memory primitives.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snMapMem.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_MAP_MEM_H +#define SN_MAP_MEM_H + +#include "sn.h" +#include "snTech.h" + +ABC_NAMESPACE_HEADER_START + +typedef enum sn_mem_split_order_t +{ + SN_MEM_SPLIT_AUTO = 0, + SN_MEM_SPLIT_WIDTH_FIRST, + SN_MEM_SPLIT_DEPTH_FIRST +} sn_mem_split_order_t; + +typedef struct sn_mem_map_options_t +{ + bool allow_lutram_fallback; + bool allow_register_fallback; + bool preserve_names; + uint32_t min_memory_bits; + uint32_t max_primitives_per_memory; + sn_mem_split_order_t split_order; +} sn_mem_map_options_t; + +static inline sn_mem_map_options_t sn_mem_map_default_options(void) +{ + sn_mem_map_options_t options = {false, false, true, 0, 0, SN_MEM_SPLIT_AUTO}; + return options; +} + +// Returns true when a memory's dimensions and port protocol can be represented +// by one technology primitive. This conservative predicate is used before the +// rewriting pass; splitting, packing, and primitive-inst construction are +// the next mapper milestone. +static inline bool sn_mem_tech_supports(const sn_mem_tech_t* tech, uint32_t width, uint32_t depth, + sn_mem_port_mode_t port_mode) +{ + assert(tech); + if (port_mode != tech->port_mode || !width || !depth || width > UINT32_MAX / depth) + return false; + if (width * depth > tech->cap_bits) + return false; + if (depth > (1u << tech->address_bits)) + return false; + for (size_t i = 0; i < tech->width_count; i++) + if (tech->widths[i] == width) + return true; + return false; +} + +// Creates a behavioral wrapper for one technology memory shape. Keeping the +// wrapper as an SN module makes the mapped result simulatable; a later Verilog +// technology writer can replace this module by RAMB/URAM cells. +static inline sn_module_id_t sn_map_mem_primitive_module(sn_design_t* design, const sn_mem_tech_t* tech, + uint32_t width, uint32_t depth) +{ + char name[128]; + int length = snprintf(name, sizeof(name), "__sn_%s_mem_%u_%u", tech->name, width, depth); + assert(length >= 0 && (size_t)length < sizeof(name)); + sn_module_id_t existing = sn_design_find_module(design, name); + if (existing != SN_INVALID_ID) + return existing; + sn_module_id_t id = sn_design_add_module(design, name); + sn_module_t* module = sn_design_get_module(design, id); + sn_obj_id_t clock = sn_module_add_pi(module, 1, false, "clock"); + sn_obj_id_t enable = sn_module_add_pi(module, 1, false, "enable"); + sn_obj_id_t write_address = sn_module_add_pi(module, 32, false, "write_address"); + sn_obj_id_t data = sn_module_add_pi(module, width, false, "write_data"); + sn_obj_id_t read_address = sn_module_add_pi(module, 32, false, "read_address"); + sn_obj_pair_t pair = sn_module_add_mem_pair(module, width, false, depth, "mem_out", "mem_in"); + sn_module_add_mem_write(module, pair.in, clock, enable, data, write_address, "write"); + sn_obj_id_t read = sn_module_add_mem_read(module, pair.out, SN_INVALID_ID, SN_INVALID_ID, read_address, "read"); + sn_module_add_po(module, width, false, "read_data", read); + sn_design_reorder_module_topo(design, id); + return id; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snMapTech.h b/src/base/sn/snMapTech.h new file mode 100644 index 000000000..64e722cf3 --- /dev/null +++ b/src/base/sn/snMapTech.h @@ -0,0 +1,1264 @@ +/**CFile**************************************************************** + + FileName [snMapTech.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Technology mapping infrastructure for SN hierarchy and hard primitives.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snMapTech.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_MAP_TECH_H +#define SN_MAP_TECH_H + +#include "snMapAdd.h" +#include "snMapDsp.h" +#include "snMapMem.h" + +ABC_NAMESPACE_HEADER_START + +// Combined technology mapping rebuilds each module once. Memory and multiplier +// expansion may temporarily introduce forward references; the result is +// finalized through one dependency-aware topological duplication. + +typedef struct sn_tech_map_options_t +{ + bool map_memories; + bool map_multipliers; + bool map_adders; + sn_mem_map_options_t memory; + sn_dsp_map_options_t dsp; + sn_add_map_options_t add; +} sn_tech_map_options_t; + +typedef struct sn_tech_map_stats_t +{ + size_t mem_insts; + size_t dsp_insts; + size_t carry_insts; +} sn_tech_map_stats_t; + +typedef struct sn_tech_mem_plan_t +{ + sn_obj_id_t memory; + sn_obj_id_t memory_in; + sn_obj_id_t read; + sn_obj_id_t write; + sn_obj_id_t reads[2]; + sn_obj_id_t writes[2]; + uint32_t read_count; + uint32_t write_count; + int8_t port_reads[2]; + int8_t port_writes[2]; + const sn_mem_tech_t* primitive; + uint32_t port_width; + uint32_t tile_depth; + uint32_t width_tiles; + uint32_t depth_tiles; +} sn_tech_mem_plan_t; + +typedef struct sn_tech_dsp_chunk_t +{ + sn_obj_id_t object; + uint32_t offset; + bool unsigned_correction; +} sn_tech_dsp_chunk_t; + +static inline sn_tech_mem_plan_t* sn_tech_read_plan(const sn_module_t* module, sn_tech_mem_plan_t* plans, + sn_obj_id_t object) +{ + return sn_obj_type(module, object) == SN_MEM_READ ? &plans[sn_obj_type_id(module, object)] : NULL; +} + +static inline sn_tech_map_options_t sn_tech_map_default_options(void) +{ + sn_tech_map_options_t options; + options.map_memories = true; + options.map_multipliers = true; + options.map_adders = false; + options.memory = sn_mem_map_default_options(); + options.dsp = sn_dsp_map_default_options(); + options.add = sn_add_map_default_options(); + return options; +} + +static inline uint32_t sn_tech_ceil_div(uint32_t value, uint32_t divisor) +{ + assert(divisor); + return value / divisor + (value % divisor != 0); +} + +static inline uint32_t sn_tech_floor_pow2(uint32_t value) +{ + assert(value); + uint32_t result = 1; + while (result <= value / 2) + result <<= 1; + return result; +} + +static inline uint32_t sn_tech_ceil_log2(uint32_t value) +{ + assert(value); + uint32_t result = 0; + for (value--; value; value >>= 1) + result++; + return result; +} + +static inline sn_obj_id_t sn_tech_add_uint_const(sn_module_t* module, uint32_t width, uint32_t value) +{ + assert(width && width <= 32); + assert(width == 32 || value < (1u << width)); + return sn_module_add_const(module, width, false, &value, NULL); +} + +static inline sn_obj_id_t sn_tech_add_cast(sn_module_t* module, sn_obj_id_t value, uint32_t width, bool is_signed) +{ + return sn_module_add_operator(module, SN_CAST, width, is_signed, 1, &value, NULL); +} + +static inline sn_obj_id_t sn_tech_add_zero_extend(sn_module_t* module, sn_obj_id_t value, uint32_t width) +{ + uint32_t old_width = sn_obj_width(module, value); + assert(old_width <= width); + if (old_width == width) + return value; + sn_obj_id_t padding = sn_module_add_named_obj(module, SN_CONST0, width - old_width, false, 0, NULL); + sn_obj_id_t fanins[2] = {value, padding}; + return sn_module_add_concat(module, 2, fanins, NULL); +} + +static inline uint32_t sn_tech_dsp_chunk_count(uint32_t width, uint32_t port_width, uint32_t low_width, + uint32_t min_width) +{ + assert(width && port_width && low_width && low_width <= port_width && min_width <= port_width); + if (width < min_width || low_width < min_width) + return UINT32_MAX; + uint32_t count = 1; + while (width > port_width) + { + uint32_t chunk_width = low_width; + if (width - chunk_width < min_width) + chunk_width = width - min_width; + if (chunk_width < min_width || chunk_width > port_width) + return UINT32_MAX; + width -= chunk_width; + count++; + } + return count; +} + +static inline uint32_t sn_tech_dsp_box_count(const sn_module_t* module, const sn_dsp_tech_t* dsp, + const sn_dsp_map_options_t* options, sn_obj_id_t a, sn_obj_id_t b) +{ + bool signed_operands = sn_obj_is_signed(module, a) && sn_obj_is_signed(module, b); + uint32_t a_width = sn_obj_width(module, a) + !signed_operands; + uint32_t b_width = sn_obj_width(module, b) + !signed_operands; + uint32_t a_low = options->a_unsigned_chunk_width ? options->a_unsigned_chunk_width : dsp->a_width; + uint32_t b_low = options->b_unsigned_chunk_width ? options->b_unsigned_chunk_width : dsp->b_width; + if (a_low < dsp->min_a_width || a_low > dsp->a_width || b_low < dsp->min_b_width || b_low > dsp->b_width) + return UINT32_MAX; + uint32_t direct_a = sn_tech_dsp_chunk_count(a_width, dsp->a_width, a_low, dsp->min_a_width); + uint32_t direct_b = sn_tech_dsp_chunk_count(b_width, dsp->b_width, b_low, dsp->min_b_width); + uint32_t swapped_a = sn_tech_dsp_chunk_count(b_width, dsp->a_width, a_low, dsp->min_a_width); + uint32_t swapped_b = sn_tech_dsp_chunk_count(a_width, dsp->b_width, b_low, dsp->min_b_width); + uint32_t direct = direct_a == UINT32_MAX || direct_b == UINT32_MAX || direct_a > UINT32_MAX / direct_b + ? UINT32_MAX + : direct_a * direct_b; + uint32_t swapped = swapped_a == UINT32_MAX || swapped_b == UINT32_MAX || swapped_a > UINT32_MAX / swapped_b + ? UINT32_MAX + : swapped_a * swapped_b; + return direct < swapped ? direct : swapped; +} + +static inline void sn_tech_dsp_make_chunks(sn_module_t* module, sn_obj_id_t value, uint32_t port_width, + uint32_t low_width, uint32_t min_width, sn_vec_t* chunks) +{ + assert(module && chunks); + uint32_t width = sn_obj_width(module, value); + uint32_t count = sn_tech_dsp_chunk_count(width, port_width, low_width, min_width); + assert(count != UINT32_MAX); + uint32_t offset = 0, remaining = width; + for (uint32_t i = 0; i < count; i++) + { + uint32_t chunk_width = remaining <= port_width ? remaining : low_width; + if (remaining > port_width && remaining - chunk_width < min_width) + chunk_width = remaining - min_width; + assert(chunk_width >= min_width && chunk_width <= port_width); + sn_obj_id_t chunk = count == 1 + ? value + : sn_module_add_slice(module, value, (int32_t)(offset + chunk_width - 1), + (int32_t)offset, NULL); + chunk = sn_tech_add_cast(module, chunk, chunk_width, true); + sn_tech_dsp_chunk_t* entry = sn_vec_push(sn_tech_dsp_chunk_t, chunks); + entry->object = chunk; + entry->offset = offset; + entry->unsigned_correction = i + 1 != count; + offset += chunk_width; + remaining -= chunk_width; + } + assert(!remaining && offset == width); +} + +static inline sn_obj_id_t sn_tech_dsp_align(sn_module_t* module, sn_obj_id_t value, uint32_t result_width, + uint32_t shift, bool is_signed) +{ + value = sn_tech_add_cast(module, value, result_width, is_signed); + if (!shift) + return value; + sn_obj_id_t amount = sn_tech_add_uint_const(module, 32, shift); + sn_obj_id_t fanins[2] = {value, amount}; + return sn_module_add_operator(module, SN_SHL, result_width, is_signed, 2, fanins, NULL); +} + +static inline void sn_tech_dsp_add_gated_correction(sn_module_t* module, sn_vec_t* partials, sn_obj_id_t condition, + sn_obj_id_t value, uint32_t result_width, uint32_t shift) +{ + if (shift >= result_width) + return; + sn_obj_id_t selected = sn_tech_add_cast(module, value, result_width, true); + sn_obj_id_t zero = sn_module_add_named_obj(module, SN_CONST0, result_width, false, 0, NULL); + sn_obj_id_t gated = sn_module_add_mux(module, condition, selected, zero, NULL); + *sn_vec_push(sn_obj_id_t, partials) = sn_tech_dsp_align(module, gated, result_width, shift, true); +} + +static inline sn_obj_id_t sn_tech_map_multiplier(sn_module_t* module, const sn_dsp_tech_t* dsp, + const sn_dsp_map_options_t* options, sn_obj_id_t a, + sn_obj_id_t b, uint32_t result_width, bool result_signed) +{ + assert(module && dsp && options && result_width); + bool signed_operands = sn_obj_is_signed(module, a) && sn_obj_is_signed(module, b); + if (!signed_operands) + { + uint32_t zero = 0; + sn_obj_id_t sign = sn_module_add_const(module, 1, false, &zero, NULL); + sn_obj_id_t fanins[2] = {a, sign}; + a = sn_module_add_concat(module, 2, fanins, NULL); + a = sn_tech_add_cast(module, a, sn_obj_width(module, a), true); + fanins[0] = b; + b = sn_module_add_concat(module, 2, fanins, NULL); + b = sn_tech_add_cast(module, b, sn_obj_width(module, b), true); + } + + uint32_t a_low = options->a_unsigned_chunk_width ? options->a_unsigned_chunk_width : dsp->a_width; + uint32_t b_low = options->b_unsigned_chunk_width ? options->b_unsigned_chunk_width : dsp->b_width; + assert(a_low <= dsp->a_width && b_low <= dsp->b_width); + uint32_t direct_a = sn_tech_dsp_chunk_count(sn_obj_width(module, a), dsp->a_width, a_low, dsp->min_a_width); + uint32_t direct_b = sn_tech_dsp_chunk_count(sn_obj_width(module, b), dsp->b_width, b_low, dsp->min_b_width); + uint32_t swapped_a = sn_tech_dsp_chunk_count(sn_obj_width(module, b), dsp->a_width, a_low, dsp->min_a_width); + uint32_t swapped_b = sn_tech_dsp_chunk_count(sn_obj_width(module, a), dsp->b_width, b_low, dsp->min_b_width); + uint32_t direct = direct_a == UINT32_MAX || direct_b == UINT32_MAX || direct_a > UINT32_MAX / direct_b + ? UINT32_MAX + : direct_a * direct_b; + uint32_t swapped = swapped_a == UINT32_MAX || swapped_b == UINT32_MAX || swapped_a > UINT32_MAX / swapped_b + ? UINT32_MAX + : swapped_a * swapped_b; + if (swapped < direct) + { + sn_obj_id_t temporary = a; + a = b; + b = temporary; + } + + sn_vec_t a_chunks, b_chunks, partials; + sn_vec_init(&a_chunks); + sn_vec_init(&b_chunks); + sn_vec_init(&partials); + sn_tech_dsp_make_chunks(module, a, dsp->a_width, a_low, dsp->min_a_width, &a_chunks); + sn_tech_dsp_make_chunks(module, b, dsp->b_width, b_low, dsp->min_b_width, &b_chunks); + assert(!options->max_dsps_per_multiply || + a_chunks.size * b_chunks.size <= options->max_dsps_per_multiply); + + for (size_t diagonal = 0; diagonal < a_chunks.size + b_chunks.size - 1; diagonal++) + for (size_t i = 0; i < a_chunks.size; i++) + { + if (diagonal < i) + continue; + size_t j = diagonal - i; + if (j >= b_chunks.size) + continue; + const sn_tech_dsp_chunk_t* ac = &sn_vec_at(sn_tech_dsp_chunk_t, &a_chunks, i); + const sn_tech_dsp_chunk_t* bc = &sn_vec_at(sn_tech_dsp_chunk_t, &b_chunks, j); + uint32_t shift = ac->offset + bc->offset; + if (options->prune_unused_high_products && shift >= result_width) + continue; + uint32_t product_width = sn_obj_width(module, ac->object) + sn_obj_width(module, bc->object); + uint32_t primitive_width = product_width < dsp->min_p_width ? dsp->min_p_width : product_width; + assert(primitive_width <= dsp->p_width); + sn_module_id_t primitive = sn_map_dsp_primitive_module( + module->design, dsp, sn_obj_width(module, ac->object), sn_obj_width(module, bc->object), + primitive_width, true, true); + sn_obj_id_t inputs[2] = {ac->object, bc->object}; + sn_obj_id_t product = sn_module_add_inst(module, primitive, 2, inputs, NULL, NULL); + *sn_vec_push(sn_obj_id_t, &partials) = sn_tech_dsp_align(module, product, result_width, shift, true); + + // A non-top radix chunk is unsigned even though the DSP input is signed. For a W-bit chunk U, + // U = signed(U) + msb(U)*2^W. Add the resulting one-bit-gated correction terms around the signed + // DSP product. This uses the full 27x18 multiplier while preserving exact unsigned chunk semantics. + uint32_t ac_width = sn_obj_width(module, ac->object); + uint32_t bc_width = sn_obj_width(module, bc->object); + sn_obj_id_t ac_sign = SN_INVALID_ID, bc_sign = SN_INVALID_ID; + if (ac->unsigned_correction) + { + ac_sign = sn_module_add_slice(module, ac->object, (int32_t)(ac_width - 1), + (int32_t)(ac_width - 1), NULL); + sn_tech_dsp_add_gated_correction(module, &partials, ac_sign, bc->object, result_width, + shift + ac_width); + } + if (bc->unsigned_correction) + { + bc_sign = sn_module_add_slice(module, bc->object, (int32_t)(bc_width - 1), + (int32_t)(bc_width - 1), NULL); + sn_tech_dsp_add_gated_correction(module, &partials, bc_sign, ac->object, result_width, + shift + bc_width); + } + if (ac->unsigned_correction && bc->unsigned_correction && shift + ac_width + bc_width < result_width) + { + sn_obj_id_t fanins[2] = {ac_sign, bc_sign}; + sn_obj_id_t both = sn_module_add_operator(module, SN_BIT_AND, 1, false, 2, fanins, NULL); + *sn_vec_push(sn_obj_id_t, &partials) = + sn_tech_dsp_align(module, both, result_width, shift + ac_width + bc_width, false); + } + } + + if (!partials.size) + *sn_vec_push(sn_obj_id_t, &partials) = sn_tech_add_uint_const(module, result_width, 0); + while (partials.size > 1) + { + sn_vec_t next; + sn_vec_init(&next); + if (options->balance_adders) + { + for (size_t i = 0; i < partials.size; i += 2) + { + if (i + 1 == partials.size) + *sn_vec_push(sn_obj_id_t, &next) = sn_vec_at(sn_obj_id_t, &partials, i); + else + { + sn_obj_id_t fanins[2] = {sn_vec_at(sn_obj_id_t, &partials, i), + sn_vec_at(sn_obj_id_t, &partials, i + 1)}; + *sn_vec_push(sn_obj_id_t, &next) = + sn_module_add_operator(module, SN_ADD, result_width, true, 2, fanins, NULL); + } + } + } + else + { + sn_obj_id_t fanins[2] = {sn_vec_at(sn_obj_id_t, &partials, 0), + sn_vec_at(sn_obj_id_t, &partials, 1)}; + *sn_vec_push(sn_obj_id_t, &next) = + sn_module_add_operator(module, SN_ADD, result_width, true, 2, fanins, NULL); + for (size_t i = 2; i < partials.size; i++) + *sn_vec_push(sn_obj_id_t, &next) = sn_vec_at(sn_obj_id_t, &partials, i); + } + sn_vec_destroy(&partials); + partials = next; + } + sn_obj_id_t result = sn_tech_add_cast(module, sn_vec_at(sn_obj_id_t, &partials, 0), result_width, result_signed); + sn_vec_destroy(&a_chunks); + sn_vec_destroy(&b_chunks); + sn_vec_destroy(&partials); + return result; +} + +static inline bool sn_tech_choose_memory_for_mode(const sn_tech_t* tech, uint32_t width, uint32_t depth, + const sn_mem_map_options_t* options, bool simple_dual, + sn_tech_mem_plan_t* plan) +{ + assert(tech && options && plan && width && depth); + uint64_t best_cost = UINT64_MAX; + uint32_t best_port_width = 0; + for (size_t i = 0; i < tech->memory_count; i++) + { + const sn_mem_tech_t* primitive = &tech->memories[i]; + const uint32_t* widths = simple_dual && primitive->simple_dual_width_count + ? primitive->simple_dual_widths + : primitive->widths; + size_t width_count = simple_dual && primitive->simple_dual_width_count + ? primitive->simple_dual_width_count + : primitive->width_count; + for (size_t j = 0; j < width_count; j++) + { + uint32_t port_width = widths[j]; + uint32_t tile_depth = sn_tech_floor_pow2(primitive->cap_bits / port_width); + if (tile_depth > (1u << primitive->address_bits)) + tile_depth = 1u << primitive->address_bits; + uint32_t width_tiles = sn_tech_ceil_div(width, port_width); + uint32_t depth_tiles = sn_tech_ceil_div(depth, tile_depth); + uint64_t count = (uint64_t)width_tiles * depth_tiles; + if (options->max_primitives_per_memory && count > options->max_primitives_per_memory) + continue; + uint64_t cost = count * primitive->mapping_cost; + bool prefer_tie = cost == best_cost && + ((options->split_order == SN_MEM_SPLIT_WIDTH_FIRST && port_width > best_port_width) || + (options->split_order == SN_MEM_SPLIT_DEPTH_FIRST && port_width < best_port_width)); + if (cost > best_cost || (cost == best_cost && !prefer_tie)) + continue; + best_cost = cost; + best_port_width = port_width; + plan->primitive = primitive; + plan->port_width = port_width; + plan->tile_depth = tile_depth; + plan->width_tiles = width_tiles; + plan->depth_tiles = depth_tiles; + } + } + return best_cost != UINT64_MAX; +} + +static inline bool sn_tech_choose_memory(const sn_tech_t* tech, uint32_t width, uint32_t depth, + const sn_mem_map_options_t* options, sn_tech_mem_plan_t* plan) +{ + return sn_tech_choose_memory_for_mode(tech, width, depth, options, false, plan); +} + +static inline sn_module_id_t sn_tech_memory_tile_module(sn_design_t* design, const sn_mem_tech_t* primitive, + uint32_t width, uint32_t depth) +{ + uint32_t address_width = sn_tech_ceil_log2(depth); + char name[128]; + int length = snprintf(name, sizeof(name), "__sn_%s_tile_%u_%u", primitive->name, width, depth); + assert(length >= 0 && (size_t)length < sizeof(name)); + sn_module_id_t existing = sn_design_find_module(design, name); + if (existing != SN_INVALID_ID) + return existing; + sn_module_id_t id = sn_design_add_module(design, name); + sn_module_t* module = sn_design_get_module(design, id); + sn_obj_id_t clock = sn_module_add_pi(module, 1, false, "clock"); + sn_obj_id_t enable = sn_module_add_pi(module, 1, false, "enable"); + sn_obj_id_t write_address = sn_module_add_pi(module, address_width, false, "write_address"); + sn_obj_id_t data = sn_module_add_pi(module, width, false, "write_data"); + sn_obj_id_t read_address = sn_module_add_pi(module, address_width, false, "read_address"); + sn_obj_pair_t pair = sn_module_add_mem_pair(module, width, false, depth, "mem_out", "mem_in"); + sn_module_add_mem_write(module, pair.in, clock, enable, data, write_address, "write"); + sn_obj_id_t read = sn_module_add_mem_read(module, pair.out, SN_INVALID_ID, SN_INVALID_ID, read_address, "read"); + sn_module_add_po(module, width, false, "read_data", read); + sn_design_reorder_module_topo(design, id); + return id; +} + +// A behavioral true-dual-port tile. Reads are deliberately asynchronous in +// this wrapper: frontend-imported synchronous reads are represented by the +// existing registers driven by SN_MEM_READ objects. Keeping those registers +// outside the wrapper preserves latency until a later RAM-cell emitter absorbs +// them into the physical primitive's registered read ports. +static inline sn_module_id_t sn_tech_memory_tdp_tile_module(sn_design_t* design, const sn_mem_tech_t* primitive, + uint32_t width, uint32_t depth) +{ + uint32_t address_width = sn_tech_ceil_log2(depth); + char name[128]; + int length = snprintf(name, sizeof(name), "__sn_%s_tdp_tile_%u_%u", primitive->name, width, depth); + assert(length >= 0 && (size_t)length < sizeof(name)); + sn_module_id_t existing = sn_design_find_module(design, name); + if (existing != SN_INVALID_ID) + return existing; + + sn_module_id_t id = sn_design_add_module(design, name); + sn_module_t* module = sn_design_get_module(design, id); + sn_obj_id_t clock[2], write_enable[2], address[2], data[2]; + for (uint32_t port = 0; port < 2; port++) + { + char suffix = (char)('a' + port); + char object_name[32]; + snprintf(object_name, sizeof(object_name), "clock_%c", suffix); + clock[port] = sn_module_add_pi(module, 1, false, object_name); + snprintf(object_name, sizeof(object_name), "write_enable_%c", suffix); + write_enable[port] = sn_module_add_pi(module, 1, false, object_name); + snprintf(object_name, sizeof(object_name), "address_%c", suffix); + address[port] = sn_module_add_pi(module, address_width, false, object_name); + snprintf(object_name, sizeof(object_name), "write_data_%c", suffix); + data[port] = sn_module_add_pi(module, width, false, object_name); + } + + sn_obj_pair_t pair = sn_module_add_mem_pair(module, width, false, depth, "mem_out", "mem_in"); + sn_module_add_mem_write(module, pair.in, clock[0], write_enable[0], data[0], address[0], "write_a"); + sn_module_add_mem_write(module, pair.in, clock[1], write_enable[1], data[1], address[1], "write_b"); + sn_obj_id_t read_a = sn_module_add_mem_read(module, pair.out, SN_INVALID_ID, SN_INVALID_ID, address[0], "read_a"); + sn_obj_id_t read_b = sn_module_add_mem_read(module, pair.out, SN_INVALID_ID, SN_INVALID_ID, address[1], "read_b"); + sn_module_add_po(module, width, false, "read_data_a", read_a); + sn_module_add_po(module, width, false, "read_data_b", read_b); + sn_design_reorder_module_topo(design, id); + return id; +} + +static inline bool sn_tech_assign_tdp_ports(const sn_module_t* source, sn_tech_mem_plan_t* plan) +{ + assert(source && plan && plan->read_count <= 2 && plan->write_count <= 2); + plan->port_reads[0] = plan->port_reads[1] = -1; + plan->port_writes[0] = plan->port_writes[1] = -1; + for (uint32_t write = 0; write < plan->write_count; write++) + plan->port_writes[write] = (int8_t)write; + + // Prefer sharing a physical port when the logical read and write use the + // same address. This recognizes the usual read-first HLS R/W port. + for (uint32_t read = 0; read < plan->read_count; read++) + { + sn_obj_id_t read_address = sn_obj_fanin(source, plan->reads[read], SN_MEM_READ_ADDRESS); + for (uint32_t port = 0; port < 2; port++) + { + int8_t write = plan->port_writes[port]; + if (write >= 0 && plan->port_reads[port] < 0 && + sn_obj_fanin(source, plan->writes[(uint32_t)write], SN_MEM_WRITE_ADDRESS) == read_address) + { + plan->port_reads[port] = (int8_t)read; + break; + } + } + } + for (uint32_t read = 0; read < plan->read_count; read++) + { + bool assigned = false; + for (uint32_t port = 0; port < 2; port++) + assigned |= plan->port_reads[port] == (int8_t)read; + if (assigned) + continue; + for (uint32_t port = 0; port < 2; port++) + if (plan->port_reads[port] < 0 && plan->port_writes[port] < 0) + { + plan->port_reads[port] = (int8_t)read; + assigned = true; + break; + } + if (!assigned) + return false; + } + return true; +} + +static inline sn_obj_id_t sn_tech_memory_bank_select(sn_module_t* module, sn_obj_id_t address, + uint32_t address_bits, uint32_t bank_bits, uint32_t bank) +{ + if (!bank_bits) + return sn_tech_add_uint_const(module, 1, 1); + uint32_t needed = address_bits + bank_bits; + address = sn_tech_add_zero_extend(module, address, needed > sn_obj_width(module, address) + ? needed + : sn_obj_width(module, address)); + sn_obj_id_t index = sn_module_add_slice(module, address, (int32_t)(needed - 1), (int32_t)address_bits, NULL); + sn_obj_id_t value = sn_tech_add_uint_const(module, bank_bits, bank); + sn_obj_id_t fanins[2] = {index, value}; + return sn_module_add_operator(module, SN_EQ, 1, false, 2, fanins, NULL); +} + +static inline sn_obj_id_t sn_tech_memory_local_address(sn_module_t* module, sn_obj_id_t address, + uint32_t address_bits) +{ + if (sn_obj_width(module, address) > address_bits) + return sn_module_add_slice(module, address, (int32_t)(address_bits - 1), 0, NULL); + return sn_tech_add_zero_extend(module, address, address_bits); +} + +static inline sn_obj_id_t sn_tech_map_memory(sn_module_t* module, const sn_module_t* source, + const sn_tech_mem_plan_t* plan, const sn_mem_map_options_t* options) +{ + assert(module && source && plan && options); + sn_obj_id_t clock = sn_vec_at(sn_obj_id_t, &source->copy_ids, + sn_obj_fanin(source, plan->write, SN_MEM_WRITE_CLOCK)); + sn_obj_id_t enable_old = sn_obj_fanin(source, plan->write, SN_MEM_WRITE_ENABLE); + sn_obj_id_t enable = enable_old == SN_INVALID_ID + ? sn_tech_add_uint_const(module, 1, 1) + : sn_vec_at(sn_obj_id_t, &source->copy_ids, enable_old); + sn_obj_id_t data = sn_vec_at(sn_obj_id_t, &source->copy_ids, + sn_obj_fanin(source, plan->write, SN_MEM_WRITE_DATA)); + sn_obj_id_t write_address = sn_vec_at(sn_obj_id_t, &source->copy_ids, + sn_obj_fanin(source, plan->write, SN_MEM_WRITE_ADDRESS)); + sn_obj_id_t read_address = sn_vec_at(sn_obj_id_t, &source->copy_ids, + sn_obj_fanin(source, plan->read, SN_MEM_READ_ADDRESS)); + uint32_t address_bits = sn_tech_ceil_log2(plan->tile_depth); + uint32_t bank_bits = sn_tech_ceil_log2(plan->depth_tiles); + sn_obj_id_t local_write = sn_tech_memory_local_address(module, write_address, address_bits); + sn_obj_id_t local_read = sn_tech_memory_local_address(module, read_address, address_bits); + sn_module_id_t primitive = + sn_tech_memory_tile_module(module->design, plan->primitive, plan->port_width, plan->tile_depth); + + sn_vec_t bank_enables, read_selects; + sn_vec_init(&bank_enables); + sn_vec_init(&read_selects); + for (uint32_t d = 0; d < plan->depth_tiles; d++) + { + sn_obj_id_t write_select = + sn_tech_memory_bank_select(module, write_address, address_bits, bank_bits, d); + sn_obj_id_t enable_fanins[2] = {enable, write_select}; + *sn_vec_push(sn_obj_id_t, &bank_enables) = + sn_module_add_operator(module, SN_BIT_AND, 1, false, 2, enable_fanins, NULL); + *sn_vec_push(sn_obj_id_t, &read_selects) = + sn_tech_memory_bank_select(module, read_address, address_bits, bank_bits, d); + } + + sn_vec_t width_results; + sn_vec_init(&width_results); + for (uint32_t w = 0; w < plan->width_tiles; w++) + { + uint32_t offset = w * plan->port_width; + uint32_t actual = sn_obj_width(source, plan->memory) - offset; + if (actual > plan->port_width) + actual = plan->port_width; + sn_obj_id_t write_data = sn_module_add_slice(module, data, (int32_t)(offset + actual - 1), + (int32_t)offset, NULL); + write_data = sn_tech_add_zero_extend(module, write_data, plan->port_width); + sn_vec_t banks; + sn_vec_init(&banks); + for (uint32_t d = 0; d < plan->depth_tiles; d++) + { + sn_obj_id_t tile_enable = sn_vec_at(sn_obj_id_t, &bank_enables, d); + sn_obj_id_t inputs[5] = {clock, tile_enable, local_write, write_data, local_read}; + *sn_vec_push(sn_obj_id_t, &banks) = sn_module_add_inst(module, primitive, 5, inputs, NULL, NULL); + } + sn_obj_id_t selected = sn_vec_at(sn_obj_id_t, &banks, 0); + for (uint32_t d = 1; d < plan->depth_tiles; d++) + { + sn_obj_id_t select = sn_vec_at(sn_obj_id_t, &read_selects, d); + selected = sn_module_add_mux(module, select, sn_vec_at(sn_obj_id_t, &banks, d), selected, NULL); + } + if (actual != plan->port_width) + selected = sn_module_add_slice(module, selected, (int32_t)(actual - 1), 0, NULL); + *sn_vec_push(sn_obj_id_t, &width_results) = selected; + sn_vec_destroy(&banks); + } + sn_obj_id_t result = width_results.size == 1 + ? sn_vec_at(sn_obj_id_t, &width_results, 0) + : sn_module_add_concat(module, (uint32_t)width_results.size, + sn_vec_data(sn_obj_id_t, &width_results), NULL); + sn_vec_destroy(&width_results); + sn_vec_destroy(&read_selects); + sn_vec_destroy(&bank_enables); + return result; +} + +static inline void sn_tech_map_tdp_memory(sn_module_t* module, const sn_module_t* source, + const sn_tech_mem_plan_t* plan, sn_obj_id_t results[2]) +{ + assert(module && source && plan && plan->read_count > 1 && plan->read_count <= 2 && + plan->write_count <= 2 && plan->primitive->port_mode == SN_MEM_PORT_TRUE_DUAL); + results[0] = results[1] = SN_INVALID_ID; + uint32_t address_bits = sn_tech_ceil_log2(plan->tile_depth); + uint32_t bank_bits = sn_tech_ceil_log2(plan->depth_tiles); + sn_module_id_t primitive = + sn_tech_memory_tdp_tile_module(module->design, plan->primitive, plan->port_width, plan->tile_depth); + sn_obj_id_t zero = sn_tech_add_uint_const(module, 1, 0); + sn_obj_id_t zero_data = sn_module_add_named_obj(module, SN_CONST0, plan->port_width, false, 0, NULL); + + sn_obj_id_t port_clocks[2] = {zero, zero}; + sn_obj_id_t port_addresses[2] = {SN_INVALID_ID, SN_INVALID_ID}; + sn_obj_id_t port_write_data[2] = {SN_INVALID_ID, SN_INVALID_ID}; + sn_vec_t port_enables[2], read_selects[2]; + for (uint32_t port = 0; port < 2; port++) + { + sn_vec_init(&port_enables[port]); + int8_t read_index = plan->port_reads[port]; + int8_t write_index = plan->port_writes[port]; + sn_obj_id_t old_address = read_index >= 0 + ? sn_obj_fanin(source, plan->reads[(uint32_t)read_index], SN_MEM_READ_ADDRESS) + : sn_obj_fanin(source, plan->writes[(uint32_t)write_index], + SN_MEM_WRITE_ADDRESS); + sn_obj_id_t address = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_address); + port_addresses[port] = sn_tech_memory_local_address(module, address, address_bits); + sn_obj_id_t enable = zero; + if (write_index >= 0) + { + sn_obj_id_t write = plan->writes[(uint32_t)write_index]; + port_clocks[port] = sn_vec_at(sn_obj_id_t, &source->copy_ids, + sn_obj_fanin(source, write, SN_MEM_WRITE_CLOCK)); + sn_obj_id_t old_enable = sn_obj_fanin(source, write, SN_MEM_WRITE_ENABLE); + enable = old_enable == SN_INVALID_ID ? sn_tech_add_uint_const(module, 1, 1) + : sn_vec_at(sn_obj_id_t, &source->copy_ids, old_enable); + port_write_data[port] = sn_vec_at(sn_obj_id_t, &source->copy_ids, + sn_obj_fanin(source, write, SN_MEM_WRITE_DATA)); + } + for (uint32_t depth_tile = 0; depth_tile < plan->depth_tiles; depth_tile++) + { + sn_obj_id_t bank_enable = zero; + if (write_index >= 0) + { + sn_obj_id_t bank_select = + sn_tech_memory_bank_select(module, address, address_bits, bank_bits, depth_tile); + sn_obj_id_t enable_fanins[2] = {enable, bank_select}; + bank_enable = sn_module_add_operator(module, SN_BIT_AND, 1, false, 2, enable_fanins, NULL); + } + *sn_vec_push(sn_obj_id_t, &port_enables[port]) = bank_enable; + } + } + for (uint32_t read = 0; read < plan->read_count; read++) + { + sn_vec_init(&read_selects[read]); + sn_obj_id_t address = sn_vec_at(sn_obj_id_t, &source->copy_ids, + sn_obj_fanin(source, plan->reads[read], SN_MEM_READ_ADDRESS)); + for (uint32_t depth_tile = 0; depth_tile < plan->depth_tiles; depth_tile++) + *sn_vec_push(sn_obj_id_t, &read_selects[read]) = + sn_tech_memory_bank_select(module, address, address_bits, bank_bits, depth_tile); + } + + sn_vec_t read_width_results[2]; + for (uint32_t read = 0; read < plan->read_count; read++) + sn_vec_init(&read_width_results[read]); + + for (uint32_t width_tile = 0; width_tile < plan->width_tiles; width_tile++) + { + uint32_t offset = width_tile * plan->port_width; + uint32_t actual = sn_obj_width(source, plan->memory) - offset; + if (actual > plan->port_width) + actual = plan->port_width; + sn_vec_t bank_results[2]; + for (uint32_t read = 0; read < plan->read_count; read++) + sn_vec_init(&bank_results[read]); + + for (uint32_t depth_tile = 0; depth_tile < plan->depth_tiles; depth_tile++) + { + sn_obj_id_t inputs[8]; + for (uint32_t port = 0; port < 2; port++) + { + int8_t write_index = plan->port_writes[port]; + sn_obj_id_t write_data = zero_data; + if (write_index >= 0) + { + write_data = sn_module_add_slice(module, port_write_data[port], + (int32_t)(offset + actual - 1), + (int32_t)offset, NULL); + write_data = sn_tech_add_zero_extend(module, write_data, plan->port_width); + } + inputs[4 * port + 0] = port_clocks[port]; + inputs[4 * port + 1] = sn_vec_at(sn_obj_id_t, &port_enables[port], depth_tile); + inputs[4 * port + 2] = port_addresses[port]; + inputs[4 * port + 3] = write_data; + } + sn_obj_id_t inst = sn_module_add_inst(module, primitive, 8, inputs, NULL, NULL); + for (uint32_t port = 0; port < 2; port++) + if (plan->port_reads[port] >= 0) + { + uint32_t read = (uint32_t)plan->port_reads[port]; + *sn_vec_push(sn_obj_id_t, &bank_results[read]) = sn_inst_output(module, inst, port); + } + } + + for (uint32_t read = 0; read < plan->read_count; read++) + { + sn_obj_id_t selected = sn_vec_at(sn_obj_id_t, &bank_results[read], 0); + for (uint32_t depth_tile = 1; depth_tile < plan->depth_tiles; depth_tile++) + { + sn_obj_id_t select = sn_vec_at(sn_obj_id_t, &read_selects[read], depth_tile); + selected = sn_module_add_mux(module, select, + sn_vec_at(sn_obj_id_t, &bank_results[read], depth_tile), selected, NULL); + } + if (actual != plan->port_width) + selected = sn_module_add_slice(module, selected, (int32_t)(actual - 1), 0, NULL); + *sn_vec_push(sn_obj_id_t, &read_width_results[read]) = selected; + sn_vec_destroy(&bank_results[read]); + } + } + + for (uint32_t read = 0; read < plan->read_count; read++) + { + results[read] = read_width_results[read].size == 1 + ? sn_vec_at(sn_obj_id_t, &read_width_results[read], 0) + : sn_module_add_concat(module, (uint32_t)read_width_results[read].size, + sn_vec_data(sn_obj_id_t, &read_width_results[read]), NULL); + sn_vec_destroy(&read_width_results[read]); + sn_vec_destroy(&read_selects[read]); + } + for (uint32_t port = 0; port < 2; port++) + sn_vec_destroy(&port_enables[port]); +} + +static inline sn_module_id_t sn_design_map_tech_internal(sn_design_t* design, sn_module_id_t source_module_id, + const sn_tech_t* tech, + const sn_tech_map_options_t* user_options, bool force_copy) +{ + assert(design && tech && source_module_id < design->modules.size); + sn_tech_map_options_t defaults = sn_tech_map_default_options(); + const sn_tech_map_options_t* options = user_options ? user_options : &defaults; + sn_module_t* source = sn_design_get_module(design, source_module_id); + size_t object_count = source->obj_types.size; + size_t read_count = source->type_objects[SN_MEM_READ].size; + bool* omit = (bool*)calloc(object_count, sizeof(bool)); + bool* map_mul = (bool*)calloc(object_count, sizeof(bool)); + bool* map_add = (bool*)calloc(object_count, sizeof(bool)); + sn_tech_mem_plan_t* read_plans = (sn_tech_mem_plan_t*)calloc(read_count, sizeof(sn_tech_mem_plan_t)); + assert((!object_count || omit) && (!object_count || map_mul) && (!object_count || map_add) && + (!read_count || read_plans)); + for (size_t i = 0; i < read_count; i++) + { + memset(&read_plans[i], 0, sizeof(read_plans[i])); + read_plans[i].memory = SN_INVALID_ID; + } + + if (options->map_memories) + for (size_t i = 0; i < source->type_objects[SN_MEM_OUT].size; i++) + { + sn_obj_id_t memory = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_MEM_OUT], i); + uint64_t bits = (uint64_t)sn_obj_width(source, memory) * sn_obj_mem_depth(source, memory); + if (bits < options->memory.min_memory_bits || sn_obj_mem_init_data(source, memory) != SN_INVALID_ID) + continue; + sn_obj_id_t reads_found[2] = {SN_INVALID_ID, SN_INVALID_ID}; + sn_obj_id_t writes_found[2] = {SN_INVALID_ID, SN_INVALID_ID}; + uint32_t reads = 0, writes = 0; + for (size_t j = 0; j < source->type_objects[SN_MEM_READ].size; j++) + { + sn_obj_id_t candidate = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_MEM_READ], j); + if (sn_obj_fanin(source, candidate, SN_MEM_READ_MEMORY) == memory) + { + if (reads < 2) + reads_found[reads] = candidate; + reads++; + } + } + sn_obj_id_t memory_in = sn_obj_pair_in(source, memory); + for (uint32_t j = 0; j < sn_obj_fanin_count(source, memory_in); j++) + { + sn_obj_id_t candidate = sn_obj_fanin(source, memory_in, j); + if (candidate != SN_INVALID_ID && sn_obj_type(source, candidate) == SN_MEM_WRITE) + { + if (writes < 2) + writes_found[writes] = candidate; + writes++; + } + } + if (!reads || reads > 2 || !writes || writes > 2) + continue; + // A single read with two independent writes needs true-dual-port + // collision analysis; preserve it until that case is modeled. + if (reads == 1 && writes != 1) + continue; + bool asynchronous_reads = true; + for (uint32_t read = 0; read < reads; read++) + asynchronous_reads &= sn_obj_fanin(source, reads_found[read], SN_MEM_READ_CLOCK) == SN_INVALID_ID && + sn_obj_fanin(source, reads_found[read], SN_MEM_READ_ENABLE) == SN_INVALID_ID; + if (!asynchronous_reads) + continue; + sn_tech_mem_plan_t plan = {0}; + plan.memory = memory; + plan.memory_in = memory_in; + plan.read = reads_found[0]; + plan.write = writes_found[0]; + plan.read_count = reads; + plan.write_count = writes; + for (uint32_t read = 0; read < reads; read++) + plan.reads[read] = reads_found[read]; + for (uint32_t write = 0; write < writes; write++) + plan.writes[write] = writes_found[write]; + if (!sn_tech_choose_memory_for_mode(tech, sn_obj_width(source, memory), + sn_obj_mem_depth(source, memory), &options->memory, + reads == 1 && writes == 1, &plan)) + continue; + if (reads > 1 && (!plan.primitive || plan.primitive->port_mode != SN_MEM_PORT_TRUE_DUAL || + !sn_tech_assign_tdp_ports(source, &plan))) + continue; + for (uint32_t read = 0; read < reads; read++) + read_plans[sn_obj_type_id(source, reads_found[read])] = plan; + omit[memory] = omit[memory_in] = true; + for (uint32_t write = 0; write < writes; write++) + omit[writes_found[write]] = true; + } + + if (options->map_multipliers) + { + assert(tech->dsp_count); + const sn_dsp_tech_t* dsp = &tech->dsps[0]; + bool mapping_failed = false; + for (size_t i = 0; i < source->type_objects[SN_MUL].size; i++) + { + sn_obj_id_t mul = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_MUL], i); + sn_obj_id_t a = sn_obj_fanin(source, mul, 0); + sn_obj_id_t b = sn_obj_fanin(source, mul, 1); + uint32_t count = sn_tech_dsp_box_count(source, dsp, &options->dsp, a, b); + map_mul[mul] = count != UINT32_MAX && + (!options->dsp.max_dsps_per_multiply || + count <= options->dsp.max_dsps_per_multiply); + mapping_failed |= !map_mul[mul] && !options->dsp.allow_soft_fallback; + } + if (mapping_failed) + { + free(omit); + free(map_mul); + free(map_add); + free(read_plans); + return SN_INVALID_ID; + } + } + + if (options->map_adders) + { + assert(tech->carry_count); + const sn_carry_tech_t* carry = &tech->carries[0]; + for (sn_obj_id_t object = 0; object < object_count; object++) + map_add[object] = sn_add_tech_supports(carry, &options->add, sn_obj_type(source, object), + sn_obj_width(source, object)); + } + + bool changed = false; + for (sn_obj_id_t object = 0; object < object_count && !changed; object++) + { + const sn_tech_mem_plan_t* read_plan = sn_tech_read_plan(source, read_plans, object); + changed = omit[object] || map_mul[object] || map_add[object] || + (read_plan && read_plan->memory != SN_INVALID_ID); + } + if (!changed && !force_copy) + { + free(omit); + free(map_mul); + free(map_add); + free(read_plans); + return source_module_id; + } + + const char* source_name = sn_name_get(&design->names, source->name); + char mapped_name[256]; + int length = snprintf(mapped_name, sizeof(mapped_name), "%s_techmap", source_name); + assert(length >= 0 && (size_t)length < sizeof(mapped_name)); + for (uint32_t suffix = 1; sn_design_find_module(design, mapped_name) != SN_INVALID_ID; suffix++) + { + length = snprintf(mapped_name, sizeof(mapped_name), "%s_techmap_%u", source_name, suffix); + assert(length >= 0 && (size_t)length < sizeof(mapped_name)); + } + sn_module_id_t mapped_id = sn_design_add_module(design, mapped_name); + sn_module_t* mapped = sn_design_get_module(design, mapped_id); + sn_vec_t order = sn_module_topo_order(source); + sn_vec_resize(sn_obj_id_t, &source->copy_ids, object_count); + for (size_t i = 0; i < object_count; i++) + sn_vec_at(sn_obj_id_t, &source->copy_ids, i) = SN_INVALID_ID; + + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + if (omit[old_object]) + continue; + bool special_mul = map_mul[old_object]; + const sn_tech_mem_plan_t* read_plan = sn_tech_read_plan(source, read_plans, old_object); + bool special_mem = read_plan && read_plan->memory != SN_INVALID_ID; + bool special_add = map_add[old_object]; + sn_obj_id_t new_object = special_mul || special_mem || special_add + ? sn_module_add_obj(mapped, SN_BUF, sn_obj_width(source, old_object), + sn_obj_is_signed(source, old_object), 1, + sn_obj_name_id(source, old_object)) + : sn_module_dup_obj_skeleton(mapped, source, old_object); + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object) = new_object; + } + + // Topological traversal can order paired IN objects differently from their + // source-order OUT objects. Restore matching type IDs before metadata and + // fanins are copied so state pairs remain paired in the provisional graph. + sn_module_clean_rebuild_pair_ids(mapped, source, SN_REG_OUT, SN_REG_IN); + sn_module_clean_rebuild_pair_ids(mapped, source, SN_MEM_OUT, SN_MEM_IN); + sn_module_clean_rebuild_pair_ids(mapped, source, SN_LOOP_OUT, SN_LOOP_IN); + + const sn_dsp_tech_t* dsp = tech->dsp_count ? &tech->dsps[0] : NULL; + const sn_carry_tech_t* carry = tech->carry_count ? &tech->carries[0] : NULL; + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_obj_id_t placeholder = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + const sn_tech_mem_plan_t* read_plan = sn_tech_read_plan(source, read_plans, old_object); + if (placeholder == SN_INVALID_ID) + continue; + if (map_mul[old_object]) + { + assert(dsp && sn_obj_fanin_count(source, old_object) == 2); + sn_obj_id_t a = sn_vec_at(sn_obj_id_t, &source->copy_ids, sn_obj_fanin(source, old_object, 0)); + sn_obj_id_t b = sn_vec_at(sn_obj_id_t, &source->copy_ids, sn_obj_fanin(source, old_object, 1)); + sn_obj_id_t result = sn_tech_map_multiplier(mapped, dsp, &options->dsp, a, b, + sn_obj_width(source, old_object), + sn_obj_is_signed(source, old_object)); + sn_obj_connect(mapped, placeholder, 0, result); + } + else if (map_add[old_object]) + { + assert(carry && sn_obj_fanin_count(source, old_object) == 2); + sn_obj_id_t a = sn_vec_at(sn_obj_id_t, &source->copy_ids, sn_obj_fanin(source, old_object, 0)); + sn_obj_id_t b = sn_vec_at(sn_obj_id_t, &source->copy_ids, sn_obj_fanin(source, old_object, 1)); + const char* name = sn_obj_name_id(source, old_object) == SN_INVALID_ID ? NULL + : sn_obj_name(source, old_object); + sn_obj_id_t result = sn_add_map_carry_chain( + mapped, carry, sn_obj_type(source, old_object), a, b, sn_obj_width(source, old_object), + sn_obj_is_signed(source, old_object), name); + sn_obj_connect(mapped, placeholder, 0, result); + } + else if (read_plan && read_plan->memory != SN_INVALID_ID) + { + const sn_tech_mem_plan_t* plan = read_plan; + if (plan->read_count == 1) + { + sn_obj_id_t result = sn_tech_map_memory(mapped, source, plan, &options->memory); + sn_obj_connect(mapped, placeholder, 0, result); + } + else if (old_object == plan->reads[0]) + { + sn_obj_id_t results[2]; + sn_tech_map_tdp_memory(mapped, source, plan, results); + for (uint32_t read = 0; read < plan->read_count; read++) + { + sn_obj_id_t read_placeholder = + sn_vec_at(sn_obj_id_t, &source->copy_ids, plan->reads[read]); + sn_obj_connect(mapped, read_placeholder, 0, results[read]); + } + } + } + } + + for (size_t i = 0; i < order.size; i++) + { + sn_obj_id_t old_object = sn_vec_at(sn_obj_id_t, &order, i); + sn_obj_id_t new_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, old_object); + const sn_tech_mem_plan_t* read_plan = sn_tech_read_plan(source, read_plans, old_object); + bool special = map_mul[old_object] || map_add[old_object] || + (read_plan && read_plan->memory != SN_INVALID_ID); + if (new_object == SN_INVALID_ID || special) + continue; + sn_module_dup_obj_metadata(mapped, sn_obj_type_id(mapped, new_object), source, old_object); + if (sn_obj_type(source, old_object) == SN_FAN) + { + sn_obj_id_t old_inst = sn_fan_inst_id(source, old_object); + sn_vec_at(sn_obj_id_t, &mapped->fan_insts, sn_obj_type_id(mapped, new_object)) = + sn_vec_at(sn_obj_id_t, &source->copy_ids, old_inst); + } + for (uint32_t j = 0; j < sn_obj_fanin_count(source, old_object); j++) + { + sn_obj_id_t old_fanin = sn_obj_fanin(source, old_object, j); + sn_obj_id_t new_fanin = old_fanin == SN_INVALID_ID + ? SN_INVALID_ID + : sn_vec_at(sn_obj_id_t, &source->copy_ids, old_fanin); + assert(new_fanin != SN_INVALID_ID || old_fanin == SN_INVALID_ID); + sn_obj_connect(mapped, new_object, j, new_fanin); + } + } + + // Finalize the provisional graph and compose its reorder map with the + // persistent source-to-mapped copy array. + char temporary_name[96]; + uint32_t temporary_suffix = 0; + do + { + length = snprintf(temporary_name, sizeof(temporary_name), "__sn_tech_topo_%u_%u", mapped_id, + temporary_suffix++); + assert(length >= 0 && (size_t)length < sizeof(temporary_name) && temporary_suffix != 0); + } while (sn_name_find(&design->names, temporary_name) != SN_INVALID_ID); + sn_module_id_t final_id = sn_design_dup_module_topo(design, mapped_id, temporary_name); + assert(final_id + 1 == design->modules.size); + sn_module_t* provisional = sn_design_get_module(design, mapped_id); + sn_module_t* final_module = sn_design_get_module(design, final_id); + sn_name_id_t temporary_name_id = final_module->name; + for (size_t i = 0; i < source->copy_ids.size; i++) + { + sn_obj_id_t provisional_object = sn_vec_at(sn_obj_id_t, &source->copy_ids, i); + if (provisional_object != SN_INVALID_ID) + sn_vec_at(sn_obj_id_t, &source->copy_ids, i) = + sn_vec_at(sn_obj_id_t, &provisional->copy_ids, provisional_object); + } + sn_name_id_t mapped_name_id = provisional->name; + bool interface_locked = provisional->interface_locked; + sn_design_invalidate_copies_to_module_except(design, mapped_id, source); + sn_module_destroy(provisional); + free(provisional); + final_module->id = mapped_id; + final_module->name = mapped_name_id; + final_module->interface_locked = interface_locked; + sn_vec_at(sn_module_t*, &design->modules, mapped_id) = final_module; + design->modules.size--; + sn_name_remove_last(&design->names, temporary_name_id); + source->copy_module = mapped_id; + assert(sn_module_is_topo(final_module)); + + free(omit); + free(map_mul); + free(map_add); + free(read_plans); + sn_vec_destroy(&order); + return mapped_id; +} + +static inline sn_module_id_t sn_design_map_tech(sn_design_t* design, sn_module_id_t source_module_id, + const sn_tech_t* tech, const sn_tech_map_options_t* user_options) +{ + return sn_design_map_tech_internal(design, source_module_id, tech, user_options, false); +} + +typedef struct sn_tech_count_frame_t +{ + sn_module_id_t module; + size_t next_inst; +} sn_tech_count_frame_t; + +static inline void sn_tech_count_hierarchy_instances(const sn_design_t* design, sn_module_id_t root, + sn_tech_map_stats_t* stats) +{ + size_t module_count = design->modules.size; + uint8_t* states = (uint8_t*)calloc(module_count, 1); + sn_tech_map_stats_t* cached = (sn_tech_map_stats_t*)calloc(module_count, sizeof(sn_tech_map_stats_t)); + sn_vec_t stack; + assert(design && root < module_count && stats && states && cached); + sn_vec_init(&stack); + states[root] = 1; + sn_tech_count_frame_t* first = sn_vec_push(sn_tech_count_frame_t, &stack); + first->module = root; + first->next_inst = 0; + while (stack.size) + { + sn_tech_count_frame_t* frame = &sn_vec_at(sn_tech_count_frame_t, &stack, stack.size - 1); + const sn_module_t* module = sn_design_get_module_const(design, frame->module); + if (frame->next_inst < module->inst_modules.size) + { + sn_module_id_t child_id = sn_vec_at(sn_module_id_t, &module->inst_modules, frame->next_inst++); + const sn_module_t* child = sn_design_get_module_const(design, child_id); + if (!sn_module_is_technology_primitive(child) && states[child_id] == 0) + { + states[child_id] = 1; + sn_tech_count_frame_t* child_frame = sn_vec_push(sn_tech_count_frame_t, &stack); + child_frame->module = child_id; + child_frame->next_inst = 0; + } + continue; + } + sn_tech_map_stats_t total = {0}; + for (size_t i = 0; i < module->inst_modules.size; i++) + { + sn_module_id_t child_id = sn_vec_at(sn_module_id_t, &module->inst_modules, i); + const sn_module_t* child = sn_design_get_module_const(design, child_id); + const char* name = sn_name_get(&design->names, child->name); + if (strncmp(name, "__sn_RAM", 8) == 0 || strncmp(name, "__sn_URAM", 9) == 0) + total.mem_insts++; + else if (strncmp(name, "__sn_DSP", 8) == 0) + total.dsp_insts++; + else if (strncmp(name, "__sn_CARRY", 10) == 0) + total.carry_insts++; + else + { + total.mem_insts += cached[child_id].mem_insts; + total.dsp_insts += cached[child_id].dsp_insts; + total.carry_insts += cached[child_id].carry_insts; + } + } + cached[frame->module] = total; + states[frame->module] = 2; + stack.size--; + } + *stats = cached[root]; + sn_vec_destroy(&stack); + free(cached); + free(states); +} + +typedef struct sn_tech_hierarchy_frame_t +{ + sn_module_id_t module; + size_t next_inst; +} sn_tech_hierarchy_frame_t; + +// Visits the reachable hierarchy bottom-up. A module is copied only when it contains a primitive selected by this +// pass or when one of its child definitions changed and its instance reference must be redirected. Untouched +// subhierarchies retain their original module IDs. The original definitions remain in the design. +static inline sn_module_id_t sn_design_map_tech_hierarchy(sn_design_t* design, sn_module_id_t top_id, + const sn_tech_t* tech, + const sn_tech_map_options_t* options, + sn_tech_map_stats_t* returned_stats) +{ + assert(design && top_id < design->modules.size && tech && options); + size_t original_count = design->modules.size; + sn_module_id_t* replacements = (sn_module_id_t*)malloc(sizeof(sn_module_id_t) * original_count); + uint8_t* states = (uint8_t*)calloc(original_count, sizeof(uint8_t)); + sn_vec_t stack, postorder; + assert(replacements && states); + for (sn_module_id_t id = 0; id < original_count; id++) + replacements[id] = id; + sn_vec_init(&stack); + sn_vec_init(&postorder); + states[top_id] = 1; + sn_tech_hierarchy_frame_t* root = sn_vec_push(sn_tech_hierarchy_frame_t, &stack); + root->module = top_id; + root->next_inst = 0; + while (stack.size) + { + sn_tech_hierarchy_frame_t* frame = &sn_vec_at(sn_tech_hierarchy_frame_t, &stack, stack.size - 1); + const sn_module_t* module = sn_design_get_module_const(design, frame->module); + if (frame->next_inst < module->inst_modules.size) + { + sn_module_id_t child = sn_vec_at(sn_module_id_t, &module->inst_modules, frame->next_inst++); + assert(child < original_count); + assert(states[child] != 1 && "recursive module instantiation is unsupported"); + if (states[child] == 0) + { + states[child] = 1; + sn_tech_hierarchy_frame_t* child_frame = sn_vec_push(sn_tech_hierarchy_frame_t, &stack); + child_frame->module = child; + child_frame->next_inst = 0; + } + continue; + } + states[frame->module] = 2; + *sn_vec_push(sn_module_id_t, &postorder) = frame->module; + stack.size--; + } + + for (size_t order = 0; order < postorder.size; order++) + { + sn_module_id_t id = sn_vec_at(sn_module_id_t, &postorder, order); + const sn_module_t* module = sn_design_get_module_const(design, id); + if (sn_module_is_technology_primitive(module)) + continue; + bool child_changed = false; + for (size_t i = 0; i < module->inst_modules.size; i++) + { + sn_module_id_t child = sn_vec_at(sn_module_id_t, &module->inst_modules, i); + child_changed |= replacements[child] != child; + } + replacements[id] = sn_design_map_tech_internal(design, id, tech, options, child_changed); + if (replacements[id] == SN_INVALID_ID) + { + for (sn_module_id_t appended = (sn_module_id_t)original_count; + appended < design->modules.size; appended++) + { + sn_module_t* discarded = sn_design_get_module(design, appended); + sn_module_destroy(discarded); + free(discarded); + } + design->modules.size = original_count; + for (sn_module_id_t reachable = 0; reachable < original_count; reachable++) + if (states[reachable]) + { + sn_module_t* original = sn_design_get_module(design, reachable); + sn_vec_destroy(&original->copy_ids); + sn_vec_init(&original->copy_ids); + original->copy_module = SN_INVALID_ID; + } + sn_vec_destroy(&postorder); + sn_vec_destroy(&stack); + free(states); + free(replacements); + if (returned_stats) + memset(returned_stats, 0, sizeof(*returned_stats)); + return SN_INVALID_ID; + } + if (replacements[id] == id) + continue; + sn_module_t* mapped = sn_design_get_module(design, replacements[id]); + for (size_t i = 0; i < mapped->inst_modules.size; i++) + { + sn_module_id_t child = sn_vec_at(sn_module_id_t, &mapped->inst_modules, i); + if (child < original_count) + sn_vec_at(sn_module_id_t, &mapped->inst_modules, i) = replacements[child]; + } + } + + sn_tech_map_stats_t stats = {0}; + sn_module_id_t result = replacements[top_id]; + sn_tech_count_hierarchy_instances(design, result, &stats); + sn_vec_destroy(&postorder); + sn_vec_destroy(&stack); + free(states); + free(replacements); + if (returned_stats) + *returned_stats = stats; + return result; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snMiniAig.h b/src/base/sn/snMiniAig.h new file mode 100644 index 000000000..ec7ab5d72 --- /dev/null +++ b/src/base/sn/snMiniAig.h @@ -0,0 +1,143 @@ +/**CFile**************************************************************** + + FileName [snMiniAig.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Reconstruction of SN logic from an unmapped MiniAIG network.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snMiniAig.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__base__sn__snMiniAig_h +#define ABC__base__sn__snMiniAig_h + +#include "snMiniLut.h" + +ABC_NAMESPACE_HEADER_START + +static inline sn_obj_id_t sn_aig_lit_object(sn_module_t* module, Mini_Aig_t* aig, const sn_obj_id_t* objects, int lit) +{ + int variable = Mini_AigLit2Var(lit); + assert(variable >= 0 && variable < Mini_AigNodeNum(aig)); + sn_obj_id_t object = objects[variable]; + assert(object != SN_INVALID_ID); + if (!Mini_AigLitIsCompl(lit)) + return object; + if (variable == 0) + { + uint32_t one = 1; + return sn_module_add_const(module, 1, false, &one, "aig_const1"); + } + return sn_module_add_operator(module, SN_BIT_NOT, 1, false, 1, &object, "aig_inv"); +} + +// Reconstructs an unmapped combinational MiniAIG as explicit one-bit SN_BIT_AND and SN_BIT_NOT objects. The MiniAIG +// CI/CO order is matched positionally against the boundary recorded by @blast. Register endpoints are reconnected by +// the shared boundary reconstruction stage; RAM/DSP endpoints are rejected by the command until they are supported. +static inline sn_module_id_t sn_design_add_aig_module(sn_design_t* design, sn_module_id_t source_top_id, + Mini_Aig_t* aig, const sn_blast_boundary_t* boundary, + const char* module_name) +{ + assert(design && source_top_id < design->modules.size && aig && boundary && module_name); + assert(Mini_AigRegNum(aig) == 0); + assert((size_t)Mini_AigPiNum(aig) == boundary->cis.size); + assert((size_t)Mini_AigPoNum(aig) == boundary->cos.size); + + const sn_module_t* source = sn_design_get_module_const(design, source_top_id); + sn_module_id_t result_id = sn_design_add_module(design, module_name); + sn_module_t* result = sn_design_get_module(design, result_id); + sn_obj_id_t* top_inputs = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * source->obj_types.size); + sn_obj_id_t* objects = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * Mini_AigNodeNum(aig)); + sn_obj_id_t* drivers = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * boundary->cos.size); + sn_boundary_regs_t regs; + assert(top_inputs && objects && drivers); + for (size_t i = 0; i < source->obj_types.size; i++) + top_inputs[i] = SN_INVALID_ID; + for (int i = 0; i < Mini_AigNodeNum(aig); i++) + objects[i] = SN_INVALID_ID; + + for (size_t i = 0; i < source->type_objects[SN_PI].size; i++) + { + sn_obj_id_t old_pi = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PI], i); + top_inputs[old_pi] = sn_module_add_pi(result, sn_obj_width(source, old_pi), sn_obj_is_signed(source, old_pi), + sn_obj_name(source, old_pi)); + } + sn_boundary_regs_init(®s, design, boundary, result, top_inputs); + uint32_t zero = 0; + objects[0] = sn_module_add_const(result, 1, false, &zero, "aig_const0"); + + uint32_t ci_index = 0; + int mini_object; + Mini_AigForEachPi(aig, mini_object) + { + sn_blast_boundary_bit_t bit = sn_vec_at(sn_blast_boundary_bit_t, &boundary->cis, ci_index++); + if (bit.kind == SN_BLAST_BOUNDARY_TOP_PI) + { + assert(bit.signal.occurrence == 0 && top_inputs[bit.signal.object] != SN_INVALID_ID); + objects[mini_object] = sn_module_add_slice(result, top_inputs[bit.signal.object], (int32_t)bit.signal.bit, + (int32_t)bit.signal.bit, "aig_pi_bit"); + } + else if (bit.kind == SN_BLAST_BOUNDARY_REG_OUTPUT) + objects[mini_object] = sn_boundary_reg_output_bit(®s, bit.owner, bit.signal.bit); + else if (bit.kind == SN_BLAST_BOUNDARY_LOOP_OUTPUT) + objects[mini_object] = sn_boundary_loop_output_bit(®s, bit.owner, bit.signal.bit); + else if (bit.kind == SN_BLAST_BOUNDARY_PRIMITIVE_OUTPUT) + objects[mini_object] = sn_boundary_primitive_output_bit(®s, bit.owner, bit.port, bit.signal.bit); + else + assert(false); + } + assert(ci_index == boundary->cis.size); + + Mini_AigForEachAnd(aig, mini_object) + { + sn_obj_id_t fanins[2] = { + sn_aig_lit_object(result, aig, objects, Mini_AigNodeFanin0(aig, mini_object)), + sn_aig_lit_object(result, aig, objects, Mini_AigNodeFanin1(aig, mini_object))}; + objects[mini_object] = sn_module_add_operator(result, SN_BIT_AND, 1, false, 2, fanins, "aig_and"); + } + + uint32_t co_index = 0; + Mini_AigForEachPo(aig, mini_object) + drivers[co_index++] = sn_aig_lit_object(result, aig, objects, Mini_AigNodeFanin0(aig, mini_object)); + assert(co_index <= boundary->cos.size); + + co_index = 0; + for (size_t i = 0; i < source->type_objects[SN_PO].size; i++) + { + sn_obj_id_t old_po = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PO], i); + uint32_t width = sn_obj_width(source, old_po); + for (uint32_t bit = 0; bit < width; bit++) + { + sn_blast_boundary_bit_t endpoint = sn_vec_at(sn_blast_boundary_bit_t, &boundary->cos, co_index + bit); + assert(endpoint.kind == SN_BLAST_BOUNDARY_TOP_PO && endpoint.port == i && endpoint.signal.bit == bit); + } + sn_obj_id_t driver = sn_lut_pack_bits(result, drivers + co_index, width, "aig_po_word"); + sn_module_add_po(result, width, sn_obj_is_signed(source, old_po), sn_obj_name(source, old_po), driver); + co_index += width; + } + assert(co_index <= boundary->cos.size); + sn_boundary_regs_finish(®s, drivers); + result = sn_design_get_module(design, result_id); + + free(drivers); + free(objects); + free(top_inputs); + if (!sn_module_is_topo(result)) + sn_design_reorder_module_topo(design, result_id); + assert(sn_module_is_topo(sn_design_get_module_const(design, result_id))); + return result_id; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snMiniGate.h b/src/base/sn/snMiniGate.h new file mode 100644 index 000000000..f122958a0 --- /dev/null +++ b/src/base/sn/snMiniGate.h @@ -0,0 +1,215 @@ +/**CFile**************************************************************** + + FileName [snMiniGate.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Reconstruction of technology-mapped SN gates from mini-mapping data.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snMiniGate.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__base__sn__snMiniGate_h +#define ABC__base__sn__snMiniGate_h + +#include "snMiniLut.h" + +ABC_NAMESPACE_HEADER_START + +typedef uint32_t (*sn_gate_id_resolver_t)(void* context, const char* gate_name); + +// Reconstructs ABC's mini-mapping array as one-bit SN_GATE objects. Mini-mapping numbers CIs first and mapped nodes +// afterward in topological order. Gate names stored at the end of the array are resolved into the current library's +// stable gate IDs; the name is also retained as the SN object name for structural Verilog emission. +static inline sn_module_id_t sn_design_add_gate_module(sn_design_t* design, sn_module_id_t source_top_id, + const int* mapping, size_t mapping_count, + const sn_blast_boundary_t* boundary, + sn_gate_id_resolver_t resolver, void* resolver_context, + const char* module_name) +{ + assert(design && source_top_id < design->modules.size && mapping && boundary && resolver && module_name); + if (mapping_count < 4 || mapping[0] < 0 || mapping[1] < 0 || mapping[2] < 0 || mapping[3] < 0) + return SN_INVALID_ID; + uint32_t ci_count = (uint32_t)mapping[0]; + uint32_t co_count = (uint32_t)mapping[1]; + uint32_t node_count = (uint32_t)mapping[2]; + uint32_t reg_count = (uint32_t)mapping[3]; + if (reg_count != 0 || ci_count != boundary->cis.size || co_count != boundary->cos.size || + node_count > UINT32_MAX - ci_count) + return SN_INVALID_ID; + + // Validate the complete structural prefix and resolve all bounded gate-name strings before mutating the design. + // A changed genlib or malformed mini-mapping can otherwise leave a partially constructed module behind. + size_t position = 4; + uint32_t* fanin_counts = node_count ? (uint32_t*)malloc(sizeof(uint32_t) * node_count) : NULL; + const uint32_t** fanin_indices = + node_count ? (const uint32_t**)malloc(sizeof(uint32_t*) * node_count) : NULL; + uint32_t* gate_ids = node_count ? (uint32_t*)malloc(sizeof(uint32_t) * node_count) : NULL; + bool valid = true; + assert((fanin_counts && fanin_indices && gate_ids) || node_count == 0); + for (uint32_t i = 0; valid && i < node_count; i++) + { + if (position >= mapping_count || mapping[position] < 0) + { + valid = false; + break; + } + uint32_t count = (uint32_t)mapping[position++]; + if (count > mapping_count - position) + { + valid = false; + break; + } + fanin_counts[i] = count; + fanin_indices[i] = (const uint32_t*)(mapping + position); + for (uint32_t k = 0; k < count; k++) + if (mapping[position + k] < 0 || (uint32_t)mapping[position + k] >= ci_count + i) + valid = false; + position += count; + } + if (valid && co_count > mapping_count - position) + valid = false; + const uint32_t* output_indices = valid ? (const uint32_t*)(mapping + position) : NULL; + for (uint32_t i = 0; valid && i < co_count; i++) + if (mapping[position + i] < 0 || (uint32_t)mapping[position + i] >= ci_count + node_count) + valid = false; + if (valid) + position += co_count; + const char* gate_names = valid ? (const char*)(mapping + position) : NULL; + const char* gate_name = gate_names; + size_t name_bytes = valid ? (mapping_count - position) * sizeof(int) : 0; + for (uint32_t i = 0; valid && i < node_count; i++) + { + const char* end = (const char*)memchr(gate_name, '\0', name_bytes); + if (!end || end == gate_name) + { + valid = false; + break; + } + gate_ids[i] = resolver(resolver_context, gate_name); + if (gate_ids[i] == SN_INVALID_ID) + { + valid = false; + break; + } + size_t length = (size_t)(end - gate_name) + 1; + gate_name += length; + name_bytes -= length; + } + if (!valid) + { + free(gate_ids); + free(fanin_indices); + free(fanin_counts); + return SN_INVALID_ID; + } + + const sn_module_t* source = sn_design_get_module_const(design, source_top_id); + sn_module_id_t result_id = sn_design_add_module(design, module_name); + sn_module_t* result = sn_design_get_module(design, result_id); + sn_obj_id_t* top_inputs = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * source->obj_types.size); + sn_obj_id_t* objects = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * (ci_count + node_count)); + sn_boundary_regs_t regs; + assert(top_inputs && objects); + for (size_t i = 0; i < source->obj_types.size; i++) + top_inputs[i] = SN_INVALID_ID; + for (uint32_t i = 0; i < ci_count + node_count; i++) + objects[i] = SN_INVALID_ID; + + for (size_t i = 0; i < source->type_objects[SN_PI].size; i++) + { + sn_obj_id_t old_pi = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PI], i); + top_inputs[old_pi] = sn_module_add_pi(result, sn_obj_width(source, old_pi), sn_obj_is_signed(source, old_pi), + sn_obj_name(source, old_pi)); + } + sn_boundary_regs_init(®s, design, boundary, result, top_inputs); + for (uint32_t i = 0; i < ci_count; i++) + { + sn_blast_boundary_bit_t bit = sn_vec_at(sn_blast_boundary_bit_t, &boundary->cis, i); + if (bit.kind == SN_BLAST_BOUNDARY_TOP_PI) + { + assert(bit.signal.occurrence == 0 && top_inputs[bit.signal.object] != SN_INVALID_ID); + objects[i] = sn_module_add_slice(result, top_inputs[bit.signal.object], (int32_t)bit.signal.bit, + (int32_t)bit.signal.bit, "gate_pi_bit"); + } + else if (bit.kind == SN_BLAST_BOUNDARY_REG_OUTPUT) + objects[i] = sn_boundary_reg_output_bit(®s, bit.owner, bit.signal.bit); + else if (bit.kind == SN_BLAST_BOUNDARY_LOOP_OUTPUT) + objects[i] = sn_boundary_loop_output_bit(®s, bit.owner, bit.signal.bit); + else if (bit.kind == SN_BLAST_BOUNDARY_PRIMITIVE_OUTPUT) + objects[i] = sn_boundary_primitive_output_bit(®s, bit.owner, bit.port, bit.signal.bit); + else + assert(false); + } + + gate_name = gate_names; + + for (uint32_t i = 0; i < node_count; i++) + { + uint32_t count = fanin_counts[i]; + sn_obj_id_t* fanins = count ? (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * count) : NULL; + assert(fanins || count == 0); + for (uint32_t k = 0; k < count; k++) + { + uint32_t fanin = fanin_indices[i][k]; + assert(fanin < ci_count + i && objects[fanin] != SN_INVALID_ID); + fanins[k] = objects[fanin]; + } + objects[ci_count + i] = sn_module_add_gate(result, count, fanins, gate_ids[i], gate_name); + free(fanins); + gate_name += strlen(gate_name) + 1; + } + + uint32_t co_index = 0; + for (size_t i = 0; i < source->type_objects[SN_PO].size; i++) + { + sn_obj_id_t old_po = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PO], i); + uint32_t width = sn_obj_width(source, old_po); + sn_obj_id_t* bits = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * width); + assert(bits); + for (uint32_t bit = 0; bit < width; bit++) + { + sn_blast_boundary_bit_t endpoint = sn_vec_at(sn_blast_boundary_bit_t, &boundary->cos, co_index); + assert(endpoint.kind == SN_BLAST_BOUNDARY_TOP_PO && endpoint.port == i && endpoint.signal.bit == bit); + assert(output_indices[co_index] < ci_count + node_count); + bits[bit] = objects[output_indices[co_index++]]; + } + sn_obj_id_t driver = sn_lut_pack_bits(result, bits, width, "gate_po_word"); + sn_module_add_po(result, width, sn_obj_is_signed(source, old_po), sn_obj_name(source, old_po), driver); + free(bits); + } + assert(co_index <= co_count); + sn_obj_id_t* co_drivers = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * co_count); + assert(co_drivers || co_count == 0); + for (uint32_t i = 0; i < co_count; i++) + { + assert(output_indices[i] < ci_count + node_count); + co_drivers[i] = objects[output_indices[i]]; + } + sn_boundary_regs_finish(®s, co_drivers); + result = sn_design_get_module(design, result_id); + + free(co_drivers); + free(fanin_indices); + free(fanin_counts); + free(gate_ids); + free(objects); + free(top_inputs); + if (!sn_module_is_topo(result)) + sn_design_reorder_module_topo(design, result_id); + assert(sn_module_is_topo(sn_design_get_module_const(design, result_id))); + return result_id; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snMiniLut.h b/src/base/sn/snMiniLut.h new file mode 100644 index 000000000..b26bcbc62 --- /dev/null +++ b/src/base/sn/snMiniLut.h @@ -0,0 +1,407 @@ +/**CFile**************************************************************** + + FileName [snMiniLut.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Analysis and reconstruction of SN LUTs from MiniLUT networks.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snMiniLut.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_MINI_LUT_H +#define SN_MINI_LUT_H + +// Utilities for validating and analyzing the MiniLUT files written by ABC's +// "&write -l" command. + +#include "snBoundary.h" +#include "aig/miniaig/minilut.h" + +#include +#include +#include + +ABC_NAMESPACE_HEADER_START + +typedef struct sn_lut_stats_t +{ + uint32_t pi_count; + uint32_t po_count; + uint32_t register_count; + uint32_t lut_count; + uint32_t lut_size; + uint32_t lut_levels; + uint32_t top_output_levels; + uint32_t register_control_levels; + uint32_t memory_input_levels; + uint32_t primitive_input_levels; + uint32_t loop_input_levels; + uint32_t register_input_levels; +} sn_lut_stats_t; + +static inline uint32_t sn_lut_max_u32(uint32_t a, uint32_t b) +{ + return a > b ? a : b; +} + +static inline Mini_Lut_t* sn_lut_load(const char* file_name) +{ + if (!file_name) + return NULL; + FILE* file = fopen(file_name, "rb"); + if (!file) + return NULL; + int32_t node_count = 0, register_count = 0, lut_size = 0; + bool valid = fread(&node_count, sizeof(node_count), 1, file) == 1 && + fread(®ister_count, sizeof(register_count), 1, file) == 1 && + fread(&lut_size, sizeof(lut_size), 1, file) == 1; + uint64_t array_count = 0, truth_count = 0; + if (valid && node_count >= 2 && register_count >= 0 && register_count <= node_count && lut_size >= 2 && + lut_size <= 16) + { + array_count = (uint64_t)(uint32_t)node_count * (uint32_t)lut_size; + truth_count = (uint64_t)(uint32_t)node_count * (uint32_t)Mini_LutWordNum(lut_size); + valid = array_count <= SIZE_MAX / sizeof(int) && truth_count <= SIZE_MAX / sizeof(unsigned); + } + else + valid = false; + if (valid) + { + uint64_t payload_bytes = (array_count + truth_count) * sizeof(uint32_t); +#if defined(_WIN32) + __int64 position = _ftelli64(file); + valid = position >= 0 && _fseeki64(file, 0, SEEK_END) == 0; + __int64 end = valid ? _ftelli64(file) : -1; + valid = end >= position && (uint64_t)(end - position) == payload_bytes && + _fseeki64(file, position, SEEK_SET) == 0; +#else + long position = ftell(file); + valid = position >= 0 && fseek(file, 0, SEEK_END) == 0; + long end = valid ? ftell(file) : -1; + valid = end >= position && (uint64_t)(end - position) == payload_bytes && fseek(file, position, SEEK_SET) == 0; +#endif + } + Mini_Lut_t* lut = valid ? (Mini_Lut_t*)calloc(1, sizeof(Mini_Lut_t)) : NULL; + if (lut) + { + lut->nSize = lut->nCap = node_count; + lut->nRegs = register_count; + lut->LutSize = lut_size; + lut->pArray = (int*)malloc((size_t)array_count * sizeof(int)); + lut->pTruths = (unsigned*)malloc((size_t)truth_count * sizeof(unsigned)); + if (!lut->pArray || !lut->pTruths) + valid = false; + else + valid = fread(lut->pArray, sizeof(int), (size_t)array_count, file) == array_count && + fread(lut->pTruths, sizeof(unsigned), (size_t)truth_count, file) == truth_count && + fgetc(file) == EOF && !ferror(file); + } + if (fclose(file) != 0) + valid = false; + if (!valid || !lut) + { + if (lut) + Mini_LutStop(lut); + return NULL; + } + for (int object = 2; object < node_count; object++) + { + int* fanins = lut->pArray + (size_t)object * lut_size; + if (fanins[0] == MINI_LUT_NULL) + { + for (int i = 1; i < lut_size; i++) + valid &= fanins[i] == MINI_LUT_NULL; + continue; + } + if (fanins[0] < 0 || fanins[0] >= object) + valid = false; + else if (fanins[0] >= 2) + { + int* source = lut->pArray + (size_t)fanins[0] * lut_size; + if (source[0] != MINI_LUT_NULL && source[1] == MINI_LUT_NULL2) + valid = false; + } + if (fanins[1] == MINI_LUT_NULL2) + { + for (int i = 2; i < lut_size; i++) + valid &= fanins[i] == MINI_LUT_NULL; + continue; + } + bool padding = false; + for (int i = 0; i < lut_size; i++) + if (fanins[i] == MINI_LUT_NULL) + padding = true; + else if (padding || fanins[i] < 0 || fanins[i] >= object || fanins[i] == MINI_LUT_NULL2) + valid = false; + else if (fanins[i] >= 2) + { + int* source = lut->pArray + (size_t)fanins[i] * lut_size; + if (source[0] != MINI_LUT_NULL && source[1] == MINI_LUT_NULL2) + valid = false; + } + } + if (!valid) + { + Mini_LutStop(lut); + return NULL; + } + return lut; +} + +static inline bool sn_lut_interface_matches(Mini_Lut_t* lut, const sn_blast_boundary_t* boundary) +{ + if (!lut || !boundary) + return false; + uint32_t pi_count = 0, po_count = 0; + int object; + Mini_LutForEachPi(lut, object) + pi_count++; + Mini_LutForEachPo(lut, object) + po_count++; + uint32_t register_count = (uint32_t)Mini_LutRegNum(lut); + if (pi_count != boundary->cis.size || po_count != boundary->cos.size || + register_count != boundary->register_bits || register_count > pi_count || register_count > po_count) + return false; + for (uint32_t i = 0; i < register_count; i++) + if (sn_vec_at(sn_blast_boundary_bit_t, &boundary->cis, pi_count - register_count + i).kind != + SN_BLAST_BOUNDARY_REG_OUTPUT || + sn_vec_at(sn_blast_boundary_bit_t, &boundary->cos, po_count - register_count + i).kind != + SN_BLAST_BOUNDARY_REG_INPUT) + return false; + return true; +} + +// Validates MiniLUT topology and its positional interface against the boundary +// saved while deriving the pre-ABC MiniAIG. Levels count LUTs; constants and +// CIs have level zero. The boundary CO vector is in the same order as MiniLUT +// POs, so depths can be reported separately for top outputs, hard-block inputs, +// register controls, and register inputs. +static inline sn_lut_stats_t sn_lut_analyze(Mini_Lut_t* lut, const sn_blast_boundary_t* boundary) +{ + assert(lut && boundary); + assert(sn_lut_interface_matches(lut, boundary)); + assert(Mini_LutSize(lut) >= 2 && Mini_LutSize(lut) <= 16); + size_t object_count = (size_t)Mini_LutNodeNum(lut); + uint32_t* levels = (uint32_t*)calloc(object_count, sizeof(uint32_t)); + assert(levels); + sn_lut_stats_t stats = {0}; + stats.lut_size = (uint32_t)Mini_LutSize(lut); + + for (int object = 0; object < Mini_LutNodeNum(lut); object++) + { + if (Mini_LutNodeIsConst(lut, object)) + continue; + if (Mini_LutNodeIsPi(lut, object)) + { + stats.pi_count++; + continue; + } + if (Mini_LutNodeIsNode(lut, object)) + { + uint32_t level = 0; + int fanin, slot; + Mini_LutForEachFanin(lut, object, fanin, slot) + { + assert(fanin >= 0 && fanin < object); + level = sn_lut_max_u32(level, levels[fanin]); + } + levels[object] = level + 1; + stats.lut_levels = sn_lut_max_u32(stats.lut_levels, levels[object]); + stats.lut_count++; + continue; + } + assert(Mini_LutNodeIsPo(lut, object)); + int fanin = Mini_LutNodeFanin(lut, object, 0); + assert(fanin >= 0 && fanin < object); + levels[object] = levels[fanin]; + stats.po_count++; + } + + stats.register_count = (uint32_t)Mini_LutRegNum(lut); + + uint32_t po_index = 0; + int object; + Mini_LutForEachPo(lut, object) + { + sn_blast_boundary_kind_t kind = + sn_vec_at(sn_blast_boundary_bit_t, &boundary->cos, po_index++).kind; + uint32_t level = levels[object]; + if (kind == SN_BLAST_BOUNDARY_TOP_PO) + stats.top_output_levels = sn_lut_max_u32(stats.top_output_levels, level); + else if (kind == SN_BLAST_BOUNDARY_REG_CONTROL) + stats.register_control_levels = sn_lut_max_u32(stats.register_control_levels, level); + else if (kind == SN_BLAST_BOUNDARY_MEMORY_INPUT) + stats.memory_input_levels = sn_lut_max_u32(stats.memory_input_levels, level); + else if (kind == SN_BLAST_BOUNDARY_PRIMITIVE_INPUT) + stats.primitive_input_levels = sn_lut_max_u32(stats.primitive_input_levels, level); + else if (kind == SN_BLAST_BOUNDARY_LOOP_INPUT) + stats.loop_input_levels = sn_lut_max_u32(stats.loop_input_levels, level); + else if (kind == SN_BLAST_BOUNDARY_REG_INPUT) + stats.register_input_levels = sn_lut_max_u32(stats.register_input_levels, level); + else + assert(false); + } + assert(po_index == stats.po_count); + free(levels); + return stats; +} + +static inline sn_obj_id_t sn_lut_pack_bits(sn_module_t* module, const sn_obj_id_t* bits, uint32_t width, + const char* name) +{ + assert(width && bits); + if (width == 1) + return bits[0]; + return sn_module_add_operator(module, SN_CONCAT, width, false, width, bits, name); +} + +static inline uint64_t sn_lut_node_truth(Mini_Lut_t* lut, int object) +{ + unsigned* words = Mini_LutNodeTruth(lut, object); + return (uint64_t)words[0] | (Mini_LutWordNum(Mini_LutSize(lut)) > 1 ? (uint64_t)words[1] << 32 : 0); +} + +// Decomposes a mapped LUT wider than the physical SN_LUT6 primitive by Shannon expansion on its most-significant +// inputs. The leaves are LUT6 objects and each internal selector is another LUT3. MiniLUT and SN both use fanin 0 as +// the least-significant truth-table variable, so each cofactor is a contiguous truth-table interval. +static inline sn_obj_id_t sn_lut_add_physical_rec(sn_module_t* module, const sn_obj_id_t* fanins, + uint32_t count, const unsigned* truth, uint32_t offset) +{ + assert(module && fanins && truth && count > 0 && count <= 16); + if (count <= 6) + { + uint64_t leaf_truth = 0; + for (uint32_t bit = 0; bit < (UINT32_C(1) << count); bit++) + leaf_truth |= (uint64_t)((truth[(offset + bit) >> 5] >> ((offset + bit) & 31)) & 1) << bit; + return sn_module_add_lut(module, count, fanins, leaf_truth, "lut"); + } + uint32_t select_bit = count - 1; + sn_obj_id_t low = sn_lut_add_physical_rec(module, fanins, select_bit, truth, offset); + sn_obj_id_t high = sn_lut_add_physical_rec(module, fanins, select_bit, truth, + offset + (UINT32_C(1) << select_bit)); + sn_obj_id_t mux_fanins[3] = {fanins[select_bit], high, low}; + return sn_module_add_lut(module, 3, mux_fanins, UINT64_C(0xd8), "lut_wide_mux"); +} + +// Reconstructs the MiniLUT combinational network and its top-level/register +// boundary as a new flat SN module. Hard-block and control reconnection is +// added by subsequent reconstruction stages; this core establishes the direct +// MiniLUT-object-to-SN-object mapping and preserves MiniLUT register order. +static inline sn_module_id_t sn_design_add_lut_module(sn_design_t* design, sn_module_id_t source_top_id, + Mini_Lut_t* lut, const sn_blast_boundary_t* boundary, + const char* module_name) +{ + assert(design && source_top_id < design->modules.size && lut && boundary && module_name); + sn_lut_analyze(lut, boundary); + const sn_module_t* source = sn_design_get_module_const(design, source_top_id); + sn_module_id_t result_id = sn_design_add_module(design, module_name); + sn_module_t* result = sn_design_get_module(design, result_id); + sn_obj_id_t* top_inputs = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * source->obj_types.size); + sn_obj_id_t* mini_objects = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * Mini_LutNodeNum(lut)); + sn_obj_id_t* co_drivers = (sn_obj_id_t*)malloc(sizeof(sn_obj_id_t) * boundary->cos.size); + sn_boundary_regs_t regs; + assert(top_inputs && mini_objects && co_drivers); + for (size_t i = 0; i < source->obj_types.size; i++) + top_inputs[i] = SN_INVALID_ID; + for (int i = 0; i < Mini_LutNodeNum(lut); i++) + mini_objects[i] = SN_INVALID_ID; + + for (size_t i = 0; i < source->type_objects[SN_PI].size; i++) + { + sn_obj_id_t old_pi = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PI], i); + top_inputs[old_pi] = sn_module_add_pi(result, sn_obj_width(source, old_pi), sn_obj_is_signed(source, old_pi), + sn_obj_name(source, old_pi)); + } + sn_boundary_regs_init(®s, design, boundary, result, top_inputs); + uint32_t zero_word = 0, one_word = 1; + mini_objects[Mini_LutNodeConst0()] = sn_module_add_const(result, 1, false, &zero_word, "lut_const0"); + mini_objects[Mini_LutNodeConst1()] = sn_module_add_const(result, 1, false, &one_word, "lut_const1"); + + uint32_t ci_index = 0; + int mini_object; + Mini_LutForEachPi(lut, mini_object) + { + sn_blast_boundary_bit_t bit = sn_vec_at(sn_blast_boundary_bit_t, &boundary->cis, ci_index++); + if (bit.kind == SN_BLAST_BOUNDARY_TOP_PI) + { + assert(bit.signal.occurrence == 0 && top_inputs[bit.signal.object] != SN_INVALID_ID); + mini_objects[mini_object] = + sn_module_add_slice(result, top_inputs[bit.signal.object], (int32_t)bit.signal.bit, + (int32_t)bit.signal.bit, "lut_pi_bit"); + } + else if (bit.kind == SN_BLAST_BOUNDARY_REG_OUTPUT) + mini_objects[mini_object] = sn_boundary_reg_output_bit(®s, bit.owner, bit.signal.bit); + else if (bit.kind == SN_BLAST_BOUNDARY_LOOP_OUTPUT) + mini_objects[mini_object] = sn_boundary_loop_output_bit(®s, bit.owner, bit.signal.bit); + else if (bit.kind == SN_BLAST_BOUNDARY_PRIMITIVE_OUTPUT) + mini_objects[mini_object] = + sn_boundary_primitive_output_bit(®s, bit.owner, bit.port, bit.signal.bit); + else + assert(false); + } + assert(ci_index == boundary->cis.size); + + Mini_LutForEachNode(lut, mini_object) + { + sn_obj_id_t fanins[16]; + int fanin, slot, count = 0; + Mini_LutForEachFanin(lut, mini_object, fanin, slot) + { + assert(count < 16 && mini_objects[fanin] != SN_INVALID_ID); + fanins[count++] = mini_objects[fanin]; + } + mini_objects[mini_object] = count <= 6 + ? sn_module_add_lut(result, (uint32_t)count, fanins, + sn_lut_node_truth(lut, mini_object), "lut") + : sn_lut_add_physical_rec(result, fanins, (uint32_t)count, + Mini_LutNodeTruth(lut, mini_object), 0); + } + uint32_t co_index = 0; + Mini_LutForEachPo(lut, mini_object) + { + int fanin = Mini_LutNodeFanin(lut, mini_object, 0); + assert(mini_objects[fanin] != SN_INVALID_ID); + co_drivers[co_index++] = mini_objects[fanin]; + } + assert(co_index == boundary->cos.size); + + co_index = 0; + for (size_t i = 0; i < source->type_objects[SN_PO].size; i++) + { + sn_obj_id_t old_po = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_PO], i); + uint32_t width = sn_obj_width(source, old_po); + sn_obj_id_t* bits = co_drivers + co_index; + for (uint32_t bit = 0; bit < width; bit++) + { + sn_blast_boundary_bit_t endpoint = sn_vec_at(sn_blast_boundary_bit_t, &boundary->cos, co_index + bit); + assert(endpoint.kind == SN_BLAST_BOUNDARY_TOP_PO && endpoint.port == i && endpoint.signal.bit == bit); + } + co_index += width; + sn_obj_id_t driver = sn_lut_pack_bits(result, bits, width, "lut_po_word"); + sn_module_add_po(result, width, sn_obj_is_signed(source, old_po), sn_obj_name(source, old_po), driver); + } + sn_boundary_regs_finish(®s, co_drivers); + result = sn_design_get_module(design, result_id); + + free(co_drivers); + free(mini_objects); + free(top_inputs); + if (!sn_module_is_topo(result)) + sn_design_reorder_module_topo(design, result_id); + assert(sn_module_is_topo(sn_design_get_module_const(design, result_id))); + return result_id; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snMux.h b/src/base/sn/snMux.h new file mode 100644 index 000000000..03e235bd9 --- /dev/null +++ b/src/base/sn/snMux.h @@ -0,0 +1,921 @@ +/**CFile**************************************************************** + + FileName [snMux.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Mux-path sharing and restructuring for word-level SN designs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snMux.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_MUX_H +#define SN_MUX_H + +// Word-level mux-path sharing for register-fed SN_MUX trees and nested SN_PMUX objects. Root-to-terminal paths are +// enumerated, structurally equal LSB-first words are represented once, and their path conditions are ORed. A hold +// terminal is moved into SN_REG_ENABLE when controls are provably exclusive. General PMUX alternatives preserve SN's +// one-hot-select semantics; as for SN_PMUX itself, behavior for multi-hot selects is unspecified. Modules are +// duplicated and rewritten transactionally; hierarchy, stable module IDs, and the complete canonical register +// interface are preserved. + +#include "sn.h" + +#include +#include +#include +#include + +ABC_NAMESPACE_HEADER_START + +typedef struct sn_share_options_t +{ + uint32_t min_width; + uint32_t min_alternatives; + uint32_t min_saved_paths; +} sn_share_options_t; + +typedef struct sn_share_stats_t +{ + uint64_t modules; + uint64_t registers; + uint64_t muxes; + uint64_t paths_before; + uint64_t paths_after; +} sn_share_stats_t; + +typedef struct sn_share_step_t +{ + sn_obj_id_t select; + uint32_t bit; + bool positive; +} sn_share_step_t; + +typedef struct sn_share_path_t +{ + sn_obj_id_t term; + uint32_t step_offset; + uint32_t step_count; + uint32_t group; +} sn_share_path_t; + +enum +{ + SN_SHARE_MAX_PATHS = 1 << 20, + SN_SHARE_MAX_DEPTH = 4096, + SN_SHARE_MAX_STEPS = 1 << 24 +}; + +static inline sn_share_options_t sn_share_default_options(void) +{ + sn_share_options_t options = {4, 6, 2}; + return options; +} + +static inline sn_obj_id_t sn_share_strip_value(const sn_module_t* module, sn_obj_id_t object) +{ + while (object != SN_INVALID_ID) + { + sn_obj_type_t type = sn_obj_type(module, object); + if ((type != SN_BUF && type != SN_POS && type != SN_CAST) || sn_obj_fanin_count(module, object) != 1 || + sn_obj_width(module, object) != sn_obj_width(module, sn_obj_fanin(module, object, 0))) + break; + object = sn_obj_fanin(module, object, 0); + } + return object; +} + +static inline bool sn_share_const_equal(const sn_module_t* module, sn_obj_id_t a, sn_obj_id_t b) +{ + if (sn_obj_width(module, a) != sn_obj_width(module, b)) + return false; + sn_obj_type_t ta = sn_obj_type(module, a), tb = sn_obj_type(module, b); + if ((ta != SN_CONST0 && ta != SN_CONST1 && ta != SN_CONST) || + (tb != SN_CONST0 && tb != SN_CONST1 && tb != SN_CONST)) + return false; + uint32_t width = sn_obj_width(module, a); + for (uint32_t bit = 0; bit < width; bit++) + { + bool av = (ta == SN_CONST1 && bit == 0) || + (ta == SN_CONST && ((sn_const_words(module, a)[bit >> 5] >> (bit & 31)) & 1)); + bool bv = (tb == SN_CONST1 && bit == 0) || + (tb == SN_CONST && ((sn_const_words(module, b)[bit >> 5] >> (bit & 31)) & 1)); + if (av != bv) + return false; + } + return true; +} + +// Returns the unique raw selector value accepted by an equality comparison, +// accounting for the comparison's signed extension. A wider constant whose +// high bits cannot equal the extended selector makes the predicate impossible. +static inline bool sn_share_decode_value(const sn_module_t* module, sn_obj_id_t value, + sn_obj_id_t constant, uint32_t* decoded) +{ + uint32_t value_width = sn_obj_width(module, value); + uint32_t constant_width = sn_obj_width(module, constant); + bool sign = sn_obj_is_signed(module, value) && sn_obj_is_signed(module, constant); + if (!value_width || value_width >= 31) + return false; + uint32_t result = 0; + for (uint32_t bit = 0; bit < value_width; bit++) + { + bool constant_bit = bit < constant_width ? sn_const_bit(module, constant, bit) + : sign && sn_const_bit(module, constant, constant_width - 1); + result |= (uint32_t)constant_bit << bit; + } + if (constant_width > value_width) + { + bool extension = sign && ((result >> (value_width - 1)) & 1); + for (uint32_t bit = value_width; bit < constant_width; bit++) + if (sn_const_bit(module, constant, bit) != extension) + return false; + } + *decoded = result; + return true; +} + +// Structural word identity through the inexpensive wiring operators used heavily by Slang lowering. This is the +// object-level counterpart of UtilMux's canonical bit-vector IDs: separately-created slices/concatenations of the +// same LSB-first source bits are recognized as the same mux terminal without bit-blasting the module. +static inline bool sn_share_value_equal(const sn_module_t* module, sn_obj_id_t a, sn_obj_id_t b) +{ + a = sn_share_strip_value(module, a); + b = sn_share_strip_value(module, b); + if (a == b) + return true; + if (sn_obj_width(module, a) != sn_obj_width(module, b)) + return false; + sn_obj_type_t ta = sn_obj_type(module, a), tb = sn_obj_type(module, b); + if ((ta == SN_CONST0 || ta == SN_CONST1 || ta == SN_CONST) && + (tb == SN_CONST0 || tb == SN_CONST1 || tb == SN_CONST)) + return sn_share_const_equal(module, a, b); + if (ta != tb) + return false; + if (ta == SN_SLICE) + { + const sn_slice_info_t* ia = sn_obj_slice_info(module, a); + const sn_slice_info_t* ib = sn_obj_slice_info(module, b); + return ia->left_index == ib->left_index && ia->right_index == ib->right_index && + sn_share_value_equal(module, sn_obj_fanin(module, a, 0), sn_obj_fanin(module, b, 0)); + } + if (ta == SN_REPLICATE) + return sn_obj_repeat_count(module, a) == sn_obj_repeat_count(module, b) && + sn_share_value_equal(module, sn_obj_fanin(module, a, 0), sn_obj_fanin(module, b, 0)); + if (ta == SN_CONCAT && sn_obj_fanin_count(module, a) == sn_obj_fanin_count(module, b)) + { + for (uint32_t i = 0; i < sn_obj_fanin_count(module, a); i++) + if (!sn_share_value_equal(module, sn_obj_fanin(module, a, i), sn_obj_fanin(module, b, i))) + return false; + return true; + } + return false; +} + +static inline uint64_t sn_share_hash_mix(uint64_t hash, uint64_t value) +{ + hash ^= value; + return hash * UINT64_C(1099511628211); +} + +// Compute structural hashes for the inexpensive wiring words recognized by sn_share_value_equal(). Modules entering +// @opt_mux are topologically ordered, so every hashed wiring fanin is already available. Unsupported terminals retain +// object identity. Hash collisions are always resolved with the exact structural comparison. +static inline uint64_t* sn_share_value_hashes(const sn_module_t* module) +{ + uint64_t* hashes = (uint64_t*)calloc(module->obj_types.size, sizeof(uint64_t)); + assert(hashes || module->obj_types.size == 0); + for (sn_obj_id_t object = 0; object < module->obj_types.size; object++) + { + sn_obj_type_t type = sn_obj_type(module, object); + uint64_t hash = sn_share_hash_mix(UINT64_C(1469598103934665603), sn_obj_width(module, object)); + if (type == SN_BUF || type == SN_POS || type == SN_CAST) + { + sn_obj_id_t fanin = sn_obj_fanin(module, object, 0); + hashes[object] = sn_obj_width(module, object) == sn_obj_width(module, fanin) + ? hashes[fanin] : sn_share_hash_mix(hash, object); + continue; + } + if (type == SN_CONST0 || type == SN_CONST1 || type == SN_CONST) + { + uint32_t count = sn_const_word_count(sn_obj_width(module, object)); + for (uint32_t i = 0; i < count; i++) + { + uint32_t word = type == SN_CONST ? sn_const_words(module, object)[i] + : type == SN_CONST1 && i == 0 ? 1 : 0; + if (i + 1 == count && (sn_obj_width(module, object) & 31)) + word &= (UINT32_C(1) << (sn_obj_width(module, object) & 31)) - 1; + hash = sn_share_hash_mix(hash, word); + } + hashes[object] = hash; + continue; + } + hash = sn_share_hash_mix(hash, type); + if (type == SN_SLICE) + { + const sn_slice_info_t* info = sn_obj_slice_info(module, object); + hash = sn_share_hash_mix(hash, (uint32_t)info->left_index); + hash = sn_share_hash_mix(hash, (uint32_t)info->right_index); + hash = sn_share_hash_mix(hash, hashes[sn_obj_fanin(module, object, 0)]); + } + else if (type == SN_REPLICATE) + { + hash = sn_share_hash_mix(hash, sn_obj_repeat_count(module, object)); + hash = sn_share_hash_mix(hash, hashes[sn_obj_fanin(module, object, 0)]); + } + else if (type == SN_CONCAT) + for (uint32_t i = 0; i < sn_obj_fanin_count(module, object); i++) + hash = sn_share_hash_mix(hash, hashes[sn_obj_fanin(module, object, i)]); + else + hash = sn_share_hash_mix(hash, object); + hashes[object] = hash; + } + return hashes; +} + +static inline bool sn_share_hashed_equal(const sn_module_t* module, const uint64_t* hashes, + sn_obj_id_t a, sn_obj_id_t b) +{ + a = sn_share_strip_value(module, a); + b = sn_share_strip_value(module, b); + return hashes[a] == hashes[b] && sn_share_value_equal(module, a, b); +} + +// Recognize a binary decode. The equality predicates compare one common selector against distinct constants, so at +// most one PMUX select bit is true and ordinary combinational CEC is valid. An incomplete decode uses the PMUX default. +static inline bool sn_share_select_is_decode(const sn_module_t* module, sn_obj_id_t select) +{ + if (sn_obj_type(module, select) != SN_CONCAT || sn_obj_fanin_count(module, select) < 2) + return false; + sn_obj_id_t common = SN_INVALID_ID; + sn_vec_t decoded_values; + sn_vec_init(&decoded_values); + for (uint32_t i = 0; i < sn_obj_fanin_count(module, select); i++) + { + sn_obj_id_t compare = sn_obj_fanin(module, select, i); + sn_obj_type_t type = sn_obj_type(module, compare); + if ((type != SN_EQ && type != SN_CASE_EQ) || sn_obj_fanin_count(module, compare) != 2) + { + sn_vec_destroy(&decoded_values); + return false; + } + sn_obj_id_t value = sn_obj_fanin(module, compare, 0), constant = sn_obj_fanin(module, compare, 1); + sn_obj_type_t constant_type = sn_obj_type(module, constant); + if (constant_type != SN_CONST0 && constant_type != SN_CONST1 && constant_type != SN_CONST) + { + sn_vec_destroy(&decoded_values); + return false; + } + if (common == SN_INVALID_ID) + common = value; + else if (sn_share_strip_value(module, value) != sn_share_strip_value(module, common) || + sn_obj_width(module, value) != sn_obj_width(module, common) || + sn_obj_is_signed(module, value) != sn_obj_is_signed(module, common)) + { + sn_vec_destroy(&decoded_values); + return false; + } + uint32_t decoded; + if (!sn_share_decode_value(module, value, constant, &decoded)) + continue; + for (size_t j = 0; j < decoded_values.size; j++) + if (sn_vec_at(uint32_t, &decoded_values, j) == decoded) + { + sn_vec_destroy(&decoded_values); + return false; + } + *sn_vec_push(uint32_t, &decoded_values) = decoded; + } + uint32_t width = sn_obj_width(module, common); + bool result = width < 31 && sn_obj_fanin_count(module, select) <= (UINT32_C(1) << width); + sn_vec_destroy(&decoded_values); + return result; +} + +static inline bool sn_share_pmux_words(const sn_module_t* module, sn_obj_id_t pmux, sn_vec_t* words) +{ + assert(sn_obj_type(module, pmux) == SN_PMUX); + sn_obj_id_t select = sn_obj_fanin(module, pmux, SN_PMUX_SELECT); + sn_obj_id_t packed = sn_obj_fanin(module, pmux, SN_PMUX_ALTERNATIVES); + uint32_t count = sn_obj_width(module, select), width = sn_obj_width(module, pmux); + sn_vec_init(words); + if (sn_obj_type(module, packed) != SN_CONCAT || sn_obj_fanin_count(module, packed) != count) + return false; + sn_vec_reserve(sn_obj_id_t, words, count); + for (uint32_t i = 0; i < count; i++) + { + sn_obj_id_t word = sn_obj_fanin(module, packed, i); + if (sn_obj_width(module, word) != width) + { + sn_vec_destroy(words); + sn_vec_init(words); + return false; + } + *sn_vec_push(sn_obj_id_t, words) = sn_share_strip_value(module, word); + } + return true; +} + +static inline sn_obj_id_t sn_share_or(sn_module_t* module, const sn_obj_id_t* values, uint32_t count) +{ + assert(count); + if (count == 1) + return values[0]; + sn_vec_t level, next; + sn_vec_init(&level); + sn_vec_init(&next); + sn_vec_reserve(sn_obj_id_t, &level, count); + for (uint32_t i = 0; i < count; i++) + *sn_vec_push(sn_obj_id_t, &level) = values[i]; + while (level.size > 1) + { + next.size = 0; + for (size_t i = 0; i < level.size; i += 2) + { + if (i + 1 == level.size) + *sn_vec_push(sn_obj_id_t, &next) = sn_vec_at(sn_obj_id_t, &level, i); + else + { + sn_obj_id_t fanins[2] = {sn_vec_at(sn_obj_id_t, &level, i), + sn_vec_at(sn_obj_id_t, &level, i + 1)}; + *sn_vec_push(sn_obj_id_t, &next) = + sn_module_add_operator(module, SN_BIT_OR, 1, false, 2, fanins, NULL); + } + } + sn_vec_t swap = level; + level = next; + next = swap; + } + sn_obj_id_t result = sn_vec_at(sn_obj_id_t, &level, 0); + sn_vec_destroy(&level); + sn_vec_destroy(&next); + return result; +} + +static inline sn_obj_id_t sn_share_and(sn_module_t* module, const sn_obj_id_t* values, uint32_t count) +{ + assert(count); + if (count == 1) + return values[0]; + sn_vec_t level, next; + sn_vec_init(&level); + sn_vec_init(&next); + for (uint32_t i = 0; i < count; i++) + *sn_vec_push(sn_obj_id_t, &level) = values[i]; + while (level.size > 1) + { + next.size = 0; + for (size_t i = 0; i < level.size; i += 2) + { + if (i + 1 == level.size) + *sn_vec_push(sn_obj_id_t, &next) = sn_vec_at(sn_obj_id_t, &level, i); + else + { + sn_obj_id_t fanins[2] = {sn_vec_at(sn_obj_id_t, &level, i), + sn_vec_at(sn_obj_id_t, &level, i + 1)}; + *sn_vec_push(sn_obj_id_t, &next) = + sn_module_add_operator(module, SN_BIT_AND, 1, false, 2, fanins, NULL); + } + } + sn_vec_t swap = level; + level = next; + next = swap; + } + sn_obj_id_t result = sn_vec_at(sn_obj_id_t, &level, 0); + sn_vec_destroy(&level); + sn_vec_destroy(&next); + return result; +} + +static inline void sn_share_collect_mux_paths(const sn_module_t* module, sn_obj_id_t object, sn_vec_t* stack, + sn_vec_t* steps, sn_vec_t* paths, uint8_t* active, bool* exclusive, + bool* overflow) +{ + if (*overflow) + return; + if (stack->size >= SN_SHARE_MAX_DEPTH || paths->size >= SN_SHARE_MAX_PATHS || + steps->size > SN_SHARE_MAX_STEPS - stack->size) + { + *overflow = true; + return; + } + object = sn_share_strip_value(module, object); + if (sn_obj_type(module, object) == SN_MUX && !active[object]) + { + active[object] = 1; + sn_share_step_t* step = sn_vec_push(sn_share_step_t, stack); + step->select = sn_obj_fanin(module, object, SN_MUX_SELECT); + step->bit = 0; + step->positive = true; + sn_share_collect_mux_paths(module, sn_obj_fanin(module, object, SN_MUX_SELECTED), stack, steps, paths, + active, exclusive, overflow); + sn_vec_at(sn_share_step_t, stack, stack->size - 1).positive = false; + sn_share_collect_mux_paths(module, sn_obj_fanin(module, object, SN_MUX_DEFAULT), stack, steps, paths, + active, exclusive, overflow); + stack->size--; + active[object] = 0; + return; + } + if (sn_obj_type(module, object) == SN_PMUX && !active[object]) + { + sn_obj_id_t select = sn_obj_fanin(module, object, SN_PMUX_SELECT); + sn_vec_t words; + sn_vec_init(&words); + if (sn_share_pmux_words(module, object, &words)) + { + if (!sn_share_select_is_decode(module, select)) + *exclusive = false; + active[object] = 1; + for (uint32_t i = 0; i < words.size; i++) + { + sn_share_step_t* step = sn_vec_push(sn_share_step_t, stack); + step->select = select; + step->bit = i; + step->positive = true; + sn_share_collect_mux_paths(module, sn_vec_at(sn_obj_id_t, &words, i), stack, steps, paths, active, + exclusive, overflow); + stack->size--; + } + size_t old_stack_size = stack->size; + for (uint32_t i = 0; i < words.size; i++) + { + sn_share_step_t* step = sn_vec_push(sn_share_step_t, stack); + step->select = select; + step->bit = i; + step->positive = false; + } + sn_share_collect_mux_paths(module, sn_obj_fanin(module, object, SN_PMUX_DEFAULT), stack, steps, paths, + active, exclusive, overflow); + stack->size = old_stack_size; + active[object] = 0; + sn_vec_destroy(&words); + return; + } + sn_vec_destroy(&words); + } + assert(steps->size <= UINT32_MAX && stack->size <= UINT32_MAX); + sn_share_path_t* path = sn_vec_push(sn_share_path_t, paths); + path->term = object; + path->step_offset = (uint32_t)steps->size; + path->step_count = (uint32_t)stack->size; + path->group = SN_INVALID_ID; + sn_vec_reserve(sn_share_step_t, steps, steps->size + stack->size); + for (size_t i = 0; i < stack->size; i++) + *sn_vec_push(sn_share_step_t, steps) = sn_vec_at(sn_share_step_t, stack, i); +} + +static inline sn_obj_id_t sn_share_path_condition(sn_module_t* target, const sn_module_t* source, + const sn_share_path_t* path, const sn_vec_t* steps) +{ + sn_vec_t literals; + sn_vec_init(&literals); + for (uint32_t i = 0; i < path->step_count; i++) + { + sn_share_step_t step = sn_vec_at(sn_share_step_t, steps, path->step_offset + i); + sn_obj_id_t literal = sn_obj_dup(source, step.select); + if (sn_obj_width(target, literal) != 1) + literal = sn_module_add_slice(target, literal, (int32_t)step.bit, (int32_t)step.bit, NULL); + if (!step.positive) + literal = sn_module_add_operator(target, SN_LOG_NOT, 1, false, 1, &literal, NULL); + *sn_vec_push(sn_obj_id_t, &literals) = literal; + } + sn_obj_id_t result = sn_share_and(target, sn_vec_data(sn_obj_id_t, &literals), (uint32_t)literals.size); + sn_vec_destroy(&literals); + return result; +} + +static inline bool sn_share_reg_mux_tree(sn_module_t* target, const sn_module_t* source, sn_obj_id_t old_reg, + const uint64_t* hashes, sn_share_options_t options, sn_share_stats_t* stats) +{ + sn_obj_id_t old_in = sn_obj_pair_in(source, old_reg); + sn_obj_id_t old_root = sn_obj_fanin(source, old_in, 0); + if (old_root == SN_INVALID_ID || sn_obj_type(source, sn_share_strip_value(source, old_root)) != SN_MUX) + return false; + if (sn_obj_width(source, old_reg) < options.min_width) + return false; + sn_vec_t stack, steps, paths, terms, term_hashes, term_links, data_terms, controls; + sn_obj_id_t hold = SN_INVALID_ID, data = SN_INVALID_ID, new_reg = SN_INVALID_ID; + uint32_t* term_buckets = NULL; + uint32_t* group_heads = NULL; + uint32_t* path_links = NULL; + uint32_t term_bucket_count = 0; + size_t hold_index = 0; + bool exclusive = true, overflow = false; + sn_vec_init(&stack); + sn_vec_init(&steps); + sn_vec_init(&paths); + sn_vec_init(&terms); + sn_vec_init(&term_hashes); + sn_vec_init(&term_links); + sn_vec_init(&data_terms); + sn_vec_init(&controls); + uint8_t* active = (uint8_t*)calloc(source->obj_types.size, sizeof(uint8_t)); + assert(active); + sn_share_collect_mux_paths(source, old_root, &stack, &steps, &paths, active, &exclusive, &overflow); + free(active); + if (overflow) + goto unchanged; + if (paths.size < options.min_alternatives) + goto unchanged; + term_bucket_count = 1; + while (term_bucket_count < 2 * paths.size) + term_bucket_count <<= 1; + term_buckets = (uint32_t*)malloc((size_t)term_bucket_count * sizeof(uint32_t)); + assert(term_buckets); + memset(term_buckets, 0xff, (size_t)term_bucket_count * sizeof(uint32_t)); + for (size_t i = 0; i < paths.size; i++) + { + sn_share_path_t* path = &sn_vec_at(sn_share_path_t, &paths, i); + sn_obj_id_t term = path->term; + uint64_t term_hash = hashes[sn_share_strip_value(source, term)]; + uint32_t bucket = (uint32_t)term_hash & (term_bucket_count - 1); + uint32_t k; + for (k = term_buckets[bucket]; k != SN_INVALID_ID; k = sn_vec_at(uint32_t, &term_links, k)) + if (sn_vec_at(uint64_t, &term_hashes, k) == term_hash && + sn_share_value_equal(source, sn_vec_at(sn_obj_id_t, &terms, k), term)) + break; + if (k == SN_INVALID_ID) + { + k = (uint32_t)terms.size; + *sn_vec_push(sn_obj_id_t, &terms) = term; + *sn_vec_push(uint64_t, &term_hashes) = term_hash; + *sn_vec_push(uint32_t, &term_links) = term_buckets[bucket]; + term_buckets[bucket] = k; + } + path->group = k; + } + if (paths.size <= terms.size || paths.size - terms.size < options.min_saved_paths || paths.size < 2 * terms.size) + goto unchanged; + group_heads = (uint32_t*)malloc(terms.size * sizeof(uint32_t)); + path_links = (uint32_t*)malloc(paths.size * sizeof(uint32_t)); + assert(group_heads && path_links); + for (size_t k = 0; k < terms.size; k++) + group_heads[k] = SN_INVALID_ID; + for (size_t i = 0; i < paths.size; i++) + { + uint32_t group = sn_vec_at(sn_share_path_t, &paths, i).group; + assert(group < terms.size); + path_links[i] = group_heads[group]; + group_heads[group] = (uint32_t)i; + } + + hold = sn_share_strip_value(source, old_reg); + hold_index = terms.size; + if (exclusive) + for (size_t k = 0; k < terms.size; k++) + if (sn_share_hashed_equal(source, hashes, sn_vec_at(sn_obj_id_t, &terms, k), hold)) + hold_index = k; + for (size_t k = 0; k < terms.size; k++) + { + if (k == hold_index) + continue; + sn_vec_t cubes; + sn_vec_init(&cubes); + for (uint32_t i = group_heads[k]; i != SN_INVALID_ID; i = path_links[i]) + { + sn_share_path_t* path = &sn_vec_at(sn_share_path_t, &paths, i); + *sn_vec_push(sn_obj_id_t, &cubes) = sn_share_path_condition(target, source, path, &steps); + } + *sn_vec_push(sn_obj_id_t, &controls) = + sn_share_or(target, sn_vec_data(sn_obj_id_t, &cubes), (uint32_t)cubes.size); + *sn_vec_push(sn_obj_id_t, &data_terms) = sn_obj_dup(source, sn_vec_at(sn_obj_id_t, &terms, k)); + sn_vec_destroy(&cubes); + } + if (!data_terms.size) + { + goto unchanged; + } + data = sn_vec_at(sn_obj_id_t, &data_terms, data_terms.size - 1); + if (data_terms.size > 1) + { + sn_obj_id_t packed_select = + sn_module_add_concat(target, (uint32_t)controls.size, sn_vec_data(sn_obj_id_t, &controls), NULL); + sn_obj_id_t packed_data = + sn_module_add_concat(target, (uint32_t)data_terms.size, sn_vec_data(sn_obj_id_t, &data_terms), NULL); + data = sn_module_add_pmux(target, packed_select, packed_data, data, NULL); + } + new_reg = sn_obj_dup(source, old_reg); + sn_obj_connect(target, sn_obj_pair_in(target, new_reg), 0, data); + if (hold_index < terms.size) + { + sn_obj_id_t update = sn_share_or(target, sn_vec_data(sn_obj_id_t, &controls), (uint32_t)controls.size); + sn_obj_id_t enable = sn_obj_fanin(target, new_reg, SN_REG_ENABLE); + if (enable != SN_INVALID_ID) + { + sn_obj_id_t fanins[2] = {enable, update}; + update = sn_module_add_operator(target, SN_BIT_AND, 1, false, 2, fanins, NULL); + } + sn_reg_set_fanin(target, new_reg, SN_REG_ENABLE, update); + } + stats->registers++; + stats->muxes++; + stats->paths_before += paths.size; + stats->paths_after += terms.size; + sn_vec_destroy(&data_terms); + sn_vec_destroy(&controls); + sn_vec_destroy(&stack); + sn_vec_destroy(&steps); + sn_vec_destroy(&paths); + sn_vec_destroy(&terms); + sn_vec_destroy(&term_hashes); + sn_vec_destroy(&term_links); + free(path_links); + free(group_heads); + free(term_buckets); + return true; + +unchanged: + sn_vec_destroy(&stack); + sn_vec_destroy(&steps); + sn_vec_destroy(&paths); + sn_vec_destroy(&terms); + sn_vec_destroy(&term_hashes); + sn_vec_destroy(&term_links); + free(path_links); + free(group_heads); + free(term_buckets); + sn_vec_destroy(&data_terms); + sn_vec_destroy(&controls); + return false; +} + +static inline sn_obj_id_t sn_share_select_bit(sn_module_t* module, sn_obj_id_t select, uint32_t bit) +{ + if (sn_obj_width(module, select) == 1) + return select; + return sn_module_add_slice(module, select, (int32_t)bit, (int32_t)bit, NULL); +} + +static inline bool sn_share_reg_pmux(sn_module_t* target, const sn_module_t* source, sn_obj_id_t old_reg, + const uint64_t* hashes, sn_share_options_t options, sn_share_stats_t* stats) +{ + sn_obj_id_t old_in = sn_obj_pair_in(source, old_reg); + sn_obj_id_t old_root = sn_obj_fanin(source, old_in, 0); + if (old_root != SN_INVALID_ID) + old_root = sn_share_strip_value(source, old_root); + if (old_root == SN_INVALID_ID || sn_obj_type(source, old_root) != SN_PMUX) + return false; + if (sn_obj_width(source, old_reg) < options.min_width) + return false; + sn_vec_t words; + if (!sn_share_pmux_words(source, old_root, &words)) + return false; + uint32_t count = (uint32_t)words.size; + if (count < options.min_alternatives || count > UINT16_MAX) + { + sn_vec_destroy(&words); + return false; + } + sn_obj_id_t old_default = sn_share_strip_value(source, sn_obj_fanin(source, old_root, SN_PMUX_DEFAULT)); + sn_obj_id_t old_hold = sn_share_strip_value(source, old_reg); + bool extracts_hold = sn_share_select_is_decode(source, sn_obj_fanin(source, old_root, SN_PMUX_SELECT)) && + sn_share_hashed_equal(source, hashes, old_default, old_hold); + sn_vec_t unique, unique_hashes, unique_links, conditions, members; + sn_vec_init(&unique); + sn_vec_init(&unique_hashes); + sn_vec_init(&unique_links); + sn_vec_init(&conditions); + sn_vec_init(&members); + uint32_t bucket_count = 1; + while (bucket_count < 2 * count) + bucket_count <<= 1; + uint32_t* buckets = (uint32_t*)malloc((size_t)bucket_count * sizeof(uint32_t)); + uint32_t* member_heads = NULL; + uint32_t* member_links = NULL; + assert(buckets); + memset(buckets, 0xff, (size_t)bucket_count * sizeof(uint32_t)); + for (uint32_t i = 0; i < count; i++) + { + sn_obj_id_t value = sn_vec_at(sn_obj_id_t, &words, i); + if (extracts_hold && sn_share_hashed_equal(source, hashes, value, old_hold)) + continue; + uint64_t value_hash = hashes[sn_share_strip_value(source, value)]; + uint32_t bucket = (uint32_t)value_hash & (bucket_count - 1); + uint32_t k; + for (k = buckets[bucket]; k != SN_INVALID_ID; k = sn_vec_at(uint32_t, &unique_links, k)) + if (sn_vec_at(uint64_t, &unique_hashes, k) == value_hash && + sn_share_value_equal(source, sn_vec_at(sn_obj_id_t, &unique, k), value)) + break; + if (k == SN_INVALID_ID) + { + k = (uint32_t)unique.size; + *sn_vec_push(sn_obj_id_t, &unique) = value; + *sn_vec_push(uint64_t, &unique_hashes) = value_hash; + *sn_vec_push(uint32_t, &unique_links) = buckets[bucket]; + buckets[bucket] = k; + } + assert(k <= UINT16_MAX && i <= UINT16_MAX); + *sn_vec_push(uint32_t, &members) = ((uint32_t)k << 16) | i; + } + uint32_t after = (uint32_t)unique.size; + uint32_t before = count + 1; + if (!extracts_hold) + after++; + if (!unique.size || before <= after || before - after < options.min_saved_paths || before < 2 * after) + { + sn_vec_destroy(&words); + sn_vec_destroy(&unique); + sn_vec_destroy(&unique_hashes); + sn_vec_destroy(&unique_links); + sn_vec_destroy(&conditions); + sn_vec_destroy(&members); + free(buckets); + return false; + } + member_heads = (uint32_t*)malloc(unique.size * sizeof(uint32_t)); + member_links = (uint32_t*)malloc(members.size * sizeof(uint32_t)); + assert(member_heads && member_links); + for (size_t k = 0; k < unique.size; k++) + member_heads[k] = SN_INVALID_ID; + for (size_t j = 0; j < members.size; j++) + { + uint32_t group = sn_vec_at(uint32_t, &members, j) >> 16; + assert(group < unique.size); + member_links[j] = member_heads[group]; + member_heads[group] = (uint32_t)j; + } + + sn_obj_id_t new_reg = sn_obj_dup(source, old_reg); + sn_obj_id_t new_in = sn_obj_pair_in(target, new_reg); + sn_obj_id_t new_select = sn_obj_dup(source, sn_obj_fanin(source, old_root, SN_PMUX_SELECT)); + for (size_t k = 0; k < unique.size; k++) + { + sn_vec_t bits; + sn_vec_init(&bits); + for (uint32_t j = member_heads[k]; j != SN_INVALID_ID; j = member_links[j]) + { + uint32_t member = sn_vec_at(uint32_t, &members, j); + *sn_vec_push(sn_obj_id_t, &bits) = sn_share_select_bit(target, new_select, member & UINT16_MAX); + } + *sn_vec_push(sn_obj_id_t, &conditions) = + sn_share_or(target, sn_vec_data(sn_obj_id_t, &bits), (uint32_t)bits.size); + sn_vec_destroy(&bits); + } + sn_obj_id_t new_data; + if (unique.size == 1) + { + sn_obj_id_t alternative = sn_obj_dup(source, sn_vec_at(sn_obj_id_t, &unique, 0)); + new_data = extracts_hold ? alternative + : sn_module_add_mux(target, sn_vec_at(sn_obj_id_t, &conditions, 0), alternative, + sn_obj_dup(source, old_default), NULL); + } + else + { + sn_vec_t alternatives; + sn_vec_init(&alternatives); + for (size_t k = 0; k < unique.size; k++) + *sn_vec_push(sn_obj_id_t, &alternatives) = sn_obj_dup(source, sn_vec_at(sn_obj_id_t, &unique, k)); + sn_obj_id_t packed_select = + sn_module_add_concat(target, (uint32_t)conditions.size, sn_vec_data(sn_obj_id_t, &conditions), NULL); + sn_obj_id_t packed_data = + sn_module_add_concat(target, (uint32_t)alternatives.size, sn_vec_data(sn_obj_id_t, &alternatives), NULL); + sn_obj_id_t default_data = extracts_hold ? sn_vec_at(sn_obj_id_t, &alternatives, alternatives.size - 1) + : sn_obj_dup(source, old_default); + new_data = sn_module_add_pmux(target, packed_select, packed_data, default_data, NULL); + sn_vec_destroy(&alternatives); + } + sn_obj_connect(target, new_in, 0, new_data); + if (extracts_hold) + { + sn_obj_id_t update = + sn_share_or(target, sn_vec_data(sn_obj_id_t, &conditions), (uint32_t)conditions.size); + sn_obj_id_t enable = sn_obj_fanin(target, new_reg, SN_REG_ENABLE); + if (enable != SN_INVALID_ID) + { + sn_obj_id_t fanins[2] = {enable, update}; + update = sn_module_add_operator(target, SN_BIT_AND, 1, false, 2, fanins, NULL); + } + sn_reg_set_fanin(target, new_reg, SN_REG_ENABLE, update); + } + stats->registers++; + stats->muxes++; + stats->paths_before += before; + stats->paths_after += after; + sn_vec_destroy(&words); + sn_vec_destroy(&unique); + sn_vec_destroy(&unique_hashes); + sn_vec_destroy(&unique_links); + sn_vec_destroy(&conditions); + sn_vec_destroy(&members); + free(member_links); + free(member_heads); + free(buckets); + return true; +} + +static inline void sn_share_replace_module(sn_design_t* design, sn_module_id_t old_id, sn_module_id_t new_id) +{ + assert(new_id + 1 == design->modules.size && old_id != new_id); + sn_module_t* old_module = sn_design_get_module(design, old_id); + sn_module_t* new_module = sn_design_get_module(design, new_id); + sn_name_id_t temporary_name_id = new_module->name; + sn_name_id_t name = old_module->name; + bool interface_locked = old_module->interface_locked; + sn_design_invalidate_copies_to_module(design, old_id); + sn_module_destroy(old_module); + free(old_module); + new_module->id = old_id; + new_module->name = name; + new_module->interface_locked = interface_locked; + sn_vec_at(sn_module_t*, &design->modules, old_id) = new_module; + design->modules.size--; + sn_name_remove_last(&design->names, temporary_name_id); +} + +static inline bool sn_share_module_has_candidate(const sn_module_t* module, sn_share_options_t options) +{ + for (size_t i = 0; i < module->type_objects[SN_REG_OUT].size; i++) + { + sn_obj_id_t reg = sn_vec_at(sn_obj_id_t, &module->type_objects[SN_REG_OUT], i); + if (sn_obj_width(module, reg) < options.min_width) + continue; + sn_obj_id_t root = sn_obj_fanin(module, sn_obj_pair_in(module, reg), 0); + if (root == SN_INVALID_ID) + continue; + root = sn_share_strip_value(module, root); + if (sn_obj_type(module, root) == SN_MUX) + return true; + if (sn_obj_type(module, root) == SN_PMUX) + { + sn_obj_id_t select = sn_obj_fanin(module, root, SN_PMUX_SELECT); + sn_obj_id_t alternatives = sn_obj_fanin(module, root, SN_PMUX_ALTERNATIVES); + uint32_t count = sn_obj_width(module, select); + if (count >= options.min_alternatives && + (uint64_t)count * sn_obj_width(module, root) == sn_obj_width(module, alternatives)) + return true; + } + } + return false; +} + +static inline bool sn_design_share_module(sn_design_t* design, sn_module_id_t module_id, + sn_share_options_t options, sn_share_stats_t* stats) +{ + sn_module_t* source = sn_design_get_module(design, module_id); + if (!sn_share_module_has_candidate(source, options)) + return false; + char name[96]; + uint32_t suffix = 0; + do + { + int length = snprintf(name, sizeof(name), "__sn_share_%u_%u", module_id, suffix++); + assert(length > 0 && (size_t)length < sizeof(name) && suffix != 0); + (void)length; + } while (sn_name_find(&design->names, name) != SN_INVALID_ID); + sn_module_id_t target_id = sn_design_dup_module_topo(design, module_id, name); + sn_module_t* target = sn_design_get_module(design, target_id); + uint64_t* hashes = sn_share_value_hashes(source); + bool changed = false; + for (size_t i = 0; i < source->type_objects[SN_REG_OUT].size; i++) + { + sn_obj_id_t reg = sn_vec_at(sn_obj_id_t, &source->type_objects[SN_REG_OUT], i); + bool reg_changed = sn_share_reg_mux_tree(target, source, reg, hashes, options, stats); + if (!reg_changed) + reg_changed = sn_share_reg_pmux(target, source, reg, hashes, options, stats); + changed |= reg_changed; + } + free(hashes); + if (!changed) + { + sn_name_id_t temporary_name_id = target->name; + sn_module_destroy(target); + free(target); + design->modules.size--; + sn_name_remove_last(&design->names, temporary_name_id); + sn_vec_destroy(&source->copy_ids); + sn_vec_init(&source->copy_ids); + source->copy_module = SN_INVALID_ID; + return false; + } + sn_share_replace_module(design, module_id, target_id); + // Do not use observable-cone cleanup here: even a constant or externally unobservable register is part of the + // canonical transition interface used by pre/post CEC. Reordering preserves every pair and its type ID. Dangling + // mux objects retained by this first implementation are harmless because hierarchical blasting is demand-driven. + sn_design_reorder_module_topo(design, module_id); + stats->modules++; + return true; +} + +static inline sn_share_stats_t sn_design_share(sn_design_t* design, sn_share_options_t options) +{ + assert(design && sn_design_is_topo(design)); + sn_share_stats_t stats = {0}; + size_t module_count = design->modules.size; + for (sn_module_id_t module = 0; module < module_count; module++) + sn_design_share_module(design, module, options, &stats); + assert(design->modules.size == module_count && sn_design_is_topo(design)); + return stats; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snPth.h b/src/base/sn/snPth.h new file mode 100644 index 000000000..eee9c4771 --- /dev/null +++ b/src/base/sn/snPth.h @@ -0,0 +1,148 @@ +/**CFile**************************************************************** + + FileName [snPth.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Bounded worker support for parallel SN mapping jobs.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snPth.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_PTH_H +#define SN_PTH_H + +// Small self-contained pthread scheduler for SN passes. The requested process count includes the coordinating caller, +// so P > 1 creates exactly P-1 workers. Windows and builds without ABC_USE_PTHREADS compile this scheduler as a +// sequential loop, avoiding any SN dependency on pthreads while retaining full P=1 functionality. + +#include +#include +#include + +#if defined(ABC_USE_PTHREADS) && !defined(_WIN32) +#define SN_PTH_USE_THREADS 1 +#include +#else +#define SN_PTH_USE_THREADS 0 +#endif + +ABC_NAMESPACE_HEADER_START + +typedef void (*sn_pth_job_fn)(void* context, void* job); + +typedef struct sn_pth_pool_t +{ + void** jobs; + size_t count; + size_t next; + void* context; + sn_pth_job_fn function; +#if SN_PTH_USE_THREADS + pthread_mutex_t mutex; +#endif +} sn_pth_pool_t; + +#if SN_PTH_USE_THREADS +static inline void* sn_pth_worker(void* argument) +{ + sn_pth_pool_t* pool = (sn_pth_pool_t*)argument; + for (;;) + { + size_t index; + int status = pthread_mutex_lock(&pool->mutex); + if (status != 0) + return NULL; + index = pool->next++; + status = pthread_mutex_unlock(&pool->mutex); + assert(status == 0); + (void)status; + if (index >= pool->count) + return NULL; + pool->function(pool->context, pool->jobs[index]); + } +} +#endif + +static inline int sn_pth_parallel_available(void) +{ + return SN_PTH_USE_THREADS; +} + +static inline void sn_pth_process(void** jobs, size_t count, unsigned processes, + sn_pth_job_fn function, void* context) +{ + assert((jobs || count == 0) && processes >= 1 && function); +#if !SN_PTH_USE_THREADS + (void)processes; + for (size_t i = 0; i < count; i++) + function(context, jobs[i]); +#else + if (processes == 1 || count < 2) + { + for (size_t i = 0; i < count; i++) + function(context, jobs[i]); + return; + } + unsigned worker_count = processes - 1; + if (worker_count > count) + worker_count = (unsigned)count; + sn_pth_pool_t pool; + pool.jobs = jobs; + pool.count = count; + pool.next = 0; + pool.context = context; + pool.function = function; + int status = pthread_mutex_init(&pool.mutex, NULL); + if (status != 0) + { + for (size_t i = 0; i < count; i++) + function(context, jobs[i]); + return; + } + pthread_t* workers = (pthread_t*)malloc(sizeof(pthread_t) * worker_count); + if (!workers) + { + status = pthread_mutex_destroy(&pool.mutex); + assert(status == 0); + (void)status; + for (size_t i = 0; i < count; i++) + function(context, jobs[i]); + return; + } + unsigned created = 0; + for (; created < worker_count; created++) + { + if (pthread_create(&workers[created], NULL, sn_pth_worker, &pool) != 0) + break; + } + for (unsigned i = 0; i < created; i++) + { + int status = pthread_join(workers[i], NULL); + assert(status == 0); + (void)status; + } + // A worker that could not use the mutex leaves its unclaimed suffix for the coordinator. + while (pool.next < count) + function(context, jobs[pool.next++]); + status = pthread_mutex_destroy(&pool.mutex); + assert(status == 0); + (void)status; + free(workers); +#endif +} + +#undef SN_PTH_USE_THREADS + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/base/sn/snTech.h b/src/base/sn/snTech.h new file mode 100644 index 000000000..49d9710c7 --- /dev/null +++ b/src/base/sn/snTech.h @@ -0,0 +1,137 @@ +/**CFile**************************************************************** + + FileName [snTech.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [New word-level design interface.] + + Synopsis [Target-technology descriptions for SN mapping passes.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: snTech.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef SN_TECH_H +#define SN_TECH_H + +// Technology-independent descriptions used by the SN memory and DSP mappers. +// These describe legal primitive configurations and mapping costs; they do not +// describe device placement or the total number of resources on a die. + +#include +#include +#include + +#include "misc/util/abc_namespaces.h" + +ABC_NAMESPACE_HEADER_START + +typedef enum sn_mem_port_mode_t +{ + SN_MEM_PORT_SINGLE = 0, + SN_MEM_PORT_SIMPLE_DUAL, + SN_MEM_PORT_TRUE_DUAL +} sn_mem_port_mode_t; + +typedef enum sn_mem_read_write_mode_t +{ + SN_MEM_READ_WRITE_NO_CHANGE = 0, + SN_MEM_READ_WRITE_READ_FIRST, + SN_MEM_READ_WRITE_WRITE_FIRST +} sn_mem_read_write_mode_t; + +typedef struct sn_mem_tech_t +{ + const char* name; + uint32_t cap_bits; + uint32_t address_bits; + const uint32_t* widths; + size_t width_count; + sn_mem_port_mode_t port_mode; + uint32_t byte_width; + bool synchronous_read; + bool has_clock_enable; + bool has_byte_enable; + bool supports_init; + bool supports_read_first; + bool supports_write_first; + bool supports_no_change; + uint32_t mapping_cost; + const uint32_t* simple_dual_widths; + size_t simple_dual_width_count; +} sn_mem_tech_t; + +typedef struct sn_dsp_tech_t +{ + const char* name; + uint32_t a_width; + uint32_t b_width; + uint32_t p_width; + uint32_t preadder_width; + uint32_t min_a_width; + uint32_t min_b_width; + uint32_t min_p_width; + bool signed_only; + bool has_preadder; + bool has_postadder; + bool has_simd; + bool has_cascade; + uint32_t max_cascade_length; + uint32_t latency; + uint32_t mapping_cost; +} sn_dsp_tech_t; + +typedef struct sn_carry_tech_t +{ + const char* name; + uint32_t width; + uint32_t min_op_width; + uint32_t mapping_cost; +} sn_carry_tech_t; + +typedef struct sn_tech_t +{ + const sn_mem_tech_t* memories; + size_t memory_count; + const sn_dsp_tech_t* dsps; + size_t dsp_count; + const sn_carry_tech_t* carries; + size_t carry_count; +} sn_tech_t; + +// AMD/Xilinx UltraScale+ primitives used by the initial mapper. Width lists +// follow the legal BRAM/URAM port widths in the Yosys Xilinx memory library. +static inline sn_tech_t sn_tech_xilinx_ultrascale(void) +{ + static const uint32_t bram18_widths[] = {1, 2, 4, 9, 18}; + static const uint32_t bram36_widths[] = {1, 2, 4, 9, 18, 36}; + static const uint32_t bram18_sdp_widths[] = {1, 2, 4, 9, 18, 36}; + static const uint32_t bram36_sdp_widths[] = {1, 2, 4, 9, 18, 36, 72}; + static const uint32_t uram_widths[] = {72, 144}; + static const sn_mem_tech_t memories[] = { + {"RAMB18E2", 18u * 1024u, 14, bram18_widths, 5, SN_MEM_PORT_TRUE_DUAL, 9, true, true, true, true, true, + true, true, 129, bram18_sdp_widths, 6}, + {"RAMB36E2", 36u * 1024u, 15, bram36_widths, 6, SN_MEM_PORT_TRUE_DUAL, 9, true, true, true, true, true, + true, true, 257, bram36_sdp_widths, 7}, + {"URAM288", 288u * 1024u, 12, uram_widths, 2, SN_MEM_PORT_TRUE_DUAL, 9, true, true, true, true, false, + true, true, 1024, NULL, 0}, + }; + static const sn_dsp_tech_t dsps[] = { + {"DSP48E2", 27, 18, 48, 27, 2, 2, 9, true, true, true, true, true, 20, 0, 1}, + }; + static const sn_carry_tech_t carries[] = {{"CARRY4", 4, 3, 1}}; + sn_tech_t result = {memories, sizeof(memories) / sizeof(memories[0]), dsps, sizeof(dsps) / sizeof(dsps[0]), + carries, sizeof(carries) / sizeof(carries[0])}; + return result; +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/src/map/if/acd/ac_decomposition.hpp b/src/map/if/acd/ac_decomposition.hpp index 8d5ddb5c4..b956494e2 100644 --- a/src/map/if/acd/ac_decomposition.hpp +++ b/src/map/if/acd/ac_decomposition.hpp @@ -482,6 +482,11 @@ private: for ( uint32_t i = 0; i < num_vars; ++i ) { pComb[i] = pInvPerm[i] = i; + /* bestPerm is written only when some combination beats the initial + * best_cost. When none does, the loop below still evaluates + * permutations[bestPerm[i]], which reads uninitialised stack and then + * indexes permutations[] with it. Seed the identity permutation. */ + bestPerm[i] = i; } /* early bail-out conditions */ @@ -1317,7 +1322,10 @@ private: { auto mask = *tt.begin(); - for ( auto i = real_num_vars; i < num_vars; ++i ) + /* Replicate within the word only. Variables 6 and above are replicated by the + * std::fill below, and shifting a 64-bit word by (1 << i) for i >= 6 is undefined + * behaviour rather than a no-op. */ + for ( auto i = real_num_vars; i < std::min( num_vars, 6u ); ++i ) { mask |= ( mask << ( 1 << i ) ); } diff --git a/src/opt/eslim/delayEngine.cpp b/src/opt/eslim/delayEngine.cpp index 5e92566cf..c0a867511 100644 --- a/src/opt/eslim/delayEngine.cpp +++ b/src/opt/eslim/delayEngine.cpp @@ -121,9 +121,8 @@ namespace eSLIM { } std::vector DelayEngine::reduceDelay(unsigned int max_size, unsigned int initial_delay) { - assert (delay_selectors.find(initial_delay) != delay_selectors.end()); std::vector last_model; - for( auto it = delay_selectors.find(initial_delay); it != delay_selectors.end(); ++it ) { + for( auto it = delay_selectors.lower_bound(initial_delay); it != delay_selectors.end(); ++it ) { int d = it->first; double timeout = getDynamicTimeout(max_size); int status = existsReplacement(max_size, d, timeout); diff --git a/src/opt/lpk/lpkAbcDec.c b/src/opt/lpk/lpkAbcDec.c index b7d4ccb1f..08d1daf30 100644 --- a/src/opt/lpk/lpkAbcDec.c +++ b/src/opt/lpk/lpkAbcDec.c @@ -202,6 +202,24 @@ pMan->timeEvalMuxAn += Abc_Clock() - clk; assert( pResMux == NULL || pResDsd == NULL ); if ( pResMux ) { + // Lpk_MuxAnalize() decides feasibility from the cached cofactor supports in + // p->puSupps. Those may have come from Lpk_ComputeSupports(), which derives + // them from two BDDs built in opposite variable orders and stitches the halves + // together, and that estimate can be a strict SUBSET of the true cofactor + // support. When it is, the component retained by the split below ends up with + // no vacant fanin slot for the component that is split off, and Lpk_MuxSplit() + // fails its assertion `iVarVac < (int)p->nVars'. Re-derive the one support the + // split actually depends on and decline the MUX decomposition if it does not fit. + unsigned * pTruthThis = Lpk_FunTruth( p, 0 ); + unsigned * pTruthCof = Lpk_FunTruth( p, 1 ); + unsigned uSuppExact; + if ( pResMux->Polarity ) + Kit_TruthCofactor1New( pTruthCof, pTruthThis, p->nVars, pResMux->Variable ); + else + Kit_TruthCofactor0New( pTruthCof, pTruthThis, p->nVars, pResMux->Variable ); + uSuppExact = Kit_TruthSupport( pTruthCof, p->nVars ) | ( 1 << pResMux->Variable ); + if ( Kit_WordCountOnes( uSuppExact ) >= (int)p->nVars ) + return 0; clk = Abc_Clock(); p2 = Lpk_MuxSplit( pMan, p, pResMux->Variable, pResMux->Polarity ); pMan->timeEvalMuxSp += Abc_Clock() - clk;