Page MenuHomePhorge

emap.hpp
No OneTemporary

Size
179 KB
Referenced Files
None
Subscribers
None

emap.hpp

/* mockturtle: C++ logic network library
* Copyright (C) 2018-2024 EPFL
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/*!
\file emap.hpp
\brief An extended technology mapper
\author Alessandro Tempia Calvino
*/
#pragma once
#include <chrono>
#include <cstdint>
#include <limits>
#include <string>
#include <unordered_map>
#include <vector>
#include <kitty/constructors.hpp>
#include <kitty/dynamic_truth_table.hpp>
#include <kitty/hash.hpp>
#include <kitty/static_truth_table.hpp>
#include <fmt/format.h>
#include <parallel_hashmap/phmap.h>
#include "../networks/aig.hpp"
#include "../networks/block.hpp"
#include "../networks/klut.hpp"
#include "../utils/cuts.hpp"
#include "../utils/node_map.hpp"
#include "../utils/stopwatch.hpp"
#include "../utils/tech_library.hpp"
#include "../views/binding_view.hpp"
#include "../views/cell_view.hpp"
#include "../views/choice_view.hpp"
#include "../views/topo_view.hpp"
#include "cleanup.hpp"
#include "cut_enumeration.hpp"
#include "detail/mffc_utils.hpp"
#include "detail/switching_activity.hpp"
namespace mockturtle
{
/*! \brief Parameters for emap.
*
* The data structure `emap_params` holds configurable parameters
* with default arguments for `emap`.
*/
struct emap_params
{
emap_params()
{
cut_enumeration_ps.cut_limit = 16;
cut_enumeration_ps.minimize_truth_table = true;
}
/*! \brief Parameters for cut enumeration
*
* The default cut limit is 16.
* The maximum cut limit is 19.
* By default, truth table minimization
* is performed.
*/
cut_enumeration_params cut_enumeration_ps{};
/*! \brief Do area-oriented mapping. */
bool area_oriented_mapping{ false };
/*! \brief Maps using multi-output gates */
bool map_multioutput{ false };
/*! \brief Matching mode
*
* Boolean uses Boolean matching (up to 6-input cells),
* Structural uses pattern matching for fully-DSD cells,
* Hybrid combines the two.
*/
enum matching_mode_t
{
boolean,
structural,
hybrid
} matching_mode = hybrid;
/*! \brief Target required time (for each PO). */
double required_time{ 0.0f };
/*! \brief Required time relaxation in percentage (10 = 10%). */
double relax_required{ 0.0f };
/*! \brief Custom input arrival times. */
std::vector<double> arrival_times{};
/*! \brief Custom output required times. */
std::vector<double> required_times{};
/*! \brief Number of rounds for area flow optimization. */
uint32_t area_flow_rounds{ 3u };
/*! \brief Number of rounds for exact area optimization. */
uint32_t ela_rounds{ 2u };
/*! \brief Number of rounds for exact switching power optimization. */
uint32_t eswp_rounds{ 0u };
/*! \brief Number of patterns for switching activity computation. */
uint32_t switching_activity_patterns{ 2048u };
/*! \brief Compute area-oriented alternative matches */
bool use_match_alternatives{ true };
/*! \brief Remove the cuts that are contained in others */
bool remove_dominated_cuts{ false };
/*! \brief Remove overlapping multi-output cuts */
bool remove_overlapping_multicuts{ false };
/*! \brief Be verbose. */
bool verbose{ false };
};
/*! \brief Statistics for emap.
*
* The data structure `emap_stats` provides data collected by running
* `emap`.
*/
struct emap_stats
{
/*! \brief Area result. */
double area{ 0 };
/*! \brief Worst delay result. */
double delay{ 0 };
/*! \brief Power result. */
double power{ 0 };
/*! \brief Power result. */
uint32_t inverters{ 0 };
/*! \brief Mapped multi-output gates. */
uint32_t multioutput_gates{ 0 };
/*! \brief Runtime for multi-output matching. */
stopwatch<>::duration time_multioutput{ 0 };
/*! \brief Total runtime. */
stopwatch<>::duration time_total{ 0 };
/*! \brief Cut enumeration stats. */
cut_enumeration_stats cut_enumeration_st{};
/*! \brief Delay and area stats for each round. */
std::vector<std::string> round_stats{};
/*! \brief Mapping error. */
bool mapping_error{ false };
void report() const
{
for ( auto const& stat : round_stats )
{
std::cout << stat;
}
std::cout << fmt::format( "[i] Area = {:>5.2f}; Delay = {:>5.2f};", area, delay );
if ( power != 0 )
std::cout << fmt::format( " Power = {:>5.2f};\n", power );
else
std::cout << "\n";
if ( multioutput_gates )
{
std::cout << fmt::format( "[i] Multi-output gates = {:>5}\n", multioutput_gates );
std::cout << fmt::format( "[i] Multi-output runtime = {:>5.2f} secs\n", to_seconds( time_multioutput ) );
}
std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) );
}
};
namespace detail
{
#pragma region cut set
template<unsigned NInputs>
struct cut_enumeration_emap_cut
{
/* stats */
uint32_t delay;
float flow;
bool ignore;
/* pattern index for structural matching*/
uint32_t pattern_index;
/* function */
kitty::static_truth_table<6> function;
/* list of supergates matching the cut for positive and negative output phases */
std::array<std::vector<supergate<NInputs>> const*, 2> supergates;
/* input negations, 0: pos, 1: neg */
std::array<uint16_t, 2> negations;
};
struct cut_enumeration_emap_multi_cut
{
/* stats */
uint64_t id{ 0 };
};
enum class emap_cut_sort_type
{
DELAY = 0,
DELAY2 = 1,
AREA = 2,
AREA2 = 3,
NONE = 4
};
template<typename CutType, uint32_t MaxCuts>
class emap_cut_set
{
public:
/*! \brief Standard constructor.
*/
emap_cut_set()
{
clear();
}
/*! \brief Assignment operator.
*/
emap_cut_set& operator=( emap_cut_set const& other )
{
if ( this != &other )
{
_pcend = _pend = _pcuts.begin();
_set_limit = other._set_limit;
auto it = other.begin();
while ( it != other.end() )
{
**_pend++ = **it++;
++_pcend;
}
}
return *this;
}
/*! \brief Clears a cut set.
*/
void clear()
{
_pcend = _pend = _pcuts.begin();
auto pit = _pcuts.begin();
for ( auto& c : _cuts )
{
*pit++ = &c;
}
}
/*! \brief Sets the cut limit.
*/
void set_cut_limit( uint32_t limit )
{
_set_limit = std::min( MaxCuts, limit );
}
/*! \brief Adds a cut to the end of the set.
*
* This function should only be called to create a set of cuts which is known
* to be sorted and irredundant (i.e., no cut in the set dominates another
* cut).
*
* \param begin Begin iterator to leaf indexes
* \param end End iterator (exclusive) to leaf indexes
* \return Reference to the added cut
*/
template<typename Iterator>
CutType& add_cut( Iterator begin, Iterator end )
{
assert( _pend != _pcuts.end() );
auto& cut = **_pend++;
cut.set_leaves( begin, end );
++_pcend;
return cut;
}
/*! \brief Appends a cut to the end of the set.
*
* This function should only be called to create a set of cuts which is known
* to be sorted and irredundant (i.e., no cut in the set dominates another
* cut).
*
* \param cut Cut to insert
*/
void append_cut( CutType const& cut )
{
assert( _pend != _pcuts.end() );
**_pend++ = cut;
++_pcend;
}
/*! \brief Checks whether cut is dominates by any cut in the set.
*
* \param cut Cut outside of the set
*/
bool is_dominated( CutType const& cut ) const
{
return std::find_if( _pcuts.begin(), _pcend, [&cut]( auto const* other ) { return other->dominates( cut ); } ) != _pcend;
}
static bool sort_delay( CutType const& c1, CutType const& c2 )
{
constexpr auto eps{ 0.005f };
if ( !c1->ignore && c2->ignore )
return true;
if ( c1->ignore && !c2->ignore )
return false;
if ( c1->delay < c2->delay - eps )
return true;
if ( c1->delay > c2->delay + eps )
return false;
if ( c1->flow < c2->flow - eps )
return true;
if ( c1->flow > c2->flow + eps )
return false;
return c1.size() < c2.size();
}
static bool sort_delay2( CutType const& c1, CutType const& c2 )
{
constexpr auto eps{ 0.005f };
if ( !c1->ignore && c2->ignore )
return true;
if ( c1->ignore && !c2->ignore )
return false;
if ( c1.size() < c2.size() )
return true;
if ( c1.size() > c2.size() )
return false;
if ( c1->delay < c2->delay - eps )
return true;
if ( c1->delay > c2->delay + eps )
return false;
return c1->flow < c2->flow - eps;
}
static bool sort_area( CutType const& c1, CutType const& c2 )
{
constexpr auto eps{ 0.005f };
if ( !c1->ignore && c2->ignore )
return true;
if ( c1->ignore && !c2->ignore )
return false;
if ( c1->flow < c2->flow - eps )
return true;
if ( c1->flow > c2->flow + eps )
return false;
if ( c1.size() < c2.size() )
return true;
if ( c1.size() > c2.size() )
return false;
return c1->delay < c2->delay - eps;
}
static bool sort_area2( CutType const& c1, CutType const& c2 )
{
constexpr auto eps{ 0.005f };
if ( !c1->ignore && c2->ignore )
return true;
if ( c1->ignore && !c2->ignore )
return false;
if ( c1->flow < c2->flow - eps )
return true;
if ( c1->flow > c2->flow + eps )
return false;
if ( c1->delay < c2->delay - eps )
return true;
if ( c1->delay > c2->delay + eps )
return false;
return c1.size() < c2.size();
}
/*! \brief Compare two cuts using sorting functions.
*
* This method compares two cuts using a sorting function.
*
* \param cut1 first cut.
* \param cut2 second cut.
* \param sort sorting function.
*/
static bool compare( CutType const& cut1, CutType const& cut2, emap_cut_sort_type sort = emap_cut_sort_type::NONE )
{
if ( sort == emap_cut_sort_type::DELAY )
{
return sort_delay( cut1, cut2 );
}
else if ( sort == emap_cut_sort_type::DELAY2 )
{
return sort_delay2( cut1, cut2 );
}
else if ( sort == emap_cut_sort_type::AREA )
{
return sort_area( cut1, cut2 );
}
else if ( sort == emap_cut_sort_type::AREA2 )
{
return sort_area2( cut1, cut2 );
}
else
{
return false;
}
}
/*! \brief Inserts a cut into a set without checking dominance.
*
* This method will insert a cut into a set and maintain an order. This
* method doesn't remove the cuts that are dominated by `cut`.
*
* If `cut` is dominated by any of the cuts in the set, it will still be
* inserted. The caller is responsible to check whether `cut` is dominated
* before inserting it into the set.
*
* \param cut Cut to insert.
* \param sort Cut prioritization function.
*/
void simple_insert( CutType const& cut, emap_cut_sort_type sort = emap_cut_sort_type::NONE )
{
/* insert cut in a sorted way */
typename std::array<CutType*, MaxCuts>::iterator ipos = _pcuts.begin();
bool limit_reached = std::distance( _pcuts.begin(), _pend ) >= _set_limit;
/* do not insert if worst than set_limit */
if ( limit_reached )
{
if ( sort == emap_cut_sort_type::AREA && !sort_area( cut, **( ipos + _set_limit - 1 ) ) )
{
return;
}
else if ( sort != emap_cut_sort_type::AREA )
{
return;
}
}
if ( sort == emap_cut_sort_type::NONE )
{
ipos = _pend;
}
else /* AREA */
{
ipos = std::upper_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_area( *a, *b ); } );
}
/* check for redundant cut */
typename std::array<CutType*, MaxCuts>::iterator jpos = ipos;
if ( cut->ignore )
{
while ( jpos != _pcuts.begin() )
{
--jpos;
if ( ( *jpos )->size() < cut.size() )
break;
if ( ( *jpos )->signature() == cut.signature() && std::equal( cut.begin(), cut.end(), ( *jpos )->begin() ) )
return;
}
}
else if ( ipos != _pcuts.begin() )
{
if ( ( *( ipos - 1 ) )->signature() == cut.signature() && std::equal( cut.begin(), cut.end(), ( *( ipos - 1 ) )->begin() ) )
{
return;
}
}
/* too many cuts, we need to remove one */
if ( _pend == _pcuts.end() || limit_reached )
{
/* cut to be inserted is worse than all the others, return */
if ( ipos == _pend )
{
return;
}
else
{
/* remove last cut */
--_pend;
--_pcend;
}
}
/* copy cut */
auto& icut = *_pend;
icut->set_leaves( cut.begin(), cut.end() );
icut->data() = cut.data();
if ( ipos != _pend )
{
auto it = _pend;
while ( it > ipos )
{
std::swap( *it, *( it - 1 ) );
--it;
}
}
/* update iterators */
_pcend++;
_pend++;
}
/*! \brief Inserts a cut into a set.
*
* This method will insert a cut into a set and maintain an order. Before the
* cut is inserted into the correct position, it will remove all cuts that are
* dominated by `cut`. Variable `skip0` tell to skip the dominance check on
* cut zero.
*
* If `cut` is dominated by any of the cuts in the set, it will still be
* inserted. The caller is responsible to check whether `cut` is dominated
* before inserting it into the set.
*
* \param cut Cut to insert.
* \param skip0 Skip dominance check on cut zero.
* \param sort Cut prioritization function.
*/
void insert( CutType const& cut, bool skip0 = false, emap_cut_sort_type sort = emap_cut_sort_type::NONE )
{
auto begin = _pcuts.begin();
if ( skip0 && _pend != _pcuts.begin() )
++begin;
/* remove elements that are dominated by new cut */
_pcend = _pend = std::stable_partition( begin, _pend, [&cut]( auto const* other ) { return !cut.dominates( *other ); } );
/* insert cut in a sorted way */
simple_insert( cut, sort );
}
/*! \brief Replaces a cut of the set.
*
* This method replaces the cut at position `index` in the set by `cut`
* and maintains the cuts order. The function does not check whether
* index is in the valid range.
*
* \param index Index of the cut to replace.
* \param cut Cut to insert.
*/
void replace( uint32_t index, CutType const& cut )
{
*_pcuts[index] = cut;
}
/*! \brief Begin iterator (constant).
*
* The iterator will point to a cut pointer.
*/
auto begin() const { return _pcuts.begin(); }
/*! \brief End iterator (constant). */
auto end() const { return _pcend; }
/*! \brief Begin iterator (mutable).
*
* The iterator will point to a cut pointer.
*/
auto begin() { return _pcuts.begin(); }
/*! \brief End iterator (mutable). */
auto end() { return _pend; }
/*! \brief Number of cuts in the set. */
auto size() const { return _pcend - _pcuts.begin(); }
/*! \brief Returns reference to cut at index.
*
* This function does not return the cut pointer but dereferences it and
* returns a reference. The function does not check whether index is in the
* valid range.
*
* \param index Index
*/
auto const& operator[]( uint32_t index ) const { return *_pcuts[index]; }
/*! \brief Returns the best cut, i.e., the first cut.
*/
auto const& best() const { return *_pcuts[0]; }
/*! \brief Updates the best cut.
*
* This method will set the cut at index `index` to be the best cut. All
* cuts before `index` will be moved one position higher.
*
* \param index Index of new best cut
*/
void update_best( uint32_t index )
{
auto* best = _pcuts[index];
for ( auto i = index; i > 0; --i )
{
_pcuts[i] = _pcuts[i - 1];
}
_pcuts[0] = best;
}
/*! \brief Resize the cut set, if it is too large.
*
* This method will resize the cut set to `size` only if the cut set has more
* than `size` elements. Otherwise, the size will remain the same.
*/
void limit( uint32_t size )
{
if ( std::distance( _pcuts.begin(), _pend ) > static_cast<long>( size ) )
{
_pcend = _pend = _pcuts.begin() + size;
}
}
/*! \brief Prints a cut set. */
friend std::ostream& operator<<( std::ostream& os, emap_cut_set const& set )
{
for ( auto const& c : set )
{
os << *c << "\n";
}
return os;
}
/*! \brief Returns if the cut set contains already `cut`. */
bool is_contained( CutType const& cut )
{
typename std::array<CutType*, MaxCuts>::iterator ipos = _pcuts.begin();
while ( ipos != _pend )
{
if ( ( *ipos )->signature() == cut.signature() && std::equal( cut.begin(), cut.end(), ( *ipos )->begin() ) )
return true;
++ipos;
}
return false;
}
private:
std::array<CutType, MaxCuts> _cuts;
std::array<CutType*, MaxCuts> _pcuts;
typename std::array<CutType*, MaxCuts>::const_iterator _pcend{ _pcuts.begin() };
typename std::array<CutType*, MaxCuts>::iterator _pend{ _pcuts.begin() };
uint32_t _set_limit{ MaxCuts };
};
#pragma endregion
#pragma region Hashing
template<uint32_t max_multioutput_cut_size>
struct emap_triple_hash
{
inline uint64_t operator()( const std::array<uint32_t, max_multioutput_cut_size>& p ) const
{
uint64_t seed = hash_block( p[0] );
for ( uint32_t i = 1; i < max_multioutput_cut_size; ++i )
{
hash_combine( seed, hash_block( p[i] ) );
}
return seed;
}
};
#pragma endregion
template<unsigned NInputs>
struct best_gate_emap
{
supergate<NInputs> const* gate;
double arrival;
float area;
float flow;
unsigned phase : 16;
unsigned cut : 12;
unsigned size : 4;
};
template<unsigned NInputs>
struct node_match_emap
{
/* best gate match for positive and negative output phases */
supergate<NInputs> const* best_gate[2];
/* alternative best gate for positibe and negative output phase */
best_gate_emap<NInputs> best_alternative[2];
/* fanin pin phases for both output phases */
uint16_t phase[2];
/* best cut index for both phases */
uint16_t best_cut[2];
/* node is mapped using only one phase */
bool same_match;
/* node is mapped to a multi-output gate */
bool multioutput_match[2];
/* arrival time at node output */
double arrival[2];
/* required time at node output */
double required[2];
/* area of the best matches */
float area[2];
/* number of references in the cover 0: pos, 1: neg */
uint32_t map_refs[2];
/* references estimation */
float est_refs[2];
/* area flow */
float flows[2];
};
template<class Ntk, unsigned CutSize, unsigned NInputs, classification_type Configuration>
class emap_impl
{
private:
union multi_match_data
{
uint64_t data{ 0 };
struct
{
uint64_t in_tfi : 1;
uint64_t cut_index : 31;
uint64_t node_index : 32;
};
};
union multioutput_info
{
uint32_t data;
struct
{
unsigned index : 29;
unsigned lowest_index : 1;
unsigned highest_index : 1;
unsigned has_info : 1;
};
};
public:
static constexpr float epsilon = 0.0005;
static constexpr uint32_t max_cut_num = 20;
using cut_t = cut<CutSize, cut_enumeration_emap_cut<NInputs>>;
using cut_set_t = emap_cut_set<cut_t, max_cut_num>;
using cut_merge_t = typename std::array<cut_set_t*, Ntk::max_fanin_size + 1>;
using fanin_cut_t = typename std::array<cut_t const*, Ntk::max_fanin_size>;
using support_t = typename std::array<uint8_t, CutSize>;
using TT = kitty::static_truth_table<6>;
using truth_compute_t = typename std::array<TT, CutSize>;
using node_match_t = std::vector<node_match_emap<NInputs>>;
using klut_map = std::unordered_map<uint32_t, std::array<signal<klut_network>, 2>>;
using block_map = std::unordered_map<uint32_t, std::array<signal<block_network>, 2>>;
static constexpr uint32_t max_multioutput_cut_size = 3;
static constexpr uint32_t max_multioutput_output_size = 2;
using multi_cuts_t = fast_network_cuts<Ntk, max_multioutput_cut_size, true, cut_enumeration_emap_multi_cut>;
using multi_cut_t = typename multi_cuts_t::cut_t;
using multi_leaves_set_t = std::array<uint32_t, max_multioutput_cut_size>;
using multi_output_set_t = std::vector<multi_match_data>;
using multi_hash_t = phmap::flat_hash_map<multi_leaves_set_t, multi_output_set_t, emap_triple_hash<max_multioutput_cut_size>>;
using multi_match_t = std::array<multi_match_data, max_multioutput_output_size>;
using multi_cut_set_t = std::vector<std::array<cut_t, max_multioutput_output_size>>;
using multi_single_matches_t = std::vector<multi_match_t>;
using multi_matches_t = std::vector<std::vector<multi_match_t>>;
using clock = typename std::chrono::steady_clock;
using time_point = typename clock::time_point;
public:
explicit emap_impl( Ntk const& ntk, tech_library<NInputs, Configuration> const& library, emap_params const& ps, emap_stats& st )
: ntk( ntk ),
library( library ),
ps( ps ),
st( st ),
node_match( ntk.size() ),
node_tuple_match( ntk.size() ),
switch_activity( ps.eswp_rounds ? switching_activity( ntk, ps.switching_activity_patterns ) : std::vector<float>( 0 ) ),
cuts( ntk.size() )
{
std::memset( node_tuple_match.data(), 0, sizeof( multioutput_info ) * ntk.size() );
std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info();
std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info();
tmp_visited.reserve( 100 );
}
explicit emap_impl( Ntk const& ntk, tech_library<NInputs, Configuration> const& library, std::vector<float> const& switch_activity, emap_params const& ps, emap_stats& st )
: ntk( ntk ),
library( library ),
ps( ps ),
st( st ),
node_match( ntk.size() ),
node_tuple_match( ntk.size() ),
switch_activity( switch_activity ),
cuts( ntk.size() )
{
std::memset( node_tuple_match.data(), 0, sizeof( multioutput_info ) * ntk.size() );
std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info();
std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info();
tmp_visited.reserve( 100 );
}
cell_view<block_network> run_block()
{
time_begin = clock::now();
auto [res, old2new] = initialize_block_network();
/* multi-output initialization */
if ( ps.map_multioutput && ps.matching_mode != emap_params::structural )
{
compute_multioutput_match();
}
/* compute and save topological order */
init_topo_order();
/* init arrival time */
if ( !init_arrivals() )
return res;
/* search for large matches */
if ( ps.matching_mode == emap_params::structural || CutSize > 6 )
{
if ( !compute_struct_match() )
{
return res;
}
}
/* compute cuts, matches, and initial mapping */
if ( !ps.area_oriented_mapping )
{
if ( !compute_mapping_match<false>() )
{
return res;
}
}
else
{
if ( !compute_mapping_match<true>() )
{
return res;
}
}
/* run area recovery */
if ( !improve_mapping() )
return res;
/* insert buffers for POs driven by PIs */
insert_buffers();
/* generate the output network */
finalize_cover_block( res, old2new );
st.time_total = ( clock::now() - time_begin );
return res;
}
binding_view<klut_network> run_klut()
{
time_begin = clock::now();
auto [res, old2new] = initialize_map_network();
/* multi-output initialization */
if ( ps.map_multioutput && ps.matching_mode != emap_params::structural )
{
compute_multioutput_match();
}
/* compute and save topological order */
init_topo_order();
/* init arrival time */
if ( !init_arrivals() )
return res;
/* search for large matches */
if ( ps.matching_mode == emap_params::structural || CutSize > 6 )
{
if ( !compute_struct_match() )
{
return res;
}
}
/* compute cuts, matches, and initial mapping */
if ( !ps.area_oriented_mapping )
{
if ( !compute_mapping_match<false>() )
{
return res;
}
}
else
{
if ( !compute_mapping_match<true>() )
{
return res;
}
}
/* run area recovery */
if ( !improve_mapping() )
return res;
/* insert buffers for POs driven by PIs */
insert_buffers();
/* generate the output network */
finalize_cover( res, old2new );
st.time_total = ( clock::now() - time_begin );
return res;
}
binding_view<klut_network> run_node_map()
{
time_begin = clock::now();
auto [res, old2new] = initialize_map_network();
/* [i] multi-output support is currently not implemented */
/* compute and save topological order */
init_topo_order();
/* init arrival time */
if ( !init_arrivals() )
return res;
/* compute cuts, matches, and initial mapping */
if ( !ps.area_oriented_mapping )
{
if ( !compute_mapping_match_node<false>() )
{
return res;
}
}
else
{
if ( !compute_mapping_match_node<true>() )
{
return res;
}
}
/* run area recovery */
if ( !improve_mapping() )
return res;
/* insert buffers for POs driven by PIs */
insert_buffers();
/* generate the output network */
finalize_cover( res, old2new );
st.time_total = ( clock::now() - time_begin );
return res;
}
private:
bool improve_mapping()
{
/* compute mapping using global area flow */
uint32_t i = 0;
while ( i++ < ps.area_flow_rounds )
{
if ( !compute_mapping<true>() )
{
return false;
}
}
/* compute mapping using exact area */
i = 0;
compute_required_time( true );
while ( i++ < ps.ela_rounds )
{
if ( !compute_mapping_exact_reversed<false>() )
{
return false;
}
}
/* compute mapping using exact switching activity estimation */
i = 0;
while ( i++ < ps.eswp_rounds )
{
if ( !compute_mapping_exact_reversed<true>() )
{
return false;
}
}
return true;
}
#pragma region Core
template<bool DO_AREA>
bool compute_mapping_match()
{
bool warning_box = false;
for ( auto const& n : topo_order )
{
auto const index = ntk.node_to_index( n );
if ( !compute_matches_node<DO_AREA>( n, warning_box ) )
{
continue;
}
/* load multi-output cuts and data */
if ( ps.map_multioutput && node_tuple_match[index].has_info )
{
match_multi_add_cuts( n );
}
/* match positive phase */
match_phase<DO_AREA>( n, 0u );
/* match negative phase */
match_phase<DO_AREA>( n, 1u );
/* try to drop one phase */
match_drop_phase<DO_AREA, false>( n );
/* select alternative matches to use */
select_alternatives<DO_AREA>( n );
/* try multi-output matches */
if constexpr ( DO_AREA )
{
if ( ps.map_multioutput && node_tuple_match[index].highest_index )
{
if ( match_multioutput<DO_AREA>( n ) )
multi_node_update<DO_AREA>( n );
}
}
}
double area_old = area;
bool success = set_mapping_refs_and_req<DO_AREA, false>();
if ( warning_box )
{
std::cerr << "[i] MAP WARNING: not mapped don't touch gates are treated as sequential black boxes\n";
}
/* round stats */
if ( ps.verbose )
{
std::stringstream stats{};
float area_gain = 0.0f;
if ( iteration != 1 )
area_gain = float( ( area_old - area ) / area_old * 100 );
if constexpr ( DO_AREA )
{
stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) );
}
else
{
stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) );
}
st.round_stats.push_back( stats.str() );
}
return success;
}
template<bool DO_AREA>
inline bool compute_matches_node( node<Ntk> const& n, bool& warning_box )
{
auto const index = ntk.node_to_index( n );
auto& node_data = node_match[index];
node_data.est_refs[0] = node_data.est_refs[1] = static_cast<double>( ntk.fanout_size( n ) );
node_data.map_refs[0] = node_data.map_refs[1] = 0;
node_data.required[0] = node_data.required[1] = std::numeric_limits<float>::max();
if ( ntk.is_constant( n ) )
{
/* all terminals have flow 0.0 */
node_data.flows[0] = node_data.flows[1] = 0.0f;
node_data.best_alternative[0].flow = node_data.best_alternative[1].flow = 0.0f;
node_data.arrival[0] = node_data.arrival[1] = 0.0f;
node_data.best_alternative[0].arrival = node_data.best_alternative[1].arrival = 0.0f;
/* skip if cuts have been computed before */
if ( cuts[index].size() == 0 )
{
add_zero_cut( index );
match_constants( index );
}
return false;
}
else if ( ntk.is_pi( n ) )
{
node_data.flows[0] = 0.0f;
node_data.best_alternative[0].flow = 0.0f;
/* PIs have the negative phase implemented with an inverter */
node_data.flows[1] = lib_inv_area / node_data.est_refs[1];
node_data.best_alternative[1].flow = lib_inv_area / node_data.est_refs[1];
/* skip if cuts have been computed before */
if ( cuts[index].size() == 0 )
{
add_unit_cut( index );
}
return false;
}
if ( ps.matching_mode == emap_params::structural )
return true;
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( n ) )
{
warning_box |= initialize_box( n );
return false;
}
}
/* compute cuts for node */
if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 )
{
merge_cuts2<DO_AREA>( n );
}
else
{
merge_cuts<DO_AREA>( n );
}
return true;
}
template<bool DO_AREA>
void merge_cuts2( node<Ntk> const& n )
{
static constexpr uint32_t max_cut_size = CutSize > 6 ? 6 : CutSize;
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
emap_cut_sort_type sort = emap_cut_sort_type::AREA;
/* compute cuts */
const auto fanin = 2;
ntk.foreach_fanin( ntk.index_to_node( index ), [this]( auto child, auto i ) {
lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )];
} );
lcuts[2] = &cuts[index];
auto& rcuts = *lcuts[fanin];
/* move pre-computed structural cuts to a temporary cutset */
bool reinsert_cuts = false;
if ( rcuts.size() )
{
temp_cuts.clear();
for ( auto& cut : rcuts )
{
if ( ( *cut )->ignore )
continue;
recompute_cut_data( *cut, n );
temp_cuts.simple_insert( *cut );
reinsert_cuts = true;
}
rcuts.clear();
}
/* set cut limit for run-time optimization*/
rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit );
cut_t new_cut;
new_cut->pattern_index = 0;
fanin_cut_t vcuts;
for ( auto const& c1 : *lcuts[0] )
{
/* skip cuts of pattern matching */
if ( ( *c1 )->pattern_index > 1 )
continue;
vcuts[0] = c1;
for ( auto const& c2 : *lcuts[1] )
{
/* skip cuts of pattern matching */
if ( ( *c2 )->pattern_index > 1 )
continue;
if ( !c1->merge( *c2, new_cut, max_cut_size ) )
{
continue;
}
if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) )
{
continue;
}
/* compute function */
vcuts[1] = c2;
compute_truth_table( index, vcuts, fanin, new_cut );
/* match cut and compute data */
compute_cut_data( new_cut, n );
if ( ps.remove_dominated_cuts )
rcuts.insert( new_cut, false, sort );
else
rcuts.simple_insert( new_cut, sort );
}
}
if ( reinsert_cuts )
{
for ( auto const& cut : temp_cuts )
{
rcuts.simple_insert( *cut, sort );
}
}
cuts_total += rcuts.size();
/* limit the maximum number of cuts */
rcuts.limit( ps.cut_enumeration_ps.cut_limit );
/* add trivial cut */
if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 )
{
add_unit_cut( index );
}
}
template<bool DO_AREA>
void merge_cuts( node<Ntk> const& n )
{
static constexpr uint32_t max_cut_size = CutSize > 6 ? 6 : CutSize;
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
emap_cut_sort_type sort = emap_cut_sort_type::AREA;
cut_t best_cut;
/* compute cuts */
std::vector<uint32_t> cut_sizes;
ntk.foreach_fanin( ntk.index_to_node( index ), [this, &cut_sizes]( auto child, auto i ) {
lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )];
cut_sizes.push_back( static_cast<uint32_t>( lcuts[i]->size() ) );
} );
const auto fanin = cut_sizes.size();
lcuts[fanin] = &cuts[index];
auto& rcuts = *lcuts[fanin];
/* set cut limit for run-time optimization*/
rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit );
fanin_cut_t vcuts;
if ( fanin > 1 && fanin <= ps.cut_enumeration_ps.fanin_limit )
{
cut_t new_cut, tmp_cut;
foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) {
auto it = vcuts.begin();
auto i = 0u;
while ( begin != end )
{
*it++ = &( ( *lcuts[i++] )[*begin++] );
}
if ( !vcuts[0]->merge( *vcuts[1], new_cut, max_cut_size ) )
{
return true; /* continue */
}
for ( i = 2; i < fanin; ++i )
{
tmp_cut = new_cut;
if ( !vcuts[i]->merge( tmp_cut, new_cut, max_cut_size ) )
{
return true; /* continue */
}
}
if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) )
{
return true; /* continue */
}
compute_truth_table( index, vcuts, fanin, new_cut );
/* match cut and compute data */
compute_cut_data( new_cut, n );
if ( ps.remove_dominated_cuts )
rcuts.insert( new_cut, false, sort );
else
rcuts.simple_insert( new_cut, sort );
return true;
} );
/* limit the maximum number of cuts */
rcuts.limit( ps.cut_enumeration_ps.cut_limit );
}
else if ( fanin == 1 )
{
for ( auto const& cut : *lcuts[0] )
{
cut_t new_cut = *cut;
vcuts[0] = cut;
compute_truth_table( index, vcuts, fanin, new_cut );
/* match cut and compute data */
compute_cut_data( new_cut, n );
if ( ps.remove_dominated_cuts )
rcuts.insert( new_cut, false, sort );
else
rcuts.simple_insert( new_cut, sort );
}
/* limit the maximum number of cuts */
rcuts.limit( ps.cut_enumeration_ps.cut_limit );
}
cuts_total += rcuts.size();
add_unit_cut( index );
}
bool compute_struct_match()
{
if ( ps.matching_mode == emap_params::boolean )
return true;
/* compatible only with AIGs */
if constexpr ( !is_aig_network_type_v<Ntk> )
{
if ( ps.matching_mode == emap_params::structural )
{
std::cerr << "[e] MAP ERROR: structural library works only with AIGs\n";
return false;
}
return true;
}
/* no large gates identified */
if ( library.num_structural_gates() == 0 )
{
if ( ps.matching_mode == emap_params::structural )
{
std::cerr << "[e] MAP ERROR: structural library is empty\n";
return false;
}
return true;
}
bool warning_box = false;
for ( auto const& n : topo_order )
{
auto const index = ntk.node_to_index( n );
auto& node_data = node_match[index];
if ( ntk.is_constant( n ) )
{
add_zero_cut( index );
match_constants( index );
continue;
}
else if ( ntk.is_pi( n ) )
{
add_unit_cut( index );
continue;
}
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( n ) )
{
add_unit_cut( index );
continue;
}
}
/* compute cuts for node */
merge_cuts_structural( n );
}
if ( warning_box )
{
std::cerr << "[i] MAP WARNING: not mapped don't touch gates are treated as sequential black boxes\n";
}
/* round stats */
if ( ps.verbose )
{
st.round_stats.push_back( fmt::format( "[i] SCuts : Cuts = {:>12d} Time = {:>12.2f}\n", cuts_total, to_seconds( clock::now() - time_begin ) ) );
}
return true;
}
void merge_cuts_structural( node<Ntk> const& n )
{
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
emap_cut_sort_type sort = emap_cut_sort_type::AREA;
/* compute cuts */
const auto fanin = 2;
std::array<uint32_t, 2> children_phase;
ntk.foreach_fanin( ntk.index_to_node( index ), [&]( auto child, auto i ) {
lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )];
children_phase[i] = ntk.is_complemented( child ) ? 1 : 0;
} );
lcuts[2] = &cuts[index];
auto& rcuts = *lcuts[fanin];
/* set cut limit for run-time optimization*/
rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit );
cut_t new_cut;
std::vector<cut_t const*> vcuts( fanin );
for ( auto const& c1 : *lcuts[0] )
{
for ( auto const& c2 : *lcuts[1] )
{
/* filter large cuts */
if ( c1->size() + c2->size() > CutSize || c1->size() + c2->size() > NInputs )
continue;
/* filter cuts involving constants */
if ( ( *c1 )->pattern_index == 0 || ( *c2 )->pattern_index == 0 )
continue;
vcuts[0] = c1;
vcuts[1] = c2;
uint32_t pattern_id1 = ( ( *c1 )->pattern_index << 1 ) | children_phase[0];
uint32_t pattern_id2 = ( ( *c2 )->pattern_index << 1 ) | children_phase[1];
if ( pattern_id1 > pattern_id2 )
{
std::swap( vcuts[0], vcuts[1] );
std::swap( pattern_id1, pattern_id2 );
}
uint32_t new_pattern = library.get_pattern_id( pattern_id1, pattern_id2 );
/* pattern not matched */
if ( new_pattern == UINT32_MAX )
continue;
create_structural_cut( new_cut, vcuts, new_pattern, pattern_id1, pattern_id2 );
if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) )
continue;
/* match cut and compute data */
compute_cut_data_structural( new_cut, n );
if ( ps.remove_dominated_cuts )
rcuts.insert( new_cut, false, sort );
else
rcuts.simple_insert( new_cut, sort );
}
}
cuts_total += rcuts.size();
/* limit the maximum number of cuts */
rcuts.limit( ps.cut_enumeration_ps.cut_limit );
/* add trivial cut */
if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 )
{
add_unit_cut( index );
}
}
template<bool DO_AREA>
bool compute_mapping_match_node()
{
for ( auto const& n : topo_order )
{
auto const index = ntk.node_to_index( n );
auto& node_data = node_match[index];
node_data.best_gate[0] = node_data.best_gate[1] = nullptr;
node_data.same_match = 0;
node_data.multioutput_match[0] = node_data.multioutput_match[1] = false;
node_data.required[0] = node_data.required[1] = std::numeric_limits<float>::max();
node_data.map_refs[0] = node_data.map_refs[1] = 0;
node_data.est_refs[0] = node_data.est_refs[1] = static_cast<float>( ntk.fanout_size( n ) );
if ( ntk.is_constant( n ) )
{
/* all terminals have flow 0 */
node_data.flows[0] = node_data.flows[1] = 0.0f;
node_data.arrival[0] = node_data.arrival[1] = 0.0f;
add_zero_cut( index );
match_constants( index );
continue;
}
else if ( ntk.is_pi( n ) )
{
/* all terminals have flow 0 */
node_data.flows[0] = 0.0f;
/* PIs have the negative phase implemented with an inverter */
node_data.flows[1] = lib_inv_area / node_data.est_refs[1];
add_unit_cut( index );
continue;
}
/* compute the node mapping */
add_node_cut<DO_AREA>( n );
/* match positive phase */
match_phase<DO_AREA>( n, 0u );
/* match negative phase */
match_phase<DO_AREA>( n, 1u );
/* try to drop one phase */
match_drop_phase<DO_AREA, false>( n );
/* select alternative matches to use */
select_alternatives<DO_AREA>( n );
}
double area_old = area;
bool success = set_mapping_refs_and_req<DO_AREA, false>();
/* round stats */
if ( ps.verbose )
{
std::stringstream stats{};
float area_gain = 0.0f;
if ( iteration != 1 )
area_gain = float( ( area_old - area ) / area_old * 100 );
if constexpr ( DO_AREA )
{
stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) );
}
else
{
stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) );
}
st.round_stats.push_back( stats.str() );
}
return success;
}
template<bool DO_AREA>
void add_node_cut( node<Ntk> const& n )
{
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
auto& rcuts = &cuts[index];
std::vector<uint32_t> fanin_indexes;
fanin_indexes.reserve( Ntk::max_fanin_size );
ntk.foreach_fanin( n, [&]( auto const& f ) {
fanin_indexes.push_back( ntk.node_to_index( ntk.get_node( f ) ) );
} );
assert( fanin_indexes.size() <= CutSize );
cut_t new_cut = rcuts.add_cut( fanin_indexes.begin(), fanin_indexes.end() );
new_cut->function = kitty::extend_to<6>( ntk.node_function( n ) );
/* match cut and compute data */
compute_cut_data( new_cut, n );
++cuts_total;
}
template<bool DO_AREA>
bool compute_mapping()
{
for ( auto const& n : topo_order )
{
uint32_t index = ntk.node_to_index( n );
/* reset mapping */
node_match[index].map_refs[0] = node_match[index].map_refs[1] = 0u;
if ( ntk.is_constant( n ) )
continue;
if ( ntk.is_pi( n ) )
{
node_match[index].flows[1] = lib_inv_area / node_match[index].est_refs[1];
node_match[index].best_alternative[1].flow = lib_inv_area / node_match[index].est_refs[1];
continue;
}
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( n ) )
{
if constexpr ( has_has_binding_v<Ntk> )
{
propagate_data_forward_white_box( n );
}
continue;
}
}
/* match positive phase */
match_phase<DO_AREA>( n, 0u );
/* match negative phase */
match_phase<DO_AREA>( n, 1u );
/* try to drop one phase */
match_drop_phase<DO_AREA, false>( n );
/* try a multi-output match */
if constexpr ( DO_AREA )
{
if ( ps.map_multioutput && node_tuple_match[index].highest_index )
{
bool multi_success = match_multioutput<DO_AREA>( n );
if ( multi_success )
multi_node_update<DO_AREA>( n );
}
}
assert( node_match[index].arrival[0] < node_match[index].required[0] + epsilon );
assert( node_match[index].arrival[1] < node_match[index].required[1] + epsilon );
}
double area_old = area;
bool success = set_mapping_refs_and_req<DO_AREA, false>();
/* round stats */
if ( ps.verbose )
{
std::stringstream stats{};
float area_gain = 0.0f;
if ( iteration != 1 )
area_gain = float( ( area_old - area ) / area_old * 100 );
if constexpr ( DO_AREA )
{
stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) );
}
else
{
stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) );
}
st.round_stats.push_back( stats.str() );
}
return success;
}
template<bool SwitchActivity>
bool compute_mapping_exact_reversed()
{
for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it )
{
if ( ntk.is_constant( *it ) || ntk.is_pi( *it ) )
continue;
const auto index = ntk.node_to_index( *it );
auto& node_data = node_match[index];
/* skip not mapped nodes */
if ( !node_data.map_refs[0] && !node_data.map_refs[1] )
continue;
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
node<Ntk> n = ntk.index_to_node( index );
if ( ntk.is_dont_touch( n ) )
{
if constexpr ( has_has_binding_v<Ntk> )
{
propagate_data_backward_white_box( n );
}
continue;
}
}
/* recursively deselect the best cut shared between
* the two phases if in use in the cover */
uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1;
double old_required = -1;
if ( node_data.same_match )
{
auto const& best_cut = cuts[index][node_data.best_cut[use_phase]];
cut_deref<SwitchActivity>( best_cut, *it, use_phase );
/* propagate required time over the output inverter if present */
if ( node_data.map_refs[use_phase ^ 1] > 0 )
{
old_required = node_data.required[use_phase];
node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[use_phase ^ 1] - lib_inv_delay );
}
}
else if ( !node_data.map_refs[0] || !node_data.map_refs[1] )
{
use_phase = node_data.map_refs[0] ? 0 : 1;
auto const& best_cut = cuts[index][node_data.best_cut[use_phase]];
cut_deref<SwitchActivity>( best_cut, *it, use_phase );
node_data.same_match = true;
}
/* match positive phase */
match_phase_exact<SwitchActivity>( *it, 0u );
/* match negative phase */
match_phase_exact<SwitchActivity>( *it, 1u );
/* restore required time */
if ( old_required > 0 )
{
node_data.required[use_phase] = old_required;
}
/* try to drop one phase */
match_drop_phase<true, true, SwitchActivity>( *it );
/* try a multi-output match */ /* TODO: fix the required time*/
if ( ps.map_multioutput && node_tuple_match[index].lowest_index )
{
bool mapped = match_multioutput_exact<SwitchActivity>( *it, true );
/* propagate required time for the selected gates */
if ( mapped )
{
match_multioutput_propagate_required( *it );
}
else
{
match_propagate_required( index );
}
}
else
{
match_propagate_required( index );
}
}
double area_old = area;
propagate_arrival_times();
/* round stats */
if ( ps.verbose )
{
float area_gain = float( ( area_old - area ) / area_old * 100 );
std::stringstream stats{};
if constexpr ( SwitchActivity )
stats << fmt::format( "[i] Switching: Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) );
else
stats << fmt::format( "[i] Area Rev : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) );
st.round_stats.push_back( stats.str() );
}
return true;
}
inline void match_propagate_required( uint32_t index )
{
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
node<Ntk> n = ntk.index_to_node( index );
if ( ntk.is_dont_touch( n ) )
{
if constexpr ( has_has_binding_v<Ntk> )
{
propagate_data_backward_white_box( n );
}
return;
}
}
auto& node_data = node_match[index];
/* propagate required time through the leaves */
unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u;
unsigned other_phase = use_phase ^ 1;
assert( node_data.best_gate[0] != nullptr || node_data.best_gate[1] != nullptr );
// assert( node_data.map_refs[0] || node_data.map_refs[1] );
/* propagate required time over the output inverter if present */
if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 )
{
node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[other_phase] - lib_inv_delay );
}
if ( node_data.map_refs[0] )
assert( node_data.arrival[0] < node_data.required[0] + epsilon );
if ( node_data.map_refs[1] )
assert( node_data.arrival[1] < node_data.required[1] + epsilon );
if ( node_data.same_match || node_data.map_refs[use_phase] > 0 )
{
auto ctr = 0u;
auto const& best_cut = cuts[index][node_data.best_cut[use_phase]];
auto const& supergate = node_data.best_gate[use_phase];
for ( auto leaf : best_cut )
{
auto phase = ( node_data.phase[use_phase] >> ctr ) & 1;
node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[use_phase] - supergate->tdelay[ctr] );
++ctr;
}
}
if ( !node_data.same_match && node_data.map_refs[other_phase] > 0 )
{
auto ctr = 0u;
auto const& best_cut = cuts[index][node_data.best_cut[other_phase]];
auto const& supergate = node_data.best_gate[other_phase];
for ( auto leaf : best_cut )
{
auto phase = ( node_data.phase[other_phase] >> ctr ) & 1;
node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[other_phase] - supergate->tdelay[ctr] );
++ctr;
}
}
}
template<bool ELA>
bool set_mapping_refs()
{
/* compute the current worst delay and update the mapping refs */
delay = 0.0f;
ntk.foreach_po( [this]( auto s ) {
const auto index = ntk.node_to_index( ntk.get_node( s ) );
if ( ntk.is_complemented( s ) )
delay = std::max( delay, node_match[index].arrival[1] );
else
delay = std::max( delay, node_match[index].arrival[0] );
if constexpr ( !ELA )
{
if ( ntk.is_complemented( s ) )
node_match[index].map_refs[1]++;
else
node_match[index].map_refs[0]++;
}
} );
/* compute current area and update mapping refs in top-down order */
area = 0.0f;
inv = 0;
for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it )
{
const auto index = ntk.node_to_index( *it );
auto& node_data = node_match[index];
/* skip constants and PIs */
if ( ntk.is_constant( *it ) )
{
if ( node_data.map_refs[0] || node_data.map_refs[1] )
{
/* if used and not available in the library launch a mapping error */
if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr )
{
std::cerr << "[e] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl;
st.mapping_error = true;
return false;
}
}
continue;
}
else if ( ntk.is_pi( *it ) )
{
if ( node_match[index].map_refs[1] > 0u )
{
/* Add inverter area over the negated fanins */
area += lib_inv_area;
++inv;
}
continue;
}
/* continue if not referenced in the cover */
if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] )
continue;
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( *it ) )
{
set_mapping_refs_dont_touch<ELA>( *it );
continue;
}
}
unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u;
if ( node_data.best_gate[use_phase] == nullptr )
{
/* Library is not complete, mapping is not possible */
std::cerr << "[e] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl;
st.mapping_error = true;
return false;
}
if ( node_data.same_match || node_data.map_refs[use_phase] > 0 )
{
if constexpr ( !ELA )
{
auto const& best_cut = cuts[index][node_data.best_cut[use_phase]];
auto ctr = 0u;
for ( auto const leaf : best_cut )
{
if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 )
node_match[leaf].map_refs[1]++;
else
node_match[leaf].map_refs[0]++;
}
}
area += node_data.area[use_phase];
if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 )
{
if ( iteration < ps.area_flow_rounds )
{
++node_data.map_refs[use_phase];
}
area += lib_inv_area;
++inv;
}
}
/* invert the phase */
use_phase = use_phase ^ 1;
/* if both phases are implemented and used */
if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 )
{
if constexpr ( !ELA )
{
auto const& best_cut = cuts[index][node_data.best_cut[use_phase]];
auto ctr = 0u;
for ( auto const leaf : best_cut )
{
if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 )
node_match[leaf].map_refs[1]++;
else
node_match[leaf].map_refs[0]++;
}
}
area += node_data.area[use_phase];
}
}
++iteration;
if constexpr ( ELA )
{
return true;
}
/* blend estimated references */
float const coef = 1.0f / ( ( iteration + 1.0f ) * ( iteration + 1.0f ) );
for ( auto i = 0u; i < ntk.size(); ++i )
{
node_match[i].est_refs[0] = std::max( 1.0f, coef * node_match[i].est_refs[0] + ( 1 - coef ) * node_match[i].map_refs[0] );
node_match[i].est_refs[1] = std::max( 1.0f, coef * node_match[i].est_refs[1] + ( 1 - coef ) * node_match[i].map_refs[1] );
}
return true;
}
template<bool DO_AREA, bool ELA>
bool set_mapping_refs_and_req()
{
for ( auto i = 0u; i < node_match.size(); ++i )
{
node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits<float>::max();
}
/* compute the current worst delay and update the mapping refs */
delay = 0.0f;
ntk.foreach_po( [this]( auto s ) {
const auto index = ntk.node_to_index( ntk.get_node( s ) );
if ( ntk.is_complemented( s ) )
delay = std::max( delay, node_match[index].arrival[1] );
else
delay = std::max( delay, node_match[index].arrival[0] );
if constexpr ( !ELA )
{
if ( ntk.is_complemented( s ) )
node_match[index].map_refs[1]++;
else
node_match[index].map_refs[0]++;
}
} );
set_output_required_time( iteration == 0 );
/* compute current area and update mapping refs in top-down order */
area = 0.0f;
inv = 0;
for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it )
{
const auto index = ntk.node_to_index( *it );
auto& node_data = node_match[index];
/* skip constants and PIs */
if ( ntk.is_constant( *it ) )
{
if ( node_match[index].map_refs[0] || node_match[index].map_refs[1] )
{
/* if used and not available in the library launch a mapping error */
if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr )
{
std::cerr << "[e] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl;
st.mapping_error = true;
return false;
}
}
continue;
}
else if ( ntk.is_pi( *it ) )
{
if ( node_match[index].map_refs[1] > 0u )
{
/* Add inverter area over the negated fanins */
area += lib_inv_area;
++inv;
}
continue;
}
/* continue if not referenced in the cover */
if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] )
continue;
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( *it ) )
{
set_mapping_refs_dont_touch<ELA>( *it );
continue;
}
}
/* refine best matches with alternatives */
if constexpr ( !DO_AREA )
{
if ( ps.use_match_alternatives )
refine_best_matches( *it );
}
unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u;
if ( node_data.best_gate[use_phase] == nullptr )
{
/* Library is not complete, mapping is not possible */
std::cerr << "[e] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl;
st.mapping_error = true;
return false;
}
if ( node_data.same_match || node_data.map_refs[use_phase] > 0 )
{
if constexpr ( !ELA )
{
auto const& best_cut = cuts[index][node_data.best_cut[use_phase]];
auto ctr = 0u;
for ( auto const leaf : best_cut )
{
if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 )
node_match[leaf].map_refs[1]++;
else
node_match[leaf].map_refs[0]++;
}
}
area += node_data.area[use_phase];
if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 )
{
if ( iteration < ps.area_flow_rounds )
{
++node_data.map_refs[use_phase];
}
area += lib_inv_area;
++inv;
}
}
/* invert the phase */
use_phase = use_phase ^ 1;
/* if both phases are implemented and used */
if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 )
{
if constexpr ( !ELA )
{
auto const& best_cut = cuts[index][node_data.best_cut[use_phase]];
auto ctr = 0u;
for ( auto const leaf : best_cut )
{
if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 )
node_match[leaf].map_refs[1]++;
else
node_match[leaf].map_refs[0]++;
}
}
area += node_data.area[use_phase];
}
if ( !ps.area_oriented_mapping )
{
match_propagate_required( index );
}
}
++iteration;
if constexpr ( ELA )
{
return true;
}
/* blend estimated references */
float const coef = 1.0f / ( ( iteration + 1.0f ) * ( iteration + 1.0f ) );
for ( auto i = 0u; i < ntk.size(); ++i )
{
node_match[i].est_refs[0] = std::max( 1.0f, coef * node_match[i].est_refs[0] + ( 1 - coef ) * node_match[i].map_refs[0] );
node_match[i].est_refs[1] = std::max( 1.0f, coef * node_match[i].est_refs[1] + ( 1 - coef ) * node_match[i].map_refs[1] );
}
return true;
}
template<bool ELA>
inline void set_mapping_refs_dont_touch( node<Ntk> const& n )
{
if constexpr ( !ELA )
{
/* reference node */
ntk.foreach_fanin( n, [&]( auto const& f ) {
uint32_t leaf = ntk.node_to_index( ntk.get_node( f ) );
uint8_t phase = ntk.is_complemented( f ) ? 1 : 0;
node_match[leaf].map_refs[phase]++;
} );
}
const auto index = ntk.node_to_index( n );
if constexpr ( has_has_binding_v<Ntk> )
{
/* increase area */
area += node_match[index].area[0];
if ( node_match[index].map_refs[1] )
{
if ( iteration < ps.area_flow_rounds )
{
++node_match[index].map_refs[0];
}
area += lib_inv_area;
++inv;
}
}
}
void set_output_required_time( bool warning )
{
double required = delay;
/* relax delay constraints */
if ( iteration == 0 && ps.required_time == 0.0f && ps.required_times.empty() && ps.relax_required > 0.0f )
{
required *= ( 100.0 + ps.relax_required ) / 100.0;
}
/* Global target time constraint */
if ( ps.required_times.empty() )
{
if ( ps.required_time != 0.0f )
{
if ( ps.required_time < delay - epsilon )
{
if ( warning )
std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f}", ps.required_time ) << std::endl;
}
else
{
required = ps.required_time;
}
}
/* set the required time at POs */
ntk.foreach_po( [&]( auto const& s ) {
const auto index = ntk.node_to_index( ntk.get_node( s ) );
if ( ntk.is_complemented( s ) )
node_match[index].required[1] = required;
else
node_match[index].required[0] = required;
} );
return;
}
/* Output-specific target time constraint */
ntk.foreach_po( [&]( auto const& s, uint32_t i ) {
const auto index = ntk.node_to_index( ntk.get_node( s ) );
uint8_t phase = ntk.is_complemented( s ) ? 1 : 0;
if ( node_match[index].arrival[phase] > ps.required_times[i] + epsilon )
{
/* maintain the same delay */
node_match[index].required[phase] = node_match[index].arrival[phase];
if ( warning )
std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f} at output {}", ps.required_times[i], i ) << std::endl;
}
else
{
node_match[index].required[phase] = ps.required_times[i];
}
} );
}
void compute_required_time( bool exit_early = false )
{
for ( auto i = 0u; i < node_match.size(); ++i )
{
node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits<float>::max();
}
/* return if mapping is area oriented */
if ( ps.area_oriented_mapping )
return;
set_output_required_time( iteration == 1 );
if ( exit_early )
return;
/* propagate required time to the PIs */
for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it )
{
if ( ntk.is_pi( *it ) || ntk.is_constant( *it ) )
break;
const auto index = ntk.node_to_index( *it );
if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] )
continue;
match_propagate_required( index );
}
}
void propagate_arrival_times()
{
area = 0.0f;
inv = 0;
for ( auto const& n : topo_order )
{
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
/* measure area */
if ( ntk.is_constant( n ) )
{
continue;
}
else if ( ntk.is_pi( n ) )
{
if ( node_data.map_refs[1] > 0u )
{
/* Add inverter area over the negated fanins */
area += lib_inv_area;
++inv;
}
continue;
}
/* reset required time */
node_data.required[0] = std::numeric_limits<float>::max();
node_data.required[1] = std::numeric_limits<float>::max();
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
node<Ntk> n = ntk.index_to_node( index );
if ( ntk.is_dont_touch( n ) )
{
if constexpr ( has_has_binding_v<Ntk> )
{
propagate_data_forward_white_box( n );
if ( node_match[index].map_refs[0] || node_match[index].map_refs[1] )
area += node_data.area[0];
if ( node_data.map_refs[1] )
{
area += lib_inv_area;
++inv;
}
}
continue;
}
}
uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1;
/* compute arrival of use_phase */
supergate<NInputs> const* best_gate = node_data.best_gate[use_phase];
double worst_arrival = 0;
uint16_t best_phase = node_data.phase[use_phase];
auto ctr = 0u;
for ( auto l : cuts[index][node_data.best_cut[use_phase]] )
{
double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr];
worst_arrival = std::max( worst_arrival, arrival_pin );
++ctr;
}
node_data.arrival[use_phase] = worst_arrival;
/* compute area */
if ( node_data.map_refs[use_phase] > 0 || ( node_data.same_match && ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) ) )
{
area += node_data.area[use_phase];
if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 )
{
area += lib_inv_area;
++inv;
}
}
/* compute arrival of the other phase */
use_phase ^= 1;
if ( node_data.same_match )
{
node_data.arrival[use_phase] = worst_arrival + lib_inv_delay;
continue;
}
assert( node_data.best_gate[use_phase] != nullptr );
best_gate = node_data.best_gate[use_phase];
worst_arrival = 0;
best_phase = node_data.phase[use_phase];
ctr = 0u;
for ( auto l : cuts[index][node_data.best_cut[use_phase]] )
{
double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr];
worst_arrival = std::max( worst_arrival, arrival_pin );
++ctr;
}
node_data.arrival[use_phase] = worst_arrival;
if ( node_data.map_refs[use_phase] > 0 )
{
area += node_data.area[use_phase];
}
}
/* compute the current worst delay */
delay = 0.0f;
ntk.foreach_po( [this]( auto s ) {
const auto index = ntk.node_to_index( ntk.get_node( s ) );
if ( ntk.is_complemented( s ) )
delay = std::max( delay, node_match[index].arrival[1] );
else
delay = std::max( delay, node_match[index].arrival[0] );
} );
/* return if mapping is area oriented */
++iteration;
if ( ps.area_oriented_mapping )
return;
/* set the required time at POs */
ntk.foreach_po( [&]( auto const& s ) {
const auto index = ntk.node_to_index( ntk.get_node( s ) );
if ( ntk.is_complemented( s ) )
node_match[index].required[1] = delay;
else
node_match[index].required[0] = delay;
} );
}
void propagate_arrival_node( node<Ntk> const& n )
{
uint32_t index = ntk.node_to_index( n );
auto& node_data = node_match[index];
uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1;
/* compute arrival of use_phase */
supergate<NInputs> const* best_gate = node_data.best_gate[use_phase];
double worst_arrival = 0;
uint16_t best_phase = node_data.phase[use_phase];
auto ctr = 0u;
for ( auto l : cuts[index][node_data.best_cut[use_phase]] )
{
double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr];
worst_arrival = std::max( worst_arrival, arrival_pin );
++ctr;
}
node_data.arrival[use_phase] = worst_arrival;
/* compute arrival of the other phase */
use_phase ^= 1;
if ( node_data.same_match )
{
node_data.arrival[use_phase] = worst_arrival + lib_inv_delay;
return;
}
assert( node_data.best_gate[0] != nullptr );
best_gate = node_data.best_gate[use_phase];
worst_arrival = 0;
best_phase = node_data.phase[use_phase];
ctr = 0u;
for ( auto l : cuts[index][node_data.best_cut[use_phase]] )
{
double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr];
worst_arrival = std::max( worst_arrival, arrival_pin );
++ctr;
}
node_data.arrival[use_phase] = worst_arrival;
}
template<bool DO_AREA>
void match_phase( node<Ntk> const& n, uint8_t phase )
{
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
uint32_t cut_index = 0u;
node_data.best_gate[phase] = nullptr;
node_data.arrival[phase] = std::numeric_limits<float>::max();
node_data.flows[phase] = std::numeric_limits<float>::max();
node_data.area[phase] = std::numeric_limits<float>::max();
uint32_t best_size = UINT32_MAX;
best_gate_emap<NInputs>& gA = node_data.best_alternative[phase];
gA.gate = nullptr;
gA.arrival = std::numeric_limits<float>::max();
gA.flow = std::numeric_limits<float>::max();
uint32_t best_sizeA = UINT32_MAX;
/* unmap multioutput */
node_data.multioutput_match[phase] = false;
/* foreach cut */
for ( auto& cut : cuts[index] )
{
/* trivial cuts or not matched cuts */
if ( ( *cut )->ignore )
{
++cut_index;
continue;
}
auto const& supergates = ( *cut )->supergates;
auto const negation = ( *cut )->negations[phase];
if ( supergates[phase] == nullptr )
{
++cut_index;
continue;
}
/* match each gate and take the best one */
for ( auto const& gate : *supergates[phase] )
{
uint16_t gate_polarity = gate.polarity ^ negation;
double worst_arrival = 0.0f;
double worst_arrivalA = 0.0f;
float area_local = gate.area;
float area_localA = gate.area;
auto ctr = 0u;
for ( auto l : *cut )
{
uint8_t leaf_phase = ( gate_polarity >> ctr ) & 1;
double arrival_pinA = node_match[l].best_alternative[leaf_phase].arrival + gate.tdelay[ctr];
worst_arrivalA = std::max( worst_arrivalA, arrival_pinA );
// if constexpr ( DO_AREA )
// {
// if ( worst_arrivalA > node_data.required[phase] + epsilon || worst_arrivalA >= std::numeric_limits<float>::max() )
// break;
// }
double arrival_pin = node_match[l].arrival[leaf_phase] + gate.tdelay[ctr];
worst_arrival = std::max( worst_arrival, arrival_pin );
area_local += node_match[l].flows[leaf_phase];
area_localA += node_match[l].best_alternative[leaf_phase].flow;
++ctr;
}
bool skip = false;
if constexpr ( DO_AREA )
{
if ( ctr < cut->size() )
continue;
if ( worst_arrival > node_data.required[phase] + epsilon || worst_arrival >= std::numeric_limits<float>::max() )
skip = true;
}
if ( !skip && compare_map<DO_AREA>( worst_arrival, node_data.arrival[phase], area_local, node_data.flows[phase], cut->size(), best_size ) )
{
node_data.best_gate[phase] = &gate;
node_data.arrival[phase] = worst_arrival;
node_data.flows[phase] = area_local;
node_data.best_cut[phase] = cut_index;
node_data.area[phase] = gate.area;
node_data.phase[phase] = gate_polarity;
best_size = cut->size();
}
/* compute the alternative */
if ( compare_map<!DO_AREA>( worst_arrivalA, gA.arrival, area_localA, gA.flow, cut->size(), best_sizeA ) )
{
gA.gate = &gate;
gA.arrival = worst_arrivalA;
gA.area = gate.area;
gA.flow = area_localA;
gA.phase = gate_polarity;
gA.cut = cut_index;
best_sizeA = cut->size();
gA.size = cut->size();
}
}
++cut_index;
}
}
template<bool SwitchActivity>
void match_phase_exact( node<Ntk> const& n, uint8_t phase )
{
double best_arrival = std::numeric_limits<float>::max();
float best_exact_area = std::numeric_limits<float>::max();
float best_area = std::numeric_limits<float>::max();
uint32_t best_size = UINT32_MAX;
uint8_t best_cut = 0u;
uint16_t best_phase = 0u;
uint8_t cut_index = 0u;
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
supergate<NInputs> const* best_gate = node_data.best_gate[phase];
/* unmap multioutput */
if ( node_data.multioutput_match[phase] )
{
/* dereference multi-output */
if ( !node_data.same_match && best_gate != nullptr && node_data.map_refs[phase] )
{
auto const& cut = multi_cut_set[node_data.best_cut[phase]][0];
cut_deref<SwitchActivity>( cut, n, phase );
}
best_gate = nullptr;
node_data.multioutput_match[phase] = false;
}
/* recompute best match info */
if ( best_gate != nullptr )
{
/* if cut is implemented, remove it from the cover */
if ( !node_data.same_match && node_data.map_refs[phase] )
{
auto const& cut = cuts[index][node_data.best_cut[phase]];
cut_deref<SwitchActivity>( cut, n, phase );
}
}
/* foreach cut */
for ( auto& cut : cuts[index] )
{
/* trivial cuts or not matched cuts */
if ( ( *cut )->ignore )
{
++cut_index;
continue;
}
auto const& supergates = ( *cut )->supergates;
auto const negation = ( *cut )->negations[phase];
if ( supergates[phase] == nullptr )
{
++cut_index;
continue;
}
/* match each gate and take the best one */
for ( auto const& gate : *supergates[phase] )
{
uint16_t gate_polarity = gate.polarity ^ negation;
double worst_arrival = 0.0f;
auto ctr = 0u;
for ( auto l : *cut )
{
double arrival_pin = node_match[l].arrival[( gate_polarity >> ctr ) & 1] + gate.tdelay[ctr];
worst_arrival = std::max( worst_arrival, arrival_pin );
++ctr;
}
if ( worst_arrival > node_data.required[phase] + epsilon || worst_arrival >= std::numeric_limits<float>::max() )
continue;
node_data.phase[phase] = gate_polarity;
node_data.area[phase] = gate.area;
float area_exact = cut_measure_mffc<SwitchActivity>( *cut, n, phase );
if ( compare_map<true>( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) )
{
best_arrival = worst_arrival;
best_exact_area = area_exact;
best_area = gate.area;
best_size = cut->size();
best_cut = cut_index;
best_phase = gate_polarity;
best_gate = &gate;
}
}
++cut_index;
}
node_data.flows[phase] = best_exact_area;
node_data.arrival[phase] = best_arrival;
node_data.area[phase] = best_area;
node_data.best_cut[phase] = best_cut;
node_data.phase[phase] = best_phase;
node_data.best_gate[phase] = best_gate;
if ( !node_data.same_match && node_data.map_refs[phase] )
{
best_exact_area = cut_ref<SwitchActivity>( cuts[index][best_cut], n, phase );
}
}
template<bool DO_AREA, bool ELA, bool SwitchActivity = false>
void match_drop_phase( node<Ntk> const& n )
{
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
/* compute arrival adding an inverter to the other match phase */
double worst_arrival_npos = node_data.arrival[1] + lib_inv_delay;
double worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay;
bool use_zero = false;
bool use_one = false;
/* only one phase is matched */
if ( node_data.best_gate[0] == nullptr )
{
set_match_complemented_phase( index, 1, worst_arrival_npos );
if constexpr ( ELA )
{
if ( node_data.map_refs[0] || node_data.map_refs[1] )
cut_ref<false>( cuts[index][node_data.best_cut[1]], n, 1 );
}
return;
}
else if ( node_data.best_gate[1] == nullptr )
{
set_match_complemented_phase( index, 0, worst_arrival_nneg );
if constexpr ( ELA )
{
if ( node_data.map_refs[0] || node_data.map_refs[1] )
cut_ref<false>( cuts[index][node_data.best_cut[0]], n, 0 );
}
return;
}
/* try to use only one match to cover both phases */
if constexpr ( !DO_AREA )
{
/* if arrival improves matching the other phase and inserting an inverter */
if ( worst_arrival_npos < node_data.arrival[0] + epsilon )
{
use_one = true;
}
if ( worst_arrival_nneg < node_data.arrival[1] + epsilon )
{
use_zero = true;
}
}
else
{
/* check if both phases + inverter meet the required time */
use_zero = worst_arrival_nneg < ( node_data.required[1] + epsilon );
use_one = worst_arrival_npos < ( node_data.required[0] + epsilon );
}
/* condition on not used phases, evaluate a substitution during exact area recovery */
if constexpr ( ELA )
{
if ( node_data.map_refs[0] == 0 || node_data.map_refs[1] == 0 )
{
/* select the used match */
auto phase = 0;
auto nphase = 0;
if ( node_data.map_refs[0] == 0 )
{
phase = 1;
use_one = true;
use_zero = false;
}
else
{
nphase = 1;
use_one = false;
use_zero = true;
}
/* select the not used match instead if it leads to area improvement and doesn't violate the required time */
if ( node_data.arrival[nphase] + lib_inv_delay < node_data.required[phase] + epsilon )
{
auto size_phase = cuts[index][node_data.best_cut[phase]].size();
auto size_nphase = cuts[index][node_data.best_cut[nphase]].size();
if ( compare_map<DO_AREA>( node_data.arrival[nphase] + lib_inv_delay, node_data.arrival[phase], node_data.flows[nphase] + lib_inv_area, node_data.flows[phase], size_nphase, size_phase ) )
{
/* invert the choice */
use_zero = !use_zero;
use_one = !use_one;
}
}
}
}
if ( ( !use_zero && !use_one ) )
{
/* use both phases */
node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0];
node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1];
node_data.same_match = false;
return;
}
/* use area flow as a tiebreaker */
if ( use_zero && use_one )
{
auto size_zero = cuts[index][node_data.best_cut[0]].size();
auto size_one = cuts[index][node_data.best_cut[1]].size();
if constexpr ( ELA )
{
if ( !node_data.same_match )
{
/* both phases were implemented --> evaluate substitution */
cut_deref<false>( cuts[index][node_data.best_cut[0]], n, 0 );
node_data.flows[1] = cut_deref<false>( cuts[index][node_data.best_cut[1]], n, 1 );
node_data.flows[0] = cut_ref<false>( cuts[index][node_data.best_cut[0]], n, 0 );
cut_ref<false>( cuts[index][node_data.best_cut[1]], n, 1 );
}
/* evaluate based on inverter cost */
if constexpr ( !SwitchActivity )
{
use_zero = lib_inv_area < node_data.flows[1] + epsilon;
use_one = lib_inv_area < node_data.flows[0] + epsilon;
}
if ( use_one && use_zero )
{
if ( compare_map<DO_AREA>( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) )
use_one = false;
else
use_zero = false;
}
else if ( !use_one && !use_zero && node_data.same_match )
{
node_data.same_match = false;
cut_ref<false>( cuts[index][node_data.best_cut[0]], n, 0 );
cut_ref<false>( cuts[index][node_data.best_cut[1]], n, 1 );
return;
}
}
else
{
/* compare flows by looking at the most convinient and referenced */
if ( node_data.flows[0] / node_data.est_refs[0] + lib_inv_area < node_data.flows[1] / node_data.est_refs[1] + epsilon )
{
use_one = false;
}
else if ( node_data.flows[1] / node_data.est_refs[1] + lib_inv_area < node_data.flows[0] / node_data.est_refs[0] + epsilon )
{
use_zero = false;
}
else
{
/* delay the decision on what to keep --> wait for better estimations */
node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0];
node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1];
node_data.same_match = false;
return;
}
}
}
if ( use_zero )
{
if constexpr ( ELA )
{
/* set cut references */
if ( !node_data.same_match )
{
/* dereference the negative phase cut if in use */
if ( node_data.map_refs[1] > 0 )
cut_deref<false>( cuts[index][node_data.best_cut[1]], n, 1 );
/* reference the positive cut if not in use before */
if ( node_data.map_refs[0] == 0 && node_data.map_refs[1] > 0 )
cut_ref<false>( cuts[index][node_data.best_cut[0]], n, 0 );
}
else if ( node_data.map_refs[0] || node_data.map_refs[1] )
cut_ref<false>( cuts[index][node_data.best_cut[0]], n, 0 );
}
set_match_complemented_phase( index, 0, worst_arrival_nneg );
}
else
{
if constexpr ( ELA )
{
/* set cut references */
if ( !node_data.same_match )
{
/* dereference the positive phase cut if in use */
if ( node_data.map_refs[0] > 0 )
cut_deref<false>( cuts[index][node_data.best_cut[0]], n, 0 );
/* reference the negative cut if not in use before */
if ( node_data.map_refs[1] == 0 && node_data.map_refs[0] > 0 )
cut_ref<false>( cuts[index][node_data.best_cut[1]], n, 1 );
}
else if ( node_data.map_refs[0] || node_data.map_refs[1] )
cut_ref<false>( cuts[index][node_data.best_cut[1]], n, 1 );
}
set_match_complemented_phase( index, 1, worst_arrival_npos );
}
}
inline void set_match_complemented_phase( uint32_t index, uint8_t phase, double worst_arrival_n )
{
auto& node_data = node_match[index];
auto phase_n = phase ^ 1;
node_data.same_match = true;
node_data.best_gate[phase_n] = nullptr;
node_data.best_cut[phase_n] = node_data.best_cut[phase];
node_data.phase[phase_n] = node_data.phase[phase];
node_data.arrival[phase_n] = worst_arrival_n;
node_data.area[phase_n] = node_data.area[phase];
node_data.flows[phase_n] = ( node_data.flows[phase] + lib_inv_area ) / node_data.est_refs[phase_n];
node_data.flows[phase] = node_data.flows[phase] / node_data.est_refs[phase];
}
template<bool DO_AREA>
inline void select_alternatives( node<Ntk> const& n )
{
if constexpr ( DO_AREA )
return;
if ( !ps.use_match_alternatives )
return;
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
best_gate_emap<NInputs>& g0 = node_data.best_alternative[0];
best_gate_emap<NInputs>& g1 = node_data.best_alternative[1];
float g0flow = g0.flow / node_data.est_refs[0];
float g1flow = g1.flow / node_data.est_refs[1];
/* process for best area */ /* removed check on required since this is executed only during a delay pass */
if ( g0.gate != nullptr && g0flow + lib_inv_area < g1flow + epsilon )
{
g1 = g0;
g1.gate = nullptr;
g1.arrival += lib_inv_delay;
g1.flow = ( g1.flow + lib_inv_area ) / node_data.est_refs[1];
g0.flow = g0flow;
return;
}
else if ( g1.gate != nullptr && g1flow + lib_inv_area < g0flow + epsilon )
{
g0 = g1;
g0.gate = nullptr;
g0.arrival += lib_inv_delay;
g0.flow = ( g0.flow + lib_inv_area ) / node_data.est_refs[0];
g1.flow = g1flow;
return;
}
g0.flow = g0flow;
g1.flow = g1flow;
}
inline void refine_best_matches( node<Ntk> const& n )
{
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
/* evaluate to change the best matches with the best alternative */
best_gate_emap<NInputs>& g0 = node_data.best_alternative[0];
best_gate_emap<NInputs>& g1 = node_data.best_alternative[1];
if ( node_data.map_refs[0] && node_data.map_refs[1] )
{
if ( node_data.same_match )
{
/* pick best implementation between the two alternatives */
unsigned best_match_phase = node_data.best_gate[0] == nullptr ? 1 : 0;
unsigned use_phase = g0.gate == nullptr ? 1 : 0;
if ( g0.gate != nullptr && g1.gate != nullptr )
{
if ( g0.arrival > node_data.required[0] + epsilon || g1.arrival > node_data.required[1] + epsilon )
return;
refine_best_matches_copy_refinement( n, 0, false );
refine_best_matches_copy_refinement( n, 1, false );
node_data.same_match = false;
return;
}
else
{
best_gate_emap<NInputs>& gUse = node_data.best_alternative[use_phase];
if ( gUse.arrival > node_data.required[use_phase] + epsilon || gUse.arrival + lib_inv_delay > node_data.required[use_phase ^ 1] + epsilon )
{
return;
}
refine_best_matches_copy_refinement( n, use_phase, true );
return;
}
}
else
{
/* not same match: evaluate both zero and one phase */
if ( g0.gate != nullptr && g0.arrival < node_data.required[0] + epsilon )
{
node_data.same_match = false;
refine_best_matches_copy_refinement( n, 0, g1.gate == nullptr && g0.arrival + lib_inv_delay < node_data.required[1] + epsilon );
}
if ( g1.gate != nullptr && g1.arrival < node_data.required[1] + epsilon )
{
node_data.same_match = false;
refine_best_matches_copy_refinement( n, 1, g0.gate == nullptr && g1.arrival + lib_inv_delay < node_data.required[0] + epsilon );
}
}
}
else if ( node_data.map_refs[0] )
{
if ( g0.gate != nullptr && g0.arrival < node_data.required[0] + epsilon )
{
node_data.same_match = false;
refine_best_matches_copy_refinement( n, 0, false );
}
else if ( g0.gate == nullptr && g1.arrival + lib_inv_delay < node_data.required[0] + epsilon )
{
refine_best_matches_copy_refinement( n, 1, true );
}
}
else
{
if ( g1.gate != nullptr && g1.arrival < node_data.required[1] + epsilon )
{
node_data.same_match = false;
refine_best_matches_copy_refinement( n, 1, false );
}
else if ( g1.gate == nullptr && g0.arrival + lib_inv_delay < node_data.required[1] + epsilon )
{
refine_best_matches_copy_refinement( n, 0, true );
}
}
}
inline void refine_best_matches_copy_refinement( node<Ntk> const& n, unsigned phase, bool both_phases )
{
auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
best_gate_emap<NInputs>& bg = node_data.best_alternative[phase];
node_data.best_gate[phase] = bg.gate;
node_data.phase[phase] = bg.phase;
node_data.best_cut[phase] = bg.cut;
node_data.arrival[phase] = bg.arrival;
node_data.area[phase] = bg.area;
node_data.flows[phase] = bg.flow;
if ( !both_phases )
return;
node_data.same_match = true;
phase ^= 1;
node_data.best_gate[phase] = nullptr;
node_data.phase[phase] = bg.phase;
node_data.best_cut[phase] = bg.cut;
node_data.arrival[phase] = bg.arrival + lib_inv_delay;
node_data.area[phase] = bg.area;
node_data.flows[phase] = ( bg.flow * node_data.est_refs[phase ^ 1] + lib_inv_area ) / node_data.est_refs[phase];
}
bool initialize_box( node<Ntk> const& n )
{
uint32_t index = ntk.node_to_index( n );
if ( cuts[index].size() == 0 )
add_unit_cut( index );
auto& node_data = node_match[index];
node_data.same_match = true;
/* if it has mapping data propagate the delays and measure the data */
if constexpr ( has_has_binding_v<Ntk> )
{
propagate_data_forward_white_box( n );
return false;
}
/* consider as a black box */
node_data.flows[0] = 0.0f;
node_data.flows[1] = lib_inv_area / node_data.est_ref[1];
node_data.arrival[0] = 0.0f;
node_data.arrival[1] = lib_inv_delay;
node_data.area[0] = node_data.area[1] = 0;
return true;
}
void propagate_data_forward_white_box( node<Ntk> const& n )
{
uint32_t index = ntk.node_to_index( n );
auto& node_data = node_match[index];
auto const& gate = ntk.get_binding( n );
/* propagate arrival time */
double arrival = 0;
ntk.foreach_fanin( n, [&]( auto const& f, auto i ) {
uint32_t f_index = ntk.node_to_index( ntk.get_node( f ) );
uint8_t phase = ntk.is_complemented( f ) ? 1 : 0;
double propagation_delay = std::max( gate.pins[i].rise_block_delay, gate.pins[i].fall_block_delay );
arrival = std::max( arrival, node_match[f_index].arrival[phase] + propagation_delay );
} );
/* set data */
node_data.arrival[0] = arrival;
node_data.arrival[1] = arrival + lib_inv_delay;
node_data.area[0] = node_data.area[1] = gate.area;
node_data.flows[1] = ( node_data.flows[0] + lib_inv_area ) / node_data.est_refs[1];
node_data.flows[0] = node_data.area[0] / node_data.est_refs[0];
}
void propagate_data_backward_white_box( node<Ntk> const& n )
{
uint32_t index = ntk.node_to_index( n );
auto& node_data = node_match[index];
auto const& gate = ntk.get_binding( n );
assert( node_data.map_refs[0] || node_data.map_refs[1] );
/* propagate required time over the output inverter if present */
if ( node_data.map_refs[1] > 0 )
{
node_data.required[0] = std::min( node_data.required[0], node_data.required[1] - lib_inv_delay );
}
if ( node_data.map_refs[0] )
assert( node_data.arrival[0] < node_data.required[0] + epsilon );
if ( node_data.map_refs[1] )
assert( node_data.arrival[1] < node_data.required[1] + epsilon );
ntk.foreach_fanin( n, [&]( auto const& f, auto i ) {
uint32_t f_index = ntk.node_to_index( ntk.get_node( f ) );
uint8_t phase = ntk.is_complemented( f ) ? 1 : 0;
double propagation_delay = std::max( gate.pins[i].rise_block_delay, gate.pins[i].fall_block_delay );
node_match[f_index].required[phase] = std::min( node_match[f_index].required[phase], node_data.required[0] - propagation_delay );
} );
}
void match_constants( uint32_t index )
{
auto& node_data = node_match[index];
kitty::static_truth_table<6> zero_tt;
auto const supergates_zero = library.get_supergates( zero_tt );
auto const supergates_one = library.get_supergates( ~zero_tt );
/* Not available in the library */
if ( supergates_zero == nullptr && supergates_one == nullptr )
{
return;
}
/* if only one is available, the other is obtained using an inverter */
if ( supergates_zero != nullptr )
{
node_data.best_gate[0] = &( ( *supergates_zero )[0] );
node_data.arrival[0] = node_data.best_gate[0]->tdelay[0];
node_data.area[0] = node_data.best_gate[0]->area;
node_data.phase[0] = 0;
}
if ( supergates_one != nullptr )
{
node_data.best_gate[1] = &( ( *supergates_one )[0] );
node_data.arrival[1] = node_data.best_gate[1]->tdelay[0];
node_data.area[1] = node_data.best_gate[1]->area;
node_data.phase[1] = 0;
}
else
{
node_data.same_match = true;
node_data.arrival[1] = node_data.arrival[0] + lib_inv_delay;
node_data.area[1] = node_data.area[0] + lib_inv_area;
node_data.phase[1] = 1;
}
if ( supergates_zero == nullptr )
{
node_data.same_match = true;
node_data.arrival[0] = node_data.arrival[1] + lib_inv_delay;
node_data.area[0] = node_data.area[1] + lib_inv_area;
node_data.phase[0] = 1;
}
}
template<bool DO_AREA>
bool match_multioutput( node<Ntk> const& n )
{
/* extract outputs tuple */
uint32_t index = ntk.node_to_index( n );
multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0];
/* get the cut */
auto const& cut0 = cuts[tuple_data[0].node_index][tuple_data[0].cut_index];
/* local values storage */
std::array<double, max_multioutput_output_size> arrival;
std::array<float, max_multioutput_output_size> area_flow;
std::array<float, max_multioutput_output_size> area;
std::array<uint8_t, max_multioutput_output_size> phase;
std::array<uint16_t, max_multioutput_output_size> pin_phase;
std::array<double, max_multioutput_output_size> est_refs;
std::array<uint32_t, max_multioutput_output_size> cut_index;
bool mapped_multioutput = false;
uint8_t iteration_phase = cut0->supergates[0] == nullptr ? 1 : 0;
/* iterate for each possible match */
for ( auto i = 0; i < cut0->supergates[iteration_phase]->size(); ++i )
{
/* store local validity and comparison info */
bool valid = true;
bool is_best = true;
bool respects_required = true;
double old_flow_sum = 0;
/* iterate for each output of the multi-output gate */
for ( auto j = 0; j < max_multioutput_output_size; ++j )
{
uint32_t node_index = tuple_data[j].node_index;
cut_index[j] = tuple_data[j].cut_index;
auto& node_data = node_match[node_index];
auto const& cut = cuts[node_index][cut_index[j]];
uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0;
supergate<NInputs> const& gate = ( *( cut->supergates[phase_inverted] ) )[i];
/* protection on complicated duplicated nodes to remap to multioutput */
if ( !node_data.same_match )
return false;
/* get the output phase */
pin_phase[j] = gate.polarity;
phase[j] = ( gate.polarity >> NInputs ) ^ phase_inverted;
/* compute arrival */
arrival[j] = 0.0;
auto ctr = 0u;
for ( auto l : cut )
{
double arrival_pin = node_match[l].arrival[( gate.polarity >> ctr ) & 1] + gate.tdelay[ctr];
arrival[j] = std::max( arrival[j], arrival_pin );
++ctr;
}
/* check required time: same_match is true */
if constexpr ( DO_AREA )
{
if ( arrival[j] > node_data.required[phase[j]] + epsilon )
{
valid = false;
break;
}
if ( arrival[j] + lib_inv_delay > node_data.required[phase[j] ^ 1] + epsilon )
{
valid = false;
break;
}
}
/* check required time of the current solution */
if ( node_data.arrival[phase[j]] > node_data.required[phase[j]] )
respects_required = false;
if ( node_data.same_match && node_data.arrival[phase[j] ^ 1] > node_data.required[phase[j] ^ 1] )
respects_required = false;
/* compute area flow */
if ( j == 0 || !node_data.multioutput_match[0] )
{
uint8_t current_phase = node_data.best_gate[0] == nullptr ? 1 : 0;
old_flow_sum += node_data.flows[current_phase];
}
uint8_t old_phase = node_data.phase[phase[j]];
node_data.phase[phase[j]] = gate.polarity;
area[j] = gate.area;
area_flow[j] = gate.area + cut_leaves_flow( cut, n, phase[j] );
node_data.phase[phase[j]] = old_phase;
/* current version may lead to delay increase */
est_refs[j] = node_data.est_refs[phase[j]];
}
/* not better than individual gates */
if ( !valid )
continue;
if constexpr ( !DO_AREA )
{
if ( !is_best )
continue;
}
/* combine evaluation for precise area flow estimantion */
/* compute equation AF(n) = ( Area(G) + |roots| * SUM_{l in leaves} AF(l) ) / SUM_{p in roots} est_refs( p ) */
float flow_sum_pos = 0, flow_sum_neg;
float combined_est_refs = 0;
for ( auto j = 0; j < max_multioutput_output_size; ++j )
{
flow_sum_pos += area_flow[j];
combined_est_refs += est_refs[j];
}
flow_sum_neg = flow_sum_pos;
flow_sum_pos /= combined_est_refs;
/* not better than individual gates */
if ( respects_required && ( flow_sum_pos > old_flow_sum + epsilon ) )
continue;
mapped_multioutput = true;
flow_sum_neg = ( flow_sum_neg + lib_inv_area ) / combined_est_refs;
/* commit multi-output gate */
for ( uint32_t j = 0; j < max_multioutput_output_size; ++j )
{
uint32_t node_index = tuple_data[j].node_index;
auto& node_data = node_match[node_index];
auto const& cut = cuts[node_index][cut_index[j]];
uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0;
supergate<NInputs> const& gate = ( *( cut->supergates[phase_inverted] ) )[i];
uint8_t mapped_phase = phase[j];
node_data.multioutput_match[mapped_phase] = true;
node_data.best_gate[mapped_phase] = &gate;
node_data.best_cut[mapped_phase] = cut_index[j];
node_data.phase[mapped_phase] = pin_phase[j];
node_data.arrival[mapped_phase] = arrival[j];
node_data.area[mapped_phase] = area[j]; /* partial area contribution */
node_data.flows[mapped_phase] = flow_sum_pos;
assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon );
/* select opposite phase */
mapped_phase ^= 1;
node_data.multioutput_match[mapped_phase] = true;
node_data.best_gate[mapped_phase] = nullptr;
node_data.best_cut[mapped_phase] = cut_index[j];
node_data.phase[mapped_phase] = pin_phase[j];
node_data.arrival[mapped_phase] = arrival[j] + lib_inv_delay;
node_data.area[mapped_phase] = area[j]; /* partial area contribution */
node_data.flows[mapped_phase] = flow_sum_neg;
assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon );
}
}
return mapped_multioutput;
}
template<bool SwitchActivity>
bool match_multioutput_exact( node<Ntk> const& n, bool last_round )
{
/* extract outputs tuple */
uint32_t index = ntk.node_to_index( n );
multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0];
/* local values storage */
std::array<float, max_multioutput_output_size> best_exact_area;
for ( int j = max_multioutput_output_size - 1; j >= 0; --j )
{
/* protection on complicated duplicated nodes to remap to multioutput */
if ( !node_match[tuple_data[j].node_index].same_match )
return false;
}
/* if one of the outputs is not referenced, do not use multi-output gate */
if ( last_round )
{
for ( uint32_t j = 0; j < max_multioutput_output_size; ++j )
{
uint32_t node_index = tuple_data[j].node_index;
if ( !node_match[node_index].map_refs[0] && !node_match[node_index].map_refs[1] )
{
return false;
}
}
}
/* if "same match" and used in the cover dereference the leaves (reverse topo order) */
for ( int j = max_multioutput_output_size - 1; j >= 0; --j )
{
uint32_t node_index = tuple_data[j].node_index;
uint8_t selected_phase = node_match[node_index].best_gate[0] == nullptr ? 1 : 0;
if ( node_match[node_index].map_refs[0] || node_match[node_index].map_refs[1] )
{
/* match is always single output here */
auto const& cut = cuts[node_index][node_match[node_index].best_cut[0]];
uint8_t use_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1;
best_exact_area[j] = cut_deref<SwitchActivity>( cut, ntk.index_to_node( node_index ), use_phase );
/* mapping a non referenced phase */
if ( node_match[node_index].map_refs[selected_phase] == 0 )
best_exact_area[j] += lib_inv_area;
}
}
/* perform mapping */
bool mapped_multioutput = false;
mapped_multioutput = match_multioutput_exact_core<SwitchActivity>( tuple_data, best_exact_area );
/* if "same match" and used in the cover reference the leaves (topo order) */
for ( auto j = 0; j < max_multioutput_output_size; ++j )
{
uint32_t node_index = tuple_data[j].node_index;
if ( node_match[node_index].map_refs[0] || node_match[node_index].map_refs[1] )
{
uint8_t use_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1;
auto const& best_cut = cuts[node_index][node_match[node_index].best_cut[use_phase]];
cut_ref<SwitchActivity>( best_cut, ntk.index_to_node( node_index ), use_phase );
}
}
return mapped_multioutput;
}
template<bool SwitchActivity>
inline bool match_multioutput_exact_core( multi_match_t const& tuple_data, std::array<float, max_multioutput_output_size>& best_exact_area )
{
/* get the cut representative */
auto const& cut0 = cuts[tuple_data[0].node_index][tuple_data[0].cut_index];
/* local values storage */
std::array<double, max_multioutput_output_size> arrival;
std::array<float, max_multioutput_output_size> area_exact;
std::array<float, max_multioutput_output_size> area;
std::array<uint8_t, max_multioutput_output_size> phase;
std::array<uint16_t, max_multioutput_output_size> pin_phase;
std::array<uint32_t, max_multioutput_output_size> cut_index;
uint8_t iteration_phase = cut0->supergates[0] == nullptr ? 1 : 0;
bool mapped_multioutput = false;
/* iterate for each possible match */
for ( auto i = 0; i < cut0->supergates[iteration_phase]->size(); ++i )
{
/* store local validity and comparison info */
bool valid = true;
bool is_best = true;
bool respects_required = true;
uint32_t it_counter = 0;
/* iterate for each output of the multi-output gate (reverse topo order) */
for ( int j = max_multioutput_output_size - 1; j >= 0; --j )
{
uint32_t node_index = tuple_data[j].node_index;
cut_index[j] = tuple_data[j].cut_index;
auto& node_data = node_match[node_index];
auto const& cut = cuts[node_index][cut_index[j]];
uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0;
supergate<NInputs> const& gate = ( *( cut->supergates[phase_inverted] ) )[i];
++it_counter;
/* get the output phase and area */
pin_phase[j] = gate.polarity;
phase[j] = ( gate.polarity >> NInputs ) ^ phase_inverted;
area[j] = gate.area;
/* compute arrival */
arrival[j] = 0.0;
auto ctr = 0u;
for ( auto l : cut )
{
double arrival_pin = node_match[l].arrival[( gate.polarity >> ctr ) & 1] + gate.tdelay[ctr];
arrival[j] = std::max( arrival[j], arrival_pin );
++ctr;
}
/* check required time */
if ( arrival[j] > node_data.required[phase[j]] + epsilon )
{
valid = false;
break;
}
if ( arrival[j] + lib_inv_delay > node_data.required[phase[j] ^ 1] + epsilon )
{
valid = false;
break;
}
/* check required time of current solution */
if ( node_data.arrival[phase[j]] > node_data.required[phase[j]] )
respects_required = false;
if ( node_data.arrival[phase[j] ^ 1] > node_data.required[phase[j] ^ 1] )
respects_required = false;
/* compute exact area for match: needed only for the first node (leaves are shared) */
if ( it_counter == 1 )
{
auto old_phase = node_data.phase[phase[j]];
auto old_area = node_data.area[phase[j]];
node_data.phase[phase[j]] = pin_phase[j];
node_data.area[phase[j]] = area[j];
area_exact[j] = cut_measure_mffc<SwitchActivity>( cut, ntk.index_to_node( node_index ), phase[j] );
node_data.phase[phase[j]] = old_phase;
node_data.area[phase[j]] = old_area;
}
else
{
area_exact[j] = area[j];
}
/* Add output inverter cost if mapping a non referenced phase */
if ( node_data.map_refs[phase[j]] == 0 && node_data.map_refs[phase[j] ^ 1] > 0 )
{
area_exact[j] += lib_inv_area;
}
}
/* check quality: TODO add output inverter in the cost if necessary */
float best_exact_area_total = 0;
float area_exact_total = 0;
for ( auto j = 0; j < max_multioutput_output_size; ++j )
{
best_exact_area_total += best_exact_area[j];
area_exact_total += area_exact[j];
}
/* not better than individual gates */
if ( !valid || ( area_exact_total > best_exact_area_total - epsilon && respects_required ) )
{
continue;
}
mapped_multioutput = true;
/* commit multi-output gate (topo order) */
for ( uint32_t j = 0; j < max_multioutput_output_size; ++j )
{
uint32_t node_index = tuple_data[j].node_index;
auto& node_data = node_match[node_index];
auto const& cut = cuts[node_index][cut_index[j]];
uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0;
supergate<NInputs> const& gate = ( *( cut->supergates[phase_inverted] ) )[i];
uint8_t mapped_phase = phase[j];
best_exact_area[j] = area_exact[j];
if ( node_data.map_refs[phase[j]] == 0 && node_data.map_refs[phase[j] ^ 1] > 0 )
{
best_exact_area[j] += lib_inv_area;
}
/* write data */
node_data.multioutput_match[mapped_phase] = true;
node_data.best_gate[mapped_phase] = &gate;
node_data.best_cut[mapped_phase] = cut_index[j];
node_data.phase[mapped_phase] = pin_phase[j];
node_data.arrival[mapped_phase] = arrival[j];
node_data.area[mapped_phase] = area[j]; /* partial area contribution */
node_data.flows[mapped_phase] = area_exact[j]; /* partial exact area contribution */
/* select opposite phase */
mapped_phase ^= 1;
node_data.multioutput_match[mapped_phase] = true;
node_data.best_gate[mapped_phase] = nullptr;
node_data.best_cut[mapped_phase] = cut_index[j];
node_data.phase[mapped_phase] = pin_phase[j];
node_data.arrival[mapped_phase] = arrival[j] + lib_inv_delay;
node_data.area[mapped_phase] = area[j]; /* partial area contribution */
node_data.flows[mapped_phase] = area_exact[j];
assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon );
}
}
return mapped_multioutput;
}
template<bool DO_AREA>
void multi_node_update( node<Ntk> const& n )
{
uint32_t check_index = ntk.node_to_index( n );
multi_match_t const& tuple_data = multi_node_match[node_tuple_match[ntk.node_to_index( n )].index][0];
uint64_t signature = 0;
/* check if a node is in TFI: there is a path of length > 1 */
bool in_tfi = false;
node<Ntk> min_node = n;
for ( auto j = 0; j < max_multioutput_output_size - 1; ++j )
{
if ( tuple_data[j].in_tfi )
{
min_node = ntk.index_to_node( tuple_data[j].node_index );
in_tfi = true;
signature |= UINT64_C( 1 ) << ( tuple_data[j].node_index & 0x3f );
}
}
if ( !in_tfi )
return;
/* recompute data in between: should I mark the leaves? (not necessary under some assumptions) */
ntk.incr_trav_id();
ntk.foreach_fanin( n, [&]( auto const& f ) {
/* TODO: this recursion works as it is for a maximum multioutput value of 2 */
multi_node_update_rec<DO_AREA>( ntk.get_node( f ), min_node + 1, signature );
} );
}
template<bool DO_AREA>
void multi_node_update_rec( node<Ntk> const& n, uint32_t min_index, uint64_t& signature )
{
uint32_t index = ntk.node_to_index( n );
if ( index < min_index )
return;
if ( ntk.visited( n ) == ntk.trav_id() )
return;
ntk.set_visited( n, ntk.trav_id() );
ntk.foreach_fanin( n, [&]( auto const& f ) {
multi_node_update_rec<DO_AREA>( ntk.get_node( f ), min_index, signature );
} );
/* update the node if uses an updated leaf */
auto& node_data = node_match[index];
bool leaf_used = multi_node_update_cut_check( index, signature, 0 );
if ( !node_data.same_match )
leaf_used |= multi_node_update_cut_check( index, signature, 1 );
if ( !leaf_used )
return;
signature |= UINT64_C( 1 ) << ( index & 0x3f );
/* avoid cycles by recomputing arrival times for multi-output gates or decomposing them */
if ( node_data.same_match && node_data.multioutput_match[0] )
{
propagate_arrival_node( n );
/* check required time */
if ( node_data.arrival[0] < node_data.required[0] + epsilon && node_data.arrival[1] < node_data.required[1] + epsilon )
return;
}
/* match positive phase */
match_phase<DO_AREA>( n, 0u );
/* match negative phase */
match_phase<DO_AREA>( n, 1u );
/* try to drop one phase */
match_drop_phase<DO_AREA, false>( n );
assert( node_data.arrival[0] < node_data.required[0] + epsilon );
assert( node_data.arrival[1] < node_data.required[1] + epsilon );
}
template<bool SwitchActivity>
void multi_node_update_exact( node<Ntk> const& n )
{
uint32_t check_index = ntk.node_to_index( n );
multi_match_t const& tuple_data = multi_node_match[node_tuple_match[ntk.node_to_index( n )].index][0];
uint64_t signature = 0;
/* check if a node is in TFI: there is a path of length > 1 */
bool in_tfi = false;
node<Ntk> min_node = n;
for ( auto j = 0; j < max_multioutput_output_size - 1; ++j )
{
if ( tuple_data[j].in_tfi )
{
min_node = ntk.index_to_node( tuple_data[j].node_index );
in_tfi = true;
signature |= UINT64_C( 1 ) << ( tuple_data[j].node_index & 0x3f );
}
}
if ( !in_tfi )
return;
/* recompute data in between: should I mark the leaves? (not necessary under some assumptions) */
ntk.incr_trav_id();
ntk.foreach_fanin( n, [&]( auto const& f ) {
/* TODO: this recursion works as it is for a maximum multioutput value of 2 */
multi_node_update_exact_rec<SwitchActivity>( ntk.get_node( f ), min_node + 1, signature );
} );
}
template<bool SwitchActivity>
void multi_node_update_exact_rec( node<Ntk> const& n, uint32_t min_index, uint64_t& signature )
{
uint32_t index = ntk.node_to_index( n );
if ( index < min_index )
return;
if ( ntk.visited( n ) == ntk.trav_id() )
return;
ntk.set_visited( n, ntk.trav_id() );
ntk.foreach_fanin( n, [&]( auto const& f ) {
multi_node_update_exact_rec<SwitchActivity>( ntk.get_node( f ), min_index, signature );
} );
/* update the node if uses an updated leaf */
auto& node_data = node_match[index];
bool leaf_used = multi_node_update_cut_check( index, signature, 0 );
if ( !node_data.same_match )
leaf_used |= multi_node_update_cut_check( index, signature, 1 );
if ( !leaf_used )
return;
signature |= UINT64_C( 1 ) << ( index & 0x3f );
assert( !node_data.multioutput_match[0] );
assert( !node_data.multioutput_match[1] );
if ( node_data.same_match && ( node_data.map_refs[0] || node_data.map_refs[1] ) )
{
uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1;
auto const& best_cut = cuts[index][node_data.best_cut[use_phase]];
cut_deref<SwitchActivity>( best_cut, n, use_phase );
}
/* match positive phase */
match_phase_exact<SwitchActivity>( n, 0u );
/* match negative phase */
match_phase_exact<SwitchActivity>( n, 1u );
/* try to drop one phase */
match_drop_phase<true, true>( n );
assert( node_data.arrival[0] < std::numeric_limits<float>::max() );
assert( node_data.arrival[1] < std::numeric_limits<float>::max() );
}
inline void match_multioutput_propagate_required( node<Ntk> const& n )
{
/* extract outputs tuple */
uint32_t index = ntk.node_to_index( n );
multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0];
for ( int j = max_multioutput_output_size - 1; j >= 0; --j )
{
const auto node_index = tuple_data[j].node_index;
match_propagate_required( node_index );
}
}
void match_multi_add_cuts( node<Ntk> const& n )
{
/* assume a single cut (current version) */
uint32_t index = ntk.node_to_index( n );
multi_match_t& matches = multi_node_match[node_tuple_match[index].index][0];
/* find the corresponding cut */
uint32_t cut_p = 0;
while ( matches[cut_p].node_index != index )
++cut_p;
assert( cut_p < matches.size() );
uint32_t cut_index = matches[cut_p].cut_index;
auto& cut = multi_cut_set[cut_index][cut_p];
auto single_cut = multi_cut_set[cut_index][cut_p];
auto& rcuts = cuts[index];
/* not enough space in the data structure: abort */
if ( rcuts.size() == max_cut_num )
{
match_multi_add_cuts_remove_entry( matches );
return;
}
/* insert single cut variation if unique (for delay preservation) */
if ( !rcuts.is_contained( single_cut ) )
{
single_cut->pattern_index = 0;
compute_cut_data( single_cut, ntk.index_to_node( index ) );
rcuts.append_cut( single_cut );
/* not enough space in the data structure: abort */
if ( rcuts.size() == max_cut_num )
{
rcuts.limit( rcuts.size() - 1 );
match_multi_add_cuts_remove_entry( matches );
return;
}
}
/* add multi-output cut */
uint32_t num_cuts_pre = rcuts.size();
cut->ignore = true;
rcuts.append_cut( cut );
uint32_t num_cuts_after = rcuts.size();
assert( num_cuts_after == num_cuts_pre + 1 );
rcuts.limit( num_cuts_pre );
/* update tuple data */
matches[cut_p].cut_index = num_cuts_pre;
}
inline void match_multi_add_cuts_remove_entry( multi_match_t const& matches )
{
/* reset matches */
for ( multi_match_data const& entry : matches )
{
node_tuple_match[entry.node_index].data = 0;
}
}
inline bool multi_node_update_cut_check( uint32_t index, uint64_t signature, uint8_t phase )
{
auto const& cut = cuts[index][node_match[index].best_cut[phase]];
if ( ( signature & cut.signature() ) > 0 )
return true;
return false;
}
#pragma endregion
#pragma region Mapping utils
inline double cut_leaves_flow( cut_t const& cut, node<Ntk> const& n, uint8_t phase )
{
double flow{ 0.0f };
auto const& node_data = node_match[ntk.node_to_index( n )];
uint8_t ctr = 0u;
for ( auto leaf : cut )
{
uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1;
flow += node_match[leaf].flows[leaf_phase];
}
return flow;
}
template<bool SwitchActivity>
float cut_ref( cut_t const& cut, node<Ntk> const& n, uint8_t phase )
{
auto const& node_data = node_match[ntk.node_to_index( n )];
float count;
if constexpr ( SwitchActivity )
count = switch_activity[ntk.node_to_index( n )];
else
count = node_data.area[phase];
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( n ) )
{
return count;
}
}
uint8_t ctr = 0;
for ( auto leaf : cut )
{
/* compute leaf phase using the current gate */
uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1;
if ( ntk.is_constant( ntk.index_to_node( leaf ) ) )
{
continue;
}
else if ( ntk.is_pi( ntk.index_to_node( leaf ) ) )
{
/* reference PIs, add inverter cost for negative phase */
if ( leaf_phase == 1u )
{
if ( node_match[leaf].map_refs[1]++ == 0u )
{
if constexpr ( SwitchActivity )
count += switch_activity[leaf];
else
count += lib_inv_area;
}
}
else
{
++node_match[leaf].map_refs[0];
}
continue;
}
if ( node_match[leaf].same_match )
{
/* Recursive referencing if leaf was not referenced */
if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] )
{
auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]];
count += cut_ref<SwitchActivity>( best_cut, ntk.index_to_node( leaf ), leaf_phase );
}
/* Add inverter area if not present yet and leaf node is implemented in the opposite phase */
if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr )
{
if constexpr ( SwitchActivity )
count += switch_activity[leaf];
else
count += lib_inv_area;
}
}
else
{
if ( node_match[leaf].map_refs[leaf_phase]++ == 0u )
{
auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]];
count += cut_ref<SwitchActivity>( best_cut, ntk.index_to_node( leaf ), leaf_phase );
}
}
}
return count;
}
template<bool SwitchActivity>
float cut_deref( cut_t const& cut, node<Ntk> const& n, uint8_t phase )
{
auto const& node_data = node_match[ntk.node_to_index( n )];
float count;
if constexpr ( SwitchActivity )
count = switch_activity[ntk.node_to_index( n )];
else
count = node_data.area[phase];
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( n ) )
{
return count;
}
}
uint8_t ctr = 0;
for ( auto leaf : cut )
{
/* compute leaf phase using the current gate */
uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1;
if ( ntk.is_constant( ntk.index_to_node( leaf ) ) )
{
continue;
}
else if ( ntk.is_pi( ntk.index_to_node( leaf ) ) )
{
/* dereference PIs, add inverter cost for negative phase */
if ( leaf_phase == 1u )
{
if ( --node_match[leaf].map_refs[1] == 0u )
{
if constexpr ( SwitchActivity )
count += switch_activity[leaf];
else
count += lib_inv_area;
}
}
else
{
--node_match[leaf].map_refs[0];
}
continue;
}
if ( node_match[leaf].same_match )
{
/* Add inverter area if it is used only by the current gate and leaf node is implemented in the opposite phase */
if ( --node_match[leaf].map_refs[leaf_phase] == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr )
{
if constexpr ( SwitchActivity )
count += switch_activity[leaf];
else
count += lib_inv_area;
}
/* Recursive dereferencing */
if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] )
{
auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]];
count += cut_deref<SwitchActivity>( best_cut, ntk.index_to_node( leaf ), leaf_phase );
}
}
else
{
if ( --node_match[leaf].map_refs[leaf_phase] == 0u )
{
auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]];
count += cut_deref<SwitchActivity>( best_cut, ntk.index_to_node( leaf ), leaf_phase );
}
}
}
return count;
}
template<bool SwitchActivity>
float cut_measure_mffc( cut_t const& cut, node<Ntk> const& n, uint8_t phase )
{
tmp_visited.clear();
float count = cut_ref_visit<SwitchActivity>( cut, n, phase );
/* dereference visited */
for ( auto s : tmp_visited )
{
uint32_t leaf = s >> 1;
--node_match[leaf].map_refs[s & 1];
}
return count;
}
template<bool SwitchActivity>
float cut_ref_visit( cut_t const& cut, node<Ntk> const& n, uint8_t phase )
{
auto const& node_data = node_match[ntk.node_to_index( n )];
float count;
if constexpr ( SwitchActivity )
count = switch_activity[ntk.node_to_index( n )];
else
count = node_data.area[phase];
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( n ) )
{
return count;
}
}
uint8_t ctr = 0;
for ( auto leaf : cut )
{
/* compute leaf phase using the current gate */
uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1;
if ( ntk.is_constant( ntk.index_to_node( leaf ) ) )
{
continue;
}
/* add to visited */
tmp_visited.push_back( ( static_cast<uint64_t>( leaf ) << 1 ) | leaf_phase );
if ( ntk.is_pi( ntk.index_to_node( leaf ) ) )
{
/* reference PIs, add inverter cost for negative phase */
if ( leaf_phase == 1u )
{
if ( node_match[leaf].map_refs[1]++ == 0u )
{
if constexpr ( SwitchActivity )
count += switch_activity[leaf];
else
count += lib_inv_area;
}
}
else
{
++node_match[leaf].map_refs[0];
}
continue;
}
if ( node_match[leaf].same_match )
{
/* Recursive referencing if leaf was not referenced */
if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] )
{
auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]];
count += cut_ref_visit<SwitchActivity>( best_cut, ntk.index_to_node( leaf ), leaf_phase );
}
/* Add inverter area if not present yet and leaf node is implemented in the opposite phase */
if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr )
{
if constexpr ( SwitchActivity )
count += switch_activity[leaf];
else
count += lib_inv_area;
}
}
else
{
if ( node_match[leaf].map_refs[leaf_phase]++ == 0u )
{
auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]];
count += cut_ref_visit<SwitchActivity>( best_cut, ntk.index_to_node( leaf ), leaf_phase );
}
}
}
return count;
}
#pragma endregion
#pragma region Initialize and dump the mapped network
void insert_buffers()
{
if ( lib_buf_id != UINT32_MAX )
{
double area_old = area;
bool buffers = false;
ntk.foreach_po( [&]( auto const& f ) {
auto const& n = ntk.get_node( f );
if ( !ntk.is_constant( n ) && ntk.is_pi( n ) && !ntk.is_complemented( f ) )
{
area += lib_buf_area;
delay = std::max( delay, node_match[ntk.node_to_index( n )].arrival[0] + lib_inv_delay );
buffers = true;
}
} );
/* round stats */
if ( ps.verbose && buffers )
{
std::stringstream stats{};
float area_gain = 0.0f;
area_gain = float( ( area_old - area ) / area_old * 100 );
stats << fmt::format( "[i] Buffering: Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) );
st.round_stats.push_back( stats.str() );
}
}
}
std::pair<binding_view<klut_network>, klut_map> initialize_map_network()
{
binding_view<klut_network> dest( library.get_gates() );
klut_map old2new;
old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false );
old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true );
ntk.foreach_pi( [&]( auto const& n ) {
old2new[ntk.node_to_index( n )][0] = dest.create_pi();
} );
return { dest, old2new };
}
std::pair<cell_view<block_network>, block_map> initialize_block_network()
{
cell_view<block_network> dest( library.get_cells() );
block_map old2new;
old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false );
old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true );
ntk.foreach_pi( [&]( auto const& n ) {
old2new[ntk.node_to_index( n )][0] = dest.create_pi();
} );
return { dest, old2new };
}
void init_topo_order()
{
topo_order.reserve( ntk.size() );
if ( multi_node_match.size() > 0 )
{
multi_init_topo_order();
return;
}
topo_view<Ntk>( ntk ).foreach_node( [this]( auto n ) {
topo_order.push_back( n );
} );
}
bool init_arrivals()
{
if ( ps.required_times.size() && ps.required_times.size() != ntk.num_pos() )
{
std::cerr << "[e] MAP ERROR: required time vector does not match the output size of the network" << std::endl;
st.mapping_error = true;
return false;
}
if ( ps.arrival_times.empty() )
{
ntk.foreach_pi( [&]( auto const& n ) {
auto& node_data = node_match[ntk.node_to_index( n )];
node_data.arrival[0] = node_data.best_alternative[0].arrival = 0;
node_data.arrival[1] = node_data.best_alternative[1].arrival = lib_inv_delay;
} );
return true;
}
if ( ps.arrival_times.size() != ntk.num_pis() )
{
std::cerr << "[e] MAP ERROR: arrival time vector does not match the input size of the network" << std::endl;
st.mapping_error = true;
return false;
}
ntk.foreach_pi( [&]( auto const& n, uint32_t i ) {
auto& node_data = node_match[ntk.node_to_index( n )];
node_data.arrival[0] = node_data.best_alternative[0].arrival = ps.arrival_times[i];
node_data.arrival[1] = node_data.best_alternative[1].arrival = ps.arrival_times[i] + lib_inv_delay;
} );
return true;
}
void finalize_cover( binding_view<klut_network>& res, klut_map& old2new )
{
uint32_t multioutput_count = 0;
for ( auto const& n : topo_order )
{
auto index = ntk.node_to_index( n );
auto const& node_data = node_match[index];
/* add inverter at PI if needed */
if ( ntk.is_constant( n ) )
{
if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr )
continue;
}
else if ( ntk.is_pi( n ) )
{
if ( node_data.map_refs[1] > 0 )
{
old2new[index][1] = res.create_not( old2new[n][0] );
res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id );
}
continue;
}
/* continue if cut is not in the cover */
if ( !node_data.map_refs[0] && !node_data.map_refs[1] )
continue;
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( n ) )
{
clone_box( res, old2new, index );
continue;
}
}
unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1;
/* add used cut */
if ( node_data.same_match || node_data.map_refs[phase] > 0 )
{
create_lut_for_gate( res, old2new, index, phase );
/* add inverted version if used */
if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 )
{
old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] );
res.add_binding( res.get_node( old2new[index][phase ^ 1] ), lib_inv_id );
}
/* count multioutput gates */
if ( ps.map_multioutput && node_tuple_match[index].lowest_index && node_data.multioutput_match[phase] )
{
++multioutput_count;
}
}
phase = phase ^ 1;
/* add the optional other match if used */
if ( !node_data.same_match && node_data.map_refs[phase] > 0 )
{
create_lut_for_gate( res, old2new, index, phase );
/* count multioutput gates */
if ( ps.map_multioutput && node_tuple_match[index].lowest_index && node_data.multioutput_match[phase] )
{
++multioutput_count;
}
}
st.multioutput_gates = multioutput_count;
}
/* create POs */
ntk.foreach_po( [&]( auto const& f ) {
if ( ntk.is_complemented( f ) )
{
res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] );
}
else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_pi( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX )
{
/* create buffers for POs */
static uint64_t _buf = 0x2;
kitty::dynamic_truth_table tt_buf( 1 );
kitty::create_from_words( tt_buf, &_buf, &_buf + 1 );
const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf );
res.create_po( buf );
res.add_binding( res.get_node( buf ), lib_buf_id );
}
else
{
res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] );
}
} );
/* write final results */
st.area = area;
st.delay = delay;
if ( ps.eswp_rounds )
st.power = compute_switching_power();
}
void finalize_cover_block( cell_view<block_network>& res, block_map& old2new )
{
uint32_t multioutput_count = 0;
/* get standard cells */
std::vector<standard_cell> const& lib = res.get_library();
/* get translation ID from GENLIB to STD_CELL */
std::vector<uint32_t> genlib_to_cell( library.get_gates().size() );
for ( standard_cell const& cell : lib )
{
for ( gate const& g : cell.gates )
{
genlib_to_cell[g.id] = cell.id;
}
}
for ( auto const& n : topo_order )
{
auto index = ntk.node_to_index( n );
auto const& node_data = node_match[index];
/* add inverter at PI if needed */
if ( ntk.is_constant( n ) )
{
if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr )
continue;
}
else if ( ntk.is_pi( n ) )
{
if ( node_data.map_refs[1] > 0 )
{
old2new[index][1] = res.create_not( old2new[n][0] );
res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell[lib_inv_id] );
}
continue;
}
/* continue if cut is not in the cover */
if ( !node_data.map_refs[0] && !node_data.map_refs[1] )
continue;
/* don't touch box */
if constexpr ( has_is_dont_touch_v<Ntk> )
{
if ( ntk.is_dont_touch( n ) )
{
clone_box2( res, old2new, index, genlib_to_cell );
continue;
}
}
unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1;
/* add used cut */
if ( node_data.same_match || node_data.map_refs[phase] > 0 )
{
/* create multioutput gates */
if ( ps.map_multioutput && node_data.multioutput_match[phase] )
{
assert( node_data.same_match == true );
if ( node_tuple_match[index].has_info && node_tuple_match[index].lowest_index )
{
++multioutput_count;
create_block_for_gate( res, old2new, index, phase, genlib_to_cell );
}
continue;
}
create_lut_for_gate2( res, old2new, index, phase, genlib_to_cell );
/* add inverted version if used */
if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 )
{
old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] );
res.add_cell( res.get_node( old2new[index][phase ^ 1] ), genlib_to_cell[lib_inv_id] );
}
}
phase = phase ^ 1;
/* add the optional other match if used */
if ( !node_data.same_match && node_data.map_refs[phase] > 0 )
{
assert( !ps.map_multioutput || !node_data.multioutput_match[phase] );
create_lut_for_gate2( res, old2new, index, phase, genlib_to_cell );
}
}
/* create POs */
ntk.foreach_po( [&]( auto const& f ) {
if ( ntk.is_complemented( f ) )
{
res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] );
}
else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_pi( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX )
{
/* create buffers for POs */
static uint64_t _buf = 0x2;
kitty::dynamic_truth_table tt_buf( 1 );
kitty::create_from_words( tt_buf, &_buf, &_buf + 1 );
const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf );
res.create_po( buf );
res.add_cell( res.get_node( buf ), genlib_to_cell[lib_buf_id] );
}
else
{
res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] );
}
} );
/* write final results */
st.area = area;
st.delay = delay;
st.multioutput_gates = multioutput_count;
if ( ps.eswp_rounds )
st.power = compute_switching_power();
}
void create_lut_for_gate( binding_view<klut_network>& res, klut_map& old2new, uint32_t index, unsigned phase )
{
auto const& node_data = node_match[index];
auto const& best_cut = cuts[index][node_data.best_cut[phase]];
auto const& gate = node_data.best_gate[phase]->root;
/* permutate and negate to obtain the matched gate truth table */
std::vector<signal<klut_network>> children( gate->num_vars );
auto ctr = 0u;
for ( auto l : best_cut )
{
if ( ctr >= gate->num_vars )
break;
children[node_data.best_gate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1];
++ctr;
}
if ( !gate->is_super )
{
/* create the node */
auto f = res.create_node( children, gate->function );
res.add_binding( res.get_node( f ), gate->root->id );
/* add the node in the data structure */
old2new[index][phase] = f;
}
else
{
/* supergate, create sub-gates */
auto f = create_lut_for_gate_rec( res, *gate, children );
/* add the node in the data structure */
old2new[index][phase] = f;
}
}
signal<klut_network> create_lut_for_gate_rec( binding_view<klut_network>& res, composed_gate<NInputs> const& gate, std::vector<signal<klut_network>> const& children )
{
std::vector<signal<klut_network>> children_local( gate.fanin.size() );
auto i = 0u;
for ( auto const fanin : gate.fanin )
{
if ( fanin->root == nullptr )
{
/* terminal condition */
children_local[i] = children[fanin->id];
}
else
{
children_local[i] = create_lut_for_gate_rec( res, *fanin, children );
}
++i;
}
auto f = res.create_node( children_local, gate.root->function );
res.add_binding( res.get_node( f ), gate.root->id );
return f;
}
void create_lut_for_gate2( cell_view<block_network>& res, block_map& old2new, uint32_t index, unsigned phase, std::vector<uint32_t> const& genlib_to_cell )
{
auto const& node_data = node_match[index];
auto const& best_cut = cuts[index][node_data.best_cut[phase]];
auto const& gate = node_data.best_gate[phase]->root;
/* permutate and negate to obtain the matched gate truth table */
std::vector<signal<block_network>> children( gate->num_vars );
auto ctr = 0u;
for ( auto l : best_cut )
{
if ( ctr >= gate->num_vars )
break;
children[node_data.best_gate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1];
++ctr;
}
if ( !gate->is_super )
{
/* create the node */
auto f = res.create_node( children, gate->function );
res.add_cell( res.get_node( f ), genlib_to_cell.at( gate->root->id ) );
/* add the node in the data structure */
old2new[index][phase] = f;
}
else
{
/* supergate, create sub-gates */
auto f = create_lut_for_gate2_rec( res, *gate, children, genlib_to_cell );
/* add the node in the data structure */
old2new[index][phase] = f;
}
}
signal<block_network> create_lut_for_gate2_rec( cell_view<block_network>& res, composed_gate<NInputs> const& gate, std::vector<signal<block_network>> const& children, std::vector<uint32_t> const& genlib_to_cell )
{
std::vector<signal<block_network>> children_local( gate.fanin.size() );
auto i = 0u;
for ( auto const fanin : gate.fanin )
{
if ( fanin->root == nullptr )
{
/* terminal condition */
children_local[i] = children[fanin->id];
}
else
{
children_local[i] = create_lut_for_gate2_rec( res, *fanin, children, genlib_to_cell );
}
++i;
}
auto f = res.create_node( children_local, gate.root->function );
res.add_cell( res.get_node( f ), genlib_to_cell.at( gate.root->id ) );
return f;
}
void create_block_for_gate( cell_view<block_network>& res, block_map& old2new, uint32_t index, unsigned phase, std::vector<uint32_t> const& genlib_to_cell )
{
std::vector<standard_cell> const& lib = res.get_library();
composed_gate<NInputs> const* local_gate = node_match[index].best_gate[phase]->root;
standard_cell const& cell = lib[genlib_to_cell.at( local_gate->root->id )];
assert( !local_gate->is_super );
auto const& best_cut = cuts[index][node_match[index].best_cut[phase]];
/* permutate and negate to obtain the matched gate truth table */
std::vector<signal<block_network>> children( cell.gates.front().num_vars );
/* output negations have already been assigned by the mapper */
auto ctr = 0u;
for ( auto l : best_cut )
{
if ( ctr >= local_gate->num_vars )
break;
children[node_match[index].best_gate[phase]->permutation[ctr]] = old2new[l][( node_match[index].phase[phase] >> ctr ) & 1];
++ctr;
}
multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0];
std::vector<uint32_t> outputs;
std::vector<kitty::dynamic_truth_table> functions;
/* re-order outputs to match the ones of the cell */
for ( gate const& g : cell.gates )
{
/* find the correct node */
for ( auto j = 0; j < max_multioutput_output_size; ++j )
{
uint32_t node_index = tuple_data[j].node_index;
assert( node_match[node_index].same_match );
uint8_t node_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1;
assert( node_match[node_index].multioutput_match[node_phase] );
gate const* node_gate = node_match[node_index].best_gate[node_phase]->root->root;
/* wrong output */
if ( node_gate->id != g.id )
continue;
outputs.push_back( node_index );
functions.push_back( g.function );
}
}
assert( outputs.size() == cell.gates.size() );
/* create the block */
auto f = res.create_node( children, functions );
res.add_cell( res.get_node( f ), genlib_to_cell.at( local_gate->root->id ) );
for ( uint32_t s : outputs )
{
/* add inverted version if used */
uint8_t node_phase = node_match[s].best_gate[0] != nullptr ? 0 : 1;
assert( node_match[s].same_match );
/* add the node in the data structure */
old2new[s][node_phase] = f;
if ( node_match[s].map_refs[node_phase ^ 1] > 0 )
{
old2new[s][node_phase ^ 1] = res.create_not( f );
res.add_cell( res.get_node( old2new[s][node_phase ^ 1] ), genlib_to_cell.at( lib_inv_id ) );
}
f = res.next_output_pin( f );
}
}
void clone_box( binding_view<klut_network>& res, klut_map& old2new, uint32_t index )
{
node<Ntk> n = ntk.index_to_node( index );
std::vector<signal<klut_network>> children;
ntk.foreach_fanin( n, [&]( auto const& f ) {
children.push_back( old2new[ntk.get_node( f )][ntk.is_complemented( f ) ? 1 : 0] );
} );
/* create the node */
auto const& tt = ntk.node_function( n );
auto f = res.create_node( children, tt );
/* add the node in the data structure */
old2new[index][0] = f;
if ( node_match[index].map_refs[1] )
{
old2new[index][1] = res.create_not( f );
res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id );
}
if constexpr ( has_has_binding_v<Ntk> )
{
if ( ntk.has_binding( n ) )
res.add_binding( res.get_node( f ), ntk.get_binding_index( n ) );
}
}
void clone_box2( cell_view<block_network>& res, klut_map& old2new, uint32_t index, std::vector<uint32_t> const& genlib_to_cell )
{
node<Ntk> n = ntk.index_to_node( index );
std::vector<signal<block_network>> children;
ntk.foreach_fanin( n, [&]( auto const& f ) {
children.push_back( old2new[ntk.get_node( f )][ntk.is_complemented( f ) ? 1 : 0] );
} );
/* check if multi-output */
std::vector<standard_cell> const& lib = res.get_library();
if constexpr ( has_has_binding_v<Ntk> )
{
bool is_multioutput = false;
if ( ntk.has_binding( n ) )
{
uint32_t cell_id = genlib_to_cell.at( ntk.get_binding_index( n ) );
if ( lib.at( cell_id ).gates.size() > 1 )
is_multioutput = true;
}
/* create the multioutput node (partially dangling) */
if ( is_multioutput )
{
standard_cell const& cell = lib.at( genlib_to_cell.at( ntk.get_binding_index( n ) ) );
std::vector<kitty::dynamic_truth_table> functions;
for ( auto const& g : cell.gates )
{
functions.push_back( g.function );
}
auto f = res.create_node( children, functions );
/* find and connect the correct pin */
for ( auto const& g : cell.gates )
{
if ( g.id == cell.id )
break;
res.next_output_pin( f );
}
old2new[index][0] = f;
res.add_cell( res.get_node( f ), cell.id );
if ( node_match[index].map_refs[1] )
{
old2new[index][1] = res.create_not( f );
res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell.at( lib_inv_id ) );
}
return;
}
}
/* create the single-output node */
auto const& tt = ntk.node_function( n );
auto f = res.create_node( children, tt );
/* add the node in the data structure */
old2new[index][0] = f;
if ( node_match[index].map_refs[1] )
{
old2new[index][1] = res.create_not( f );
res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell.at( lib_inv_id ) );
}
if constexpr ( has_has_binding_v<Ntk> )
{
if ( ntk.has_binding( n ) )
res.add_cell( res.get_node( f ), genlib_to_cell.at( ntk.get_binding_index( n ) ) );
}
}
#pragma endregion
#pragma region Cuts and matching utils
void compute_cut_data( cut_t& cut, node<Ntk> const& n )
{
cut->delay = std::numeric_limits<uint32_t>::max();
cut->flow = std::numeric_limits<float>::max();
cut->ignore = false;
if ( cut.size() > NInputs || cut.size() > 6 )
{
/* Ignore cuts too big to be mapped using the library */
cut->ignore = true;
return;
}
const auto tt = cut->function;
const kitty::static_truth_table<6> fe = kitty::extend_to<6>( tt );
auto fe_canon = fe;
uint16_t negations_pos = 0;
uint16_t negations_neg = 0;
/* match positive polarity */
if constexpr ( Configuration == classification_type::p_configurations )
{
auto canon = kitty::exact_n_canonization_support( fe, cut.size() );
fe_canon = std::get<0>( canon );
negations_pos = std::get<1>( canon );
}
auto const supergates_pos = library.get_supergates( fe_canon );
/* match negative polarity */
if constexpr ( Configuration == classification_type::p_configurations )
{
auto canon = kitty::exact_n_canonization_support( ~fe, cut.size() );
fe_canon = std::get<0>( canon );
negations_neg = std::get<1>( canon );
}
else
{
fe_canon = ~fe;
}
auto const supergates_neg = library.get_supergates( fe_canon );
if ( supergates_pos != nullptr || supergates_neg != nullptr )
{
cut->supergates = { supergates_pos, supergates_neg };
cut->negations = { negations_pos, negations_neg };
}
else
{
/* Ignore not matched cuts */
cut->ignore = true;
return;
}
/* compute cut cost based on LUT area */
recompute_cut_data( cut, n );
}
void compute_cut_data_structural( cut_t& cut, node<Ntk> const& n )
{
cut->delay = std::numeric_limits<uint32_t>::max();
cut->flow = std::numeric_limits<float>::max();
cut->ignore = false;
assert( cut.size() <= NInputs );
const auto supergates_pos = library.get_supergates_pattern( cut->pattern_index, false );
const auto supergates_neg = library.get_supergates_pattern( cut->pattern_index, true );
if ( supergates_pos != nullptr || supergates_neg != nullptr )
{
cut->supergates = { supergates_pos, supergates_neg };
}
else
{
/* Ignore not matched cuts */
cut->ignore = true;
return;
}
/* compute cut cost based on LUT area */
recompute_cut_data( cut, n );
}
void recompute_cut_data( cut_t& cut, node<Ntk> const& n )
{
/* compute cut cost based on LUT area */
uint32_t best_arrival = 0;
float best_area_flow = cut.size() > 1 ? cut.size() : 0;
for ( auto leaf : cut )
{
const auto& best_leaf_cut = cuts[leaf][0];
best_arrival = std::max( best_arrival, best_leaf_cut->delay );
best_area_flow += best_leaf_cut->flow;
}
cut->delay = best_arrival + ( cut.size() > 1 ) ? 1 : 0;
cut->flow = best_area_flow / ntk.fanout_size( n );
}
/* compute positions of leave indices in cut `sub` (subset) with respect to
* leaves in cut `sup` (super set).
*
* Example:
* compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4}
*/
void compute_truth_table_support( cut_t const& sub, cut_t const& sup, TT& tt )
{
size_t j = 0;
auto itp = sup.begin();
for ( auto i : sub )
{
itp = std::find( itp, sup.end(), i );
lsupport[j++] = static_cast<uint8_t>( std::distance( sup.begin(), itp ) );
}
/* swap variables in the truth table */
for ( int i = j - 1; i >= 0; --i )
{
assert( i <= lsupport[i] );
kitty::swap_inplace( tt, i, lsupport[i] );
}
}
void add_zero_cut( uint32_t index )
{
auto& cut = cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */
cut->ignore = true;
cut->delay = 0;
cut->flow = 0;
cut->pattern_index = 0;
cut->negations[0] = cut->negations[1] = 0;
}
void add_unit_cut( uint32_t index )
{
auto& cut = cuts[index].add_cut( &index, &index + 1 );
kitty::create_nth_var( cut->function, 0 );
cut->ignore = true;
cut->delay = 0;
cut->flow = 0;
cut->pattern_index = 1;
cut->negations[0] = cut->negations[1] = 0;
}
inline void create_structural_cut( cut_t& new_cut, std::vector<cut_t const*> const& vcuts, uint32_t new_pattern, uint32_t pattern_id1, uint32_t pattern_id2 )
{
new_cut.set_leaves( *vcuts[0] );
new_cut.add_leaves( vcuts[1]->begin(), vcuts[1]->end() );
new_cut->pattern_index = new_pattern;
/* get the polarity of the leaves of the new cut */
uint16_t neg_l = 0, neg_r = 0;
if ( ( *vcuts[0] )->pattern_index == 1 )
{
neg_r = static_cast<uint16_t>( pattern_id1 & 1 );
}
else
{
neg_r = ( *vcuts[0] )->negations[0];
}
if ( ( *vcuts[1] )->pattern_index == 1 )
{
neg_l = static_cast<uint16_t>( pattern_id2 & 1 );
}
else
{
neg_l = ( *vcuts[1] )->negations[0];
}
new_cut->negations[0] = ( neg_l << vcuts[0]->size() ) | neg_r;
new_cut->negations[1] = new_cut->negations[0];
}
inline bool fast_support_minimization( TT const& tt, cut_t& res )
{
uint32_t support = 0u;
uint32_t support_size = 0u;
for ( uint32_t i = 0u; i < tt.num_vars(); ++i )
{
if ( kitty::has_var( tt, i ) )
{
support |= 1u << i;
++support_size;
}
}
/* has not minimized support? */
if ( ( support & ( support + 1u ) ) != 0u )
{
return false;
}
/* variables not in the support are the most significative */
if ( support_size != res.size() )
{
std::vector<uint32_t> leaves( res.begin(), res.begin() + support_size );
res.set_leaves( leaves.begin(), leaves.end() );
}
return true;
}
void compute_truth_table( uint32_t index, fanin_cut_t const& vcuts, uint32_t fanin, cut_t& res )
{
for ( uint32_t i = 0; i < fanin; ++i )
{
cut_t const* cut = vcuts[i];
ltruth[i] = ( *cut )->function;
compute_truth_table_support( *cut, res, ltruth[i] );
}
auto tt_res = ntk.compute( ntk.index_to_node( index ), ltruth.begin(), ltruth.begin() + fanin );
if ( ps.cut_enumeration_ps.minimize_truth_table && !fast_support_minimization( tt_res, res ) )
{
const auto support = kitty::min_base_inplace( tt_res );
std::vector<uint32_t> leaves_before( res.begin(), res.end() );
std::vector<uint32_t> leaves_after( support.size() );
auto it_support = support.begin();
auto it_leaves = leaves_after.begin();
while ( it_support != support.end() )
{
*it_leaves++ = leaves_before[*it_support++];
}
res.set_leaves( leaves_after.begin(), leaves_after.end() );
}
res->function = tt_res;
}
#pragma endregion
template<bool DO_AREA>
inline bool compare_map( double arrival, double best_arrival, float area_flow, float best_area_flow, uint32_t size, uint32_t best_size )
{
if constexpr ( DO_AREA )
{
if ( area_flow < best_area_flow - epsilon )
{
return true;
}
else if ( area_flow > best_area_flow + epsilon )
{
return false;
}
else if ( arrival < best_arrival - epsilon )
{
return true;
}
else if ( arrival > best_arrival + epsilon )
{
return false;
}
return size < best_size;
}
else
{
if ( arrival < best_arrival - epsilon )
{
return true;
}
else if ( arrival > best_arrival + epsilon )
{
return false;
}
else if ( area_flow < best_area_flow - epsilon )
{
return true;
}
else if ( area_flow > best_area_flow + epsilon )
{
return false;
}
return size < best_size;
}
}
double compute_switching_power()
{
double power = 0.0f;
for ( auto const& n : topo_order )
{
const auto index = ntk.node_to_index( n );
auto& node_data = node_match[index];
if ( ntk.is_constant( n ) )
{
if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr )
continue;
}
else if ( ntk.is_pi( n ) )
{
if ( node_data.map_refs[1] > 0 )
power += switch_activity[ntk.node_to_index( n )];
continue;
}
/* continue if cut is not in the cover */
if ( !node_data.map_refs[0] && !node_data.map_refs[1] )
continue;
unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1;
if ( node_data.same_match || node_data.map_refs[phase] > 0 )
{
power += switch_activity[ntk.node_to_index( n )];
if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 )
power += switch_activity[ntk.node_to_index( n )];
}
phase = phase ^ 1;
if ( !node_data.same_match && node_data.map_refs[phase] > 0 )
{
power += switch_activity[ntk.node_to_index( n )];
}
}
return power;
}
#pragma region multioutput
/* Experimental code */
void compute_multioutput_match()
{
stopwatch t( st.time_multioutput );
if ( library.num_multioutput_gates() == 0 )
return;
/* compute cuts: first simple method without proper matching */
cut_enumeration_params multi_ps;
multi_ps.minimize_truth_table = false;
multi_cuts_t multi_cuts = fast_cut_enumeration<Ntk, max_multioutput_cut_size, true, cut_enumeration_emap_multi_cut>( ntk, multi_ps );
/* cuts leaves classes */
multi_hash_t multi_cuts_classes;
multi_cuts_classes.reserve( 2000 );
/* Multi-output matching */
multi_enumerate_matches( multi_cuts, multi_cuts_classes );
multi_single_matches_t multi_node_match_local;
multi_node_match_local.reserve( multi_cuts_classes.size() );
multi_compute_matches( multi_cuts, multi_cuts_classes, multi_node_match_local );
if ( ps.remove_overlapping_multicuts )
multi_filter_and_match<true>( multi_cuts, multi_node_match_local ); /* it also adds the tuple for node mapping */
else
multi_filter_and_match<false>( multi_cuts, multi_node_match_local ); /* it also adds the tuple for node mapping */
}
void multi_init_topo_order()
{
/* create and initialize a choice view to store the tuples */
choice_view<Ntk> choice_ntk{ ntk };
multi_add_choices( choice_ntk );
ntk.incr_trav_id();
ntk.incr_trav_id();
/* add constants and CIs */
const auto c0 = ntk.get_node( ntk.get_constant( false ) );
topo_order.push_back( c0 );
ntk.set_visited( c0, ntk.trav_id() );
if ( const auto c1 = ntk.get_node( ntk.get_constant( true ) ); ntk.visited( c1 ) != ntk.trav_id() )
{
topo_order.push_back( c1 );
ntk.set_visited( c1, ntk.trav_id() );
}
ntk.foreach_ci( [&]( auto const& n ) {
if ( ntk.visited( n ) != ntk.trav_id() )
{
topo_order.push_back( n );
ntk.set_visited( n, ntk.trav_id() );
}
} );
/* sort topologically */
ntk.foreach_co( [&]( auto const& f ) {
if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() )
return;
multi_topo_sort_rec( choice_ntk, ntk.get_node( f ) );
} );
}
/* Experimental code resticted to only half adders and full adders */
void multi_enumerate_matches( multi_cuts_t const& multi_cuts, multi_hash_t& multi_cuts_classes )
{
static_assert( max_multioutput_cut_size > 1 && max_multioutput_cut_size < 7 );
uint32_t counter = 0;
multi_leaves_set_t leaves = { 0 };
ntk.foreach_gate( [&]( auto const& n ) {
uint32_t cut_index = 0;
for ( auto& cut : multi_cuts.cuts( ntk.node_to_index( n ) ) )
{
kitty::static_truth_table<max_multioutput_cut_size> tt = multi_cuts.truth_table( *cut );
/* reduce support for matching ID */
uint64_t tt_id = ( cut->size() < 3 ) ? ( tt._bits & 0xF ) : tt._bits;
uint64_t id = library.get_multi_function_id( tt_id );
if ( !id )
{
++cut_index;
continue;
}
( *cut )->data.id = id;
multi_match_data data;
data.node_index = ntk.node_to_index( n );
data.cut_index = cut_index;
leaves[2] = 0;
uint32_t i = 0;
for ( auto l : *cut )
leaves[i++] = l;
/* add to hash table */
multi_cuts_classes[leaves].push_back( data );
++cut_index;
}
} );
}
/* Experimental code */
void multi_compute_matches( multi_cuts_t const& multi_cuts, multi_hash_t& multi_cuts_classes, multi_single_matches_t& multi_node_match_local )
{
ntk.clear_values();
/* copy set and sort by gate size: improve, too slow */
std::vector<std::pair<multi_leaves_set_t, multi_output_set_t>> class_list;
class_list.reserve( multi_cuts_classes.size() );
for ( auto& it : multi_cuts_classes )
{
/* insert multiple occurring cuts */
if ( it.second.size() > 1 )
class_list.push_back( it );
}
std::stable_sort( class_list.begin(), class_list.end(), [&]( auto const& a, auto const& b ) {
return a.first[2] > b.first[2];
} );
/* combine and match: specific code for 2-output cells */
for ( auto it : class_list )
{
for ( uint32_t i = 0; i < it.second.size() - 1; ++i )
{
multi_match_data data_i = it.second[i];
uint32_t index_i = data_i.node_index;
uint32_t cut_index_i = data_i.cut_index;
auto const& cut_i = multi_cuts.cuts( index_i )[cut_index_i];
for ( uint32_t j = i + 1; j < it.second.size(); ++j )
{
multi_match_data data_j = it.second[j];
uint32_t index_j = data_j.node_index;
uint32_t cut_index_j = data_j.cut_index;
auto const& cut_j = multi_cuts.cuts( index_j )[cut_index_j];
/* not compatible -> TODO: change */
if ( cut_i->data.id == cut_j->data.id )
continue;
/* check compatibility */
if ( !multi_check_partally_dangling( index_i, index_j, cut_i ) )
continue;
multi_node_match_local.push_back( { data_i, data_j } );
}
}
}
}
/* Experimental code */
template<bool OverlapFilter>
void multi_filter_and_match( multi_cuts_t const& multi_cuts, multi_single_matches_t const& multi_node_match_local )
{
multi_cut_set.reserve( multi_node_match_local.size() );
multi_node_match.reserve( multi_node_match_local.size() );
ntk.incr_trav_id();
for ( auto& pair : multi_node_match_local )
{
uint32_t index1 = pair[0].node_index;
uint32_t index2 = pair[1].node_index;
uint32_t cut_index1 = pair[0].cut_index;
uint32_t cut_index2 = pair[1].cut_index;
multi_cut_t const& cut1 = multi_cuts.cuts( index1 )[cut_index1];
multi_cut_t const& cut2 = multi_cuts.cuts( index2 )[cut_index2];
assert( index1 < index2 );
/* remove incompatible multi-output cuts */
bool is_new = true;
uint32_t insertion_index = multi_node_match.size();
if constexpr ( OverlapFilter )
{
if ( multi_gate_check_overlapping( index1, index2, cut1 ) )
continue;
}
else
{
if ( multi_gate_check_incompatible( index1, index2, is_new, insertion_index ) )
continue;
// if ( is_new && multi_gate_check_overlapping( index1, index2, cut1 ) )
// continue;
}
/* copy cuts */
cut_t new_cut1, new_cut2;
new_cut1.set_leaves( cut1.begin(), cut1.end() );
new_cut2.set_leaves( cut2.begin(), cut2.end() );
new_cut1->function = kitty::extend_to<6>( multi_cuts.truth_table( cut1 ) );
new_cut2->function = kitty::extend_to<6>( multi_cuts.truth_table( cut2 ) );
/* Multi-output Boolean matching, continue if no match */
std::array<cut_t, max_multioutput_output_size> cut_pair = { new_cut1, new_cut2 };
if ( !multi_compute_cut_data( cut_pair ) )
continue;
/* mark multioutput gate */
if constexpr ( OverlapFilter )
{
multi_gate_mark_visited( index1, index2, cut1 );
node_tuple_match[index1].has_info = 1;
node_tuple_match[index1].lowest_index = 1;
node_tuple_match[index1].index = multi_node_match.size();
node_tuple_match[index2].has_info = 1;
node_tuple_match[index2].highest_index = 1;
node_tuple_match[index2].index = multi_node_match.size();
}
else
{
// multi_gate_mark_visited( index1, index2, cut1 );
multi_gate_mark_compatibility( index1, index2, insertion_index );
}
/* add cut */
multi_cut_set.push_back( cut_pair );
/* re-index data */
multi_match_data new_data1, new_data2;
new_data1.node_index = index1;
new_data1.cut_index = multi_cut_set.size() - 1;
new_data2.node_index = index2;
new_data2.cut_index = multi_cut_set.size() - 1;
multi_match_t p = { new_data1, new_data2 };
/* add cuts to the correct bucket */
if ( is_new )
{
multi_node_match.push_back( { p } );
}
else
{
multi_node_match[insertion_index].push_back( p );
}
}
}
bool multi_compute_cut_data( std::array<cut_t, max_multioutput_output_size>& cut_tuple )
{
std::array<kitty::static_truth_table<6>, max_multioutput_output_size> tts;
std::array<kitty::static_truth_table<6>, max_multioutput_output_size> tts_order;
std::array<size_t, max_multioutput_output_size> order = {};
std::array<uint16_t, max_multioutput_output_size> phase = { 0 };
std::array<uint8_t, max_multioutput_output_size> phase_order;
std::iota( order.begin(), order.end(), 0 );
for ( auto i = 0; i < max_multioutput_output_size; ++i )
{
tts[i] = kitty::extend_to<6>( cut_tuple[i]->function );
if ( ( tts[i]._bits & 1 ) == 1 )
{
tts[i] = ~tts[i];
phase[i] = 1;
}
}
std::stable_sort( order.begin(), order.end(), [&]( size_t a, size_t b ) {
return tts[a] < tts[b];
} );
std::transform( order.begin(), order.end(), tts_order.begin(), [&]( size_t a ) {
return tts[a];
} );
std::transform( order.begin(), order.end(), phase_order.begin(), [&]( uint8_t a ) {
return phase[a];
} );
auto const multigates_match = library.get_multi_supergates( tts_order );
/* Ignore not matched cuts */
if ( multigates_match == nullptr )
return false;
/* add cut matches */
for ( auto i = 0; i < max_multioutput_output_size; ++i )
{
cut_tuple[order[i]]->supergates[0] = nullptr;
cut_tuple[order[i]]->supergates[1] = nullptr;
cut_tuple[order[i]]->ignore = false;
std::vector<supergate<NInputs>> const* multigate = &( ( *multigates_match )[i] );
cut_tuple[order[i]]->supergates[phase_order[i]] = multigate;
}
return true;
}
inline bool multi_check_partally_dangling( uint32_t index1, uint32_t index2, multi_cut_t const& cut1 )
{
bool valid = true;
/* check containment of cut1 in cut2 and viceversa */
if ( index1 > index2 )
{
std::swap( index1, index2 );
}
ntk.foreach_fanin( ntk.index_to_node( index2 ), [&]( auto const& f ) {
auto g = ntk.get_node( f );
if ( ntk.node_to_index( g ) == index1 && ntk.fanout_size( g ) == 1 )
{
valid = false;
}
return valid;
} );
if ( !valid )
return false;
if ( !is_contained_mffc( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut1 ) )
return false;
return true;
}
inline bool multi_gate_check_overlapping( uint32_t index1, uint32_t index2, multi_cut_t const& cut )
{
bool contained = false;
/* mark leaves */
for ( auto leaf : cut )
{
ntk.incr_value( ntk.index_to_node( leaf ) );
}
contained = multi_mark_visited_rec<false>( ntk.index_to_node( index1 ) );
contained |= multi_mark_visited_rec<false>( ntk.index_to_node( index2 ) );
/* unmark leaves */
for ( auto leaf : cut )
{
ntk.decr_value( ntk.index_to_node( leaf ) );
}
return contained;
}
inline bool multi_gate_check_incompatible( uint32_t index1, uint32_t index2, bool& is_new, uint32_t& data_index )
{
/* check cut assigned cut outputs, specialized code for 2 outputs */
if ( !node_tuple_match[index1].has_info && !node_tuple_match[index2].has_info )
return false;
if ( node_tuple_match[index1].has_info && node_tuple_match[index2].has_info )
{
uint32_t current_assignment = node_tuple_match[index1].index;
if ( current_assignment != node_tuple_match[index2].index )
return true;
is_new = false;
data_index = current_assignment;
return false;
}
return true;
}
inline void multi_gate_mark_compatibility( uint32_t index1, uint32_t index2, uint32_t mark_value )
{
node_tuple_match[index1].has_info = 1;
node_tuple_match[index1].lowest_index = 1;
node_tuple_match[index1].index = mark_value;
node_tuple_match[index2].has_info = 1;
node_tuple_match[index2].highest_index = 1;
node_tuple_match[index2].index = mark_value;
}
inline void multi_gate_mark_visited( uint32_t index1, uint32_t index2, multi_cut_t const& cut )
{
/* mark leaves */
for ( auto leaf : cut )
{
ntk.incr_value( ntk.index_to_node( leaf ) );
}
/* mark */
multi_mark_visited_rec<true>( ntk.index_to_node( index1 ) );
multi_mark_visited_rec<true>( ntk.index_to_node( index2 ) );
/* unmark leaves */
for ( auto leaf : cut )
{
ntk.decr_value( ntk.index_to_node( leaf ) );
}
}
template<bool MARK>
bool multi_mark_visited_rec( node<Ntk> const& n )
{
/* leaf */
if ( ntk.value( n ) )
return false;
/* already visited */
if ( ntk.visited( n ) == ntk.trav_id() )
return true;
if constexpr ( MARK )
{
ntk.set_visited( n, ntk.trav_id() );
}
bool contained = false;
ntk.foreach_fanin( n, [&]( auto const& f ) {
contained |= multi_mark_visited_rec<MARK>( ntk.get_node( f ) );
if constexpr ( !MARK )
{
if ( contained )
return false;
}
return true;
} );
return contained;
}
bool is_contained_mffc( node<Ntk> root, node<Ntk> n, multi_cut_t const& cut )
{
/* reference cut leaves */
for ( auto leaf : cut )
{
ntk.incr_value( ntk.index_to_node( leaf ) );
}
bool valid = true;
tmp_visited.clear();
dereference_node_rec( root );
if ( ntk.fanout_size( n ) == 0 )
valid = false;
for ( uint64_t g : tmp_visited )
ntk.incr_fanout_size( ntk.index_to_node( g ) );
/* dereference leaves */
for ( auto leaf : cut )
{
ntk.decr_value( ntk.index_to_node( leaf ) );
}
return valid;
}
void dereference_node_rec( node<Ntk> const& n )
{
/* leaf */
if ( ntk.value( n ) )
return;
ntk.foreach_fanin( n, [&]( auto const& f ) {
node<Ntk> g = ntk.get_node( f );
if ( ntk.decr_fanout_size( g ) == 0 )
{
dereference_node_rec( g );
}
tmp_visited.push_back( ntk.node_to_index( g ) );
} );
}
void multi_add_choices( choice_view<Ntk>& choice_ntk )
{
for ( auto& field : multi_node_match )
{
auto& pair = field.front();
uint32_t index1 = pair[0].node_index;
uint32_t index2 = pair[1].node_index;
uint32_t cut_index1 = pair[0].cut_index;
cut_t const& cut = multi_cut_set[cut_index1][0];
/* don't add choice if in TFI, set TFI bit */
if ( multi_is_in_tfi( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut ) )
{
/* if there is a path of length > 1 linking node 1 and 2, save as TFI node */
uint32_t in_tfi = multi_is_in_direct_tfi( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ) ) ? 0 : 1;
for ( auto& match : field )
match[0].in_tfi = in_tfi;
/* add a TFI dependency */
ntk.set_value( ntk.index_to_node( index1 ), index2 );
// multi_set_tfi_dependency( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut );
continue;
}
choice_ntk.add_choice( ntk.index_to_node( index1 ), ntk.index_to_node( index2 ) );
assert( choice_ntk.count_choices( ntk.index_to_node( index1 ) ) == 2 );
}
}
bool multi_topo_sort_rec( choice_view<Ntk>& choice_ntk, node<Ntk> const& n )
{
/* is permanently marked? */
if ( ntk.visited( n ) == ntk.trav_id() )
return true;
/* loop detected: backtrack to remove the cause */
if ( ntk.visited( n ) == ntk.trav_id() - 1 )
return false;
/* get the representative (smallest index) */
node<Ntk> repr = choice_ntk.get_choice_representative( n );
/* loop detected: backtrack to remove the cause */
if ( ntk.visited( repr ) == ntk.trav_id() - 1 )
return false;
/* solve the TFI dependency first */
node<Ntk> dependency_node = ntk.index_to_node( ntk.value( n ) );
if ( dependency_node > 0 && ntk.visited( dependency_node ) != ntk.trav_id() - 1 )
{
if ( !multi_topo_sort_rec( choice_ntk, dependency_node ) )
return false;
assert( ntk.visited( n ) == ntk.trav_id() );
return true;
}
/* for all the choices */
uint32_t i = 0;
bool check = true;
choice_ntk.foreach_choice( repr, [&]( auto const& g ) {
/* ensure that the node is not visited or temporarily marked */
assert( ntk.visited( g ) != ntk.trav_id() );
assert( ntk.visited( g ) != ntk.trav_id() - 1 );
/* mark node temporarily */
ntk.set_visited( g, ntk.trav_id() - 1 );
/* mark children */
ntk.foreach_fanin( g, [&]( auto const& f ) {
check = multi_topo_sort_rec( choice_ntk, ntk.get_node( f ) );
return check;
} );
/* cycle detected: backtrack to the last choice jump */
if ( !check )
{
/* revert visited */
ntk.set_visited( g, ntk.trav_id() - 2 );
if ( i > 0 && n == repr )
{
/* fix cycle: remove multi-output match */
choice_ntk.foreach_choice( repr, [&]( auto const& p ) {
node_tuple_match[ntk.node_to_index( p )].data = 0;
return true;
} );
choice_ntk.remove_choice( g );
check = true;
}
return false;
}
++i;
return true;
} );
if ( !check )
{
return false;
}
choice_ntk.foreach_choice( repr, [&]( auto const& g ) {
/* ensure that the node is not visited */
assert( ntk.visited( g ) != ntk.trav_id() );
/* mark node n permanently */
ntk.set_visited( g, ntk.trav_id() );
/* visit node */
topo_order.push_back( g );
return true;
} );
return true;
}
inline bool multi_is_in_tfi( node<Ntk> const& root, node<Ntk> const& n, cut_t const& cut )
{
/* reference cut leaves */
for ( auto leaf : cut )
{
ntk.incr_value( ntk.index_to_node( leaf ) );
}
ntk.incr_trav_id();
multi_mark_visited_rec<true>( root );
bool contained = ntk.visited( n ) == ntk.trav_id();
/* dereference leaves */
for ( auto leaf : cut )
{
ntk.decr_value( ntk.index_to_node( leaf ) );
}
return contained;
}
inline bool multi_is_in_direct_tfi( node<Ntk> const& root, node<Ntk> const& n )
{
bool contained = false;
ntk.foreach_fanin( root, [&]( auto const& f ) {
if ( ntk.get_node( f ) == n )
contained = true;
} );
return contained;
}
inline void multi_set_tfi_dependency( node<Ntk> const& root, node<Ntk> const& n, cut_t const& cut )
{
/* reference cut leaves */
for ( auto leaf : cut )
{
ntk.incr_value( ntk.index_to_node( leaf ) );
}
ntk.incr_trav_id();
/* add a TFI dependencies */
ntk.set_value( n, ntk.node_to_index( root ) );
ntk.set_visited( n, ntk.trav_id() );
multi_set_tfi_dependency_rec( root, ntk.node_to_index( root ) );
/* reset root's dependency info */
ntk.set_value( root, 0 );
/* dereference leaves */
for ( auto leaf : cut )
{
ntk.decr_value( ntk.index_to_node( leaf ) );
}
}
void multi_set_tfi_dependency_rec( node<Ntk> const& n, uint32_t const dependency_info )
{
/* leaf */
if ( ntk.value( n ) )
return;
/* already visited */
if ( ntk.visited( n ) == ntk.trav_id() )
return;
ntk.set_visited( n, ntk.trav_id() );
ntk.set_value( n, dependency_info );
ntk.foreach_fanin( n, [&]( auto const& f ) {
multi_set_tfi_dependency_rec( ntk.get_node( f ), dependency_info );
} );
}
#pragma endregion
private:
Ntk const& ntk;
tech_library<NInputs, Configuration> const& library;
emap_params const& ps;
emap_stats& st;
uint32_t iteration{ 0 }; /* current mapping iteration */
double delay{ 0.0f }; /* current delay of the mapping */
double area{ 0.0f }; /* current area of the mapping */
uint32_t inv{ 0 }; /* current inverter count */
/* lib inverter info */
float lib_inv_area;
float lib_inv_delay;
uint32_t lib_inv_id;
/* lib buffer info */
float lib_buf_area;
float lib_buf_delay;
uint32_t lib_buf_id;
std::vector<node<Ntk>> topo_order;
node_match_t node_match;
std::vector<multioutput_info> node_tuple_match;
std::vector<float> switch_activity;
std::vector<uint64_t> tmp_visited;
/* cut computation */
std::vector<cut_set_t> cuts; /* compressed representation of cuts */
cut_merge_t lcuts; /* cut merger container */
cut_set_t temp_cuts; /* temporary cut set container */
truth_compute_t ltruth; /* truth table merger container */
support_t lsupport; /* support merger container */
uint32_t cuts_total{ 0 }; /* current computed cuts */
/* multi-output matching */
multi_cut_set_t multi_cut_set; /* set of multi-output cuts */
multi_matches_t multi_node_match; /* matched multi-output gates */
time_point time_begin;
};
} /* namespace detail */
/*! \brief Technology mapping.
*
* This function implements a technology mapping algorithm.
*
* The function takes the size of the cuts in the template parameter `CutSize`.
*
* The function returns a block network that supports multi-output cells.
*
* The novelties of this mapper are contained in 2 publications:
* - A. Tempia Calvino and G. De Micheli, "Technology Mapping Using Multi-Output Library Cells," ICCAD, 2023.
* - G. Radi, A. Tempia Calvino, and G. De Micheli, "In Medio Stat Virtus: Combining Boolean and Pattern Matching," ASP-DAC, 2024.
*
* **Required network functions:**
* - `size`
* - `is_pi`
* - `is_constant`
* - `node_to_index`
* - `index_to_node`
* - `get_node`
* - `foreach_po`
* - `foreach_node`
* - `fanout_size`
*
* \param ntk Network
* \param library Technology library
* \param ps Mapping params
* \param pst Mapping statistics
*
*/
template<unsigned CutSize = 6u, class Ntk, unsigned NInputs, classification_type Configuration>
cell_view<block_network> emap( Ntk const& ntk, tech_library<NInputs, Configuration> const& library, emap_params const& ps = {}, emap_stats* pst = nullptr )
{
static_assert( is_network_type_v<Ntk>, "Ntk is not a network type" );
static_assert( has_size_v<Ntk>, "Ntk does not implement the size method" );
static_assert( has_is_pi_v<Ntk>, "Ntk does not implement the is_pi method" );
static_assert( has_is_constant_v<Ntk>, "Ntk does not implement the is_constant method" );
static_assert( has_node_to_index_v<Ntk>, "Ntk does not implement the node_to_index method" );
static_assert( has_index_to_node_v<Ntk>, "Ntk does not implement the index_to_node method" );
static_assert( has_get_node_v<Ntk>, "Ntk does not implement the get_node method" );
static_assert( has_foreach_po_v<Ntk>, "Ntk does not implement the foreach_po method" );
static_assert( has_foreach_node_v<Ntk>, "Ntk does not implement the foreach_node method" );
static_assert( has_fanout_size_v<Ntk>, "Ntk does not implement the fanout_size method" );
emap_stats st;
detail::emap_impl<Ntk, CutSize, NInputs, Configuration> p( ntk, library, ps, st );
auto res = p.run_block();
if ( ps.verbose && !st.mapping_error )
{
st.report();
}
if ( pst )
{
*pst = st;
}
return res;
}
/*! \brief Technology mapping.
*
* This function implements a technology mapping algorithm.
*
* The function takes the size of the cuts in the template parameter `CutSize`.
*
* The function returns a k-LUT network. Each LUT abstacts a gate of the technology library.
*
* The novelties of this mapper are contained in 2 publications:
* - A. Tempia Calvino and G. De Micheli, "Technology Mapping Using Multi-Output Library Cells," ICCAD, 2023.
* - G. Radi, A. Tempia Calvino, and G. De Micheli, "In Medio Stat Virtus: Combining Boolean and Pattern Matching," ASP-DAC, 2024.
*
* **Required network functions:**
* - `size`
* - `is_pi`
* - `is_constant`
* - `node_to_index`
* - `index_to_node`
* - `get_node`
* - `foreach_po`
* - `foreach_node`
* - `fanout_size`
*
* \param ntk Network
* \param library Technology library
* \param ps Mapping params
* \param pst Mapping statistics
*
*/
template<unsigned CutSize = 6u, class Ntk, unsigned NInputs, classification_type Configuration>
binding_view<klut_network> emap_klut( Ntk const& ntk, tech_library<NInputs, Configuration> const& library, emap_params const& ps = {}, emap_stats* pst = nullptr )
{
static_assert( is_network_type_v<Ntk>, "Ntk is not a network type" );
static_assert( has_size_v<Ntk>, "Ntk does not implement the size method" );
static_assert( has_is_pi_v<Ntk>, "Ntk does not implement the is_pi method" );
static_assert( has_is_constant_v<Ntk>, "Ntk does not implement the is_constant method" );
static_assert( has_node_to_index_v<Ntk>, "Ntk does not implement the node_to_index method" );
static_assert( has_index_to_node_v<Ntk>, "Ntk does not implement the index_to_node method" );
static_assert( has_get_node_v<Ntk>, "Ntk does not implement the get_node method" );
static_assert( has_foreach_po_v<Ntk>, "Ntk does not implement the foreach_po method" );
static_assert( has_foreach_node_v<Ntk>, "Ntk does not implement the foreach_node method" );
static_assert( has_fanout_size_v<Ntk>, "Ntk does not implement the fanout_size method" );
emap_stats st;
detail::emap_impl<Ntk, CutSize, NInputs, Configuration> p( ntk, library, ps, st );
auto res = p.run_klut();
if ( ps.verbose && !st.mapping_error )
{
st.report();
}
if ( pst )
{
*pst = st;
}
return res;
}
/*! \brief Technology node mapping.
*
* This function implements a simple technology mapping algorithm.
* The algorithm maps each node to the best implementation in the technology library.
*
* **Required network functions:**
* - `size`
* - `is_pi`
* - `is_constant`
* - `node_to_index`
* - `index_to_node`
* - `get_node`
* - `foreach_po`
* - `foreach_node`
* - `fanout_size`
* - `has_binding`
*
* \param ntk Network
* \param library Technology library
* \param ps Mapping params
* \param pst Mapping statistics
*
*/
template<unsigned CutSize = 6u, class Ntk, unsigned NInputs, classification_type Configuration>
binding_view<klut_network> emap_node_map( Ntk const& ntk, tech_library<NInputs, Configuration> const& library, emap_params const& ps = {}, emap_stats* pst = nullptr )
{
static_assert( is_network_type_v<Ntk>, "Ntk is not a network type" );
static_assert( has_size_v<Ntk>, "Ntk does not implement the size method" );
static_assert( has_is_pi_v<Ntk>, "Ntk does not implement the is_pi method" );
static_assert( has_is_constant_v<Ntk>, "Ntk does not implement the is_constant method" );
static_assert( has_node_to_index_v<Ntk>, "Ntk does not implement the node_to_index method" );
static_assert( has_index_to_node_v<Ntk>, "Ntk does not implement the index_to_node method" );
static_assert( has_get_node_v<Ntk>, "Ntk does not implement the get_node method" );
static_assert( has_foreach_po_v<Ntk>, "Ntk does not implement the foreach_po method" );
static_assert( has_foreach_node_v<Ntk>, "Ntk does not implement the foreach_node method" );
static_assert( has_has_binding_v<Ntk>, "Ntk does not implement the has_binding method" );
emap_stats st;
detail::emap_impl<Ntk, CutSize, NInputs, Configuration> p( ntk, library, ps, st );
auto res = p.run_node_map();
if ( ps.verbose && !st.mapping_error )
{
st.report();
}
if ( pst )
{
*pst = st;
}
return res;
}
/*! \brief Technology node mapping.
*
* This function implements a simple technology mapping algorithm.
* The algorithm maps each node to the first implementation in the technology library.
*
* The input must be a binding_view with the gates correctly loaded.
*
* **Required network functions:**
* - `size`
* - `is_pi`
* - `is_constant`
* - `node_to_index`
* - `index_to_node`
* - `get_node`
* - `foreach_po`
* - `foreach_node`
* - `fanout_size`
* - `has_binding`
*
* \param ntk Network
*
*/
template<class Ntk>
void emap_load_mapping( Ntk& ntk )
{
static_assert( is_network_type_v<Ntk>, "Ntk is not a network type" );
static_assert( has_size_v<Ntk>, "Ntk does not implement the size method" );
static_assert( has_is_pi_v<Ntk>, "Ntk does not implement the is_pi method" );
static_assert( has_is_constant_v<Ntk>, "Ntk does not implement the is_constant method" );
static_assert( has_node_to_index_v<Ntk>, "Ntk does not implement the node_to_index method" );
static_assert( has_index_to_node_v<Ntk>, "Ntk does not implement the index_to_node method" );
static_assert( has_get_node_v<Ntk>, "Ntk does not implement the get_node method" );
static_assert( has_foreach_po_v<Ntk>, "Ntk does not implement the foreach_po method" );
static_assert( has_foreach_node_v<Ntk>, "Ntk does not implement the foreach_node method" );
static_assert( has_has_binding_v<Ntk>, "Ntk does not implement the has_binding method" );
/* build the library map */
using lib_t = std::unordered_map<kitty::dynamic_truth_table, uint32_t, kitty::hash<kitty::dynamic_truth_table>>;
lib_t tt_to_gate;
for ( auto const& g : ntk.get_library() )
{
tt_to_gate[g.function] = g.id;
}
ntk.foreach_gate( [&]( auto const& n ) {
if ( auto it = tt_to_gate.find( ntk.node_function( n ) ); it != tt_to_gate.end() )
{
ntk.add_binding( n, it->second );
}
else
{
std::cout << fmt::format( "[e] node mapping for node {} failed: no match in the tech library\n", ntk.node_to_index( n ) );
}
} );
}
} /* namespace mockturtle */

File Metadata

Mime Type
text/x-c++
Expires
Sun, Jul 12, 6:06 PM (16 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
351904
Default Alt Text
emap.hpp (179 KB)

Event Timeline