From c69e45916a2bfb00b27c5f16de34c5104630e9bc Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Fri, 1 Aug 2025 14:29:45 -0700 Subject: [PATCH 01/22] Update &append to share primary inputs. --- src/aig/gia/gia.h | 2 +- src/aig/gia/giaDup.c | 4 ++-- src/base/abci/abc.c | 17 +++++++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/aig/gia/gia.h b/src/aig/gia/gia.h index ee79ccd7a..1d0290c04 100644 --- a/src/aig/gia/gia.h +++ b/src/aig/gia/gia.h @@ -1358,7 +1358,7 @@ extern Gia_Man_t * Gia_ManDupZero( Gia_Man_t * p ); extern Gia_Man_t * Gia_ManDupPerm( Gia_Man_t * p, Vec_Int_t * vPiPerm ); extern Gia_Man_t * Gia_ManDupPermFlop( Gia_Man_t * p, Vec_Int_t * vFfPerm ); extern Gia_Man_t * Gia_ManDupPermFlopGap( Gia_Man_t * p, Vec_Int_t * vFfPerm ); -extern void Gia_ManDupAppend( Gia_Man_t * p, Gia_Man_t * pTwo ); +extern void Gia_ManDupAppend( Gia_Man_t * p, Gia_Man_t * pTwo, int fShareCis ); extern void Gia_ManDupAppendShare( Gia_Man_t * p, Gia_Man_t * pTwo ); extern Gia_Man_t * Gia_ManDupAppendNew( Gia_Man_t * pOne, Gia_Man_t * pTwo ); extern Gia_Man_t * Gia_ManDupAppendCones( Gia_Man_t * p, Gia_Man_t ** ppCones, int nCones, int fOnlyRegs ); diff --git a/src/aig/gia/giaDup.c b/src/aig/gia/giaDup.c index cca6f3d4c..be6eef7a2 100644 --- a/src/aig/gia/giaDup.c +++ b/src/aig/gia/giaDup.c @@ -1134,7 +1134,7 @@ Gia_Man_t * Gia_ManDupRandPerm( Gia_Man_t * p ) SeeAlso [] ***********************************************************************/ -void Gia_ManDupAppend( Gia_Man_t * pNew, Gia_Man_t * pTwo ) +void Gia_ManDupAppend( Gia_Man_t * pNew, Gia_Man_t * pTwo, int fShareCis ) { Gia_Obj_t * pObj; int i; @@ -1148,7 +1148,7 @@ void Gia_ManDupAppend( Gia_Man_t * pNew, Gia_Man_t * pTwo ) if ( Gia_ObjIsAnd(pObj) ) pObj->Value = Gia_ManAppendAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); else if ( Gia_ObjIsCi(pObj) ) - pObj->Value = Gia_ManAppendCi( pNew ); + pObj->Value = fShareCis ? Gia_ManCiLit(pNew, Gia_ObjCioId(pObj)) : Gia_ManAppendCi( pNew ); else if ( Gia_ObjIsCo(pObj) ) pObj->Value = Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) ); } diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 146f54ac8..d6fd5c5c6 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -39767,13 +39767,16 @@ int Abc_CommandAbc9Append( Abc_Frame_t * pAbc, int argc, char ** argv ) char * FileName, * pTemp; char ** pArgvNew; int nArgcNew; - int c; + int c, fShareCis = 0; int fVerbose = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "ivh" ) ) != EOF ) { switch ( c ) { + case 'i': + fShareCis ^= 1; + break; case 'v': fVerbose ^= 1; break; @@ -39813,14 +39816,20 @@ int Abc_CommandAbc9Append( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -1, "Reading AIGER has failed.\n" ); return 0; } + if ( fShareCis && Gia_ManCiNum(pAbc->pGia) != Gia_ManCiNum(pSecond) ) + { + Abc_Print( -1, "The AIGs have different number of combinational inputs.\n" ); + return 0; + } // compute the miter - Gia_ManDupAppend( pAbc->pGia, pSecond ); + Gia_ManDupAppend( pAbc->pGia, pSecond, fShareCis ); Gia_ManStop( pSecond ); return 0; usage: - Abc_Print( -2, "usage: &append [-vh] \n" ); + Abc_Print( -2, "usage: &append [-ivh] \n" ); Abc_Print( -2, "\t appends to the current AIG using new PIs and POs\n" ); + Abc_Print( -2, "\t-i : toggle sharing combinational inputs [default = %s]\n", fShareCis? "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"); Abc_Print( -2, "\t : AIGER file with the design to miter\n"); From 3aa8a4a6395e13cf8626f447cef3e292457eb140 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Sat, 2 Aug 2025 08:53:22 -0700 Subject: [PATCH 02/22] New command to dump circuit structure into a file. --- src/base/io/io.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/base/io/io.c b/src/base/io/io.c index 54c084dc8..3de20c094 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -72,6 +72,7 @@ static int IoCommandWriteBaf ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandWriteBblif ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandWriteBlif ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandWriteEdgelist( Abc_Frame_t * pAbc, int argc, char **argv ); +static int IoCommandWriteNtk ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandWriteBlifMv ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandWriteBench ( Abc_Frame_t * pAbc, int argc, char **argv ); static int IoCommandWriteBook ( Abc_Frame_t * pAbc, int argc, char **argv ); @@ -162,6 +163,7 @@ void Io_Init( Abc_Frame_t * pAbc ) Cmd_CommandAdd( pAbc, "I/O", "write_dot", IoCommandWriteDot, 0 ); Cmd_CommandAdd( pAbc, "I/O", "write_eqn", IoCommandWriteEqn, 0 ); Cmd_CommandAdd( pAbc, "I/O", "write_edgelist",IoCommandWriteEdgelist, 0 ); + Cmd_CommandAdd( pAbc, "I/O", "write_ntk", IoCommandWriteNtk, 0 ); Cmd_CommandAdd( pAbc, "I/O", "write_gml", IoCommandWriteGml, 0 ); // Cmd_CommandAdd( pAbc, "I/O", "write_list", IoCommandWriteList, 0 ); Cmd_CommandAdd( pAbc, "I/O", "write_hmetis", IoCommandWriteHMetis, 0 ); @@ -3557,6 +3559,74 @@ usage: return 1; } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int IoCommandWriteNtk( Abc_Frame_t * pAbc, int argc, char **argv ) +{ + char * pFileName; + int c; + + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Nh" ) ) != EOF ) + { + switch ( c ) + { + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pAbc->pNtkCur == NULL ) + { + fprintf( pAbc->Out, "Empty network.\n" ); + return 0; + } + if ( argc != globalUtilOptind + 1 ) + goto usage; + Abc_Obj_t * pObj, * pFanin; int i, k, nIds = 1; + int * pId = ABC_CALLOC( int, Abc_NtkObjNumMax(pAbc->pNtkCur) ); + Abc_NtkForEachCi( pAbc->pNtkCur, pObj, i ) + pId[pObj->Id] = nIds++; + Abc_NtkForEachNode( pAbc->pNtkCur, pObj, i ) + pId[pObj->Id] = nIds++; + Abc_NtkForEachCo( pAbc->pNtkCur, pObj, i ) + pId[pObj->Id] = nIds++; + // get the output file name + pFileName = argv[globalUtilOptind]; + FILE * pFile = fopen( pFileName, "wb" ); + fprintf( pFile, "%d\n", 0 ); + Abc_NtkForEachCi( pAbc->pNtkCur, pObj, i ) + fprintf( pFile, "%d\n", pId[pObj->Id] ); + Abc_NtkForEachNode( pAbc->pNtkCur, pObj, i ) { + fprintf( pFile, "%d", pId[pObj->Id] ); + Abc_ObjForEachFanin( pObj, pFanin, k ) + fprintf( pFile, " %d", pId[pFanin->Id] ); + fprintf( pFile, "\n" ); + } + Abc_NtkForEachCo( pAbc->pNtkCur, pObj, i ) + fprintf( pFile, "%d %d\n", pId[pObj->Id], pId[Abc_ObjFanin0(pObj)->Id] ); + fclose( pFile ); + ABC_FREE( pId ); + return 0; + +usage: + fprintf( pAbc->Err, "usage: write_ntk \n" ); + fprintf( pAbc->Err, "\t writes the network into a text file\n" ); + fprintf( pAbc->Err, "\t-h : print the help massage\n" ); + fprintf( pAbc->Err, "\tfile : the name of the file to write\n" ); + return 1; +} + /**Function************************************************************* From aeef2c6692f82db2a3af7cb2ab5d865f74b2860a Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Sat, 2 Aug 2025 08:58:01 -0700 Subject: [PATCH 03/22] Fixing compiler warning. --- src/base/io/io.c | 49 +++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index 3de20c094..6b761347f 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -3593,30 +3593,33 @@ int IoCommandWriteNtk( Abc_Frame_t * pAbc, int argc, char **argv ) } if ( argc != globalUtilOptind + 1 ) goto usage; - Abc_Obj_t * pObj, * pFanin; int i, k, nIds = 1; - int * pId = ABC_CALLOC( int, Abc_NtkObjNumMax(pAbc->pNtkCur) ); - Abc_NtkForEachCi( pAbc->pNtkCur, pObj, i ) - pId[pObj->Id] = nIds++; - Abc_NtkForEachNode( pAbc->pNtkCur, pObj, i ) - pId[pObj->Id] = nIds++; - Abc_NtkForEachCo( pAbc->pNtkCur, pObj, i ) - pId[pObj->Id] = nIds++; - // get the output file name - pFileName = argv[globalUtilOptind]; - FILE * pFile = fopen( pFileName, "wb" ); - fprintf( pFile, "%d\n", 0 ); - Abc_NtkForEachCi( pAbc->pNtkCur, pObj, i ) - fprintf( pFile, "%d\n", pId[pObj->Id] ); - Abc_NtkForEachNode( pAbc->pNtkCur, pObj, i ) { - fprintf( pFile, "%d", pId[pObj->Id] ); - Abc_ObjForEachFanin( pObj, pFanin, k ) - fprintf( pFile, " %d", pId[pFanin->Id] ); - fprintf( pFile, "\n" ); + else + { + Abc_Obj_t * pObj, * pFanin; int i, k, nIds = 1; + int * pId = ABC_CALLOC( int, Abc_NtkObjNumMax(pAbc->pNtkCur) ); + Abc_NtkForEachCi( pAbc->pNtkCur, pObj, i ) + pId[pObj->Id] = nIds++; + Abc_NtkForEachNode( pAbc->pNtkCur, pObj, i ) + pId[pObj->Id] = nIds++; + Abc_NtkForEachCo( pAbc->pNtkCur, pObj, i ) + pId[pObj->Id] = nIds++; + // get the output file name + pFileName = argv[globalUtilOptind]; + FILE * pFile = fopen( pFileName, "wb" ); + fprintf( pFile, "%d\n", 0 ); + Abc_NtkForEachCi( pAbc->pNtkCur, pObj, i ) + fprintf( pFile, "%d\n", pId[pObj->Id] ); + Abc_NtkForEachNode( pAbc->pNtkCur, pObj, i ) { + fprintf( pFile, "%d", pId[pObj->Id] ); + Abc_ObjForEachFanin( pObj, pFanin, k ) + fprintf( pFile, " %d", pId[pFanin->Id] ); + fprintf( pFile, "\n" ); + } + Abc_NtkForEachCo( pAbc->pNtkCur, pObj, i ) + fprintf( pFile, "%d %d\n", pId[pObj->Id], pId[Abc_ObjFanin0(pObj)->Id] ); + fclose( pFile ); + ABC_FREE( pId ); } - Abc_NtkForEachCo( pAbc->pNtkCur, pObj, i ) - fprintf( pFile, "%d %d\n", pId[pObj->Id], pId[Abc_ObjFanin0(pObj)->Id] ); - fclose( pFile ); - ABC_FREE( pId ); return 0; usage: From 0218e3e4cb93be5873e60aed2e5c64d63d0e2f11 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Sun, 3 Aug 2025 20:10:26 -0700 Subject: [PATCH 04/22] New command for bound-set evaluation. --- abclib.dsp | 4 + src/aig/gia/giaBsFind.c | 580 ++++++++++++++++++++++++++++++++++++++++ src/aig/gia/giaDup.c | 6 +- src/aig/gia/module.make | 1 + src/base/abci/abc.c | 97 ++++++- 5 files changed, 684 insertions(+), 4 deletions(-) create mode 100644 src/aig/gia/giaBsFind.c diff --git a/abclib.dsp b/abclib.dsp index 2bcb6e1b8..33bc81c99 100644 --- a/abclib.dsp +++ b/abclib.dsp @@ -5755,6 +5755,10 @@ SOURCE=.\src\aig\gia\giaBound.c # End Source File # Begin Source File +SOURCE=.\src\aig\gia\giaBsFind.c +# End Source File +# Begin Source File + SOURCE=.\src\aig\gia\giaCCof.c # End Source File # Begin Source File diff --git a/src/aig/gia/giaBsFind.c b/src/aig/gia/giaBsFind.c new file mode 100644 index 000000000..61cb2c2cf --- /dev/null +++ b/src/aig/gia/giaBsFind.c @@ -0,0 +1,580 @@ +/**CFile**************************************************************** + + FileName [giaBsFind.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Scalable AIG package.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: giaBsFind.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "gia.h" +#include "misc/util/utilTruth.h" + +ABC_NAMESPACE_IMPL_START + + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// Cost function: sum of squared differences between all pairs +int cost_sum_squares(int* subset, int K, void * pData) { + double cost = 0; + for (int i = 0; i < K - 1; i++) { + for (int j = i + 1; j < K; j++) { + int diff = subset[i] - subset[j]; + cost += diff * diff; + } + } + // Normalize to be between 2 and 100 + return (int)(2.0 + (cost / (K * K)) * 0.98); +} + +// Compare function for qsort - for sorting individual subsets +int compare_ints(const void* a, const void* b) { + return *(int*)a - *(int*)b; +} + +// Compare function for qsort - for sorting subsets by cost +// Subset format: [K, element1, element2, ..., elementK, cost] +int compare_subsets_by_cost(const void* a, const void* b) { + const int* subset_a = (const int*)a; + const int* subset_b = (const int*)b; + int K_a = subset_a[0]; + int K_b = subset_b[0]; + assert( K_a == K_b ); + // Cost is at position K+1 (after K and K elements) + return subset_a[K_a + 1] - subset_b[K_b + 1]; +} + +// Generate a random subset of K numbers from 0 to N-1 +// Format: [K, element1, element2, ..., elementK, cost] +void generate_random_subset(int* subset, int K, int N) { + subset[0] = K; // Store K in first position + int count = 0; + + // First, generate K unique random numbers + while (count < K) { + int num = rand() % N; + + // Check if number already exists in subset + int exists = 0; + for (int i = 0; i < count; i++) { + if (subset[i + 1] == num) { + exists = 1; + break; + } + } + + if (!exists) { + subset[count + 1] = num; + count++; + } + } + + // Then, sort the subset using bubble sort + for (int i = 1; i < K; i++) { + for (int j = 1; j < K - i + 1; j++) { + if (subset[j] > subset[j + 1]) { + int temp = subset[j]; + subset[j] = subset[j + 1]; + subset[j + 1] = temp; + } + } + } +} + +// Create offspring from two parent subsets +// Uses pre-allocated arrays to avoid repeated memory allocation +void create_offspring(int* parent1, int* parent2, int* offspring, int K, int N, + int* in_offspring, int* in_parent1, int* in_parent2, int* candidates) { + int count = 0; + offspring[0] = K; // Store K in first position + + // Mark which numbers are in each parent (skip first element which is K) + for (int i = 1; i <= K; i++) { + in_parent1[parent1[i]] = 1; + in_parent2[parent2[i]] = 1; + } + + // First, add numbers that appear in both parents + for (int i = 0; i < N && count < K; i++) { + if (in_parent1[i] && in_parent2[i]) { + offspring[count + 1] = i; + count++; + in_offspring[i] = 1; + } + } + + // Create array of numbers that appear in exactly one parent + int num_candidates = 0; + + for (int i = 0; i < N; i++) { + if ((in_parent1[i] || in_parent2[i]) && !in_offspring[i]) { + candidates[num_candidates++] = i; + } + } + + // Randomly add from candidates until we have K numbers + while (count < K && num_candidates > 0) { + int idx = rand() % num_candidates; + offspring[count + 1] = candidates[idx]; + count++; + in_offspring[candidates[idx]] = 1; + + // Remove selected candidate + candidates[idx] = candidates[--num_candidates]; + } + + // Sort the offspring elements (not including the first K value) + qsort(&offspring[1], K, sizeof(int), compare_ints); + + // Clean up the intean arrays for next use - only clean used entries + for (int i = 1; i <= K; i++) { + in_parent1[parent1[i]] = 0; + in_parent2[parent2[i]] = 0; + } + for (int i = 1; i <= K; i++) { + in_offspring[offspring[i]] = 0; + } +} + +// Count unique subsets in a sorted array of subsets +// Returns the number of unique subsets and prints the percentage +int count_unique_subsets(int* sorted_subsets, int num_subsets, int subset_size, const char* label) { + if (num_subsets == 0) return 0; + + int unique_count = 1; // First subset is always unique + int K = sorted_subsets[0]; // Get K from first subset + + // Compare each subset with the previous one + for (int i = 1; i < num_subsets; i++) { + int* current = &sorted_subsets[i * subset_size]; + int* previous = &sorted_subsets[(i - 1) * subset_size]; + + // Compare all K elements (skip position 0 which has K, and K+1 which has cost) + int is_different = 0; + for (int j = 1; j <= K; j++) { + if (current[j] != previous[j]) { + is_different = 1; + break; + } + } + + if (is_different) { + unique_count++; + } + } + + double percentage = (unique_count * 100.0) / num_subsets; + printf("%s: %d unique subsets out of %d (%.1f%%)\n", + label, unique_count, num_subsets, percentage); + + return unique_count; +} + +// Main genetic algorithm +int genetic_subset_selection(int N, int K, int B, int L, + int verbose, + int (*cost_function)(int*, int, void *), + int** best_subsets_history, + int* iterations_used, + void * pUserData) { + int print_unique = 0; + int M = B * (B - 1) / 2; + int subset_size = K + 2; // K value + K elements + cost + + // Allocate arrays + int* current_generation = (int*)malloc(M * subset_size * sizeof(int)); + int* next_generation = (int*)malloc(M * subset_size * sizeof(int)); + int* all_best_subsets = (int*)calloc(B * L * subset_size, sizeof(int)); + + // Pre-allocate reusable arrays for create_offspring + int* in_offspring = (int*)calloc(N, sizeof(int)); + int* in_parent1 = (int*)calloc(N, sizeof(int)); + int* in_parent2 = (int*)calloc(N, sizeof(int)); + int* candidates = (int*)malloc(K * 2 * sizeof(int)); + + // Generate initial population + for (int i = 0; i < M; i++) { + int* subset = ¤t_generation[i * subset_size]; + generate_random_subset(subset, K, N); + // Cost function uses elements starting at position 1 + subset[K + 1] = cost_function(&subset[1], K, pUserData); + } + + // Sort to get best B subsets + qsort(current_generation, M, subset_size * sizeof(int), compare_subsets_by_cost); + if ( print_unique ) count_unique_subsets(current_generation, M, subset_size, "Initial population"); + + int best_cost = current_generation[K + 1]; + int generation = 0; + int total_best_count = 0; + + // Main evolutionary loop + while (generation < L) { + // Store best B subsets from current generation + for (int i = 0; i < B && total_best_count < B * L; i++) { + memcpy(&all_best_subsets[total_best_count * subset_size], + ¤t_generation[i * subset_size], + subset_size * sizeof(int)); + total_best_count++; + } + + if ( verbose ) { + printf( "Iter %d\n", generation ); + for (int i = 0; i < B; i++ ) { + printf( "Subset %2d : {", i ); + for (int k = 0; k < K; k++ ) + printf( " %2d", (¤t_generation[i * subset_size])[k+1] ); + printf( " } " ); + printf( "Cost %2d\n", (¤t_generation[i * subset_size])[K+1] ); + } + } + + // Create next generation from pairs of best B subsets + int offspring_idx = 0; + for (int i = 0; i < B; i++) { + for (int j = i + 1; j < B; j++) { + int* parent1 = ¤t_generation[i * subset_size]; + int* parent2 = ¤t_generation[j * subset_size]; + int* offspring = &next_generation[offspring_idx * subset_size]; + + create_offspring(parent1, parent2, offspring, K, N, + in_offspring, in_parent1, in_parent2, candidates); + offspring[K + 1] = cost_function(&offspring[1], K, pUserData); + offspring_idx++; + } + } + + // Sort next generation + qsort(next_generation, M, subset_size * sizeof(int), compare_subsets_by_cost); + if ( print_unique ) count_unique_subsets(next_generation, M, subset_size, "Next generation"); + + generation++; + + // Check for improvement + int new_best_cost = next_generation[K + 1]; + if (new_best_cost >= best_cost) { + break; // No improvement + } + + best_cost = new_best_cost; + + // Swap generations + int* temp = current_generation; + current_generation = next_generation; + next_generation = temp; + } + + // Sort all best subsets collected using qsort + qsort(all_best_subsets, total_best_count, subset_size * sizeof(int), compare_subsets_by_cost); + if ( print_unique ) count_unique_subsets(all_best_subsets, total_best_count, subset_size, "All best subsets"); + + if ( verbose ) { + printf( "Final best\n" ); + for (int i = 0; i < B; i++ ) { + printf( "Subset %2d : {", i ); + for (int k = 0; k < K; k++ ) + printf( " %2d", (&all_best_subsets[i * subset_size])[k+1] ); + printf( " } " ); + printf( "Cost %2d\n", (&all_best_subsets[i * subset_size])[K+1] ); + } + } + + // Free pre-allocated arrays + free(in_offspring); + free(in_parent1); + free(in_parent2); + free(candidates); + + // Return results + if (best_subsets_history != NULL) { + *best_subsets_history = all_best_subsets; + } else { + free(all_best_subsets); + } + + if (iterations_used != NULL) { + *iterations_used = generation + 1; + } + + free(current_generation); + free(next_generation); + + return best_cost; +} + +// Test bench +/* +int bs_find_test() { + srand(time(NULL)); + + // Test parameters + int N = 30; // Total numbers (0 to 29) + int K = 6; // Subset size + int B = 10; // Number of best subsets to keep + int L = 50; // Maximum iterations + + printf("Genetic Algorithm for Subset Selection\n"); + printf("======================================\n"); + printf("Parameters:\n"); + printf(" N (total numbers): %d\n", N); + printf(" K (subset size): %d\n", K); + printf(" B (best subsets): %d\n", B); + printf(" L (max iterations): %d\n", L); + printf("\n"); + + // Run the algorithm + int* best_subsets_history = NULL; + int iterations_used = 0; + int best_cost = genetic_subset_selection(N, K, B, L, 0, + cost_sum_squares, + &best_subsets_history, + &iterations_used); + + printf("Best cost found: %d\n", best_cost); + printf("Iterations until convergence: %d\n\n", iterations_used); + + // Display top 5 best subsets + printf("Top 5 best subsets:\n"); + int subset_size = K + 2; + for (int i = 0; i < 5 && i < B * L; i++) { + int* subset = &best_subsets_history[i * subset_size]; + if (subset[K + 1] == 0) break; // Check if cost is 0 (uninitialized) + + printf("Subset %d: {", i + 1); + for (int j = 1; j <= K; j++) { + printf("%d", subset[j]); + if (j < K) printf(", "); + } + printf("} - Cost: %d\n", subset[K + 1]); + } + + // Test with different parameters + printf("\n\nTesting with different parameters:\n"); + printf("==================================\n"); + + // Test 1: Smaller problem + N = 20; K = 4; B = 6; L = 30; + free(best_subsets_history); + best_subsets_history = NULL; + iterations_used = 0; + + best_cost = genetic_subset_selection(N, K, B, L, 0, + cost_sum_squares, + &best_subsets_history, + &iterations_used); + + printf("\nTest 1 - N=%d, K=%d, B=%d, L=%d\n", N, K, B, L); + printf("Best cost: %d\n", best_cost); + printf("Iterations until convergence: %d\n", iterations_used); + printf("Best subset: {"); + for (int j = 1; j <= K; j++) { + printf("%d", best_subsets_history[j]); + if (j < K) printf(", "); + } + printf("}\n"); + + // Test 2: Larger problem + N = 50; K = 8; B = 15; L = 100; + free(best_subsets_history); + best_subsets_history = NULL; + iterations_used = 0; + + best_cost = genetic_subset_selection(N, K, B, L, 0, + cost_sum_squares, + &best_subsets_history, + &iterations_used); + + printf("\nTest 2 - N=%d, K=%d, B=%d, L=%d\n", N, K, B, L); + printf("Best cost: %d\n", best_cost); + printf("Iterations until convergence: %d\n", iterations_used); + printf("Best subset: {"); + for (int j = 1; j <= K; j++) { + printf("%d", best_subsets_history[j]); + if (j < K) printf(", "); + } + printf("}\n"); + + // Test 3: Test convergence speed with different B values + printf("\n\nConvergence Speed Analysis:\n"); + printf("===========================\n"); + N = 40; K = 7; L = 100; + + for (int B_test = 5; B_test <= 20; B_test += 5) { + free(best_subsets_history); + best_subsets_history = NULL; + iterations_used = 0; + + best_cost = genetic_subset_selection(N, K, B_test, L, 0, + cost_sum_squares, + &best_subsets_history, + &iterations_used); + + printf("B=%2d: Best cost=%3d, Iterations=%3d\n", + B_test, best_cost, iterations_used); + } + + free(best_subsets_history); + + return 0; +} +*/ + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Wrd_t * Gia_ManBsFindStart( Gia_Man_t * pGia, int nWords ) +{ + Vec_Wrd_t * vSims = Vec_WrdStartRandom( (Gia_ManCiNum(pGia) + 1) * nWords ); + Vec_WrdFillExtra( vSims, Gia_ManObjNum(pGia) * nWords, 0 ); + Abc_TtClear( Vec_WrdArray(vSims), nWords ); + return vSims; +} +void Gia_ManBsFindNext( Gia_Man_t * pGia, int nWords, Vec_Wrd_t * vSims, Vec_Wrd_t * vSimsOuts, int iMint ) +{ + Gia_Obj_t * pObj; int i; + word * pSims[2], * pSim; + Gia_ManForEachAnd( pGia, pObj, i ) { + pSim = Vec_WrdEntryP( vSims, i * nWords ); + pSims[0] = Vec_WrdEntryP( vSims, Gia_ObjFaninId0(pObj, i) * nWords ); + pSims[1] = Vec_WrdEntryP( vSims, Gia_ObjFaninId1(pObj, i) * nWords ); + Abc_TtAndCompl( pSim, pSims[0], Gia_ObjFaninC0(pObj), pSims[1], Gia_ObjFaninC1(pObj), nWords ); + } + Gia_ManForEachCo( pGia, pObj, i ) { + pSim = Vec_WrdEntryP( vSimsOuts, (iMint * Gia_ManCoNum(pGia) + i) * nWords ); + pSims[0] = Vec_WrdEntryP( vSims, Gia_ObjFaninId0p(pGia, pObj) * nWords ); + Abc_TtCopy( pSim, pSims[0], nWords, Gia_ObjFaninC0(pObj) ); + } +} +int Gia_ManBsFindMyu( Gia_Man_t * p, int nWords, Vec_Wrd_t * vSims, Vec_Wrd_t * vSimsOuts, Vec_Int_t * vVarNums, Vec_Wrd_t * vTemp ) +{ + int nMints = 1 << Vec_IntSize(vVarNums); + int nWordsAll = Gia_ManCoNum(p) * nWords; + int nMyu = 0, pMyu[256], i, k, Var; + assert( nMints <= 256 ); + assert( Vec_WrdSize(vTemp) == nWords * Vec_IntSize(vVarNums) ); + assert( Vec_WrdSize(vSimsOuts) == nMints * nWordsAll ); + Vec_IntForEachEntry( vVarNums, Var, i ) + Abc_TtCopy( Vec_WrdEntryP(vTemp, i * nWords), Vec_WrdEntryP(vSims, Gia_ManCiIdToId(p, Var) * nWords), nWords, 0 ); + for ( int m = 0; m < nMints; m++ ) { + Vec_IntForEachEntry( vVarNums, Var, i ) + Abc_TtConst( Vec_WrdEntryP(vSims, Gia_ManCiIdToId(p, Var) * nWords), nWords, (m >> i) & 1 ); + Gia_ManBsFindNext( p, nWords, vSims, vSimsOuts, m ); + } + Vec_IntForEachEntry( vVarNums, Var, i ) + Abc_TtCopy( Vec_WrdEntryP(vSims, Gia_ManCiIdToId(p, Var) * nWords), Vec_WrdEntryP(vTemp, i * nWords), nWords, 0 ); + for ( i = 0; i < nMints; i++ ) { + word * pSim = Vec_WrdEntryP(vSimsOuts, nWordsAll * i); + for ( k = 0; k < nMyu; k++ ) + if ( Abc_TtEqual(pSim, Vec_WrdEntryP(vSimsOuts, nWordsAll * pMyu[k]), nWordsAll) ) + break; + if ( k == nMyu ) + pMyu[nMyu++] = i; + } + return nMyu; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ + +typedef struct BsFind_UserData_t_ { + Gia_Man_t * pGia; + int nWords; + Vec_Wrd_t * vSims; + Vec_Wrd_t * vSimsOuts; + Vec_Int_t * vVarNums; + Vec_Wrd_t * vTemp; +} BsFind_UserData_t; + +BsFind_UserData_t * Gia_ManBsFindMyuFunctionStart( Gia_Man_t * pGia, int nWords, int nLutSize ) +{ + BsFind_UserData_t * p = ABC_CALLOC( BsFind_UserData_t, 1 ); + p->pGia = pGia; + p->nWords = nWords; + p->vSims = Gia_ManBsFindStart( pGia, nWords ); + p->vSimsOuts = Vec_WrdStart( (1 << nLutSize) * Gia_ManCoNum(pGia) * nWords ); + p->vVarNums = Vec_IntAlloc( nLutSize ); + p->vTemp = Vec_WrdStart( nLutSize * nWords ); + return p; +} +int Gia_ManBsFindMyuFunction( int * subset, int K, void * pUserData ) +{ + BsFind_UserData_t * p = (BsFind_UserData_t *)pUserData; + Vec_IntClear( p->vVarNums ); + for ( int i = 0; i < K; i++ ) + Vec_IntPush( p->vVarNums, subset[i] ); + return Gia_ManBsFindMyu( p->pGia, p->nWords, p->vSims, p->vSimsOuts, p->vVarNums, p->vTemp ); +} +void Gia_ManBsFindMyuFunctionStop( BsFind_UserData_t * p ) +{ + Vec_WrdFree( p->vSims ); + Vec_WrdFree( p->vSimsOuts ); + Vec_WrdFree( p->vTemp ); + Vec_IntFree( p->vVarNums ); + ABC_FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Gia_ManBsFindBest( Gia_Man_t * pGia, int nWords, int nLutSize, int nBest, int nIterMax, int fVerbose ) +{ + abctime clk = Abc_Clock(); + Abc_Random(1); + BsFind_UserData_t * p = Gia_ManBsFindMyuFunctionStart( pGia, nWords, nLutSize ); + int nIters = 0, Res = genetic_subset_selection( Gia_ManCiNum(pGia), nLutSize, nBest, nIterMax, fVerbose, Gia_ManBsFindMyuFunction, NULL, &nIters, (void *)p ); + printf( "The best Myu %d was found after considering %d bound-sets in %d iterations. ", Res, nIters*nBest*(nBest-1)/2, nIters ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + Gia_ManBsFindMyuFunctionStop( p ); + return Res; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + +ABC_NAMESPACE_IMPL_END + diff --git a/src/aig/gia/giaDup.c b/src/aig/gia/giaDup.c index be6eef7a2..099f33693 100644 --- a/src/aig/gia/giaDup.c +++ b/src/aig/gia/giaDup.c @@ -6041,7 +6041,7 @@ Vec_Wec_t * Gia_ManCollectIntTfos( Gia_Man_t * p, Vec_Int_t * vVarNums ) Gia_Man_t * Gia_ManDupCofs( Gia_Man_t * p, Vec_Int_t * vVarNums ) { int i, iLit, nMints = 1 << Vec_IntSize(vVarNums); - Vec_Int_t * vOutLits = Vec_IntAlloc( nMints * Gia_ManCoNum(p) ); + Vec_Int_t * vOutLits = Vec_IntStartFull( nMints * Gia_ManCoNum(p) ); Vec_Wec_t * vTfos = Gia_ManCollectIntTfos( p, vVarNums ); Gia_Man_t * pNew, * pTemp; Gia_Obj_t * pObj; assert( Gia_ManRegNum(p) == 0 ); @@ -6057,7 +6057,7 @@ Gia_Man_t * Gia_ManDupCofs( Gia_Man_t * p, Vec_Int_t * vVarNums ) Gia_ManForEachAnd( p, pObj, i ) pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); Gia_ManForEachCo( p, pObj, i ) - Vec_IntPush( vOutLits, Gia_ObjFanin0Copy(pObj) ); + Vec_IntWriteEntry( vOutLits, i, Gia_ObjFanin0Copy(pObj) ); int m, g, x, b = 0; for ( m = 1; m < nMints; m++ ) { @@ -6067,7 +6067,7 @@ Gia_Man_t * Gia_ManDupCofs( Gia_Man_t * p, Vec_Int_t * vVarNums ) Gia_ManForEachObjVec( vNode, p, pObj, i ) pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); Gia_ManForEachCo( p, pObj, i ) - Vec_IntPush( vOutLits, Gia_ObjFanin0Copy(pObj) ); + Vec_IntWriteEntry( vOutLits, g * Gia_ManCoNum(p) + i, Gia_ObjFanin0Copy(pObj) ); } assert( Vec_IntFindMin(vOutLits) >= 0 ); Vec_IntForEachEntry( vOutLits, iLit, i ) diff --git a/src/aig/gia/module.make b/src/aig/gia/module.make index 2e64d1d1b..b4e2efb8b 100644 --- a/src/aig/gia/module.make +++ b/src/aig/gia/module.make @@ -6,6 +6,7 @@ SRC += src/aig/gia/giaAig.c \ src/aig/gia/giaBalLut.c \ src/aig/gia/giaBalMap.c \ src/aig/gia/giaBidec.c \ + src/aig/gia/giaBsFind.c \ src/aig/gia/giaCCof.c \ src/aig/gia/giaCex.c \ src/aig/gia/giaClp.c \ diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index d6fd5c5c6..646e60696 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -641,6 +641,7 @@ static int Abc_CommandAbc9FunAbs ( Abc_Frame_t * pAbc, int argc, cha static int Abc_CommandAbc9DsdInfo ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9FunTrace ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9MulFind ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAbc9BsFind ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9AndCare ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9Test ( Abc_Frame_t * pAbc, int argc, char ** argv ); @@ -1465,7 +1466,8 @@ void Abc_Init( Abc_Frame_t * pAbc ) Cmd_CommandAdd( pAbc, "ABC9", "&dsdinfo", Abc_CommandAbc9DsdInfo, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&funtrace", Abc_CommandAbc9FunTrace, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&mulfind", Abc_CommandAbc9MulFind, 0 ); - Cmd_CommandAdd( pAbc, "ABC9", "&andcare", Abc_CommandAbc9AndCare, 0 ); + Cmd_CommandAdd( pAbc, "ABC9", "&bsfind", Abc_CommandAbc9BsFind, 0 ); + Cmd_CommandAdd( pAbc, "ABC9", "&andcare", Abc_CommandAbc9AndCare, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&test", Abc_CommandAbc9Test, 0 ); @@ -57248,6 +57250,99 @@ usage: return 1; } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAbc9BsFind( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + extern int Gia_ManBsFindBest( Gia_Man_t * pGia, int nWords, int nLutSize, int nBest, int nIterMax, int fVerbose ); + int c, nWords = 256, nLutSize = 6, nBest = 20, nIterMax = 10, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "WKBIvh" ) ) != EOF ) + { + switch ( c ) + { + case 'W': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-W\" should be followed by an integer.\n" ); + goto usage; + } + nWords = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nWords < 0 ) + goto usage; + break; + case 'K': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-K\" should be followed by an integer.\n" ); + goto usage; + } + nLutSize = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nLutSize < 0 ) + goto usage; + break; + case 'B': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-B\" should be followed by an integer.\n" ); + goto usage; + } + nBest = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nBest < 0 ) + goto usage; + break; + case 'I': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + nIterMax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nIterMax < 0 ) + goto usage; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pAbc->pGia == NULL ) + { + Abc_Print( -1, "Abc_CommandAbc9MulFind(): There is no AIG.\n" ); + return 0; + } + Gia_ManBsFindBest( pAbc->pGia, nWords, nLutSize, nBest, nIterMax, fVerbose ); + return 0; + +usage: + Abc_Print( -2, "usage: &bsfind [-WKBI num] [-vh]\n" ); + Abc_Print( -2, "\t found a good boundset for the multi-output function\n" ); + Abc_Print( -2, "\t-W num : the number of simulation words to use [default = %d]\n", nWords ); + Abc_Print( -2, "\t-K num : the number of bound-set variables (LUT size) [default = %d]\n", nLutSize ); + Abc_Print( -2, "\t-B num : the number of best bound-sets to consider [default = %d]\n", nBest ); + Abc_Print( -2, "\t-I num : the number of refinement iterations to perform [default = %d]\n", nIterMax ); + Abc_Print( -2, "\t-v : toggles printing verbose information [default = %s]\n", fVerbose ? "yes": "no" ); + Abc_Print( -2, "\t-h : print the command usage\n"); + return 1; +} + /**Function************************************************************* Synopsis [] From c738ed6e86e6ca10bcef0d4c649cc07d925b4be8 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Tue, 5 Aug 2025 22:50:06 -0700 Subject: [PATCH 05/22] =?UTF-8?q?Integrating=20prefix=20adder=20generation?= =?UTF-8?q?=20code=20by=20Martin=20Povi=C5=A1er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abclib.dsp | 4 + src/aig/gia/giaMan.c | 32 ++ src/base/abci/abc.c | 92 ++- src/misc/util/module.make | 1 + src/misc/util/utilPrefix.cpp | 1029 ++++++++++++++++++++++++++++++++++ 5 files changed, 1157 insertions(+), 1 deletion(-) create mode 100644 src/misc/util/utilPrefix.cpp diff --git a/abclib.dsp b/abclib.dsp index 33bc81c99..9e99fb946 100644 --- a/abclib.dsp +++ b/abclib.dsp @@ -4991,6 +4991,10 @@ SOURCE=.\src\misc\util\utilPth.c # End Source File # Begin Source File +SOURCE=.\src\misc\util\utilPrefix.cpp +# End Source File +# Begin Source File + SOURCE=.\src\misc\util\utilSignal.c # End Source File # Begin Source File diff --git a/src/aig/gia/giaMan.c b/src/aig/gia/giaMan.c index 2f271e35c..efedcfb0a 100644 --- a/src/aig/gia/giaMan.c +++ b/src/aig/gia/giaMan.c @@ -2373,6 +2373,38 @@ Gia_Man_t * Gia_GenPutOnTop( char ** pFNames, int nFNames ) return pNew; } + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Gia_Man_t * Gia_ManDupFromArray( int * pObjs, int nObjs, int nIns, int nLatches, int nOuts, int nAnds ) +{ + Gia_Man_t * pNew = Gia_ManStart( nObjs ); int i; + for ( i = 0; i < nIns + nLatches; i++ ) + Gia_ManAppendCi(pNew); + for ( i = 0; i < nAnds; i++ ) + { + int uLit = 2*(1+nIns+nLatches+i); + int uLit0 = pObjs[uLit+0]; + int uLit1 = pObjs[uLit+1]; + int uLit2 = Gia_ManAppendAnd( pNew, uLit0, uLit1 ); + assert( uLit2 == uLit ); + } + for ( i = 0; i < nOuts + nLatches; i++ ) + Gia_ManAppendCo( pNew, pObjs[2*(nObjs-nOuts-nLatches+i)+0] ); + Gia_ManSetRegNum(pNew, nLatches); + return pNew; +} + + //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 646e60696..67fec1cb2 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -636,6 +636,7 @@ static int Abc_CommandAbc9GenComp ( Abc_Frame_t * pAbc, int argc, cha static int Abc_CommandAbc9GenSorter ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9GenNeuron ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9GenAdder ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAbc9GenPrefix ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9Window ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9FunAbs ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9DsdInfo ( Abc_Frame_t * pAbc, int argc, char ** argv ); @@ -1461,6 +1462,7 @@ void Abc_Init( Abc_Frame_t * pAbc ) Cmd_CommandAdd( pAbc, "ABC9", "&gensorter", Abc_CommandAbc9GenSorter, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&genneuron", Abc_CommandAbc9GenNeuron, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&genadder", Abc_CommandAbc9GenAdder, 0 ); + Cmd_CommandAdd( pAbc, "ABC9", "&genprefix", Abc_CommandAbc9GenPrefix, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&window", Abc_CommandAbc9Window, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&funabs", Abc_CommandAbc9FunAbs, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&dsdinfo", Abc_CommandAbc9DsdInfo, 0 ); @@ -56697,7 +56699,7 @@ int Abc_CommandAbc9GenAdder( Abc_Frame_t * pAbc, int argc, char ** argv ) return 0; usage: - Abc_Print( -2, "usage: &genadder [-N ] [-sbhcv] \n" ); + Abc_Print( -2, "usage: &genadder [-N ] [-sbhcv]\n" ); Abc_Print( -2, "\t generates a prefix adder (by default, the ripple carry adder)\n" ); Abc_Print( -2, "\t-N num : the bit-width of the adder [default = undefined]\n" ); Abc_Print( -2, "\t-s : toggles using Sklansky adder [default = %s]\n", fSK ? "yes": "no" ); @@ -56708,6 +56710,94 @@ usage: return 1; } + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAbc9GenPrefix( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + extern int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose); + extern Gia_Man_t * Gia_ManDupFromArray( int * pObjs, int nObjs, int nIns, int nLatches, int nOuts, int nAnds ); + int c, nBits = 8, nFans = 4, fDumpVer = 0, fDumpMiter = 0, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "NFdmv" ) ) != EOF ) + { + switch ( c ) + { + case 'N': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-N\" should be followed by an integer.\n" ); + goto usage; + } + nBits = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nBits < 0 ) + goto usage; + break; + case 'F': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-F\" should be followed by an integer.\n" ); + goto usage; + } + nFans = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nFans < 0 ) + goto usage; + break; + case 'd': + fDumpVer ^= 1; + break; + case 'm': + fDumpMiter ^= 1; + break; + case 'v': + fVerbose ^= 1; + break; + default: + goto usage; + } + } + if ( nBits < 1 ) + { + Abc_Print( -1, "Abc_CommandAbc9GenPrefix(): The number of inputs should be defined on the command line \"-N num\".\n" ); + return 0; + } + else + { + int nObjs = 0, nIns = 0, nLatches = 0, nOuts = 0, nAnds = 0; + int * pObjs = adder_return_array( nBits, nFans, &nObjs, &nIns, &nLatches, &nOuts, &nAnds, fDumpVer, fDumpMiter, fVerbose ); + Gia_Man_t * pTemp = Gia_ManDupFromArray( pObjs, nObjs, nIns, nLatches, nOuts, nAnds ); + Abc_FrameUpdateGia( pAbc, pTemp ); + ABC_FREE( pObjs ); + } + return 0; + +usage: + Abc_Print( -2, "usage: &genprefix [-NF ] [-dcv]\n" ); + Abc_Print( -2, "\t generates a prefix adder with minimum depth\n" ); + Abc_Print( -2, "\t-N num : the bit-width of the adder [default = %d]\n", nBits ); + Abc_Print( -2, "\t-F num : the limit on the fanout count [default = %d]\n", nFans ); + Abc_Print( -2, "\t-d : toggles dumping the adder in Verilog [default = %s]\n", fDumpVer ? "yes": "no" ); + Abc_Print( -2, "\t-c : toggles dumping the miter in Verilog [default = %s]\n", fDumpMiter ? "yes": "no" ); + Abc_Print( -2, "\t-v : toggles printing verbose information [default = %s]\n\n", fVerbose ? "yes": "no" ); + Abc_Print( -2, "\t The code of this command is contributed by Martin Povišer \n\n" ); + Abc_Print( -2, "\t The implementation is inspired by S. Roy, M. Choudhury, R. Puri, D. Pan,\n" ); + Abc_Print( -2, "\t \"Polynomial time algorithm for area and power efficient adder synthesis\n" ); + Abc_Print( -2, "\t in high-performance designs\", Proc. ASP-DAC 2025.\n" ); + Abc_Print( -2, "\t https://www.cerc.utexas.edu/utda/publications/C166.pdf\n" ); + return 1; +} + /**Function************************************************************* Synopsis [] diff --git a/src/misc/util/module.make b/src/misc/util/module.make index bf3538360..937c4a982 100644 --- a/src/misc/util/module.make +++ b/src/misc/util/module.make @@ -5,6 +5,7 @@ SRC += src/misc/util/utilBridge.c \ src/misc/util/utilFile.c \ src/misc/util/utilIsop.c \ src/misc/util/utilNam.c \ + src/misc/util/utilPrefix.cpp \ src/misc/util/utilPth.c \ src/misc/util/utilSignal.c \ src/misc/util/utilSort.c diff --git a/src/misc/util/utilPrefix.cpp b/src/misc/util/utilPrefix.cpp new file mode 100644 index 000000000..ca3e54d82 --- /dev/null +++ b/src/misc/util/utilPrefix.cpp @@ -0,0 +1,1029 @@ +/**CFile**************************************************************** + + FileName [utilPrefix.cpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Generating prefix adders.] + + Synopsis [Generating prefix adders.] + + Author [Martin Povišer] + + Affiliation [] + + Date [Ver. 1.0. Started -August 5, 2025.] + + Revision [$Id: utilPrefix.cpp,v 1.00 2025/08/05 00:00:00 Exp $] + +***********************************************************************/ + +/* + The implementation is inspired by S. Roy, M. Choudhury, R. Puri, D. Pan, + "Polynomial time algorithm for area and power efficient adder synthesis + in high-performance designs", Proc. ASP-DAC 2025. + + https://www.cerc.utexas.edu/utda/publications/C166.pdf +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class Graph; +class Node { +public: + Node(int bitpos, int level=0) + : level(level), msb(bitpos), lsb(bitpos), pi(true) + { + } + + Node(Node *fanin1, Node *fanin2) + : fanin1(fanin1), fanin2(fanin2) + { + assert(fanin1 && fanin2); + assert(fanin1->lsb == fanin2->msb + 1); + level = std::max(fanin1->level, fanin2->level) + 1; + msb = fanin1->msb; + lsb = fanin2->lsb; + ref_fanins(); + } + + ~Node() + { + assert(fanouts.empty()); + deref_fanins(); + } + + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + Node(Node&& other) noexcept { + fanin1 = other.fanin1; + fanin2 = other.fanin2; + level = other.level; + msb = other.msb; + lsb = other.lsb; + other.clear_fanins(); + ref_fanins(); + } + + Node& operator=(Node&& other) noexcept { + pi = other.pi; + deref_fanins(); + fanin1 = other.fanin1; + fanin2 = other.fanin2; + level = other.level; + msb = other.msb; + lsb = other.lsb; + other.clear_fanins(); + ref_fanins(); + return *this; + } + + void update_levels() { + if (!pi) + level = std::max(fanin1->level, fanin2->level) + 1; + } + + void update_cap(Graph &graph, bool force=false); + + void clear_fanins() { + deref_fanins(); + fanin1 = nullptr; + fanin2 = nullptr; + } + + int level; + int level_cap = std::numeric_limits::max(); + std::set fanouts; + int msb, lsb; + bool pi = false; + Node *fanin1 = nullptr, *fanin2 = nullptr; + + int node_id; + + int nfanouts() { + return fanouts.size(); + } + + void deref_fanins() { + if (fanin1) + fanin1->fanouts.erase(this); + if (fanin2) + fanin2->fanouts.erase(this); + } + + void ref_fanins() { + if (fanin1) + fanin1->fanouts.insert(this); + if (fanin2) + fanin2->fanouts.insert(this); + } +}; + +class Column { +public: + Column(int position) + { + nodes.emplace_back(position); + } + + ~Column() + { + while (!nodes.empty()) + nodes.pop_back(); + } + + Column(const Column&) = delete; + Column& operator=(const Column&) = delete; + + std::list nodes; + int level_constraint; + + void link(Node *fanin2) + { + Node *fanin1 = &nodes.back(); + nodes.emplace_back(Node(fanin1, fanin2)); + } + + void link(Column &other) + { + Node *fanin1 = &nodes.back(); + Node *fanin2 = &other.nodes.back(); + nodes.emplace_back(Node(fanin1, fanin2)); + } + + bool has_level(int number) + { + for (auto &node : nodes) + if (node.level == number) + return true; + return false; + } + + int no_nodes() + { + return (int) nodes.size(); + } + + int level_target = std::numeric_limits::max(); +}; + +struct Graph { + int bitwidth_; + int fanout_constraint = std::numeric_limits::max(); + Column **columns; + + ~Graph() + { + for (int i = bitwidth_ - 1; i >= 0; i--) + delete columns[i]; + delete[] columns; + } + + void initialize(int ninputs) + { + bitwidth_ = ninputs; + columns = new Column*[ninputs]; + for (int i = 0; i < ninputs; i++) { + columns[i] = new Column(i); + } + } + + void set_max_fanout(int fanout) + { + fanout_constraint = fanout; + } + + void set_max_depth(int depth) + { + for (int i = 0; i < bitwidth(); i++) { + columns[i]->level_target = depth; + } + } + + int bitwidth() + { + return bitwidth_; + } + + Column &operator[](int i) + { + return *(columns[i]); + } + + int min_level() + { + int level = std::numeric_limits::max();; + for (int i = 0; i < bitwidth(); i++) + level = std::min(level, columns[i]->nodes.front().level); + return level; + } + + int max_level() + { + int level = std::numeric_limits::min(); + for (int i = 0; i < bitwidth(); i++) { + level = std::max(level, columns[i]->nodes.back().level); + } + return level; + } + + int size() + { + int size = 0; + for (int i = 0; i < bitwidth(); i++) + size += columns[i]->nodes.size() - 1; + return size; + } + + int max_fanout() + { + int max_fanout = 0; + for (int i = 0; i < bitwidth(); i++) { + for (auto &node : columns[i]->nodes) { + max_fanout = std::max(max_fanout, node.nfanouts()); // + (node.lsb == 0)); + } + } + return max_fanout; + } + + bool validate() + { + for (int i = 0; i < bitwidth(); i++) { + if (columns[i]->nodes.back().lsb != 0) + return false; + } + return true; + } + + void print() + { + for (int j = 0; j < bitwidth(); j++) + for (auto &node : (*this)[j].nodes) + node.update_levels(); + + int min_level1 = min_level(); + int max_level1 = max_level(); + + std::cout << "\n" << "size=" << size() \ + << " max_fanout=" << max_fanout() << " depth=" << max_level1 - min_level1 \ + << " " << (validate() ? "(validates)" : "") << "\n"; + + for (int i = max_level1 + 1; i >= min_level1; i--) { + std::cout << " "; + for (int j = bitwidth() - 1; j >= 0; j--) { + if (i == (*this)[j].nodes.front().level) { + std::cout << j % 10; + } else if (i >= (*this)[j].nodes.front().level && i <= (*this)[j].nodes.back().level) { + if ((*this)[j].has_level(i)) + std::cout << "#"; + else + std::cout << "."; + } else if (i - 1 == (*this)[j].level_target) { + std::cout << "-"; + } else { + std::cout << " "; + } + } + std::cout << "\n"; + } + } +}; + +void Node::update_cap(Graph &graph, bool force) { + int cap = std::numeric_limits::max(); + if (lsb == 0) { + cap = std::min(cap, graph.columns[msb]->level_target); + } + for (auto fanout : fanouts) { + cap = std::min(cap, fanout->level_cap - 1); + } + + if (cap != level_cap || force) { + level_cap = cap; + if (fanin1) + fanin1->update_cap(graph); + if (fanin2) + fanin2->update_cap(graph); + } +} + +int ceil_log2(int x) +{ + if (x <= 0) + return 0; + x -= 1; + for (int i = 0;; i++, x >>= 1) + if (!x) + return i; +} + +void search(Node &node, Graph &graph, std::vector &candidate, + std::vector &best, int level_cap, int lsb, int ttl, bool first=false) +{ + if (node.lsb == lsb) { + /* + printf("- "); + for (auto &node : candidate) { + printf("[%d:%d] ", node->msb, node->lsb); + } + printf("\n"); + */ + if (best.empty() || (candidate.size() <= best.size())) { + best = candidate; + } + } else if ((best.empty() || candidate.size() <= best.size() - 1) && ttl >= 1) { + auto &nodes = graph.columns[node.lsb - 1]->nodes; + for (auto it = nodes.rbegin(); it != nodes.rend(); ++it) { + auto &next_node = *it; + if ((first || next_node.level > node.level) && next_node.level <= level_cap - 1 + && next_node.nfanouts() < graph.fanout_constraint && next_node.lsb >= lsb) { + candidate.push_back(&next_node); + search(next_node, graph, candidate, best, level_cap, lsb, + std::min(ttl, level_cap - next_node.level - 1)); + candidate.pop_back(); + } + } + } +} + +void greedy(Graph &graph, bool algo2=false, bool print=false) +{ + for (int i = graph.bitwidth() - 1; i >= 0; i--) + graph[i].nodes.back().update_cap(graph); + + for (int i = graph.bitwidth() - 1; i >= 0; i--) { + Column &column = graph[i]; + + for (auto base = std::prev(std::end(column.nodes)); base != std::begin(column.nodes);) { + auto head = base; + + // find consecutive nodes `base` to `head` within the column which we can replace; + int prior_area = 1; + while (!std::prev(base)->pi && std::prev(base)->nfanouts() == 1 \ + && (!algo2 || !std::prev(std::prev(base))->pi)) { + base--; + prior_area++; + } + + if (prior_area >= 2) { + std::vector candidate, best; + auto next_head = std::prev(base); + + for (auto it = base; it != std::next(head); it++) + it->deref_fanins(); + + search(*next_head, graph, candidate, best, head->level_cap, + head->lsb, std::numeric_limits::max(), true); + + for (auto it = base; it != std::next(head); it++) + it->ref_fanins(); + + std::vector old_fanins; + if (best.size() < static_cast(prior_area) && !best.empty()) { + if (print) { + printf("(%d) replacing ", i); + for (auto it = base; it != std::next(head); it++) + printf("[%d:%d] ", it->fanin2->msb, it->fanin2->lsb); + printf("with "); + for (auto node : best) + printf("[%d:%d] ", node->msb, node->lsb); + printf("\n"); + } + + // clear the head so that it derefs its fanin1 + old_fanins.push_back(head->fanin2); + head->clear_fanins(); + + // remove all but the head; head will be replaced + // in place so that its pointer is preserved (it has + // fanouts) + auto to_remove = std::prev(head); + while (to_remove != next_head) { + auto next = std::prev(to_remove); + old_fanins.push_back(to_remove->fanin2); + column.nodes.erase(to_remove); + to_remove = next; + } + + // insert replacement nodes + auto fanin1 = next_head; + for (auto fanin2 : best) { + if (fanin2->lsb == head->lsb) { + *head = Node(&*fanin1, fanin2); + } else { + fanin1 = column.nodes.insert(std::next(fanin1), Node(&*fanin1, fanin2)); + } + } + + for (auto it = head; it != next_head; it--) + it->update_cap(graph); + for (auto old_fanin : old_fanins) + old_fanin->update_cap(graph); + for (auto new_fanin : best) + new_fanin->update_cap(graph); + next_head->update_cap(graph, true); + + base = next_head; + } else { + base--; + } + } else { + base--; + } + } + } + + for (int j = 0; j < graph.bitwidth(); j++) + for (auto &node : graph[j].nodes) + node.update_levels(); +} + +void seed(Graph &graph) +{ + for (int l = 1; l <= ceil_log2(graph.bitwidth()); l++) { + for (int i = (graph.bitwidth() / 2) * 2 - 1; + i >= ((l == 1) ? 3 : ((1 << l) + (1 << (l - 1)) + 1)); i -= 2) { + graph[i].link(graph[i - (1 << (l - 1))]); + } + } + + for (int i = 1; i < (graph.bitwidth() / 2) * 2; i += 2) { + if (i == 1) { + graph[i].link(graph[0]); + } else { + int base = 1 << (ceil_log2(i) - 1); + graph[i].link(graph[((i - base) % (base / 2)) + (base / 2)]); + } + } +} + +// `search2` saves the first candidate of minimal size (compared to `search` which saves the last) +void search2(Node &node, Graph &graph, std::vector &candidate, + std::vector &best, int level_cap, int lsb, int ttl, bool first=false) +{ + if (node.lsb == lsb) { + /* + printf("- "); + for (auto &node : candidate) { + printf("[%d:%d] ", node->msb, node->lsb); + } + printf("\n"); + */ + if (best.empty() || (candidate.size() < best.size())) { + best = candidate; + } + } else if ((best.empty() || candidate.size() < best.size() - 1) && ttl >= 1) { + auto &nodes = graph.columns[node.lsb - 1]->nodes; + for (auto it = nodes.rbegin(); it != nodes.rend(); ++it) { + auto &next_node = *it; + if ((first || next_node.level > node.level) && next_node.level <= level_cap - 1 + && next_node.nfanouts() < graph.fanout_constraint && next_node.lsb >= lsb) { + candidate.push_back(&next_node); + search2(next_node, graph, candidate, best, level_cap, lsb, + std::min(ttl, level_cap - next_node.level - 1)); + candidate.pop_back(); + } + } + } +} + +void algo3(Graph &graph) +{ + for (int i = ((graph.bitwidth() - 1) / 2) * 2; i >= 2; i -= 2) { + Column &column = graph[i]; + std::vector candidate, best; + search2(column.nodes.back(), graph, candidate, best, column.level_target, + 0, std::numeric_limits::max(), true); + // if you hit the assert perhaps the fanout constraint was too low + assert(!best.empty()); + for (auto fanin2 : best) { + column.nodes.push_back(Node(&column.nodes.back(), fanin2)); + } + column.nodes.back().update_cap(graph); + } + + for (int i = 0; i < graph.bitwidth(); i++) + for (auto &node : graph[i].nodes) + node.update_cap(graph); +} + +void algo4(Graph &graph) +{ + for (int i = ((graph.bitwidth() - 1) / 2) * 2; i >= 2; i -= 2) { + Column &column = graph[i]; + + if (column.nodes.size() >= 3) { + Node &node1 = graph[i - 1].nodes.back(); + std::vector old_fanins; + + // Try transformation 1 + if (node1.nfanouts() < graph.fanout_constraint + && node1.level < column.nodes.back().level_cap) { + auto head = std::prev(std::end(column.nodes)); + + old_fanins.push_back(head->fanin2); + head->clear_fanins(); + + // Remove nodes before their fanins + auto to_remove = std::prev(head); + while (to_remove != std::begin(column.nodes)) { + auto next = std::prev(to_remove); + old_fanins.push_back(to_remove->fanin2); + column.nodes.erase(to_remove); + to_remove = next; + } + + column.nodes.back() = Node(&column.nodes.front(), &node1); + column.nodes.back().update_cap(graph, true); + } else { + // Try transformation 2 + Node &node2 = graph[i - 2].nodes.back(); + + if (node2.nfanouts() < graph.fanout_constraint + && graph[i - 1].nodes.front().nfanouts() < graph.fanout_constraint + && node2.level < column.nodes.back().level_cap) { + + auto head = std::prev(std::end(column.nodes)); + old_fanins.push_back(head->fanin2); + head->clear_fanins(); + + // Remove nodes before their fanins + auto to_remove = std::prev(head); + while (to_remove != std::begin(column.nodes)) { + auto next = std::prev(to_remove); + old_fanins.push_back(to_remove->fanin2); + column.nodes.erase(to_remove); + to_remove = next; + } + + auto it = std::prev(std::end(column.nodes)); + column.nodes.insert(it, Node(&column.nodes.front(), &graph[i - 1].nodes.front())); + column.nodes.back() = Node(&*std::prev(it), &graph[i - 2].nodes.back()); + column.nodes.back().update_cap(graph, true); + } + } + + for (auto old_fanin : old_fanins) + old_fanin->update_cap(graph); + } + } +} + +// initialize graph with Kogge-Stone +void kogge_stone(Graph &graph) +{ + int width = graph.bitwidth(); + + for (int l = 0; l < ceil_log2(width); l++) { + int stride = 1 << l; + for (int i = width - 1; i >= stride; i--) + graph[i].link(graph[i - stride]); + } +} + +// initialize graph with ripple carry +void ripple(Graph &graph) +{ + int width = graph.bitwidth(); + + for (int i = 1; i < width; i++) { + graph[i].link(graph[i - 1]); + } +} + +// Generate complete prefix adder Verilog +void generate_prefix_adder_verilog(Graph& graph, int width, int mfo, int print_miter = 0) +{ + char filename[100]; + sprintf(filename, "prefix_adder_%d_%d.v", width, mfo); + FILE *fp = fopen(filename, "w"); + if (!fp) { + std::cerr << "Error: Cannot create " << filename << std::endl; + return; + } + + int actual_mfo = graph.max_fanout(); + int size = graph.size(); + // bool valid = graph.validate(); // unused + + fprintf(fp, "// Complete Prefix Adder: width=%d, mfo=%d, tree_size=%d\n", width, actual_mfo, size); + fprintf(fp, "// Generated by C++ version\n"); + fprintf(fp, "module prefix_adder_%d (\n", width); + fprintf(fp, " input [%d:0] a,\n", width-1); + fprintf(fp, " input [%d:0] b,\n", width-1); + fprintf(fp, " output [%d:0] sum\n", width); + fprintf(fp, ");\n\n"); + + // Generate propagate and generate signals + fprintf(fp, " // Generate propagate (p) and generate (g) signals\n"); + for (int i = 0; i < width; i++) { + fprintf(fp, " wire p%d = a[%d] ^ b[%d];\n", i, i, i); + fprintf(fp, " wire g%d = a[%d] & b[%d];\n", i, i, i); + } + fprintf(fp, "\n"); + + // Assign unique wire names to all nodes + // int wire_id = 0; // unused + std::map node_to_p_wire; + std::map node_to_g_wire; + + // Assign wire names to input nodes + for (int i = 0; i < graph.bitwidth(); i++) { + Node* node = &graph[i].nodes.front(); + char p_name[20], g_name[20]; + sprintf(p_name, "p%d", i); + sprintf(g_name, "g%d", i); + node_to_p_wire[node] = p_name; + node_to_g_wire[node] = g_name; + } + + // Assign wire names to internal nodes + for (int i = 0; i < graph.bitwidth(); i++) { + for (auto &node : graph[i].nodes) { + if (!node.pi) { + char p_name[20], g_name[20]; + sprintf(p_name, "p_%d_%d", node.msb, node.lsb); + sprintf(g_name, "g_%d_%d", node.msb, node.lsb); + node_to_p_wire[&node] = p_name; + node_to_g_wire[&node] = g_name; + } + } + } + + // Generate prefix tree internal nodes using compact prefix cells + fprintf(fp, " // Prefix tree computation using traditional prefix cells\n"); + fprintf(fp, " // Each prefix cell computes:\n"); + fprintf(fp, " // p_out = p_in1 & p_in2\n"); + fprintf(fp, " // g_out = g_in1 | (p_in1 & g_in2)\n"); + fprintf(fp, "\n"); + + for (int i = 0; i < graph.bitwidth(); i++) { + for (auto &node : graph[i].nodes) { + if (!node.pi) { + // Generate the propagate output + fprintf(fp, " wire %s = %s & %s;\n", + node_to_p_wire[&node].c_str(), + node_to_p_wire[node.fanin1].c_str(), + node_to_p_wire[node.fanin2].c_str()); + + // Generate the generate output + fprintf(fp, " wire %s = %s | (%s & %s); // [%d:%d] = [%d:%d] o [%d:%d]\n", + node_to_g_wire[&node].c_str(), + node_to_g_wire[node.fanin1].c_str(), + node_to_p_wire[node.fanin1].c_str(), + node_to_g_wire[node.fanin2].c_str(), + node.msb, node.lsb, + node.fanin1->msb, node.fanin1->lsb, + node.fanin2->msb, node.fanin2->lsb); + } + } + } + fprintf(fp, "\n"); + + // Extract carry signals from prefix tree + fprintf(fp, " // Extract carry signals from prefix tree\n"); + fprintf(fp, " wire cin = 1'b0; // No carry input\n"); + for (int i = 0; i < graph.bitwidth(); i++) { + if (graph[i].nodes.back().lsb == 0) { + fprintf(fp, " wire c%d = %s; // Carry out of bit %d\n", + graph[i].nodes.back().msb, + node_to_g_wire[&graph[i].nodes.back()].c_str(), + graph[i].nodes.back().msb); + } + } + fprintf(fp, "\n"); + + // Generate final sum + fprintf(fp, " // Compute final sum\n"); + fprintf(fp, " assign sum[0] = p0 ^ cin;\n"); + for (int i = 1; i < width; i++) { + fprintf(fp, " assign sum[%d] = p%d ^ c%d;\n", i, i, i-1); + } + fprintf(fp, " assign sum[%d] = c%d; // Final carry out\n", width, width-1); + + fprintf(fp, "\nendmodule\n"); + + // Generate miter module if requested + if (print_miter) { + fprintf(fp, "\n// Miter module for verification\n"); + fprintf(fp, "module prefix_adder_%d_miter (\n", width); + fprintf(fp, " input [%d:0] a,\n", width-1); + fprintf(fp, " input [%d:0] b,\n", width-1); + fprintf(fp, " output miter_output\n"); + fprintf(fp, ");\n\n"); + + fprintf(fp, " // Instantiate the prefix adder\n"); + fprintf(fp, " wire [%d:0] sum_impl;\n", width); + fprintf(fp, " prefix_adder_%d adder_inst (\n", width); + fprintf(fp, " .a(a),\n"); + fprintf(fp, " .b(b),\n"); + fprintf(fp, " .sum(sum_impl)\n"); + fprintf(fp, " );\n\n"); + + fprintf(fp, " // Golden reference using simple addition\n"); + fprintf(fp, " wire [%d:0] sum_gold = a + b;\n\n", width); + + fprintf(fp, " // Compare outputs - miter_output is 1 if they differ\n"); + fprintf(fp, " assign miter_output = (sum_gold != sum_impl);\n\n"); + + fprintf(fp, "endmodule\n"); + } + + fclose(fp); + std::cerr << "Generated complete prefix adder: " << filename << std::endl; +} + +// Write variable-length encoded unsigned integer for binary AIGER +void write_aiger_encoded(FILE* fp, unsigned x) { + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + fputc(ch, fp); + x >>= 7; + } + ch = x; + fputc(ch, fp); +} + +// Create integer array representation of prefix adder AIG +// Returns array in same format as Mini_AigerReadInt, compatible with generate_prefix_adder_aiger_int +extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose) { + // Build the prefix adder graph + Graph graph; + graph.initialize(width); + graph.set_max_depth(ceil_log2(width)); + graph.set_max_fanout(mfo / 2); + + // Build the prefix tree using the same algorithm sequence as main + seed(graph); + greedy(graph); + greedy(graph); + graph.set_max_fanout(mfo); + algo3(graph); + algo4(graph); + greedy(graph); + greedy(graph); + + // generate prefix adder with miter + if ( fDumpVer ) + generate_prefix_adder_verilog(graph, width, mfo, fDumpMiter); + + // Count components + int num_inputs = 2 * width; // a[0..width-1], b[0..width-1] + int num_outputs = width + 1; // sum[0..width] + int num_latches = 0; // combinational circuit + + // We'll count AND gates as we generate them + std::vector>> and_gates; + + // Variable allocation + int next_var = 2; + std::map signal_map; + + // Allocate input variables + for (int i = 0; i < width; i++) { + signal_map[std::string("a") + std::to_string(i)] = next_var; + next_var += 2; + } + for (int i = 0; i < width; i++) { + signal_map[std::string("b") + std::to_string(i)] = next_var; + next_var += 2; + } + + // Generate propagate and generate signals (same as in AIGER generation) + for (int i = 0; i < width; i++) { + // pi = ai XOR bi = (ai | bi) & !(ai & bi) + // First create ai & bi + int ai = signal_map[std::string("a") + std::to_string(i)]; + int bi = signal_map[std::string("b") + std::to_string(i)]; + + signal_map[std::string("g") + std::to_string(i)] = next_var; + and_gates.push_back({next_var, {ai, bi}}); + next_var += 2; + + // Create ai | bi = !(!ai & !bi) + int not_ai_and_not_bi = next_var; + and_gates.push_back({not_ai_and_not_bi, {ai ^ 1, bi ^ 1}}); + next_var += 2; + + // pi = (ai | bi) & !(ai & bi) + signal_map[std::string("p") + std::to_string(i)] = next_var; + and_gates.push_back({next_var, {not_ai_and_not_bi ^ 1, signal_map[std::string("g") + std::to_string(i)] ^ 1}}); + next_var += 2; + } + + // Generate prefix tree + std::map, std::pair> computed_nodes; + + for (int i = 0; i < width; i++) { + computed_nodes[{i, i}] = { + signal_map[std::string("p") + std::to_string(i)], + signal_map[std::string("g") + std::to_string(i)] + }; + } + + // Process each node in the graph + for (int i = 0; i < graph.bitwidth(); i++) { + for (auto &node : graph[i].nodes) { + if (node.pi) + continue; + + int msb1 = node.fanin1->msb; + int lsb1 = node.fanin1->lsb; + int msb2 = node.fanin2->msb; + int lsb2 = node.fanin2->lsb; + + int p1 = computed_nodes[{msb1, lsb1}].first; + int g1 = computed_nodes[{msb1, lsb1}].second; + int p2 = computed_nodes[{msb2, lsb2}].first; + int g2 = computed_nodes[{msb2, lsb2}].second; + + // p_out = p1 & p2 + int p_out = next_var; + and_gates.push_back({p_out, {p1, p2}}); + next_var += 2; + + // g_out = g1 | (p1 & g2) + int p1_and_g2 = next_var; + and_gates.push_back({p1_and_g2, {p1, g2}}); + next_var += 2; + + // g1 | (p1 & g2) = !(!g1 & !(p1 & g2)) + int g_out = next_var; + and_gates.push_back({g_out, {g1 ^ 1, p1_and_g2 ^ 1}}); + next_var += 2; + g_out = g_out ^ 1; + + int msb = node.msb; + int lsb = node.lsb; + computed_nodes[{msb, lsb}] = {p_out, g_out}; + } + } + + // Generate sum outputs + std::vector output_signals; + + // sum[0] = p0 ^ cin, where cin = 0 + output_signals.push_back(signal_map[std::string("p0")]); + + // sum[i] = pi ^ c[i-1] for i = 1 to width-1 + for (int i = 1; i < width; i++) { + int pi = signal_map[std::string("p") + std::to_string(i)]; + int gi_minus_1 = computed_nodes[{i-1, 0}].second; + + // pi XOR gi_minus_1 = (pi | gi_minus_1) & !(pi & gi_minus_1) + int pi_and_gi = next_var; + and_gates.push_back({pi_and_gi, {pi, gi_minus_1}}); + next_var += 2; + + int not_pi_and_not_gi = next_var; + and_gates.push_back({not_pi_and_not_gi, {pi ^ 1, gi_minus_1 ^ 1}}); + next_var += 2; + + int sum_i = next_var; + and_gates.push_back({sum_i, {not_pi_and_not_gi ^ 1, pi_and_gi ^ 1}}); + next_var += 2; + + output_signals.push_back(sum_i); + } + + // sum[width] = c[width-1] = g[width-1:0] + output_signals.push_back(computed_nodes[{width-1, 0}].second); + + // Now create the integer array in miniaig format + int nObjs = 1 + num_inputs + 2*num_latches + num_outputs + and_gates.size(); + int* pObjs = (int*)calloc(nObjs * 2, sizeof(int)); + + // Initialize all entries to NULL (0) + for (int i = 0; i < nObjs * 2; i++) { + pObjs[i] = 0; // MINI_AIG_NULL is typically 0 + } + + // Primary inputs don't need initialization beyond NULL + // Primary outputs + for (int i = 0; i < num_outputs; i++) { + pObjs[2*(nObjs-num_outputs-num_latches+i)+0] = output_signals[i]; + pObjs[2*(nObjs-num_outputs-num_latches+i)+1] = 0; // NULL + } + + // AND gates + for (size_t i = 0; i < and_gates.size(); i++) { + int node_lit = and_gates[i].first; + int lit0 = and_gates[i].second.first; + int lit1 = and_gates[i].second.second; + + // Ensure lit0 < lit1 + if (lit0 > lit1) { + std::swap(lit0, lit1); + } + + int node_var = node_lit / 2; + int and_index = node_var - 1 - num_inputs - num_latches; + + pObjs[2*(1+num_inputs+num_latches+and_index)+0] = lit0; + pObjs[2*(1+num_inputs+num_latches+and_index)+1] = lit1; + } + + // Set output parameters + *pnObjs = nObjs; + *pnIns = num_inputs; + *pnLatches = num_latches; + *pnOuts = num_outputs; + *pnAnds = and_gates.size(); + + return pObjs; +} + +void generate_prefix_adder_aiger_int( char * pFileName, int * pObjs, int nObjs, int nIns, int nLatches, int nOuts, int nAnds ) +{ + FILE * pFile = fopen( pFileName, "wb" ); int i; + if ( pFile == NULL ) + { + fprintf( stdout, "generate_prefix_adder_aiger_int(): Cannot open the output file \"%s\".\n", pFileName ); + return; + } + fprintf( pFile, "aig %d %d %d %d %d\n", nIns + nLatches + nAnds, nIns, nLatches, nOuts, nAnds ); + for ( i = 0; i < nLatches; i++ ) + fprintf( pFile, "%d\n", pObjs[2*(nObjs-nLatches+i)+0] ); + for ( i = 0; i < nOuts; i++ ) + fprintf( pFile, "%d\n", pObjs[2*(nObjs-nOuts-nLatches+i)+0] ); + for ( i = 0; i < nAnds; i++ ) + { + int uLit = 2*(1+nIns+nLatches+i); + int uLit0 = pObjs[uLit+0]; + int uLit1 = pObjs[uLit+1]; + write_aiger_encoded( pFile, uLit - uLit1 ); + write_aiger_encoded( pFile, uLit1 - uLit0 ); + } + fprintf( pFile, "c\n" ); + fclose( pFile ); +} +void generate_prefix_adder_aiger( int width, int mfo ) +{ + char pFileName[100]; + sprintf( pFileName, "prefix_adder_%d_%d.aig", width, mfo ); + int nObjs = 0, nIns = 0, nLatches = 0, nOuts = 0, nAnds = 0; + int * pObjs = adder_return_array( width, mfo, &nObjs, &nIns, &nLatches, &nOuts, &nAnds, 0, 0, 0 ); + generate_prefix_adder_aiger_int( pFileName, pObjs, nObjs, nIns, nLatches, nOuts, nAnds ); + printf( "Written prefix adder into AIGER file \"%s\".\n", pFileName ); + free(pObjs); +} + +/* +int main(int argc, char const *argv[]) +{ + for (auto width : {8, 16, 32, 64, 96, 128}) { + for (auto mfo : {4, 6, 8, 10, 12, 16, 32}) { + Graph graph; + graph.initialize(width); + graph.set_max_depth(ceil_log2(width)); + graph.set_max_fanout(mfo / 2); + seed(graph); + greedy(graph); + greedy(graph); + graph.set_max_fanout(mfo); + algo3(graph); + algo4(graph); + greedy(graph); + greedy(graph); + + // Validate the graph + bool valid = graph.validate(); + int actual_mfo = graph.max_fanout(); + int size = graph.size(); + int depth = graph.max_level() - graph.min_level(); + + std::cerr << "width=" << graph.bitwidth() << "\tmfo=" << actual_mfo << "\tsize=" << size; + + // Add validation status and check constraints + if (!valid) { + std::cerr << "\t[INVALID: not all columns reach lsb=0]"; + } + if (actual_mfo > mfo) { + std::cerr << "\t[ERROR: max_fanout " << actual_mfo << " exceeds constraint " << mfo << "]"; + } + if (depth > ceil_log2(width)) { + std::cerr << "\t[WARNING: depth " << depth << " exceeds target " << ceil_log2(width) << "]"; + } + std::cerr << "\n"; + + // Generate prefix adder with miter for verification + //generate_prefix_adder_verilog(graph, width, mfo, "cpp_miter", 1); + + // Generate AIGER format + generate_prefix_adder_aiger(width, mfo); + } + } + return 0; +} +*/ From 260fa85161b0ac9ab174b2214063bc7f4fc14b89 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 6 Aug 2025 07:38:27 -0700 Subject: [PATCH 06/22] Fixing a linker problem. --- src/base/abci/abc.c | 11 ++++++++++- src/misc/util/utilPrefix.cpp | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 67fec1cb2..85425af30 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -56710,6 +56710,16 @@ usage: return 1; } +#ifdef __cplusplus +extern "C" { +#endif + +int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose); + +#ifdef __cplusplus +} +#endif + /**Function************************************************************* @@ -56724,7 +56734,6 @@ usage: ***********************************************************************/ int Abc_CommandAbc9GenPrefix( Abc_Frame_t * pAbc, int argc, char ** argv ) { - extern int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose); extern Gia_Man_t * Gia_ManDupFromArray( int * pObjs, int nObjs, int nIns, int nLatches, int nOuts, int nAnds ); int c, nBits = 8, nFans = 4, fDumpVer = 0, fDumpMiter = 0, fVerbose = 0; Extra_UtilGetoptReset(); diff --git a/src/misc/util/utilPrefix.cpp b/src/misc/util/utilPrefix.cpp index ca3e54d82..31a8fab22 100644 --- a/src/misc/util/utilPrefix.cpp +++ b/src/misc/util/utilPrefix.cpp @@ -777,6 +777,8 @@ extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, // generate prefix adder with miter if ( fDumpVer ) generate_prefix_adder_verilog(graph, width, mfo, fDumpMiter); + if ( fVerbose ) + graph.print(); // Count components int num_inputs = 2 * width; // a[0..width-1], b[0..width-1] From fd74cb8e8a64a50ba3ec740aeee586eb1e82c23a Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Thu, 7 Aug 2025 10:51:05 -0700 Subject: [PATCH 07/22] Refactored the code to return prefix tree as an array of GP-nodes. --- src/base/abci/abc.c | 16 +- src/misc/util/utilPrefix.cpp | 579 ++++++++++++++++++++++++----------- 2 files changed, 416 insertions(+), 179 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 85425af30..9e5b5e6ed 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -56714,7 +56714,7 @@ usage: extern "C" { #endif -int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose); +int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose, int use_or); #ifdef __cplusplus } @@ -56735,9 +56735,9 @@ int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatc int Abc_CommandAbc9GenPrefix( Abc_Frame_t * pAbc, int argc, char ** argv ) { extern Gia_Man_t * Gia_ManDupFromArray( int * pObjs, int nObjs, int nIns, int nLatches, int nOuts, int nAnds ); - int c, nBits = 8, nFans = 4, fDumpVer = 0, fDumpMiter = 0, fVerbose = 0; + int c, nBits = 8, nFans = 4, fDumpVer = 0, fDumpMiter = 0, fVerbose = 0, use_or = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "NFdmv" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "NFdmov" ) ) != EOF ) { switch ( c ) { @@ -56769,6 +56769,9 @@ int Abc_CommandAbc9GenPrefix( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'm': fDumpMiter ^= 1; break; + case 'o': + use_or ^= 1; + break; case 'v': fVerbose ^= 1; break; @@ -56784,7 +56787,7 @@ int Abc_CommandAbc9GenPrefix( Abc_Frame_t * pAbc, int argc, char ** argv ) else { int nObjs = 0, nIns = 0, nLatches = 0, nOuts = 0, nAnds = 0; - int * pObjs = adder_return_array( nBits, nFans, &nObjs, &nIns, &nLatches, &nOuts, &nAnds, fDumpVer, fDumpMiter, fVerbose ); + int * pObjs = adder_return_array( nBits, nFans, &nObjs, &nIns, &nLatches, &nOuts, &nAnds, fDumpVer, fDumpMiter, fVerbose, use_or ); Gia_Man_t * pTemp = Gia_ManDupFromArray( pObjs, nObjs, nIns, nLatches, nOuts, nAnds ); Abc_FrameUpdateGia( pAbc, pTemp ); ABC_FREE( pObjs ); @@ -56792,12 +56795,13 @@ int Abc_CommandAbc9GenPrefix( Abc_Frame_t * pAbc, int argc, char ** argv ) return 0; usage: - Abc_Print( -2, "usage: &genprefix [-NF ] [-dcv]\n" ); + Abc_Print( -2, "usage: &genprefix [-NF ] [-dmov]\n" ); Abc_Print( -2, "\t generates a prefix adder with minimum depth\n" ); Abc_Print( -2, "\t-N num : the bit-width of the adder [default = %d]\n", nBits ); Abc_Print( -2, "\t-F num : the limit on the fanout count [default = %d]\n", nFans ); Abc_Print( -2, "\t-d : toggles dumping the adder in Verilog [default = %s]\n", fDumpVer ? "yes": "no" ); - Abc_Print( -2, "\t-c : toggles dumping the miter in Verilog [default = %s]\n", fDumpMiter ? "yes": "no" ); + Abc_Print( -2, "\t-m : toggles dumping the miter in Verilog [default = %s]\n", fDumpMiter ? "yes": "no" ); + Abc_Print( -2, "\t-o : toggles using additional optimization [default = %s]\n", use_or ? "yes": "no" ); Abc_Print( -2, "\t-v : toggles printing verbose information [default = %s]\n\n", fVerbose ? "yes": "no" ); Abc_Print( -2, "\t The code of this command is contributed by Martin Povišer \n\n" ); Abc_Print( -2, "\t The implementation is inspired by S. Roy, M. Choudhury, R. Puri, D. Pan,\n" ); diff --git a/src/misc/util/utilPrefix.cpp b/src/misc/util/utilPrefix.cpp index 31a8fab22..0345d375a 100644 --- a/src/misc/util/utilPrefix.cpp +++ b/src/misc/util/utilPrefix.cpp @@ -26,7 +26,6 @@ https://www.cerc.utexas.edu/utda/publications/C166.pdf */ - #include #include #include @@ -35,7 +34,6 @@ #include #include #include -#include class Graph; @@ -601,23 +599,173 @@ void ripple(Graph &graph) } } -// Generate complete prefix adder Verilog -void generate_prefix_adder_verilog(Graph& graph, int width, int mfo, int print_miter = 0) + +int* prefix_return_array_int(Graph& graph) { + int N = graph.bitwidth(); // Number of inputs + int P = 0; // Number of internal prefix nodes + + // Count internal prefix nodes and assign IDs + std::map node_to_id; + std::vector internal_nodes; + + // Assign IDs to input nodes (0 to N-1) + for (int i = 0; i < N; i++) { + node_to_id[&graph[i].nodes.front()] = i; + } + + // Collect and count internal nodes in topological order + std::vector> level_nodes; + for (int i = 0; i < N; i++) { + for (auto &node : graph[i].nodes) { + if (!node.pi) { + level_nodes.push_back({node.level, &node}); + P++; + } + } + } + + // Sort by level for topological order + std::sort(level_nodes.begin(), level_nodes.end()); + + // Assign IDs to internal nodes (N to N+P-1) + int id = N; + for (auto &pair : level_nodes) { + node_to_id[pair.second] = id++; + internal_nodes.push_back(pair.second); + } + + // Calculate max level + int L = 0; + if (!level_nodes.empty()) { + L = level_nodes.back().first; + } + + // Allocate array: 5 + 2*N + 3*P + int array_size = 5 + 2*N + 3*P; + int* array = (int*)calloc(array_size, sizeof(int)); + int idx = 0; + + // (1) Total size + array[idx++] = array_size; + + // (2) Number of inputs + array[idx++] = N; + + // (3) Number of prefix nodes + array[idx++] = P; + + // (4) Number of levels + array[idx++] = L; + + // (5) Input delays (all 0 for now) + for (int i = 0; i < N; i++) { + array[idx++] = 0; + } + + // (6) Prefix nodes (LSB_ID, MSB_ID, current_ID) + for (Node* node : internal_nodes) { + int lsb_id = node_to_id[node->fanin2]; // fanin2 is the LSB node + int msb_id = node_to_id[node->fanin1]; // fanin1 is the MSB node + int cur_id = node_to_id[node]; + array[idx++] = lsb_id; + array[idx++] = msb_id; + array[idx++] = cur_id; + } + + // (7) Output nodes + for (int i = 0; i < N; i++) { + // Find the node that computes [i:0] + Node* output_node = nullptr; + for (auto &node : graph[i].nodes) { + if (node.lsb == 0) { + output_node = &node; + break; + } + } + if (output_node) { + array[idx++] = node_to_id[output_node]; + } else { + // If no node computes [i:0], use input node i + array[idx++] = i; + } + } + + // Last output is the carry-out (propagate of the last node) + Node* carry_node = nullptr; + for (auto &node : graph[N-1].nodes) { + if (node.lsb == 0) { + carry_node = &node; + break; + } + } + if (carry_node) { + array[idx++] = node_to_id[carry_node]; + } else { + array[idx++] = N-1; + } + + assert(idx == array_size); + return array; +} + +// Create compact integer array representation of prefix tree structure in this format: +// (1) [1 integer]: Total number of integers in the array, including this entry +// (2) [1 integer]: N = number of inputs to prefix tree (one input = one PG-pair) +// (3) [1 integer]: P = number of prefix nodes +// (4) [1 integer]: L = number of levels of prefix nodes +// (5) [N integers]: Delays of each input prefix node (default 0) +// (6) [3*P integers]: Each prefix node as (LSB_ID, MSB_ID, current_ID) +// - Input nodes have IDs 0 to N-1 +// - Internal nodes have IDs N to N+P-1 (in topological order) +// (7) [N+1 integers]: Output prefix nodes; last one produces carry-out +// Total array size: 5 + 2*N + 3*P integers +extern "C" int* prefix_return_array(int width, int mfo, int fVerbose) { + // Build the prefix adder graph + Graph graph; + graph.initialize(width); + graph.set_max_depth(ceil_log2(width)); + graph.set_max_fanout(mfo / 2); + + // Build the prefix tree using the same algorithm sequence + seed(graph); + greedy(graph); + greedy(graph); + graph.set_max_fanout(mfo); + algo3(graph); + algo4(graph); + greedy(graph); + greedy(graph); + if ( fVerbose ) + graph.print(); + // Get the prefix array representation + int* prefix_array = prefix_return_array_int(graph); + return prefix_array; +} + +void generate_prefix_adder_verilog(int* array, int width, int mfo, int print_miter = 0, int use_or = 0) { + // Parse array header + int idx = 0; + int array_size = array[idx++]; + int N = array[idx++]; + int P = array[idx++]; + int L = array[idx++]; + + // Verify width matches + if (N != width) { + std::cerr << "Error: Array N=" << N << " doesn't match width=" << width << std::endl; + return; + } + char filename[100]; - sprintf(filename, "prefix_adder_%d_%d.v", width, mfo); + sprintf(filename, "prefix_adder_%d_%d%s.v", width, mfo, print_miter ? "_miter":"" ); FILE *fp = fopen(filename, "w"); if (!fp) { std::cerr << "Error: Cannot create " << filename << std::endl; return; } - int actual_mfo = graph.max_fanout(); - int size = graph.size(); - // bool valid = graph.validate(); // unused - - fprintf(fp, "// Complete Prefix Adder: width=%d, mfo=%d, tree_size=%d\n", width, actual_mfo, size); - fprintf(fp, "// Generated by C++ version\n"); + fprintf(fp, "// Prefix adder: width=%d, internal_nodes=%d, levels=%d\n", N, P, L); fprintf(fp, "module prefix_adder_%d (\n", width); fprintf(fp, " input [%d:0] a,\n", width-1); fprintf(fp, " input [%d:0] b,\n", width-1); @@ -625,92 +773,94 @@ void generate_prefix_adder_verilog(Graph& graph, int width, int mfo, int print_m fprintf(fp, ");\n\n"); // Generate propagate and generate signals - fprintf(fp, " // Generate propagate (p) and generate (g) signals\n"); - for (int i = 0; i < width; i++) { - fprintf(fp, " wire p%d = a[%d] ^ b[%d];\n", i, i, i); - fprintf(fp, " wire g%d = a[%d] & b[%d];\n", i, i, i); + if (use_or) { + fprintf(fp, " // Generate propagate (p) and generate (g) signals\n"); + for (int i = 0; i < width; i++) { + fprintf(fp, " wire p%d = a[%d] | b[%d]; // OR gate\n", i, i, i); + fprintf(fp, " wire g%d = a[%d] & b[%d];\n", i, i, i); + } + } else { + fprintf(fp, " // Generate propagate (p) and generate (g) signals\n"); + for (int i = 0; i < width; i++) { + fprintf(fp, " wire p%d = a[%d] ^ b[%d];\n", i, i, i); + fprintf(fp, " wire g%d = a[%d] & b[%d];\n", i, i, i); + } } fprintf(fp, "\n"); - // Assign unique wire names to all nodes - // int wire_id = 0; // unused - std::map node_to_p_wire; - std::map node_to_g_wire; + // Skip input delays (idx already points to start of prefix nodes) + idx += N; - // Assign wire names to input nodes - for (int i = 0; i < graph.bitwidth(); i++) { - Node* node = &graph[i].nodes.front(); - char p_name[20], g_name[20]; - sprintf(p_name, "p%d", i); - sprintf(g_name, "g%d", i); - node_to_p_wire[node] = p_name; - node_to_g_wire[node] = g_name; - } - - // Assign wire names to internal nodes - for (int i = 0; i < graph.bitwidth(); i++) { - for (auto &node : graph[i].nodes) { - if (!node.pi) { - char p_name[20], g_name[20]; - sprintf(p_name, "p_%d_%d", node.msb, node.lsb); - sprintf(g_name, "g_%d_%d", node.msb, node.lsb); - node_to_p_wire[&node] = p_name; - node_to_g_wire[&node] = g_name; - } - } - } - - // Generate prefix tree internal nodes using compact prefix cells + // Generate internal prefix nodes fprintf(fp, " // Prefix tree computation using traditional prefix cells\n"); fprintf(fp, " // Each prefix cell computes:\n"); fprintf(fp, " // p_out = p_in1 & p_in2\n"); fprintf(fp, " // g_out = g_in1 | (p_in1 & g_in2)\n"); fprintf(fp, "\n"); - for (int i = 0; i < graph.bitwidth(); i++) { - for (auto &node : graph[i].nodes) { - if (!node.pi) { - // Generate the propagate output - fprintf(fp, " wire %s = %s & %s;\n", - node_to_p_wire[&node].c_str(), - node_to_p_wire[node.fanin1].c_str(), - node_to_p_wire[node.fanin2].c_str()); - - // Generate the generate output - fprintf(fp, " wire %s = %s | (%s & %s); // [%d:%d] = [%d:%d] o [%d:%d]\n", - node_to_g_wire[&node].c_str(), - node_to_g_wire[node.fanin1].c_str(), - node_to_p_wire[node.fanin1].c_str(), - node_to_g_wire[node.fanin2].c_str(), - node.msb, node.lsb, - node.fanin1->msb, node.fanin1->lsb, - node.fanin2->msb, node.fanin2->lsb); - } - } + // Process prefix nodes + for (int i = 0; i < P; i++) { + int lsb_id = array[idx++]; + int msb_id = array[idx++]; + int cur_id = array[idx++]; + + // Determine wire names based on node IDs + std::string p_lsb = (lsb_id < N) ? "p" + std::to_string(lsb_id) : "p_" + std::to_string(lsb_id); + std::string g_lsb = (lsb_id < N) ? "g" + std::to_string(lsb_id) : "g_" + std::to_string(lsb_id); + std::string p_msb = (msb_id < N) ? "p" + std::to_string(msb_id) : "p_" + std::to_string(msb_id); + std::string g_msb = (msb_id < N) ? "g" + std::to_string(msb_id) : "g_" + std::to_string(msb_id); + std::string p_out = "p_" + std::to_string(cur_id); + std::string g_out = "g_" + std::to_string(cur_id); + + // Generate propagate output + fprintf(fp, " wire %s = %s & %s;\n", p_out.c_str(), p_msb.c_str(), p_lsb.c_str()); + + // Generate generate output + fprintf(fp, " wire %s = %s | (%s & %s);\n", + g_out.c_str(), g_msb.c_str(), p_msb.c_str(), g_lsb.c_str()); } fprintf(fp, "\n"); - // Extract carry signals from prefix tree + // Extract carry signals from output nodes fprintf(fp, " // Extract carry signals from prefix tree\n"); fprintf(fp, " wire cin = 1'b0; // No carry input\n"); - for (int i = 0; i < graph.bitwidth(); i++) { - if (graph[i].nodes.back().lsb == 0) { - fprintf(fp, " wire c%d = %s; // Carry out of bit %d\n", - graph[i].nodes.back().msb, - node_to_g_wire[&graph[i].nodes.back()].c_str(), - graph[i].nodes.back().msb); + + // Process output nodes to get carry signals + std::vector output_nodes; + for (int i = 0; i < N+1; i++) { + output_nodes.push_back(array[idx++]); + } + assert(idx == array_size); + + // Generate carry signals c0 through c[N-1] + for (int i = 0; i < N; i++) { + int node_id = output_nodes[i]; + if (node_id < N) { + // This is an input node, so carry is just g[i] + fprintf(fp, " wire c%d = g%d; // Carry out of bit %d\n", i, node_id, i); + } else { + // This is an internal node + fprintf(fp, " wire c%d = g_%d; // Carry out of bit %d\n", i, node_id, i); } } fprintf(fp, "\n"); - // Generate final sum - fprintf(fp, " // Compute final sum\n"); - fprintf(fp, " assign sum[0] = p0 ^ cin;\n"); - for (int i = 1; i < width; i++) { - fprintf(fp, " assign sum[%d] = p%d ^ c%d;\n", i, i, i-1); + // Generate sum outputs + fprintf(fp, " // Generate sum outputs\n"); + if (use_or) { + fprintf(fp, " assign sum[0] = p0 & ~g0; // No carry-in\n"); + for (int i = 1; i < width; i++) { + fprintf(fp, " assign sum[%d] = (p%d & ~g%d) ^ c%d;\n", i, i, i, i-1); + } + } else { + fprintf(fp, " assign sum[0] = p0 ^ cin; // cin = 0\n"); + for (int i = 1; i < width; i++) { + fprintf(fp, " assign sum[%d] = p%d ^ c%d;\n", i, i, i-1); + } } - fprintf(fp, " assign sum[%d] = c%d; // Final carry out\n", width, width-1); + // Carry out + fprintf(fp, " assign sum[%d] = c%d; // Carry out\n", width, width-1); fprintf(fp, "\nendmodule\n"); // Generate miter module if requested @@ -740,31 +890,18 @@ void generate_prefix_adder_verilog(Graph& graph, int width, int mfo, int print_m } fclose(fp); - std::cerr << "Generated complete prefix adder: " << filename << std::endl; + std::cerr << "Generated complete prefix adder from array: " << filename << std::endl; } -// Write variable-length encoded unsigned integer for binary AIGER -void write_aiger_encoded(FILE* fp, unsigned x) { - unsigned char ch; - while (x & ~0x7f) { - ch = (x & 0x7f) | 0x80; - fputc(ch, fp); - x >>= 7; - } - ch = x; - fputc(ch, fp); -} - -// Create integer array representation of prefix adder AIG -// Returns array in same format as Mini_AigerReadInt, compatible with generate_prefix_adder_aiger_int -extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose) { +// Create AIG array from the prefix array +extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose, int use_or) { // Build the prefix adder graph Graph graph; graph.initialize(width); graph.set_max_depth(ceil_log2(width)); graph.set_max_fanout(mfo / 2); - // Build the prefix tree using the same algorithm sequence as main + // Build the prefix tree using the same algorithm sequence seed(graph); greedy(graph); greedy(graph); @@ -773,19 +910,63 @@ extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, algo4(graph); greedy(graph); greedy(graph); - - // generate prefix adder with miter - if ( fDumpVer ) - generate_prefix_adder_verilog(graph, width, mfo, fDumpMiter); if ( fVerbose ) graph.print(); - // Count components + // Get the prefix array representation + int* prefix_array = prefix_return_array_int(graph); + if ( fDumpVer ) + generate_prefix_adder_verilog(prefix_array, width, mfo, fDumpMiter); + + // Parse prefix array + int idx = 0; + int array_size = prefix_array[idx++]; + int N = prefix_array[idx++]; // Number of inputs + int P = prefix_array[idx++]; // Number of prefix nodes + int L = prefix_array[idx++]; // Number of levels + assert( L > 0 ); + + // Verify width matches + if (N != width) { + free(prefix_array); + return NULL; + } + + // Skip input delays (N integers) + idx += N; + + // Create mapping for prefix node IDs to their computation + struct PrefixNode { + int lsb_id; + int msb_id; + int id; + }; + std::vector prefix_nodes; + + // Read prefix nodes (P nodes, each with 3 integers: lsb_id, msb_id, current_id) + for (int i = 0; i < P; i++) { + PrefixNode node; + node.lsb_id = prefix_array[idx++]; + node.msb_id = prefix_array[idx++]; + node.id = prefix_array[idx++]; + prefix_nodes.push_back(node); + } + + // Read output node IDs (N+1 integers) + std::vector output_ids; + for (int i = 0; i <= N; i++) { + output_ids.push_back(prefix_array[idx++]); + } + assert(idx == array_size); + + // Free prefix array - we've extracted all information + free(prefix_array); + + // Now build AIG using the prefix tree structure int num_inputs = 2 * width; // a[0..width-1], b[0..width-1] int num_outputs = width + 1; // sum[0..width] int num_latches = 0; // combinational circuit - // We'll count AND gates as we generate them std::vector>> and_gates; // Variable allocation @@ -802,105 +983,146 @@ extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, next_var += 2; } - // Generate propagate and generate signals (same as in AIGER generation) + // Generate propagate and generate signals for (int i = 0; i < width; i++) { - // pi = ai XOR bi = (ai | bi) & !(ai & bi) - // First create ai & bi int ai = signal_map[std::string("a") + std::to_string(i)]; int bi = signal_map[std::string("b") + std::to_string(i)]; + // gi = ai & bi signal_map[std::string("g") + std::to_string(i)] = next_var; and_gates.push_back({next_var, {ai, bi}}); next_var += 2; - // Create ai | bi = !(!ai & !bi) - int not_ai_and_not_bi = next_var; - and_gates.push_back({not_ai_and_not_bi, {ai ^ 1, bi ^ 1}}); - next_var += 2; - - // pi = (ai | bi) & !(ai & bi) - signal_map[std::string("p") + std::to_string(i)] = next_var; - and_gates.push_back({next_var, {not_ai_and_not_bi ^ 1, signal_map[std::string("g") + std::to_string(i)] ^ 1}}); - next_var += 2; + if (use_or) { + // pi = ai | bi = !(!ai & !bi) + int not_ai_and_not_bi = next_var; + and_gates.push_back({not_ai_and_not_bi, {ai ^ 1, bi ^ 1}}); + next_var += 2; + + signal_map[std::string("p") + std::to_string(i)] = not_ai_and_not_bi ^ 1; + } else { + // pi = ai XOR bi = (ai | bi) & !(ai & bi) + // Create ai | bi = !(!ai & !bi) + int not_ai_and_not_bi = next_var; + and_gates.push_back({not_ai_and_not_bi, {ai ^ 1, bi ^ 1}}); + next_var += 2; + + // pi = (ai | bi) & !(ai & bi) + signal_map[std::string("p") + std::to_string(i)] = next_var; + and_gates.push_back({next_var, {not_ai_and_not_bi ^ 1, signal_map[std::string("g") + std::to_string(i)] ^ 1}}); + next_var += 2; + } } - // Generate prefix tree - std::map, std::pair> computed_nodes; + // Map from prefix node ID to (p, g) signals + std::map> node_signals; - for (int i = 0; i < width; i++) { - computed_nodes[{i, i}] = { + // Initialize input nodes (ID 0 to N-1) + for (int i = 0; i < N; i++) { + node_signals[i] = { signal_map[std::string("p") + std::to_string(i)], signal_map[std::string("g") + std::to_string(i)] }; } - // Process each node in the graph - for (int i = 0; i < graph.bitwidth(); i++) { - for (auto &node : graph[i].nodes) { - if (node.pi) - continue; - - int msb1 = node.fanin1->msb; - int lsb1 = node.fanin1->lsb; - int msb2 = node.fanin2->msb; - int lsb2 = node.fanin2->lsb; - - int p1 = computed_nodes[{msb1, lsb1}].first; - int g1 = computed_nodes[{msb1, lsb1}].second; - int p2 = computed_nodes[{msb2, lsb2}].first; - int g2 = computed_nodes[{msb2, lsb2}].second; - - // p_out = p1 & p2 - int p_out = next_var; - and_gates.push_back({p_out, {p1, p2}}); - next_var += 2; - - // g_out = g1 | (p1 & g2) - int p1_and_g2 = next_var; - and_gates.push_back({p1_and_g2, {p1, g2}}); - next_var += 2; - - // g1 | (p1 & g2) = !(!g1 & !(p1 & g2)) - int g_out = next_var; - and_gates.push_back({g_out, {g1 ^ 1, p1_and_g2 ^ 1}}); - next_var += 2; - g_out = g_out ^ 1; - - int msb = node.msb; - int lsb = node.lsb; - computed_nodes[{msb, lsb}] = {p_out, g_out}; - } + // Process prefix nodes in order (they're already topologically sorted) + for (const PrefixNode& pnode : prefix_nodes) { + // Get signals from fanins + int p1 = node_signals[pnode.msb_id].first; + int g1 = node_signals[pnode.msb_id].second; + int p2 = node_signals[pnode.lsb_id].first; + int g2 = node_signals[pnode.lsb_id].second; + + // p_out = p1 & p2 + int p_out = next_var; + and_gates.push_back({p_out, {p1, p2}}); + next_var += 2; + + // g_out = g1 | (p1 & g2) + int p1_and_g2 = next_var; + and_gates.push_back({p1_and_g2, {p1, g2}}); + next_var += 2; + + // g1 | (p1 & g2) = !(!g1 & !(p1 & g2)) + int g_out = next_var; + and_gates.push_back({g_out, {g1 ^ 1, p1_and_g2 ^ 1}}); + next_var += 2; + g_out = g_out ^ 1; + + node_signals[pnode.id] = {p_out, g_out}; } // Generate sum outputs std::vector output_signals; - // sum[0] = p0 ^ cin, where cin = 0 - output_signals.push_back(signal_map[std::string("p0")]); - - // sum[i] = pi ^ c[i-1] for i = 1 to width-1 - for (int i = 1; i < width; i++) { - int pi = signal_map[std::string("p") + std::to_string(i)]; - int gi_minus_1 = computed_nodes[{i-1, 0}].second; + if (use_or) { + // sum[i] = (pi & ~gi) ^ c[i-1] + // For i=0, cin=0, so sum[0] = p0 & ~g0 + int p0 = signal_map[std::string("p0")]; + int g0 = signal_map[std::string("g0")]; - // pi XOR gi_minus_1 = (pi | gi_minus_1) & !(pi & gi_minus_1) - int pi_and_gi = next_var; - and_gates.push_back({pi_and_gi, {pi, gi_minus_1}}); + // p0 & ~g0 + int sum_0 = next_var; + and_gates.push_back({sum_0, {p0, g0 ^ 1}}); next_var += 2; + output_signals.push_back(sum_0); - int not_pi_and_not_gi = next_var; - and_gates.push_back({not_pi_and_not_gi, {pi ^ 1, gi_minus_1 ^ 1}}); - next_var += 2; + // For i = 1 to width-1 + for (int i = 1; i < width; i++) { + int pi = signal_map[std::string("p") + std::to_string(i)]; + int gi = signal_map[std::string("g") + std::to_string(i)]; + // Get carry from output_ids[i-1] which gives us node ID for [i-1:0] + int ci_minus_1 = node_signals[output_ids[i-1]].second; + + // pi & ~gi + int pi_and_not_gi = next_var; + and_gates.push_back({pi_and_not_gi, {pi, gi ^ 1}}); + next_var += 2; + + // (pi & ~gi) XOR ci_minus_1 + int pi_and_not_gi_and_ci = next_var; + and_gates.push_back({pi_and_not_gi_and_ci, {pi_and_not_gi, ci_minus_1}}); + next_var += 2; + + int not_pi_and_not_gi_and_not_ci = next_var; + and_gates.push_back({not_pi_and_not_gi_and_not_ci, {pi_and_not_gi ^ 1, ci_minus_1 ^ 1}}); + next_var += 2; + + int sum_i = next_var; + and_gates.push_back({sum_i, {not_pi_and_not_gi_and_not_ci ^ 1, pi_and_not_gi_and_ci ^ 1}}); + next_var += 2; + + output_signals.push_back(sum_i); + } + } else { + // Regular: sum[0] = p0 ^ cin, where cin = 0 + output_signals.push_back(signal_map[std::string("p0")]); - int sum_i = next_var; - and_gates.push_back({sum_i, {not_pi_and_not_gi ^ 1, pi_and_gi ^ 1}}); - next_var += 2; - - output_signals.push_back(sum_i); + // sum[i] = pi ^ c[i-1] for i = 1 to width-1 + for (int i = 1; i < width; i++) { + int pi = signal_map[std::string("p") + std::to_string(i)]; + // Get carry from output_ids[i-1] which gives us node ID for [i-1:0] + int gi_minus_1 = node_signals[output_ids[i-1]].second; + + // pi XOR gi_minus_1 = (pi | gi_minus_1) & !(pi & gi_minus_1) + int pi_and_gi = next_var; + and_gates.push_back({pi_and_gi, {pi, gi_minus_1}}); + next_var += 2; + + int not_pi_and_not_gi = next_var; + and_gates.push_back({not_pi_and_not_gi, {pi ^ 1, gi_minus_1 ^ 1}}); + next_var += 2; + + int sum_i = next_var; + and_gates.push_back({sum_i, {not_pi_and_not_gi ^ 1, pi_and_gi ^ 1}}); + next_var += 2; + + output_signals.push_back(sum_i); + } } - // sum[width] = c[width-1] = g[width-1:0] - output_signals.push_back(computed_nodes[{width-1, 0}].second); + // sum[width] = c[width-1] = g[width-1:0] from the last output node + output_signals.push_back(node_signals[output_ids[N]].second); // Now create the integer array in miniaig format int nObjs = 1 + num_inputs + 2*num_latches + num_outputs + and_gates.size(); @@ -911,7 +1133,6 @@ extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, pObjs[i] = 0; // MINI_AIG_NULL is typically 0 } - // Primary inputs don't need initialization beyond NULL // Primary outputs for (int i = 0; i < num_outputs; i++) { pObjs[2*(nObjs-num_outputs-num_latches+i)+0] = output_signals[i]; @@ -946,6 +1167,18 @@ extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, return pObjs; } +// Write variable-length encoded unsigned integer for binary AIGER +void write_aiger_encoded(FILE* fp, unsigned x) { + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + fputc(ch, fp); + x >>= 7; + } + ch = x; + fputc(ch, fp); +} + void generate_prefix_adder_aiger_int( char * pFileName, int * pObjs, int nObjs, int nIns, int nLatches, int nOuts, int nAnds ) { FILE * pFile = fopen( pFileName, "wb" ); int i; @@ -975,7 +1208,7 @@ void generate_prefix_adder_aiger( int width, int mfo ) char pFileName[100]; sprintf( pFileName, "prefix_adder_%d_%d.aig", width, mfo ); int nObjs = 0, nIns = 0, nLatches = 0, nOuts = 0, nAnds = 0; - int * pObjs = adder_return_array( width, mfo, &nObjs, &nIns, &nLatches, &nOuts, &nAnds, 0, 0, 0 ); + int * pObjs = adder_return_array( width, mfo, &nObjs, &nIns, &nLatches, &nOuts, &nAnds, 0, 0, 0, 0 ); generate_prefix_adder_aiger_int( pFileName, pObjs, nObjs, nIns, nLatches, nOuts, nAnds ); printf( "Written prefix adder into AIGER file \"%s\".\n", pFileName ); free(pObjs); From 1a18c9a3d88aadf0f67cfaa1f3ca0171b14b408b Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Thu, 7 Aug 2025 12:35:03 -0700 Subject: [PATCH 08/22] : lutexact --- src/base/abci/abc.c | 20 ++++++++++-------- src/sat/bmc/bmc.h | 1 + src/sat/bmc/bmcMaj.c | 49 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 9e5b5e6ed..d9b688b9b 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -10639,14 +10639,14 @@ int Abc_CommandLutExact( Abc_Frame_t * pAbc, int argc, char ** argv ) Bmc_EsPar_t Pars, * pPars = &Pars; Bmc_EsParSetDefault( pPars ); Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "INKTFUSYiaorfgvh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "NMKTFUSYiaorfgdvh" ) ) != EOF ) { switch ( c ) { - case 'I': + case 'N': if ( globalUtilOptind >= argc ) { - Abc_Print( -1, "Command line switch \"-I\" should be followed by an integer.\n" ); + Abc_Print( -1, "Command line switch \"-N\" should be followed by an integer.\n" ); goto usage; } pPars->nVars = atoi(argv[globalUtilOptind]); @@ -10654,10 +10654,10 @@ int Abc_CommandLutExact( Abc_Frame_t * pAbc, int argc, char ** argv ) if ( pPars->nVars < 0 ) goto usage; break; - case 'N': + case 'M': if ( globalUtilOptind >= argc ) { - Abc_Print( -1, "Command line switch \"-N\" should be followed by an integer.\n" ); + Abc_Print( -1, "Command line switch \"-M\" should be followed by an integer.\n" ); goto usage; } pPars->nNodes = atoi(argv[globalUtilOptind]); @@ -10743,6 +10743,9 @@ int Abc_CommandLutExact( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'g': pPars->fGlucose ^= 1; break; + case 'd': + pPars->fDumpBlif ^= 1; + break; case 'v': pPars->fVerbose ^= 1; break; @@ -10803,10 +10806,10 @@ int Abc_CommandLutExact( Abc_Frame_t * pAbc, int argc, char ** argv ) return 0; usage: - Abc_Print( -2, "usage: lutexact [-INKTFUS ] [-Y string] [-iaorfgvh] \n" ); + Abc_Print( -2, "usage: lutexact [-NMKTFUS ] [-Y string] [-iaorfgdvh] \n" ); Abc_Print( -2, "\t exact synthesis of I-input function using N K-input gates\n" ); - Abc_Print( -2, "\t-I : the number of input variables [default = %d]\n", pPars->nVars ); - Abc_Print( -2, "\t-N : the number of K-input nodes [default = %d]\n", pPars->nNodes ); + Abc_Print( -2, "\t-N : the number of input variables [default = %d]\n", pPars->nVars ); + Abc_Print( -2, "\t-M : the number of K-input nodes [default = %d]\n", pPars->nNodes ); Abc_Print( -2, "\t-K : the number of node fanins [default = %d]\n", pPars->nLutSize ); Abc_Print( -2, "\t-T : the runtime limit in seconds [default = %d]\n", pPars->RuntimeLim ); Abc_Print( -2, "\t-F : the number of random functions to try [default = unused]\n" ); @@ -10819,6 +10822,7 @@ usage: Abc_Print( -2, "\t-r : toggle synthesizing a single-rail cascade [default = %s]\n", pPars->fLutCascade ? "yes" : "no" ); Abc_Print( -2, "\t-f : toggle fixing LUT inputs in cascade mapping [default = %s]\n", pPars->fLutInFixed ? "yes" : "no" ); Abc_Print( -2, "\t-g : toggle using Glucose 3.0 by Gilles Audemard and Laurent Simon [default = %s]\n", pPars->fGlucose ? "yes" : "no" ); + Abc_Print( -2, "\t-d : toggle dumping decomposed networks into BLIF files [default = %s]\n", pPars->fDumpBlif ? "yes" : "no" ); Abc_Print( -2, "\t-v : toggle verbose printout [default = %s]\n", pPars->fVerbose ? "yes" : "no" ); Abc_Print( -2, "\t-h : print the command usage\n" ); Abc_Print( -2, "\t : truth table in hex notation\n" ); diff --git a/src/sat/bmc/bmc.h b/src/sat/bmc/bmc.h index b4d993c0a..3efe5a76d 100644 --- a/src/sat/bmc/bmc.h +++ b/src/sat/bmc/bmc.h @@ -69,6 +69,7 @@ struct Bmc_EsPar_t_ int nRandFuncs; int nMintNum; int Seed; + int fDumpBlif; int fVerbose; char * pTtStr; char * pSymStr; diff --git a/src/sat/bmc/bmcMaj.c b/src/sat/bmc/bmcMaj.c index 7c62c5c3a..b54418a41 100644 --- a/src/sat/bmc/bmcMaj.c +++ b/src/sat/bmc/bmcMaj.c @@ -1642,21 +1642,40 @@ int Exa3_ManExactSynthesis( Bmc_EsPar_t * pPars ) else if ( status == GLUCOSE_UNDEC ) printf( "The solver timed out after %d sec.\n", pPars->RuntimeLim ); else - printf( "The problem has no solution.\n" ); + printf( "The problem has no solution.\n" ), Res = 2; printf( "Added = %d. Tried = %d. ", p->nUsed[1], p->nUsed[0] ); Abc_PrintTime( 1, "Total runtime", Abc_Clock() - clkTotal ); - if ( iMint == -1 ) + if ( iMint == -1 && pPars->fDumpBlif ) Exa3_ManDumpBlif( p, fCompl ); if ( pPars->pSymStr ) ABC_FREE( pPars->pTtStr ); Exa3_ManFree( p ); return Res; } + +char * Exa_TimeStamp() +{ + static char Buffer[100]; + time_t ltime; + struct tm *tm_info; + + // Get the current time + time(<ime); + tm_info = localtime(<ime); + + // Format the time as YYYY_MM_DD__HH_MM_SS + strftime(Buffer, sizeof(Buffer), "%Y_%m_%d__%H_%M_%S", tm_info); + + return Buffer; +} + void Exa3_ManExactSynthesisRand( Bmc_EsPar_t * pPars ) { - int i, k, nDecs = 0, nWords = Abc_TtWordNum(pPars->nVars); + abctime clk = Abc_Clock(); + int i, k, Status, nDecs[3] = {0}, nWords = Abc_TtWordNum(pPars->nVars); word * pFun = ABC_ALLOC( word, nWords ); unsigned Rand0 = Abc_Random(1); + Vec_Str_t * vUndec = Vec_StrAlloc( 100 ); for ( i = 0; i < pPars->Seed; i++ ) Rand0 = Abc_Random(0); for ( i = 0; i < pPars->nRandFuncs; i++ ) { @@ -1680,11 +1699,31 @@ void Exa3_ManExactSynthesisRand( Bmc_EsPar_t * pPars ) printf( "\n" ); if ( pPars->fVerbose ) printf( "Truth table : %s\n", pPars->pTtStr ); - nDecs += Exa3_ManExactSynthesis( pPars ); + Status = Exa3_ManExactSynthesis( pPars ); + nDecs[Status]++; + if ( Status == 0 ) // undecided + Vec_StrPrintF( vUndec, "%s\n", pPars->pTtStr ); ABC_FREE( pPars->pTtStr ); } - printf( "\nDecomposable are %d (out of %d) functions (%.2f %%).\n\n", nDecs, pPars->nRandFuncs, 100.0*nDecs/pPars->nRandFuncs ); + printf( "\n" ); + printf( "Decomposable are %6d (out of %6d) functions (%6.2f %%).\n", nDecs[1], pPars->nRandFuncs, 100.0*nDecs[1]/pPars->nRandFuncs ); + printf( "Non-decomposable are %6d (out of %6d) functions (%6.2f %%).\n", nDecs[2], pPars->nRandFuncs, 100.0*nDecs[2]/pPars->nRandFuncs ); + printf( "Undecided are %6d (out of %6d) functions (%6.2f %%).\n", nDecs[0], pPars->nRandFuncs, 100.0*nDecs[0]/pPars->nRandFuncs ); ABC_FREE( pFun ); + if ( nDecs[0] > 0 ) { + char filename[1000]; + sprintf( filename, "undecided_%d_out_of_F%d_with_N%d_M%d_K%d_U%d_S%d_T%d%s__%s.txt", + nDecs[0], pPars->nRandFuncs, pPars->nVars, pPars->nNodes, pPars->nLutSize, + pPars->nMintNum, pPars->Seed, pPars->RuntimeLim, pPars->fLutCascade ? "_r" : "", Exa_TimeStamp() ); + FILE * pFile = fopen( filename, "wb" ); + if ( pFile ) { + fwrite( Vec_StrArray(vUndec), 1, Vec_StrSize(vUndec), pFile ); + fclose( pFile ); + printf( "The resulting undecided functions were written into file \"%s\".\n", filename ); + } + } + Abc_PrintTime( 1, "Total time", Abc_Clock() - clk ); + Vec_StrFree( vUndec ); } From 5e09cca9640f9a51c42a5364c8d0d4d57cc38d37 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Sat, 9 Aug 2025 14:46:45 -0700 Subject: [PATCH 09/22] Handing the case of signed comparators. --- src/aig/gia/giaGen.c | 13 ++++++++++--- src/base/abci/abc.c | 18 +++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/aig/gia/giaGen.c b/src/aig/gia/giaGen.c index 088b64191..c22cb21f8 100644 --- a/src/aig/gia/giaGen.c +++ b/src/aig/gia/giaGen.c @@ -1192,9 +1192,9 @@ Gia_Man_t * Gia_ManGenNeuron( char * pFileName, int nIBits, int nLutSize, int fD SeeAlso [] ***********************************************************************/ -Gia_Man_t * Gia_ManDupGenComp( int nBits, int fInterleave ) +Gia_Man_t * Gia_ManDupGenComp( int nBits, int fInterleave, int fSigned ) { - Gia_Man_t * pNew, * pTemp; int i, iLit = 1; + Gia_Man_t * pNew, * pTemp; int i, iLit = 1, iLitXor = 0, iLitB = 0; Vec_Int_t * vBitsA = Vec_IntAlloc( nBits + 1 ); Vec_Int_t * vBitsB = Vec_IntAlloc( nBits + 1 ); pNew = Gia_ManStart( 6*nBits+10 ); @@ -1211,6 +1211,10 @@ Gia_Man_t * Gia_ManDupGenComp( int nBits, int fInterleave ) for ( i = 0; i < nBits; i++ ) Vec_IntPush( vBitsB, Gia_ManAppendCi(pNew) ); } + if ( fSigned ) { + iLitXor = Gia_ManHashXor( pNew, Vec_IntPop(vBitsA), (iLitB = Vec_IntPop(vBitsB)) ); + nBits--; + } Vec_IntPush( vBitsA, 0 ); Vec_IntPush( vBitsB, 0 ); for ( i = 0; i < nBits; i++ ) { @@ -1227,7 +1231,10 @@ Gia_Man_t * Gia_ManDupGenComp( int nBits, int fInterleave ) int iOrLit = Gia_ManHashOr(pNew, iOrLit0, iOrLit1 ); iLit = Gia_ManHashOr(pNew, Abc_LitNot(iLit), iOrLit ); } - Gia_ManAppendCo( pNew, Abc_LitNotCond(iLit, nBits&1) ); + iLit = Abc_LitNotCond(iLit, nBits&1); + if ( fSigned ) + iLit = Gia_ManHashMux(pNew, iLitXor, iLitB, iLit ); + Gia_ManAppendCo( pNew, iLit ); pNew = Gia_ManCleanup( pTemp = pNew ); Gia_ManStop( pTemp ); Vec_IntFree( vBitsA ); diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index d9b688b9b..8f17275bf 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -56446,11 +56446,11 @@ usage: ***********************************************************************/ int Abc_CommandAbc9GenComp( Abc_Frame_t * pAbc, int argc, char ** argv ) { - extern Gia_Man_t * Gia_ManDupGenComp( int nBits, int fInterleave ); + extern Gia_Man_t * Gia_ManDupGenComp( int nBits, int fInterleave, int fSigned ); Gia_Man_t * pTemp = NULL; - int c, nBits = 0, fInter = 0, fVerbose = 0; + int c, nBits = 4, fInter = 0, fSigned = 0, fVerbose = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "Kivh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "Kisvh" ) ) != EOF ) { switch ( c ) { @@ -56468,6 +56468,9 @@ int Abc_CommandAbc9GenComp( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'i': fInter ^= 1; break; + case 's': + fSigned ^= 1; + break; case 'v': fVerbose ^= 1; break; @@ -56482,17 +56485,18 @@ int Abc_CommandAbc9GenComp( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -1, "Abc_CommandAbc9GenComp(): The number of inputs should be defined on the command line \"-K num\".\n" ); return 0; } - pTemp = Gia_ManDupGenComp( nBits, fInter ); + pTemp = Gia_ManDupGenComp( nBits, fInter, fSigned ); Abc_FrameUpdateGia( pAbc, pTemp ); if ( fVerbose ) Abc_Print( 1, "Generated %d-bit comparator.\n", nBits ); return 0; usage: - Abc_Print( -2, "usage: &gencomp [-K ] [-ivh]\n" ); - Abc_Print( -2, "\t generates the comparator\n" ); - Abc_Print( -2, "\t-K num : the number of control inputs [default = undefined]\n" ); + Abc_Print( -2, "usage: &gencomp [-K ] [-isvh]\n" ); + Abc_Print( -2, "\t generates the comparator (a > b)\n" ); + Abc_Print( -2, "\t-K num : the bitwidth of the inputs [default = %d]\n", nBits ); Abc_Print( -2, "\t-i : toggles using interleaved variable ordering [default = %s]\n", fInter ? "yes": "no" ); + Abc_Print( -2, "\t-s : toggles generating signed comparator [default = %s]\n", fSigned ? "yes": "no" ); Abc_Print( -2, "\t-v : toggles printing verbose information [default = %s]\n", fVerbose ? "yes": "no" ); Abc_Print( -2, "\t-h : print the command usage\n"); return 1; From a5715bc32d61d64f028507d1c249b8016a53fb7a Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Sat, 9 Aug 2025 16:43:55 -0700 Subject: [PATCH 10/22] Updates to the prefix tree generation. --- src/base/abci/abc.c | 58 ++++++-- src/misc/util/utilPrefix.cpp | 251 +++++++++++++++++++++++++++-------- 2 files changed, 243 insertions(+), 66 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 8f17275bf..522def0b8 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -56722,7 +56722,7 @@ usage: extern "C" { #endif -int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose, int use_or); +int* adder_return_array(int width, int mfo, int use_or, int seed, int num_rounds, int delay_relaxation, int fVerbose, int fDumpVer, int fDumpMiter, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds); #ifdef __cplusplus } @@ -56743,9 +56743,9 @@ int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatc int Abc_CommandAbc9GenPrefix( Abc_Frame_t * pAbc, int argc, char ** argv ) { extern Gia_Man_t * Gia_ManDupFromArray( int * pObjs, int nObjs, int nIns, int nLatches, int nOuts, int nAnds ); - int c, nBits = 8, nFans = 4, fDumpVer = 0, fDumpMiter = 0, fVerbose = 0, use_or = 0; + int c, nBits = 8, nFans = 4, Seed = 0, nIters = 1, DelayRelax = 0, fDumpVer = 0, fDumpMiter = 0, fVerbose = 0, use_or = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "NFdmov" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "NFSIRdmov" ) ) != EOF ) { switch ( c ) { @@ -56771,6 +56771,41 @@ int Abc_CommandAbc9GenPrefix( Abc_Frame_t * pAbc, int argc, char ** argv ) if ( nFans < 0 ) goto usage; break; + case 'S': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-S\" should be followed by an integer.\n" ); + goto usage; + } + Seed = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( Seed < 0 ) + goto usage; + break; + case 'I': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + nIters = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( nIters < 0 ) + goto usage; + break; + case 'R': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-R\" should be followed by an integer.\n" ); + goto usage; + } + DelayRelax = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( DelayRelax < 0 ) + goto usage; + break; + + case 'd': fDumpVer ^= 1; break; @@ -56795,18 +56830,25 @@ int Abc_CommandAbc9GenPrefix( Abc_Frame_t * pAbc, int argc, char ** argv ) else { int nObjs = 0, nIns = 0, nLatches = 0, nOuts = 0, nAnds = 0; - int * pObjs = adder_return_array( nBits, nFans, &nObjs, &nIns, &nLatches, &nOuts, &nAnds, fDumpVer, fDumpMiter, fVerbose, use_or ); - Gia_Man_t * pTemp = Gia_ManDupFromArray( pObjs, nObjs, nIns, nLatches, nOuts, nAnds ); - Abc_FrameUpdateGia( pAbc, pTemp ); - ABC_FREE( pObjs ); + int * pObjs = adder_return_array( nBits, nFans, use_or, Seed, nIters, DelayRelax, fVerbose, fDumpVer, fDumpMiter, &nObjs, &nIns, &nLatches, &nOuts, &nAnds ); + if ( pObjs == NULL ) { + printf( "Prefix tree with %d inputs and %d maximum fanout does not exist.\n", nBits, nFans ); + } else { + Gia_Man_t * pTemp = Gia_ManDupFromArray( pObjs, nObjs, nIns, nLatches, nOuts, nAnds ); + Abc_FrameUpdateGia( pAbc, pTemp ); + ABC_FREE( pObjs ); + } } return 0; usage: - Abc_Print( -2, "usage: &genprefix [-NF ] [-dmov]\n" ); + Abc_Print( -2, "usage: &genprefix [-NFSIR ] [-dmov]\n" ); Abc_Print( -2, "\t generates a prefix adder with minimum depth\n" ); Abc_Print( -2, "\t-N num : the bit-width of the adder [default = %d]\n", nBits ); Abc_Print( -2, "\t-F num : the limit on the fanout count [default = %d]\n", nFans ); + Abc_Print( -2, "\t-S num : the user's seed used to randomize search [default = %d]\n", Seed ); + Abc_Print( -2, "\t-I num : the number of iterations to find the smallest prefix [default = %d]\n", nIters ); + Abc_Print( -2, "\t-R num : the delay relaxation (the allowed level increase over log2(N)) [default = %d]\n", DelayRelax ); Abc_Print( -2, "\t-d : toggles dumping the adder in Verilog [default = %s]\n", fDumpVer ? "yes": "no" ); Abc_Print( -2, "\t-m : toggles dumping the miter in Verilog [default = %s]\n", fDumpMiter ? "yes": "no" ); Abc_Print( -2, "\t-o : toggles using additional optimization [default = %s]\n", use_or ? "yes": "no" ); diff --git a/src/misc/util/utilPrefix.cpp b/src/misc/util/utilPrefix.cpp index 0345d375a..bc99bc1d6 100644 --- a/src/misc/util/utilPrefix.cpp +++ b/src/misc/util/utilPrefix.cpp @@ -34,6 +34,12 @@ #include #include #include +#include +#include +#include +//#include +//#include +//#include class Graph; @@ -272,7 +278,7 @@ struct Graph { int min_level1 = min_level(); int max_level1 = max_level(); - std::cout << "\n" << "size=" << size() \ + std::cout << "Parameters: size=" << size() \ << " max_fanout=" << max_fanout() << " depth=" << max_level1 - min_level1 \ << " " << (validate() ? "(validates)" : "") << "\n"; @@ -326,7 +332,8 @@ int ceil_log2(int x) } void search(Node &node, Graph &graph, std::vector &candidate, - std::vector &best, int level_cap, int lsb, int ttl, bool first=false) + std::vector &best, int level_cap, int lsb, int ttl, + std::mt19937* rng = nullptr, bool first=false) { if (node.lsb == lsb) { /* @@ -336,8 +343,14 @@ void search(Node &node, Graph &graph, std::vector &candidate, } printf("\n"); */ - if (best.empty() || (candidate.size() <= best.size())) { + if (best.empty() || (candidate.size() < best.size())) { best = candidate; + } else if (candidate.size() == best.size() && rng != nullptr) { + // Randomize tie-breaking when sizes are equal + std::uniform_int_distribution dist(0, 1); + if (dist(*rng)) { + best = candidate; + } } } else if ((best.empty() || candidate.size() <= best.size() - 1) && ttl >= 1) { auto &nodes = graph.columns[node.lsb - 1]->nodes; @@ -347,14 +360,14 @@ void search(Node &node, Graph &graph, std::vector &candidate, && next_node.nfanouts() < graph.fanout_constraint && next_node.lsb >= lsb) { candidate.push_back(&next_node); search(next_node, graph, candidate, best, level_cap, lsb, - std::min(ttl, level_cap - next_node.level - 1)); + std::min(ttl, level_cap - next_node.level - 1), rng); candidate.pop_back(); } } } } -void greedy(Graph &graph, bool algo2=false, bool print=false) +void greedy(Graph &graph, bool algo2=false, bool print=false, std::mt19937* rng = nullptr) { for (int i = graph.bitwidth() - 1; i >= 0; i--) graph[i].nodes.back().update_cap(graph); @@ -381,7 +394,7 @@ void greedy(Graph &graph, bool algo2=false, bool print=false) it->deref_fanins(); search(*next_head, graph, candidate, best, head->level_cap, - head->lsb, std::numeric_limits::max(), true); + head->lsb, std::numeric_limits::max(), rng, true); for (auto it = base; it != std::next(head); it++) it->ref_fanins(); @@ -467,7 +480,8 @@ void seed(Graph &graph) // `search2` saves the first candidate of minimal size (compared to `search` which saves the last) void search2(Node &node, Graph &graph, std::vector &candidate, - std::vector &best, int level_cap, int lsb, int ttl, bool first=false) + std::vector &best, int level_cap, int lsb, int ttl, + std::mt19937* rng = nullptr, bool first=false) { if (node.lsb == lsb) { /* @@ -479,6 +493,12 @@ void search2(Node &node, Graph &graph, std::vector &candidate, */ if (best.empty() || (candidate.size() < best.size())) { best = candidate; + } else if (candidate.size() == best.size() && rng != nullptr) { + // Randomize tie-breaking when sizes are equal + std::uniform_int_distribution dist(0, 1); + if (dist(*rng)) { + best = candidate; + } } } else if ((best.empty() || candidate.size() < best.size() - 1) && ttl >= 1) { auto &nodes = graph.columns[node.lsb - 1]->nodes; @@ -488,21 +508,23 @@ void search2(Node &node, Graph &graph, std::vector &candidate, && next_node.nfanouts() < graph.fanout_constraint && next_node.lsb >= lsb) { candidate.push_back(&next_node); search2(next_node, graph, candidate, best, level_cap, lsb, - std::min(ttl, level_cap - next_node.level - 1)); + std::min(ttl, level_cap - next_node.level - 1), rng); candidate.pop_back(); } } } } -void algo3(Graph &graph) +int algo3(Graph &graph, std::mt19937* rng = nullptr) { for (int i = ((graph.bitwidth() - 1) / 2) * 2; i >= 2; i -= 2) { Column &column = graph[i]; std::vector candidate, best; search2(column.nodes.back(), graph, candidate, best, column.level_target, - 0, std::numeric_limits::max(), true); + 0, std::numeric_limits::max(), rng, true); // if you hit the assert perhaps the fanout constraint was too low + if ( best.empty() ) + return 0; assert(!best.empty()); for (auto fanin2 : best) { column.nodes.push_back(Node(&column.nodes.back(), fanin2)); @@ -513,6 +535,7 @@ void algo3(Graph &graph) for (int i = 0; i < graph.bitwidth(); i++) for (auto &node : graph[i].nodes) node.update_cap(graph); + return 1; } void algo4(Graph &graph) @@ -708,6 +731,26 @@ int* prefix_return_array_int(Graph& graph) { return array; } + +// Function to build prefix tree with optional random seed and delay relaxation +int build_prefix_tree(Graph& graph, int width, int mfo, std::mt19937* rng = nullptr, int delay_relaxation = 0) { + graph.initialize(width); + // Allow relaxed delay constraint: ceil_log2(width) + delay_relaxation + int target_depth = ceil_log2(width) + delay_relaxation; + graph.set_max_depth(target_depth); + graph.set_max_fanout(mfo / 2); + seed(graph); + greedy(graph, false, false, rng); + greedy(graph, false, false, rng); + graph.set_max_fanout(mfo); + if ( !algo3(graph, rng) ) + return 0; + algo4(graph); + greedy(graph, false, false, rng); + greedy(graph, false, false, rng); + return 1; +} + // Create compact integer array representation of prefix tree structure in this format: // (1) [1 integer]: Total number of integers in the array, including this entry // (2) [1 integer]: N = number of inputs to prefix tree (one input = one PG-pair) @@ -719,22 +762,11 @@ int* prefix_return_array_int(Graph& graph) { // - Internal nodes have IDs N to N+P-1 (in topological order) // (7) [N+1 integers]: Output prefix nodes; last one produces carry-out // Total array size: 5 + 2*N + 3*P integers -extern "C" int* prefix_return_array(int width, int mfo, int fVerbose) { +extern "C" int* prefix_return_array(int width, int mfo, int fVerbose, int delay_relaxation = 0) { // Build the prefix adder graph Graph graph; - graph.initialize(width); - graph.set_max_depth(ceil_log2(width)); - graph.set_max_fanout(mfo / 2); - - // Build the prefix tree using the same algorithm sequence - seed(graph); - greedy(graph); - greedy(graph); - graph.set_max_fanout(mfo); - algo3(graph); - algo4(graph); - greedy(graph); - greedy(graph); + if ( !build_prefix_tree( graph, width, mfo, nullptr, delay_relaxation ) ) + return NULL; if ( fVerbose ) graph.print(); // Get the prefix array representation @@ -890,33 +922,11 @@ void generate_prefix_adder_verilog(int* array, int width, int mfo, int print_mit } fclose(fp); - std::cerr << "Generated complete prefix adder from array: " << filename << std::endl; + //std::cerr << "Generated complete prefix adder from array: " << filename << std::endl; } // Create AIG array from the prefix array -extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose, int use_or) { - // Build the prefix adder graph - Graph graph; - graph.initialize(width); - graph.set_max_depth(ceil_log2(width)); - graph.set_max_fanout(mfo / 2); - - // Build the prefix tree using the same algorithm sequence - seed(graph); - greedy(graph); - greedy(graph); - graph.set_max_fanout(mfo); - algo3(graph); - algo4(graph); - greedy(graph); - greedy(graph); - if ( fVerbose ) - graph.print(); - - // Get the prefix array representation - int* prefix_array = prefix_return_array_int(graph); - if ( fDumpVer ) - generate_prefix_adder_verilog(prefix_array, width, mfo, fDumpMiter); +int* adder_return_array_int(int* prefix_array, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds, int fDumpVer, int fDumpMiter, int fVerbose, int use_or) { // Parse prefix array int idx = 0; @@ -925,12 +935,7 @@ extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, int P = prefix_array[idx++]; // Number of prefix nodes int L = prefix_array[idx++]; // Number of levels assert( L > 0 ); - - // Verify width matches - if (N != width) { - free(prefix_array); - return NULL; - } + int width = N; // Skip input delays (N integers) idx += N; @@ -959,9 +964,6 @@ extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, } assert(idx == array_size); - // Free prefix array - we've extracted all information - free(prefix_array); - // Now build AIG using the prefix tree structure int num_inputs = 2 * width; // a[0..width-1], b[0..width-1] int num_outputs = width + 1; // sum[0..width] @@ -1167,6 +1169,136 @@ extern "C" int* adder_return_array(int width, int mfo, int* pnObjs, int* pnIns, return pObjs; } +// Function to explore randomized prefix tree generation with optional delay relaxation +// Returns the prefix array of the best solution found +int* prefix_tree_explore(int width, int mfo, int seed, int num_rounds, int verbose, int delay_relaxation = 0, int verify = 0) { + // Print configuration in one line + if ( verbose ) + std::cout << "Exploring: width=" << width << ", mfo=" << mfo + << ", delay=" << ceil_log2(width) + delay_relaxation + << ", seed=" << seed << ", rounds=" << num_rounds << std::endl; + + // Statistics tracking + int min_size = INT_MAX, max_size = 0; + int total_size = 0; + int passed_count = 0; + + // Track best solution + int* best_array = nullptr; + int best_size = INT_MAX; + int best_depth = INT_MAX; + int best_fanout = INT_MAX; + int best_round = -1; + + for (int round = 0; round < num_rounds; round++) { + // Derive seed for this round deterministically from base seed + int round_seed = seed + round * 1000; // Simple deterministic derivation + + // Create random generator with specific seed + std::mt19937 rng(round_seed); + + // Build prefix tree with randomization and delay relaxation + Graph graph; + if ( !build_prefix_tree(graph, width, mfo, &rng, delay_relaxation) ) + continue; + + // Validate and get metrics + bool valid = graph.validate(); + int actual_mfo = graph.max_fanout(); + int size = graph.size(); + int depth = graph.max_level() - graph.min_level(); + + // Print one line per adder + if ( verbose ) + std::cout << " Round " << round << ": size=" << size + << ", fanout=" << actual_mfo + << ", depth=" << depth + << ", valid=" << (valid ? "Y" : "N") << std::endl; + + // Check if this is the best solution so far + // Tie-breaking: size, then depth, then fanout + bool is_better = false; + if (size < best_size) { + is_better = true; + } else if (size == best_size) { + if (depth < best_depth) { + is_better = true; + } else if (depth == best_depth) { + if (actual_mfo < best_fanout) { + is_better = true; + } + } + } + + if (is_better) { + // Free previous best array if exists + if (best_array) { + free(best_array); + } + // Store new best array + best_array = prefix_return_array_int(graph); + best_size = size; + best_depth = depth; + best_fanout = actual_mfo; + best_round = round; + } + + // Track statistics + total_size += size; + min_size = std::min(min_size, size); + max_size = std::max(max_size, size); + + /* + // Optionally verify + if (verify) { + extern bool verify_prefix_adder(const char* miter_filename, bool verbose = true); + char miter_filename[256]; + sprintf(miter_filename, "prefix_adder_%d_%d_seed%d_miter.v", width, mfo, round_seed); + int* array = prefix_return_array_int(graph); + generate_prefix_adder_verilog(array, width, mfo, 1, 0); + free(array); + char rename_cmd[512]; + sprintf(rename_cmd, "mv prefix_adder_%d_%d_miter.v %s 2>/dev/null", width, mfo, miter_filename); + int status = system(rename_cmd); + bool passed = verify_prefix_adder(miter_filename, false); + if (passed) passed_count++; + remove(miter_filename); + } + */ + } + + // Print summary + if ( verbose ) + std::cout << "Summary: best=" << best_size << " (round " << best_round + << "), range=[" << min_size << "," << max_size + << "], avg=" << std::fixed << std::setprecision(1) + << (double)total_size / num_rounds; + if (verbose && verify) { + std::cout << ", verified=" << passed_count << "/" << num_rounds; + } + std::cout << std::endl; + + // Return the prefix array of the best solution + return best_array; +} + +// Create AIG array from the prefix array +extern "C" int* adder_return_array(int width, int mfo, int use_or, int seed, int num_rounds, int delay_relaxation, int fVerbose, int fDumpVer, int fDumpMiter, int* pnObjs, int* pnIns, int* pnLatches, int* pnOuts, int* pnAnds) { + int* prefix_array = NULL; + if ( num_rounds == 1 ) + prefix_array = prefix_return_array(width, mfo, fVerbose, delay_relaxation); + else + prefix_array = prefix_tree_explore(width, mfo, seed, num_rounds, fVerbose, delay_relaxation); + if ( prefix_array == NULL ) + return NULL; + if ( fDumpVer ) + generate_prefix_adder_verilog(prefix_array, width, mfo, fDumpMiter); + int* result = adder_return_array_int(prefix_array, pnObjs, pnIns, pnLatches, pnOuts, pnAnds, fDumpVer, fDumpMiter, fVerbose, use_or); + free( prefix_array); + return result; +} + + // Write variable-length encoded unsigned integer for binary AIGER void write_aiger_encoded(FILE* fp, unsigned x) { unsigned char ch; @@ -1203,12 +1335,15 @@ void generate_prefix_adder_aiger_int( char * pFileName, int * pObjs, int nObjs, fprintf( pFile, "c\n" ); fclose( pFile ); } + void generate_prefix_adder_aiger( int width, int mfo ) { char pFileName[100]; sprintf( pFileName, "prefix_adder_%d_%d.aig", width, mfo ); int nObjs = 0, nIns = 0, nLatches = 0, nOuts = 0, nAnds = 0; - int * pObjs = adder_return_array( width, mfo, &nObjs, &nIns, &nLatches, &nOuts, &nAnds, 0, 0, 0, 0 ); + int * pObjs = adder_return_array( width, mfo, 0, 0, 1, 0, 0, 0, 0, &nObjs, &nIns, &nLatches, &nOuts, &nAnds ); + if ( pObjs == NULL ) + return; generate_prefix_adder_aiger_int( pFileName, pObjs, nObjs, nIns, nLatches, nOuts, nAnds ); printf( "Written prefix adder into AIGER file \"%s\".\n", pFileName ); free(pObjs); From 00910e36ff1c104a24a3c2420cb53f49b02affc5 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Sat, 9 Aug 2025 17:00:02 -0700 Subject: [PATCH 11/22] Fixing typos. --- src/base/abci/abc.c | 8 ++++---- src/misc/util/utilPrefix.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 522def0b8..6b2e1493d 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -56846,9 +56846,9 @@ usage: Abc_Print( -2, "\t generates a prefix adder with minimum depth\n" ); Abc_Print( -2, "\t-N num : the bit-width of the adder [default = %d]\n", nBits ); Abc_Print( -2, "\t-F num : the limit on the fanout count [default = %d]\n", nFans ); - Abc_Print( -2, "\t-S num : the user's seed used to randomize search [default = %d]\n", Seed ); - Abc_Print( -2, "\t-I num : the number of iterations to find the smallest prefix [default = %d]\n", nIters ); - Abc_Print( -2, "\t-R num : the delay relaxation (the allowed level increase over log2(N)) [default = %d]\n", DelayRelax ); + Abc_Print( -2, "\t-S num : the random seed used to randomize search [default = %d]\n", Seed ); + Abc_Print( -2, "\t-I num : the number of iterations to find the smallest prefix tree [default = %d]\n", nIters ); + Abc_Print( -2, "\t-R num : the delay relaxation (the max allowed level increase over log2(N)) [default = %d]\n", DelayRelax ); Abc_Print( -2, "\t-d : toggles dumping the adder in Verilog [default = %s]\n", fDumpVer ? "yes": "no" ); Abc_Print( -2, "\t-m : toggles dumping the miter in Verilog [default = %s]\n", fDumpMiter ? "yes": "no" ); Abc_Print( -2, "\t-o : toggles using additional optimization [default = %s]\n", use_or ? "yes": "no" ); @@ -56856,7 +56856,7 @@ usage: Abc_Print( -2, "\t The code of this command is contributed by Martin Povišer \n\n" ); Abc_Print( -2, "\t The implementation is inspired by S. Roy, M. Choudhury, R. Puri, D. Pan,\n" ); Abc_Print( -2, "\t \"Polynomial time algorithm for area and power efficient adder synthesis\n" ); - Abc_Print( -2, "\t in high-performance designs\", Proc. ASP-DAC 2025.\n" ); + Abc_Print( -2, "\t in high-performance designs\", Proc. ASP-DAC 2015.\n" ); Abc_Print( -2, "\t https://www.cerc.utexas.edu/utda/publications/C166.pdf\n" ); return 1; } diff --git a/src/misc/util/utilPrefix.cpp b/src/misc/util/utilPrefix.cpp index bc99bc1d6..50db0723c 100644 --- a/src/misc/util/utilPrefix.cpp +++ b/src/misc/util/utilPrefix.cpp @@ -21,7 +21,7 @@ /* The implementation is inspired by S. Roy, M. Choudhury, R. Puri, D. Pan, "Polynomial time algorithm for area and power efficient adder synthesis - in high-performance designs", Proc. ASP-DAC 2025. + in high-performance designs", Proc. ASP-DAC 2015. https://www.cerc.utexas.edu/utda/publications/C166.pdf */ From 15151c58ed3fabcc90e7e0dbfe46cbc03f728f78 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Sat, 9 Aug 2025 18:10:32 -0700 Subject: [PATCH 12/22] Updating &stochsyn with switch '-d' to support level-preserving AIG optimizations. --- src/aig/gia/giaStoch.c | 80 ++++++++++++++++++++++++++++++++---------- src/base/abci/abc.c | 16 +++++---- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/aig/gia/giaStoch.c b/src/aig/gia/giaStoch.c index c88bdae6b..20bfd1d8e 100644 --- a/src/aig/gia/giaStoch.c +++ b/src/aig/gia/giaStoch.c @@ -296,7 +296,7 @@ void Gia_ManStochSynthesis( Vec_Ptr_t * vAigs, char * pScript ) SeeAlso [] ***********************************************************************/ -int Gia_ManFilterPartitions( Gia_Man_t * p, Vec_Ptr_t * vvIns, Vec_Ptr_t * vvNodes, Vec_Ptr_t * vvOuts, Vec_Ptr_t * vWins, Vec_Int_t * vGains ) +int Gia_ManFilterPartitions( Gia_Man_t * p, Vec_Ptr_t * vvIns, Vec_Ptr_t * vvNodes, Vec_Ptr_t * vvOuts, Vec_Ptr_t * vWins, Vec_Int_t * vGains, int fDelayOpt ) { int RetValue = Vec_PtrSize(vvIns); Vec_Ptr_t * vvInsNew = Vec_PtrAlloc( 10 ); @@ -312,8 +312,8 @@ int Gia_ManFilterPartitions( Gia_Man_t * p, Vec_Ptr_t * vvIns, Vec_Ptr_t * vvNod Vec_PtrPush( vvInsNew, Vec_IntDup((Vec_Int_t *)Vec_PtrEntry(vvIns, iEntry)) ); Vec_PtrPush( vvOutsNew, Vec_IntDup((Vec_Int_t *)Vec_PtrEntry(vvOuts, iEntry)) ); Vec_PtrPush( vvWinsNew, Gia_ManDupDfs((Gia_Man_t *)Vec_PtrEntry(vWins, iEntry)) ); - extern void Gia_ManMarkTfiTfo( Vec_Int_t * vOne, Gia_Man_t * pMan ); - Gia_ManMarkTfiTfo( (Vec_Int_t *)Vec_PtrEntryLast(vvInsNew), p ); + extern void Gia_ManMarkTfiTfo( Vec_Int_t * vOne, Gia_Man_t * pMan, int fDelayOpt ); + Gia_ManMarkTfiTfo( (Vec_Int_t *)Vec_PtrEntryLast(vvInsNew), p, fDelayOpt ); Vec_IntForEachEntry( vGains, Gain, i ) { if ( Gain < 0 ) continue; @@ -556,12 +556,14 @@ void Gia_ManSelectRemove( Vec_Wec_t * vSupps, Vec_Int_t * vOne ) Vec_WecRemoveEmpty( vSupps ); } // marks TFI/TFO of this one -void Gia_ManMarkTfiTfo( Vec_Int_t * vOne, Gia_Man_t * pMan ) +void Gia_ManMarkTfiTfo( Vec_Int_t * vOne, Gia_Man_t * pMan, int fDelayOpt ) { int i; Gia_Obj_t * pObj; Gia_ManForEachObjVec( vOne, pMan, pObj, i ) { - //Gia_ObjSetTravIdPrevious(pMan, pObj); - //Gia_ObjDfsMark_rec( pMan, pObj ); + if ( fDelayOpt ) { + Gia_ObjSetTravIdPrevious(pMan, pObj); + Gia_ObjDfsMark_rec( pMan, pObj ); + } Gia_ObjSetTravIdPrevious(pMan, pObj); Gia_ObjDfsMark2_rec( pMan, pObj ); } @@ -632,7 +634,7 @@ Vec_Ptr_t * Gia_ManDeriveWinInsAll( Vec_Wec_t * vSupps, int nSuppMax, Gia_Man_t } return vRes; } -Gia_Man_t * Gia_ManDupFromArrays( Gia_Man_t * p, Vec_Int_t * vCis, Vec_Int_t * vAnds, Vec_Int_t * vCos ) +Gia_Man_t * Gia_ManDupFromArrays( Gia_Man_t * p, Vec_Int_t * vCis, Vec_Int_t * vAnds, Vec_Int_t * vCos, Vec_Int_t * vLevels[2], int nLevels ) { Gia_Man_t * pNew; Gia_Obj_t * pObj; @@ -647,10 +649,28 @@ Gia_Man_t * Gia_ManDupFromArrays( Gia_Man_t * p, Vec_Int_t * vCis, Vec_Int_t * v pObj->Value = Gia_ManAppendAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); Gia_ManForEachObjVec( vCos, p, pObj, i ) pObj->Value = Gia_ManAppendCo( pNew, pObj->Value ); + if ( vLevels[0] ) { + pNew->vCiArrs = Vec_IntAlloc( Gia_ManCiNum(pNew) ); + Gia_ManForEachObjVec( vCis, p, pObj, i ) + Vec_IntPush( pNew->vCiArrs, Gia_ObjLevel(p, pObj) ); + pNew->vCoReqs = Vec_IntAlloc( Gia_ManCoNum(pNew) ); + Gia_ManForEachObjVec( vCos, p, pObj, i ) + Vec_IntPush( pNew->vCoReqs, nLevels - Gia_ObjLevel(p, pObj) ); + } return pNew; } -Vec_Ptr_t * Gia_ManDupWindows( Gia_Man_t * pMan, Vec_Ptr_t * vvIns, Vec_Ptr_t * vvNodes, Vec_Ptr_t * vvOuts ) +Vec_Ptr_t * Gia_ManDupWindows( Gia_Man_t * pMan, Vec_Ptr_t * vvIns, Vec_Ptr_t * vvNodes, Vec_Ptr_t * vvOuts, int fDelayOpt ) { + // compute direct and reverse level + Vec_Int_t * vLevels[2] = {NULL}; + if ( fDelayOpt ) { + int Levels[2]; + Levels[0] = Gia_ManLevelNum( pMan ); + ABC_SWAP( Vec_Int_t *, vLevels[0], pMan->vLevels ); + Levels[1] = Gia_ManLevelRNum( pMan ); + ABC_SWAP( Vec_Int_t *, vLevels[1], pMan->vLevels ); + assert( Levels[0] == Levels[1] ); + } Vec_Int_t * vNodes; int i; Vec_Ptr_t * vWins = Vec_PtrAlloc( Vec_PtrSize(vvIns) ); assert( Vec_PtrSize(vvIns) == Vec_PtrSize(vvNodes) ); @@ -660,9 +680,11 @@ Vec_Ptr_t * Gia_ManDupWindows( Gia_Man_t * pMan, Vec_Ptr_t * vvIns, Vec_Ptr_t * Vec_PtrForEachEntry( Vec_Int_t *, vvNodes, vNodes, i ) { Vec_Int_t * vIns = (Vec_Int_t *)Vec_PtrEntry(vvIns, i); Vec_Int_t * vOuts = (Vec_Int_t *)Vec_PtrEntry(vvOuts, i); - Gia_Man_t * pNew = Gia_ManDupFromArrays( pMan, vIns, vNodes, vOuts ); + Gia_Man_t * pNew = Gia_ManDupFromArrays( pMan, vIns, vNodes, vOuts, vLevels, pMan->nLevels ); Vec_PtrPush( vWins, pNew ); } + Vec_IntFreeP( &vLevels[0] ); + Vec_IntFreeP( &vLevels[1] ); return vWins; } int Gia_ManLevelR( Gia_Man_t * pMan ) @@ -675,7 +697,7 @@ int Gia_ManLevelR( Gia_Man_t * pMan ) Gia_ObjSetLevel( pMan, pNode, 0 ); return LevelMax; } -Vec_Ptr_t * Gia_ManExtractPartitions( Gia_Man_t * pMan, int Iter, int nSuppMax, Vec_Ptr_t ** pvIns, Vec_Ptr_t ** pvOuts, Vec_Ptr_t ** pvNodes, int fOverlap ) +Vec_Ptr_t * Gia_ManExtractPartitions( Gia_Man_t * pMan, int Iter, int nSuppMax, Vec_Ptr_t ** pvIns, Vec_Ptr_t ** pvOuts, Vec_Ptr_t ** pvNodes, int fOverlap, int fDelayOpt ) { // if ( Gia_ManCiNum(pMan) <= nSuppMax ) { // Vec_Ptr_t * vWins = Vec_PtrAlloc( 1 ); @@ -695,7 +717,7 @@ Vec_Ptr_t * Gia_ManExtractPartitions( Gia_Man_t * pMan, int Iter, int nSuppMax, Vec_Ptr_t * vIns = Gia_ManDeriveWinInsAll( vSupps, nSuppMax, pMan, fOverlap ); Vec_Ptr_t * vNodes = Gia_ManDeriveWinNodesAll( pMan, vIns, vStore ); Vec_Ptr_t * vOuts = Gia_ManDeriveWinOutsAll( pMan, vNodes ); - Vec_Ptr_t * vWins = Gia_ManDupWindows( pMan, vIns, vNodes, vOuts ); + Vec_Ptr_t * vWins = Gia_ManDupWindows( pMan, vIns, vNodes, vOuts, fDelayOpt ); Vec_WecFree( vSupps ); Vec_WecFree( vStore ); *pvIns = vIns; @@ -742,7 +764,7 @@ void Gia_ManCollectNodes( Gia_Man_t * p, Vec_Int_t * vCis, Vec_Int_t * vAnds, Ve Vec_IntForEachEntry( vCos, iObj, i ) Gia_ManCollectNodes_rec( p, iObj, vAnds ); } -Gia_Man_t * Gia_ManDupDivideOne( Gia_Man_t * p, Vec_Int_t * vCis, Vec_Int_t * vAnds, Vec_Int_t * vCos ) +Gia_Man_t * Gia_ManDupDivideOne( Gia_Man_t * p, Vec_Int_t * vCis, Vec_Int_t * vAnds, Vec_Int_t * vCos, Vec_Int_t * vLevels[2], int nLevels ) { Vec_Int_t * vMapping; int i; Gia_Man_t * pNew; Gia_Obj_t * pObj; @@ -757,8 +779,16 @@ Gia_Man_t * Gia_ManDupDivideOne( Gia_Man_t * p, Vec_Int_t * vCis, Vec_Int_t * vA Gia_ManForEachObjVec( vCos, p, pObj, i ) Gia_ManAppendCo( pNew, pObj->Value ); assert( Gia_ManCiNum(pNew) > 0 && Gia_ManCoNum(pNew) > 0 ); - if ( !Gia_ManHasMapping(p) ) + if ( !Gia_ManHasMapping(p) ) { + if ( vLevels[0] == NULL ) return pNew; + pNew->vCiArrs = Vec_IntAlloc( Gia_ManCiNum(pNew) ); + Gia_ManForEachObjVec( vCis, p, pObj, i ) + Vec_IntPush( pNew->vCiArrs, Gia_ObjLevel(p, pObj) ); + pNew->vCoReqs = Vec_IntAlloc( Gia_ManCoNum(pNew) ); + Gia_ManForEachObjVec( vCos, p, pObj, i ) + Vec_IntPush( pNew->vCoReqs, nLevels - Gia_ObjLevel(p, pObj) ); return pNew; + } vMapping = Vec_IntAlloc( 4*Gia_ManObjNum(pNew) ); Vec_IntFill( vMapping, Gia_ManObjNum(pNew), 0 ); Gia_ManForEachObjVec( vAnds, p, pObj, i ) @@ -776,17 +806,29 @@ Gia_Man_t * Gia_ManDupDivideOne( Gia_Man_t * p, Vec_Int_t * vCis, Vec_Int_t * vA pNew->vMapping = vMapping; return pNew; } -Vec_Ptr_t * Gia_ManDupDivide( Gia_Man_t * p, Vec_Wec_t * vCis, Vec_Wec_t * vAnds, Vec_Wec_t * vCos, char * pScript, int nProcs, int TimeOut ) +Vec_Ptr_t * Gia_ManDupDivide( Gia_Man_t * p, Vec_Wec_t * vCis, Vec_Wec_t * vAnds, Vec_Wec_t * vCos, char * pScript, int nProcs, int TimeOut, int fDelayOpt ) { + // compute direct and reverse level + Vec_Int_t * vLevels[2] = {NULL}; + if ( fDelayOpt ) { + int Levels[2]; + Levels[0] = Gia_ManLevelNum( p ); + ABC_SWAP( Vec_Int_t *, vLevels[0], p->vLevels ); + Levels[1] = Gia_ManLevelRNum( p ); + ABC_SWAP( Vec_Int_t *, vLevels[1], p->vLevels ); + assert( Levels[0] == Levels[1] ); + } Vec_Ptr_t * vAigs = Vec_PtrAlloc( Vec_WecSize(vCis) ); int i; for ( i = 0; i < Vec_WecSize(vCis); i++ ) { Gia_ManCollectNodes( p, Vec_WecEntry(vCis, i), Vec_WecEntry(vAnds, i), Vec_WecEntry(vCos, i) ); - Vec_PtrPush( vAigs, Gia_ManDupDivideOne(p, Vec_WecEntry(vCis, i), Vec_WecEntry(vAnds, i), Vec_WecEntry(vCos, i)) ); + Vec_PtrPush( vAigs, Gia_ManDupDivideOne(p, Vec_WecEntry(vCis, i), Vec_WecEntry(vAnds, i), Vec_WecEntry(vCos, i), vLevels, p->nLevels) ); } //Gia_ManStochSynthesis( vAigs, pScript ); Vec_Int_t * vGains = Gia_StochProcess( vAigs, pScript, nProcs, TimeOut, 0 ); Vec_IntFree( vGains ); + Vec_IntFreeP( &vLevels[0] ); + Vec_IntFreeP( &vLevels[1] ); return vAigs; } Gia_Man_t * Gia_ManDupStitch( Gia_Man_t * p, Vec_Wec_t * vCis, Vec_Wec_t * vAnds, Vec_Wec_t * vCos, Vec_Ptr_t * vAigs, int fHash ) @@ -982,7 +1024,7 @@ Vec_Wec_t * Gia_ManStochOutputs( Gia_Man_t * p, Vec_Wec_t * vAnds ) SeeAlso [] ***********************************************************************/ -void Gia_ManStochSyn( int nSuppMax, int nMaxSize, int nIters, int TimeOut, int Seed, int fVerbose, char * pScript, int nProcs ) +void Gia_ManStochSyn( int nSuppMax, int nMaxSize, int nIters, int TimeOut, int Seed, int fVerbose, char * pScript, int nProcs, int fDelayOpt ) { abctime nTimeToStop = TimeOut ? Abc_Clock() + TimeOut * CLOCKS_PER_SEC : 0; abctime clkStart = Abc_Clock(); @@ -1008,7 +1050,7 @@ void Gia_ManStochSyn( int nSuppMax, int nMaxSize, int nIters, int TimeOut, int S Vec_Wec_t * vAnds = Gia_ManStochNodes( pGia, nMaxSize, Abc_Random(0) & 0x7FFFFFFF ); Vec_Wec_t * vIns = Gia_ManStochInputs( pGia, vAnds ); Vec_Wec_t * vOuts = Gia_ManStochOutputs( pGia, vAnds ); - Vec_Ptr_t * vAigs = Gia_ManDupDivide( pGia, vIns, vAnds, vOuts, pScript, nProcs, TimeOut ); + Vec_Ptr_t * vAigs = Gia_ManDupDivide( pGia, vIns, vAnds, vOuts, pScript, nProcs, TimeOut, fDelayOpt ); Gia_Man_t * pNew = Gia_ManDupStitchMap( pGia, vIns, vAnds, vOuts, vAigs ); int fMapped = Gia_ManHasMapping(pGia) && Gia_ManHasMapping(pNew); Abc_FrameUpdateGia( Abc_FrameGetGlobalFrame(), pNew ); @@ -1039,9 +1081,9 @@ void Gia_ManStochSyn( int nSuppMax, int nMaxSize, int nIters, int TimeOut, int S extern Gia_Man_t * Gia_ManDupInsertWindows( Gia_Man_t * p, Vec_Ptr_t * vvIns, Vec_Ptr_t * vvOuts, Vec_Ptr_t * vAigs ); abctime clk = Abc_Clock(); Gia_Man_t * pGia = Gia_ManDup( Abc_FrameReadGia(Abc_FrameGetGlobalFrame()) ); Gia_ManStaticFanoutStart(pGia); - Vec_Ptr_t * vAigs = Gia_ManExtractPartitions( pGia, i, nSuppMax, &vIns, &vOuts, &vNodes, fOverlap ); + Vec_Ptr_t * vAigs = Gia_ManExtractPartitions( pGia, i, nSuppMax, &vIns, &vOuts, &vNodes, fOverlap, fDelayOpt ); Vec_Int_t * vGains = Gia_StochProcess( vAigs, pScript, nProcs, TimeOut, 0 ); - int nPartsInit = fOverlap ? Gia_ManFilterPartitions( pGia, vIns, vNodes, vOuts, vAigs, vGains ) : Vec_PtrSize(vIns); + int nPartsInit = fOverlap ? Gia_ManFilterPartitions( pGia, vIns, vNodes, vOuts, vAigs, vGains, fDelayOpt ) : Vec_PtrSize(vIns); Gia_Man_t * pNew = Gia_ManDupInsertWindows( pGia, vIns, vOuts, vAigs ); Gia_ManStaticFanoutStop(pGia); Abc_FrameUpdateGia( Abc_FrameGetGlobalFrame(), pNew ); if ( fVerbose ) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 6b2e1493d..8ab2ff672 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -53618,10 +53618,10 @@ usage: ***********************************************************************/ int Abc_CommandAbc9StochSyn( Abc_Frame_t * pAbc, int argc, char ** argv ) { - extern void Gia_ManStochSyn( int nSuppMax, int nMaxSize, int nIters, int TimeOut, int Seed, int fVerbose, char * pScript, int nProcs ); - int c, nSuppMax = 0, nMaxSize = 1000, nIters = 10, TimeOut = 0, Seed = 0, nProcs = 1, fVerbose = 0; char * pScript; + extern void Gia_ManStochSyn( int nSuppMax, int nMaxSize, int nIters, int TimeOut, int Seed, int fVerbose, char * pScript, int nProcs, int fDelayOpt ); + int c, nSuppMax = 0, nMaxSize = 1000, nIters = 10, TimeOut = 0, Seed = 0, nProcs = 1, fDelayOpt = 0, fVerbose = 0; char * pScript; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "NMITSPvh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "NMITSPdvh" ) ) != EOF ) { switch ( c ) { @@ -53691,6 +53691,9 @@ int Abc_CommandAbc9StochSyn( Abc_Frame_t * pAbc, int argc, char ** argv ) if ( nProcs < 0 ) goto usage; break; + case 'd': + fDelayOpt ^= 1; + break; case 'v': fVerbose ^= 1; break; @@ -53711,19 +53714,20 @@ int Abc_CommandAbc9StochSyn( Abc_Frame_t * pAbc, int argc, char ** argv ) goto usage; } pScript = Abc_UtilStrsav( argv[globalUtilOptind] ); - Gia_ManStochSyn( nSuppMax, nMaxSize, nIters, TimeOut, Seed, fVerbose, pScript, nProcs ); + Gia_ManStochSyn( nSuppMax, nMaxSize, nIters, TimeOut, Seed, fVerbose, pScript, nProcs, fDelayOpt ); ABC_FREE( pScript ); return 0; usage: - Abc_Print( -2, "usage: &stochsyn [-NMITSP ] [-tvh]