From 4b42ca687068b36e9c018b6292dc5551c604f258 Mon Sep 17 00:00:00 2001 From: "Siang-Yun (Sonia) Lee" Date: Tue, 5 Mar 2024 13:14:23 +0100 Subject: [PATCH] AQFP technology legalization flow (#636) * Flow for AQFP TCAD * Adding AQFP mapping and optimization as a command * Adding ISCAS SCE benchmarks, updating flow using the best version * final changes * remove benchmarks * revert changes in experiments.hpp * remove old code * rename new code * update exp code * rename mapping as legalization --------- Co-authored-by: aletempiac --- experiments/aqfp_flow_tcad.cpp | 94 +++++ .../algorithms/aqfp/aqfp_legalization.hpp | 331 ++++++++++++++++++ .../algorithms/aqfp/buffer_insertion.hpp | 9 +- include/mockturtle/utils/node_map.hpp | 8 + 4 files changed, 440 insertions(+), 2 deletions(-) create mode 100644 experiments/aqfp_flow_tcad.cpp create mode 100644 include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp diff --git a/experiments/aqfp_flow_tcad.cpp b/experiments/aqfp_flow_tcad.cpp new file mode 100644 index 000000000..47000fe6f --- /dev/null +++ b/experiments/aqfp_flow_tcad.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace mockturtle; +using namespace experiments; + +// relative path to repo cloned from https://github.com/lsils/SCE-benchmarks +static const std::string benchmark_repo_path = "../../SCE-benchmarks"; + +/* AQFP benchmarks */ +std::vector aqfp_benchmarks = { + "adder1", "adder8", "mult8", "counter16", "counter32", "counter64", "counter128", "c17", + "c432", "c499", "c880", "c1355", "c1908", "c2670", "c3540", "c5315", "c6288", "c7552", + "sorter32", "sorter48", "alu32"}; + +std::string benchmark_aqfp_path( std::string const& benchmark_name ) +{ + return fmt::format( "{}/ISCAS/strashed/{}.v", benchmark_repo_path, benchmark_name ); +} + +template +inline bool abc_cec_aqfp( Ntk const& ntk, std::string const& benchmark ) +{ + return abc_cec_impl( ntk, benchmark_aqfp_path( benchmark ) ); +} + +int main() +{ + experiment exp( + "aqfp_tcad", "Bench", "Size_init", "Depth_init", "B/S", "JJs", "Depth", "Time (s)", "cec" ); + + for ( auto const& benchmark : aqfp_benchmarks ) + { + fmt::print( "[i] processing {}\n", benchmark ); + + mig_network mig; + if ( lorina::read_verilog( benchmark_aqfp_path( benchmark ), verilog_reader( mig ) ) != lorina::return_code::success ) + { + continue; + } + + /* MIG-based logic optimization can be added here */ + auto mig_opt = cleanup_dangling( mig ); + + const uint32_t size_before = mig_opt.num_gates(); + const uint32_t depth_before = depth_view( mig_opt ).depth(); + + aqfp_assumptions_legacy aqfp_ps; + aqfp_ps.splitter_capacity = 4; + aqfp_ps.branch_pis = true; + aqfp_ps.balance_pis = true; + aqfp_ps.balance_pos = true; + + aqfp_legalization_params ps; + ps.aqfp_assumptions_ps = aqfp_ps; + ps.legalization_mode = aqfp_legalization_params::portfolio; + ps.verbose = true; + ps.max_chunk_size = UINT32_MAX; + ps.retime_iterations = UINT32_MAX; + ps.optimization_rounds = UINT32_MAX; + aqfp_legalization_stats st; + + buffered_aqfp_network res = aqfp_legalization( mig_opt, ps, &st ); + + /* cec */ + auto cec = abc_cec_aqfp( res, benchmark ); + std::vector pi_levels; + for ( auto i = 0u; i < res.num_pis(); ++i ) + pi_levels.emplace_back( 0 ); + cec &= verify_aqfp_buffer( res, aqfp_ps, pi_levels ); + + exp( benchmark, size_before, depth_before, st.num_bufs, st.num_jjs, st.depth, to_seconds( st.time_total ), cec ); + } + + exp.save(); + exp.table(); + + return 0; +} diff --git a/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp b/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp new file mode 100644 index 000000000..9de592070 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp @@ -0,0 +1,331 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 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 aqfp_legalization.hpp + \brief Legalization + buffer optimization flow for AQFP networks + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../../networks/aqfp.hpp" +#include "../../networks/buffered.hpp" +#include "../../networks/mig.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/depth_view.hpp" +#include "../cleanup.hpp" +#include "aqfp_assumptions.hpp" +#include "aqfp_rebuild.hpp" +#include "aqfp_retiming.hpp" +#include "buffer_insertion.hpp" + +namespace mockturtle +{ + +struct aqfp_legalization_params +{ + aqfp_legalization_params() + { + aqfp_assumptions_ps.branch_pis = true; + aqfp_assumptions_ps.balance_pis = true; + aqfp_assumptions_ps.balance_pos = true; + } + + /*! \brief legalization mode. */ + enum + { + better, + portfolio + } legalization_mode = portfolio; + + /*! \brief AQFP technology assumptions. */ + aqfp_assumptions_legacy aqfp_assumptions_ps{}; + + /*! \brief Max number of optimization rounds (zero performs only insertion)*/ + uint32_t optimization_rounds{ 10 }; + + /*! \brief Maximum chunk size for chunk optimization. */ + uint32_t max_chunk_size{ 100 }; + + /*! \brief Maximum number of iterations for optimization using retiming. */ + uint32_t retime_iterations{ 250 }; + + /*! \brief Enable optimization of splitters using retiming. */ + bool retime_splitters{ true }; + + /*! \brief Enables the randomization of topological order. */ + bool topological_randomization{ true }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct aqfp_legalization_stats +{ + /*! \brief Number of Josephson Junctions (JJs). */ + uint32_t num_jjs{ 0 }; + + /*! \brief Number of buffers and splitters. */ + uint32_t num_bufs{ 0 }; + + /*! \brief Depth of the circuit. */ + uint32_t depth{ 0 }; + + /*! \brief Total number of optimization rounds. */ + uint32_t rounds_total{ 0 }; + + /*! \brief Time insertion. */ + stopwatch<>::duration time_insertion{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] JJs = {:7d}\t B/S = {:7d}\t Depth = {:5d}\n", num_jjs, num_bufs, depth ); + std::cout << fmt::format( "[i] Rounds = {:7d}\t Time Insertion = {:>5.2f} secs\t Total Runtime = {:>5.2f} secs\n", rounds_total, to_seconds( time_insertion ), to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class aqfp_legalization_impl +{ +public: + explicit aqfp_legalization_impl( Ntk const& ntk, aqfp_legalization_params const& ps, aqfp_legalization_stats& st ) + : _ntk( ntk ), _ps( ps ), _st( st ) + { + } + +public: + buffered_aqfp_network run() + { + stopwatch t( _st.time_total ); + _st.rounds_total = 0; + + /* convert the initial circuit as an AQFP network */ + aqfp_network aqfp_start = cleanup_dangling( _ntk ); + /* creates buffer_insertion instance for scheduling (can be reused) */ + buffer_insertion_params insertion_ps; + insertion_ps.optimization_effort = buffer_insertion_params::none; + insertion_ps.assume = legacy_to_realistic( _ps.aqfp_assumptions_ps ); + buffer_insertion buf_inst( aqfp_start, insertion_ps ); + + bool retiming_backward_first = false; + + /* Starndard flow: insertion + optimization */ + if ( _ps.legalization_mode == aqfp_legalization_params::better ) + { + buffered_aqfp_network buffered_aqfp = aqfp_buffer_insertion( buf_inst, retiming_backward_first ); + buffered_aqfp_network aqfp_res = aqfp_buffer_optimize( buffered_aqfp, retiming_backward_first ); + compute_stats( aqfp_res ); + return aqfp_res; + } + + /* Portfolio flow: 2 insertions + 2 optimizations */ + buffered_aqfp_network buffered_aqfp_alap = aqfp_buffer_insertion( buf_inst, retiming_backward_first, true ); + buffered_aqfp_network aqfp_res_alap = aqfp_buffer_optimize( buffered_aqfp_alap, retiming_backward_first ); + + buffered_aqfp_network buffered_aqfp_asap = aqfp_buffer_insertion( buf_inst, retiming_backward_first, false ); + buffered_aqfp_network aqfp_res_asap = aqfp_buffer_optimize( buffered_aqfp_asap, retiming_backward_first ); + + buffered_aqfp_network aqfp_res; + if ( aqfp_res_alap.size() < aqfp_res_asap.size() ) + { + aqfp_res = aqfp_res_alap; + } + else + { + aqfp_res = aqfp_res_asap; + } + + compute_stats( aqfp_res ); + return aqfp_res; + } + +private: + buffered_aqfp_network aqfp_buffer_insertion( buffer_insertion& buf_inst, bool& direction, bool is_alap = false ) + { + stopwatch t( _st.time_insertion ); + + if ( _ps.legalization_mode == aqfp_legalization_params::portfolio ) + { + if ( is_alap ) + { + buf_inst.set_scheduling_policy( buffer_insertion_params::ALAP_depth ); + } + else + { + buf_inst.set_scheduling_policy( buffer_insertion_params::ASAP_depth ); + } + } + else if ( _ps.legalization_mode == aqfp_legalization_params::better ) + { + buf_inst.set_scheduling_policy( buffer_insertion_params::better_depth ); + } + + buffered_aqfp_network buffered_aqfp; + buf_inst.run( buffered_aqfp ); + direction = buf_inst.is_scheduled_ASAP(); + + return buffered_aqfp; + } + + buffered_aqfp_network aqfp_buffer_optimize( buffered_aqfp_network& start, bool direction ) + { + if ( _ps.optimization_rounds == 0 ) + return start; + + /* retiming params */ + aqfp_retiming_params aps; + aps.aqfp_assumptions_ps = _ps.aqfp_assumptions_ps; + aps.backwards_first = direction; + aps.iterations = _ps.retime_iterations; + aps.retime_splitters = _ps.retime_splitters; + + /* chunk movement params */ + buffer_insertion_params buf_ps; + buf_ps.scheduling = buffer_insertion_params::provided; + buf_ps.optimization_effort = buffer_insertion_params::until_sat; + buf_ps.max_chunk_size = _ps.max_chunk_size; + buf_ps.assume = legacy_to_realistic( _ps.aqfp_assumptions_ps ); + + aqfp_reconstruct_params reconstruct_ps; + aqfp_reconstruct_stats reconstruct_st; + reconstruct_ps.buffer_insertion_ps = buf_ps; + + /* aqfp network */ + buffered_aqfp_network buffered_aqfp; + + /* first retiming */ + { + auto buf_aqfp_ret = aqfp_retiming( start, aps ); + buffered_aqfp = buf_aqfp_ret; + } + + /* repeat loop */ + uint32_t iterations = _ps.optimization_rounds; + aps.det_randomization = _ps.topological_randomization; + std::default_random_engine rng( 111 ); + while ( iterations-- > 0 ) + { + uint32_t size_previous = buffered_aqfp.size(); + + /* chunk movement */ + auto buf_aqfp_chunk = aqfp_reconstruct( buffered_aqfp, reconstruct_ps, &reconstruct_st ); + + /* retiming */ + aps.seed = rng(); + auto buf_aqfp_ret = aqfp_retiming( buf_aqfp_chunk, aps ); + + _st.rounds_total++; + + if ( buf_aqfp_ret.size() >= size_previous ) + break; + + buffered_aqfp = buf_aqfp_ret; + } + + return buffered_aqfp; + } + + void compute_stats( buffered_aqfp_network const& buffered_aqfp ) + { + _st.depth = depth_view( buffered_aqfp ).depth(); + _st.num_bufs = 0; + _st.num_jjs = 0; + + buffered_aqfp.foreach_node( [&]( auto const& n ) { + if ( buffered_aqfp.is_pi( n ) || buffered_aqfp.is_constant( n ) ) + return; + if ( buffered_aqfp.is_buf( n ) ) + { + _st.num_jjs += 2; + _st.num_bufs++; + } + else + { + _st.num_jjs += 6; + } + } ); + } + +private: + Ntk const& _ntk; + aqfp_legalization_params const& _ps; + aqfp_legalization_stats& _st; +}; + +} /* namespace detail */ + +/*! \brief AQFP legalization. + * + * This function returns an optimized AQFP circuit + * derived from the input one by inserting buffer + * and splitter elements and optimizing their number. + * + * Parameters can be used to set the B/S insertion and + * optimization. + * + * \param ntk Boolean network as an MIG or AQFP network + * \param ps AQFP legalization parameters + */ +template +buffered_aqfp_network aqfp_legalization( Ntk const& ntk, aqfp_legalization_params const& ps = {}, aqfp_legalization_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( std::is_same_v || std::is_same_v, "Ntk in not an MIG or AQFP network type" ); + + aqfp_legalization_stats st; + + detail::aqfp_legalization_impl p( ntk, ps, st ); + auto res = p.run(); + + if ( ps.verbose ) + st.report(); + + if ( pst ) + *pst = st; + + return res; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp b/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp index 52a2a92ad..5f3e608b4 100644 --- a/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp +++ b/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp @@ -72,7 +72,7 @@ struct buffer_insertion_params * - `better_depth` = ALAP_depth followed by ASAP_depth, then count buffers for both assignments * and choose the better one */ - enum + enum scheduling_policy { provided, ASAP, @@ -581,6 +581,11 @@ class buffer_insertion #pragma region Initial level assignment public: + void set_scheduling_policy( buffer_insertion_params::scheduling_policy p ) + { + _ps.scheduling = p; + } + /*! \brief Obtain the initial level assignment using the specified scheduling policy */ void schedule() { @@ -1947,7 +1952,7 @@ std::pair remove_buffer_chains_rec( BufNtk& ntk using fanouts_by_level = std::list; Ntk const& _ntk; - buffer_insertion_params const _ps; + buffer_insertion_params _ps; bool _outdated{ true }; bool _is_scheduled_ASAP{ true }; diff --git a/include/mockturtle/utils/node_map.hpp b/include/mockturtle/utils/node_map.hpp index 0c0e1b401..6fb4e1279 100644 --- a/include/mockturtle/utils/node_map.hpp +++ b/include/mockturtle/utils/node_map.hpp @@ -119,6 +119,14 @@ class node_map> return data->size(); } + /*! \brief Deep copy. */ + node_map copy() const + { + node_map copy( ntk ); + *( copy.data ) = *data; + return copy; + } + /*! \brief Mutable access to value by node. */ reference operator[]( node const& n ) {