2019-01-10 19:18:47 +08:00
/*
* nextpnr - - Next Generation Place and Route
*
2021-06-09 20:09:08 +08:00
* Copyright ( C ) 2019 gatecat < gatecat @ ds0 . me >
2019-01-10 19:18:47 +08:00
*
* Permission to use , copy , modify , and / or distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*
* [[cite]] HeAP
* Analytical Placement for Heterogeneous FPGAs , Marcel Gort and Jason H . Anderson
* https : //janders.eecg.utoronto.ca/pdfs/marcelfpl12.pdf
*
* [[cite]] SimPL
* SimPL : An Effective Placement Algorithm , Myung - Chul Kim , Dong - Jin Lee and Igor L . Markov
* http : //www.ece.umich.edu/cse/awards/pdfs/iccad10-simpl.pdf
2019-01-26 21:22:44 +08:00
*
* Notable changes from the original algorithm
* - Following the other nextpnr placer , Bels are placed rather than CLBs . This means a strict legalisation pass is
* added in addition to coarse legalisation ( referred to as " spreading " to avoid confusion with strict legalisation )
* as described in HeAP to ensure validity . This searches random bels in the vicinity of the position chosen by
* spreading , with diameter increasing over iterations , with a heuristic to prefer lower wirelength choices .
* - To make the placer timing - driven , the bound2bound weights are multiplied by ( 1 + 10 * crit ^ 2 )
2019-01-10 19:18:47 +08:00
*/
2019-02-25 19:56:10 +08:00
# ifdef WITH_HEAP
2019-02-25 20:48:01 +08:00
# include "placer_heap.h"
2019-02-24 01:33:47 +08:00
# include <Eigen/Core>
# include <Eigen/IterativeLinearSolvers>
2019-01-26 21:22:44 +08:00
# include <boost/optional.hpp>
# include <chrono>
2019-01-10 19:18:47 +08:00
# include <deque>
2019-01-26 21:22:44 +08:00
# include <fstream>
2019-01-11 00:42:29 +08:00
# include <numeric>
2019-01-11 19:59:34 +08:00
# include <queue>
2019-01-26 21:22:44 +08:00
# include <tuple>
2021-02-02 06:28:32 +08:00
# include "fast_bels.h"
2019-01-10 19:18:47 +08:00
# include "log.h"
# include "nextpnr.h"
2021-12-20 03:29:44 +08:00
# include "parallel_refine.h"
2019-01-10 19:18:47 +08:00
# include "place_common.h"
2019-01-24 00:36:53 +08:00
# include "placer1.h"
2021-02-27 03:37:27 +08:00
# include "scope_lock.h"
2019-01-25 21:15:36 +08:00
# include "timing.h"
2019-01-26 21:22:44 +08:00
# include "util.h"
2021-01-29 10:32:42 +08:00
2019-01-10 19:18:47 +08:00
NEXTPNR_NAMESPACE_BEGIN
namespace {
// A simple internal representation for a sparse system of equations Ax = rhs
// This is designed to decouple the functions that build the matrix to the engine that
// solves it, and the representation that requires
template < typename T > struct EquationSystem
{
2019-02-24 01:33:47 +08:00
2019-01-10 19:18:47 +08:00
EquationSystem ( size_t rows , size_t cols )
{
A . resize ( cols ) ;
rhs . resize ( rows ) ;
}
// Simple sparse format, easy to convert to CCS for solver
std : : vector < std : : vector < std : : pair < int , T > > > A ; // col -> (row, x[row, col]) sorted by row
std : : vector < T > rhs ; // RHS vector
void reset ( )
{
for ( auto & col : A )
col . clear ( ) ;
std : : fill ( rhs . begin ( ) , rhs . end ( ) , T ( ) ) ;
}
void add_coeff ( int row , int col , T val )
{
2019-01-11 03:10:47 +08:00
auto & Ac = A . at ( col ) ;
2019-01-10 19:18:47 +08:00
// Binary search
int b = 0 , e = int ( Ac . size ( ) ) - 1 ;
while ( b < = e ) {
int i = ( b + e ) / 2 ;
2019-01-11 03:10:47 +08:00
if ( Ac . at ( i ) . first = = row ) {
Ac . at ( i ) . second + = val ;
2019-01-10 19:18:47 +08:00
return ;
}
2019-01-11 03:10:47 +08:00
if ( Ac . at ( i ) . first > row )
2019-01-10 19:18:47 +08:00
e = i - 1 ;
else
b = i + 1 ;
}
Ac . insert ( Ac . begin ( ) + b , std : : make_pair ( row , val ) ) ;
}
void add_rhs ( int row , T val ) { rhs [ row ] + = val ; }
2019-01-11 00:42:29 +08:00
2020-02-02 23:28:11 +08:00
void solve ( std : : vector < T > & x , float tolerance )
2019-01-11 00:42:29 +08:00
{
2019-02-24 01:33:47 +08:00
using namespace Eigen ;
2019-04-01 19:18:02 +08:00
if ( x . empty ( ) )
return ;
2019-01-11 03:10:47 +08:00
NPNR_ASSERT ( x . size ( ) = = A . size ( ) ) ;
2019-02-24 01:33:47 +08:00
VectorXd vx ( x . size ( ) ) , vb ( rhs . size ( ) ) ;
SparseMatrix < T > mat ( A . size ( ) , A . size ( ) ) ;
std : : vector < int > colnnz ;
for ( auto & Ac : A )
colnnz . push_back ( int ( Ac . size ( ) ) ) ;
mat . reserve ( colnnz ) ;
2019-01-11 00:42:29 +08:00
for ( int col = 0 ; col < int ( A . size ( ) ) ; col + + ) {
2019-02-24 01:33:47 +08:00
auto & Ac = A . at ( col ) ;
for ( auto & el : Ac )
mat . insert ( el . first , col ) = el . second ;
2019-01-11 00:42:29 +08:00
}
2019-01-11 03:10:47 +08:00
2019-02-24 01:33:47 +08:00
for ( int i = 0 ; i < int ( x . size ( ) ) ; i + + )
vx [ i ] = x . at ( i ) ;
for ( int i = 0 ; i < int ( rhs . size ( ) ) ; i + + )
vb [ i ] = rhs . at ( i ) ;
ConjugateGradient < SparseMatrix < T > , Lower | Upper > solver ;
2020-02-02 23:28:11 +08:00
solver . setTolerance ( tolerance ) ;
2019-02-24 01:33:47 +08:00
VectorXd xr = solver . compute ( mat ) . solveWithGuess ( vb , vx ) ;
for ( int i = 0 ; i < int ( x . size ( ) ) ; i + + )
x . at ( i ) = xr [ i ] ;
2019-01-11 03:10:47 +08:00
// for (int i = 0; i < int(x.size()); i++)
// log_info("x[%d] = %f\n", i, x.at(i));
2019-01-11 00:42:29 +08:00
}
2019-01-10 19:18:47 +08:00
} ;
2019-01-11 00:42:29 +08:00
2019-01-10 19:18:47 +08:00
} // namespace
class HeAPPlacer
{
public :
2021-03-04 18:14:20 +08:00
HeAPPlacer ( Context * ctx , PlacerHeapCfg cfg )
: ctx ( ctx ) , cfg ( cfg ) , fast_bels ( ctx , /*check_bel_available=*/ true , - 1 ) , tmg ( ctx )
2021-02-02 06:28:32 +08:00
{
Eigen : : initParallel ( ) ;
2021-03-05 18:04:35 +08:00
tmg . setup_only = true ;
2021-03-04 18:14:20 +08:00
tmg . setup ( ) ;
2021-04-28 22:43:02 +08:00
2021-06-01 23:51:18 +08:00
for ( auto & cell : ctx - > cells )
2022-06-24 01:48:31 +08:00
if ( ! cell . second - > isPseudo ( ) & & cell . second - > cluster ! = ClusterId ( ) )
2021-06-01 23:51:18 +08:00
cluster2cells [ cell . second - > cluster ] . push_back ( cell . second . get ( ) ) ;
2021-02-02 06:28:32 +08:00
}
2019-02-24 01:33:47 +08:00
2019-01-11 00:42:29 +08:00
bool place ( )
{
2019-01-24 21:36:23 +08:00
auto startt = std : : chrono : : high_resolution_clock : : now ( ) ;
2021-03-16 01:25:46 +08:00
ScopeLock < Context > lock ( ctx ) ;
2019-01-11 00:42:29 +08:00
place_constraints ( ) ;
2021-01-30 03:11:05 +08:00
build_fast_bels ( ) ;
2019-01-11 00:42:29 +08:00
seed_placement ( ) ;
update_all_chains ( ) ;
2019-01-11 19:31:56 +08:00
wirelen_t hpwl = total_hpwl ( ) ;
2019-03-25 23:39:15 +08:00
log_info ( " Creating initial analytic placement for %d cells, random placement wirelen = %d. \n " ,
int ( place_cells . size ( ) ) , int ( hpwl ) ) ;
2019-01-25 19:57:58 +08:00
for ( int i = 0 ; i < 4 ; i + + ) {
2019-01-11 19:31:56 +08:00
setup_solve_cells ( ) ;
2019-01-25 22:04:19 +08:00
auto solve_startt = std : : chrono : : high_resolution_clock : : now ( ) ;
2020-05-24 03:50:09 +08:00
# ifdef NPNR_DISABLE_THREADS
build_solve_direction ( false , - 1 ) ;
build_solve_direction ( true , - 1 ) ;
# else
2019-07-05 16:22:42 +08:00
boost : : thread xaxis ( [ & ] ( ) { build_solve_direction ( false , - 1 ) ; } ) ;
2019-01-26 02:26:14 +08:00
build_solve_direction ( true , - 1 ) ;
xaxis . join ( ) ;
2020-05-24 03:50:09 +08:00
# endif
2019-01-25 22:04:19 +08:00
auto solve_endt = std : : chrono : : high_resolution_clock : : now ( ) ;
solve_time + = std : : chrono : : duration < double > ( solve_endt - solve_startt ) . count ( ) ;
2019-01-11 03:10:47 +08:00
update_all_chains ( ) ;
2019-01-11 19:31:56 +08:00
hpwl = total_hpwl ( ) ;
2019-03-25 23:39:15 +08:00
log_info ( " at initial placer iter %d, wirelen = %d \n " , i , int ( hpwl ) ) ;
2019-01-11 03:10:47 +08:00
}
2019-03-24 19:18:38 +08:00
wirelen_t solved_hpwl = 0 , spread_hpwl = 0 , legal_hpwl = 0 , best_hpwl = std : : numeric_limits < wirelen_t > : : max ( ) ;
2019-01-11 19:59:34 +08:00
int iter = 0 , stalled = 0 ;
2019-01-24 22:05:16 +08:00
2019-01-26 21:22:44 +08:00
std : : vector < std : : tuple < CellInfo * , BelId , PlaceStrength > > solution ;
2019-01-24 22:05:16 +08:00
2021-06-02 00:52:25 +08:00
std : : vector < pool < BelBucketId > > heap_runs ;
pool < BelBucketId > all_buckets ;
dict < BelBucketId , int > bucket_count ;
2019-01-25 22:04:19 +08:00
for ( auto cell : place_cells ) {
2021-01-30 06:55:10 +08:00
BelBucketId bucket = ctx - > getBelBucketForCellType ( cell - > type ) ;
if ( ! all_buckets . count ( bucket ) ) {
2021-06-02 00:52:25 +08:00
heap_runs . push_back ( pool < BelBucketId > { bucket } ) ;
2021-01-30 06:55:10 +08:00
all_buckets . insert ( bucket ) ;
2019-01-25 22:04:19 +08:00
}
2021-01-30 06:55:10 +08:00
bucket_count [ bucket ] + + ;
2019-01-25 22:04:19 +08:00
}
2019-01-26 02:26:14 +08:00
// If more than 98% of cells are one cell type, always solve all at once
// Otherwise, follow full HeAP strategy of rotate&all
2021-01-30 06:55:10 +08:00
for ( auto & c : bucket_count ) {
2019-01-26 02:26:14 +08:00
if ( c . second > = 0.98 * int ( place_cells . size ( ) ) ) {
heap_runs . clear ( ) ;
break ;
}
2021-01-29 10:32:42 +08:00
}
2019-01-26 02:26:14 +08:00
2020-02-02 23:40:23 +08:00
if ( cfg . placeAllAtOnce ) {
2021-01-28 23:19:06 +08:00
// Never want to deal with LUTs, FFs, MUXFxs separately,
// for now disable all single-cell-type runs and only have heterogeneous
2020-02-02 23:40:23 +08:00
// runs
heap_runs . clear ( ) ;
}
2021-01-30 06:55:10 +08:00
heap_runs . push_back ( all_buckets ) ;
2019-01-26 21:22:44 +08:00
// The main HeAP placer loop
2022-12-21 03:15:06 +08:00
if ( cfg . cell_placement_timeout > 0 )
log_info ( " Running main analytical placer, max placement attempts per cell = %d. \n " , cfg . cell_placement_timeout ) ;
else
log_info ( " Running main analytical placer. \n " ) ;
2019-01-26 21:22:44 +08:00
while ( stalled < 5 & & ( solved_hpwl < = legal_hpwl * 0.8 ) ) {
2021-01-28 23:19:06 +08:00
// Alternate between particular bel types and all bels
2019-01-25 22:04:19 +08:00
for ( auto & run : heap_runs ) {
2019-01-26 02:26:14 +08:00
auto run_startt = std : : chrono : : high_resolution_clock : : now ( ) ;
2019-01-25 22:04:19 +08:00
setup_solve_cells ( & run ) ;
if ( solve_cells . empty ( ) )
continue ;
// Heuristic: don't bother with threading below a certain size
auto solve_startt = std : : chrono : : high_resolution_clock : : now ( ) ;
2019-01-24 22:05:16 +08:00
2021-02-23 18:53:35 +08:00
// Build the connectivity matrix and run the solver; multithreaded between x and y axes if applicable
2020-05-24 03:50:09 +08:00
# ifndef NPNR_DISABLE_THREADS
if ( solve_cells . size ( ) > = 500 ) {
2019-07-05 16:22:42 +08:00
boost : : thread xaxis ( [ & ] ( ) { build_solve_direction ( false , ( iter = = 0 ) ? - 1 : iter ) ; } ) ;
2019-01-26 02:26:14 +08:00
build_solve_direction ( true , ( iter = = 0 ) ? - 1 : iter ) ;
2019-01-25 22:04:19 +08:00
xaxis . join ( ) ;
2020-05-24 03:50:09 +08:00
} else
# endif
{
build_solve_direction ( false , ( iter = = 0 ) ? - 1 : iter ) ;
build_solve_direction ( true , ( iter = = 0 ) ? - 1 : iter ) ;
2019-01-25 22:04:19 +08:00
}
auto solve_endt = std : : chrono : : high_resolution_clock : : now ( ) ;
solve_time + = std : : chrono : : duration < double > ( solve_endt - solve_startt ) . count ( ) ;
update_all_chains ( ) ;
solved_hpwl = total_hpwl ( ) ;
2019-01-24 21:36:23 +08:00
2019-01-25 22:04:19 +08:00
update_all_chains ( ) ;
2020-02-02 23:40:23 +08:00
2021-02-23 18:53:35 +08:00
// Run the spreader
2020-02-02 23:40:23 +08:00
for ( const auto & group : cfg . cellGroups )
CutSpreader ( this , group ) . run ( ) ;
2021-06-02 00:52:25 +08:00
for ( auto type : run )
2020-02-02 23:40:23 +08:00
if ( std : : all_of ( cfg . cellGroups . begin ( ) , cfg . cellGroups . end ( ) ,
2021-06-02 00:52:25 +08:00
[ type ] ( const pool < BelBucketId > & grp ) { return ! grp . count ( type ) ; } ) )
2020-02-02 23:40:23 +08:00
CutSpreader ( this , { type } ) . run ( ) ;
2019-01-11 19:59:34 +08:00
2021-02-23 18:53:35 +08:00
// Run strict legalisation to find a valid bel for all cells
2019-01-25 22:04:19 +08:00
update_all_chains ( ) ;
2019-03-24 19:18:38 +08:00
spread_hpwl = total_hpwl ( ) ;
2019-01-26 21:22:44 +08:00
legalise_placement_strict ( true ) ;
2019-01-25 22:04:19 +08:00
update_all_chains ( ) ;
2019-01-24 21:36:23 +08:00
2019-01-25 22:04:19 +08:00
legal_hpwl = total_hpwl ( ) ;
2019-01-26 02:26:14 +08:00
auto run_stopt = std : : chrono : : high_resolution_clock : : now ( ) ;
2021-01-29 11:08:14 +08:00
2021-01-30 06:55:10 +08:00
IdString bucket_name = ctx - > getBelBucketName ( * run . begin ( ) ) ;
2019-03-25 23:39:15 +08:00
log_info ( " at iteration #%d, type %s: wirelen solved = %d, spread = %d, legal = %d; time = %.02fs \n " ,
2021-01-30 06:55:10 +08:00
iter + 1 , ( run . size ( ) > 1 ? " ALL " : bucket_name . c_str ( ctx ) ) , int ( solved_hpwl ) ,
2019-03-25 23:39:15 +08:00
int ( spread_hpwl ) , int ( legal_hpwl ) ,
std : : chrono : : duration < double > ( run_stopt - run_startt ) . count ( ) ) ;
2019-01-25 22:04:19 +08:00
}
2019-01-25 21:15:36 +08:00
2021-02-23 18:53:35 +08:00
// Update timing weights
2019-06-28 19:43:55 +08:00
if ( cfg . timing_driven )
2021-03-04 18:14:20 +08:00
tmg . run ( ) ;
2019-01-25 21:15:36 +08:00
2019-01-11 19:59:34 +08:00
if ( legal_hpwl < best_hpwl ) {
best_hpwl = legal_hpwl ;
stalled = 0 ;
2019-01-26 21:22:44 +08:00
// Save solution
solution . clear ( ) ;
2021-06-01 23:51:18 +08:00
for ( auto & cell : ctx - > cells ) {
2022-06-24 01:48:31 +08:00
if ( cell . second - > isPseudo ( ) )
continue ;
2021-06-01 23:51:18 +08:00
solution . emplace_back ( cell . second . get ( ) , cell . second - > bel , cell . second - > belStrength ) ;
2019-01-24 22:05:16 +08:00
}
2019-01-11 19:59:34 +08:00
} else {
+ + stalled ;
}
2019-01-24 22:05:16 +08:00
for ( auto & cl : cell_locs ) {
cl . second . legal_x = cl . second . x ;
cl . second . legal_y = cl . second . y ;
}
2019-01-11 19:59:34 +08:00
ctx - > yield ( ) ;
+ + iter ;
}
2019-01-24 22:05:16 +08:00
// Apply saved solution
for ( auto & sc : solution ) {
CellInfo * cell = std : : get < 0 > ( sc ) ;
if ( cell - > bel ! = BelId ( ) )
ctx - > unbindBel ( cell - > bel ) ;
}
for ( auto & sc : solution ) {
CellInfo * cell ;
BelId bel ;
PlaceStrength strength ;
std : : tie ( cell , bel , strength ) = sc ;
ctx - > bindBel ( bel , cell , strength ) ;
}
2021-06-01 23:51:18 +08:00
for ( auto & cell : ctx - > cells ) {
2022-06-24 01:48:31 +08:00
if ( cell . second - > isPseudo ( ) )
continue ;
2019-01-31 00:36:01 +08:00
if ( cell . second - > bel = = BelId ( ) )
log_error ( " Found unbound cell %s \n " , cell . first . c_str ( ctx ) ) ;
2021-06-01 23:51:18 +08:00
if ( ctx - > getBoundBelCell ( cell . second - > bel ) ! = cell . second . get ( ) )
2019-01-31 00:36:01 +08:00
log_error ( " Found cell %s with mismatched binding \n " , cell . first . c_str ( ctx ) ) ;
if ( ctx - > debug )
2021-01-29 20:12:12 +08:00
log_info ( " AP soln: %s -> %s \n " , cell . first . c_str ( ctx ) , ctx - > nameOfBel ( cell . second - > bel ) ) ;
2019-01-31 00:36:01 +08:00
}
2021-02-27 03:26:52 +08:00
bool any_bad_placements = false ;
for ( auto bel : ctx - > getBels ( ) ) {
CellInfo * cell = ctx - > getBoundBelCell ( bel ) ;
2022-12-07 17:27:58 +08:00
if ( ! ctx - > isBelLocationValid ( bel , /* explain_invalid */ true ) ) {
2021-02-27 03:26:52 +08:00
std : : string cell_text = " no cell " ;
if ( cell ! = nullptr )
cell_text = std : : string ( " cell ' " ) + ctx - > nameOf ( cell ) + " ' " ;
log_warning ( " post-placement validity check failed for Bel '%s' "
" (%s) \n " ,
ctx - > nameOfBel ( bel ) , cell_text . c_str ( ) ) ;
any_bad_placements = true ;
}
}
if ( any_bad_placements ) {
return false ;
}
2021-02-27 03:37:27 +08:00
auto endtt = std : : chrono : : high_resolution_clock : : now ( ) ;
log_info ( " HeAP Placer Time: %.02fs \n " , std : : chrono : : duration < double > ( endtt - startt ) . count ( ) ) ;
log_info ( " of which solving equations: %.02fs \n " , solve_time ) ;
log_info ( " of which spreading cells: %.02fs \n " , cl_time ) ;
log_info ( " of which strict legalisation: %.02fs \n " , sl_time ) ;
ctx - > check ( ) ;
2021-03-02 01:41:29 +08:00
lock . unlock_early ( ) ;
2021-02-27 03:37:27 +08:00
2022-03-09 01:24:29 +08:00
# if !defined(__wasm)
2021-12-20 03:29:44 +08:00
if ( cfg . parallelRefine ) {
if ( ! parallel_refine ( ctx , ParallelRefineCfg ( ctx ) ) ) {
return false ;
}
2022-03-09 01:24:29 +08:00
} else
# endif
{
2022-12-22 23:11:10 +08:00
auto placer1_cfg = Placer1Cfg ( ctx ) ;
placer1_cfg . hpwl_scale_x = cfg . hpwl_scale_x ;
placer1_cfg . hpwl_scale_y = cfg . hpwl_scale_y ;
placer1_cfg . netShareWeight = cfg . netShareWeight ;
if ( ! placer1_refine ( ctx , placer1_cfg ) ) {
2021-12-20 03:29:44 +08:00
return false ;
}
2021-02-27 03:26:52 +08:00
}
2019-01-24 00:36:53 +08:00
2019-01-11 00:42:29 +08:00
return true ;
}
2019-01-10 19:18:47 +08:00
private :
Context * ctx ;
2019-02-25 20:48:01 +08:00
PlacerHeapCfg cfg ;
2019-01-10 19:18:47 +08:00
2019-01-11 03:10:47 +08:00
int max_x = 0 , max_y = 0 ;
2021-01-29 07:40:26 +08:00
FastBels fast_bels ;
2021-06-02 00:52:25 +08:00
dict < IdString , std : : tuple < int , int > > bel_types ;
2019-01-10 19:18:47 +08:00
2021-03-04 18:14:20 +08:00
TimingAnalyser tmg ;
2021-06-02 00:52:25 +08:00
dict < IdString , BoundingBox > constraint_region_bounds ;
2019-11-26 18:01:33 +08:00
2019-01-10 19:18:47 +08:00
// In some cases, we can't use bindBel because we allow overlap in the earlier stages. So we use this custom
// structure instead
struct CellLocation
{
int x , y ;
2019-01-24 22:05:16 +08:00
int legal_x , legal_y ;
2019-01-15 23:20:38 +08:00
double rawx , rawy ;
2019-01-10 19:18:47 +08:00
bool locked , global ;
} ;
2021-06-02 00:52:25 +08:00
dict < IdString , CellLocation > cell_locs ;
2019-01-10 19:18:47 +08:00
// The set of cells that we will actually place. This excludes locked cells and children cells of macros/chains
// (only the root of each macro is placed.)
std : : vector < CellInfo * > place_cells ;
2019-01-11 19:31:56 +08:00
// The cells in the current equation being solved (a subset of place_cells in some cases, where we only place
// cells of a certain type)
std : : vector < CellInfo * > solve_cells ;
2021-06-02 00:52:25 +08:00
dict < ClusterId , std : : vector < CellInfo * > > cluster2cells ;
dict < ClusterId , int > chain_size ;
2019-01-24 21:36:23 +08:00
// Performance counting
double solve_time = 0 , cl_time = 0 , sl_time = 0 ;
2019-01-10 19:18:47 +08:00
// Place cells with the BEL attribute set to constrain them
void place_constraints ( )
{
size_t placed_cells = 0 ;
// Initial constraints placer
for ( auto & cell_entry : ctx - > cells ) {
CellInfo * cell = cell_entry . second . get ( ) ;
2022-06-24 01:48:31 +08:00
if ( cell - > isPseudo ( ) )
continue ;
2019-01-10 19:18:47 +08:00
auto loc = cell - > attrs . find ( ctx - > id ( " BEL " ) ) ;
if ( loc ! = cell - > attrs . end ( ) ) {
2019-08-01 21:28:21 +08:00
std : : string loc_name = loc - > second . as_string ( ) ;
2021-01-29 20:12:12 +08:00
BelId bel = ctx - > getBelByNameStr ( loc_name ) ;
2019-01-10 19:18:47 +08:00
if ( bel = = BelId ( ) ) {
log_error ( " No Bel named \' %s \' located for "
" this chip (processing BEL attribute on \' %s \' ) \n " ,
loc_name . c_str ( ) , cell - > name . c_str ( ctx ) ) ;
}
2021-01-29 07:40:26 +08:00
if ( ! ctx - > isValidBelForCellType ( cell - > type , bel ) ) {
IdString bel_type = ctx - > getBelType ( bel ) ;
2019-01-10 19:18:47 +08:00
log_error ( " Bel \' %s \' of type \' %s \' does not match cell "
" \' %s \' of type \' %s \' \n " ,
loc_name . c_str ( ) , bel_type . c_str ( ctx ) , cell - > name . c_str ( ctx ) , cell - > type . c_str ( ctx ) ) ;
}
auto bound_cell = ctx - > getBoundBelCell ( bel ) ;
if ( bound_cell ) {
log_error ( " Cell \' %s \' cannot be bound to bel \' %s \' since it is already bound to cell \' %s \' \n " ,
cell - > name . c_str ( ctx ) , loc_name . c_str ( ) , bound_cell - > name . c_str ( ctx ) ) ;
}
ctx - > bindBel ( bel , cell , STRENGTH_USER ) ;
2022-12-07 17:27:58 +08:00
if ( ! ctx - > isBelLocationValid ( bel , /* explain_invalid */ true ) ) {
2021-02-16 19:52:16 +08:00
IdString bel_type = ctx - > getBelType ( bel ) ;
log_error ( " Bel \' %s \' of type \' %s \' is not valid for cell "
" \' %s \' of type \' %s \' \n " ,
loc_name . c_str ( ) , bel_type . c_str ( ctx ) , cell - > name . c_str ( ctx ) , cell - > type . c_str ( ctx ) ) ;
}
2019-01-10 19:18:47 +08:00
placed_cells + + ;
}
}
log_info ( " Placed %d cells based on constraints. \n " , int ( placed_cells ) ) ;
ctx - > yield ( ) ;
}
2021-01-30 03:11:05 +08:00
void build_fast_bels ( )
2019-01-10 19:18:47 +08:00
{
for ( auto bel : ctx - > getBels ( ) ) {
if ( ! ctx - > checkBelAvail ( bel ) )
continue ;
Loc loc = ctx - > getBelLocation ( bel ) ;
max_x = std : : max ( max_x , loc . x ) ;
max_y = std : : max ( max_y , loc . y ) ;
}
2019-01-11 03:10:47 +08:00
2021-06-02 00:52:25 +08:00
pool < IdString > cell_types_in_use ;
pool < BelBucketId > buckets_in_use ;
2021-06-01 23:51:18 +08:00
for ( auto & cell : ctx - > cells ) {
2022-06-24 01:48:31 +08:00
if ( cell . second - > isPseudo ( ) )
continue ;
2021-01-30 03:11:05 +08:00
IdString cell_type = cell . second - > type ;
cell_types_in_use . insert ( cell_type ) ;
2021-01-30 06:55:10 +08:00
BelBucketId bucket = ctx - > getBelBucketForCellType ( cell_type ) ;
buckets_in_use . insert ( bucket ) ;
2021-01-30 03:11:05 +08:00
}
2021-02-02 06:28:32 +08:00
for ( auto cell_type : cell_types_in_use ) {
2021-01-30 03:11:05 +08:00
fast_bels . addCellType ( cell_type ) ;
}
2021-02-02 06:28:32 +08:00
for ( auto bucket : buckets_in_use ) {
2021-01-30 06:55:10 +08:00
fast_bels . addBelBucket ( bucket ) ;
2021-01-30 03:11:05 +08:00
}
2019-11-26 18:01:33 +08:00
// Determine bounding boxes of region constraints
2021-06-01 23:51:18 +08:00
for ( auto & region : ctx - > region ) {
Region * r = region . second . get ( ) ;
2019-11-26 18:01:33 +08:00
BoundingBox bb ;
if ( r - > constr_bels ) {
bb . x0 = std : : numeric_limits < int > : : max ( ) ;
bb . x1 = std : : numeric_limits < int > : : min ( ) ;
bb . y0 = std : : numeric_limits < int > : : max ( ) ;
bb . y1 = std : : numeric_limits < int > : : min ( ) ;
for ( auto bel : r - > bels ) {
Loc loc = ctx - > getBelLocation ( bel ) ;
bb . x0 = std : : min ( bb . x0 , loc . x ) ;
bb . x1 = std : : max ( bb . x1 , loc . x ) ;
bb . y0 = std : : min ( bb . y0 , loc . y ) ;
bb . y1 = std : : max ( bb . y1 , loc . y ) ;
}
} else {
bb . x0 = 0 ;
bb . y0 = 0 ;
bb . x1 = max_x ;
bb . y1 = max_y ;
}
constraint_region_bounds [ r - > name ] = bb ;
}
2019-01-10 19:18:47 +08:00
}
2019-01-25 19:57:58 +08:00
// Build and solve in one direction
2019-01-26 21:22:44 +08:00
void build_solve_direction ( bool yaxis , int iter )
{
2019-01-25 19:57:58 +08:00
for ( int i = 0 ; i < 5 ; i + + ) {
EquationSystem < double > esx ( solve_cells . size ( ) , solve_cells . size ( ) ) ;
build_equations ( esx , yaxis , iter ) ;
solve_equations ( esx , yaxis ) ;
}
}
2019-01-11 03:10:47 +08:00
// Check if a cell has any meaningful connectivity
bool has_connectivity ( CellInfo * cell )
{
for ( auto port : cell - > ports ) {
if ( port . second . net ! = nullptr & & port . second . net - > driver . cell ! = nullptr & &
! port . second . net - > users . empty ( ) )
return true ;
}
return false ;
}
2019-01-10 19:18:47 +08:00
// Build up a random initial placement, without regard to legality
// FIXME: Are there better approaches to the initial placement (e.g. greedy?)
void seed_placement ( )
{
2021-06-02 00:52:25 +08:00
pool < IdString > cell_types ;
2021-01-29 10:32:42 +08:00
for ( const auto & cell : ctx - > cells ) {
2022-06-24 01:48:31 +08:00
if ( cell . second - > isPseudo ( ) )
continue ;
2021-01-29 10:32:42 +08:00
cell_types . insert ( cell . second - > type ) ;
}
2021-06-02 00:52:25 +08:00
pool < BelId > bels_used ;
dict < IdString , std : : deque < BelId > > available_bels ;
2021-01-29 10:32:42 +08:00
2019-01-10 19:18:47 +08:00
for ( auto bel : ctx - > getBels ( ) ) {
2021-01-29 10:32:42 +08:00
if ( ! ctx - > checkBelAvail ( bel ) ) {
2019-01-10 19:18:47 +08:00
continue ;
2021-01-29 10:32:42 +08:00
}
2021-02-02 06:28:32 +08:00
for ( auto cell_type : cell_types ) {
if ( ctx - > isValidBelForCellType ( cell_type , bel ) ) {
2021-01-29 10:32:42 +08:00
available_bels [ cell_type ] . push_back ( bel ) ;
}
}
2019-01-10 19:18:47 +08:00
}
2021-01-29 10:32:42 +08:00
2019-01-24 00:36:53 +08:00
for ( auto & t : available_bels ) {
2020-12-31 01:53:32 +08:00
ctx - > shuffle ( t . second . begin ( ) , t . second . end ( ) ) ;
2019-01-24 00:36:53 +08:00
}
2021-01-29 10:32:42 +08:00
2021-06-01 23:51:18 +08:00
for ( auto & cell : ctx - > cells ) {
CellInfo * ci = cell . second . get ( ) ;
2022-06-24 01:48:31 +08:00
if ( ci - > isPseudo ( ) ) {
Loc loc = ci - > pseudo_cell - > getLocation ( ) ;
cell_locs [ cell . first ] . x = loc . x ;
cell_locs [ cell . first ] . y = loc . y ;
cell_locs [ cell . first ] . locked = true ;
cell_locs [ cell . first ] . global = false ;
continue ;
}
2019-01-10 19:18:47 +08:00
if ( ci - > bel ! = BelId ( ) ) {
Loc loc = ctx - > getBelLocation ( ci - > bel ) ;
cell_locs [ cell . first ] . x = loc . x ;
cell_locs [ cell . first ] . y = loc . y ;
cell_locs [ cell . first ] . locked = true ;
cell_locs [ cell . first ] . global = ctx - > getBelGlobalBuf ( ci - > bel ) ;
2021-04-28 22:43:02 +08:00
} else if ( ci - > cluster = = ClusterId ( ) | | ctx - > getClusterRootCell ( ci - > cluster ) = = ci ) {
2019-01-24 00:36:53 +08:00
bool placed = false ;
2020-06-26 02:42:53 +08:00
int attempt_count = 0 ;
2019-01-24 00:36:53 +08:00
while ( ! placed ) {
2020-06-26 02:42:53 +08:00
+ + attempt_count ;
2021-01-29 10:32:42 +08:00
if ( attempt_count > 25000 ) {
2020-06-26 02:42:53 +08:00
log_error ( " Unable to find a placement location for cell '%s' \n " , ci - > name . c_str ( ctx ) ) ;
2021-01-29 10:32:42 +08:00
}
2021-02-01 23:26:54 +08:00
// Make sure this cell type is in the available BEL map at
// all.
if ( ! available_bels . count ( ci - > type ) ) {
log_error ( " Unable to place cell '%s', no BELs remaining to implement cell type '%s' \n " ,
2021-02-02 06:28:32 +08:00
ci - > name . c_str ( ctx ) , ci - > type . c_str ( ctx ) ) ;
2021-02-01 23:26:54 +08:00
}
2021-01-29 10:32:42 +08:00
// Find an unused BEL from bels_for_cell_type.
auto & bels_for_cell_type = available_bels . at ( ci - > type ) ;
BelId bel ;
2021-02-02 06:28:32 +08:00
while ( true ) {
2021-02-01 23:26:54 +08:00
if ( bels_for_cell_type . empty ( ) ) {
log_error ( " Unable to place cell '%s', no BELs remaining to implement cell type '%s' \n " ,
2021-02-02 06:28:32 +08:00
ci - > name . c_str ( ctx ) , ci - > type . c_str ( ctx ) ) ;
2021-02-01 23:26:54 +08:00
}
2021-01-29 10:32:42 +08:00
BelId candidate_bel = bels_for_cell_type . back ( ) ;
bels_for_cell_type . pop_back ( ) ;
2021-02-02 06:28:32 +08:00
if ( bels_used . count ( candidate_bel ) ) {
2021-01-29 10:32:42 +08:00
// candidate_bel has already been used by another
// cell type, skip it.
continue ;
}
bel = candidate_bel ;
break ;
}
2019-01-24 00:36:53 +08:00
Loc loc = ctx - > getBelLocation ( bel ) ;
cell_locs [ cell . first ] . x = loc . x ;
cell_locs [ cell . first ] . y = loc . y ;
cell_locs [ cell . first ] . locked = false ;
cell_locs [ cell . first ] . global = ctx - > getBelGlobalBuf ( bel ) ;
2021-01-29 10:32:42 +08:00
2019-01-24 00:36:53 +08:00
// FIXME
2021-06-01 23:51:18 +08:00
if ( has_connectivity ( cell . second . get ( ) ) & & ! cfg . ioBufTypes . count ( ci - > type ) ) {
2021-01-29 10:32:42 +08:00
bels_used . insert ( bel ) ;
2019-01-24 00:36:53 +08:00
place_cells . push_back ( ci ) ;
placed = true ;
} else {
2021-02-16 19:52:16 +08:00
ctx - > bindBel ( bel , ci , STRENGTH_STRONG ) ;
if ( ctx - > isBelLocationValid ( bel ) ) {
2019-01-24 00:36:53 +08:00
cell_locs [ cell . first ] . locked = true ;
placed = true ;
2021-01-29 10:32:42 +08:00
bels_used . insert ( bel ) ;
2019-01-24 00:36:53 +08:00
} else {
2021-02-16 19:52:16 +08:00
ctx - > unbindBel ( bel ) ;
2019-01-24 00:36:53 +08:00
available_bels . at ( ci - > type ) . push_front ( bel ) ;
}
}
2019-01-11 03:10:47 +08:00
}
2019-01-10 19:18:47 +08:00
}
}
}
2019-01-11 19:31:56 +08:00
// Setup the cells to be solved, returns the number of rows
2021-06-02 00:52:25 +08:00
int setup_solve_cells ( pool < BelBucketId > * buckets = nullptr )
2019-01-11 19:59:34 +08:00
{
2019-01-11 19:31:56 +08:00
int row = 0 ;
solve_cells . clear ( ) ;
// First clear the udata of all cells
2022-06-24 01:48:31 +08:00
for ( auto & cell : ctx - > cells ) {
2019-01-11 19:31:56 +08:00
cell . second - > udata = dont_solve ;
2022-06-24 01:48:31 +08:00
}
2019-01-11 19:31:56 +08:00
// Then update cells to be placed, which excludes cell children
for ( auto cell : place_cells ) {
2021-01-30 06:55:10 +08:00
if ( buckets & & ! buckets - > count ( ctx - > getBelBucketForCellType ( cell - > type ) ) )
2019-01-11 19:31:56 +08:00
continue ;
cell - > udata = row + + ;
solve_cells . push_back ( cell ) ;
}
// Finally, update the udata of children
2021-04-28 22:43:02 +08:00
for ( auto & cluster : cluster2cells )
for ( auto child : cluster . second )
child - > udata = ctx - > getClusterRootCell ( cluster . first ) - > udata ;
2019-01-11 19:31:56 +08:00
return row ;
}
2019-01-10 19:18:47 +08:00
// Update all chains
void update_all_chains ( )
{
for ( auto cell : place_cells ) {
2019-01-11 19:59:34 +08:00
chain_size [ cell - > name ] = 1 ;
2021-04-28 22:43:02 +08:00
if ( cell - > cluster ! = ClusterId ( ) ) {
2021-06-12 20:07:11 +08:00
const auto base = cell_locs [ cell - > name ] ;
2021-04-28 22:43:02 +08:00
for ( auto child : cluster2cells . at ( cell - > cluster ) ) {
2022-12-17 18:49:51 +08:00
if ( child ! = cell )
2021-04-28 22:43:02 +08:00
chain_size [ cell - > name ] + + ;
Loc offset = ctx - > getClusterOffset ( child ) ;
cell_locs [ child - > name ] . x = std : : max ( 0 , std : : min ( max_x , base . x + offset . x ) ) ;
cell_locs [ child - > name ] . y = std : : max ( 0 , std : : min ( max_y , base . y + offset . y ) ) ;
}
}
2019-01-10 19:18:47 +08:00
}
}
// Run a function on all ports of a net - including the driver and all users
template < typename Tf > void foreach_port ( NetInfo * net , Tf func )
{
if ( net - > driver . cell ! = nullptr )
2022-02-26 23:17:46 +08:00
func ( net - > driver , store_index < PortRef > ( ) ) ;
for ( auto usr : net - > users . enumerate ( ) )
func ( usr . value , usr . index ) ;
2019-01-10 19:18:47 +08:00
}
// Build the system of equations for either X or Y
2019-01-11 19:59:34 +08:00
void build_equations ( EquationSystem < double > & es , bool yaxis , int iter = - 1 )
2019-01-10 19:18:47 +08:00
{
// Return the x or y position of a cell, depending on ydir
auto cell_pos = [ & ] ( CellInfo * cell ) { return yaxis ? cell_locs . at ( cell - > name ) . y : cell_locs . at ( cell - > name ) . x ; } ;
2019-01-26 21:22:44 +08:00
auto legal_pos = [ & ] ( CellInfo * cell ) {
return yaxis ? cell_locs . at ( cell - > name ) . legal_y : cell_locs . at ( cell - > name ) . legal_x ;
} ;
2019-01-10 19:18:47 +08:00
es . reset ( ) ;
2021-06-01 23:51:18 +08:00
for ( auto & net : ctx - > nets ) {
NetInfo * ni = net . second . get ( ) ;
2019-01-10 19:18:47 +08:00
if ( ni - > driver . cell = = nullptr )
continue ;
if ( ni - > users . empty ( ) )
continue ;
if ( cell_locs . at ( ni - > driver . cell - > name ) . global )
continue ;
// Find the bounds of the net in this axis, and the ports that correspond to these bounds
PortRef * lbport = nullptr , * ubport = nullptr ;
int lbpos = std : : numeric_limits < int > : : max ( ) , ubpos = std : : numeric_limits < int > : : min ( ) ;
2022-02-26 23:17:46 +08:00
foreach_port ( ni , [ & ] ( PortRef & port , store_index < PortRef > user_idx ) {
2019-01-10 19:18:47 +08:00
int pos = cell_pos ( port . cell ) ;
if ( pos < lbpos ) {
lbpos = pos ;
lbport = & port ;
}
if ( pos > ubpos ) {
ubpos = pos ;
ubport = & port ;
}
} ) ;
2019-01-11 03:10:47 +08:00
NPNR_ASSERT ( lbport ! = nullptr ) ;
NPNR_ASSERT ( ubport ! = nullptr ) ;
2019-01-11 19:31:56 +08:00
auto stamp_equation = [ & ] ( PortRef & var , PortRef & eqn , double weight ) {
if ( eqn . cell - > udata = = dont_solve )
return ;
int row = eqn . cell - > udata ;
int v_pos = cell_pos ( var . cell ) ;
if ( var . cell - > udata ! = dont_solve ) {
es . add_coeff ( row , var . cell - > udata , weight ) ;
} else {
es . add_rhs ( row , - v_pos * weight ) ;
}
2021-04-28 22:43:02 +08:00
if ( var . cell - > cluster ! = ClusterId ( ) ) {
Loc offset = ctx - > getClusterOffset ( var . cell ) ;
es . add_rhs ( row , - ( yaxis ? offset . y : offset . x ) * weight ) ;
2019-01-11 19:31:56 +08:00
}
} ;
2019-01-10 19:18:47 +08:00
// Add all relevant connections to the matrix
2022-02-26 23:17:46 +08:00
foreach_port ( ni , [ & ] ( PortRef & port , store_index < PortRef > user_idx ) {
2019-01-10 19:18:47 +08:00
int this_pos = cell_pos ( port . cell ) ;
auto process_arc = [ & ] ( PortRef * other ) {
if ( other = = & port )
return ;
int o_pos = cell_pos ( other - > cell ) ;
2022-02-26 23:17:46 +08:00
double weight = 1.0 / ( ni - > users . entries ( ) *
2020-02-02 23:52:28 +08:00
std : : max < double > ( 1 , ( yaxis ? cfg . hpwl_scale_y : cfg . hpwl_scale_x ) *
std : : abs ( o_pos - this_pos ) ) ) ;
2019-01-25 21:15:36 +08:00
2022-02-26 23:17:46 +08:00
if ( user_idx ) {
2021-03-04 18:14:20 +08:00
weight * = ( 1.0 + cfg . timingWeight * std : : pow ( tmg . get_criticality ( CellPortKey ( port ) ) ,
cfg . criticalityExponent ) ) ;
2019-01-25 21:15:36 +08:00
}
2019-01-10 19:18:47 +08:00
// If cell 0 is not fixed, it will stamp +w on its equation and -w on the other end's equation,
// if the other end isn't fixed
2019-01-11 19:31:56 +08:00
stamp_equation ( port , port , weight ) ;
stamp_equation ( port , * other , - weight ) ;
stamp_equation ( * other , * other , weight ) ;
stamp_equation ( * other , port , - weight ) ;
2019-01-10 19:18:47 +08:00
} ;
process_arc ( lbport ) ;
process_arc ( ubport ) ;
} ) ;
}
2019-01-11 19:59:34 +08:00
if ( iter ! = - 1 ) {
2019-02-25 20:48:01 +08:00
float alpha = cfg . alpha ;
2019-01-11 19:59:34 +08:00
for ( size_t row = 0 ; row < solve_cells . size ( ) ; row + + ) {
2019-01-24 22:05:16 +08:00
int l_pos = legal_pos ( solve_cells . at ( row ) ) ;
int c_pos = cell_pos ( solve_cells . at ( row ) ) ;
2020-02-02 23:52:28 +08:00
double weight =
alpha * iter /
std : : max < double > ( 1 , ( yaxis ? cfg . hpwl_scale_y : cfg . hpwl_scale_x ) * std : : abs ( l_pos - c_pos ) ) ;
2019-01-11 19:59:34 +08:00
// Add an arc from legalised to current position
es . add_coeff ( row , row , weight ) ;
2019-01-24 22:05:16 +08:00
es . add_rhs ( row , weight * l_pos ) ;
2019-01-11 19:59:34 +08:00
}
}
2019-01-10 19:18:47 +08:00
}
2019-01-11 00:42:29 +08:00
// Build the system of equations for either X or Y
2019-01-22 23:16:00 +08:00
void solve_equations ( EquationSystem < double > & es , bool yaxis )
{
2019-01-11 00:42:29 +08:00
// Return the x or y position of a cell, depending on ydir
auto cell_pos = [ & ] ( CellInfo * cell ) { return yaxis ? cell_locs . at ( cell - > name ) . y : cell_locs . at ( cell - > name ) . x ; } ;
std : : vector < double > vals ;
2019-01-11 19:59:34 +08:00
std : : transform ( solve_cells . begin ( ) , solve_cells . end ( ) , std : : back_inserter ( vals ) , cell_pos ) ;
2020-02-02 23:28:11 +08:00
es . solve ( vals , cfg . solverTolerance ) ;
2019-01-11 03:10:47 +08:00
for ( size_t i = 0 ; i < vals . size ( ) ; i + + )
2019-01-15 23:20:38 +08:00
if ( yaxis ) {
cell_locs . at ( solve_cells . at ( i ) - > name ) . rawy = vals . at ( i ) ;
2019-01-23 23:02:49 +08:00
cell_locs . at ( solve_cells . at ( i ) - > name ) . y = std : : min ( max_y , std : : max ( 0 , int ( vals . at ( i ) ) ) ) ;
2019-11-26 18:01:33 +08:00
if ( solve_cells . at ( i ) - > region ! = nullptr )
cell_locs . at ( solve_cells . at ( i ) - > name ) . y =
limit_to_reg ( solve_cells . at ( i ) - > region , cell_locs . at ( solve_cells . at ( i ) - > name ) . y , true ) ;
2019-01-15 23:20:38 +08:00
} else {
cell_locs . at ( solve_cells . at ( i ) - > name ) . rawx = vals . at ( i ) ;
2019-01-23 23:02:49 +08:00
cell_locs . at ( solve_cells . at ( i ) - > name ) . x = std : : min ( max_x , std : : max ( 0 , int ( vals . at ( i ) ) ) ) ;
2019-11-26 18:01:33 +08:00
if ( solve_cells . at ( i ) - > region ! = nullptr )
cell_locs . at ( solve_cells . at ( i ) - > name ) . x =
limit_to_reg ( solve_cells . at ( i ) - > region , cell_locs . at ( solve_cells . at ( i ) - > name ) . x , false ) ;
2019-01-15 23:20:38 +08:00
}
2019-01-11 03:10:47 +08:00
}
// Compute HPWL
wirelen_t total_hpwl ( )
{
wirelen_t hpwl = 0 ;
2021-06-01 23:51:18 +08:00
for ( auto & net : ctx - > nets ) {
NetInfo * ni = net . second . get ( ) ;
2019-01-11 03:10:47 +08:00
if ( ni - > driver . cell = = nullptr )
continue ;
CellLocation & drvloc = cell_locs . at ( ni - > driver . cell - > name ) ;
if ( drvloc . global )
continue ;
int xmin = drvloc . x , xmax = drvloc . x , ymin = drvloc . y , ymax = drvloc . y ;
for ( auto & user : ni - > users ) {
CellLocation & usrloc = cell_locs . at ( user . cell - > name ) ;
xmin = std : : min ( xmin , usrloc . x ) ;
xmax = std : : max ( xmax , usrloc . x ) ;
ymin = std : : min ( ymin , usrloc . y ) ;
ymax = std : : max ( ymax , usrloc . y ) ;
}
2020-02-02 23:52:28 +08:00
hpwl + = cfg . hpwl_scale_x * ( xmax - xmin ) + cfg . hpwl_scale_y * ( ymax - ymin ) ;
2019-01-11 03:10:47 +08:00
}
return hpwl ;
2019-01-11 00:42:29 +08:00
}
2019-01-11 19:31:56 +08:00
2019-01-26 21:22:44 +08:00
// Strict placement legalisation, performed after the initial HeAP spreading
void legalise_placement_strict ( bool require_validity = false )
2019-01-11 19:59:34 +08:00
{
2019-01-24 21:36:23 +08:00
auto startt = std : : chrono : : high_resolution_clock : : now ( ) ;
2019-01-11 19:59:34 +08:00
// Unbind all cells placed in this solution
2021-06-01 23:51:18 +08:00
for ( auto & cell : ctx - > cells ) {
CellInfo * ci = cell . second . get ( ) ;
2021-04-28 22:43:02 +08:00
if ( ci - > bel ! = BelId ( ) & &
( ci - > udata ! = dont_solve | |
( ci - > cluster ! = ClusterId ( ) & & ctx - > getClusterRootCell ( ci - > cluster ) - > udata ! = dont_solve ) ) )
2019-01-11 19:59:34 +08:00
ctx - > unbindBel ( ci - > bel ) ;
}
// At the moment we don't follow the full HeAP algorithm using cuts for legalisation, instead using
// the simple greedy largest-macro-first approach.
std : : priority_queue < std : : pair < int , IdString > > remaining ;
for ( auto cell : solve_cells ) {
remaining . emplace ( chain_size [ cell - > name ] , cell - > name ) ;
}
2019-01-31 01:31:33 +08:00
int ripup_radius = 2 ;
int total_iters = 0 ;
2019-11-26 18:16:27 +08:00
int total_iters_noreset = 0 ;
2019-01-11 19:59:34 +08:00
while ( ! remaining . empty ( ) ) {
auto top = remaining . top ( ) ;
remaining . pop ( ) ;
CellInfo * ci = ctx - > cells . at ( top . second ) . get ( ) ;
// Was now placed, ignore
if ( ci - > bel ! = BelId ( ) )
continue ;
2022-12-17 18:49:51 +08:00
// log_info(" Legalising %s (%s) %d\n", top.second.c_str(ctx), ci->type.c_str(ctx), top.first);
2021-01-29 07:40:26 +08:00
FastBels : : FastBelsData * fb ;
fast_bels . getBelsForCellType ( ci - > type , & fb ) ;
2019-01-11 19:59:34 +08:00
int radius = 0 ;
int iter = 0 ;
2019-01-26 03:24:54 +08:00
int iter_at_radius = 0 ;
2022-12-18 07:50:14 +08:00
int total_iters_for_cell = 0 ;
2019-01-11 19:59:34 +08:00
bool placed = false ;
2019-01-26 03:24:54 +08:00
BelId bestBel ;
int best_inp_len = std : : numeric_limits < int > : : max ( ) ;
2019-01-31 01:31:33 +08:00
total_iters + + ;
2019-11-26 18:16:27 +08:00
total_iters_noreset + + ;
2019-01-31 01:31:33 +08:00
if ( total_iters > int ( solve_cells . size ( ) ) ) {
total_iters = 0 ;
ripup_radius = std : : max ( std : : max ( max_x , max_y ) , ripup_radius * 2 ) ;
}
2019-11-27 05:36:29 +08:00
if ( total_iters_noreset > std : : max ( 5000 , 8 * int ( ctx - > cells . size ( ) ) ) ) {
2019-11-26 18:16:27 +08:00
log_error ( " Unable to find legal placement for all cells, design is probably at utilisation limit. \n " ) ;
}
2019-01-11 19:59:34 +08:00
while ( ! placed ) {
2022-12-21 03:15:06 +08:00
if ( cfg . cell_placement_timeout > 0 & & total_iters_for_cell > cfg . cell_placement_timeout )
log_error ( " Unable to find legal placement for cell '%s' after %d attempts, check constraints and utilisation. Use `--placer-heap-cell-placement-timeout` to change the number of attempts. \n " ,
2022-12-18 07:50:14 +08:00
ctx - > nameOf ( ci ) , total_iters_for_cell ) ;
2019-11-26 18:16:27 +08:00
2021-02-23 18:53:35 +08:00
// Determine a search radius around the solver location (which increases over time) that is clamped to
// the region constraint for the cell (if applicable)
2019-11-26 18:01:33 +08:00
int rx = radius , ry = radius ;
if ( ci - > region ! = nullptr ) {
rx = std : : min ( radius , ( constraint_region_bounds [ ci - > region - > name ] . x1 -
constraint_region_bounds [ ci - > region - > name ] . x0 ) /
2 +
1 ) ;
ry = std : : min ( radius , ( constraint_region_bounds [ ci - > region - > name ] . y1 -
constraint_region_bounds [ ci - > region - > name ] . y0 ) /
2 +
1 ) ;
}
2021-02-23 18:53:35 +08:00
// Pick a random X and Y location within our search radius
2019-11-26 18:01:33 +08:00
int nx = ctx - > rng ( 2 * rx + 1 ) + std : : max ( cell_locs . at ( ci - > name ) . x - rx , 0 ) ;
int ny = ctx - > rng ( 2 * ry + 1 ) + std : : max ( cell_locs . at ( ci - > name ) . y - ry , 0 ) ;
2019-01-11 19:59:34 +08:00
iter + + ;
2019-01-26 03:24:54 +08:00
iter_at_radius + + ;
if ( iter > = ( 10 * ( radius + 1 ) ) ) {
2021-02-23 18:53:35 +08:00
// No luck yet, increase radius
2019-01-11 19:59:34 +08:00
radius = std : : min ( std : : max ( max_x , max_y ) , radius + 1 ) ;
2019-01-31 01:31:33 +08:00
while ( radius < std : : max ( max_x , max_y ) ) {
2021-02-23 18:53:35 +08:00
// Keep increasing the radius until it will actually increase the number of cells we are
// checking (e.g. BRAM and DSP will not be in all cols/rows), so we don't waste effort
2019-01-31 01:31:33 +08:00
for ( int x = std : : max ( 0 , cell_locs . at ( ci - > name ) . x - radius ) ;
x < = std : : min ( max_x , cell_locs . at ( ci - > name ) . x + radius ) ; x + + ) {
2021-01-29 07:40:26 +08:00
if ( x > = int ( fb - > size ( ) ) )
2019-01-31 01:31:33 +08:00
break ;
for ( int y = std : : max ( 0 , cell_locs . at ( ci - > name ) . y - radius ) ;
y < = std : : min ( max_y , cell_locs . at ( ci - > name ) . y + radius ) ; y + + ) {
2021-01-29 07:40:26 +08:00
if ( y > = int ( fb - > at ( x ) . size ( ) ) )
2019-01-31 01:31:33 +08:00
break ;
2021-01-29 07:40:26 +08:00
if ( fb - > at ( x ) . at ( y ) . size ( ) > 0 )
2019-01-31 01:31:33 +08:00
goto notempty ;
}
}
radius = std : : min ( std : : max ( max_x , max_y ) , radius + 1 ) ;
}
notempty :
2019-01-26 03:24:54 +08:00
iter_at_radius = 0 ;
iter = 0 ;
}
2021-02-23 18:53:35 +08:00
// If our randomly chosen cooridnate is out of bounds; or points to a tile with no relevant bels; ignore
// it
2019-01-11 19:59:34 +08:00
if ( nx < 0 | | nx > max_x )
continue ;
2019-01-24 22:05:16 +08:00
if ( ny < 0 | | ny > max_y )
2019-01-11 19:59:34 +08:00
continue ;
2021-01-29 07:40:26 +08:00
if ( nx > = int ( fb - > size ( ) ) )
2019-01-11 19:59:34 +08:00
continue ;
2021-01-29 07:40:26 +08:00
if ( ny > = int ( fb - > at ( nx ) . size ( ) ) )
2019-01-11 19:59:34 +08:00
continue ;
2021-01-29 07:40:26 +08:00
if ( fb - > at ( nx ) . at ( ny ) . empty ( ) )
2019-01-11 19:59:34 +08:00
continue ;
2021-02-23 18:53:35 +08:00
// The number of attempts to find a location to try
2019-01-26 03:24:54 +08:00
int need_to_explore = 2 * radius ;
2021-02-23 18:53:35 +08:00
// If we have found at least one legal location; and made enough attempts; assume it's good enough and
// finish
2019-01-26 03:24:54 +08:00
if ( iter_at_radius > = need_to_explore & & bestBel ! = BelId ( ) ) {
CellInfo * bound = ctx - > getBoundBelCell ( bestBel ) ;
if ( bound ! = nullptr ) {
ctx - > unbindBel ( bound - > bel ) ;
remaining . emplace ( chain_size [ bound - > name ] , bound - > name ) ;
}
ctx - > bindBel ( bestBel , ci , STRENGTH_WEAK ) ;
placed = true ;
Loc loc = ctx - > getBelLocation ( bestBel ) ;
cell_locs [ ci - > name ] . x = loc . x ;
cell_locs [ ci - > name ] . y = loc . y ;
break ;
}
2021-04-28 22:43:02 +08:00
if ( ci - > cluster = = ClusterId ( ) ) {
2021-02-23 18:53:35 +08:00
// The case where we have no relative constraints
2021-01-29 07:40:26 +08:00
for ( auto sz : fb - > at ( nx ) . at ( ny ) ) {
2021-02-23 18:53:35 +08:00
// Look through all bels in this tile; checking region constraint if applicable
2021-02-23 19:42:33 +08:00
if ( ! ci - > testRegion ( sz ) )
2019-11-26 18:01:33 +08:00
continue ;
2021-02-23 18:53:35 +08:00
// Prefer available bels; unless we are dealing with a wide radius (e.g. difficult control sets)
// or occasionally trigger a tiebreaker
2019-01-31 03:28:15 +08:00
if ( ctx - > checkBelAvail ( sz ) | | ( radius > ripup_radius | | ctx - > rng ( 20000 ) < 10 ) ) {
2019-01-11 19:59:34 +08:00
CellInfo * bound = ctx - > getBoundBelCell ( sz ) ;
if ( bound ! = nullptr ) {
2021-02-23 18:53:35 +08:00
// Only rip up cells without constraints
2021-04-28 22:43:02 +08:00
if ( bound - > cluster ! = ClusterId ( ) )
2019-01-11 19:59:34 +08:00
continue ;
ctx - > unbindBel ( bound - > bel ) ;
}
2021-02-23 18:53:35 +08:00
// Provisionally bind the bel
2019-01-11 19:59:34 +08:00
ctx - > bindBel ( sz , ci , STRENGTH_WEAK ) ;
if ( require_validity & & ! ctx - > isBelLocationValid ( sz ) ) {
2021-02-23 18:53:35 +08:00
// New location is not legal; unbind the cell (and rebind the cell we ripped up if
// applicable)
2019-01-11 19:59:34 +08:00
ctx - > unbindBel ( sz ) ;
if ( bound ! = nullptr )
ctx - > bindBel ( sz , bound , STRENGTH_WEAK ) ;
2019-01-26 03:24:54 +08:00
} else if ( iter_at_radius < need_to_explore ) {
2021-02-23 18:53:35 +08:00
// It's legal, but we haven't tried enough locations yet
2019-01-26 03:24:54 +08:00
ctx - > unbindBel ( sz ) ;
if ( bound ! = nullptr )
ctx - > bindBel ( sz , bound , STRENGTH_WEAK ) ;
int input_len = 0 ;
2021-02-23 18:53:35 +08:00
// Compute a fast input wirelength metric at this bel; and save if better than our last
// try
2019-01-26 03:24:54 +08:00
for ( auto & port : ci - > ports ) {
auto & p = port . second ;
if ( p . type ! = PORT_IN | | p . net = = nullptr | | p . net - > driver . cell = = nullptr )
continue ;
CellInfo * drv = p . net - > driver . cell ;
auto drv_loc = cell_locs . find ( drv - > name ) ;
if ( drv_loc = = cell_locs . end ( ) )
continue ;
if ( drv_loc - > second . global )
continue ;
input_len + = std : : abs ( drv_loc - > second . x - nx ) + std : : abs ( drv_loc - > second . y - ny ) ;
}
if ( input_len < best_inp_len ) {
best_inp_len = input_len ;
bestBel = sz ;
}
break ;
2019-01-11 19:59:34 +08:00
} else {
2021-02-23 18:53:35 +08:00
// It's legal, and we've tried enough. Finish.
2019-01-26 03:24:54 +08:00
if ( bound ! = nullptr )
remaining . emplace ( chain_size [ bound - > name ] , bound - > name ) ;
2019-01-11 19:59:34 +08:00
Loc loc = ctx - > getBelLocation ( sz ) ;
cell_locs [ ci - > name ] . x = loc . x ;
cell_locs [ ci - > name ] . y = loc . y ;
placed = true ;
break ;
}
}
}
} else {
2021-02-23 18:53:35 +08:00
// We do have relative constraints
2021-01-29 07:40:26 +08:00
for ( auto sz : fb - > at ( nx ) . at ( ny ) ) {
2021-02-23 18:53:35 +08:00
// List of cells and their destination
2019-01-31 00:36:01 +08:00
std : : vector < std : : pair < CellInfo * , BelId > > targets ;
2021-02-23 18:53:35 +08:00
// List of bels we placed things at; and the cell that was there before if applicable
2019-01-31 00:36:01 +08:00
std : : vector < std : : pair < BelId , CellInfo * > > swaps_made ;
2021-04-28 22:43:02 +08:00
if ( ! ctx - > getClusterPlacement ( ci - > cluster , sz , targets ) )
continue ;
for ( auto & target : targets ) {
2021-02-23 18:53:35 +08:00
// Check it satisfies the region constraint if applicable
2021-04-28 22:43:02 +08:00
if ( ! target . first - > testRegion ( target . second ) )
2019-01-31 00:36:01 +08:00
goto fail ;
2021-04-28 22:43:02 +08:00
CellInfo * bound = ctx - > getBoundBelCell ( target . second ) ;
2021-02-23 18:53:35 +08:00
// Chains cannot overlap; so if we have to ripup a cell make sure it isn't part of a chain
2019-01-31 00:36:01 +08:00
if ( bound ! = nullptr )
2021-04-28 22:43:02 +08:00
if ( bound - > cluster ! = ClusterId ( ) | | bound - > belStrength > STRENGTH_WEAK )
2019-01-31 00:36:01 +08:00
goto fail ;
}
2021-02-23 18:53:35 +08:00
// Actually perform the move; keeping track of the moves we make so we can revert them if needed
2019-01-31 00:36:01 +08:00
for ( auto & target : targets ) {
CellInfo * bound = ctx - > getBoundBelCell ( target . second ) ;
if ( bound ! = nullptr )
ctx - > unbindBel ( target . second ) ;
ctx - > bindBel ( target . second , target . first , STRENGTH_STRONG ) ;
swaps_made . emplace_back ( target . second , bound ) ;
}
2021-02-23 18:53:35 +08:00
// Check that the move we have made is legal
2019-01-31 00:36:01 +08:00
for ( auto & sm : swaps_made ) {
if ( ! ctx - > isBelLocationValid ( sm . first ) )
goto fail ;
}
if ( false ) {
fail :
2021-02-23 18:53:35 +08:00
// If the move turned out to be illegal; revert all the moves we made
2019-01-31 00:36:01 +08:00
for ( auto & swap : swaps_made ) {
ctx - > unbindBel ( swap . first ) ;
if ( swap . second ! = nullptr )
ctx - > bindBel ( swap . first , swap . second , STRENGTH_WEAK ) ;
}
continue ;
}
for ( auto & target : targets ) {
Loc loc = ctx - > getBelLocation ( target . second ) ;
cell_locs [ target . first - > name ] . x = loc . x ;
cell_locs [ target . first - > name ] . y = loc . y ;
// log_info("%s %d %d %d\n", target.first->name.c_str(ctx), loc.x, loc.y, loc.z);
}
for ( auto & swap : swaps_made ) {
2021-02-23 18:53:35 +08:00
// Where we have ripped up cells; add them to the queue
2019-01-31 00:36:01 +08:00
if ( swap . second ! = nullptr )
remaining . emplace ( chain_size [ swap . second - > name ] , swap . second - > name ) ;
}
placed = true ;
break ;
}
2019-01-11 19:59:34 +08:00
}
2022-12-18 07:50:14 +08:00
total_iters_for_cell + + ;
2019-01-11 19:59:34 +08:00
}
}
2019-01-24 21:36:23 +08:00
auto endt = std : : chrono : : high_resolution_clock : : now ( ) ;
sl_time + = std : : chrono : : duration < float > ( endt - startt ) . count ( ) ;
2019-01-11 19:59:34 +08:00
}
2019-01-26 21:22:44 +08:00
// Implementation of the cut-based spreading as described in the HeAP/SimPL papers
2019-01-11 19:59:34 +08:00
2019-11-26 18:01:33 +08:00
template < typename T > T limit_to_reg ( Region * reg , T val , bool dir )
{
if ( reg = = nullptr )
return val ;
int limit_low = dir ? constraint_region_bounds [ reg - > name ] . y0 : constraint_region_bounds [ reg - > name ] . x0 ;
int limit_high = dir ? constraint_region_bounds [ reg - > name ] . y1 : constraint_region_bounds [ reg - > name ] . x1 ;
return std : : max < T > ( std : : min < T > ( val , limit_high ) , limit_low ) ;
}
2019-01-11 19:59:34 +08:00
struct ChainExtent
{
int x0 , y0 , x1 , y1 ;
} ;
2019-01-26 21:22:44 +08:00
struct SpreaderRegion
2019-01-11 19:59:34 +08:00
{
int id ;
int x0 , y0 , x1 , y1 ;
2020-02-02 23:40:23 +08:00
std : : vector < int > cells , bels ;
2020-02-02 23:45:01 +08:00
bool overused ( float beta ) const
2019-01-11 19:59:34 +08:00
{
2020-02-02 23:40:23 +08:00
for ( size_t t = 0 ; t < cells . size ( ) ; t + + ) {
if ( bels . at ( t ) < 4 ) {
if ( cells . at ( t ) > bels . at ( t ) )
return true ;
} else {
if ( cells . at ( t ) > beta * bels . at ( t ) )
return true ;
}
}
return false ;
2019-01-11 19:59:34 +08:00
}
} ;
2019-01-26 21:22:44 +08:00
class CutSpreader
2019-01-11 19:59:34 +08:00
{
public :
2021-06-02 00:52:25 +08:00
CutSpreader ( HeAPPlacer * p , const pool < BelBucketId > & buckets ) : p ( p ) , ctx ( p - > ctx ) , buckets ( buckets )
2019-01-11 19:59:34 +08:00
{
2021-01-30 06:55:10 +08:00
// Get fast BELs data for all buckets being Cut/Spread.
2021-01-30 02:02:12 +08:00
size_t idx = 0 ;
2021-06-02 00:52:25 +08:00
for ( BelBucketId bucket : buckets ) {
2021-01-30 06:55:10 +08:00
type_index [ bucket ] = idx ;
2021-01-29 10:32:42 +08:00
FastBels : : FastBelsData * fast_bels ;
2021-01-30 06:55:10 +08:00
p - > fast_bels . getBelsForBelBucket ( bucket , & fast_bels ) ;
2021-01-29 10:32:42 +08:00
fb . push_back ( fast_bels ) ;
2020-02-02 23:40:23 +08:00
+ + idx ;
2021-01-29 10:32:42 +08:00
NPNR_ASSERT ( fb . size ( ) = = idx ) ;
2020-02-02 23:40:23 +08:00
}
2019-01-11 19:59:34 +08:00
}
2019-01-23 23:02:49 +08:00
static int seq ;
2019-01-11 19:59:34 +08:00
void run ( )
{
2019-01-24 21:36:23 +08:00
auto startt = std : : chrono : : high_resolution_clock : : now ( ) ;
2019-01-11 19:59:34 +08:00
init ( ) ;
find_overused_regions ( ) ;
2019-01-26 03:24:54 +08:00
for ( auto & r : regions ) {
if ( merged_regions . count ( r . id ) )
continue ;
2019-01-26 21:22:44 +08:00
#if 0
2019-01-26 03:24:54 +08:00
log_info ( " %s (%d, %d) |_> (%d, %d) %d/%d \n " , beltype . c_str ( ctx ) , r . x0 , r . y0 , r . x1 , r . y1 , r . cells ,
r . bels ) ;
2019-01-26 21:22:44 +08:00
# endif
2019-01-26 03:24:54 +08:00
}
2019-01-11 19:59:34 +08:00
expand_regions ( ) ;
2019-01-23 22:25:34 +08:00
std : : queue < std : : pair < int , bool > > workqueue ;
2019-01-26 21:22:44 +08:00
#if 0
2019-01-23 23:02:49 +08:00
std : : vector < std : : pair < double , double > > orig ;
if ( ctx - > debug )
for ( auto c : p - > solve_cells )
orig . emplace_back ( p - > cell_locs [ c - > name ] . rawx , p - > cell_locs [ c - > name ] . rawy ) ;
2019-01-26 21:22:44 +08:00
# endif
2019-01-11 19:59:34 +08:00
for ( auto & r : regions ) {
2019-01-22 23:16:00 +08:00
if ( merged_regions . count ( r . id ) )
continue ;
2019-01-26 21:22:44 +08:00
#if 0
2020-02-02 23:40:23 +08:00
for ( auto t : sorted ( beltype ) ) {
log_info ( " %s (%d, %d) |_> (%d, %d) %d/%d \n " , t . c_str ( ctx ) , r . x0 , r . y0 , r . x1 , r . y1 ,
r . cells . at ( type_index . at ( t ) ) , r . bels . at ( type_index . at ( t ) ) ) ;
}
2019-01-26 21:22:44 +08:00
# endif
2019-01-23 22:25:34 +08:00
workqueue . emplace ( r . id , false ) ;
}
while ( ! workqueue . empty ( ) ) {
auto front = workqueue . front ( ) ;
workqueue . pop ( ) ;
auto & r = regions . at ( front . first ) ;
2020-02-02 23:40:23 +08:00
if ( std : : all_of ( r . cells . begin ( ) , r . cells . end ( ) , [ ] ( int x ) { return x = = 0 ; } ) )
2019-01-24 22:05:16 +08:00
continue ;
2019-01-23 22:25:34 +08:00
auto res = cut_region ( r , front . second ) ;
if ( res ) {
workqueue . emplace ( res - > first , ! front . second ) ;
workqueue . emplace ( res - > second , ! front . second ) ;
2019-01-24 22:05:16 +08:00
} else {
// Try the other dir, in case stuck in one direction only
auto res2 = cut_region ( r , ! front . second ) ;
if ( res2 ) {
workqueue . emplace ( res2 - > first , front . second ) ;
workqueue . emplace ( res2 - > second , front . second ) ;
}
2019-01-23 22:25:34 +08:00
}
2019-01-11 19:59:34 +08:00
}
2019-01-26 21:22:44 +08:00
#if 0
2019-01-23 23:02:49 +08:00
if ( ctx - > debug ) {
std : : ofstream sp ( " spread " + std : : to_string ( seq ) + " .csv " ) ;
for ( size_t i = 0 ; i < p - > solve_cells . size ( ) ; i + + ) {
auto & c = p - > solve_cells . at ( i ) ;
2019-01-24 22:05:16 +08:00
if ( c - > type ! = beltype )
continue ;
2019-01-23 23:02:49 +08:00
sp < < orig . at ( i ) . first < < " , " < < orig . at ( i ) . second < < " , " < < p - > cell_locs [ c - > name ] . rawx < < " , " < < p - > cell_locs [ c - > name ] . rawy < < std : : endl ;
}
2019-01-24 22:05:16 +08:00
std : : ofstream oc ( " cells " + std : : to_string ( seq ) + " .csv " ) ;
for ( size_t y = 0 ; y < = p - > max_y ; y + + ) {
for ( size_t x = 0 ; x < = p - > max_x ; x + + ) {
oc < < cells_at_location . at ( x ) . at ( y ) . size ( ) < < " , " ;
}
oc < < std : : endl ;
}
2019-01-23 23:02:49 +08:00
+ + seq ;
}
2019-01-26 21:22:44 +08:00
# endif
2019-01-24 21:36:23 +08:00
auto endt = std : : chrono : : high_resolution_clock : : now ( ) ;
p - > cl_time + = std : : chrono : : duration < float > ( endt - startt ) . count ( ) ;
2019-01-11 19:59:34 +08:00
}
private :
HeAPPlacer * p ;
Context * ctx ;
2021-06-02 00:52:25 +08:00
pool < BelBucketId > buckets ;
dict < BelBucketId , size_t > type_index ;
2020-02-02 23:40:23 +08:00
std : : vector < std : : vector < std : : vector < int > > > occupancy ;
2019-01-11 19:59:34 +08:00
std : : vector < std : : vector < int > > groups ;
std : : vector < std : : vector < ChainExtent > > chaines ;
2019-01-15 23:20:38 +08:00
std : : map < IdString , ChainExtent > cell_extents ;
2020-02-02 23:40:23 +08:00
std : : vector < std : : vector < std : : vector < std : : vector < BelId > > > * > fb ;
2019-01-11 19:59:34 +08:00
2019-01-26 21:22:44 +08:00
std : : vector < SpreaderRegion > regions ;
2021-06-02 00:52:25 +08:00
pool < int > merged_regions ;
2019-01-15 23:20:38 +08:00
// Cells at a location, sorted by real (not integer) x and y
2019-01-23 23:02:49 +08:00
std : : vector < std : : vector < std : : vector < CellInfo * > > > cells_at_location ;
2019-01-11 19:59:34 +08:00
2020-02-02 23:40:23 +08:00
int occ_at ( int x , int y , int type ) { return occupancy . at ( x ) . at ( y ) . at ( type ) ; }
2019-01-11 19:59:34 +08:00
2020-02-02 23:40:23 +08:00
int bels_at ( int x , int y , int type )
2019-01-11 19:59:34 +08:00
{
2020-02-02 23:40:23 +08:00
if ( x > = int ( fb . at ( type ) - > size ( ) ) | | y > = int ( fb . at ( type ) - > at ( x ) . size ( ) ) )
2019-01-11 19:59:34 +08:00
return 0 ;
2020-02-02 23:40:23 +08:00
return int ( fb . at ( type ) - > at ( x ) . at ( y ) . size ( ) ) ;
2019-01-11 19:59:34 +08:00
}
2021-02-02 06:28:32 +08:00
bool is_cell_fixed ( const CellInfo & cell ) const
{
2021-01-30 06:55:10 +08:00
return buckets . count ( ctx - > getBelBucketForCellType ( cell . type ) ) = = 0 ;
2021-01-29 10:32:42 +08:00
}
2021-02-02 06:28:32 +08:00
size_t cell_index ( const CellInfo & cell ) const { return type_index . at ( ctx - > getBelBucketForCellType ( cell . type ) ) ; }
2021-01-29 10:32:42 +08:00
2019-01-22 23:16:00 +08:00
void init ( )
{
2020-02-02 23:40:23 +08:00
occupancy . resize ( p - > max_x + 1 ,
2021-01-30 06:55:10 +08:00
std : : vector < std : : vector < int > > ( p - > max_y + 1 , std : : vector < int > ( buckets . size ( ) , 0 ) ) ) ;
2019-01-11 19:59:34 +08:00
groups . resize ( p - > max_x + 1 , std : : vector < int > ( p - > max_y + 1 , - 1 ) ) ;
chaines . resize ( p - > max_x + 1 , std : : vector < ChainExtent > ( p - > max_y + 1 ) ) ;
2019-01-23 23:02:49 +08:00
cells_at_location . resize ( p - > max_x + 1 , std : : vector < std : : vector < CellInfo * > > ( p - > max_y + 1 ) ) ;
2019-01-11 19:59:34 +08:00
for ( int x = 0 ; x < = p - > max_x ; x + + )
for ( int y = 0 ; y < = p - > max_y ; y + + ) {
2021-01-30 06:55:10 +08:00
for ( int t = 0 ; t < int ( buckets . size ( ) ) ; t + + ) {
2020-02-02 23:40:23 +08:00
occupancy . at ( x ) . at ( y ) . at ( t ) = 0 ;
}
2019-01-11 19:59:34 +08:00
groups . at ( x ) . at ( y ) = - 1 ;
chaines . at ( x ) . at ( y ) = { x , y , x , y } ;
}
auto set_chain_ext = [ & ] ( IdString cell , int x , int y ) {
2019-01-15 23:20:38 +08:00
if ( ! cell_extents . count ( cell ) )
cell_extents [ cell ] = { x , y , x , y } ;
2019-01-11 19:59:34 +08:00
else {
2019-01-15 23:20:38 +08:00
cell_extents [ cell ] . x0 = std : : min ( cell_extents [ cell ] . x0 , x ) ;
cell_extents [ cell ] . y0 = std : : min ( cell_extents [ cell ] . y0 , y ) ;
cell_extents [ cell ] . x1 = std : : max ( cell_extents [ cell ] . x1 , x ) ;
cell_extents [ cell ] . y1 = std : : max ( cell_extents [ cell ] . y1 , y ) ;
2019-01-11 19:59:34 +08:00
}
} ;
2021-01-29 10:32:42 +08:00
for ( auto & cell_loc : p - > cell_locs ) {
IdString cell_name = cell_loc . first ;
2021-02-02 06:28:32 +08:00
const CellInfo & cell = * ctx - > cells . at ( cell_name ) ;
const CellLocation & loc = cell_loc . second ;
if ( is_cell_fixed ( cell ) ) {
2019-01-24 22:05:16 +08:00
continue ;
2021-01-29 10:32:42 +08:00
}
if ( cell . belStrength > STRENGTH_STRONG ) {
2019-01-31 04:41:00 +08:00
continue ;
2021-01-29 10:32:42 +08:00
}
occupancy . at ( cell_loc . second . x ) . at ( cell_loc . second . y ) . at ( cell_index ( cell ) ) + + ;
2019-01-11 19:59:34 +08:00
// Compute ultimate extent of each chain root
2021-04-28 22:43:02 +08:00
if ( cell . cluster ! = ClusterId ( ) ) {
set_chain_ext ( ctx - > getClusterRootCell ( cell . cluster ) - > name , loc . x , loc . y ) ;
2019-01-11 19:59:34 +08:00
}
}
2021-01-29 10:32:42 +08:00
for ( auto & cell_loc : p - > cell_locs ) {
IdString cell_name = cell_loc . first ;
2021-02-02 06:28:32 +08:00
const CellInfo & cell = * ctx - > cells . at ( cell_name ) ;
const CellLocation & loc = cell_loc . second ;
if ( is_cell_fixed ( cell ) ) {
2019-01-24 22:05:16 +08:00
continue ;
2021-01-29 10:32:42 +08:00
}
2021-04-12 20:42:20 +08:00
if ( cell . belStrength > STRENGTH_STRONG ) {
continue ;
}
2021-01-29 10:32:42 +08:00
// Transfer chain extents to the actual chains structure
2019-01-11 19:59:34 +08:00
ChainExtent * ce = nullptr ;
2021-04-28 22:43:02 +08:00
if ( cell . cluster ! = ClusterId ( ) ) {
ce = & ( cell_extents . at ( ctx - > getClusterRootCell ( cell . cluster ) - > name ) ) ;
2021-01-29 10:32:42 +08:00
}
2019-01-11 19:59:34 +08:00
if ( ce ) {
2021-01-29 10:32:42 +08:00
auto & lce = chaines . at ( loc . x ) . at ( loc . y ) ;
2019-01-11 19:59:34 +08:00
lce . x0 = std : : min ( lce . x0 , ce - > x0 ) ;
lce . y0 = std : : min ( lce . y0 , ce - > y0 ) ;
lce . x1 = std : : max ( lce . x1 , ce - > x1 ) ;
lce . y1 = std : : max ( lce . y1 , ce - > y1 ) ;
}
}
2021-01-29 10:32:42 +08:00
2019-01-15 23:20:38 +08:00
for ( auto cell : p - > solve_cells ) {
2021-02-02 06:28:32 +08:00
if ( is_cell_fixed ( * cell ) ) {
2019-01-24 22:05:16 +08:00
continue ;
2021-01-29 10:32:42 +08:00
}
2019-01-26 21:22:44 +08:00
cells_at_location . at ( p - > cell_locs . at ( cell - > name ) . x ) . at ( p - > cell_locs . at ( cell - > name ) . y ) . push_back ( cell ) ;
2019-01-15 23:20:38 +08:00
}
2019-01-11 19:59:34 +08:00
}
2021-01-29 10:32:42 +08:00
2019-01-26 21:22:44 +08:00
void merge_regions ( SpreaderRegion & merged , SpreaderRegion & mergee )
2019-01-11 19:59:34 +08:00
{
// Prevent grow_region from recursing while doing this
2021-01-29 10:32:42 +08:00
for ( int x = mergee . x0 ; x < = mergee . x1 ; x + + ) {
2019-01-11 19:59:34 +08:00
for ( int y = mergee . y0 ; y < = mergee . y1 ; y + + ) {
// log_info("%d %d\n", groups.at(x).at(y), mergee.id);
NPNR_ASSERT ( groups . at ( x ) . at ( y ) = = mergee . id ) ;
groups . at ( x ) . at ( y ) = merged . id ;
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
merged . cells . at ( t ) + = occ_at ( x , y , t ) ;
merged . bels . at ( t ) + = bels_at ( x , y , t ) ;
}
2019-01-11 19:59:34 +08:00
}
2021-01-29 10:32:42 +08:00
}
2019-01-11 19:59:34 +08:00
merged_regions . insert ( mergee . id ) ;
grow_region ( merged , mergee . x0 , mergee . y0 , mergee . x1 , mergee . y1 ) ;
}
2019-01-26 21:22:44 +08:00
void grow_region ( SpreaderRegion & r , int x0 , int y0 , int x1 , int y1 , bool init = false )
2019-01-11 19:59:34 +08:00
{
// log_info("growing to (%d, %d) |_> (%d, %d)\n", x0, y0, x1, y1);
if ( ( x0 > = r . x0 & & y0 > = r . y0 & & x1 < = r . x1 & & y1 < = r . y1 ) | | init )
return ;
int old_x0 = r . x0 + ( init ? 1 : 0 ) , old_y0 = r . y0 , old_x1 = r . x1 , old_y1 = r . y1 ;
r . x0 = std : : min ( r . x0 , x0 ) ;
r . y0 = std : : min ( r . y0 , y0 ) ;
r . x1 = std : : max ( r . x1 , x1 ) ;
r . y1 = std : : max ( r . y1 , y1 ) ;
auto process_location = [ & ] ( int x , int y ) {
// Merge with any overlapping regions
2019-01-24 00:36:53 +08:00
if ( groups . at ( x ) . at ( y ) = = - 1 ) {
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
r . bels . at ( t ) + = bels_at ( x , y , t ) ;
r . cells . at ( t ) + = occ_at ( x , y , t ) ;
}
2019-01-11 19:59:34 +08:00
}
2021-01-29 10:32:42 +08:00
2019-01-11 19:59:34 +08:00
if ( groups . at ( x ) . at ( y ) ! = - 1 & & groups . at ( x ) . at ( y ) ! = r . id )
merge_regions ( r , regions . at ( groups . at ( x ) . at ( y ) ) ) ;
groups . at ( x ) . at ( y ) = r . id ;
// Grow to cover any chains
auto & chaine = chaines . at ( x ) . at ( y ) ;
grow_region ( r , chaine . x0 , chaine . y0 , chaine . x1 , chaine . y1 ) ;
} ;
for ( int x = r . x0 ; x < old_x0 ; x + + )
for ( int y = r . y0 ; y < = r . y1 ; y + + )
process_location ( x , y ) ;
for ( int x = old_x1 + 1 ; x < = x1 ; x + + )
for ( int y = r . y0 ; y < = r . y1 ; y + + )
process_location ( x , y ) ;
for ( int y = r . y0 ; y < old_y0 ; y + + )
for ( int x = r . x0 ; x < = r . x1 ; x + + )
process_location ( x , y ) ;
for ( int y = old_y1 + 1 ; y < = r . y1 ; y + + )
for ( int x = r . x0 ; x < = r . x1 ; x + + )
process_location ( x , y ) ;
}
void find_overused_regions ( )
{
for ( int x = 0 ; x < = p - > max_x ; x + + )
for ( int y = 0 ; y < = p - > max_y ; y + + ) {
// Either already in a group, or not overutilised. Ignore
2020-02-02 23:40:23 +08:00
if ( groups . at ( x ) . at ( y ) ! = - 1 )
continue ;
bool overutilised = false ;
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
if ( occ_at ( x , y , t ) > bels_at ( x , y , t ) ) {
overutilised = true ;
break ;
}
}
2021-01-29 10:32:42 +08:00
2020-02-02 23:40:23 +08:00
if ( ! overutilised )
2019-01-11 19:59:34 +08:00
continue ;
// log_info("%d %d %d\n", x, y, occ_at(x, y));
int id = int ( regions . size ( ) ) ;
groups . at ( x ) . at ( y ) = id ;
2019-01-26 21:22:44 +08:00
SpreaderRegion reg ;
2019-01-11 19:59:34 +08:00
reg . id = id ;
reg . x0 = reg . x1 = x ;
reg . y0 = reg . y1 = y ;
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
reg . bels . push_back ( bels_at ( x , y , t ) ) ;
reg . cells . push_back ( occ_at ( x , y , t ) ) ;
}
2019-01-11 19:59:34 +08:00
// Make sure we cover carries, etc
grow_region ( reg , reg . x0 , reg . y0 , reg . x1 , reg . y1 , true ) ;
bool expanded = true ;
while ( expanded ) {
expanded = false ;
// Keep trying expansion in x and y, until we find no over-occupancy cells
// or hit grouped cells
// First try expanding in x
if ( reg . x1 < p - > max_x ) {
bool over_occ_x = false ;
for ( int y1 = reg . y0 ; y1 < = reg . y1 ; y1 + + ) {
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
if ( occ_at ( reg . x1 + 1 , y1 , t ) > bels_at ( reg . x1 + 1 , y1 , t ) ) {
over_occ_x = true ;
break ;
}
2019-01-11 19:59:34 +08:00
}
}
if ( over_occ_x ) {
expanded = true ;
grow_region ( reg , reg . x0 , reg . y0 , reg . x1 + 1 , reg . y1 ) ;
}
}
if ( reg . y1 < p - > max_y ) {
bool over_occ_y = false ;
for ( int x1 = reg . x0 ; x1 < = reg . x1 ; x1 + + ) {
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
if ( occ_at ( x1 , reg . y1 + 1 , t ) > bels_at ( x1 , reg . y1 + 1 , t ) ) {
over_occ_y = true ;
break ;
}
2019-01-11 19:59:34 +08:00
}
}
if ( over_occ_y ) {
expanded = true ;
grow_region ( reg , reg . x0 , reg . y0 , reg . x1 , reg . y1 + 1 ) ;
}
}
}
regions . push_back ( reg ) ;
}
}
void expand_regions ( )
{
std : : queue < int > overu_regions ;
2020-02-02 23:45:01 +08:00
float beta = p - > cfg . beta ;
2019-01-11 19:59:34 +08:00
for ( auto & r : regions ) {
2020-02-02 23:45:01 +08:00
if ( ! merged_regions . count ( r . id ) & & r . overused ( beta ) )
2019-01-11 19:59:34 +08:00
overu_regions . push ( r . id ) ;
}
while ( ! overu_regions . empty ( ) ) {
int rid = overu_regions . front ( ) ;
overu_regions . pop ( ) ;
if ( merged_regions . count ( rid ) )
continue ;
auto & reg = regions . at ( rid ) ;
2020-02-02 23:45:01 +08:00
while ( reg . overused ( beta ) ) {
2019-01-11 19:59:34 +08:00
bool changed = false ;
2020-02-02 23:52:28 +08:00
for ( int j = 0 ; j < p - > cfg . spread_scale_x ; j + + ) {
2020-02-02 23:40:23 +08:00
if ( reg . x0 > 0 ) {
grow_region ( reg , reg . x0 - 1 , reg . y0 , reg . x1 , reg . y1 ) ;
changed = true ;
2020-02-02 23:45:01 +08:00
if ( ! reg . overused ( beta ) )
2020-02-02 23:40:23 +08:00
break ;
}
if ( reg . x1 < p - > max_x ) {
grow_region ( reg , reg . x0 , reg . y0 , reg . x1 + 1 , reg . y1 ) ;
changed = true ;
2020-02-02 23:45:01 +08:00
if ( ! reg . overused ( beta ) )
2020-02-02 23:40:23 +08:00
break ;
}
2019-01-11 19:59:34 +08:00
}
2020-02-02 23:52:28 +08:00
for ( int j = 0 ; j < p - > cfg . spread_scale_y ; j + + ) {
if ( reg . y0 > 0 ) {
grow_region ( reg , reg . x0 , reg . y0 - 1 , reg . x1 , reg . y1 ) ;
changed = true ;
if ( ! reg . overused ( beta ) )
break ;
}
if ( reg . y1 < p - > max_y ) {
grow_region ( reg , reg . x0 , reg . y0 , reg . x1 , reg . y1 + 1 ) ;
changed = true ;
if ( ! reg . overused ( beta ) )
break ;
}
2019-01-11 19:59:34 +08:00
}
2019-01-25 22:04:19 +08:00
if ( ! changed ) {
2021-06-02 00:52:25 +08:00
for ( auto bucket : buckets ) {
2021-01-29 10:32:42 +08:00
if ( reg . cells > reg . bels ) {
2021-01-30 06:55:10 +08:00
IdString bucket_name = ctx - > getBelBucketName ( bucket ) ;
2020-02-02 23:40:23 +08:00
log_error ( " Failed to expand region (%d, %d) |_> (%d, %d) of %d %ss \n " , reg . x0 , reg . y0 ,
2021-02-02 06:28:32 +08:00
reg . x1 , reg . y1 , reg . cells . at ( type_index . at ( bucket ) ) , bucket_name . c_str ( ctx ) ) ;
2021-01-29 10:32:42 +08:00
}
2020-02-02 23:40:23 +08:00
}
break ;
2019-01-25 22:04:19 +08:00
}
2019-01-11 19:59:34 +08:00
}
}
}
2019-01-15 23:20:38 +08:00
// Implementation of the recursive cut-based spreading as described in the HeAP paper
// Note we use "left" to mean "-x/-y" depending on dir and "right" to mean "+x/+y" depending on dir
std : : vector < CellInfo * > cut_cells ;
2019-01-26 21:22:44 +08:00
boost : : optional < std : : pair < int , int > > cut_region ( SpreaderRegion & r , bool dir )
2019-01-22 23:16:00 +08:00
{
2019-01-15 23:20:38 +08:00
cut_cells . clear ( ) ;
2019-01-23 23:02:49 +08:00
auto & cal = cells_at_location ;
2019-01-24 22:05:16 +08:00
int total_cells = 0 , total_bels = 0 ;
2019-01-23 23:02:49 +08:00
for ( int x = r . x0 ; x < = r . x1 ; x + + ) {
2019-01-15 23:20:38 +08:00
for ( int y = r . y0 ; y < = r . y1 ; y + + ) {
2019-01-23 23:02:49 +08:00
std : : copy ( cal . at ( x ) . at ( y ) . begin ( ) , cal . at ( x ) . at ( y ) . end ( ) , std : : back_inserter ( cut_cells ) ) ;
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + )
2020-02-02 23:40:23 +08:00
total_bels + = bels_at ( x , y , t ) ;
2019-01-15 23:20:38 +08:00
}
}
2019-01-24 22:05:16 +08:00
for ( auto & cell : cut_cells ) {
total_cells + = p - > chain_size . count ( cell - > name ) ? p - > chain_size . at ( cell - > name ) : 1 ;
}
2019-01-23 23:02:49 +08:00
std : : sort ( cut_cells . begin ( ) , cut_cells . end ( ) , [ & ] ( const CellInfo * a , const CellInfo * b ) {
2019-01-26 21:22:44 +08:00
return dir ? ( p - > cell_locs . at ( a - > name ) . rawy < p - > cell_locs . at ( b - > name ) . rawy )
: ( p - > cell_locs . at ( a - > name ) . rawx < p - > cell_locs . at ( b - > name ) . rawx ) ;
2019-01-23 23:02:49 +08:00
} ) ;
2019-01-24 22:05:16 +08:00
if ( cut_cells . size ( ) < 2 )
2019-01-23 22:25:34 +08:00
return { } ;
2019-01-15 23:20:38 +08:00
// Find the cells midpoint, counting chains in terms of their total size - making the initial source cut
int pivot_cells = 0 ;
int pivot = 0 ;
for ( auto & cell : cut_cells ) {
pivot_cells + = p - > chain_size . count ( cell - > name ) ? p - > chain_size . at ( cell - > name ) : 1 ;
2019-01-24 22:05:16 +08:00
if ( pivot_cells > = total_cells / 2 )
2019-01-15 23:20:38 +08:00
break ;
pivot + + ;
}
2020-02-02 23:40:23 +08:00
if ( pivot > = int ( cut_cells . size ( ) ) ) {
2019-01-23 22:25:34 +08:00
pivot = int ( cut_cells . size ( ) ) - 1 ;
2020-02-02 23:40:23 +08:00
}
// log_info("orig pivot %d/%d lc %d rc %d\n", pivot, int(cut_cells.size()), pivot_cells, total_cells -
// pivot_cells);
2019-01-22 23:16:00 +08:00
2019-01-15 23:20:38 +08:00
// Find the clearance required either side of the pivot
int clearance_l = 0 , clearance_r = 0 ;
for ( size_t i = 0 ; i < cut_cells . size ( ) ; i + + ) {
int size ;
if ( cell_extents . count ( cut_cells . at ( i ) - > name ) ) {
auto & ce = cell_extents . at ( cut_cells . at ( i ) - > name ) ;
size = dir ? ( ce . y1 - ce . y0 + 1 ) : ( ce . x1 - ce . x0 + 1 ) ;
} else {
size = 1 ;
}
2019-01-22 23:16:00 +08:00
if ( int ( i ) < pivot )
2019-01-15 23:20:38 +08:00
clearance_l = std : : max ( clearance_l , size ) ;
else
clearance_r = std : : max ( clearance_r , size ) ;
}
// Find the target cut that minimises difference in utilisation, whilst trying to ensure that all chains
// still fit
// First trim the boundaries of the region in the axis-of-interest, skipping any rows/cols without any
// bels of the appropriate type
int trimmed_l = dir ? r . y0 : r . x0 , trimmed_r = dir ? r . y1 : r . x1 ;
while ( trimmed_l < ( dir ? r . y1 : r . x1 ) ) {
bool have_bels = false ;
2021-01-29 10:32:42 +08:00
for ( int i = dir ? r . x0 : r . y0 ; i < = ( dir ? r . x1 : r . y1 ) ; i + + ) {
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
if ( bels_at ( dir ? i : trimmed_l , dir ? trimmed_l : i , t ) > 0 ) {
have_bels = true ;
break ;
}
2021-01-29 10:32:42 +08:00
}
}
2019-01-15 23:20:38 +08:00
if ( have_bels )
break ;
2021-01-29 10:32:42 +08:00
2019-01-15 23:20:38 +08:00
trimmed_l + + ;
}
while ( trimmed_r > ( dir ? r . y0 : r . x0 ) ) {
bool have_bels = false ;
2021-01-29 10:32:42 +08:00
for ( int i = dir ? r . x0 : r . y0 ; i < = ( dir ? r . x1 : r . y1 ) ; i + + ) {
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
if ( bels_at ( dir ? i : trimmed_r , dir ? trimmed_r : i , t ) > 0 ) {
have_bels = true ;
break ;
}
2021-01-29 10:32:42 +08:00
}
}
2019-01-15 23:20:38 +08:00
if ( have_bels )
break ;
2021-01-29 10:32:42 +08:00
2019-01-15 23:20:38 +08:00
trimmed_r - - ;
}
2019-01-26 21:22:44 +08:00
// log_info("tl %d tr %d cl %d cr %d\n", trimmed_l, trimmed_r, clearance_l, clearance_r);
2019-01-22 23:16:00 +08:00
if ( ( trimmed_r - trimmed_l + 1 ) < = std : : max ( clearance_l , clearance_r ) )
return { } ;
2019-01-15 23:20:38 +08:00
// Now find the initial target cut that minimises utilisation imbalance, whilst
// meeting the clearance requirements for any large macros
2021-01-30 06:55:10 +08:00
std : : vector < int > left_cells_v ( buckets . size ( ) , 0 ) , right_cells_v ( buckets . size ( ) , 0 ) ;
std : : vector < int > left_bels_v ( buckets . size ( ) , 0 ) , right_bels_v ( r . bels ) ;
2020-02-02 23:40:23 +08:00
for ( int i = 0 ; i < = pivot ; i + + )
2021-01-29 10:32:42 +08:00
left_cells_v . at ( cell_index ( * cut_cells . at ( i ) ) ) + =
2020-02-02 23:40:23 +08:00
p - > chain_size . count ( cut_cells . at ( i ) - > name ) ? p - > chain_size . at ( cut_cells . at ( i ) - > name ) : 1 ;
for ( int i = pivot + 1 ; i < int ( cut_cells . size ( ) ) ; i + + )
2021-01-29 10:32:42 +08:00
right_cells_v . at ( cell_index ( * cut_cells . at ( i ) ) ) + =
2020-02-02 23:40:23 +08:00
p - > chain_size . count ( cut_cells . at ( i ) - > name ) ? p - > chain_size . at ( cut_cells . at ( i ) - > name ) : 1 ;
2019-01-15 23:20:38 +08:00
int best_tgt_cut = - 1 ;
double best_deltaU = std : : numeric_limits < double > : : max ( ) ;
2020-02-02 23:40:23 +08:00
// std::pair<int, int> target_cut_bels;
2021-01-30 06:55:10 +08:00
std : : vector < int > slither_bels ( buckets . size ( ) , 0 ) ;
2019-01-15 23:20:38 +08:00
for ( int i = trimmed_l ; i < = trimmed_r ; i + + ) {
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + )
2020-02-02 23:40:23 +08:00
slither_bels . at ( t ) = 0 ;
2019-01-15 23:20:38 +08:00
for ( int j = dir ? r . x0 : r . y0 ; j < = ( dir ? r . x1 : r . y1 ) ; j + + ) {
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + )
2020-02-02 23:40:23 +08:00
slither_bels . at ( t ) + = dir ? bels_at ( j , i , t ) : bels_at ( i , j , t ) ;
2019-01-15 23:20:38 +08:00
}
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
left_bels_v . at ( t ) + = slither_bels . at ( t ) ;
right_bels_v . at ( t ) - = slither_bels . at ( t ) ;
}
2019-01-15 23:20:38 +08:00
if ( ( ( i - trimmed_l ) + 1 ) > = clearance_l & & ( ( trimmed_r - i ) + 1 ) > = clearance_r ) {
// Solution is potentially valid
2020-02-02 23:40:23 +08:00
double aU = 0 ;
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + )
2020-02-02 23:40:23 +08:00
aU + = ( left_cells_v . at ( t ) + right_cells_v . at ( t ) ) *
std : : abs ( double ( left_cells_v . at ( t ) ) / double ( std : : max ( left_bels_v . at ( t ) , 1 ) ) -
double ( right_cells_v . at ( t ) ) / double ( std : : max ( right_bels_v . at ( t ) , 1 ) ) ) ;
2019-01-15 23:20:38 +08:00
if ( aU < best_deltaU ) {
best_deltaU = aU ;
best_tgt_cut = i ;
}
}
}
2019-01-31 00:36:01 +08:00
if ( best_tgt_cut = = - 1 )
return { } ;
2020-02-02 23:40:23 +08:00
// left_bels = target_cut_bels.first;
// right_bels = target_cut_bels.second;
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
left_bels_v . at ( t ) = 0 ;
right_bels_v . at ( t ) = 0 ;
}
for ( int x = r . x0 ; x < = ( dir ? r . x1 : best_tgt_cut ) ; x + + )
for ( int y = r . y0 ; y < = ( dir ? best_tgt_cut : r . y1 ) ; y + + ) {
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
left_bels_v . at ( t ) + = bels_at ( x , y , t ) ;
}
}
for ( int x = dir ? r . x0 : ( best_tgt_cut + 1 ) ; x < = r . x1 ; x + + )
for ( int y = dir ? ( best_tgt_cut + 1 ) : r . y0 ; y < = r . y1 ; y + + ) {
2021-01-30 06:55:10 +08:00
for ( size_t t = 0 ; t < buckets . size ( ) ; t + + ) {
2020-02-02 23:40:23 +08:00
right_bels_v . at ( t ) + = bels_at ( x , y , t ) ;
}
}
if ( std : : accumulate ( left_bels_v . begin ( ) , left_bels_v . end ( ) , 0 ) = = 0 | |
std : : accumulate ( right_bels_v . begin ( ) , right_bels_v . end ( ) , 0 ) = = 0 )
return { } ;
2019-01-22 23:16:00 +08:00
2021-01-28 23:19:06 +08:00
// Perturb the source cut to eliminate overutilisation
2020-02-02 23:40:23 +08:00
auto is_part_overutil = [ & ] ( bool r ) {
double delta = 0 ;
for ( size_t t = 0 ; t < left_cells_v . size ( ) ; t + + ) {
delta + = double ( left_cells_v . at ( t ) ) / double ( std : : max ( left_bels_v . at ( t ) , 1 ) ) -
double ( right_cells_v . at ( t ) ) / double ( std : : max ( right_bels_v . at ( t ) , 1 ) ) ;
}
return r ? delta < 0 : delta > 0 ;
} ;
while ( pivot > 0 & & is_part_overutil ( false ) ) {
2019-01-22 23:16:00 +08:00
auto & move_cell = cut_cells . at ( pivot ) ;
int size = p - > chain_size . count ( move_cell - > name ) ? p - > chain_size . at ( move_cell - > name ) : 1 ;
2021-01-29 10:32:42 +08:00
left_cells_v . at ( cell_index ( * cut_cells . at ( pivot ) ) ) - = size ;
right_cells_v . at ( cell_index ( * cut_cells . at ( pivot ) ) ) + = size ;
2019-01-22 23:16:00 +08:00
pivot - - ;
}
2020-02-02 23:40:23 +08:00
while ( pivot < int ( cut_cells . size ( ) ) - 1 & & is_part_overutil ( true ) ) {
2019-01-22 23:16:00 +08:00
auto & move_cell = cut_cells . at ( pivot + 1 ) ;
int size = p - > chain_size . count ( move_cell - > name ) ? p - > chain_size . at ( move_cell - > name ) : 1 ;
2021-01-29 10:32:42 +08:00
left_cells_v . at ( cell_index ( * cut_cells . at ( pivot ) ) ) + = size ;
right_cells_v . at ( cell_index ( * cut_cells . at ( pivot ) ) ) - = size ;
2019-01-22 23:16:00 +08:00
pivot + + ;
}
2021-01-28 22:59:13 +08:00
2019-01-22 23:16:00 +08:00
// Split regions into bins, and then spread cells by linear interpolation within those bins
auto spread_binlerp = [ & ] ( int cells_start , int cells_end , double area_l , double area_r ) {
2019-01-23 22:25:34 +08:00
int N = cells_end - cells_start ;
if ( N < = 2 ) {
for ( int i = cells_start ; i < cells_end ; i + + ) {
auto & pos = dir ? p - > cell_locs . at ( cut_cells . at ( i ) - > name ) . rawy
: p - > cell_locs . at ( cut_cells . at ( i ) - > name ) . rawx ;
pos = area_l + i * ( ( area_r - area_l ) / N ) ;
}
return ;
}
2019-01-22 23:16:00 +08:00
// Split region into up to 10 (K) bins
int K = std : : min < int > ( N , 10 ) ;
2019-01-26 03:24:54 +08:00
std : : vector < std : : pair < int , double > > bin_bounds ; // [(cell start, area start)]
2019-01-22 23:16:00 +08:00
bin_bounds . emplace_back ( cells_start , area_l ) ;
for ( int i = 1 ; i < K ; i + + )
2019-01-26 21:22:44 +08:00
bin_bounds . emplace_back ( cells_start + ( N * i ) / K , area_l + ( ( area_r - area_l + 0.99 ) * i ) / K ) ;
2019-01-24 22:05:16 +08:00
bin_bounds . emplace_back ( cells_end , area_r + 0.99 ) ;
2019-01-22 23:16:00 +08:00
for ( int i = 0 ; i < K ; i + + ) {
auto & bl = bin_bounds . at ( i ) , br = bin_bounds . at ( i + 1 ) ;
double orig_left = dir ? p - > cell_locs . at ( cut_cells . at ( bl . first ) - > name ) . rawy
: p - > cell_locs . at ( cut_cells . at ( bl . first ) - > name ) . rawx ;
double orig_right = dir ? p - > cell_locs . at ( cut_cells . at ( br . first - 1 ) - > name ) . rawy
: p - > cell_locs . at ( cut_cells . at ( br . first - 1 ) - > name ) . rawx ;
2019-01-26 03:24:54 +08:00
double m = ( br . second - bl . second ) / std : : max ( 0.00001 , orig_right - orig_left ) ;
2019-01-22 23:16:00 +08:00
for ( int j = bl . first ; j < br . first ; j + + ) {
2019-11-26 18:01:33 +08:00
Region * cr = cut_cells . at ( j ) - > region ;
if ( cr ! = nullptr ) {
// Limit spreading bounds to constraint region; if applicable
double brsc = p - > limit_to_reg ( cr , br . second , dir ) ;
double blsc = p - > limit_to_reg ( cr , bl . second , dir ) ;
double mr = ( brsc - blsc ) / std : : max ( 0.00001 , orig_right - orig_left ) ;
auto & pos = dir ? p - > cell_locs . at ( cut_cells . at ( j ) - > name ) . rawy
: p - > cell_locs . at ( cut_cells . at ( j ) - > name ) . rawx ;
NPNR_ASSERT ( pos > = orig_left & & pos < = orig_right ) ;
pos = blsc + mr * ( pos - orig_left ) ;
} else {
auto & pos = dir ? p - > cell_locs . at ( cut_cells . at ( j ) - > name ) . rawy
: p - > cell_locs . at ( cut_cells . at ( j ) - > name ) . rawx ;
NPNR_ASSERT ( pos > = orig_left & & pos < = orig_right ) ;
pos = bl . second + m * ( pos - orig_left ) ;
}
2019-01-22 23:16:00 +08:00
}
}
} ;
spread_binlerp ( 0 , pivot + 1 , trimmed_l , best_tgt_cut ) ;
spread_binlerp ( pivot + 1 , int ( cut_cells . size ( ) ) , best_tgt_cut + 1 , trimmed_r ) ;
// Update various data structures
for ( int x = r . x0 ; x < = r . x1 ; x + + )
for ( int y = r . y0 ; y < = r . y1 ; y + + ) {
2019-01-23 23:02:49 +08:00
cells_at_location . at ( x ) . at ( y ) . clear ( ) ;
2019-01-22 23:16:00 +08:00
}
for ( auto cell : cut_cells ) {
auto & cl = p - > cell_locs . at ( cell - > name ) ;
2019-01-23 23:02:49 +08:00
cl . x = std : : min ( r . x1 , std : : max ( r . x0 , int ( cl . rawx ) ) ) ;
cl . y = std : : min ( r . y1 , std : : max ( r . y0 , int ( cl . rawy ) ) ) ;
cells_at_location . at ( cl . x ) . at ( cl . y ) . push_back ( cell ) ;
2019-01-22 23:16:00 +08:00
}
2019-01-26 21:22:44 +08:00
SpreaderRegion rl , rr ;
2019-01-22 23:16:00 +08:00
rl . id = int ( regions . size ( ) ) ;
rl . x0 = r . x0 ;
rl . y0 = r . y0 ;
rl . x1 = dir ? r . x1 : best_tgt_cut ;
rl . y1 = dir ? best_tgt_cut : r . y1 ;
2020-02-02 23:40:23 +08:00
rl . cells = left_cells_v ;
rl . bels = left_bels_v ;
2019-01-22 23:16:00 +08:00
rr . id = int ( regions . size ( ) ) + 1 ;
rr . x0 = dir ? r . x0 : ( best_tgt_cut + 1 ) ;
rr . y0 = dir ? ( best_tgt_cut + 1 ) : r . y0 ;
rr . x1 = r . x1 ;
rr . y1 = r . y1 ;
2020-02-02 23:40:23 +08:00
rr . cells = right_cells_v ;
rr . bels = right_bels_v ;
2019-01-22 23:16:00 +08:00
regions . push_back ( rl ) ;
regions . push_back ( rr ) ;
for ( int x = rl . x0 ; x < = rl . x1 ; x + + )
for ( int y = rl . y0 ; y < = rl . y1 ; y + + )
groups . at ( x ) . at ( y ) = rl . id ;
for ( int x = rr . x0 ; x < = rr . x1 ; x + + )
for ( int y = rr . y0 ; y < = rr . y1 ; y + + )
groups . at ( x ) . at ( y ) = rr . id ;
return std : : make_pair ( rl . id , rr . id ) ;
} ;
2019-01-11 19:59:34 +08:00
} ;
2019-01-11 19:31:56 +08:00
typedef decltype ( CellInfo : : udata ) cell_udata_t ;
cell_udata_t dont_solve = std : : numeric_limits < cell_udata_t > : : max ( ) ;
2019-01-10 19:18:47 +08:00
} ;
2019-01-26 21:22:44 +08:00
int HeAPPlacer : : CutSpreader : : seq = 0 ;
2019-01-10 19:18:47 +08:00
2019-02-25 20:48:01 +08:00
bool placer_heap ( Context * ctx , PlacerHeapCfg cfg ) { return HeAPPlacer ( ctx , cfg ) . place ( ) ; }
2019-06-15 19:09:49 +08:00
PlacerHeapCfg : : PlacerHeapCfg ( Context * ctx )
2019-02-25 20:48:01 +08:00
{
2021-02-18 06:06:23 +08:00
alpha = ctx - > setting < float > ( " placerHeap/alpha " ) ;
beta = ctx - > setting < float > ( " placerHeap/beta " ) ;
criticalityExponent = ctx - > setting < int > ( " placerHeap/criticalityExponent " ) ;
timingWeight = ctx - > setting < int > ( " placerHeap/timingWeight " ) ;
2021-12-20 03:29:44 +08:00
parallelRefine = ctx - > setting < bool > ( " placerHeap/parallelRefine " , false ) ;
2019-06-28 19:43:55 +08:00
timing_driven = ctx - > setting < bool > ( " timing_driven " ) ;
2020-02-02 23:28:11 +08:00
solverTolerance = 1e-5 ;
2020-02-02 23:40:23 +08:00
placeAllAtOnce = false ;
2020-02-02 23:52:28 +08:00
2022-12-21 05:10:37 +08:00
int timeout_divisor = ctx - > setting < int > ( " placerHeap/cellPlacementTimeout " , 8 ) ;
if ( timeout_divisor > 0 ) {
// Set a conservative default. This is a rather large number and could probably
// be shaved down, but for now it will keep the process from running indefinite.
cell_placement_timeout = std : : max ( 10000 , ( int ( ctx - > cells . size ( ) ) * int ( ctx - > cells . size ( ) ) / timeout_divisor ) ) ;
} else {
cell_placement_timeout = 0 ;
}
2020-02-02 23:52:28 +08:00
hpwl_scale_x = 1 ;
hpwl_scale_y = 1 ;
spread_scale_x = 1 ;
spread_scale_y = 1 ;
2019-02-25 20:48:01 +08:00
}
2019-02-25 19:56:10 +08:00
NEXTPNR_NAMESPACE_END
# else
# include "log.h"
# include "nextpnr.h"
2019-02-25 20:48:01 +08:00
# include "placer_heap.h"
2019-01-11 00:42:29 +08:00
2019-02-25 19:56:10 +08:00
NEXTPNR_NAMESPACE_BEGIN
2019-02-25 20:48:01 +08:00
bool placer_heap ( Context * ctx , PlacerHeapCfg cfg )
2019-02-25 19:56:10 +08:00
{
log_error ( " nextpnr was built without the HeAP placer \n " ) ;
return false ;
}
2019-02-25 20:48:01 +08:00
2019-06-15 19:09:49 +08:00
PlacerHeapCfg : : PlacerHeapCfg ( Context * ctx ) { }
2019-02-25 20:48:01 +08:00
2019-01-31 04:41:00 +08:00
NEXTPNR_NAMESPACE_END
2019-02-25 19:56:10 +08:00
# endif