Dumping LUT-mapped networks in Vivado-readable Verilog.

This commit is contained in:
Alan Mishchenko 2026-02-15 19:26:35 -08:00
parent 8475386dfa
commit 3cdb1c4c3b
2 changed files with 238 additions and 4 deletions

View File

@ -1292,12 +1292,201 @@ static void GiaHie_DumpInterfaceAssigns( Gia_Man_t * p, char * pFileName )
fclose( pFile );
}
/**Function*************************************************************
Synopsis [Dumps mapped AIG as LUT6 instances in Verilog]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static void GiaHie_DumpMappedLuts( Gia_Man_t * p, char * pFileName )
{
int i, k, iFanin, nLutSize, nDigits;
FILE * pFile;
Vec_Int_t * vLeaves;
word * pTruth;
Gia_Obj_t * pObj;
// Check if AIG is mapped
if ( !Gia_ManHasMapping(p) )
{
printf( "Cannot write LUT-based Verilog because AIG is not mapped.\n" );
return;
}
pFile = fopen( pFileName, "wb" );
if ( pFile == NULL )
{
printf( "Cannot open output file \"%s\".\n", pFileName );
return;
}
nDigits = Abc_Base10Log( Gia_ManObjNum(p) );
if ( nDigits < 3 )
nDigits = 3;
// Write header
fprintf( pFile, "`timescale 1ns/1ps\n\n" );
// Write LUT module definitions for Yosys compatibility (compact version)
fprintf( pFile, "module LUT2 #( parameter INIT = 04\'h0 ) ( output O, input I0, I1 );\n" );
fprintf( pFile, " assign O = INIT[ {I1, I0} ];\n" );
fprintf( pFile, "endmodule\n" );
fprintf( pFile, "module LUT3 #( parameter INIT = 08\'h0 ) ( output O, input I0, I1, I2 );\n" );
fprintf( pFile, " assign O = INIT[ {I2, I1, I0} ];\n" );
fprintf( pFile, "endmodule\n" );
fprintf( pFile, "module LUT4 #( parameter INIT = 16\'h0 ) ( output O, input I0, I1, I2, I3 );\n" );
fprintf( pFile, " assign O = INIT[ {I3, I2, I1, I0} ];\n" );
fprintf( pFile, "endmodule\n" );
fprintf( pFile, "module LUT5 #( parameter INIT = 32\'h0 ) ( output O, input I0, I1, I2, I3, I4 );\n" );
fprintf( pFile, " assign O = INIT[ {I4, I3, I2, I1, I0} ];\n" );
fprintf( pFile, "endmodule\n" );
fprintf( pFile, "module LUT6 #( parameter INIT = 64\'h0 ) ( output O, input I0, I1, I2, I3, I4, I5 );\n" );
fprintf( pFile, " assign O = INIT[ {I5, I4, I3, I2, I1, I0} ];\n" );
fprintf( pFile, "endmodule\n\n" );
// Write main module
fprintf( pFile, "module " );
GiaHie_DumpModuleName( pFile, p->pName );
fprintf( pFile, " (\n" );
GiaHie_DumpPortDecls( p, pFile );
fprintf( pFile, "\n);\n\n" );
// Declare wires for inputs (using object IDs like regular &write_ver)
if ( Gia_ManCiNum(p) )
{
fprintf( pFile, " wire " );
GiaHie_WriteObjRange( pFile, p, 0, Gia_ManCiNum(p), nDigits, 7, 4, 0, 1 );
fprintf( pFile, ";\n\n" );
GiaHie_DumpInputAssigns( p, pFile, nDigits );
fprintf( pFile, "\n" );
}
// Declare wires for outputs (using object IDs like regular &write_ver)
if ( Gia_ManCoNum(p) )
{
fprintf( pFile, " wire " );
GiaHie_WriteObjRange( pFile, p, 0, Gia_ManCoNum(p), nDigits, 7, 4, 0, 0 );
fprintf( pFile, ";\n\n" );
GiaHie_DumpOutputAssigns( p, pFile, nDigits );
fprintf( pFile, "\n" );
}
// Declare internal wires for LUT outputs (10 per line)
{
int nWiresPerLine = 10;
int nWireCount = 0;
int fFirst = 1;
Gia_ManForEachLut( p, i )
{
if ( nWireCount == 0 )
fprintf( pFile, " wire " );
if ( !fFirst )
fprintf( pFile, ", " );
fprintf( pFile, "n%0*d", nDigits, i );
fFirst = 0;
nWireCount++;
if ( nWireCount == nWiresPerLine )
{
fprintf( pFile, ";\n" );
nWireCount = 0;
fFirst = 1;
}
}
if ( nWireCount > 0 )
fprintf( pFile, ";\n" );
}
fprintf( pFile, "\n" );
// Initialize truth table computation
vLeaves = Vec_IntAlloc( 6 );
Gia_ObjComputeTruthTableStart( p, 6 );
// Write LUT6 instances
Gia_ManForEachLut( p, i )
{
nLutSize = Gia_ObjLutSize( p, i );
// Collect LUT inputs
Vec_IntClear( vLeaves );
Gia_LutForEachFanin( p, i, iFanin, k )
Vec_IntPush( vLeaves, iFanin );
// Compute truth table
pTruth = Gia_ObjComputeTruthTableCut( p, Gia_ManObj(p, i), vLeaves );
// Write LUT instance - use appropriate size LUT based on inputs
if ( nLutSize <= 1 )
nLutSize = 2; // minimum LUT size is 2
// Determine INIT width and padding
int nInitBits = (1 << nLutSize);
unsigned long long truthValue = (nLutSize <= 6) ? pTruth[0] : 0;
// Mask truth value to the appropriate number of bits
if ( nLutSize < 6 )
truthValue &= ((1ULL << nInitBits) - 1);
// Write LUT instance with padding for alignment
fprintf( pFile, " (* DONT_TOUCH = \"yes\" *) LUT%d #(", nLutSize );
// Write INIT parameter with appropriate width and padding aligned to 64-bit width
if ( nLutSize == 2 )
fprintf( pFile, ".INIT(04'h%01llx )) u_lut%0*d (", truthValue, nDigits, i );
else if ( nLutSize == 3 )
fprintf( pFile, ".INIT(08'h%02llx )) u_lut%0*d (", truthValue, nDigits, i );
else if ( nLutSize == 4 )
fprintf( pFile, ".INIT(16'h%04llx )) u_lut%0*d (", truthValue, nDigits, i );
else if ( nLutSize == 5 )
fprintf( pFile, ".INIT(32'h%08llx )) u_lut%0*d (", truthValue, nDigits, i );
else // nLutSize == 6
fprintf( pFile, ".INIT(64'h%016llx)) u_lut%0*d (", truthValue, nDigits, i );
// Write LUT inputs - only the ones actually used by this LUT size
for ( k = 0; k < nLutSize; k++ )
{
iFanin = Vec_IntEntry( vLeaves, k );
if ( k > 0 )
fprintf( pFile, ", " );
fprintf( pFile, ".I%d(n%0*d)", k, nDigits, iFanin );
}
// Write LUT output
fprintf( pFile, ", .O(n%0*d));\n", nDigits, i );
}
// Cleanup truth table computation
Gia_ObjComputeTruthTableStop( p );
Vec_IntFree( vLeaves );
fprintf( pFile, "\n" );
// Connect internal nodes to outputs (like regular &write_ver)
Gia_ManForEachCo( p, pObj, i )
{
fprintf( pFile, " assign n%0*d = ", nDigits, Gia_ManCoIdToId(p, i) );
GiaHie_PrintObjLit( pFile, Gia_ObjFaninId0p(p, pObj), Gia_ObjFaninC0(pObj), nDigits );
fprintf( pFile, ";\n" );
}
fprintf( pFile, "\nendmodule\n" );
fclose( pFile );
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
@ -1314,6 +1503,30 @@ void Gia_WriteVerilog( char * pFileName, Gia_Man_t * pGia, int fUseGates, int fV
GiaHie_DumpInterfaceAssigns( pGia, pFileName );
}
/**Function*************************************************************
Synopsis [Writes mapped AIG as LUT6-based Verilog]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
void Gia_WriteMappedVerilog( char * pFileName, Gia_Man_t * pGia, int fVerbose )
{
(void)fVerbose;
if ( pFileName == NULL || pGia == NULL )
return;
if ( !Gia_ManHasMapping(pGia) )
{
printf( "Cannot write LUT-based Verilog because AIG is not mapped.\n" );
return;
}
GiaHie_DumpMappedLuts( pGia, pFileName );
}
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////

View File

@ -35109,15 +35109,17 @@ usage:
int Abc_CommandAbc9WriteVer( Abc_Frame_t * pAbc, int argc, char ** argv )
{
extern void Gia_WriteVerilog( char * pFileName, Gia_Man_t * pGia, int fUseGates, int fVerbose );
extern void Gia_WriteMappedVerilog( char * pFileName, Gia_Man_t * pGia, int fVerbose );
char * pFileSpec = NULL;
Abc_Ntk_t * pNtkSpec = NULL;
char * pFileName;
char ** pArgvNew;
int c, nArgcNew;
int fUseGates = 0;
int fUseLuts = 0;
int fVerbose = 0;
Extra_UtilGetoptReset();
while ( ( c = Extra_UtilGetopt( argc, argv, "Sgvh" ) ) != EOF )
while ( ( c = Extra_UtilGetopt( argc, argv, "Sglvh" ) ) != EOF )
{
switch ( c )
{
@ -35133,6 +35135,9 @@ int Abc_CommandAbc9WriteVer( Abc_Frame_t * pAbc, int argc, char ** argv )
case 'g':
fUseGates ^= 1;
break;
case 'l':
fUseLuts ^= 1;
break;
case 'v':
fVerbose ^= 1;
break;
@ -35157,7 +35162,21 @@ int Abc_CommandAbc9WriteVer( Abc_Frame_t * pAbc, int argc, char ** argv )
Abc_Print( -1, "There is no AIG to write.\n" );
return 1;
}
Gia_WriteVerilog( pFileName, pAbc->pGia, fUseGates, fVerbose );
// Check if we should write LUT-based Verilog
if ( fUseLuts || (Gia_ManHasMapping(pAbc->pGia) && !fUseGates) )
{
if ( !Gia_ManHasMapping(pAbc->pGia) )
{
Abc_Print( -1, "Cannot write LUT-based Verilog because AIG is not mapped.\n" );
Abc_Print( -1, "Use \"&if\" to map the AIG first, or omit the -l flag.\n" );
return 1;
}
Gia_WriteMappedVerilog( pFileName, pAbc->pGia, fVerbose );
}
else
{
Gia_WriteVerilog( pFileName, pAbc->pGia, fUseGates, fVerbose );
}
}
else
{
@ -35179,13 +35198,15 @@ int Abc_CommandAbc9WriteVer( Abc_Frame_t * pAbc, int argc, char ** argv )
return 0;
usage:
Abc_Print( -2, "usage: &write_ver [-S <file>] [-gvh] <file>\n" );
Abc_Print( -2, "usage: &write_ver [-S <file>] [-glvh] <file>\n" );
Abc_Print( -2, "\t writes hierarchical Verilog\n" );
Abc_Print( -2, "\t-S file : file name for the original design (required when hierarchy is present)\n" );
Abc_Print( -2, "\t-g : toggle output gates vs assign-statements [default = %s]\n", fUseGates? "gates": "assigns" );
Abc_Print( -2, "\t-l : write LUT6-based Verilog for mapped AIGs [default = %s]\n", fUseLuts? "yes": "no" );
Abc_Print( -2, "\t-v : toggle verbose output [default = %s]\n", fVerbose? "yes": "no" );
Abc_Print( -2, "\t-h : print the command usage\n");
Abc_Print( -2, "\t<file> : the file name\n");
Abc_Print( -2, "\tNote: When AIG is mapped and -l is not specified, LUT-based output is automatically used.\n");
return 1;
}