From 2d9af6c9a41850622f06691a0152295463efb115 Mon Sep 17 00:00:00 2001 From: aletempiac Date: Thu, 8 Feb 2024 09:36:58 +0100 Subject: [PATCH] Adding ACD for 66 LUT structure using a new method --- src/base/abci/abc.c | 27 +- src/map/if/acd/ac_wrapper.cpp | 42 + src/map/if/acd/ac_wrapper.h | 3 + src/map/if/acd/acd66.hpp | 1093 +++++++++++++++++++++++++++ src/map/if/acd/kitty_operations.hpp | 18 + src/map/if/acd/kitty_static_tt.hpp | 110 ++- src/map/if/if.h | 2 + src/map/if/ifDec66.c | 90 +++ src/map/if/module.make | 1 + 9 files changed, 1383 insertions(+), 3 deletions(-) create mode 100644 src/map/if/acd/acd66.hpp create mode 100644 src/map/if/ifDec66.c diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index e0ca6e948..3c5d3cbb8 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -19447,7 +19447,7 @@ int Abc_CommandIf( Abc_Frame_t * pAbc, int argc, char ** argv ) If_ManSetDefaultPars( pPars ); pPars->pLutLib = (If_LibLut_t *)Abc_FrameReadLibLut(); Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "KCFAGRNTXYZDEWSqaflepmrsdbgxyzuojiktncvh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "KCFAGRNTXYZDEWSJqaflepmrsdbgxyzuojiktncvh" ) ) != EOF ) { switch ( c ) { @@ -19621,6 +19621,21 @@ int Abc_CommandIf( Abc_Frame_t * pAbc, int argc, char ** argv ) goto usage; } break; + case 'J': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-J\" should be followed by string.\n" ); + goto usage; + } + pPars->pLutStruct = argv[globalUtilOptind]; + pPars->fEnableStructN = 1; + globalUtilOptind++; + if ( strlen(pPars->pLutStruct) != 2 ) + { + Abc_Print( -1, "Command line switch \"-J\" should be followed by a 2-char string (e.g. \"66\").\n" ); + goto usage; + } + break; case 'q': pPars->fPreprocess ^= 1; break; @@ -19801,7 +19816,14 @@ int Abc_CommandIf( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -1, "This feature only works for [6;16]-LUTs.\n" ); return 1; } - pPars->pFuncCell = pPars->fDelayOptLut ? NULL : If_CutPerformCheck16; + if ( pPars->fEnableStructN ) + { + pPars->pFuncCell = pPars->fDelayOptLut ? NULL : If_CutPerformCheck66; + } + else + { + pPars->pFuncCell = pPars->fDelayOptLut ? NULL : If_CutPerformCheck16; + } pPars->fCutMin = 1; } @@ -20003,6 +20025,7 @@ usage: Abc_Print( -2, "\t-E float : sets epsilon used for tie-breaking [default = %f]\n", pPars->Epsilon ); Abc_Print( -2, "\t-W float : sets wire delay between adjects LUTs [default = %f]\n", pPars->WireDelay ); Abc_Print( -2, "\t-S str : string representing the LUT structure [default = %s]\n", pPars->pLutStruct ? pPars->pLutStruct : "not used" ); + Abc_Print( -2, "\t-J str : string representing the LUT structure (new method) [default = %s]\n", pPars->pLutStruct ? pPars->pLutStruct : "not used" ); Abc_Print( -2, "\t-q : toggles preprocessing using several starting points [default = %s]\n", pPars->fPreprocess? "yes": "no" ); Abc_Print( -2, "\t-a : toggles area-oriented mapping [default = %s]\n", pPars->fArea? "yes": "no" ); Abc_Print( -2, "\t-r : enables expansion/reduction of the best cuts [default = %s]\n", pPars->fExpRed? "yes": "no" ); diff --git a/src/map/if/acd/ac_wrapper.cpp b/src/map/if/acd/ac_wrapper.cpp index fd8015f95..7268fcad4 100644 --- a/src/map/if/acd/ac_wrapper.cpp +++ b/src/map/if/acd/ac_wrapper.cpp @@ -18,6 +18,7 @@ #include "ac_wrapper.h" #include "ac_decomposition.hpp" +#include "acd66.hpp" ABC_NAMESPACE_IMPL_START @@ -69,4 +70,45 @@ int acd_decompose( word * pTruth, unsigned nVars, int lutSize, unsigned *pdelay, return 0; } +int acd66_evaluate( word * pTruth, unsigned nVars, int verify ) +{ + using namespace acd; + + acd66_params ps; + ps.verify = static_cast( verify ); + acd66_impl acd( nVars, ps ); + + if ( acd.run( pTruth ) == 0 ) + return 0; + + if ( !verify ) + return 1; + + int val = acd.compute_decomposition(); + if ( val != 0 ) + { + return 0; + } + + return 1; +} + +int acd66_decompose( word * pTruth, unsigned nVars, unsigned char *decomposition ) +{ + using namespace acd; + + acd66_params ps; + acd66_impl acd( nVars, ps ); + acd.run( pTruth ); + + int val = acd.compute_decomposition(); + if ( val != 0 ) + { + return -1; + } + + acd.get_decomposition( decomposition ); + return 0; +} + ABC_NAMESPACE_IMPL_END diff --git a/src/map/if/acd/ac_wrapper.h b/src/map/if/acd/ac_wrapper.h index a384b4404..2b832c287 100644 --- a/src/map/if/acd/ac_wrapper.h +++ b/src/map/if/acd/ac_wrapper.h @@ -28,6 +28,9 @@ ABC_NAMESPACE_HEADER_START int acd_evaluate( word * pTruth, unsigned nVars, int lutSize, unsigned *pdelay, unsigned *cost, int try_no_late_arrival ); int acd_decompose( word * pTruth, unsigned nVars, int lutSize, unsigned *pdelay, unsigned char *decomposition ); +int acd66_evaluate( word * pTruth, unsigned nVars, int verify ); +int acd66_decompose( word * pTruth, unsigned nVars, unsigned char *decomposition ); + ABC_NAMESPACE_HEADER_END #endif \ No newline at end of file diff --git a/src/map/if/acd/acd66.hpp b/src/map/if/acd/acd66.hpp new file mode 100644 index 000000000..a1369db83 --- /dev/null +++ b/src/map/if/acd/acd66.hpp @@ -0,0 +1,1093 @@ +/**C++File************************************************************** + + FileName [acd66.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Ashenhurst-Curtis decomposition.] + + Synopsis [Interface with the FPGA mapping package.] + + Author [Alessandro Tempia Calvino] + + Affiliation [EPFL] + + Date [Ver. 1.0. Started - Feb 8, 2024.] + +***********************************************************************/ +/*! + \file acd66.hpp + \brief Ashenhurst-Curtis decomposition for "66" cascade + + \author Alessandro Tempia Calvino +*/ + +#ifndef _ACD66_H_ +#define _ACD66_H_ +#pragma once + +#include +#include +#include +#include +#include + +#include "kitty_constants.hpp" +#include "kitty_constructors.hpp" +#include "kitty_dynamic_tt.hpp" +#include "kitty_operations.hpp" +#include "kitty_operators.hpp" +#include "kitty_static_tt.hpp" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace acd +{ + +/*! \brief Parameters for acd66 */ +struct acd66_params +{ + /*! \brief Maximum size of the free set (1 < num < 6). */ + uint32_t max_free_set_vars{ 5 }; + + /*! \brief Number of configurations to test for decomposition. */ + uint32_t max_evaluations{ 3 }; + + /*! \brief Run verification before returning. */ + bool verify{ true }; +}; + +/*! \brief Statistics for acd66 */ +struct acd66_stats +{ + uint32_t num_edges{ 0 }; +}; + +class acd66_impl +{ +private: + static constexpr uint32_t max_num_vars = 11; + using STT = kitty::static_truth_table; + using LTT = kitty::static_truth_table<6>; + +public: + explicit acd66_impl( uint32_t num_vars, acd66_params const& ps, acd66_stats* pst = nullptr ) + : num_vars( num_vars ), ps( ps ), pst( pst ) + { + std::iota( permutations.begin(), permutations.end(), 0 ); + } + + /*! \brief Runs ACD 66 */ + int run( word* ptt ) + { + assert( num_vars > 6 ); + + /* truth table is too large for the settings */ + if ( num_vars > max_num_vars || num_vars > 11 ) + { + return -1; + } + + /* convert to static TT */ + init_truth_table( ptt ); + + /* run ACD trying different bound sets and free sets */ + return find_decomposition() ? 1 : 0; + } + + int compute_decomposition() + { + if ( best_multiplicity == UINT32_MAX ) + return -1; + + compute_decomposition_impl(); + + if ( ps.verify && !verify_impl() ) + { + return 1; + } + + if ( pst ) + { + pst->num_edges = bs_support_size + best_free_set + 1 + ( best_multiplicity > 2 ? 1 : 0 ); + } + + return 0; + } + + /* contains a 1 for BS variables */ + unsigned get_profile() + { + unsigned profile = 0; + + if ( bs_support_size == UINT32_MAX ) + return -1; + + for ( uint32_t i = 0; i < bs_support_size; ++i ) + { + profile |= 1 << permutations[best_free_set + bs_support[i]]; + } + + return profile; + } + + void get_decomposition( unsigned char* decompArray ) + { + if ( bs_support_size == UINT32_MAX ) + return; + + get_decomposition_abc( decompArray ); + } + +private: + bool find_decomposition() + { + best_multiplicity = UINT32_MAX; + best_free_set = UINT32_MAX; + + /* array of functions to compute the column multiplicity */ + std::function column_multiplicity_fn[5] = { + [this]( STT const& tt ) { return column_multiplicity<1u>( tt ); }, + [this]( STT const& tt ) { return column_multiplicity<2u>( tt ); }, + [this]( STT const& tt ) { return column_multiplicity<3u>( tt ); }, + [this]( STT const& tt ) { return column_multiplicity5<4u>( tt ); }, + [this]( STT const& tt ) { return column_multiplicity5<5u>( tt ); } }; + + /* find AC decompositions with minimal multiplicity */ + for ( uint32_t i = num_vars - 6; i <= 5 && i <= ps.max_free_set_vars; ++i ) + { + auto [tt_p, perm, multiplicity] = enumerate_iset_combinations( i, column_multiplicity_fn[i - 1] ); + + /* check for feasible solution into "66" with one possible shared variable */ + if ( multiplicity <= 2 || ( multiplicity <= 4 && i < 5 ) ) + { + best_tt = tt_p; + permutations = perm; + best_multiplicity = multiplicity; + best_free_set = i; + + if ( multiplicity <= 2 || check_shared_set() ) + return true; + } + } + + best_multiplicity = UINT32_MAX; + return false; + } + + void init_truth_table( word* ptt ) + { + uint32_t const num_blocks = ( num_vars <= 6 ) ? 1 : ( 1 << ( num_vars - 6 ) ); + + for ( uint32_t i = 0; i < num_blocks; ++i ) + { + start_tt._bits[i] = ptt[i]; + } + + local_extend_to( start_tt, num_vars ); + } + + template + uint32_t column_multiplicity( STT tt ) + { + uint64_t multiplicity_set[4] = { 0u, 0u, 0u, 0u }; + uint32_t multiplicity = 0; + uint32_t const num_blocks = ( num_vars > 6 ) ? ( 1u << ( num_vars - 6 ) ) : 1; + uint64_t constexpr masks_bits[] = { 0x0, 0x3, 0xF, 0x3F }; + uint64_t constexpr masks_idx[] = { 0x0, 0x0, 0x0, 0x3 }; + + /* supports up to 64 values of free set (256 for |FS| == 3)*/ + static_assert( free_set_size <= 3 ); + + /* extract iset functions */ + auto it = std::begin( tt ); + for ( auto i = 0u; i < num_blocks; ++i ) + { + for ( auto j = 0; j < ( 64 >> free_set_size ); ++j ) + { + multiplicity_set[( *it >> 6 ) & masks_idx[free_set_size]] |= UINT64_C( 1 ) << ( *it & masks_bits[free_set_size] ); + *it >>= ( 1u << free_set_size ); + } + ++it; + } + + multiplicity = __builtin_popcountl( multiplicity_set[0] ); + + if constexpr ( free_set_size == 3 ) + { + multiplicity += __builtin_popcountl( multiplicity_set[1] ); + multiplicity += __builtin_popcountl( multiplicity_set[2] ); + multiplicity += __builtin_popcountl( multiplicity_set[3] ); + } + + return multiplicity; + } + + template + uint32_t column_multiplicity5( STT tt ) + { + uint32_t const num_blocks = ( num_vars > 6 ) ? ( 1u << ( num_vars - 6 ) ) : 1; + uint64_t constexpr masks[] = { 0x0, 0x3, 0xF, 0xFF, 0xFFFF, 0xFFFFFFFF }; + + static_assert( free_set_size == 5 || free_set_size == 4 ); + + uint32_t size = 0; + uint64_t prev = -1; + std::array multiplicity_set; + + /* extract iset functions */ + auto it = std::begin( tt ); + for ( auto i = 0u; i < num_blocks; ++i ) + { + for ( auto j = 0; j < ( 64 >> free_set_size ); ++j ) + { + uint32_t fs_fn = static_cast( *it & masks[free_set_size] ); + if ( fs_fn != prev ) + { + multiplicity_set[size++] = fs_fn; + prev = fs_fn; + } + *it >>= ( 1u << free_set_size ); + } + ++it; + } + + std::sort( multiplicity_set.begin(), multiplicity_set.begin() + size ); + + /* count unique */ + uint32_t multiplicity = 1; + for ( auto i = 1u; i < size; ++i ) + { + multiplicity += multiplicity_set[i] != multiplicity_set[i - 1] ? 1 : 0; + } + + return multiplicity; + } + + inline bool combinations_next( uint32_t k, uint32_t* pComb, uint32_t* pInvPerm, STT& tt ) + { + uint32_t i; + + for ( i = k - 1; pComb[i] == num_vars - k + i; --i ) + { + if ( i == 0 ) + return false; + } + + /* move vars */ + uint32_t var_old = pComb[i]; + uint32_t pos_new = pInvPerm[var_old + 1]; + std::swap( pInvPerm[var_old + 1], pInvPerm[var_old] ); + std::swap( pComb[i], pComb[pos_new] ); + swap_inplace_local( tt, i, pos_new ); + + for ( uint32_t j = i + 1; j < k; j++ ) + { + var_old = pComb[j]; + pos_new = pInvPerm[pComb[j - 1] + 1]; + std::swap( pInvPerm[pComb[j - 1] + 1], pInvPerm[var_old] ); + std::swap( pComb[j], pComb[pos_new] ); + swap_inplace_local( tt, j, pos_new ); + } + + return true; + } + + template + std::tuple, uint32_t> enumerate_iset_combinations( uint32_t free_set_size, Fn&& fn ) + { + STT tt = start_tt; + + /* TT with best cost */ + STT best_tt = tt; + uint32_t best_cost = UINT32_MAX; + + /* works up to 16 input truth tables */ + assert( num_vars <= 16 ); + + /* init combinations */ + uint32_t pComb[16], pInvPerm[16], bestPerm[16]; + for ( uint32_t i = 0; i < num_vars; ++i ) + { + pComb[i] = pInvPerm[i] = i; + } + + /* enumerate combinations */ + do + { + uint32_t cost = fn( tt ); + if ( cost < best_cost ) + { + best_tt = tt; + best_cost = cost; + for ( uint32_t i = 0; i < num_vars; ++i ) + { + bestPerm[i] = pComb[i]; + } + } + } while ( combinations_next( free_set_size, pComb, pInvPerm, tt ) ); + + std::array res_perm; + for ( uint32_t i = 0; i < num_vars; ++i ) + { + res_perm[i] = bestPerm[i]; + } + + return std::make_tuple( best_tt, res_perm, best_cost ); + } + + bool check_shared_var( STT tt, uint32_t free_set_size, uint32_t shared_var, uint32_t multiplicity_limit ) + { + uint64_t multiplicity_set[2][4] = { { 0u, 0u, 0u, 0u }, { 0u, 0u, 0u, 0u } }; + uint32_t multiplicity0 = 0, multiplicity1 = 0; + uint32_t const num_blocks = ( num_vars > 6 ) ? ( 1u << ( num_vars - 6 ) ) : 1; + uint64_t constexpr masks_bits[] = { 0x0, 0x3, 0xF, 0x3F }; + uint64_t constexpr masks_idx[] = { 0x0, 0x0, 0x0, 0x3 }; + + /* supports up to 64 values of free set (256 for |FS| == 3)*/ + assert( free_set_size <= 3 ); + + uint32_t shared_var_shift = shared_var - free_set_size; + + /* extract iset functions */ + uint64_t iteration_counter = 0; + auto it = std::begin( tt ); + for ( auto i = 0u; i < num_blocks; ++i ) + { + for ( auto j = 0; j < ( 64 >> free_set_size ); ++j ) + { + multiplicity_set[( iteration_counter >> shared_var_shift ) & 1][( *it >> 6 ) & masks_idx[free_set_size]] |= UINT64_C( 1 ) << ( *it & masks_bits[free_set_size] ); + *it >>= ( 1u << free_set_size ); + ++iteration_counter; + } + ++it; + } + + multiplicity0 = __builtin_popcountl( multiplicity_set[0][0] ); + multiplicity1 = __builtin_popcountl( multiplicity_set[1][0] ); + + if ( free_set_size == 3 ) + { + multiplicity0 += __builtin_popcountl( multiplicity_set[0][1] ); + multiplicity0 += __builtin_popcountl( multiplicity_set[0][2] ); + multiplicity0 += __builtin_popcountl( multiplicity_set[0][3] ); + + multiplicity1 += __builtin_popcountl( multiplicity_set[1][1] ); + multiplicity1 += __builtin_popcountl( multiplicity_set[1][2] ); + multiplicity1 += __builtin_popcountl( multiplicity_set[1][3] ); + } + + if ( multiplicity0 > multiplicity_limit || multiplicity1 > multiplicity_limit ) + return false; + + best_multiplicity0 = multiplicity0; + best_multiplicity1 = multiplicity1; + + return true; + } + + bool check_shared_var5( STT tt, uint32_t free_set_size, uint32_t shared_var, uint32_t multiplicity_limit ) + { + uint32_t const num_blocks = 1u << ( num_vars - 6 ); + uint64_t constexpr masks[] = { 0x0, 0x3, 0xF, 0xFF, 0xFFFF, 0xFFFFFFFF }; + + assert( free_set_size == 5 || free_set_size == 4 ); + + uint32_t size[2] = { 0, 0 }; + uint64_t prev[2] = { UINT64_MAX, UINT64_MAX }; + std::array multiplicity_set[2]; + + uint32_t shared_var_shift = shared_var - free_set_size; + + /* extract iset functions */ + uint64_t iteration_counter = 0; + auto it = std::begin( tt ); + for ( auto i = 0u; i < num_blocks; ++i ) + { + for ( auto j = 0; j < ( 64 >> free_set_size ); ++j ) + { + uint32_t fs_fn = static_cast( *it & masks[free_set_size] ); + uint32_t cofactor = ( iteration_counter >> shared_var_shift ) & 1; + if ( fs_fn != prev[cofactor] ) + { + multiplicity_set[cofactor][size[cofactor]++] = fs_fn; + prev[cofactor] = fs_fn; + } + *it >>= ( 1u << free_set_size ); + ++iteration_counter; + } + ++it; + } + + std::sort( multiplicity_set[0].begin(), multiplicity_set[0].begin() + size[0] ); + + /* count unique in 0 cofactor */ + uint32_t multiplicity = 1; + for ( auto i = 1u; i < size[0]; ++i ) + { + multiplicity += multiplicity_set[0][i] != multiplicity_set[0][i - 1] ? 1 : 0; + } + + if ( multiplicity > multiplicity_limit ) + return false; + + best_multiplicity0 = multiplicity; + + std::sort( multiplicity_set[1].begin(), multiplicity_set[1].begin() + size[1] ); + + /* count unique in 1 cofactor */ + multiplicity = 1; + for ( auto i = 1u; i < size[1]; ++i ) + { + multiplicity += multiplicity_set[1][i] != multiplicity_set[1][i - 1] ? 1 : 0; + } + + best_multiplicity1 = multiplicity; + + return multiplicity <= multiplicity_limit; + } + + bool check_shared_set() + { + /* find one shared set variable */ + for ( uint32_t i = best_free_set; i < num_vars; ++i ) + { + /* check the multiplicity of cofactors */ + if ( best_free_set < 4 ) + { + if ( check_shared_var( best_tt, best_free_set, i, 2 ) ) + { + /* move shared variable as the most significative one */ + swap_inplace_local( best_tt, i, num_vars - 1 ); + std::swap( permutations[i], permutations[num_vars - 1] ); + return true; + } + } + else + { + if ( check_shared_var5( best_tt, best_free_set, i, 2 ) ) + { + /* move shared variable as the most significative one */ + swap_inplace_local( best_tt, i, num_vars - 1 ); + std::swap( permutations[i], permutations[num_vars - 1] ); + return true; + } + } + } + + return false; + } + + void compute_decomposition_impl( bool verbose = false ) + { + bool has_shared_set = best_multiplicity > 2; + + /* construct isets involved in multiplicity */ + LTT isets0[2]; + LTT isets1[2]; + + /* construct isets */ + STT tt = best_tt; + uint32_t offset = 0; + uint32_t num_blocks = ( num_vars > 6 ) ? ( 1u << ( num_vars - 6 ) ) : 1; + uint64_t constexpr masks[] = { 0x0, 0x3, 0xF, 0xFF, 0xFFFF, 0xFFFFFFFF }; + + /* limit analysis on 0 cofactor of the shared variable */ + if ( has_shared_set ) + num_blocks >>= 1; + + auto it = std::begin( tt ); + uint64_t fs_fun[4] = { *it & masks[best_free_set], 0, 0, 0 }; + + for ( auto i = 0u; i < num_blocks; ++i ) + { + for ( auto j = 0; j < ( 64 >> best_free_set ); ++j ) + { + uint64_t val = *it & masks[best_free_set]; + + if ( val == fs_fun[0] ) + { + isets0[0]._bits |= UINT64_C( 1 ) << ( j + offset ); + } + else + { + isets0[1]._bits |= UINT64_C( 1 ) << ( j + offset ); + fs_fun[1] = val; + } + + *it >>= ( 1u << best_free_set ); + } + + offset = ( offset + ( 64 >> best_free_set ) ) % 64; + ++it; + } + + /* continue on the 1 cofactor if shared set */ + if ( has_shared_set ) + { + fs_fun[2] = *it & masks[best_free_set]; + for ( auto i = num_blocks; i < ( num_blocks << 1 ); ++i ) + { + for ( auto j = 0; j < ( 64 >> best_free_set ); ++j ) + { + uint64_t val = *it & masks[best_free_set]; + + if ( val == fs_fun[2] ) + { + isets1[0]._bits |= UINT64_C( 1 ) << ( j + offset ); + } + else + { + isets1[1]._bits |= UINT64_C( 1 ) << ( j + offset ); + fs_fun[3] = val; + } + + *it >>= ( 1u << best_free_set ); + } + + offset = ( offset + ( 64 >> best_free_set ) ) % 64; + ++it; + } + } + + /* find the support minimizing combination with shared set */ + compute_functions( isets0, isets1, fs_fun ); + + /* print functions */ + if ( verbose ) + { + LTT f; + f._bits = dec_funcs[0]; + std::cout << "BS function : "; + kitty::print_hex( f ); + std::cout << "\n"; + f._bits = dec_funcs[1]; + std::cout << "Composition function: "; + kitty::print_hex( f ); + std::cout << "\n"; + } + } + + inline void compute_functions( LTT isets0[2], LTT isets1[2], uint64_t fs_fun[4] ) + { + /* u = 2 no support minimization */ + if ( best_multiplicity < 3 ) + { + dec_funcs[0] = isets0[0]._bits; + bs_support_size = num_vars - best_free_set; + for ( uint32_t i = 0; i < num_vars - best_free_set; ++i ) + { + bs_support[i] = i; + } + compute_composition( fs_fun ); + return; + } + + /* u = 4 two possibilities */ + if ( best_multiplicity == 4 ) + { + compute_functions4( isets0, isets1, fs_fun ); + return; + } + + /* u = 3 if both sets have multiplicity 2 there are no don't cares */ + if ( best_multiplicity0 == best_multiplicity1 ) + { + compute_functions4( isets0, isets1, fs_fun ); + return; + } + + /* u = 3 one set has multiplicity 1, use don't cares */ + compute_functions3( isets0, isets1, fs_fun ); + compute_composition( fs_fun ); + } + + inline void compute_functions4( LTT isets0[2], LTT isets1[2], uint64_t fs_fun[4] ) + { + uint64_t constexpr masks[] = { 0x0, 0x3, 0xF, 0xFF, 0xFFFF, 0xFFFFFFFF, UINT64_MAX }; + LTT f = isets0[0] | isets1[1]; + LTT care; + care._bits = masks[num_vars - best_free_set]; + + /* count the number of support variables */ + uint32_t support_vars1 = 0; + for ( uint32_t i = 0; i < num_vars - best_free_set; ++i ) + { + support_vars1 += has_var6( f, care, i ) ? 1 : 0; + bs_support[i] = i; + } + + /* use a different set */ + f = isets0[0] | isets1[0]; + + uint32_t support_vars2 = 0; + for ( uint32_t i = 0; i < num_vars - best_free_set; ++i ) + { + support_vars2 += has_var6( f, care, i ) ? 1 : 0; + } + + bs_support_size = support_vars2; + if ( support_vars2 > support_vars1 ) + { + f = isets0[0] | isets1[1]; + std::swap( fs_fun[3], fs_fun[4] ); + bs_support_size = support_vars1; + } + + /* move variables */ + if ( bs_support_size < num_vars - best_free_set ) + { + support_vars1 = 0; + for ( uint32_t i = 0; i < num_vars - best_free_set; ++i ) + { + if ( !has_var6( f, care, i ) ) + { + adjust_truth_table_on_dc( f, care, i ); + continue; + } + + if ( support_vars1 < i ) + { + kitty::swap_inplace( f, support_vars1, i ); + } + + bs_support[support_vars1] = i; + ++support_vars1; + } + } + + dec_funcs[0] = f._bits; + compute_composition( fs_fun ); + } + + inline void compute_functions3( LTT isets0[2], LTT isets1[2], uint64_t fs_fun[4] ) + { + uint64_t constexpr masks[] = { 0x0, 0x3, 0xF, 0xFF, 0xFFFF, 0xFFFFFFFF, UINT64_MAX }; + LTT f = isets0[0] | isets1[0]; + LTT care; + + /* init the care set */ + if ( best_multiplicity0 == 1 ) + { + care._bits = masks[num_vars - best_free_set] & ( ~isets0[0]._bits ); + fs_fun[1] = fs_fun[0]; + } + else + { + care._bits = masks[num_vars - best_free_set] & ( ~isets1[0]._bits ); + fs_fun[3] = fs_fun[2]; + } + + /* count the number of support variables */ + uint32_t support_vars = 0; + for ( uint32_t i = 0; i < num_vars - best_free_set; ++i ) + { + if ( !has_var6( f, care, i ) ) + { + adjust_truth_table_on_dc( f, care, i ); + continue; + } + + if ( support_vars < i ) + { + kitty::swap_inplace( f, support_vars, i ); + } + + bs_support[support_vars] = i; + ++support_vars; + } + + bs_support_size = support_vars; + dec_funcs[0] = f._bits; + compute_composition( fs_fun ); + } + + void compute_composition( uint64_t fs_fun[4] ) + { + dec_funcs[1] = fs_fun[0] << ( 1 << best_free_set ); + dec_funcs[1] |= fs_fun[1]; + + if ( best_multiplicity > 2 ) + { + dec_funcs[1] |= fs_fun[2] << ( ( 2 << best_free_set ) + ( 1 << best_free_set ) ); + dec_funcs[1] |= fs_fun[3] << ( 2 << best_free_set ); + } + } + + template + void local_extend_to( TT_type& tt, uint32_t real_num_vars ) + { + if ( real_num_vars < 6 ) + { + auto mask = *tt.begin(); + + for ( auto i = real_num_vars; i < num_vars; ++i ) + { + mask |= ( mask << ( 1 << i ) ); + } + + std::fill( tt.begin(), tt.end(), mask ); + } + else + { + uint32_t num_blocks = ( 1u << ( real_num_vars - 6 ) ); + auto it = tt.begin(); + while ( it != tt.end() ) + { + it = std::copy( tt.cbegin(), tt.cbegin() + num_blocks, it ); + } + } + } + + void swap_inplace_local( STT& tt, uint8_t var_index1, uint8_t var_index2 ) + { + if ( var_index1 == var_index2 ) + { + return; + } + + if ( var_index1 > var_index2 ) + { + std::swap( var_index1, var_index2 ); + } + + assert( num_vars > 6 ); + const uint32_t num_blocks = 1 << ( num_vars - 6 ); + + if ( var_index2 <= 5 ) + { + const auto& pmask = kitty::detail::ppermutation_masks[var_index1][var_index2]; + const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 ); + std::transform( std::begin( tt._bits ), std::begin( tt._bits ) + num_blocks, std::begin( tt._bits ), + [shift, &pmask]( uint64_t word ) { + return ( word & pmask[0] ) | ( ( word & pmask[1] ) << shift ) | ( ( word & pmask[2] ) >> shift ); + } ); + } + else if ( var_index1 <= 5 ) /* in this case, var_index2 > 5 */ + { + const auto step = 1 << ( var_index2 - 6 ); + const auto shift = 1 << var_index1; + auto it = std::begin( tt._bits ); + while ( it != std::begin( tt._bits ) + num_blocks ) + { + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) + { + const auto low_to_high = ( *( it + i ) & kitty::detail::projections[var_index1] ) >> shift; + const auto high_to_low = ( *( it + i + step ) << shift ) & kitty::detail::projections[var_index1]; + *( it + i ) = ( *( it + i ) & ~kitty::detail::projections[var_index1] ) | high_to_low; + *( it + i + step ) = ( *( it + i + step ) & kitty::detail::projections[var_index1] ) | low_to_high; + } + it += 2 * step; + } + } + else + { + const auto step1 = 1 << ( var_index1 - 6 ); + const auto step2 = 1 << ( var_index2 - 6 ); + auto it = std::begin( tt._bits ); + while ( it != std::begin( tt._bits ) + num_blocks ) + { + for ( auto i = 0; i < step2; i += 2 * step1 ) + { + for ( auto j = 0; j < step1; ++j ) + { + std::swap( *( it + i + j + step1 ), *( it + i + j + step2 ) ); + } + } + it += 2 * step2; + } + } + } + + inline bool has_var6( const LTT& tt, const LTT& care, uint8_t var_index ) + { + if ( ( ( ( tt._bits >> ( uint64_t( 1 ) << var_index ) ) ^ tt._bits ) & kitty::detail::projections_neg[var_index] & ( care._bits >> ( uint64_t( 1 ) << var_index ) ) & care._bits ) != 0 ) + { + return true; + } + + return false; + } + + bool has_var_support( const STT& tt, const STT& care, uint32_t real_num_vars, uint8_t var_index ) + { + assert( var_index < real_num_vars ); + assert( real_num_vars <= tt.num_vars() ); + assert( tt.num_vars() == care.num_vars() ); + + const uint32_t num_blocks = real_num_vars <= 6 ? 1 : ( 1 << ( real_num_vars - 6 ) ); + if ( real_num_vars <= 6 || var_index < 6 ) + { + auto it_tt = std::begin( tt._bits ); + auto it_care = std::begin( care._bits ); + while ( it_tt != std::begin( tt._bits ) + num_blocks ) + { + if ( ( ( ( *it_tt >> ( uint64_t( 1 ) << var_index ) ) ^ *it_tt ) & kitty::detail::projections_neg[var_index] & ( *it_care >> ( uint64_t( 1 ) << var_index ) ) & *it_care ) != 0 ) + { + return true; + } + ++it_tt; + ++it_care; + } + + return false; + } + + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < num_blocks; i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + if ( ( ( tt._bits[i + j] ^ tt._bits[i + j + step] ) & care._bits[i + j] & care._bits[i + j + step] ) != 0 ) + { + return true; + } + } + } + + return false; + } + + template + bool has_var_support( const TT_type& tt, const TT_type& care, uint32_t real_num_vars, uint8_t var_index ) + { + assert( var_index < real_num_vars ); + assert( real_num_vars <= tt.num_vars() ); + assert( tt.num_vars() == care.num_vars() ); + + const uint32_t num_blocks = real_num_vars <= 6 ? 1 : ( 1 << ( real_num_vars - 6 ) ); + if ( real_num_vars <= 6 || var_index < 6 ) + { + auto it_tt = std::begin( tt._bits ); + auto it_care = std::begin( care._bits ); + while ( it_tt != std::begin( tt._bits ) + num_blocks ) + { + if ( ( ( ( *it_tt >> ( uint64_t( 1 ) << var_index ) ) ^ *it_tt ) & kitty::detail::projections_neg[var_index] & ( *it_care >> ( uint64_t( 1 ) << var_index ) ) & *it_care ) != 0 ) + { + return true; + } + ++it_tt; + ++it_care; + } + + return false; + } + + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < num_blocks; i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + if ( ( ( tt._bits[i + j] ^ tt._bits[i + j + step] ) & care._bits[i + j] & care._bits[i + j + step] ) != 0 ) + { + return true; + } + } + } + + return false; + } + + void adjust_truth_table_on_dc( LTT& tt, LTT& care, uint32_t var_index ) + { + uint64_t new_bits = tt._bits & care._bits; + tt._bits = ( ( new_bits | ( new_bits >> ( uint64_t( 1 ) << var_index ) ) ) & kitty::detail::projections_neg[var_index] ) | + ( ( new_bits | ( new_bits << ( uint64_t( 1 ) << var_index ) ) ) & kitty::detail::projections[var_index] ); + care._bits = care._bits | ( care._bits >> ( uint64_t( 1 ) << var_index ) ); + } + + /* Decomposition format for ABC + * + * The record is an array of unsigned chars where: + * - the first unsigned char entry stores the number of unsigned chars in the record + * - the second entry stores the number of LUTs + * After this, several sub-records follow, each representing one LUT as follows: + * - an unsigned char entry listing the number of fanins + * - a list of fanins, from the LSB to the MSB of the truth table. The N inputs of the original function + * have indexes from 0 to N-1, followed by the internal signals in a topological order + * - the LUT truth table occupying 2^(M-3) bytes, where M is the fanin count of the LUT, from the LSB to the MSB. + * A 2-input LUT, which takes 4 bits, should be stretched to occupy 8 bits (one unsigned char) + * A 0- or 1-input LUT can be represented similarly but it is not expected that such LUTs will be represented + */ + void get_decomposition_abc( unsigned char* decompArray ) + { + unsigned char* pArray = decompArray; + unsigned char bytes = 2; + + /* write number of LUTs */ + pArray++; + *pArray = 2; + pArray++; + + /* write BS LUT */ + /* write fanin size */ + *pArray = bs_support_size; + pArray++; + ++bytes; + + /* write support */ + for ( uint32_t i = 0; i < bs_support_size; ++i ) + { + *pArray = (unsigned char)permutations[bs_support[i] + best_free_set]; + pArray++; + ++bytes; + } + + /* write truth table */ + uint32_t tt_num_bytes = ( bs_support_size <= 3 ) ? 1 : ( 1 << ( bs_support_size - 3 ) ); + for ( uint32_t i = 0; i < tt_num_bytes; ++i ) + { + *pArray = (unsigned char)( ( dec_funcs[0] >> ( 8 * i ) ) & 0xFF ); + pArray++; + ++bytes; + } + + /* write top LUT */ + /* write fanin size */ + uint32_t support_size = best_free_set + 1 + ( best_multiplicity > 2 ? 1 : 0 ); + *pArray = support_size; + pArray++; + ++bytes; + + /* write support */ + for ( uint32_t i = best_free_set; i < best_free_set; ++i ) + { + *pArray = (unsigned char)permutations[i]; + pArray++; + ++bytes; + } + + *pArray = (unsigned char)num_vars; + pArray++; + ++bytes; + + if ( best_multiplicity > 2 ) + { + *pArray = (unsigned char)permutations[num_vars - 1]; + pArray++; + ++bytes; + } + + /* write truth table */ + tt_num_bytes = ( support_size <= 3 ) ? 1 : ( 1 << ( support_size - 3 ) ); + for ( uint32_t i = 0; i < tt_num_bytes; ++i ) + { + *pArray = (unsigned char)( ( dec_funcs[1] >> ( 8 * i ) ) & 0xFF ); + pArray++; + ++bytes; + } + + /* write numBytes */ + *decompArray = bytes; + } + + bool verify_impl() + { + /* create PIs */ + STT pis[max_num_vars]; + for ( uint32_t i = 0; i < num_vars; ++i ) + { + kitty::create_nth_var( pis[i], permutations[i] ); + } + + /* BS function patterns */ + STT bsi[6]; + for ( uint32_t i = 0; i < bs_support_size; ++i ) + { + bsi[i] = pis[best_free_set + bs_support[i]]; + } + + /* compute first function */ + STT bsf_sim; + for ( uint32_t i = 0u; i < ( 1 << num_vars ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0; j < bs_support_size; ++j ) + { + pattern |= get_bit( bsi[j], i ) << j; + } + if ( ( dec_funcs[0] >> pattern ) & 1 ) + { + set_bit( bsf_sim, i ); + } + } + + /* compute first function */ + STT top_sim; + for ( uint32_t i = 0u; i < ( 1 << num_vars ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0; j < best_free_set; ++j ) + { + pattern |= get_bit( pis[j], i ) << j; + } + pattern |= get_bit( bsf_sim, i ) << best_free_set; + if ( best_multiplicity > 2 ) + { + pattern |= get_bit( pis[num_vars - 1], i ) << ( best_free_set + 1 ); + } + + if ( ( dec_funcs[1] >> pattern ) & 1 ) + { + set_bit( top_sim, i ); + } + } + + for ( uint32_t i = 0; i < ( 1 << ( num_vars - 6 ) ); ++i ) + { + if ( top_sim._bits[i] != start_tt._bits[i] ) + { + /* convert to dynamic_truth_table */ + // report_tt( bsf_sim ); + std::cout << "Found incorrect decomposition\n"; + report_tt( top_sim ); + std::cout << " instead_of\n"; + report_tt( start_tt ); + return false; + } + } + + return true; + } + + uint32_t get_bit( const STT& tt, uint64_t index ) + { + return ( tt._bits[index >> 6] >> ( index & 0x3f ) ) & 0x1; + } + + void set_bit( STT& tt, uint64_t index ) + { + tt._bits[index >> 6] |= uint64_t( 1 ) << ( index & 0x3f ); + } + + void report_tt( STT const& stt ) + { + kitty::dynamic_truth_table tt( num_vars ); + + std::copy( std::begin( stt._bits ), std::begin( stt._bits ) + ( 1 << ( num_vars - 6 ) ), std::begin( tt ) ); + kitty::print_hex( tt ); + std::cout << "\n"; + } + +private: + uint32_t best_multiplicity{ UINT32_MAX }; + uint32_t best_free_set{ UINT32_MAX }; + uint32_t best_multiplicity0{ UINT32_MAX }; + uint32_t best_multiplicity1{ UINT32_MAX }; + uint32_t bs_support_size{ UINT32_MAX }; + STT best_tt; + STT start_tt; + uint64_t dec_funcs[2]; + uint32_t bs_support[6]; + + uint32_t num_vars; + acd66_params const& ps; + acd66_stats* pst; + std::array permutations; +}; + +} // namespace acd + +ABC_NAMESPACE_CXX_HEADER_END + +#endif // _ACD66_H_ \ No newline at end of file diff --git a/src/map/if/acd/kitty_operations.hpp b/src/map/if/acd/kitty_operations.hpp index bf8e38007..48a4b7c67 100644 --- a/src/map/if/acd/kitty_operations.hpp +++ b/src/map/if/acd/kitty_operations.hpp @@ -133,6 +133,24 @@ void swap_inplace( TT& tt, uint8_t var_index1, uint8_t var_index2 ) } } +template +inline void swap_inplace( static_truth_table& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + if ( var_index1 == var_index2 ) + { + return; + } + + if ( var_index1 > var_index2 ) + { + std::swap( var_index1, var_index2 ); + } + + const auto& pmask = detail::ppermutation_masks[var_index1][var_index2]; + const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 ); + tt._bits = ( tt._bits & pmask[0] ) | ( ( tt._bits & pmask[1] ) << shift ) | ( ( tt._bits & pmask[2] ) >> shift ); +} + /*! \brief Extends smaller truth table to larger one The most significant variables will not be in the functional support of the diff --git a/src/map/if/acd/kitty_static_tt.hpp b/src/map/if/acd/kitty_static_tt.hpp index ab5a5d1c9..2b1613a6a 100644 --- a/src/map/if/acd/kitty_static_tt.hpp +++ b/src/map/if/acd/kitty_static_tt.hpp @@ -12,8 +12,116 @@ ABC_NAMESPACE_CXX_HEADER_START namespace kitty { +template +struct static_truth_table; + +/*! Truth table (for up to 6 variables) in which number of variables is known at compile time. + */ template -struct static_truth_table +struct static_truth_table +{ + /*! \cond PRIVATE */ + enum + { + NumBits = uint64_t( 1 ) << NumVars + }; + /*! \endcond */ + + /*! Constructs a new static truth table instance with the same number of variables. */ + inline static_truth_table construct() const + { + return static_truth_table(); + } + + /*! Returns number of variables. + */ + inline auto num_vars() const noexcept { return NumVars; } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return 1u; } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return NumBits; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() noexcept { return &_bits; } + + /*! \brief End iterator to bits. + */ + inline auto end() noexcept { return ( &_bits ) + 1; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() const noexcept { return &_bits; } + + /*! \brief End iterator to bits. + */ + inline auto end() const noexcept { return ( &_bits ) + 1; } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin() noexcept { return &_bits; } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend() noexcept { return ( &_bits ) + 1; } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin() const noexcept { return &_bits; } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend() const noexcept { return ( &_bits ) + 1; } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin() const noexcept { return &_bits; } + + /*! \brief Constant everse end iterator to bits. + */ + inline auto crend() const noexcept { return ( &_bits ) + 1; } + + /*! \brief Assign other truth table if number of variables match. + + This replaces the current truth table with another truth table, if `other` + has the same number of variables. Otherwise, the truth table is not + changed. + + \param other Other truth table + */ + template + static_truth_table& operator=( const TT& other ) + { + if ( other.num_vars() == num_vars() ) + { + std::copy( other.begin(), other.end(), begin() ); + } + + return *this; + } + + /*! Masks the number of valid truth table bits. + + If the truth table has less than 6 variables, it may not use all + the bits. This operation makes sure to zero out all non-valid + bits. + */ + inline void mask_bits() noexcept { _bits &= detail::masks[NumVars]; } + + /*! \cond PRIVATE */ +public: /* fields */ + uint64_t _bits = 0; + /*! \endcond */ +}; + +/*! Truth table (more than 6 variables) in which number of variables is known at compile time. + */ +template +struct static_truth_table { /*! \cond PRIVATE */ enum diff --git a/src/map/if/if.h b/src/map/if/if.h index f8c99fdf1..33621ac92 100644 --- a/src/map/if/if.h +++ b/src/map/if/if.h @@ -151,6 +151,7 @@ struct If_Par_t_ int fVerbose; // the verbosity flag int fVerboseTrace; // the verbosity flag char * pLutStruct; // LUT structure + int fEnableStructN;// LUT structure using a new method float WireDelay; // wire delay // internal parameters int fSkipCutFilter;// skip cut filter @@ -551,6 +552,7 @@ extern int If_CutPerformCheck07( If_Man_t * p, unsigned * pTruth, in extern int If_CutPerformCheck08( If_Man_t * p, unsigned * pTruth, int nVars, int nLeaves, char * pStr ); extern int If_CutPerformCheck10( If_Man_t * p, unsigned * pTruth, int nVars, int nLeaves, char * pStr ); extern int If_CutPerformCheck16( If_Man_t * p, unsigned * pTruth, int nVars, int nLeaves, char * pStr ); +extern int If_CutPerformCheck66( If_Man_t * p, unsigned * pTruth, int nVars, int nLeaves, char * pStr ); extern int If_CutPerformCheck45( If_Man_t * p, unsigned * pTruth, int nVars, int nLeaves, char * pStr ); extern int If_CutPerformCheck54( If_Man_t * p, unsigned * pTruth, int nVars, int nLeaves, char * pStr ); extern int If_CutPerformCheck75( If_Man_t * p, unsigned * pTruth, int nVars, int nLeaves, char * pStr ); diff --git a/src/map/if/ifDec66.c b/src/map/if/ifDec66.c new file mode 100644 index 000000000..932f5feb2 --- /dev/null +++ b/src/map/if/ifDec66.c @@ -0,0 +1,90 @@ +/**CFile**************************************************************** + + FileName [ifDec16.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [FPGA mapping based on priority cuts.] + + Synopsis [Fast checking procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - November 21, 2006.] + + Revision [$Id: ifDec16.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "if.h" +#include "bool/kit/kit.h" +#include "misc/vec/vecMem.h" + +ABC_NAMESPACE_IMPL_START + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Performs ACD into 66 cascade.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int If_CutPerformCheck66( If_Man_t * p, unsigned * pTruth0, int nVars, int nLeaves, char * pStr ) +{ + unsigned pTruth[IF_MAX_FUNC_LUTSIZE > 5 ? 1 << (IF_MAX_FUNC_LUTSIZE - 5) : 1]; + int i, Length; + // stretch the truth table + assert( nVars >= 6 ); + memcpy( pTruth, pTruth0, sizeof(word) * Abc_TtWordNum(nVars) ); + Abc_TtStretch6( (word *)pTruth, nLeaves, p->pPars->nLutSize ); + + // if cutmin is disabled, minimize the function + if ( !p->pPars->fCutMin ) + nLeaves = Abc_TtMinBase( (word *)pTruth, NULL, nLeaves, nVars ); + + // quit if parameters are wrong + Length = strlen(pStr); + if ( Length != 2 ) + { + printf( "Wrong LUT struct (%s)\n", pStr ); + return 0; + } + for ( i = 0; i < Length; i++ ) + { + if ( pStr[i] != '6' ) + { + printf( "The LUT size (%d) should belong to {6}.\n", pStr[i] - '0' ); + return 0; + } + } + + if ( nLeaves > 11 ) + { + printf( "The cut size (%d) is too large for the LUT structure %s.\n", nLeaves, pStr ); + return 0; + } + + // consider easy case + if ( nLeaves <= 6 ) + return 1; + + // derive the decomposition + return (int)(acd66_evaluate( (word *)pTruth, nVars, 1 ) > 0 ); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + +ABC_NAMESPACE_IMPL_END \ No newline at end of file diff --git a/src/map/if/module.make b/src/map/if/module.make index 6651d465b..bd652f35b 100644 --- a/src/map/if/module.make +++ b/src/map/if/module.make @@ -7,6 +7,7 @@ SRC += src/map/if/ifCom.c \ src/map/if/ifDec08.c \ src/map/if/ifDec10.c \ src/map/if/ifDec16.c \ + src/map/if/ifDec66.c \ src/map/if/ifDec75.c \ src/map/if/ifDelay.c \ src/map/if/ifDsd.c \