├── .gitattributes ├── thirdparty └── pybind11 │ └── CMakeLists.txt ├── .gitignore ├── pycoloquinte ├── CMakeLists.txt ├── wheel.sh └── setup.py ├── .gitmodules ├── src ├── utils │ ├── helpers.hpp │ └── norm.hpp ├── place_detailed │ ├── abacus_legalizer.hpp │ ├── tetris_legalizer.hpp │ ├── row_legalizer.hpp │ ├── row_neighbourhood.hpp │ ├── row_legalizer.cpp │ ├── abacus_legalizer.cpp │ ├── tetris_legalizer.cpp │ ├── incr_net_model.hpp │ ├── legalizer.hpp │ ├── place_detailed.hpp │ ├── row_neighbourhood.cpp │ ├── detailed_placement.hpp │ ├── incr_net_model.cpp │ └── legalizer.cpp ├── export.cpp └── place_global │ ├── place_global.hpp │ ├── transportation.hpp │ ├── density_legalizer.hpp │ ├── transportation_1d.hpp │ ├── net_model.hpp │ └── place_global.cpp ├── .github └── workflows │ ├── warnings.yml │ └── build.yml ├── TODO.md ├── test ├── CMakeLists.txt ├── run_benchmarks.sh ├── test_density_grid.cpp ├── test_incr_net_model.cpp ├── test_net_model.cpp ├── test_expansion.cpp ├── test_density_legalizer.cpp ├── test_transportation.cpp ├── test_row_neighbourhood.cpp ├── test_row_legalizer.cpp ├── test_detailed_placement.cpp └── test_legalizer.cpp ├── default.nix ├── .clang-tidy ├── LICENSE ├── CMakeLists.txt ├── meson.build ├── README.md └── .clang-format /.gitattributes: -------------------------------------------------------------------------------- 1 | *.xz filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /thirdparty/pybind11/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_subdirectory(pybind11) 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | pycoloquinte/__pycache__ 3 | pycoloquinte/coloquinte.egg-info/ 4 | pycoloquinte/coloquinte_pybind.egg-info/ 5 | pycoloquinte/dist/ 6 | .vscode 7 | -------------------------------------------------------------------------------- /pycoloquinte/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | pybind11_add_module(coloquinte_pybind module.cpp) 3 | target_link_libraries(coloquinte_pybind PRIVATE coloquinte ${Boost_LIBRARIES}) 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "thirdparty/pybind11/pybind11"] 2 | path = thirdparty/pybind11/pybind11 3 | url = https://github.com/pybind/pybind11.git 4 | [submodule "subprojects/lemon"] 5 | path = subprojects/lemon 6 | url = https://github.com/Coloquinte/lemon 7 | -------------------------------------------------------------------------------- /src/utils/helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace coloquinte { 7 | /** 8 | * @brief Compute the limits when subdividing an interval 9 | */ 10 | inline std::vector computeSubdivisions(int min, int max, int number) { 11 | assert(number >= 1); 12 | assert(max >= min); 13 | std::vector ret; 14 | for (int i = 0; i < number + 1; ++i) { 15 | ret.push_back(min + (i * (max - min) / number)); 16 | } 17 | assert((int)ret.size() == number + 1); 18 | assert(ret.front() == min); 19 | assert(ret.back() == max); 20 | return ret; 21 | } 22 | } // namespace coloquinte 23 | -------------------------------------------------------------------------------- /.github/workflows/warnings.yml: -------------------------------------------------------------------------------- 1 | name: No warnings 2 | 3 | on: workflow_dispatch 4 | # push: 5 | # branches: [ "*" ] 6 | # pull_request: 7 | # branches: [ "*" ] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | 12 | jobs: 13 | no-warnings: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: recursive 20 | 21 | - name: Install dependencies 22 | run: sudo apt-get install libboost-all-dev libeigen3-dev ninja-build; pip3 install cmake meson 23 | 24 | - name: Build 25 | run: meson setup build --warnlevel=3 --werror; meson compile -C build 26 | 27 | - name: Test 28 | run: meson test -C build 29 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## Features 2 | 3 | * Timing-driven placement: integrate timing-driven placement with a callback 4 | * Routing-driven placement: integrate routing-driven placement with a callback 5 | 6 | ## Ease-of-development 7 | 8 | * More randomization: 9 | * Make the detailed placer less deterministic 10 | * Randomize lower-bound and upper-bound positions 11 | 12 | * Configuration handling: 13 | * Expose an help message for the options 14 | * Organize options hierarchically 15 | 16 | ## Performance 17 | 18 | * Optimization for various quality/time tradeoffs 19 | * Parallel implementation for detailed placement 20 | * Legalization cost models other than L1 21 | * Reoptimization in legalization 22 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | FIND_PACKAGE(Boost REQUIRED COMPONENTS unit_test_framework) 3 | 4 | SET(TESTS 5 | test_net_model 6 | test_density_legalizer 7 | test_density_grid 8 | test_row_legalizer 9 | test_legalizer 10 | test_incr_net_model 11 | test_detailed_placement 12 | test_row_neighbourhood 13 | test_transportation 14 | test_expansion 15 | ) 16 | 17 | FOREACH(TEST IN LISTS TESTS) 18 | add_executable(${TEST} ${TEST}.cpp) 19 | target_include_directories(${TEST} PRIVATE ${Boost_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/include/) 20 | target_link_libraries(${TEST} ${Boost_LIBRARIES} coloquinte) 21 | add_test(${TEST} ${TEST}) 22 | ENDFOREACH(TEST) 23 | 24 | 25 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { lib, stdenv, fetchFromGitHub, cmake, lemon-graph, eigen, boost 2 | }: 3 | 4 | stdenv.mkDerivation rec { 5 | pname = "coloquinte"; 6 | version = "0.3.1"; 7 | 8 | meta = with lib; { 9 | description = "Placement library for electronic circuits"; 10 | homepage = "https://github.com/Coloquinte/PlaceRoute"; 11 | license = licenses.mit; 12 | platforms = platforms.linux; 13 | }; 14 | 15 | src = fetchFromGitHub { 16 | owner = "coloquinte"; 17 | repo = "PlaceRoute"; 18 | rev = "${version}"; 19 | hash = "sha256-bPDXaNZCNBM0qiu+46cL/zH/41lwqHPqfqTzJaERgVQ="; 20 | }; 21 | 22 | nativeBuildInputs = [ 23 | cmake 24 | ]; 25 | 26 | buildInputs = [ 27 | lemon-graph 28 | eigen 29 | boost 30 | ]; 31 | } 32 | -------------------------------------------------------------------------------- /pycoloquinte/wheel.sh: -------------------------------------------------------------------------------- 1 | # Run on docker run -it quay.io/pypa/manylinux2014_x86_64 /bin/bash 2 | 3 | # Install the dependencies 4 | yum install boost-devel boost-static eigen3-devel -y 5 | # Download and install lemon; package coin-or-lemon-devel not available 6 | yum install wget -y 7 | wget http://lemon.cs.elte.hu/pub/sources/lemon-1.3.1.tar.gz; tar -xzf lemon-1.3.1.tar.gz; cd lemon-1.3.1; mkdir build; cd build; cmake ..; make install; cd ../.. 8 | 9 | # Build the wheels 10 | git clone https://github.com/Coloquinte/PlaceRoute --recursive 11 | cd PlaceRoute/pycoloquinte/ 12 | for p in /usr/local/bin/python3*; do ${p} setup.py bdist_wheel; done 13 | for w in dist/coloquinte*; do auditwheel repair $w; done 14 | 15 | # Upload the wheels 16 | python3.11 -m pip install twine 17 | python3.11 -m twine upload wheelhouse/* 18 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | FormatStyle: file 2 | 3 | Checks: '-*,clang-diagnostic-*,clang-analyzer-*,readability-identifier-naming,performance-*,modernize-*,-modernize-use-trailing-return-type,-modernize-use-nodiscard' 4 | CheckOptions: 5 | - { key: readability-identifier-naming.NamespaceCase, value: lower_case } 6 | - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } 7 | - { key: readability-identifier-naming.ClassCase, value: CamelCase } 8 | - { key: readability-identifier-naming.StructCase, value: CamelCase } 9 | - { key: readability-identifier-naming.FunctionCase, value: camelCase } 10 | - { key: readability-identifier-naming.VariableCase, value: camelCase } 11 | - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } 12 | - { key: readability-identifier-naming.TemplateParameterCase, value: UPPER_CASE } 13 | -------------------------------------------------------------------------------- /test/run_benchmarks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | efforts="1 3 6 9" 4 | benchmarks=$(find ISPD06 -name "*.aux" | sort) 5 | names="" 6 | 7 | for benchmark in $benchmarks 8 | do 9 | name=$(basename "${benchmark}" .aux) 10 | names="${names} ${name}" 11 | done 12 | 13 | echo "Running placement benchmarks" 14 | echo "Benchmarks: ${names}" 15 | echo "Efforts: ${efforts}" 16 | echo 17 | 18 | echo 19 | 20 | mkdir -p logs 21 | 22 | for effort in $efforts 23 | do 24 | resfile="results_${effort}.csv" 25 | echo "Benchmark Effort WL" > "${resfile}" 26 | for benchmark in $benchmarks 27 | do 28 | name=$(basename "${benchmark}" .aux) 29 | logfile="logs/log_${name}_${effort}.txt" 30 | 31 | # Run placement 32 | echo " Running benchmark ${name} effort ${effort}" 33 | coloquinte "${benchmark}" --effort "${effort}" 2>&1 > "${logfile}" || echo " Failure" 34 | 35 | # Write the results (WL) in a csv 36 | echo -n "${name} ${effort} " >> "${resfile}" 37 | grep "Detailed placement done" "${logfile}" | sed "s/.*(WL \(.*\)).*/\1/" >> "${resfile}" 38 | done 39 | done 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Gabriel Gouvine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/utils/norm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "coloquinte.hpp" 7 | 8 | namespace coloquinte { 9 | 10 | template 11 | inline T computeNorm(T x, T y, LegalizationModel leg) { 12 | T z; 13 | switch (leg) { 14 | case LegalizationModel::L1: 15 | return std::abs(x) + std::abs(y); 16 | case LegalizationModel::L2: 17 | return std::sqrt(x * x + y * y); 18 | case LegalizationModel::LInf: 19 | return std::max(std::abs(x), std::abs(y)); 20 | case LegalizationModel::L1Squared: 21 | z = std::abs(x) + std::abs(y); 22 | return z * z; 23 | case LegalizationModel::L2Squared: 24 | return x * x + y * y; 25 | case LegalizationModel::LInfSquared: 26 | z = std::max(std::abs(x), std::abs(y)); 27 | return z * z; 28 | default: 29 | throw std::runtime_error("Unknown legalization model"); 30 | } 31 | } 32 | 33 | /** 34 | * @brief Compute the norm of the 2D vector with the given cost model 35 | */ 36 | inline float norm(float x, float y, LegalizationModel leg) { 37 | return computeNorm(x, y, leg); 38 | } 39 | 40 | /** 41 | * @brief Compute the norm of the 2D vector with the given cost model 42 | */ 43 | inline long long norm(int x, int y, LegalizationModel leg) { 44 | return computeNorm(x, y, leg); 45 | } 46 | } // namespace coloquinte -------------------------------------------------------------------------------- /src/place_detailed/abacus_legalizer.hpp: -------------------------------------------------------------------------------- 1 | 2 | #include "place_detailed/legalizer.hpp" 3 | 4 | namespace coloquinte { 5 | /** 6 | * Legalization following the "Abacus" algorithm with L1 cost 7 | * 8 | * Cells are placed in rows, and are allowed to push other cells to get to a 9 | * better overall solution. 10 | */ 11 | class AbacusLegalizer : public LegalizerBase { 12 | public: 13 | /** 14 | * @brief Initialization of the datastructure 15 | */ 16 | AbacusLegalizer(const std::vector &rows, const std::vector &width, 17 | const std::vector &height, 18 | const std::vector &polarities, 19 | const std::vector &targetX, 20 | const std::vector &targetY, 21 | const std::vector &targetOrientation); 22 | 23 | /** 24 | * @brief Run the algorithm 25 | */ 26 | void run(); 27 | 28 | /** 29 | * @brief Place a single cell optimally 30 | * Return true if successful 31 | */ 32 | void placeCell(int cell); 33 | 34 | /** 35 | * @brief Find the optimal placement in the row 36 | * 37 | * Return a pair: true if successful, and the distance with this placement 38 | */ 39 | std::pair evaluatePlacement(int cell, int row); 40 | 41 | /** 42 | * @brief Check consistency 43 | */ 44 | void check() const; 45 | 46 | private: 47 | // Algorithm state 48 | std::vector > rowToCells_; 49 | std::vector rowLegalizers_; 50 | }; 51 | } // namespace coloquinte -------------------------------------------------------------------------------- /src/place_detailed/tetris_legalizer.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "place_detailed/legalizer.hpp" 4 | 5 | namespace coloquinte { 6 | /** 7 | * Very simple legalization following the "Tetris" algorithm. 8 | * 9 | * Cells are placed one after the other, as close as possible to their target. 10 | * This legalizer handles macros too, and keeps their target orientation unless 11 | * a row polarity has been specified. 12 | */ 13 | class TetrisLegalizer : public LegalizerBase { 14 | public: 15 | /** 16 | * @brief Initialization of the datastructure 17 | */ 18 | TetrisLegalizer(const std::vector &rows, const std::vector &width, 19 | const std::vector &height, 20 | const std::vector &polarities, 21 | const std::vector &targetX, 22 | const std::vector &targetY, 23 | const std::vector &targetOrientation); 24 | 25 | /** 26 | * @brief Run the algorithm 27 | */ 28 | void run(); 29 | 30 | /** 31 | * @brief Place a single cell 32 | */ 33 | void placeCell(int c); 34 | 35 | /** 36 | * @brief Place a single cell 37 | */ 38 | std::pair attemptPlacement(int c, int y) const; 39 | 40 | /** 41 | * @brief Return possible placement intervals for a given width, height and y 42 | */ 43 | std::vector > getPossibleIntervals(int w, int h, 44 | int y) const; 45 | 46 | /** 47 | * @brief Instanciate the placement of a cell 48 | */ 49 | void instanciateCell(int x, int y, int w, int h); 50 | 51 | /// @brief First free position in the row 52 | std::vector rowFreePos_; 53 | }; 54 | } // namespace coloquinte -------------------------------------------------------------------------------- /test/test_density_grid.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE DENSITY_GRID 2 | 3 | #include 4 | 5 | #include "place_global/density_grid.hpp" 6 | 7 | using namespace coloquinte; 8 | 9 | BOOST_AUTO_TEST_CASE(BasicGrid) { 10 | Rectangle area(0, 49, 0, 70); 11 | DensityGrid grid(7, area); 12 | grid.check(); 13 | BOOST_CHECK_EQUAL(grid.nbBinsX(), 7); 14 | BOOST_CHECK_EQUAL(grid.nbBinsY(), 10); 15 | } 16 | 17 | BOOST_AUTO_TEST_CASE(BasicHierarchicalPlacement1) { 18 | Rectangle area(0, 49, 0, 70); 19 | DensityGrid grid(7, area); 20 | std::vector demands = {1, 2, 3}; 21 | HierarchicalDensityPlacement hpl(grid, demands); 22 | hpl.check(); 23 | BOOST_CHECK_EQUAL(hpl.nbCells(), 3); 24 | BOOST_CHECK_EQUAL(hpl.nbBinsX(), 1); 25 | BOOST_CHECK_EQUAL(hpl.nbBinsY(), 1); 26 | BOOST_CHECK_EQUAL(hpl.levelX(), hpl.nbLevelX() - 1); 27 | BOOST_CHECK_EQUAL(hpl.levelY(), hpl.nbLevelY() - 1); 28 | for (int i = 0; i + 1 < hpl.nbLevelX(); ++i) { 29 | hpl.refineX(); 30 | hpl.check(); 31 | } 32 | for (int i = 0; i + 1 < hpl.nbLevelY(); ++i) { 33 | hpl.refineY(); 34 | hpl.check(); 35 | } 36 | for (int i = 0; i + 1 < hpl.nbLevelX(); ++i) { 37 | hpl.coarsenX(); 38 | hpl.check(); 39 | } 40 | for (int i = 0; i + 1 < hpl.nbLevelY(); ++i) { 41 | hpl.coarsenY(); 42 | hpl.check(); 43 | } 44 | } 45 | 46 | BOOST_AUTO_TEST_CASE(HierarchicalPlacementFindCoord) { 47 | Rectangle area(0, 49, -20, 70); 48 | DensityGrid grid(7, area); 49 | std::vector demands = {1, 2, 3}; 50 | HierarchicalDensityPlacement hpl(grid, demands); 51 | hpl.check(); 52 | hpl.refineX(); 53 | hpl.refineX(); 54 | hpl.refineY(); 55 | for (int i = -10; i < 60; ++i) { 56 | hpl.findBinByX(i); 57 | } 58 | for (int i = -30; i < 80; ++i) { 59 | hpl.findBinByY(i); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "*" ] 6 | pull_request: 7 | branches: [ "*" ] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: recursive 20 | 21 | - name: Install dependencies 22 | run: sudo apt-get install libboost-all-dev libeigen3-dev liblemon-dev 23 | 24 | - name: Configure CMake 25 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 26 | 27 | - name: Build 28 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 29 | 30 | - name: Test 31 | working-directory: ${{github.workspace}}/build 32 | run: ctest -C ${{env.BUILD_TYPE}} 33 | 34 | build-meson: 35 | runs-on: ubuntu-latest 36 | 37 | steps: 38 | - uses: actions/checkout@v3 39 | with: 40 | submodules: recursive 41 | 42 | - name: Install dependencies 43 | run: sudo apt-get install libboost-all-dev libeigen3-dev ninja-build; pip3 install cmake meson 44 | 45 | - name: Build 46 | run: meson setup build; meson compile -C build 47 | 48 | - name: Test 49 | run: meson test -C build 50 | 51 | build-python: 52 | runs-on: ubuntu-latest 53 | 54 | steps: 55 | - uses: actions/checkout@v3 56 | with: 57 | submodules: recursive 58 | 59 | - name: Install dependencies 60 | run: sudo apt-get install libboost-all-dev libeigen3-dev ninja-build liblemon-dev libbz2-dev liblzma-dev; pip3 install cmake meson 61 | 62 | - name: Build 63 | run: cd pycoloquinte; python3 setup.py install --user 64 | 65 | - name: Download test 66 | run: wget https://github.com/Coloquinte/PlaceRoute/releases/download/benchmark/adaptec1.tar.gz; tar -xzf adaptec1.tar.gz 67 | 68 | - name: Run typical test 69 | run: coloquinte --effort 1 adaptec1 70 | 71 | - name: Run scaled test 72 | run: coloquinte --effort 1 --density 0.75 adaptec1 73 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.10) 3 | PROJECT(COLOQUINTE) 4 | SET(CMAKE_CXX_STANDARD 17) 5 | 6 | OPTION(BUILD_PYCOLOQUINTE "Build Coloquinte Python package") 7 | 8 | IF(BUILD_PYCOLOQUINTE) 9 | SET(Boost_USE_STATIC_LIBS ON) 10 | ELSE() 11 | ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK) 12 | ENDIF() 13 | 14 | FIND_PACKAGE(Boost REQUIRED COMPONENTS system filesystem iostreams program_options unit_test_framework) 15 | FIND_PACKAGE(LEMON NAMES LEMON lemon REQUIRED) 16 | FIND_PACKAGE(Eigen3 3.3 REQUIRED NO_MODULE) 17 | FIND_PACKAGE(Threads REQUIRED) 18 | 19 | INCLUDE_DIRECTORIES( 20 | ${COLOQUINTE_SOURCE_DIR}/src 21 | ${LEMON_INCLUDE_DIRS} 22 | ${COLOQUINTE_SOURCE_DIR}/thirdparty/pybind11 23 | ) 24 | 25 | SET(SOURCES 26 | src/coloquinte.cpp 27 | src/parameters.cpp 28 | src/export.cpp 29 | src/place_global/net_model.cpp 30 | src/place_global/density_legalizer.cpp 31 | src/place_global/density_grid.cpp 32 | src/place_global/place_global.cpp 33 | src/place_detailed/legalizer.cpp 34 | src/place_detailed/abacus_legalizer.cpp 35 | src/place_detailed/tetris_legalizer.cpp 36 | src/place_detailed/row_legalizer.cpp 37 | src/place_detailed/place_detailed.cpp 38 | src/place_detailed/detailed_placement.cpp 39 | src/place_detailed/incr_net_model.cpp 40 | src/place_detailed/row_neighbourhood.cpp 41 | src/place_global/transportation.cpp 42 | src/place_global/transportation_1d.cpp 43 | ) 44 | 45 | IF(BUILD_PYCOLOQUINTE) 46 | add_library(coloquinte STATIC ${SOURCES}) 47 | ELSE() 48 | add_library(coloquinte SHARED ${SOURCES}) 49 | ENDIF() 50 | 51 | set_target_properties(coloquinte PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 52 | target_compile_features(coloquinte PUBLIC cxx_std_17) 53 | target_link_libraries(coloquinte 54 | PRIVATE 55 | ${Boost_LIBRARIES} 56 | Threads::Threads 57 | ) 58 | 59 | IF(BUILD_PYCOLOQUINTE) 60 | add_subdirectory(thirdparty/pybind11) 61 | add_subdirectory(pycoloquinte) 62 | ENDIF(BUILD_PYCOLOQUINTE) 63 | 64 | IF(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 65 | enable_testing() 66 | add_subdirectory(test) 67 | ENDIF() 68 | 69 | INSTALL( 70 | TARGETS coloquinte 71 | LIBRARY DESTINATION lib 72 | COMPONENT library 73 | ) 74 | INSTALL( 75 | FILES ${COLOQUINTE_SOURCE_DIR}/src/coloquinte.hpp 76 | DESTINATION include/coloquinte 77 | COMPONENT headers 78 | ) 79 | -------------------------------------------------------------------------------- /test/test_incr_net_model.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE INCR_NET_MODEL 2 | 3 | #include 4 | 5 | #include "place_detailed/incr_net_model.hpp" 6 | 7 | using namespace coloquinte; 8 | 9 | BOOST_AUTO_TEST_CASE(TestConstruction1) { 10 | IncrNetModelBuilder bd(0); 11 | IncrNetModel model = bd.build(); 12 | model.check(); 13 | BOOST_CHECK_EQUAL(model.value(), 0LL); 14 | } 15 | 16 | BOOST_AUTO_TEST_CASE(TestConstruction2) { 17 | IncrNetModelBuilder bd(0); 18 | bd.addNet({}, {}); 19 | IncrNetModel model = bd.build(); 20 | model.check(); 21 | BOOST_CHECK_EQUAL(model.value(), 0LL); 22 | BOOST_CHECK_EQUAL(model.nbNets(), 0); 23 | } 24 | 25 | BOOST_AUTO_TEST_CASE(TestConstruction3) { 26 | IncrNetModelBuilder bd(3); 27 | bd.addNet({1, 2}, {0, 0}); 28 | bd.addNet({1, 0}, {0, 0}); 29 | IncrNetModel model = bd.build({0, 1, 2}); 30 | model.check(); 31 | BOOST_CHECK_EQUAL(model.value(), 2LL); 32 | BOOST_CHECK_EQUAL(model.nbNets(), 2); 33 | } 34 | 35 | BOOST_AUTO_TEST_CASE(TestConstruction4) { 36 | IncrNetModelBuilder bd(3); 37 | bd.addNet({1, 2, 0}, {8, 0, 2}); 38 | bd.addNet({1, 0}, {0, 4}); 39 | IncrNetModel model = bd.build({0, 1, 2}); 40 | model.check(); 41 | BOOST_CHECK_EQUAL(model.value(), 10LL); 42 | BOOST_CHECK_EQUAL(model.nbNets(), 2); 43 | } 44 | 45 | BOOST_AUTO_TEST_CASE(TestMove1) { 46 | IncrNetModelBuilder bd(3); 47 | bd.addNet({0, 1, 2}, {2, 4, 6}); 48 | IncrNetModel model = bd.build({0, 1, 2}); 49 | model.check(); 50 | BOOST_CHECK_EQUAL(model.value(), 6LL); 51 | BOOST_CHECK_EQUAL(model.nbNets(), 1); 52 | model.updateCellPos(0, 2); 53 | model.check(); 54 | model.updateCellPos(0, 2); 55 | model.check(); 56 | model.updateCellPos(1, -2); 57 | model.check(); 58 | model.updateCellPos(1, 8); 59 | model.check(); 60 | model.updateCellPos(2, 1); 61 | model.check(); 62 | } 63 | 64 | BOOST_AUTO_TEST_CASE(TestMove2) { 65 | IncrNetModelBuilder bd(3); 66 | bd.addNet({0, 1, 2}, {0, 0, 0}); 67 | IncrNetModel model = bd.build({0, 1, 2}); 68 | model.check(); 69 | BOOST_CHECK_EQUAL(model.nbNets(), 1); 70 | BOOST_CHECK_EQUAL(model.value(), 2LL); 71 | model.updateCellPos(0, 2); 72 | model.check(); 73 | BOOST_CHECK_EQUAL(model.value(), 1LL); 74 | model.updateCellPos(0, 2); 75 | model.check(); 76 | BOOST_CHECK_EQUAL(model.value(), 1LL); 77 | model.updateCellPos(1, -2); 78 | model.check(); 79 | BOOST_CHECK_EQUAL(model.value(), 4LL); 80 | model.updateCellPos(1, 8); 81 | model.check(); 82 | BOOST_CHECK_EQUAL(model.value(), 6LL); 83 | model.updateCellPos(2, 1); 84 | model.check(); 85 | BOOST_CHECK_EQUAL(model.value(), 7LL); 86 | } 87 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | 2 | project('coloquinte', 'cpp', 3 | version : '0.3.1', 4 | license : 'MIT', 5 | default_options: [ 6 | 'cpp_std=c++17', 7 | ], 8 | ) 9 | 10 | if build_machine.system() == 'darwin' 11 | add_project_arguments('-mmacosx-version-min=13.0', language: ['c','cpp']) 12 | endif 13 | 14 | coloquinte_includes = include_directories('src') 15 | boost_base_dep = dependency('boost', required: true) 16 | if boost_base_dep.version().split('.')[1].to_int() < 89 17 | boost_modules = ['system', 'filesystem', 'iostreams', 'program_options', 'unit_test_framework'] 18 | else 19 | boost_modules = ['filesystem', 'iostreams', 'program_options', 'unit_test_framework'] 20 | endif 21 | boost_dep = dependency('boost', required: true, modules: boost_modules) 22 | 23 | eigen_dep = dependency('eigen3', required: true) 24 | thread_dep = dependency('threads', required: true) 25 | 26 | cmake =import('cmake') 27 | cmake_opts = cmake.subproject_options() 28 | cmake_opts.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': true}) 29 | cmake_opts.set_install(false) 30 | lemon = cmake.subproject('lemon', options: cmake_opts) 31 | lemon_dep = lemon.dependency('lemon') 32 | 33 | sources = [ 34 | 'src/coloquinte.cpp', 35 | 'src/parameters.cpp', 36 | 'src/export.cpp', 37 | 'src/place_global/net_model.cpp', 38 | 'src/place_global/density_legalizer.cpp', 39 | 'src/place_global/density_grid.cpp', 40 | 'src/place_global/place_global.cpp', 41 | 'src/place_detailed/legalizer.cpp', 42 | 'src/place_detailed/abacus_legalizer.cpp', 43 | 'src/place_detailed/tetris_legalizer.cpp', 44 | 'src/place_detailed/row_legalizer.cpp', 45 | 'src/place_detailed/place_detailed.cpp', 46 | 'src/place_detailed/detailed_placement.cpp', 47 | 'src/place_detailed/incr_net_model.cpp', 48 | 'src/place_detailed/row_neighbourhood.cpp', 49 | 'src/place_global/transportation.cpp', 50 | 'src/place_global/transportation_1d.cpp', 51 | ] 52 | 53 | libcoloquinte = shared_library('coloquinte', 54 | sources, 55 | include_directories : coloquinte_includes, 56 | dependencies : [boost_dep, lemon_dep, eigen_dep, thread_dep], 57 | install : true) 58 | 59 | install_headers('src/coloquinte.hpp') 60 | 61 | coloquinte_dep = declare_dependency(link_with : libcoloquinte, include_directories: coloquinte_includes) 62 | 63 | tests = [ 64 | 'test_net_model' 65 | , 'test_density_legalizer' 66 | , 'test_density_grid' 67 | , 'test_row_legalizer' 68 | , 'test_legalizer' 69 | , 'test_incr_net_model' 70 | , 'test_detailed_placement' 71 | , 'test_row_neighbourhood' 72 | , 'test_transportation' 73 | , 'test_expansion' 74 | ] 75 | 76 | foreach testcase: tests 77 | test_exec = executable(testcase, 'test/' + testcase + '.cpp', dependencies: [boost_dep, coloquinte_dep]) 78 | test(testcase, test_exec) 79 | endforeach 80 | -------------------------------------------------------------------------------- /test/test_net_model.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE NET_MODEL 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "place_global/net_model.hpp" 11 | 12 | using namespace coloquinte; 13 | 14 | BOOST_AUTO_TEST_CASE(b2b_singlepin) { 15 | int nbCells = 4; 16 | NetModel model(nbCells); 17 | model.addNet({0}, {0.0f}, 4.0f, 4.0f); 18 | model.addNet({1}, {5.0f}, 1.0f, 1.0f); 19 | model.addNet({2}, {-6.0f}, 2.0f, 2.0f); 20 | model.addNet({3}, {2.0f}, 3.0f, 3.0f); 21 | BOOST_CHECK_EQUAL(model.nbCells(), 4); 22 | BOOST_CHECK_EQUAL(model.nbNets(), 4); 23 | BOOST_CHECK_EQUAL(model.nbPins(0), 2); 24 | BOOST_CHECK_EQUAL(model.nbPins(1), 2); 25 | BOOST_CHECK_EQUAL(model.nbPins(2), 2); 26 | BOOST_CHECK_EQUAL(model.nbPins(3), 2); 27 | std::vector place = {10.0f, 20.0f, 30.0f, 40.0f}; 28 | auto res = model.solveB2B(place, NetModel::Parameters()); 29 | BOOST_CHECK_CLOSE(res[0], 4.0, 0.001); 30 | BOOST_CHECK_CLOSE(res[1], -4.0, 0.001); 31 | BOOST_CHECK_CLOSE(res[2], 8.0, 0.001); 32 | BOOST_CHECK_CLOSE(res[3], 1.0, 0.001); 33 | } 34 | BOOST_AUTO_TEST_CASE(b2b_mid) { 35 | int nbCells = 2; 36 | NetModel model(nbCells); 37 | model.addNet({0, 1}, {0.0f, 0.0f}, 0.0f, 4.0f); 38 | std::vector place = {2.0f, 3.0f}; 39 | NetModel::Parameters params; 40 | params.approximationDistance = 1.0e-8; 41 | auto res = model.solveB2B(place, params); 42 | BOOST_CHECK_CLOSE(res[0], 2.0, 0.001); 43 | BOOST_CHECK_CLOSE(res[1], 3.0, 0.001); 44 | } 45 | 46 | BOOST_AUTO_TEST_CASE(b2b_two_pins) { 47 | int nbCells = 1; 48 | NetModel model(nbCells); 49 | model.addNet({0}, {0.0f}, 0.0f, 4.0f); 50 | std::vector place = {8.0f}; 51 | NetModel::Parameters params; 52 | params.approximationDistance = 1.0e-8; 53 | auto res = model.solveB2B(place, params); 54 | BOOST_CHECK_CLOSE(res[0], 8.0 / 3, 0.001); 55 | } 56 | 57 | BOOST_AUTO_TEST_CASE(star_singlepin) { 58 | int nbCells = 4; 59 | NetModel model(nbCells); 60 | model.addNet({0}, {0.0f}, 4.0f, 4.0f); 61 | model.addNet({1}, {5.0f}, 1.0f, 1.0f); 62 | model.addNet({2}, {-6.0f}, 2.0f, 2.0f); 63 | model.addNet({3}, {2.0f}, 3.0f, 3.0f); 64 | std::vector place = {10.0f, 20.0f, 30.0f, 40.0f}; 65 | auto res = model.solveStar(place, NetModel::Parameters()); 66 | BOOST_CHECK_CLOSE(res[0], 4.0, 0.001); 67 | BOOST_CHECK_CLOSE(res[1], -4.0, 0.001); 68 | BOOST_CHECK_CLOSE(res[2], 8.0, 0.001); 69 | BOOST_CHECK_CLOSE(res[3], 1.0, 0.001); 70 | } 71 | 72 | BOOST_AUTO_TEST_CASE(star_mid) { 73 | int nbCells = 2; 74 | NetModel model(nbCells); 75 | model.addNet({0, 1}, {0.0f, 0.0f}, 0.0f, 4.0f); 76 | std::vector place = {2.0f, 3.0f}; 77 | auto res = model.solveStar(place, NetModel::Parameters()); 78 | BOOST_CHECK_CLOSE(res[0], 2.0, 0.001); 79 | BOOST_CHECK_CLOSE(res[1], 3.0, 0.001); 80 | } 81 | -------------------------------------------------------------------------------- /test/test_expansion.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE EXPANSION 2 | 3 | #include 4 | 5 | #include "coloquinte.hpp" 6 | 7 | using namespace coloquinte; 8 | 9 | BOOST_AUTO_TEST_CASE(TestDensityExpansion) { 10 | Circuit circuit(1); 11 | circuit.setCellWidth({20}); 12 | circuit.setCellHeight({10}); 13 | circuit.setRows({Row(0, 40, 0, 10, CellOrientation::N)}); 14 | circuit.expandCellsToDensity(0.75, 0.0); 15 | BOOST_CHECK_EQUAL(circuit.cellWidth()[0], 30); 16 | } 17 | 18 | BOOST_AUTO_TEST_CASE(TestDensityExpansionWithMargin) { 19 | Circuit circuit(1); 20 | circuit.setCellWidth({20}); 21 | circuit.setCellHeight({10}); 22 | circuit.setRows({Row(0, 50, 0, 10, CellOrientation::N)}); 23 | circuit.expandCellsToDensity(0.75, 0.5); 24 | BOOST_CHECK_EQUAL(circuit.cellWidth()[0], 30); 25 | } 26 | 27 | BOOST_AUTO_TEST_CASE(TestFactorExpansion) { 28 | Circuit circuit(1); 29 | circuit.setCellWidth({20}); 30 | circuit.setCellHeight({10}); 31 | circuit.setRows({Row(0, 40, 0, 10, CellOrientation::N)}); 32 | circuit.expandCellsByFactor({1.5f}); 33 | BOOST_CHECK_EQUAL(circuit.cellWidth()[0], 30); 34 | } 35 | 36 | BOOST_AUTO_TEST_CASE(TestFactorExpansionWithMargin) { 37 | Circuit circuit(1); 38 | circuit.setCellWidth({20}); 39 | circuit.setCellHeight({10}); 40 | circuit.setRows({Row(0, 50, 0, 10, CellOrientation::N)}); 41 | circuit.expandCellsByFactor({1.5f}, 1.0, 0.5); 42 | BOOST_CHECK_EQUAL(circuit.cellWidth()[0], 30); 43 | } 44 | 45 | BOOST_AUTO_TEST_CASE(TestFactorExpansionTooLarge) { 46 | Circuit circuit(1); 47 | circuit.setCellWidth({20}); 48 | circuit.setCellHeight({10}); 49 | circuit.setRows({Row(0, 40, 0, 10, CellOrientation::N)}); 50 | circuit.expandCellsByFactor({10.0f}); 51 | BOOST_CHECK_EQUAL(circuit.cellWidth()[0], 40); 52 | } 53 | 54 | BOOST_AUTO_TEST_CASE(TestFactorExpansionMultiple) { 55 | Circuit circuit(2); 56 | circuit.setCellWidth({20, 12}); 57 | circuit.setCellHeight({10, 10}); 58 | circuit.setRows({Row(0, 100, 0, 10, CellOrientation::N)}); 59 | circuit.expandCellsByFactor({1.5f, 1.34f}, 1.0, 0.0); 60 | BOOST_CHECK_EQUAL(circuit.cellWidth()[0], 30); 61 | BOOST_CHECK_EQUAL(circuit.cellWidth()[1], 16); 62 | } 63 | 64 | BOOST_AUTO_TEST_CASE(TestExpansionComputation) { 65 | Circuit circuit(4); 66 | circuit.setCellWidth({20, 20, 20, 20}); 67 | circuit.setCellHeight({10, 10, 10, 10}); 68 | circuit.setCellX({40, 9, 10, -5}); 69 | circuit.setCellY({-100, 19, 20, 19}); 70 | std::vector > congestionMap; 71 | congestionMap.emplace_back(Rectangle({0, 10, 10, 20}), 1.2); 72 | congestionMap.emplace_back(Rectangle({-5, 0, 10, 20}), 1.5); 73 | std::vector expansion = circuit.computeCellExpansion(congestionMap); 74 | BOOST_CHECK_EQUAL(expansion.size(), circuit.nbCells()); 75 | BOOST_CHECK_EQUAL(expansion[0], 1.0f); 76 | BOOST_CHECK_EQUAL(expansion[1], 1.2f); 77 | BOOST_CHECK_EQUAL(expansion[2], 1.0f); 78 | BOOST_CHECK_EQUAL(expansion[3], 1.5f); 79 | } 80 | -------------------------------------------------------------------------------- /src/export.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "coloquinte.hpp" 5 | 6 | namespace coloquinte { 7 | 8 | void exportIspdAux(const std::string &filename) { 9 | std::ofstream f(filename + ".aux"); 10 | f << "RowBasedPlacement : " << filename << ".nodes " << filename << ".nets " 11 | << filename << ".pl " << filename << ".scl" << std::endl; 12 | } 13 | 14 | void exportIspdNodes(const Circuit &circuit, const std::string &filename) { 15 | std::ofstream f(filename + ".nodes"); 16 | f << "UCLA nodes 1.0\n\n"; 17 | f << "NumNodes : " << circuit.nbCells() << "\n"; 18 | int numTerminals = 0; 19 | for (int i = 0; i < circuit.nbCells(); ++i) { 20 | if (circuit.isFixed(i)) { 21 | ++numTerminals; 22 | } 23 | } 24 | f << "NumTerminals : " << numTerminals << "\n"; 25 | 26 | for (int i = 0; i < circuit.nbCells(); ++i) { 27 | f << "\to" << i << "\t" << circuit.cellWidth_[i] << "\t" 28 | << circuit.cellHeight_[i]; 29 | if (circuit.isFixed(i)) { 30 | f << "\tterminal"; 31 | } 32 | f << "\n"; 33 | } 34 | } 35 | 36 | void exportIspdPlace(const Circuit &circuit, const std::string &filename) { 37 | std::ofstream f(filename + ".pl"); 38 | f << "UCLA pl 1.0\n\n"; 39 | 40 | for (int i = 0; i < circuit.nbCells(); ++i) { 41 | f << "o" << i << "\t" << circuit.x(i) << "\t" << circuit.y(i) 42 | << "\t: " << toString(circuit.orientation(i)) << "\n"; 43 | } 44 | } 45 | 46 | void exportIspdNets(const Circuit &circuit, const std::string &filename) { 47 | std::ofstream f(filename + ".nets"); 48 | f << "UCLA nets 1.0\n\n"; 49 | 50 | f << "NumNets : " << circuit.nbNets() << "\n"; 51 | f << "NumPins : " << circuit.nbPins() << "\n\n"; 52 | 53 | for (int i = 0; i < circuit.nbNets(); ++i) { 54 | f << "NetDegree : " << circuit.nbPinsNet(i) << " n" << i << "\n"; 55 | for (int j = 0; j < circuit.nbPinsNet(i); ++j) { 56 | int c = circuit.pinCell(i, j); 57 | f << "\to" << c << " I : "; 58 | double x = circuit.pinXOffset(i, j) - 0.5 * circuit.cellWidth_[c]; 59 | double y = circuit.pinYOffset(i, j) - 0.5 * circuit.cellHeight_[c]; 60 | f << x << " " << y << "\n"; 61 | } 62 | } 63 | } 64 | 65 | void exportIspdRows(const Circuit &circuit, const std::string &filename) { 66 | std::ofstream f(filename + ".scl"); 67 | f << "UCLA scl 1.0\n\n"; 68 | 69 | f << "NumRows : " << circuit.nbRows() << "\n\n"; 70 | for (int i = 0; i < circuit.nbRows(); ++i) { 71 | f << "CoreRow Horizontal\n"; 72 | f << " Coordinate : " << circuit.rows()[i].minY << "\n"; 73 | f << " Height : " << circuit.rows()[i].height() << "\n"; 74 | f << " Sitewidth : 1\n"; 75 | f << " Sitespacing : 1\n"; 76 | f << " Siteorient : 1\n"; 77 | f << " Sitesymmetry : 1\n"; 78 | f << " SubrowOrigin : " << circuit.rows()[i].minX 79 | << " NumSites : " << circuit.rows()[i].width() << "\n"; 80 | f << "End\n"; 81 | } 82 | } 83 | 84 | void Circuit::exportIspd(const std::string &filename) const { 85 | exportIspdAux(filename); 86 | exportIspdNodes(*this, filename); 87 | exportIspdPlace(*this, filename); 88 | exportIspdNets(*this, filename); 89 | exportIspdRows(*this, filename); 90 | } 91 | } // namespace coloquinte -------------------------------------------------------------------------------- /src/place_detailed/row_legalizer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace coloquinte { 7 | /** 8 | * @brief Obtain the positions minimizing total weighted displacement along a 9 | * row. 10 | * 11 | * It is an ordered single row problem/fixed order single machine scheduling 12 | * problem, solved by the specialized cascading descent algorithm. 13 | * 14 | * The cost model is linear in the distance to the target position, weighted by 15 | * the width of the cells 16 | **/ 17 | class RowLegalizer { 18 | public: 19 | /// Initialize 20 | RowLegalizer(int b, int e) : begin_(b), end_(e), cumWidth_(1, 0) {} 21 | 22 | /** 23 | * @brief Return the space already used in the row 24 | */ 25 | int usedSpace() const { return cumWidth_.back(); } 26 | 27 | /** 28 | * @brief Return the space remaining in the row 29 | */ 30 | int remainingSpace() const { return end_ - begin_ - usedSpace(); } 31 | 32 | /** 33 | * @brief Return the current last position 34 | */ 35 | int lastAvailablePos() const { return constrainingPos_.back() + usedSpace(); } 36 | 37 | /** 38 | * @brief Return the cost of pushing a new cell, without updating the 39 | * datastructure 40 | */ 41 | long long getCost(int width, int targetPos); 42 | 43 | /** 44 | * @brief Update the datastructure with a new cell and return the cost 45 | */ 46 | long long push(int width, int targetPos); 47 | 48 | /** 49 | * @brief Return the placement of each cell in the datastructure 50 | */ 51 | std::vector getPlacement() const; 52 | 53 | /** 54 | * @brief Check the consistency 55 | */ 56 | void check() const; 57 | 58 | /** 59 | * @brief Remove all cells from the datastructure 60 | */ 61 | void clear(); 62 | 63 | private: 64 | /** 65 | * @brief Representation of the events in the cascading descent algorithm 66 | */ 67 | struct Bound { 68 | /// Will be the target absolute position of the cell 69 | int absolutePos; 70 | /// Will be proportional to the width of the cell 71 | int weight; 72 | 73 | bool operator<(Bound const o) const { 74 | return absolutePos < o.absolutePos || 75 | (absolutePos == o.absolutePos && weight < o.weight); 76 | } 77 | Bound(int w, int absPos) : absolutePos(absPos), weight(w) {} 78 | }; 79 | 80 | /** 81 | * @brief Return the number of elements 82 | */ 83 | int nbElements() const { return cumWidth_.size() - 1; } 84 | 85 | /** 86 | * @brief Return the width of this element 87 | */ 88 | int width(int ind) const { return cumWidth_[ind + 1] - cumWidth_[ind]; } 89 | 90 | /// Get the cost of pushing a cell on the row 91 | long long getDisplacement(int width, int targetPos, bool update); 92 | 93 | /// Leftmost coordinate of the region 94 | int begin_; 95 | 96 | /// Rightmost coordinate of the region 97 | int end_; 98 | 99 | /// Where the cells constrain the positions of preceding cells 100 | std::vector constrainingPos_; 101 | 102 | /// Cumulative width of the cells 103 | std::vector cumWidth_; 104 | 105 | /// Priority queue for the cascading descent algorithm 106 | std::priority_queue bounds; 107 | }; 108 | } // namespace coloquinte -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Build](https://github.com/Coloquinte/PlaceRoute/actions/workflows/build.yml/badge.svg) 2 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/8cfe5dc06da74f399fc007e69b742cdc)](https://www.codacy.com/gh/Coloquinte/PlaceRoute/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Coloquinte/PlaceRoute&utm_campaign=Badge_Grade) 3 | [![GitHub](https://img.shields.io/github/license/coloquinte/PlaceRoute?color=blue)](https://opensource.org/licenses/MIT) 4 | [![PyPI](https://img.shields.io/pypi/v/coloquinte?color=orange)](https://pypi.org/project/coloquinte/) 5 | 6 | # Coloquinte Place&Route 7 | 8 | Coloquinte is a Place&Route tool for electronic circuits. 9 | Its goal is to provide a single package of well-tested and well-tuned Place&Route algorithms, to be used in open source electronic design toolchains. 10 | 11 | This replaces and extends the [placement library](https://github.com/Coloquinte/Coloquinte_placement) used in the [Coriolis](https://gitlab.lip6.fr/vlsi-eda/coriolis/) toolchain. 12 | 13 | ## Using Coloquinte 14 | 15 | ISPD placement and routing benchmarks are available directly in this repository using Git LFS. [Placement benchmarks](https://github.com/Coloquinte/PlaceRouteBenchmarks) can be run with the Python package: 16 | ``` bash 17 | coloquinte ISPD06/adaptec1 18 | ``` 19 | 20 | For other applications, you can use Coloquinte as a C++ library or a Python package: see `src/coloquinte.hpp` or `help(coloquinte.Circuit)`. 21 | 22 | ## Installing Coloquinte 23 | 24 | ### Python package 25 | 26 | On Linux, you may install Coloquinte from pip: 27 | ``` bash 28 | pip install coloquinte 29 | ``` 30 | 31 | For the latest version or for other OSes, install the dependencies and build the module. For example: 32 | ``` bash 33 | sudo apt-get install g++ cmake libboost-all-dev libeigen3-dev liblemon-dev 34 | cd pycoloquinte 35 | python setup.py install 36 | ``` 37 | 38 | ### C++ library 39 | 40 | Install dependencies and build Coloquinte using CMake: 41 | ``` bash 42 | sudo apt-get install g++ cmake libboost-all-dev libeigen3-dev liblemon-dev 43 | cmake -B build; cmake --build build; ctest --test-dir build 44 | ``` 45 | 46 | Or using Meson: 47 | ``` bash 48 | meson setup build; meson compile -C build; meson test -C build 49 | ``` 50 | 51 | ## Benchmarks 52 | 53 | Coloquinte is tested on the [ISPD06 benchmark suite](https://dl.acm.org/doi/10.1145/1123008.1123042). Below is the reported half-perimeter wirelength on these benchmarks (x107) for various effort parameters. 54 | Higher effort = higher quality but higher runtime. 55 | 56 | | Benchmark | Effort 1 WL | Effort 3 WL | Effort 6 WL | 57 | | --------- | ----------- | ----------- | ----------- | 58 | | adaptec1 | 8,04 | 7,62 | 7,55 | 59 | | adaptec2 | 8,83 | 8,48 | 8,38 | 60 | | adaptec3 | 21,07 | 20,31 | 19,95 | 61 | | adaptec4 | 18,84 | 17,97 | 17,77 | 62 | | adaptec5 | 32,65 | 31,18 | 30,89 | 63 | | bigblue1 | 10,01 | 9,36 | 9,20 | 64 | | bigblue2 | 14,79 | 14,25 | 14,17 | 65 | | bigblue3 | 35,23 | 32,67 | 31,98 | 66 | | bigblue4 | 83,15 | 79,16 | 77,66 | 67 | | newblue1 | 60,68 | 18,75 | 34,54 | 68 | | newblue2 | 19,38 | 18,31 | 17,97 | 69 | | newblue3 | 33,70 | 26,06 | 25,84 | 70 | | newblue4 | 24,32 | 23,35 | 23,33 | 71 | | newblue5 | 42,29 | 39,39 | 39,02 | 72 | | newblue6 | 48,43 | 45,80 | 45,39 | 73 | | newblue7 | 102,11 | 97,81 | 96,47 | 74 | -------------------------------------------------------------------------------- /src/place_detailed/row_neighbourhood.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "coloquinte.hpp" 4 | 5 | namespace coloquinte { 6 | /** 7 | * @brief Given standard cell rows, access neighbouring rows easily 8 | */ 9 | class RowNeighbourhood { 10 | public: 11 | /** 12 | * @brief Initialize the datastructure 13 | */ 14 | explicit RowNeighbourhood(const std::vector &rows, 15 | int nbNeighbourRows = 1); 16 | 17 | /** 18 | * @brief Initialize the datastructure 19 | */ 20 | explicit RowNeighbourhood(const std::vector &rows, 21 | int nbNeighbourRows = 1); 22 | 23 | /** 24 | * @brief Return the number of rows 25 | */ 26 | int nbRows() const { return rowsBelow_.size(); } 27 | 28 | /** 29 | * @brief Test if r1 is below r2 30 | */ 31 | static bool isBelow(Rectangle r1, Rectangle r2); 32 | 33 | /** 34 | * @brief Test if r1 is above r2 35 | */ 36 | static bool isAbove(Rectangle r1, Rectangle r2); 37 | 38 | /** 39 | * @brief Test if r1 is left of r2 40 | */ 41 | static bool isLeft(Rectangle r1, Rectangle r2); 42 | 43 | /** 44 | * @brief Test if r1 is right of r2 45 | */ 46 | static bool isRight(Rectangle r1, Rectangle r2); 47 | 48 | /** 49 | * @brief Return a few rows below, closest ones first 50 | */ 51 | const std::vector &rowsBelow(int row) const { return rowsBelow_[row]; } 52 | 53 | /** 54 | * @brief Return a few rows above, closest ones first 55 | */ 56 | const std::vector &rowsAbove(int row) const { return rowsAbove_[row]; } 57 | 58 | /** 59 | * @brief Return a few rows on the left, closest ones first 60 | */ 61 | const std::vector &rowsLeft(int row) const { return rowsLeft_[row]; } 62 | 63 | /** 64 | * @brief Return a few rows on the right, closest ones first 65 | */ 66 | const std::vector &rowsRight(int row) const { return rowsRight_[row]; } 67 | 68 | /** 69 | * @brief Check consistency of the datastructure 70 | */ 71 | void check() const; 72 | 73 | private: 74 | /** 75 | * @brief Simple unoptimized setup with quadratic complexity 76 | * 77 | * TODO: Implement a setup method with lower complexity 78 | */ 79 | void simpleSetup(const std::vector &rows, int nbNeighbourRows); 80 | 81 | /** 82 | * @brief Build the structure indicating the rows below 83 | */ 84 | static std::vector > rowsBelow( 85 | const std::vector &rows, int nbNeighbourRows); 86 | 87 | /** 88 | * @brief Build the structure indicating the rows above 89 | */ 90 | static std::vector > rowsAbove( 91 | const std::vector &rows, int nbNeighbourRows); 92 | 93 | /** 94 | * @brief Build the structure indicating the rows on the sides (assumes rows 95 | * above/below are built already) 96 | */ 97 | void buildRowsSides(const std::vector &rows, int nbNeighbourRows); 98 | 99 | std::vector buildLeftFrom(Rectangle row, 100 | const std::vector &rows, 101 | int ind) const; 102 | std::vector buildRightFrom(Rectangle row, 103 | const std::vector &rows, 104 | int ind) const; 105 | 106 | private: 107 | std::vector > rowsBelow_; 108 | std::vector > rowsAbove_; 109 | std::vector > rowsLeft_; 110 | std::vector > rowsRight_; 111 | }; 112 | } // Namespace coloquinte -------------------------------------------------------------------------------- /test/test_density_legalizer.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE DENSITY_LEGALIZER 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "place_global/density_legalizer.hpp" 12 | 13 | using namespace coloquinte; 14 | 15 | BOOST_AUTO_TEST_CASE(EqualSplit) { 16 | Rectangle area(0, 10, 0, 5); 17 | std::vector cellDemand = {4, 4, 4, 4}; 18 | std::vector cellTargetX = {0.0, 0.0, 6.0, 6.0}; 19 | std::vector cellTargetY = {0.0, 0.0, 0.0, 0.0}; 20 | DensityGrid grid(5, area); 21 | DensityLegalizer leg(grid, cellDemand); 22 | leg.updateCellDemand(cellDemand); 23 | leg.updateCellTargetX(cellTargetX); 24 | leg.updateCellTargetY(cellTargetY); 25 | leg.run(); 26 | BOOST_CHECK_EQUAL(leg.binCells(0, 0).size(), 2); 27 | BOOST_CHECK_EQUAL(leg.binCells(1, 0).size(), 2); 28 | BOOST_CHECK_EQUAL(leg.binUsage(0, 0), 8); 29 | BOOST_CHECK_EQUAL(leg.binUsage(1, 0), 8); 30 | BOOST_CHECK_EQUAL(leg.cellBinX(0), 0); 31 | BOOST_CHECK_EQUAL(leg.cellBinX(1), 0); 32 | BOOST_CHECK_EQUAL(leg.cellBinX(2), 1); 33 | BOOST_CHECK_EQUAL(leg.cellBinX(3), 1); 34 | } 35 | 36 | BOOST_AUTO_TEST_CASE(OneSide) { 37 | Rectangle area(0, 8, 0, 2); 38 | std::vector cellDemand = {4, 4, 4, 4}; 39 | std::vector cellTargetX = {0.0, 6.0, 6.0, 6.0}; 40 | std::vector cellTargetY = {0.0, 0.0, 0.0, 0.0}; 41 | DensityGrid grid(4, area); 42 | DensityLegalizer leg(grid, cellDemand); 43 | leg.updateCellDemand(cellDemand); 44 | leg.updateCellTargetX(cellTargetX); 45 | leg.updateCellTargetY(cellTargetY); 46 | leg.run(); 47 | BOOST_CHECK_EQUAL(leg.binCells(0, 0).size(), 2); 48 | BOOST_CHECK_EQUAL(leg.binCells(1, 0).size(), 2); 49 | BOOST_CHECK_EQUAL(leg.binCapacity(0, 0), 8); 50 | BOOST_CHECK_EQUAL(leg.binCapacity(1, 0), 8); 51 | BOOST_CHECK_EQUAL(leg.binUsage(0, 0), 8); 52 | BOOST_CHECK_EQUAL(leg.binUsage(1, 0), 8); 53 | BOOST_CHECK_EQUAL(leg.cellBinX(0), 0); 54 | BOOST_CHECK_EQUAL(leg.cellBinX(1), 0); 55 | BOOST_CHECK_EQUAL(leg.cellBinX(2), 1); 56 | BOOST_CHECK_EQUAL(leg.cellBinX(3), 1); 57 | } 58 | 59 | BOOST_AUTO_TEST_CASE(Random) { 60 | Rectangle area(0, 10, 0, 10); 61 | std::vector cellDemand = {4, 4, 4, 4}; 62 | std::vector cellTargetX = {0.0, 6.0, 0.0, 6.0}; 63 | std::vector cellTargetY = {0.0, 0.0, 0.0, 0.0}; 64 | DensityGrid grid(5, area); 65 | DensityLegalizer leg(grid, cellDemand); 66 | leg.updateCellDemand(cellDemand); 67 | leg.updateCellTargetX(cellTargetX); 68 | leg.updateCellTargetY(cellTargetY); 69 | leg.run(); 70 | BOOST_CHECK_EQUAL(leg.cellBinX(0), 0); 71 | BOOST_CHECK_EQUAL(leg.cellBinX(1), 1); 72 | BOOST_CHECK_EQUAL(leg.cellBinX(2), 0); 73 | BOOST_CHECK_EQUAL(leg.cellBinX(3), 1); 74 | } 75 | 76 | BOOST_AUTO_TEST_CASE(Coord) { 77 | Rectangle area(0, 10, 0, 5); 78 | DensityGrid grid(1, area); 79 | HierarchicalDensityPlacement pl(grid, 0); 80 | pl.refineX(); 81 | pl.refineY(); 82 | pl.refineX(); 83 | BOOST_CHECK_EQUAL(pl.nbBinsX(), 4); 84 | BOOST_CHECK_EQUAL(pl.nbBinsY(), 2); 85 | BOOST_CHECK_CLOSE(pl.binX(0, 0), 1.0f, 0.0001f); 86 | BOOST_CHECK_CLOSE(pl.binX(1, 0), 3.5f, 0.0001f); 87 | BOOST_CHECK_CLOSE(pl.binX(2, 0), 6.0f, 0.0001f); 88 | BOOST_CHECK_CLOSE(pl.binX(3, 0), 8.5f, 0.0001f); 89 | BOOST_CHECK_CLOSE(pl.binY(0, 0), 1.0f, 0.0001f); 90 | BOOST_CHECK_CLOSE(pl.binY(0, 1), 3.5f, 0.0001f); 91 | } 92 | 93 | BOOST_AUTO_TEST_CASE(UnitArea) { 94 | Rectangle area(0, 10, 0, 5); 95 | DensityGrid grid(1, area); 96 | DensityLegalizer pl(grid, std::vector(50, 1)); 97 | pl.run(); 98 | pl.check(); 99 | BOOST_CHECK_EQUAL(pl.totalOverflow(), 0); 100 | pl.coarsenFully(); 101 | pl.check(); 102 | } 103 | -------------------------------------------------------------------------------- /test/test_transportation.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE TRANSPORTATION 2 | 3 | #include 4 | 5 | #include "place_global/transportation.hpp" 6 | 7 | using namespace coloquinte; 8 | 9 | BOOST_AUTO_TEST_CASE(TestAddSink) { 10 | std::vector capacities = {5, 6}; 11 | std::vector demands = {1, 2, 3, 4, 5}; 12 | std::vector > costs = {{0.0f, 1.0f, 2.0f, 3.0f, 4.0f}, 13 | {5.0f, 6.0f, 7.0f, 8.0f, 9.0f}}; 14 | TransportationProblem solver(capacities, demands, costs); 15 | solver.check(); 16 | BOOST_CHECK(!solver.isBalanced()); 17 | BOOST_CHECK_EQUAL(solver.nbSinks(), 2); 18 | BOOST_CHECK_EQUAL(solver.nbSources(), 5); 19 | 20 | solver.addDummyCapacity(); 21 | solver.addDummyDemand(); 22 | BOOST_CHECK_EQUAL(solver.nbSinks(), 3); 23 | BOOST_CHECK_EQUAL(solver.nbSources(), 5); 24 | BOOST_CHECK(solver.isBalanced()); 25 | BOOST_CHECK(!solver.isFeasible()); 26 | 27 | solver.makeFeasible(); 28 | BOOST_CHECK(solver.isFeasible()); 29 | solver.check(); 30 | } 31 | 32 | BOOST_AUTO_TEST_CASE(TestAddSource) { 33 | std::vector capacities = {5, 6}; 34 | std::vector demands = {1, 2, 3, 4}; 35 | std::vector > costs = {{0.0f, 1.0f, 2.0f, 3.0f}, 36 | {4.0f, 5.0f, 6.0f, 7.0f}}; 37 | TransportationProblem solver(capacities, demands, costs); 38 | solver.check(); 39 | BOOST_CHECK(!solver.isBalanced()); 40 | BOOST_CHECK_EQUAL(solver.nbSinks(), 2); 41 | BOOST_CHECK_EQUAL(solver.nbSources(), 4); 42 | 43 | solver.addDummyCapacity(); 44 | solver.addDummyDemand(); 45 | BOOST_CHECK_EQUAL(solver.nbSinks(), 2); 46 | BOOST_CHECK_EQUAL(solver.nbSources(), 5); 47 | BOOST_CHECK(solver.isBalanced()); 48 | BOOST_CHECK(!solver.isFeasible()); 49 | 50 | solver.makeFeasible(); 51 | BOOST_CHECK(solver.isFeasible()); 52 | solver.check(); 53 | } 54 | 55 | BOOST_AUTO_TEST_CASE(TestAlreadyFeasible) { 56 | std::vector capacities = {5, 5}; 57 | std::vector demands = {1, 2, 3, 4}; 58 | std::vector > costs = {{0.0f, 1.0f, 2.0f, 3.0f}, 59 | {4.0f, 5.0f, 6.0f, 7.0f}}; 60 | std::vector > allocations = {{1, 2, 2, 0}, 61 | {0, 0, 1, 4}}; 62 | TransportationProblem solver(capacities, demands, costs); 63 | solver.setAllocations(allocations); 64 | solver.check(); 65 | BOOST_CHECK(solver.isBalanced()); 66 | BOOST_CHECK(solver.isFeasible()); 67 | BOOST_CHECK_EQUAL(solver.nbSinks(), 2); 68 | BOOST_CHECK_EQUAL(solver.nbSources(), 4); 69 | 70 | solver.addDummyCapacity(); 71 | solver.addDummyDemand(); 72 | BOOST_CHECK_EQUAL(solver.nbSinks(), 2); 73 | BOOST_CHECK_EQUAL(solver.nbSources(), 4); 74 | 75 | solver.makeFeasible(); 76 | BOOST_CHECK(solver.isFeasible()); 77 | } 78 | 79 | BOOST_AUTO_TEST_CASE(TestRun) { 80 | std::vector capacities = {12, 12, 12}; 81 | std::vector demands = {1, 2, 3, 4, 5, 6, 7, 8}; 82 | std::vector > costs = { 83 | {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f}, 84 | {7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f, 0.0f}, 85 | {4.0f, 4.0f, 4.0f, 4.0f, 0.0f, 0.0f, 0.0f, 0.0f}}; 86 | std::vector > allocations = {{1, 2, 3, 4, 2, 0, 0, 0}, 87 | {0, 0, 0, 0, 3, 6, 3, 0}, 88 | {0, 0, 0, 0, 0, 0, 4, 8}}; 89 | TransportationProblem solver(capacities, demands, costs); 90 | solver.setAllocations(allocations); 91 | solver.check(); 92 | BOOST_CHECK(solver.isBalanced()); 93 | BOOST_CHECK(solver.isFeasible()); 94 | BOOST_CHECK_EQUAL(solver.nbSinks(), 3); 95 | BOOST_CHECK_EQUAL(solver.nbSources(), 8); 96 | 97 | solver.solve(); 98 | } 99 | -------------------------------------------------------------------------------- /src/place_detailed/row_legalizer.cpp: -------------------------------------------------------------------------------- 1 | #include "place_detailed/row_legalizer.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace coloquinte { 10 | inline long long RowLegalizer::getDisplacement(int width, int targetPos, 11 | bool update) { 12 | int targetAbsPos = targetPos - usedSpace(); 13 | int slope = -width; 14 | 15 | int cur_pos = end_; 16 | long long cur_cost = 0; 17 | 18 | std::vector passed_bounds; 19 | 20 | // While the slope is negative or the position is not legal yet 21 | while (not bounds.empty() and 22 | ((slope < 0 and bounds.top().absolutePos > targetAbsPos) or 23 | bounds.top().absolutePos > end_ - usedSpace() - width)) { 24 | int old_pos = cur_pos; 25 | cur_pos = bounds.top().absolutePos; 26 | cur_cost += static_cast(old_pos - cur_pos) * (slope + width); 27 | slope += bounds.top().weight; 28 | 29 | // Remember which bounds we encountered in order to reset the object to its 30 | // initial state 31 | if (not update) { 32 | passed_bounds.push_back(bounds.top()); 33 | } 34 | bounds.pop(); 35 | } 36 | assert(cur_pos >= begin_); 37 | 38 | // Always before the end and not pushed beyond the target position 39 | int finalAbsPos = 40 | std::min(end_ - usedSpace() - width, 41 | std::max(begin_, slope >= 0 ? cur_pos : targetAbsPos)); 42 | 43 | cur_cost += (cur_pos - finalAbsPos) * (slope + width); 44 | 45 | assert(finalAbsPos >= begin_); 46 | assert(finalAbsPos <= end_ - usedSpace() - width); 47 | 48 | if (update) { 49 | cumWidth_.push_back(width + usedSpace()); 50 | constrainingPos_.push_back(finalAbsPos); 51 | if (slope > 0) { // Remaining capacity of an encountered bound 52 | bounds.push(Bound(slope, cur_pos)); 53 | } 54 | // The new bound, depending on whether it was passed or not 55 | if (targetAbsPos > begin_) { 56 | bounds.push(Bound(2 * width + std::min(slope, 0), 57 | std::min(targetAbsPos, finalAbsPos))); 58 | } 59 | } else { 60 | for (Bound b : passed_bounds) { 61 | bounds.push(b); 62 | } 63 | } 64 | 65 | return cur_cost + 66 | width * std::abs(finalAbsPos - 67 | targetAbsPos); // Add the cost of the new cell 68 | } 69 | 70 | long long RowLegalizer::getCost(int width, int targetPos) { 71 | return getDisplacement(width, targetPos, false); 72 | } 73 | 74 | long long RowLegalizer::push(int width, int targetPos) { 75 | return getDisplacement(width, targetPos, true); 76 | } 77 | 78 | void RowLegalizer::clear() { 79 | cumWidth_.assign(1, 0); 80 | bounds = std::priority_queue(); 81 | constrainingPos_.clear(); 82 | } 83 | 84 | std::vector RowLegalizer::getPlacement() const { 85 | auto finalAbsPos = constrainingPos_; 86 | std::partial_sum(finalAbsPos.rbegin(), finalAbsPos.rend(), 87 | finalAbsPos.rbegin(), 88 | [](int a, int b) -> int { return std::min(a, b); }); 89 | 90 | std::vector ret(finalAbsPos.size()); 91 | for (size_t i = 0; i < finalAbsPos.size(); ++i) { 92 | ret[i] = finalAbsPos[i] + cumWidth_[i]; 93 | 94 | assert(finalAbsPos[i] >= begin_); 95 | assert(finalAbsPos[i] + cumWidth_[i + 1] <= end_); 96 | } 97 | return ret; 98 | } 99 | 100 | void RowLegalizer::check() const { 101 | std::vector pl = getPlacement(); 102 | for (int i = 0; i < nbElements(); ++i) { 103 | if (pl[i] < begin_) { 104 | throw std::runtime_error("A cell is placed before the row"); 105 | } 106 | if (pl[i] + width(i) > end_) { 107 | throw std::runtime_error("A cell is placed after the row"); 108 | } 109 | } 110 | for (int i = 0; i + 1 < nbElements(); ++i) { 111 | if (pl[i + 1] - pl[i] < width(i)) { 112 | throw std::runtime_error("Two cells overlap"); 113 | } 114 | } 115 | } 116 | } // namespace coloquinte -------------------------------------------------------------------------------- /src/place_global/place_global.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "coloquinte.hpp" 7 | #include "place_global/density_legalizer.hpp" 8 | #include "place_global/net_model.hpp" 9 | 10 | namespace coloquinte { 11 | 12 | /** 13 | * @brief Main class for global placement 14 | */ 15 | class GlobalPlacer { 16 | public: 17 | /** 18 | * @brief Run global placement on the circuit representation 19 | * 20 | * @param circuit The circuit to be modified 21 | * @param params Placement parameters 22 | */ 23 | static void place(Circuit &circuit, const ColoquinteParameters ¶ms, 24 | const std::optional &callback = {}); 25 | 26 | private: 27 | /** 28 | * @brief Initialize the datastructure 29 | */ 30 | explicit GlobalPlacer(Circuit &circuit, const ColoquinteParameters ¶ms); 31 | 32 | /** 33 | * @brief Run the whole global placement algorithm 34 | */ 35 | void run(); 36 | 37 | /** 38 | * @brief Return the net model length of the lower-bound placement 39 | */ 40 | float valueLB() const; 41 | 42 | /** 43 | * @brief Return the net model length of the upper-bound placement 44 | */ 45 | float valueUB() const; 46 | 47 | /** 48 | * @brief Obtain the initial lower-bound placement 49 | */ 50 | void runInitialLB(); 51 | 52 | /** 53 | * @brief Obtain the lower-bound placement for one iteration 54 | */ 55 | void runLB(); 56 | 57 | /** 58 | * @brief Obtain the upper-bound placement for one iteration 59 | */ 60 | void runUB(); 61 | 62 | /** 63 | * @brief Call the callback with this placement 64 | */ 65 | void callback(PlacementStep step, const std::vector &xplace, 66 | const std::vector &yplace); 67 | 68 | /** 69 | * @brief Export the placement to the ISPD circuit 70 | */ 71 | void exportPlacement(Circuit &) const; 72 | 73 | /** 74 | * @brief Export a given placement to the ISPD circuit 75 | */ 76 | static void exportPlacement(Circuit &, const std::vector &xplace, 77 | const std::vector &yplace); 78 | 79 | /** 80 | * @brief Compute the average cell size of the circuit 81 | */ 82 | float computeAverageCellSize() const; 83 | 84 | /** 85 | * @brief Compute the base penalty forces from the area of the cells 86 | */ 87 | std::vector computePerCellPenalty() const; 88 | 89 | /** 90 | * @brief Approximation distance used by the continuous model 91 | */ 92 | float initialApproximationDistance() const { 93 | return params_.global.continuousModel.approximationDistance * 94 | averageCellLength_; 95 | } 96 | 97 | /** 98 | * @brief Distance at which the full displacement penalty is obtained 99 | */ 100 | float initialPenaltyCutoffDistance() const { 101 | return params_.global.penalty.cutoffDistance * averageCellLength_; 102 | } 103 | 104 | /** 105 | * @brief Distance between upper and lower bound at which we stop placement 106 | */ 107 | float distanceTolerance() const { 108 | return params_.global.distanceTolerance * averageCellLength_; 109 | } 110 | 111 | /** 112 | * @brief Distance between upper and lower bound at which we start routing 113 | * and timing driven placement 114 | */ 115 | float penaltyUpdateDistance() const { 116 | return params_.global.penaltyUpdateDistance * averageCellLength_; 117 | } 118 | 119 | /** 120 | * @brief Compute the penalty forces for this iteration 121 | */ 122 | std::vector computeIterationPerCellPenalty(); 123 | 124 | /** 125 | * @brief React to a change in cell sizes during a callback 126 | */ 127 | void updateCellSizes(); 128 | 129 | /** 130 | * @brief React to a change in nets during a callback 131 | */ 132 | void updateNets(); 133 | 134 | private: 135 | DensityLegalizer leg_; 136 | NetModel xtopo_; 137 | NetModel ytopo_; 138 | 139 | std::vector xPlacementLB_; 140 | std::vector yPlacementLB_; 141 | std::vector xPlacementUB_; 142 | std::vector yPlacementUB_; 143 | 144 | ColoquinteParameters params_; 145 | 146 | float averageCellLength_; 147 | std::vector perCellPenalty_; 148 | int step_; 149 | float penalty_; 150 | float penaltyCutoffDistance_; 151 | float approximationDistance_; 152 | std::mt19937 rgen_; 153 | 154 | // Only for callbacks 155 | Circuit &circuit_; 156 | std::optional callback_; 157 | }; 158 | 159 | } // namespace coloquinte 160 | -------------------------------------------------------------------------------- /src/place_detailed/abacus_legalizer.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "place_detailed/abacus_legalizer.hpp" 4 | 5 | #include "utils/norm.hpp" 6 | 7 | namespace coloquinte { 8 | 9 | AbacusLegalizer::AbacusLegalizer( 10 | const std::vector &rows, const std::vector &width, 11 | const std::vector &height, 12 | const std::vector &polarities, 13 | const std::vector &targetX, const std::vector &targetY, 14 | const std::vector &targetOrientation) 15 | : LegalizerBase(rows, width, height, polarities, targetX, targetY, 16 | targetOrientation) { 17 | rowToCells_.resize(rows_.size()); 18 | for (const Row &row : rows_) { 19 | rowLegalizers_.emplace_back(row.minX, row.maxX); 20 | } 21 | } 22 | 23 | void AbacusLegalizer::run() { 24 | for (int c = 0; c < nbCells(); ++c) { 25 | placeCell(c); 26 | } 27 | for (int i = 0; i < nbRows(); ++i) { 28 | std::vector pl = rowLegalizers_[i].getPlacement(); 29 | assert(pl.size() == rowToCells_[i].size()); 30 | for (size_t j = 0; j < pl.size(); ++j) { 31 | int cell = rowToCells_[i][j]; 32 | cellToX_[cell] = pl[j]; 33 | cellToY_[cell] = rows_[i].minY; 34 | cellToOrientation_[cell] = getOrientation(cell, i); 35 | } 36 | } 37 | check(); 38 | } 39 | 40 | std::pair AbacusLegalizer::evaluatePlacement(int cell, 41 | int row) { 42 | if (rowLegalizers_[row].remainingSpace() < cellWidth_[cell]) { 43 | return std::make_pair(false, 0); 44 | } 45 | if (getOrientation(cell, row) == CellOrientation::INVALID) { 46 | return std::make_pair(false, 0); 47 | } 48 | int dist = rowLegalizers_[row].getCost(cellWidth_[cell], cellTargetX_[cell]); 49 | return std::make_pair(true, dist); 50 | } 51 | 52 | void AbacusLegalizer::placeCell(int cell) { 53 | /** 54 | * Simple algorithm that tries close row first and stops early if no 55 | * improvement can be found 56 | */ 57 | int targetX = cellTargetX_[cell]; 58 | int targetY = cellTargetY_[cell]; 59 | int bestRow = -1; 60 | long long bestDist = std::numeric_limits::max(); 61 | 62 | auto tryPlace = [&](int row) { 63 | if (rows_[row].height() != cellHeight_[cell]) { 64 | // Forbidden to place a row in a different-height cell 65 | return false; 66 | } 67 | long long yDist = cellWidth_[cell] * 68 | norm(0, rows_[row].minY - targetY, LegalizationModel::L1); 69 | if (bestRow != -1 && yDist > bestDist) { 70 | // Not possible to do better since the rows are sorted 71 | return true; 72 | } 73 | // Find the best position for the cell 74 | auto [ok, xDist] = evaluatePlacement(cell, row); 75 | // TODO: extend this to non-L1 cases 76 | long long dist = xDist + yDist; 77 | if (!ok) { 78 | // Not possible to place in this row, but cannot stop yet 79 | return false; 80 | } 81 | if (bestRow == -1 || dist < bestDist) { 82 | bestRow = row; 83 | bestDist = dist; 84 | } 85 | // Cannot stop yet 86 | return false; 87 | }; 88 | 89 | // Try promising candidates first 90 | int initialRow = closestRow(targetY); 91 | for (int row = initialRow; row < nbRows(); ++row) { 92 | bool canStop = tryPlace(row); 93 | if (canStop) { 94 | break; 95 | } 96 | } 97 | for (int row = initialRow - 1; row >= 0; --row) { 98 | bool canStop = tryPlace(row); 99 | if (canStop) { 100 | break; 101 | } 102 | } 103 | 104 | if (bestRow == -1) { 105 | return; 106 | } 107 | rowLegalizers_[bestRow].push(cellWidth_[cell], targetX); 108 | rowToCells_[bestRow].push_back(cell); 109 | cellIsPlaced_[cell] = true; 110 | } 111 | 112 | void AbacusLegalizer::check() const { 113 | LegalizerBase::check(); 114 | for (int i = 0; i < nbRows(); ++i) { 115 | for (int c : rowToCells_[i]) { 116 | if (cellToX_[c] < rows_[i].minX) { 117 | throw std::runtime_error("Cell placed before the row"); 118 | } 119 | if (cellToX_[c] + cellWidth_[c] > rows_[i].maxX) { 120 | throw std::runtime_error("Cell placed after the row"); 121 | } 122 | } 123 | } 124 | for (int i = 0; i < nbRows(); ++i) { 125 | for (size_t j = 0; j + 1 < rowToCells_[i].size(); ++j) { 126 | int c1 = rowToCells_[i][j]; 127 | int c2 = rowToCells_[i][j + 1]; 128 | if (cellToX_[c1] + cellWidth_[c1] > cellToX_[c2]) { 129 | throw std::runtime_error("Cell overlap detected"); 130 | } 131 | } 132 | } 133 | } 134 | 135 | } // namespace coloquinte 136 | -------------------------------------------------------------------------------- /test/test_row_neighbourhood.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE ROW_NEIGHBOURHOOD 2 | 3 | #include 4 | 5 | #include "place_detailed/row_neighbourhood.hpp" 6 | 7 | using namespace coloquinte; 8 | 9 | BOOST_AUTO_TEST_CASE(TestBasic1) { 10 | std::vector rows = {{0, 100, 0, 12}, {10, 90, 12, 24}}; 11 | RowNeighbourhood neigh(rows); 12 | 13 | BOOST_CHECK_EQUAL(neigh.rowsAbove(0).size(), 1); 14 | BOOST_CHECK_EQUAL(neigh.rowsBelow(0).size(), 0); 15 | BOOST_CHECK_EQUAL(neigh.rowsLeft(0).size(), 0); 16 | BOOST_CHECK_EQUAL(neigh.rowsRight(0).size(), 0); 17 | BOOST_CHECK_EQUAL(neigh.rowsAbove(1).size(), 0); 18 | BOOST_CHECK_EQUAL(neigh.rowsBelow(1).size(), 1); 19 | BOOST_CHECK_EQUAL(neigh.rowsLeft(1).size(), 0); 20 | BOOST_CHECK_EQUAL(neigh.rowsRight(1).size(), 0); 21 | 22 | BOOST_CHECK_EQUAL(neigh.rowsAbove(0)[0], 1); 23 | BOOST_CHECK_EQUAL(neigh.rowsBelow(1)[0], 0); 24 | } 25 | 26 | BOOST_AUTO_TEST_CASE(TestBasic2) { 27 | std::vector rows = { 28 | {0, 100, 0, 12}, {10, 90, 12, 24}, {-200, -10, 0, 12}, {-100, 5, 12, 24}}; 29 | RowNeighbourhood neigh(rows, 3); 30 | 31 | BOOST_CHECK_EQUAL(neigh.rowsAbove(0).size(), 2); 32 | BOOST_CHECK_EQUAL(neigh.rowsBelow(0).size(), 0); 33 | BOOST_CHECK_EQUAL(neigh.rowsLeft(0).size(), 1); 34 | BOOST_CHECK_EQUAL(neigh.rowsRight(0).size(), 0); 35 | 36 | BOOST_CHECK_EQUAL(neigh.rowsAbove(1).size(), 0); 37 | BOOST_CHECK_EQUAL(neigh.rowsBelow(1).size(), 1); 38 | BOOST_CHECK_EQUAL(neigh.rowsLeft(1).size(), 2); 39 | BOOST_CHECK_EQUAL(neigh.rowsRight(1).size(), 0); 40 | 41 | BOOST_CHECK_EQUAL(neigh.rowsAbove(2).size(), 1); 42 | BOOST_CHECK_EQUAL(neigh.rowsBelow(2).size(), 0); 43 | BOOST_CHECK_EQUAL(neigh.rowsLeft(2).size(), 0); 44 | BOOST_CHECK_EQUAL(neigh.rowsRight(2).size(), 2); 45 | 46 | BOOST_CHECK_EQUAL(neigh.rowsAbove(3).size(), 0); 47 | BOOST_CHECK_EQUAL(neigh.rowsBelow(3).size(), 2); 48 | BOOST_CHECK_EQUAL(neigh.rowsLeft(3).size(), 0); 49 | BOOST_CHECK_EQUAL(neigh.rowsRight(3).size(), 1); 50 | } 51 | 52 | /** 53 | * @brief Not valid anymore as we don't build all left/right neighbours 54 | */ 55 | void checkNeighbourhoodComplete(const RowNeighbourhood &neigh, int nbRows) { 56 | BOOST_CHECK_EQUAL(neigh.nbRows(), nbRows); 57 | for (int i = 0; i < nbRows; ++i) { 58 | int nb = 0; 59 | nb += neigh.rowsBelow(i).size(); 60 | nb += neigh.rowsAbove(i).size(); 61 | nb += neigh.rowsLeft(i).size(); 62 | nb += neigh.rowsRight(i).size(); 63 | BOOST_CHECK_EQUAL(nb, nbRows - 1); 64 | } 65 | } 66 | 67 | BOOST_AUTO_TEST_CASE(TestAbove) { 68 | std::vector rows = {{0, 100, 0, 12}, 69 | {10, 90, 12, 24}, 70 | {-200, 1, 12, 24}, 71 | {-100, 5, 24, 36}, 72 | {99, 105, 10, 22}}; 73 | int nbRows = rows.size(); 74 | RowNeighbourhood neigh(rows, nbRows); 75 | // checkNeighbourhoodComplete(neigh, nbRows); 76 | BOOST_CHECK_EQUAL(neigh.rowsAbove(0).size(), nbRows - 1); 77 | } 78 | 79 | BOOST_AUTO_TEST_CASE(TestBelow) { 80 | std::vector rows = {{0, 100, 0, 12}, 81 | {10, 90, -12, 0}, 82 | {-200, 1, -12, 0}, 83 | {-100, 5, -36, 0}, 84 | {99, 105, -24, 12}}; 85 | int nbRows = rows.size(); 86 | RowNeighbourhood neigh(rows, nbRows); 87 | // checkNeighbourhoodComplete(neigh, nbRows); 88 | BOOST_CHECK_EQUAL(neigh.rowsBelow(0).size(), nbRows - 1); 89 | } 90 | 91 | BOOST_AUTO_TEST_CASE(TestLeft) { 92 | std::vector rows = {{0, 100, 0, 12}, 93 | {-10, 0, -12, 0}, 94 | {-100, -10, -12, 0}, 95 | {-90, -1, 0, 12}, 96 | {-20, -5, 24, 36}}; 97 | int nbRows = rows.size(); 98 | RowNeighbourhood neigh(rows, nbRows); 99 | // checkNeighbourhoodComplete(neigh, nbRows); 100 | BOOST_CHECK_EQUAL(neigh.rowsLeft(0).size(), nbRows - 1); 101 | } 102 | 103 | BOOST_AUTO_TEST_CASE(TestRight) { 104 | std::vector rows = {{0, 100, 0, 12}, 105 | {100, 140, 0, 12}, 106 | {130, 300, -12, 0}, 107 | {100, 201, -36, -24}, 108 | {104, 105, 12, 24}}; 109 | int nbRows = rows.size(); 110 | RowNeighbourhood neigh(rows, nbRows); 111 | // checkNeighbourhoodComplete(neigh, nbRows); 112 | BOOST_CHECK_EQUAL(neigh.rowsRight(0).size(), nbRows - 1); 113 | } 114 | -------------------------------------------------------------------------------- /test/test_row_legalizer.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE ROW_LEGALIZER 2 | 3 | #include 4 | 5 | #include "place_detailed/row_legalizer.hpp" 6 | 7 | using namespace coloquinte; 8 | 9 | BOOST_AUTO_TEST_CASE(TestMiddle) { 10 | RowLegalizer leg(-10, 20); 11 | int c1 = leg.push(8, -5); 12 | int c2 = leg.push(5, 4); 13 | BOOST_CHECK_EQUAL(leg.getPlacement().size(), 2); 14 | BOOST_CHECK_EQUAL(leg.getPlacement()[0], -5); 15 | BOOST_CHECK_EQUAL(leg.getPlacement()[1], 4); 16 | BOOST_CHECK_EQUAL(c1, 0); 17 | BOOST_CHECK_EQUAL(c2, 0); 18 | } 19 | 20 | BOOST_AUTO_TEST_CASE(TestConstrainLeft) { 21 | RowLegalizer leg(0, 20); 22 | int c1 = leg.push(8, -5); 23 | BOOST_CHECK_EQUAL(leg.getPlacement().size(), 1); 24 | BOOST_CHECK_EQUAL(leg.getPlacement()[0], 0); 25 | BOOST_CHECK_EQUAL(c1, 40); 26 | } 27 | 28 | BOOST_AUTO_TEST_CASE(TestConstrainRight) { 29 | RowLegalizer leg(0, 20); 30 | int c1 = leg.push(8, 15); 31 | BOOST_CHECK_EQUAL(leg.getPlacement().size(), 1); 32 | BOOST_CHECK_EQUAL(leg.getPlacement()[0], 12); 33 | BOOST_CHECK_EQUAL(c1, 24); 34 | } 35 | 36 | BOOST_AUTO_TEST_CASE(TestEquilibrium1) { 37 | RowLegalizer leg(-10, 20); 38 | int c1 = leg.push(8, 4); 39 | int c2 = leg.push(7, 4); 40 | BOOST_CHECK_EQUAL(leg.getPlacement().size(), 2); 41 | BOOST_CHECK_EQUAL(leg.getPlacement()[0], 4); 42 | BOOST_CHECK_EQUAL(leg.getPlacement()[1], 12); 43 | BOOST_CHECK_EQUAL(c1, 0); 44 | BOOST_CHECK_EQUAL(c2, 56); 45 | } 46 | 47 | BOOST_AUTO_TEST_CASE(TestEquilibrium2) { 48 | RowLegalizer leg(-10, 20); 49 | int c1 = leg.push(7, 4); 50 | int c2 = leg.push(8, 4); 51 | BOOST_CHECK_EQUAL(leg.getPlacement().size(), 2); 52 | BOOST_CHECK_EQUAL(leg.getPlacement()[0], -3); 53 | BOOST_CHECK_EQUAL(leg.getPlacement()[1], 4); 54 | BOOST_CHECK_EQUAL(c1, 0); 55 | BOOST_CHECK_EQUAL(c2, 49); 56 | } 57 | 58 | BOOST_AUTO_TEST_CASE(TestBothSides) { 59 | RowLegalizer leg(-10, 20); 60 | int c1 = leg.push(7, -20); 61 | int c2 = leg.push(8, 30); 62 | BOOST_CHECK_EQUAL(leg.getPlacement().size(), 2); 63 | BOOST_CHECK_EQUAL(leg.getPlacement()[0], -10); 64 | BOOST_CHECK_EQUAL(leg.getPlacement()[1], 12); 65 | BOOST_CHECK_EQUAL(c1, 70); 66 | BOOST_CHECK_EQUAL(c2, 144); 67 | } 68 | 69 | BOOST_AUTO_TEST_CASE(TestWinLeft) { 70 | RowLegalizer leg(-10, 20); 71 | int c1 = leg.push(3, 50); 72 | int c2 = leg.push(8, -5); 73 | BOOST_CHECK_EQUAL(leg.getPlacement().size(), 2); 74 | BOOST_CHECK_EQUAL(leg.getPlacement()[0], -8); 75 | BOOST_CHECK_EQUAL(leg.getPlacement()[1], -5); 76 | BOOST_CHECK_EQUAL(c1, 99); 77 | BOOST_CHECK_EQUAL(c2, 75); 78 | } 79 | 80 | BOOST_AUTO_TEST_CASE(TestWinRight) { 81 | RowLegalizer leg(-10, 20); 82 | int c1 = leg.push(8, 5); 83 | int c2 = leg.push(3, -50); 84 | BOOST_CHECK_EQUAL(leg.getPlacement().size(), 2); 85 | BOOST_CHECK_EQUAL(leg.getPlacement()[0], 5); 86 | BOOST_CHECK_EQUAL(leg.getPlacement()[1], 13); 87 | BOOST_CHECK_EQUAL(c1, 0); 88 | BOOST_CHECK_EQUAL(c2, 189); 89 | } 90 | 91 | BOOST_AUTO_TEST_CASE(TestKeepAll) { 92 | RowLegalizer leg(0, 100); 93 | for (int i = 0; i < 20; ++i) { 94 | int c = leg.push(1, 2 * i); 95 | BOOST_CHECK_EQUAL(c, 0); 96 | } 97 | auto pl = leg.getPlacement(); 98 | BOOST_CHECK_EQUAL(pl.size(), 20); 99 | for (int i = 0; i < 20; ++i) { 100 | BOOST_CHECK_EQUAL(pl[i], 2 * i); 101 | } 102 | } 103 | 104 | BOOST_AUTO_TEST_CASE(TestEmpty) { 105 | RowLegalizer leg(-10, 20); 106 | BOOST_CHECK(leg.getPlacement().empty()); 107 | } 108 | 109 | int computeCost(const std::vector& widths, const std::vector& targets, 110 | const std::vector& positions) { 111 | assert(widths.size() == targets.size()); 112 | assert(widths.size() == positions.size()); 113 | int ret = 0; 114 | for (size_t i = 0; i < widths.size(); ++i) { 115 | ret += widths[i] * std::abs(targets[i] - positions[i]); 116 | } 117 | return ret; 118 | } 119 | 120 | void fuzzRowLegalizer(int begin, int end, const std::vector& widths, 121 | const std::vector& targets) { 122 | assert(widths.size() == targets.size()); 123 | 124 | RowLegalizer leg(begin, end); 125 | int cumCost = 0; 126 | for (size_t i = 0; i < widths.size(); ++i) { 127 | int predictedCost = leg.getCost(widths[i], targets[i]); 128 | int cost = leg.push(widths[i], targets[i]); 129 | BOOST_CHECK_EQUAL(predictedCost, cost); 130 | cumCost += cost; 131 | std::vector curWidths(widths.begin(), widths.begin() + i + 1); 132 | std::vector curTargets(targets.begin(), targets.begin() + i + 1); 133 | BOOST_CHECK_EQUAL(cumCost, 134 | computeCost(curWidths, curTargets, leg.getPlacement())); 135 | leg.check(); 136 | } 137 | } 138 | 139 | BOOST_AUTO_TEST_CASE(TestComplex1) { 140 | std::vector widths = {5, 5, 8, 7, 6, 3}; 141 | std::vector targets = {20, 10, 45, 67, 32, 49}; 142 | fuzzRowLegalizer(0, 100, widths, targets); 143 | } 144 | 145 | BOOST_AUTO_TEST_CASE(TestComplex2) { 146 | std::vector widths = {5, 5, 8, 7, 6, 3, 23, 4, 3, 6, 7, 3}; 147 | std::vector targets = {20, -10, -45, 67, 32, 49, 148 | 20, 80, 36, 120, 110, 90}; 149 | fuzzRowLegalizer(0, 100, widths, targets); 150 | } -------------------------------------------------------------------------------- /src/place_detailed/tetris_legalizer.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "place_detailed/tetris_legalizer.hpp" 3 | 4 | #include 5 | #include 6 | 7 | namespace coloquinte { 8 | TetrisLegalizer::TetrisLegalizer( 9 | const std::vector &rows, const std::vector &width, 10 | const std::vector &height, 11 | const std::vector &polarities, 12 | const std::vector &targetX, const std::vector &targetY, 13 | const std::vector &targetOrientation) 14 | : LegalizerBase(rows, width, height, polarities, targetX, targetY, 15 | targetOrientation) { 16 | for (Row r : rows_) { 17 | rowFreePos_.push_back(r.minX); 18 | } 19 | } 20 | 21 | void TetrisLegalizer::run() { 22 | for (int c = 0; c < nbCells(); ++c) { 23 | placeCell(c); 24 | } 25 | } 26 | 27 | std::pair TetrisLegalizer::attemptPlacement(int cell, int y) const { 28 | CellOrientation orient = getOrientation(cell, closestRow(y)); 29 | if (orient == CellOrientation::INVALID) { 30 | // Incompatible due to row orientation 31 | return std::make_pair(false, 0); 32 | } 33 | // Need to handle non-classical orientation 34 | int width = cellWidth_[cell]; 35 | int height = cellHeight_[cell]; 36 | if (isTurn(orient)) { 37 | std::swap(width, height); 38 | } 39 | auto p = getPossibleIntervals(width, height, y); 40 | if (p.empty()) { 41 | // Incompatible due to obstructions or placed cells 42 | return std::make_pair(false, 0); 43 | } 44 | // Find the closest available interval 45 | int x = cellTargetX_[cell]; 46 | int dest = 0; 47 | bool found = false; 48 | for (auto [b, e] : p) { 49 | int pos = std::clamp(x, b, e); 50 | if (!found || std::abs(pos - x) < std::abs(dest - x)) { 51 | dest = pos; 52 | found = true; 53 | } 54 | } 55 | return std::make_pair(true, dest); 56 | } 57 | 58 | void TetrisLegalizer::placeCell(int cell) { 59 | int targetX = cellTargetX_[cell]; 60 | int targetY = cellTargetY_[cell]; 61 | int bestX = 0; 62 | int bestY = 0; 63 | int bestDist = std::numeric_limits::max(); 64 | bool found = false; 65 | 66 | auto tryPlace = [&](int y) -> bool { 67 | if (found && std::abs(targetY - y) >= bestDist) { 68 | // No need to continue searching as we cannot find better solution 69 | return true; 70 | } 71 | auto [ok, x] = attemptPlacement(cell, y); 72 | if (!ok) { 73 | // Not possible to place in this row 74 | return false; 75 | } 76 | int dist = std::abs(targetX - x) + std::abs(targetY - y); 77 | if (!found || dist < bestDist) { 78 | found = true; 79 | bestDist = dist; 80 | bestX = x; 81 | bestY = y; 82 | } 83 | return false; 84 | }; 85 | 86 | // Try promising candidates first 87 | int initialRow = closestRow(targetY); 88 | for (int row = initialRow; row < nbRows(); ++row) { 89 | bool canStop = tryPlace(rows_[row].minY); 90 | if (canStop) { 91 | break; 92 | } 93 | } 94 | for (int row = initialRow - 1; row >= 0; --row) { 95 | bool canStop = tryPlace(rows_[row].minY); 96 | if (canStop) { 97 | break; 98 | } 99 | } 100 | 101 | if (!found) { 102 | return; 103 | } 104 | cellToX_[cell] = bestX; 105 | cellToY_[cell] = bestY; 106 | cellToOrientation_[cell] = getOrientation(cell, closestRow(bestY)); 107 | cellIsPlaced_[cell] = true; 108 | // Need to handle non-classical orientation 109 | int width = cellWidth_[cell]; 110 | int height = cellHeight_[cell]; 111 | if (isTurn(cellToOrientation_[cell])) { 112 | std::swap(width, height); 113 | } 114 | instanciateCell(bestX, bestY, width, height); 115 | } 116 | 117 | void TetrisLegalizer::instanciateCell(int x, int y, int w, int h) { 118 | if (h <= 0 || w <= 0) { 119 | return; 120 | } 121 | int rowInd = closestRow(y); 122 | for (int r = rowInd; r < nbRows(); ++r) { 123 | if (rows_[r].minY != y) { 124 | break; 125 | } 126 | if (x < rows_[r].maxX && x + w > rows_[r].minX) { 127 | rowFreePos_[r] = x + w; 128 | } 129 | } 130 | if (h <= rowHeight()) { 131 | return; 132 | } 133 | instanciateCell(x, y + rowHeight(), w, h - rowHeight()); 134 | } 135 | 136 | std::vector > TetrisLegalizer::getPossibleIntervals( 137 | int w, int h, int y) const { 138 | int rowInd = closestRow(y); 139 | std::vector > intervals; 140 | for (int r = rowInd; r < nbRows(); ++r) { 141 | if (rows_[r].minY != y) { 142 | break; 143 | } 144 | int b = rowFreePos_[r]; 145 | int e = rows_[r].maxX - w; 146 | if (e >= b) { 147 | intervals.emplace_back(b, e); 148 | } 149 | } 150 | if (h <= rowHeight() || intervals.empty()) { 151 | return intervals; 152 | } 153 | std::vector > other = 154 | getPossibleIntervals(w, h - rowHeight(), y + rowHeight()); 155 | 156 | std::vector > ret; 157 | for (auto [b1, e1] : intervals) { 158 | for (auto [b2, e2] : other) { 159 | if (b1 <= e2 && b2 <= e1) { 160 | ret.emplace_back(std::max(b1, b2), std::min(e1, e2)); 161 | } 162 | } 163 | } 164 | return ret; 165 | } 166 | } // namespace coloquinte 167 | -------------------------------------------------------------------------------- /test/test_detailed_placement.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE DETAILED_PLACEMENT 2 | 3 | #include 4 | 5 | #include "place_detailed/detailed_placement.hpp" 6 | 7 | using namespace coloquinte; 8 | 9 | std::vector toRows(const std::vector &recs) { 10 | std::vector rows; 11 | for (Rectangle r : recs) { 12 | rows.emplace_back(r, CellOrientation::N); 13 | } 14 | return rows; 15 | } 16 | 17 | BOOST_AUTO_TEST_CASE(TestConstruction) { 18 | // 3 cells in two rows: 19 | // Row 0, x from 2 to 12, y 10, with cell 1 from 2 to 8 and cell 0 from 8 to 20 | // 12 Row 1, x from 2 to 12, y 20, with cell 2 from 2 to 7 21 | std::vector widths = {4, 6, 5}; 22 | std::vector cellX = {8, 2, 2}; 23 | std::vector cellY = {10, 10, 20}; 24 | std::vector rows = {{2, 12, 10, 20}, {2, 12, 20, 30}}; 25 | DetailedPlacement pl = 26 | DetailedPlacement::fromPos(toRows(rows), widths, cellX, cellY); 27 | pl.check(); 28 | } 29 | 30 | BOOST_AUTO_TEST_CASE(TestBeforeRow) { 31 | std::vector widths = {2}; 32 | std::vector cellX = {5}; 33 | std::vector cellY = {10}; 34 | std::vector rows = {{6, 12, 10, 20}}; 35 | BOOST_CHECK_THROW( 36 | DetailedPlacement::fromPos(toRows(rows), widths, cellX, cellY), 37 | std::runtime_error); 38 | } 39 | 40 | BOOST_AUTO_TEST_CASE(TestAfterRow) { 41 | std::vector widths = {2}; 42 | std::vector cellX = {5}; 43 | std::vector cellY = {10}; 44 | std::vector rows = {{-10, 6, 10, 20}}; 45 | BOOST_CHECK_THROW( 46 | DetailedPlacement::fromPos(toRows(rows), widths, cellX, cellY), 47 | std::runtime_error); 48 | } 49 | 50 | BOOST_AUTO_TEST_CASE(TestBadY) { 51 | std::vector widths = {2}; 52 | std::vector cellX = {5}; 53 | std::vector cellY = {11}; 54 | std::vector rows = {{-20, 20, 10, 20}}; 55 | BOOST_CHECK_THROW( 56 | DetailedPlacement::fromPos(toRows(rows), widths, cellX, cellY), 57 | std::runtime_error); 58 | } 59 | 60 | BOOST_AUTO_TEST_CASE(TestOverlap) { 61 | std::vector widths = {2, 2}; 62 | std::vector cellX = {5, 6}; 63 | std::vector cellY = {10, 10}; 64 | std::vector rows = {{-20, 20, 10, 20}}; 65 | BOOST_CHECK_THROW( 66 | DetailedPlacement::fromPos(toRows(rows), widths, cellX, cellY), 67 | std::runtime_error); 68 | } 69 | 70 | BOOST_AUTO_TEST_CASE(TestChanges) { 71 | std::vector widths = {8, 8, 8}; 72 | std::vector cellX = {0, 8, 16}; 73 | std::vector cellY = {10, 10, 10}; 74 | std::vector rows = {{0, 32, 10, 20}, {0, 32, 20, 30}}; 75 | DetailedPlacement pl = 76 | DetailedPlacement::fromPos(toRows(rows), widths, cellX, cellY); 77 | pl.check(); 78 | pl.unplace(0); 79 | pl.check(); 80 | pl.place(0, 0, 2, 24); 81 | pl.check(); 82 | pl.unplace(0); 83 | pl.check(); 84 | pl.unplace(2); 85 | pl.check(); 86 | pl.place(2, 1, -1, 12); 87 | pl.check(); 88 | pl.place(0, 1, 2, 22); 89 | pl.check(); 90 | } 91 | 92 | BOOST_AUTO_TEST_CASE(TestSwap) { 93 | std::vector widths = {4, 5, 6, 7, 8}; 94 | std::vector cellX = {0, 8, 16, 4, 14}; 95 | std::vector cellY = {10, 10, 10, 20, 20}; 96 | std::vector rows = {{0, 32, 10, 20}, {0, 32, 20, 30}}; 97 | DetailedPlacement pl = 98 | DetailedPlacement::fromPos(toRows(rows), widths, cellX, cellY); 99 | pl.check(); 100 | BOOST_CHECK(pl.canSwap(0, 1)); 101 | BOOST_CHECK(pl.canSwap(1, 2)); 102 | BOOST_CHECK(pl.canSwap(3, 4)); 103 | BOOST_CHECK(pl.canSwap(0, 2)); 104 | BOOST_CHECK(pl.canSwap(0, 3)); 105 | BOOST_CHECK(pl.canSwap(0, 4)); 106 | pl.swap(0, 1); 107 | pl.check(); 108 | pl.swap(3, 4); 109 | pl.check(); 110 | pl.swap(2, 4); 111 | pl.check(); 112 | } 113 | 114 | BOOST_AUTO_TEST_CASE(TestInsert) { 115 | std::vector widths = {4, 4, 4, 4, 4}; 116 | std::vector cellX = {0, 8, 16, 4, 8}; 117 | std::vector cellY = {10, 10, 10, 20, 20}; 118 | std::vector rows = {{0, 32, 10, 20}, {0, 32, 20, 30}}; 119 | DetailedPlacement pl = 120 | DetailedPlacement::fromPos(toRows(rows), widths, cellX, cellY); 121 | pl.check(); 122 | BOOST_CHECK(pl.canInsert(0, 0, 1)); 123 | BOOST_CHECK(pl.canInsert(0, 0, 2)); 124 | BOOST_CHECK(pl.canInsert(0, 1, -1)); 125 | BOOST_CHECK(!pl.canInsert(0, 1, 3)); 126 | pl.insert(0, 0, 2); 127 | pl.check(); 128 | pl.insert(0, 1, -1); 129 | pl.check(); 130 | pl.insert(0, 0, 1); 131 | pl.check(); 132 | pl.insert(0, 0, -1); 133 | pl.check(); 134 | } 135 | 136 | BOOST_AUTO_TEST_CASE(TestNoSwap) { 137 | std::vector widths = {4, 5, 6, 7, 8}; 138 | std::vector cellX = {0, 4, 9, 0, 7}; 139 | std::vector cellY = {10, 10, 10, 20, 20}; 140 | std::vector rows = {{0, 15, 10, 20}, {0, 15, 20, 30}}; 141 | DetailedPlacement pl = 142 | DetailedPlacement::fromPos(toRows(rows), widths, cellX, cellY); 143 | pl.check(); 144 | BOOST_CHECK(pl.canSwap(0, 1)); 145 | BOOST_CHECK(!pl.canSwap(0, 2)); 146 | BOOST_CHECK(!pl.canSwap(0, 3)); 147 | BOOST_CHECK(!pl.canSwap(0, 4)); 148 | BOOST_CHECK(pl.canSwap(1, 2)); 149 | BOOST_CHECK(!pl.canSwap(1, 3)); 150 | BOOST_CHECK(!pl.canSwap(1, 4)); 151 | BOOST_CHECK(!pl.canSwap(2, 3)); 152 | BOOST_CHECK(pl.canSwap(3, 4)); 153 | } 154 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: false 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Left 10 | AlignOperands: true 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: Never 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: All 18 | AllowShortLambdasOnASingleLine: All 19 | AllowShortIfStatementsOnASingleLine: WithoutElse 20 | AllowShortLoopsOnASingleLine: true 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: true 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BinPackArguments: true 26 | BinPackParameters: true 27 | BraceWrapping: 28 | AfterCaseLabel: false 29 | AfterClass: false 30 | AfterControlStatement: false 31 | AfterEnum: false 32 | AfterFunction: false 33 | AfterNamespace: false 34 | AfterObjCDeclaration: false 35 | AfterStruct: false 36 | AfterUnion: false 37 | AfterExternBlock: false 38 | BeforeCatch: false 39 | BeforeElse: false 40 | IndentBraces: false 41 | SplitEmptyFunction: true 42 | SplitEmptyRecord: true 43 | SplitEmptyNamespace: true 44 | BreakBeforeBinaryOperators: None 45 | BreakBeforeBraces: Attach 46 | BreakBeforeInheritanceComma: false 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializersBeforeComma: false 50 | BreakConstructorInitializers: BeforeColon 51 | BreakAfterJavaFieldAnnotations: false 52 | BreakStringLiterals: true 53 | ColumnLimit: 80 54 | CommentPragmas: '^ IWYU pragma:' 55 | CompactNamespaces: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 57 | ConstructorInitializerIndentWidth: 4 58 | ContinuationIndentWidth: 4 59 | Cpp11BracedListStyle: true 60 | DeriveLineEnding: true 61 | DerivePointerAlignment: true 62 | DisableFormat: false 63 | ExperimentalAutoDetectBinPacking: false 64 | FixNamespaceComments: true 65 | ForEachMacros: 66 | - foreach 67 | - Q_FOREACH 68 | - BOOST_FOREACH 69 | IncludeBlocks: Regroup 70 | IncludeCategories: 71 | - Regex: '^' 72 | Priority: 2 73 | SortPriority: 0 74 | - Regex: '^<.*\.h>' 75 | Priority: 1 76 | SortPriority: 0 77 | - Regex: '^<.*' 78 | Priority: 2 79 | SortPriority: 0 80 | - Regex: '.*' 81 | Priority: 3 82 | SortPriority: 0 83 | IncludeIsMainRegex: '([-_](test|unittest))?$' 84 | IncludeIsMainSourceRegex: '' 85 | IndentCaseLabels: true 86 | IndentGotoLabels: true 87 | IndentPPDirectives: None 88 | IndentWidth: 2 89 | IndentWrappedFunctionNames: false 90 | JavaScriptQuotes: Leave 91 | JavaScriptWrapImports: true 92 | KeepEmptyLinesAtTheStartOfBlocks: false 93 | MacroBlockBegin: '' 94 | MacroBlockEnd: '' 95 | MaxEmptyLinesToKeep: 1 96 | NamespaceIndentation: None 97 | ObjCBinPackProtocolList: Never 98 | ObjCBlockIndentWidth: 2 99 | ObjCSpaceAfterProperty: false 100 | ObjCSpaceBeforeProtocolList: true 101 | PenaltyBreakAssignment: 2 102 | PenaltyBreakBeforeFirstCallParameter: 1 103 | PenaltyBreakComment: 300 104 | PenaltyBreakFirstLessLess: 120 105 | PenaltyBreakString: 1000 106 | PenaltyBreakTemplateDeclaration: 10 107 | PenaltyExcessCharacter: 1000000 108 | PenaltyReturnTypeOnItsOwnLine: 200 109 | PointerAlignment: Left 110 | RawStringFormats: 111 | - Language: Cpp 112 | Delimiters: 113 | - cc 114 | - CC 115 | - cpp 116 | - Cpp 117 | - CPP 118 | - 'c++' 119 | - 'C++' 120 | CanonicalDelimiter: '' 121 | BasedOnStyle: google 122 | - Language: TextProto 123 | Delimiters: 124 | - pb 125 | - PB 126 | - proto 127 | - PROTO 128 | EnclosingFunctions: 129 | - EqualsProto 130 | - EquivToProto 131 | - PARSE_PARTIAL_TEXT_PROTO 132 | - PARSE_TEST_PROTO 133 | - PARSE_TEXT_PROTO 134 | - ParseTextOrDie 135 | - ParseTextProtoOrDie 136 | CanonicalDelimiter: '' 137 | BasedOnStyle: google 138 | ReflowComments: true 139 | SortIncludes: true 140 | SortUsingDeclarations: true 141 | SpaceAfterCStyleCast: false 142 | SpaceAfterLogicalNot: false 143 | SpaceAfterTemplateKeyword: true 144 | SpaceBeforeAssignmentOperators: true 145 | SpaceBeforeCpp11BracedList: false 146 | SpaceBeforeCtorInitializerColon: true 147 | SpaceBeforeInheritanceColon: true 148 | SpaceBeforeParens: ControlStatements 149 | SpaceBeforeRangeBasedForLoopColon: true 150 | SpaceInEmptyBlock: false 151 | SpaceInEmptyParentheses: false 152 | SpacesBeforeTrailingComments: 2 153 | SpacesInAngles: false 154 | SpacesInConditionalStatement: false 155 | SpacesInContainerLiterals: true 156 | SpacesInCStyleCastParentheses: false 157 | SpacesInParentheses: false 158 | SpacesInSquareBrackets: false 159 | SpaceBeforeSquareBrackets: false 160 | Standard: Auto 161 | StatementMacros: 162 | - Q_UNUSED 163 | - QT_REQUIRE_VERSION 164 | TabWidth: 8 165 | UseCRLF: false 166 | UseTab: Never 167 | ... 168 | 169 | -------------------------------------------------------------------------------- /src/place_global/transportation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace coloquinte { 7 | 8 | using DemandType = long long; 9 | using CostType = int; 10 | 11 | /** 12 | * @brief A transportation solver for problems with few sinks and many sources 13 | */ 14 | class TransportationProblem { 15 | public: 16 | /** 17 | * @brief Initialize the datastructure 18 | */ 19 | TransportationProblem(const std::vector& capacities, 20 | const std::vector& demands, 21 | const std::vector >& costs); 22 | 23 | TransportationProblem(const std::vector& capacities, 24 | const std::vector& demands, 25 | const std::vector >& costs); 26 | 27 | /** 28 | * @brief Number of sources in the problem 29 | */ 30 | int nbSources() const { return demands_.size(); } 31 | 32 | /** 33 | * @brief Number of sinks in the problem 34 | */ 35 | int nbSinks() const { return capacities_.size(); } 36 | 37 | /** 38 | * @brief Compute the total demand from the sources 39 | */ 40 | DemandType totalDemand() const; 41 | 42 | /** 43 | * @brief Compute the total capacity at the sinks 44 | */ 45 | DemandType totalCapacity() const; 46 | 47 | /** 48 | * @brief Demand of all sources 49 | */ 50 | const std::vector& demands() const { return demands_; } 51 | 52 | /** 53 | * @brief Capacities for all sinks 54 | */ 55 | const std::vector& capacities() const { return capacities_; } 56 | 57 | /** 58 | * @brief Costs between sinks and sources 59 | */ 60 | const std::vector >& costs() const { return costs_; } 61 | 62 | /** 63 | * @brief Allocations between sinks and sources 64 | */ 65 | const std::vector >& allocations() const { 66 | return allocations_; 67 | } 68 | 69 | /** 70 | * @brief Demand for this source 71 | */ 72 | DemandType demand(int src) const { return demands_[src]; } 73 | 74 | /** 75 | * @brief Capacity for this sink 76 | */ 77 | DemandType capacity(int snk) const { return capacities_[snk]; } 78 | 79 | /** 80 | * @brief Allocation between one sink and source 81 | */ 82 | DemandType allocation(int snk, int src) const { 83 | return allocations_[snk][src]; 84 | } 85 | 86 | /** 87 | * @brief Cost between one sink and source 88 | */ 89 | CostType cost(int snk, int src) const { return costs_[snk][src]; } 90 | 91 | /** 92 | * @brief Cost moving a source between sinks 93 | */ 94 | CostType movingCost(int src, int snk1, int snk2) const; 95 | 96 | /** 97 | * @brief Cost between one sink and source, with scaling removed 98 | */ 99 | double originalCost(int snk, int src) const { 100 | return costs_[snk][src] / conversionFactor_; 101 | } 102 | 103 | /** 104 | * @brief Demand allocated in the current solution for this source 105 | */ 106 | DemandType allocatedDemand(int src) const; 107 | 108 | /** 109 | * @brief Capacity allocated in the current solution for this sink 110 | */ 111 | DemandType allocatedCapacity(int snk) const; 112 | 113 | /** 114 | * @brief Cost of the current allocation 115 | */ 116 | double allocationCost() const; 117 | 118 | /** 119 | * @brief Add a dummy source with 0 cost 120 | */ 121 | void addSource(DemandType demand); 122 | 123 | /** 124 | * @brief Add a dummy sink with 0 cost 125 | */ 126 | void addSink(DemandType capa); 127 | 128 | /** 129 | * @brief Return true if the problem is balanced 130 | */ 131 | bool isBalanced() const { return totalDemand() == totalCapacity(); } 132 | 133 | /** 134 | * @brief Return true if the allocation is feasible 135 | */ 136 | bool isFeasible() const; 137 | 138 | /** 139 | * @brief Ensure that the capacity is greater or equal than the demand 140 | */ 141 | void increaseCapacity(); 142 | 143 | /** 144 | * @brief Ensure that the demand is greater or equal than the capacity 145 | */ 146 | void increaseDemand(); 147 | 148 | /** 149 | * @brief Ensure that the capacity is greater or equal than the demand with an 150 | * additional sink 151 | */ 152 | void addDummyCapacity(); 153 | 154 | /** 155 | * @brief Ensure that the demand is greater or equal than the capacity with an 156 | * additional source 157 | */ 158 | void addDummyDemand(); 159 | 160 | /** 161 | * @brief Ensure a feasible solution (demands + capacities met) 162 | */ 163 | void makeFeasible(); 164 | 165 | /** 166 | * @brief Initialize with an initial solution 167 | */ 168 | void setAllocations(const std::vector >& allocations); 169 | 170 | /** 171 | * @brief Initialize with an initial solution from a target assignment 172 | */ 173 | void setAssignment(const std::vector& assignment); 174 | 175 | /** 176 | * @brief Obtain an assignment corresponding to the allocation (one bin index 177 | * for each source) 178 | */ 179 | std::vector toAssignment() const; 180 | 181 | /** 182 | * @brief Reset the allocation to all-zeros 183 | */ 184 | void resetAllocations(); 185 | 186 | /** 187 | * @brief Solve the transportation problem 188 | */ 189 | void solve(); 190 | 191 | /** 192 | * @brief Check the datastructure 193 | */ 194 | void check() const; 195 | 196 | private: 197 | /** 198 | * @brief Convert floating-point costs to a fixed-point equivalent 199 | */ 200 | void costsFromIntegers(const std::vector >& costs); 201 | 202 | private: 203 | // Problem data 204 | std::vector demands_; 205 | std::vector capacities_; 206 | std::vector > costs_; 207 | 208 | // Current solution 209 | std::vector > allocations_; 210 | 211 | // Optional conversion factor between internal fixed-point costs and external 212 | double conversionFactor_; 213 | 214 | friend class TransportationCycleCanceling; 215 | friend class TransportationSuccessiveShortestPath; 216 | }; 217 | 218 | } // namespace coloquinte -------------------------------------------------------------------------------- /src/place_detailed/incr_net_model.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "coloquinte.hpp" 7 | 8 | namespace coloquinte { 9 | 10 | class IncrNetModel; 11 | 12 | /** 13 | * @brief Builder to construct IncrNetModel objects 14 | * 15 | */ 16 | class IncrNetModelBuilder { 17 | public: 18 | /** 19 | * @brief Initialize the datastructure 20 | */ 21 | explicit IncrNetModelBuilder(int nbCells); 22 | 23 | /** 24 | * @brief Return the number of cells 25 | */ 26 | int nbCells() const { return nbCells_; } 27 | 28 | /** 29 | * @brief Add a new net 30 | */ 31 | void addNet(const std::vector &cells, 32 | const std::vector &pinOffsets); 33 | 34 | /** 35 | * @brief Finalize the object 36 | */ 37 | IncrNetModel build() const; 38 | 39 | /** 40 | * @brief Finalize the object with initial positions 41 | */ 42 | IncrNetModel build(const std::vector &pos) const; 43 | 44 | private: 45 | int nbCells_; 46 | std::vector netLimits_; 47 | std::vector netCells_; 48 | std::vector netPinOffsets_; 49 | }; 50 | 51 | /** 52 | * Incremental computation of the 1D HPWL for detailed placement algorithms 53 | */ 54 | class IncrNetModel { 55 | public: 56 | /** 57 | * @brief Create the horizontal view of the HPWL model 58 | */ 59 | static IncrNetModel xTopology(const Circuit &); 60 | 61 | /** 62 | * @brief Create the vertical view of the HPWL model 63 | */ 64 | static IncrNetModel yTopology(const Circuit &); 65 | 66 | /** 67 | * @brief Create the horizontal view of the HPWL model 68 | * 69 | * @param cells Subset of the cells to extract 70 | */ 71 | static IncrNetModel xTopology(const Circuit &, const std::vector &cells); 72 | 73 | /** 74 | * @brief Create the vertical view of the HPWL model 75 | * 76 | * @param cells Subset of the cells to extract 77 | */ 78 | static IncrNetModel yTopology(const Circuit &, const std::vector &cells); 79 | 80 | /** 81 | * @brief Export the horizontal placement to the ISPD circuit 82 | */ 83 | void exportPlacementX(Circuit &circuit) const; 84 | 85 | /** 86 | * @brief Export the vertical placement to the ISPD circuit 87 | */ 88 | void exportPlacementY(Circuit &circuit) const; 89 | 90 | /** 91 | * @brief Return the number of cells 92 | */ 93 | int nbCells() const { return cellPos_.size(); } 94 | 95 | /** 96 | * @brief Return the number of nets 97 | */ 98 | int nbNets() const { return netLimits_.size() - 1; } 99 | 100 | /** 101 | * @brief Return the total number of pins 102 | */ 103 | int nbPins() const { return netLimits_.back(); } 104 | 105 | int cellPos(int cell) const { 106 | assert(cell >= 0); 107 | assert(cell < nbCells()); 108 | return cellPos_[cell]; 109 | } 110 | 111 | /** 112 | * @brief Return the number of pins for a given net 113 | */ 114 | int nbNetPins(int net) const { 115 | assert(net >= 0); 116 | assert(net < nbNets()); 117 | return netLimits_[net + 1] - netLimits_[net]; 118 | } 119 | 120 | /** 121 | * @brief Return the number of pins for a given net 122 | */ 123 | int nbCellPins(int cell) const { 124 | assert(cell >= 0); 125 | assert(cell < nbCells()); 126 | return cellLimits_[cell + 1] - cellLimits_[cell]; 127 | } 128 | 129 | /** 130 | * @brief Return the cell for a given net pin 131 | */ 132 | int pinCell(int net, int pin) const { 133 | assert(pin >= 0); 134 | assert(pin < nbNetPins(net)); 135 | return netCells_[netLimits_[net] + pin]; 136 | } 137 | 138 | /** 139 | * @brief Return the offset for a given net pin 140 | */ 141 | int netPinOffset(int net, int pin) const { 142 | assert(pin >= 0); 143 | assert(pin < nbNetPins(net)); 144 | return netPinOffsets_[netLimits_[net] + pin]; 145 | } 146 | 147 | /** 148 | * @brief Return the position for a given net pin 149 | */ 150 | float netPinPosition(int net, int pin) const { 151 | int c = pinCell(net, pin); 152 | assert(c >= 0); 153 | assert(c < nbCells()); 154 | return cellPos_[c] + netPinOffset(net, pin); 155 | } 156 | 157 | /** 158 | * @brief Return the net for a given cell pin 159 | */ 160 | int pinNet(int cell, int pin) const { 161 | assert(pin >= 0); 162 | assert(pin < nbCellPins(cell)); 163 | return cellNets_[cellLimits_[cell] + pin]; 164 | } 165 | 166 | /** 167 | * @brief Return the offset for a given cell pin 168 | */ 169 | int cellPinOffset(int cell, int pin) const { 170 | assert(pin >= 0); 171 | assert(pin < nbCellPins(cell)); 172 | return cellPinOffsets_[cellLimits_[cell] + pin]; 173 | } 174 | 175 | /** 176 | * @brief Return the position for a given net pin 177 | */ 178 | float cellPinPosition(int cell, int pin) const { 179 | assert(cell >= 0); 180 | assert(cell < nbCells()); 181 | return cellPos_[cell] + cellPinOffset(cell, pin); 182 | } 183 | 184 | /** 185 | * @brief Compute the length for a given placement 186 | */ 187 | long long value() const { return value_; } 188 | 189 | /** 190 | * @brief Update the cell position and change the value accordingly 191 | */ 192 | void updateCellPos(int cell, int pos); 193 | 194 | /** 195 | * @brief Check the consistency of the datastructure 196 | */ 197 | void check() const; 198 | 199 | private: 200 | /** 201 | * @brief Compute the minimum and maximum position of a net from scratch 202 | */ 203 | std::pair computeNetMinMaxPos(int net) const; 204 | 205 | /** 206 | * @brief Compute the minimum and maximum position of each net from scratch 207 | */ 208 | std::vector > computeNetMinMaxPos() const; 209 | 210 | /** 211 | * @brief Compute the total value from scratch 212 | */ 213 | long long computeValue() const; 214 | 215 | /** 216 | * @brief Recompute a single net 217 | */ 218 | void recomputeNet(int net); 219 | 220 | /** 221 | * @brief Initialize the datastructure; private, you should use 222 | * IncrNetModelBuilder 223 | */ 224 | IncrNetModel() {} 225 | 226 | /** 227 | * @brief Finalize the datastructure during build 228 | */ 229 | void finalize(); 230 | 231 | private: 232 | // Positions of the cells 233 | std::vector cellPos_; 234 | 235 | // CSR representation from the nets 236 | std::vector netLimits_; 237 | std::vector netCells_; 238 | std::vector netPinOffsets_; 239 | 240 | // CSR representation from the cells 241 | std::vector cellLimits_; 242 | std::vector cellNets_; 243 | std::vector cellPinOffsets_; 244 | 245 | // Current minimum and maximum for each net 246 | std::vector > netMinMaxPos_; 247 | 248 | // Current value 249 | long long value_; 250 | 251 | friend class IncrNetModelBuilder; 252 | }; 253 | } // namespace coloquinte 254 | -------------------------------------------------------------------------------- /src/place_global/density_legalizer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "coloquinte.hpp" 4 | #include "place_global/density_grid.hpp" 5 | 6 | namespace coloquinte { 7 | /** 8 | * Representation of an almost legalized placement, where density constraints 9 | * are met per bin 10 | */ 11 | class DensityLegalizer : public HierarchicalDensityPlacement { 12 | public: 13 | /** 14 | * @brief Parameters to perform rough legalization 15 | */ 16 | struct Parameters { 17 | // Number of improvement steps at each grid coarsening level 18 | int nbSteps; 19 | LegalizationModel costModel; 20 | int lineReoptSize; 21 | int lineReoptOverlap; 22 | int diagReoptSize; 23 | int diagReoptOverlap; 24 | int squareReoptSize; 25 | int squareReoptOverlap; 26 | double quadraticPenaltyFactor; 27 | double coarseningLimit; 28 | bool unidimensionalTransport; 29 | 30 | Parameters(); 31 | }; 32 | 33 | /** 34 | * @brief Initialize the datastructure from the grid and the cell demands 35 | * (single bin) 36 | */ 37 | DensityLegalizer(DensityGrid grid, std::vector cellDemand, 38 | const Parameters ¶ms = Parameters()); 39 | 40 | /** 41 | * @brief Initialize from an existing hierarchical placement 42 | */ 43 | explicit DensityLegalizer(HierarchicalDensityPlacement pl, 44 | const Parameters ¶ms = Parameters()); 45 | 46 | /** 47 | * @brief Initialize from a circuit 48 | */ 49 | static DensityLegalizer fromIspdCircuit(const Circuit &circuit, 50 | float sizeFactor, float sideMargin); 51 | 52 | /** 53 | * @brief Access the parameters 54 | */ 55 | const Parameters ¶ms() const { return params_; } 56 | 57 | /** 58 | * @brief Set the parameters 59 | */ 60 | void setParams(const Parameters ¶ms) { params_ = params; } 61 | 62 | /** 63 | * @brief Target x position for the cell 64 | */ 65 | float cellTargetX(int c) const { return cellTargetX_[c]; } 66 | 67 | /** 68 | * @brief Target y position for the cell 69 | */ 70 | float cellTargetY(int c) const { return cellTargetY_[c]; } 71 | 72 | /** 73 | * @brief Update the x coordinates to target 74 | */ 75 | void updateCellTargetX(const std::vector &cellTargetX) { 76 | assert((int)cellTargetX.size() == nbCells()); 77 | cellTargetX_ = cellTargetX; 78 | } 79 | /** 80 | * @brief Update the y coordinates to target 81 | */ 82 | void updateCellTargetY(const std::vector &cellTargetY) { 83 | assert((int)cellTargetY.size() == nbCells()); 84 | cellTargetY_ = cellTargetY; 85 | } 86 | 87 | /** 88 | * @brief Return the mean distance 89 | */ 90 | float meanDistance() const; 91 | 92 | /** 93 | * @brief Return the root-mean-square distance 94 | */ 95 | float rmsDistance() const; 96 | 97 | /** 98 | * @brief Return the maximum distance 99 | */ 100 | float maxDistance() const; 101 | 102 | /** 103 | * @brief Run the whole legalization process 104 | */ 105 | void run(); 106 | 107 | /** 108 | * @brief Run the coarsening part of the legalization process 109 | */ 110 | void runCoarsening(); 111 | 112 | /** 113 | * @brief Run the refinement part of the legalization process 114 | */ 115 | void runRefinement(); 116 | 117 | /** 118 | * @brief Do one refinement step 119 | */ 120 | void refine(); 121 | 122 | /** 123 | * @brief Improve the solution at the current refinement level 124 | */ 125 | void improve(); 126 | 127 | /** 128 | * @brief Check the consistency of the datastructure 129 | */ 130 | void check() const; 131 | 132 | /** 133 | * @brief Report on stdout 134 | */ 135 | void report(bool verbose = false) const; 136 | 137 | private: 138 | /** 139 | * @brief Improve neighbouring bin pairs in the x direction 140 | */ 141 | void improveXNeighbours(bool sameParent = true); 142 | 143 | /** 144 | * @brief Improve neighbouring bin pairs in the y direction 145 | */ 146 | void improveYNeighbours(bool sameParent = true); 147 | 148 | /** 149 | * @brief Improve neighbouring bin squares 150 | */ 151 | void improveSquareNeighbours(bool sameParentX = true, 152 | bool sameParentY = true); 153 | 154 | /** 155 | * @brief Improve groups of side-adjacent bins 156 | */ 157 | void improveXY(); 158 | 159 | /** 160 | * @brief Improve all bins in x or y direction at once 161 | */ 162 | void improveUnidimensionalTransport(); 163 | 164 | /** 165 | * @brief Improve all bins in x direction 166 | */ 167 | void improveXTransport(); 168 | 169 | /** 170 | * @brief Improve all bins in x direction 171 | */ 172 | void improveYTransport(); 173 | 174 | /** 175 | * @brief Improve groups of diagonally adjacent bins 176 | */ 177 | void improveDiagonals(); 178 | 179 | /** 180 | * @brief Improve squares of adjacent bins 181 | */ 182 | void improveSquare(); 183 | 184 | /** 185 | * @brief Generic improvement of rectangles applied over the grid 186 | */ 187 | void improveRectangles(int width, int height, int strideX, int strideY, 188 | int startX, int startY); 189 | 190 | /** 191 | * @brief Generic improvement of diagonal rectangles applied over the grid 192 | */ 193 | void improveDiagonalRectangles(int xmySize, int xpySize, int strideX, 194 | int strideY, int startX, int startY); 195 | 196 | /** 197 | * @brief Redo the bisection for two bins 198 | */ 199 | void rebisect(int x1, int y1, int x2, int y2); 200 | 201 | /** 202 | * @brief Improve a single rectangle of the grid 203 | */ 204 | void improveRectangle(int i, int j, int width, int height); 205 | 206 | /** 207 | * @brief Redo the distribution using a transportation algorithm 208 | */ 209 | void reoptimize(const std::vector > &bins); 210 | 211 | // Bisection algorithm helpers 212 | std::vector > computeCellCosts( 213 | float cx1, float cy1, float cx2, float cy2, 214 | const std::vector &cells) const; 215 | int findIdealSplitPos( 216 | const std::vector > &cellCosts) const; 217 | int findConstrainedSplitPos( 218 | const std::vector > &cellCosts, int targetPos, 219 | long long capa1, long long capa2) const; 220 | std::pair, std::vector > doSplit( 221 | const std::vector > &cellCosts, int ind) const; 222 | 223 | /** 224 | * @brief Return the distance given the parameters (cost model + penalty) 225 | */ 226 | float distance(float x, float y) const; 227 | 228 | /** 229 | * @brief Return all distances 230 | */ 231 | std::vector allDistances() const; 232 | 233 | private: 234 | Parameters params_; 235 | 236 | std::vector cellTargetX_; 237 | std::vector cellTargetY_; 238 | }; 239 | } // namespace coloquinte -------------------------------------------------------------------------------- /src/place_detailed/legalizer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "coloquinte.hpp" 4 | #include "place_detailed/row_legalizer.hpp" 5 | 6 | namespace coloquinte { 7 | /** 8 | * @brief Algorithms to obtain a legal placement for standard cells 9 | * 10 | * TODO: handle multi-row cells and macros 11 | * TODO: Handle more cost models with Abacus-like legalization 12 | * TODO: Handle backtracking during legalization for hard cases 13 | */ 14 | class LegalizerBase { 15 | public: 16 | /** 17 | * @brief Initialize the datastructure 18 | * @param rows: Available rows for placement; must all be the right 19 | * height for the cells 20 | * @param width: Width of the cells 21 | * @param height: Height of the cells 22 | * @param polarity: Polarity of the cells with respect to rows 23 | * @param targetX: Target x coordinate for legalization 24 | * @param targetY: Target y coordinate for legalization 25 | */ 26 | LegalizerBase(const std::vector &rows, const std::vector &width, 27 | const std::vector &height, 28 | const std::vector &polarity, 29 | const std::vector &targetX, 30 | const std::vector &targetY, 31 | const std::vector &targetOrientation); 32 | 33 | /** 34 | * @brief Return the number of rows 35 | */ 36 | int nbRows() const { return rows_.size(); } 37 | 38 | /** 39 | * @brief Return the number of cells 40 | */ 41 | int nbCells() const { return cellWidth_.size(); } 42 | 43 | /** 44 | * @brief Return the standard cell rows 45 | */ 46 | const std::vector &rows() const { return rows_; } 47 | 48 | /** 49 | * @brief Return the width of the cells 50 | */ 51 | const std::vector &cellWidth() const { return cellWidth_; } 52 | 53 | /** 54 | * @brief Return the height of the cells 55 | */ 56 | const std::vector &cellHeight() const { return cellHeight_; } 57 | 58 | /** 59 | * @brief Return the polarity of the cells 60 | */ 61 | const std::vector &cellRowPolarity() const { 62 | return cellRowPolarity_; 63 | } 64 | 65 | /** 66 | * @brief Return the target x coordinates for legalization 67 | */ 68 | const std::vector &cellTargetX() const { return cellTargetX_; } 69 | 70 | /** 71 | * @brief Return the target y coordinates for legalization 72 | */ 73 | const std::vector &cellTargetY() const { return cellTargetY_; } 74 | 75 | /** 76 | * @brief Return the mean displacement with the given cost model 77 | */ 78 | float meanDistance(LegalizationModel model) const; 79 | 80 | /** 81 | * @brief Return the root-mean-square displacement with the given cost model 82 | */ 83 | float rmsDistance(LegalizationModel model) const; 84 | 85 | /** 86 | * @brief Return the maximum displacement with the given cost model 87 | */ 88 | float maxDistance(LegalizationModel model) const; 89 | 90 | /** 91 | * @brief Return the sum of the areas of the cells 92 | */ 93 | long long totalCellArea() const; 94 | 95 | /** 96 | * @brief Return the row height 97 | */ 98 | int rowHeight() const; 99 | 100 | /** 101 | * @brief Obtain the orientation of the cell from the row orientation and 102 | * cell polarity 103 | */ 104 | CellOrientation getOrientation(int cell, int row) const; 105 | 106 | /** 107 | * @brief Return the remaining rows (placed cells removed) 108 | */ 109 | std::vector remainingRows() const; 110 | 111 | /** 112 | * @brief Import the result of an auxiliary legalizer 113 | */ 114 | void importLegalization(const LegalizerBase &leg, 115 | const std::vector &cells); 116 | 117 | /** 118 | * @brief Compute the x coordinates after legalization 119 | */ 120 | const std::vector &cellLegalX() const; 121 | 122 | /** 123 | * @brief Compute the y coordinates after legalization 124 | */ 125 | const std::vector &cellLegalY() const; 126 | 127 | /** 128 | * @brief Compute the cell orientation after legalization 129 | */ 130 | const std::vector &cellLegalOrientation() const; 131 | 132 | /** 133 | * @brief Check consistency of the datastructure 134 | */ 135 | void check() const; 136 | 137 | /** 138 | * @brief Check that the legalization is done 139 | */ 140 | void checkAllPlaced() const; 141 | 142 | protected: 143 | /** 144 | * @brief Return all distances with a given cost model 145 | */ 146 | std::vector allDistances(LegalizationModel model) const; 147 | 148 | /** 149 | * @brief Compute the ordering of the cells 150 | * 151 | * @param weightX Weight allocated to the target x coordinate; main ordering 152 | * component, should usually be 1 153 | * @param weightWidth Weight allocated to the width; allows ordering by left 154 | * side (0), center (0.5) or right side (1) 155 | * @param weightY Weight allocated to the target y coordinate; allows diagonal 156 | * ordering, should usually be close to 0 157 | * 158 | * @return An ordering of the cell indices 159 | */ 160 | std::vector computeCellOrder(float weightX, float weightWidth, 161 | float weightY, float weightHeight) const; 162 | 163 | /** 164 | * @brief Returns true if the cell is already placed by the algorithm 165 | */ 166 | bool isPlaced(int cell) const { return cellIsPlaced_[cell]; } 167 | 168 | /** 169 | * @brief Find the row that is closest to the target position 170 | */ 171 | int closestRow(int y) const; 172 | 173 | protected: 174 | // Placement data 175 | std::vector rows_; 176 | std::vector cellWidth_; 177 | std::vector cellHeight_; 178 | std::vector cellRowPolarity_; 179 | std::vector cellTargetX_; 180 | std::vector cellTargetY_; 181 | std::vector cellTargetOrientation_; 182 | 183 | // Placement status 184 | // X position of the cell 185 | std::vector cellToX_; 186 | // Y position of the cell 187 | std::vector cellToY_; 188 | // Y position of the cell 189 | std::vector cellToOrientation_; 190 | // Is the cell placed already 191 | std::vector cellIsPlaced_; 192 | }; 193 | 194 | class Legalizer : public LegalizerBase { 195 | public: 196 | /** 197 | * @brief Initialize the datastructure from a circuit 198 | */ 199 | static Legalizer fromIspdCircuit(const Circuit &circuit); 200 | 201 | /** 202 | * @brief Export the placement obtained to the circuit datastructure 203 | */ 204 | void exportPlacement(Circuit &circuit); 205 | 206 | /** 207 | * @brief Run the algorithm 208 | */ 209 | void run(const ColoquinteParameters ¶meters); 210 | 211 | /** 212 | * @brief Run the Tetris algorithm: allocate the given cells in order 213 | */ 214 | void runTetris(const std::vector &cells); 215 | 216 | /** 217 | * @brief Run the Abacus algorithm: allocate the given cells in order and push 218 | * previous cells to minimize displacement 219 | * 220 | * This requires all cells to have the same height 221 | */ 222 | void runAbacus(const std::vector &cells); 223 | 224 | Legalizer(const std::vector &rows, const std::vector &width, 225 | const std::vector &height, 226 | const std::vector &polarities, 227 | const std::vector &targetX, const std::vector &targetY, 228 | const std::vector &targetOrientation); 229 | }; 230 | 231 | } // namespace coloquinte -------------------------------------------------------------------------------- /src/place_detailed/place_detailed.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "coloquinte.hpp" 4 | #include "place_detailed/detailed_placement.hpp" 5 | #include "place_detailed/incr_net_model.hpp" 6 | 7 | namespace coloquinte { 8 | /** 9 | * @brief Main class for detailed placement 10 | */ 11 | class DetailedPlacer { 12 | public: 13 | /** 14 | * @brief Run detailed placement on the circuit 15 | * 16 | * @param circuit The circuit to be modified 17 | * @param params Placement parameters 18 | */ 19 | static void place(Circuit &circuit, const ColoquinteParameters ¶ms, 20 | const std::optional &callback = {}); 21 | 22 | /** 23 | * @brief Run legalization 24 | * 25 | * @param circuit The circuit to be modified 26 | * @param params Placement parameters 27 | */ 28 | static void legalize(Circuit &circuit, const ColoquinteParameters ¶ms, 29 | const std::optional &callback = {}); 30 | 31 | /** 32 | * @brief Initialize the datastructure 33 | */ 34 | explicit DetailedPlacer(Circuit &circuit, const ColoquinteParameters ¶ms); 35 | 36 | /** 37 | * @brief Run the whole detailed placement algorithm 38 | */ 39 | void run(); 40 | 41 | /** 42 | * @brief Call the callback with the current placement 43 | */ 44 | void callback(); 45 | 46 | /** 47 | * @brief Check the consistency of the datastructure 48 | */ 49 | void check() const; 50 | 51 | /** 52 | * @brief Run a simple optimization using only cell swapping 53 | * 54 | * @param nbRows Number of neighbouring rows to look at for each row 55 | * @param nbNeighbours Number of closest neighbours to consider for each cell 56 | */ 57 | void runSwaps(int nbRows, int nbNeighbours); 58 | 59 | /** 60 | * @brief Run a simple optimization using only cell insertion 61 | * 62 | * @param nbRows Number of neighbouring rows to look at for each row 63 | * @param nbNeighbours Number of closest neighbours to consider for each cell 64 | */ 65 | void runInserts(int nbRows, int nbNeighbours); 66 | 67 | /** 68 | * @brief Run a simple optimization using only cell shifting (no reordering) 69 | * 70 | * @param nbRows Number of neighbouring rows considered 71 | */ 72 | void runShifts(int nbRows, int maxNbCells); 73 | 74 | /** 75 | * @brief Run the cell swapping optimization within a row 76 | * 77 | * @param row Row to look for swaps 78 | * @param nbNeighbours Number of closest neighbours to consider for each cell 79 | */ 80 | void runSwapsOneRow(int row, int nbNeighbours); 81 | 82 | /** 83 | * @brief Run the cell insertion optimization within a row 84 | * 85 | * @param row Row to look for insertions 86 | * @param nbNeighbours Number of closest neighbours to consider for each cell 87 | */ 88 | void runInsertsOneRow(int row, int nbNeighbours); 89 | 90 | /** 91 | * @brief Run the cell swapping optimization on the two rows 92 | * 93 | * @param r1 First row to look for swaps 94 | * @param r2 Second row to look for swaps 95 | * @param nbNeighbours Number of closest neighbours to consider for each cell 96 | */ 97 | void runSwapsTwoRows(int r1, int r2, int nbNeighbours); 98 | 99 | /** 100 | * @brief Run the cell insertion optimization on the two rows 101 | * 102 | * @param r1 Row to look for cells 103 | * @param r2 Row to insert into 104 | * @param nbNeighbours Number of closest neighbours to consider for each cell 105 | */ 106 | void runInsertsTwoRows(int r1, int r2, int nbNeighbours); 107 | 108 | /** 109 | * @brief Run the cell swapping optimization on the two rows 110 | * 111 | * After a swap is found, retry to find other improvements 112 | * 113 | * @param r1 First row to look for swaps 114 | * @param r2 Second row to look for swaps 115 | * @param nbNeighbours Number of closest neighbours to consider for each cell 116 | */ 117 | void runSwapsTwoRowsAmplify(int r1, int r2, int nbNeighbours); 118 | 119 | /** 120 | * @brief Run a simple optimization using only cell shifting (no reordering) 121 | * on the given rows 122 | */ 123 | void runShiftsOnRows(const std::vector &rows, int maxNbCells); 124 | 125 | /** 126 | * @brief Change the cell coordinates to optimize the wirelength without 127 | * reordering them 128 | */ 129 | void runShiftsOnCells(const std::vector &cells); 130 | 131 | /** 132 | * @brief Run cell ordering optimization 133 | */ 134 | void runReordering(int maxNbRows, int maxNbCells); 135 | 136 | /** 137 | * @brief Optimize cell ordering over multiple rows 138 | */ 139 | void runReorderingOnRows(const std::vector &rows, int maxNbCells); 140 | 141 | /** 142 | * @brief Optimize cell ordering on multiple cells 143 | */ 144 | void runReorderingOnCells(const std::vector &cells); 145 | 146 | /** 147 | * @brief Return the current objective value 148 | */ 149 | long long value() const { return xtopo_.value() + ytopo_.value(); } 150 | 151 | /** 152 | * @brief Export the placement result 153 | */ 154 | void exportPlacement(Circuit &circuit); 155 | 156 | private: 157 | /** 158 | * @brief Given two ordered rows, obtain the index of the closest cell in the 159 | * second row for each cell in the firt row 160 | */ 161 | std::vector computeClosestIndexInRow( 162 | const std::vector &row1Cells, 163 | const std::vector &row2Cells) const; 164 | 165 | /** 166 | * @brief Swap the two cells 167 | */ 168 | void doSwap(int c1, int c2); 169 | 170 | /** 171 | * @brief Insert the cell here 172 | */ 173 | void doInsert(int c, int row, int pred); 174 | 175 | /** 176 | * @brief Try to swap two cells 177 | * 178 | * @return True on a change 179 | */ 180 | bool trySwap(int c1, int c2) { return bestSwap(c1, {c2}); } 181 | 182 | /** 183 | * @brief Insert the cell at a given position 184 | * 185 | * @return True on a change 186 | */ 187 | bool tryInsert(int c, int row, int pred) { 188 | return bestInsert(c, row, {pred}); 189 | } 190 | 191 | /** 192 | * @brief Perform the best swap out of many candidates 193 | * 194 | * @return True on a change 195 | */ 196 | bool bestSwap(int c, const std::vector &candidates); 197 | 198 | /** 199 | * @brief Perform the best insert out of many candidates 200 | * 201 | * @return True on a change 202 | */ 203 | bool bestInsert(int c, int row, const std::vector &candidates); 204 | 205 | /** 206 | * @brief Perfom the best improvement swap between a cell and another row; 207 | * update the inputs if necessary on a swap to stay in the same row 208 | * 209 | * @return True on a change 210 | */ 211 | bool bestSwapUpdate(int &c, int &from, int nbNeighbours); 212 | 213 | /** 214 | * @brief Return the feasibility of the swap, and the new value if feasible 215 | */ 216 | std::pair valueOnSwap(int c1, int c2); 217 | 218 | /** 219 | * @brief Return the feasibility of the insertion, and the new value if 220 | * feasible 221 | */ 222 | std::pair valueOnInsert(int c, int row, int pred); 223 | 224 | /** 225 | * @brief Return the first cell in a row that has x larger or equal to the 226 | * target cell 227 | */ 228 | int findCellAfter(int target, int fromCell) const; 229 | 230 | /** 231 | * @brief Return the first cell in a row that has x smaller or equal to the 232 | * target cell 233 | */ 234 | int findCellBefore(int target, int fromCell) const; 235 | 236 | private: 237 | /** 238 | * @brief Update the cell position in the objective after a change in the 239 | * placement 240 | */ 241 | void updateCellPos(int c); 242 | 243 | /** 244 | * @brief Update the cell position in the objective (not the placement) 245 | */ 246 | void updateCellPos(int c, Point pos); 247 | 248 | private: 249 | DetailedPlacement placement_; 250 | IncrNetModel xtopo_; 251 | IncrNetModel ytopo_; 252 | 253 | ColoquinteParameters params_; 254 | 255 | // Only for callbacks 256 | Circuit &circuit_; 257 | std::optional callback_; 258 | }; 259 | } // namespace coloquinte 260 | -------------------------------------------------------------------------------- /src/place_global/transportation_1d.hpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | class Transportation1d; 8 | class Transportation1dSorter; 9 | class Transportation1dSolver; 10 | 11 | /** 12 | * @brief A transportation solver for unidimensional optimal transport 13 | */ 14 | class Transportation1d { 15 | public: 16 | using Solution = std::vector>; 17 | 18 | /** 19 | * @brief Initialize the datastructure 20 | * 21 | * @param u Source positions 22 | * @param v Sink positions 23 | * @param s Source supplies 24 | * @param d Sink demands 25 | */ 26 | Transportation1d(const std::vector &u, 27 | const std::vector &v, 28 | const std::vector &s, 29 | const std::vector &d); 30 | /** 31 | * @brief Initialize the datastructure 32 | * 33 | * @param u Source positions 34 | * @param v Sink positions 35 | * @param s Source supplies 36 | * @param d Sink demands 37 | */ 38 | Transportation1d(std::vector &&u, std::vector &&v, 39 | std::vector &&s, std::vector &&d); 40 | 41 | /** 42 | * @brief Solve a 1D transportation problem 43 | */ 44 | Solution solve(); 45 | 46 | /** 47 | * @brief Give a rounded solution for a 1D transportation problem; exact only 48 | * for assignment problems 49 | */ 50 | std::vector assign(); 51 | 52 | /** 53 | * @brief Number of sources in the problem 54 | */ 55 | int nbSources() const { return u.size(); } 56 | 57 | /** 58 | * @brief Number of sinks in the problem 59 | */ 60 | int nbSinks() const { return v.size(); } 61 | 62 | /** 63 | * @brief Total source supply for the problem 64 | */ 65 | long long totalSupply() const; 66 | 67 | /** 68 | * @brief Total sink demand for the problem 69 | */ 70 | long long totalDemand() const; 71 | 72 | /** 73 | * @brief Add more demand if there is not enough 74 | */ 75 | void balanceDemand(); 76 | 77 | /** 78 | * @brief Compute the cost of allocating a source to a sink 79 | */ 80 | long long cost(int i, int j) const { return std::abs(u[i] - v[j]); } 81 | 82 | /** 83 | * @brief Position of the sources 84 | */ 85 | const std::vector &sourcePosition() const { return u; } 86 | 87 | /** 88 | * @brief Position of the sinks 89 | */ 90 | const std::vector &sinkPosition() const { return v; } 91 | 92 | /** 93 | * @brief Supply of the sources 94 | */ 95 | const std::vector &sourceSupply() const { return s; } 96 | 97 | /** 98 | * @brief Demand of the sinks 99 | */ 100 | const std::vector &sinkDemand() const { return d; } 101 | 102 | /** 103 | * @brief Read a serialized problem 104 | */ 105 | static Transportation1d read(std::istream &f); 106 | 107 | /** 108 | * @brief Read a serialized solution 109 | */ 110 | static Solution readSolution(std::istream &f); 111 | 112 | /** 113 | * @brief Write a serialized problem 114 | */ 115 | void write(std::ostream &f) const; 116 | 117 | /** 118 | * @brief Write a serialized solution 119 | */ 120 | static void writeSolution(const Solution &sol, std::ostream &f); 121 | 122 | /** 123 | * @brief Write a serialized assignment 124 | */ 125 | static void writeAssignment(const std::vector &sol, std::ostream &f); 126 | 127 | /** 128 | * @brief Check that a solution is valid 129 | */ 130 | void checkSolutionValid(const Solution &sol) const; 131 | 132 | /** 133 | * @brief Compute the cost for a solution 134 | */ 135 | long long cost(const Solution &sol) const; 136 | 137 | /** 138 | * @brief Basic checking 139 | */ 140 | void check() const; 141 | 142 | /** 143 | * @brief Check that the problem is sorted 144 | */ 145 | void checkSorted() const; 146 | 147 | /** 148 | * @brief Check that the problem is strictly sorted 149 | */ 150 | void checkStrictlySorted() const; 151 | 152 | /** 153 | * @brief Check that all capacities are non-zero 154 | */ 155 | void checkNonZeroCapacities() const; 156 | 157 | protected: 158 | std::vector u; 159 | std::vector v; 160 | std::vector s; 161 | std::vector d; 162 | 163 | friend class Transportation1dSorter; 164 | }; 165 | 166 | class Transportation1dSorter { 167 | public: 168 | using Solution = Transportation1d::Solution; 169 | 170 | /** 171 | * @brief Initialize the datastructure 172 | */ 173 | Transportation1dSorter(const std::vector &u, 174 | const std::vector &v, 175 | const std::vector &s, 176 | const std::vector &d); 177 | 178 | Transportation1dSolver convert(const Transportation1d &pb) const; 179 | 180 | /** 181 | * Convert back a solution given by the solver through preprocessing 182 | */ 183 | Solution convertSolutionBack(const Solution &sol) const; 184 | 185 | /** 186 | * Convert back an assignment given by the solver through preprocessing 187 | */ 188 | std::vector convertAssignmentBack(const std::vector &a) const; 189 | 190 | private: 191 | std::vector srcOrder; 192 | std::vector snkOrder; 193 | }; 194 | 195 | /** 196 | * @brief Implementation of the transportation solver for unidimensional optimal 197 | * transport 198 | */ 199 | class Transportation1dSolver : public Transportation1d { 200 | public: 201 | using Event = std::pair; 202 | using PrioQueue = std::priority_queue; 203 | /** 204 | * @brief Initialize the datastructure 205 | * 206 | * @param u Source positions 207 | * @param v Sink positions 208 | * @param s Source supplies 209 | * @param d Sink demands 210 | */ 211 | Transportation1dSolver(std::vector &&u, std::vector &&v, 212 | std::vector &&s, 213 | std::vector &&d); 214 | 215 | /** 216 | * @brief Compute the total supply at the sources 217 | */ 218 | long long totalSupply() const { return S.back(); } 219 | 220 | /** 221 | * @brief Compute the total demand at the sinks 222 | */ 223 | long long totalDemand() const { return D.back(); }; 224 | 225 | /** 226 | * @brief Compute the change in reduced cost at a boundary 227 | */ 228 | long long delta(int i, int j) const { 229 | return cost(i, j + 1) + cost(i + 1, j) - cost(i + 1, j + 1) - cost(i, j); 230 | } 231 | 232 | /** 233 | * @brief Run the whole optimization with efficient algorithm 234 | */ 235 | void run(); 236 | 237 | /** 238 | * @brief Obtain the current solution 239 | */ 240 | Solution computeSolution() const; 241 | 242 | /** 243 | * @brief Obtain an assignment close to the fractional solution 244 | */ 245 | std::vector computeAssignment() const; 246 | 247 | /** 248 | * @brief Check that the given solution is optimal 249 | */ 250 | void checkSolutionOptimal(const Solution &sol) const; 251 | 252 | /** 253 | * @brief Check the datastructure 254 | */ 255 | void check() const; 256 | 257 | private: 258 | /** 259 | * @brief Initialize the additional datastructures 260 | */ 261 | void setupData(); 262 | 263 | /** 264 | * @brief Flush the positions according to the non-overlap constraints 265 | */ 266 | void flushPositions(); 267 | 268 | /** 269 | * @brief Push a single source 270 | */ 271 | void push(int i); 272 | 273 | /** 274 | * @brief Push a single source 275 | */ 276 | void pushOnce(int i); 277 | 278 | /** 279 | * @brief Push to the last sink to be used 280 | */ 281 | void pushToLastSink(int i); 282 | 283 | /** 284 | * @brief Push to the next sink to be used 285 | */ 286 | void pushToNewSink(int i); 287 | 288 | /** 289 | * @brief Push the events corresponding to a given source 290 | */ 291 | void pushNewSourceEvents(int i); 292 | 293 | /** 294 | * @brief Push the events corresponding to entering a new sink 295 | */ 296 | void pushNewSinkEvents(int i, int j); 297 | 298 | /** 299 | * @brief Get the reduced cost at the current position 300 | */ 301 | long long getSlope(bool pop = false); 302 | 303 | /** 304 | * @brief Update the optimal sink 305 | */ 306 | void updateOptimalSink(int i); 307 | 308 | private: 309 | // Additional data 310 | std::vector S; 311 | std::vector D; 312 | 313 | // Solution and state 314 | std::vector p; 315 | PrioQueue events; 316 | long long lastPosition; 317 | int lastOccupiedSink; 318 | int optimalSink; 319 | }; 320 | -------------------------------------------------------------------------------- /src/place_global/net_model.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "coloquinte.hpp" 7 | 8 | namespace coloquinte { 9 | /** 10 | * Representation of the nets as 1D HPWL for global placement algorithms 11 | */ 12 | class NetModel { 13 | public: 14 | /** 15 | * @brief Parameters to build and solve the model 16 | */ 17 | struct Parameters { 18 | NetModelOption netModel; 19 | float approximationDistance; 20 | float penaltyCutoffDistance; 21 | float tolerance; 22 | int maxNbIterations; 23 | 24 | Parameters(); 25 | }; 26 | 27 | /** 28 | * @brief Create the horizontal view of the HPWL model 29 | */ 30 | static NetModel xTopology(const Circuit &); 31 | 32 | /** 33 | * @brief Create the vertical view of the HPWL model 34 | */ 35 | static NetModel yTopology(const Circuit &); 36 | 37 | /** 38 | * @brief Export the horizontal placement to the ISPD circuit 39 | */ 40 | void exportPlacementX(Circuit &circuit, 41 | const std::vector &xplace) const; 42 | 43 | /** 44 | * @brief Export the vertical placement to the ISPD circuit 45 | */ 46 | void exportPlacementY(Circuit &circuit, 47 | const std::vector &yplace) const; 48 | 49 | /** 50 | * @brief Return the number of cells 51 | */ 52 | int nbCells() const { return nbCells_; } 53 | 54 | /** 55 | * @brief Return the number of nets 56 | */ 57 | int nbNets() const { return netLimits_.size() - 1; } 58 | 59 | /** 60 | * @brief Return the total number of pins 61 | */ 62 | int nbPins() const { return netLimits_.back(); } 63 | 64 | /** 65 | * @brief Return the number of pins for a given net 66 | */ 67 | int nbPins(int net) const { 68 | assert(net >= 0); 69 | assert(net < nbNets()); 70 | return netLimits_[net + 1] - netLimits_[net]; 71 | } 72 | 73 | /** 74 | * @brief Return the cell for a given net pin 75 | */ 76 | int pinCell(int net, int pin) const { 77 | assert(pin >= 0); 78 | assert(pin < nbPins(net)); 79 | return netCells_[netLimits_[net] + pin]; 80 | } 81 | 82 | /** 83 | * @brief Return the offset for a given net pin 84 | */ 85 | float pinOffset(int net, int pin) const { 86 | assert(pin >= 0); 87 | assert(pin < nbPins(net)); 88 | return netPinOffsets_[netLimits_[net] + pin]; 89 | } 90 | 91 | /** 92 | * @brief Return the position for a given net pin with this placement 93 | */ 94 | float pinPosition(int net, int pin, const std::vector &pl) const { 95 | int c = pinCell(net, pin); 96 | assert(c >= -1); 97 | assert(c < nbCells()); 98 | float pos = c == -1 ? 0.0f : pl[c]; 99 | return pos + pinOffset(net, pin); 100 | } 101 | 102 | /** 103 | * @brief Return the weight associated with a given net 104 | */ 105 | float netWeight(int net) const { 106 | assert(net < nbNets()); 107 | return netWeight_[net]; 108 | } 109 | 110 | /** 111 | * @brief Initialize the datastructure 112 | */ 113 | explicit NetModel(int nbCells); 114 | 115 | /** 116 | * @brief Add a new net 117 | */ 118 | void addNet(const std::vector &cells); 119 | 120 | /** 121 | * @brief Add a new net 122 | */ 123 | void addNet(const std::vector &cells, 124 | const std::vector &pinOffsets, float weight = 1.0f); 125 | 126 | /** 127 | * @brief Add a new net 128 | */ 129 | void addNet(const std::vector &cells, 130 | const std::vector &pinOffsets, float minPin, float maxPin, 131 | float weight = 1.0f); 132 | 133 | /** 134 | * @brief Compute the length for a given placement 135 | */ 136 | float value(const std::vector &pl) const; 137 | 138 | /** 139 | * @brief Solve using a local approximation of the actual wirelength, 140 | * specified in the parameters 141 | * 142 | * @param netPlacement Initial positions to build the approximation 143 | * @param params Net model parameters 144 | * 145 | * @return New positions for the cells 146 | */ 147 | std::vector solve(const std::vector &netPlacement, 148 | const Parameters ¶ms = Parameters()) const; 149 | 150 | /** 151 | * @brief Solve using a local approximation of the actual wirelength, 152 | * specified in the parameters 153 | * 154 | * @param netPlacement Initial positions to build the approximation 155 | * @param placementTarget Target positions to pull the cells 156 | * @param penaltyStrength Magnitude of the penalty towards the target 157 | * positions 158 | * @param params Net model parameters 159 | * 160 | * @return New positions for the cells 161 | */ 162 | std::vector solveWithPenalty( 163 | const std::vector &netPlacement, 164 | const std::vector &placementTarget, 165 | const std::vector &penaltyStrength, 166 | const Parameters ¶ms = Parameters()) const; 167 | 168 | /** 169 | * @brief Solve with a quadratic star model 170 | * 171 | * @return Positions for the cells that minimize the wirelength 172 | */ 173 | std::vector solveStar(const Parameters ¶ms = Parameters()) const; 174 | 175 | /** 176 | * @brief Solve using the star model as a local approximation of the actual 177 | * wirelength 178 | * 179 | * @param placement Initial positions to build the approximation 180 | * @param params Model parameters 181 | * 182 | * @return New positions for the cells 183 | */ 184 | std::vector solveStar(const std::vector &placement, 185 | const Parameters ¶ms = Parameters()) const; 186 | 187 | /** 188 | * @brief Solve using the star model as a local approximation of the actual 189 | * wirelength 190 | * 191 | * @param netPlacement Initial positions to build the approximation 192 | * @param placementTarget Target positions to pull the cells 193 | * @param penaltyStrength Magnitude of the penalty towards the target 194 | * positions 195 | * 196 | * @return New positions for the cells 197 | */ 198 | std::vector solveStar(const std::vector &netPlacement, 199 | const std::vector &placementTarget, 200 | const std::vector &penaltyStrength, 201 | const Parameters ¶ms = Parameters()) const; 202 | 203 | /** 204 | * @brief Solve using the bound-to-bound model as a local approximation of the 205 | * actual wirelength 206 | * 207 | * @param placement Initial positions to build the approximation 208 | * 209 | * @return New positions for the cells 210 | */ 211 | std::vector solveB2B(const std::vector &placement, 212 | const Parameters ¶ms = Parameters()) const; 213 | 214 | /** 215 | * @brief Solve the bound-to-bound model as a local approximation of the 216 | * actual wirelength 217 | * 218 | * @param netPlacement Initial positions to build the approximation 219 | * @param placementTarget Target positions to pull the cells 220 | * @param penaltyStrength Magnitude of the penalty towards the target 221 | * positions 222 | * 223 | * @return New positions for the cells 224 | */ 225 | 226 | std::vector solveB2B(const std::vector &netPlacement, 227 | const std::vector &placementTarget, 228 | const std::vector &penaltyStrength, 229 | const Parameters ¶ms = Parameters()) const; 230 | 231 | /** 232 | * @brief Check the consistency of the datastructure 233 | */ 234 | void check() const; 235 | 236 | private: 237 | /** 238 | * @brief Obtain the positions of the pins with a given placement (corresponds 239 | * to netCells_ and pinOffsets_) 240 | * 241 | */ 242 | std::vector pinPositions(const std::vector &pl) const; 243 | 244 | /** 245 | * @brief Return the pin index, cell, offset and position associated with the 246 | * leftmost pin for the net. Cell may be -1 for a fixed pin 247 | */ 248 | std::tuple minPin(int net, 249 | const std::vector &pl) const; 250 | 251 | /** 252 | * @brief eturn the pin index, cell, offset and position associated with the 253 | * rightmost pin for the net. Cell may be -1 for a fixed pin 254 | */ 255 | std::tuple maxPin(int net, 256 | const std::vector &pl) const; 257 | 258 | private: 259 | int nbCells_; 260 | std::vector netLimits_; 261 | std::vector netWeight_; 262 | std::vector netCells_; 263 | std::vector netPinOffsets_; 264 | 265 | friend class MatrixCreator; 266 | }; 267 | } // namespace coloquinte -------------------------------------------------------------------------------- /src/place_detailed/row_neighbourhood.cpp: -------------------------------------------------------------------------------- 1 | #include "place_detailed/row_neighbourhood.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace coloquinte { 7 | RowNeighbourhood::RowNeighbourhood(const std::vector &rows, 8 | int nbNeighbourRows) { 9 | simpleSetup(rows, nbNeighbourRows); 10 | } 11 | 12 | RowNeighbourhood::RowNeighbourhood(const std::vector &rows, 13 | int nbNeighbourRows) { 14 | std::vector r; 15 | r.reserve(rows.size()); 16 | for (Rectangle row : rows) { 17 | r.push_back(row); 18 | } 19 | simpleSetup(r, nbNeighbourRows); 20 | } 21 | 22 | void RowNeighbourhood::check() const { 23 | if ((int)rowsBelow_.size() != nbRows()) { 24 | throw std::runtime_error("Inconsistent number of rows in neighbourhood"); 25 | } 26 | if ((int)rowsAbove_.size() != nbRows()) { 27 | throw std::runtime_error("Inconsistent number of rows in neighbourhood"); 28 | } 29 | if ((int)rowsLeft_.size() != nbRows()) { 30 | throw std::runtime_error("Inconsistent number of rows in neighbourhood"); 31 | } 32 | if ((int)rowsRight_.size() != nbRows()) { 33 | throw std::runtime_error("Inconsistent number of rows in neighbourhood"); 34 | } 35 | } 36 | 37 | namespace { 38 | std::vector keepFirstK(const std::vector &inds, int nb) { 39 | if ((int)inds.size() <= nb) { 40 | return inds; 41 | } 42 | return std::vector(inds.begin(), inds.begin() + nb); 43 | } 44 | 45 | bool orderBelow(std::pair a, std::pair b) { 46 | int ay = a.second.minY; 47 | int by = b.second.minY; 48 | return ay > by || (ay == by && a.first < b.first); 49 | } 50 | 51 | bool orderAbove(std::pair a, std::pair b) { 52 | int ay = a.second.minY; 53 | int by = b.second.minY; 54 | return ay < by || (ay == by && a.first < b.first); 55 | } 56 | 57 | bool orderSide(std::pair a, std::pair b) { 58 | int ax = a.second.minX; 59 | int bx = b.second.minX; 60 | int ay = a.second.minY; 61 | int by = b.second.minY; 62 | return ay < by || (ay == by && ax < bx); 63 | } 64 | } // namespace 65 | 66 | void RowNeighbourhood::simpleSetup(const std::vector &rows, 67 | int nbNeighbourRows) { 68 | rowsBelow_ = rowsBelow(rows, nbNeighbourRows); 69 | rowsAbove_ = rowsAbove(rows, nbNeighbourRows); 70 | buildRowsSides(rows, nbNeighbourRows); 71 | } 72 | 73 | bool RowNeighbourhood::isBelow(Rectangle r1, Rectangle r2) { 74 | // Only rows strictly below 75 | if (r2.minY <= r1.minY) { 76 | return false; 77 | } 78 | // Only rows that share part of their x range 79 | if (r2.minX >= r1.maxX) { 80 | return false; 81 | } 82 | if (r2.maxX <= r1.minX) { 83 | return false; 84 | } 85 | return true; 86 | } 87 | 88 | bool RowNeighbourhood::isAbove(Rectangle r1, Rectangle r2) { 89 | // Only rows strictly above 90 | if (r2.minY >= r1.minY) { 91 | return false; 92 | } 93 | // Only rows that share part of their x range 94 | if (r2.minX >= r1.maxX) { 95 | return false; 96 | } 97 | if (r2.maxX <= r1.minX) { 98 | return false; 99 | } 100 | return true; 101 | } 102 | 103 | bool RowNeighbourhood::isLeft(Rectangle r1, Rectangle r2) { 104 | // Only rows strictly on the left 105 | return r2.minX >= r1.maxX; 106 | } 107 | 108 | bool RowNeighbourhood::isRight(Rectangle r1, Rectangle r2) { 109 | // Only rows strictly on the right 110 | return r2.maxX <= r1.minX; 111 | } 112 | 113 | std::vector > RowNeighbourhood::rowsBelow( 114 | const std::vector &rows, int nbNeighbourRows) { 115 | std::vector > ret(rows.size()); 116 | std::vector > sortedRows; 117 | sortedRows.reserve(rows.size()); 118 | 119 | for (int i = 0; i < (int)rows.size(); ++i) { 120 | sortedRows.emplace_back(i, rows[i]); 121 | } 122 | std::sort(sortedRows.begin(), sortedRows.end(), orderBelow); 123 | for (size_t i = 0; i < sortedRows.size(); ++i) { 124 | auto [ind1, row1] = sortedRows[i]; 125 | int nbFound = 0; 126 | for (size_t j = i + 1; j < sortedRows.size(); ++j) { 127 | auto [ind2, row2] = sortedRows[j]; 128 | if (isBelow(row2, row1)) { 129 | ret[ind1].push_back(ind2); 130 | ++nbFound; 131 | } 132 | if (nbFound >= nbNeighbourRows) { 133 | break; 134 | } 135 | } 136 | } 137 | return ret; 138 | } 139 | 140 | std::vector > RowNeighbourhood::rowsAbove( 141 | const std::vector &rows, int nbNeighbourRows) { 142 | std::vector > ret(rows.size()); 143 | std::vector > sortedRows; 144 | sortedRows.reserve(rows.size()); 145 | 146 | for (int i = 0; i < (int)rows.size(); ++i) { 147 | sortedRows.emplace_back(i, rows[i]); 148 | } 149 | std::sort(sortedRows.begin(), sortedRows.end(), orderAbove); 150 | for (size_t i = 0; i < sortedRows.size(); ++i) { 151 | auto [ind1, row1] = sortedRows[i]; 152 | int nbFound = 0; 153 | for (size_t j = i + 1; j < sortedRows.size(); ++j) { 154 | auto [ind2, row2] = sortedRows[j]; 155 | if (isAbove(row2, row1)) { 156 | ret[ind1].push_back(ind2); 157 | ++nbFound; 158 | } 159 | if (nbFound >= nbNeighbourRows) { 160 | break; 161 | } 162 | } 163 | } 164 | return ret; 165 | } 166 | 167 | std::vector RowNeighbourhood::buildLeftFrom( 168 | Rectangle row, const std::vector &rows, int ind) const { 169 | std::vector candidates; 170 | candidates.push_back(ind); 171 | for (int c : rowsAbove(ind)) { 172 | candidates.push_back(c); 173 | } 174 | for (int c : rowsBelow(ind)) { 175 | candidates.push_back(c); 176 | } 177 | std::vector > sortedRows; 178 | for (int c : candidates) { 179 | if (isLeft(rows[c], row)) { 180 | sortedRows.emplace_back(c, rows[c]); 181 | } 182 | } 183 | std::sort(sortedRows.begin(), sortedRows.end(), 184 | [row](std::pair a, std::pair b) { 185 | int ax = a.second.maxX; 186 | int bx = b.second.maxX; 187 | int ay = a.second.minY; 188 | int by = b.second.minY; 189 | int rx = row.minX; 190 | int ry = row.minY; 191 | int ad = std::abs(ax - rx) + std::abs(ay - ry); 192 | int bd = std::abs(bx - rx) + std::abs(by - ry); 193 | return ad < bd || (ad == bd && a.first < b.first); 194 | }); 195 | std::vector ret; 196 | ret.reserve(sortedRows.size()); 197 | 198 | for (auto p : sortedRows) { 199 | ret.push_back(p.first); 200 | } 201 | return ret; 202 | } 203 | 204 | std::vector RowNeighbourhood::buildRightFrom( 205 | Rectangle row, const std::vector &rows, int ind) const { 206 | std::vector candidates; 207 | candidates.push_back(ind); 208 | for (int c : rowsAbove(ind)) { 209 | candidates.push_back(c); 210 | } 211 | for (int c : rowsBelow(ind)) { 212 | candidates.push_back(c); 213 | } 214 | std::vector > sortedRows; 215 | for (int c : candidates) { 216 | if (isRight(rows[c], row)) { 217 | sortedRows.emplace_back(c, rows[c]); 218 | } 219 | } 220 | std::sort(sortedRows.begin(), sortedRows.end(), 221 | [row](std::pair a, std::pair b) { 222 | int ax = a.second.maxX; 223 | int bx = b.second.maxX; 224 | int ay = a.second.minY; 225 | int by = b.second.minY; 226 | int rx = row.maxX; 227 | int ry = row.minY; 228 | int ad = std::abs(ax - rx) + std::abs(ay - ry); 229 | int bd = std::abs(bx - rx) + std::abs(by - ry); 230 | return ad < bd || (ad == bd && a.first < b.first); 231 | }); 232 | std::vector ret; 233 | ret.reserve(sortedRows.size()); 234 | 235 | for (auto p : sortedRows) { 236 | ret.push_back(p.first); 237 | } 238 | return ret; 239 | } 240 | 241 | void RowNeighbourhood::buildRowsSides(const std::vector &rows, 242 | int nbNeighbourRows) { 243 | rowsLeft_.clear(); 244 | rowsLeft_.resize(rows.size()); 245 | rowsRight_.clear(); 246 | rowsRight_.resize(rows.size()); 247 | std::vector > sortedRows; 248 | sortedRows.reserve(rows.size()); 249 | 250 | for (int i = 0; i < (int)rows.size(); ++i) { 251 | sortedRows.emplace_back(i, rows[i]); 252 | } 253 | std::sort(sortedRows.begin(), sortedRows.end(), orderSide); 254 | for (size_t i = 0; i + 1 < sortedRows.size(); ++i) { 255 | auto [ind1, row1] = sortedRows[i]; 256 | auto [ind2, row2] = sortedRows[i + 1]; 257 | if (isLeft(row1, row2)) { 258 | rowsLeft_[ind2] = 259 | keepFirstK(buildLeftFrom(row2, rows, ind1), nbNeighbourRows); 260 | } 261 | if (isRight(row2, row1)) { 262 | rowsRight_[ind1] = 263 | keepFirstK(buildRightFrom(row1, rows, ind2), nbNeighbourRows); 264 | } 265 | } 266 | } 267 | 268 | } // Namespace coloquinte 269 | -------------------------------------------------------------------------------- /pycoloquinte/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 The Pybind Development Team, All rights reserved. 2 | # 3 | # Redistribution and use in source and binary forms, with or without 4 | # modification, are permitted provided that the following conditions are met: 5 | # 6 | # 1. Redistributions of source code must retain the above copyright notice, this 7 | # list of conditions and the following disclaimer. 8 | # 9 | # 2. Redistributions in binary form must reproduce the above copyright notice, 10 | # this list of conditions and the following disclaimer in the documentation 11 | # and/or other materials provided with the distribution. 12 | # 13 | # 3. Neither the name of the copyright holder nor the names of its contributors 14 | # may be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | # 28 | # You are under no obligation whatsoever to provide any bug fixes, patches, or 29 | # upgrades to the features, functionality or performance of the source code 30 | # ("Enhancements") to anyone; however, if you choose to make your Enhancements 31 | # available either publicly, or directly to the author of this software, without 32 | # imposing a separate written license agreement for such Enhancements, then you 33 | # hereby grant the following license: a non-exclusive, royalty-free perpetual 34 | # license to install, use, modify, prepare derivative works, incorporate into 35 | # other computer software, distribute, and sublicense such enhancements or 36 | # derivative works thereof, in binary and source code form. 37 | 38 | import os 39 | import re 40 | import subprocess 41 | import sys 42 | 43 | from setuptools import Extension, setup 44 | from setuptools.command.build_ext import build_ext 45 | 46 | # Convert distutils Windows platform specifiers to CMake -A arguments 47 | PLAT_TO_CMAKE = { 48 | "win32": "Win32", 49 | "win-amd64": "x64", 50 | "win-arm32": "ARM", 51 | "win-arm64": "ARM64", 52 | } 53 | 54 | 55 | # A CMakeExtension needs a sourcedir instead of a file list. 56 | # The name must be the _single_ output extension from the CMake build. 57 | # If you need multiple extensions, see scikit-build. 58 | class CMakeExtension(Extension): 59 | def __init__(self, name, sourcedir=""): 60 | Extension.__init__(self, name, sources=[]) 61 | self.sourcedir = os.path.abspath(sourcedir) 62 | 63 | 64 | class CMakeBuild(build_ext): 65 | def build_extension(self, ext): 66 | extdir = os.path.abspath(os.path.dirname( 67 | self.get_ext_fullpath(ext.name))) 68 | 69 | # required for auto-detection & inclusion of auxiliary "native" libs 70 | if not extdir.endswith(os.path.sep): 71 | extdir += os.path.sep 72 | 73 | if self.debug or bool(os.environ.get("DEBUG", 0)): 74 | cfg = "Debug" 75 | elif bool(os.environ.get("DEBUG_INFO", 0)): 76 | cfg = "RelWithDebInfo" 77 | else: 78 | cfg = "Release" 79 | 80 | # CMake lets you override the generator - we need to check this. 81 | # Can be set with Conda-Build, for example. 82 | cmake_generator = os.environ.get("CMAKE_GENERATOR", "") 83 | 84 | # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON 85 | # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code 86 | # from Python. 87 | cmake_args = [ 88 | "-DBUILD_PYCOLOQUINTE=ON", 89 | f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}", 90 | f"-DPYTHON_EXECUTABLE={sys.executable}", 91 | f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm 92 | ] 93 | build_args = [] 94 | # Adding CMake arguments set as environment variable 95 | # (needed e.g. to build for ARM OSx on conda-forge) 96 | if "CMAKE_ARGS" in os.environ: 97 | cmake_args += [ 98 | item for item in os.environ["CMAKE_ARGS"].split(" ") if item] 99 | 100 | # In this example, we pass in the version to C++. You might not need to. 101 | # cmake_args += [f"-DEXAMPLE_VERSION_INFO={self.distribution.get_version()}"] 102 | 103 | if self.compiler.compiler_type != "msvc": 104 | # Using Ninja-build since it a) is available as a wheel and b) 105 | # multithreads automatically. MSVC would require all variables be 106 | # exported for Ninja to pick it up, which is a little tricky to do. 107 | # Users can override the generator with CMAKE_GENERATOR in CMake 108 | # 3.15+. 109 | if not cmake_generator or cmake_generator == "Ninja": 110 | try: 111 | import ninja # noqa: F401 112 | 113 | ninja_executable_path = os.path.join( 114 | ninja.BIN_DIR, "ninja") 115 | cmake_args += [ 116 | "-GNinja", 117 | f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}", 118 | ] 119 | except ImportError: 120 | pass 121 | 122 | else: 123 | 124 | # Single config generators are handled "normally" 125 | single_config = any( 126 | x in cmake_generator for x in {"NMake", "Ninja"}) 127 | 128 | # CMake allows an arch-in-generator style for backward compatibility 129 | contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) 130 | 131 | # Specify the arch if using MSVC generator, but only if it doesn't 132 | # contain a backward-compatibility arch spec already in the 133 | # generator name. 134 | if not single_config and not contains_arch: 135 | cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] 136 | 137 | # Multi-config generators have a different way to specify configs 138 | if not single_config: 139 | cmake_args += [ 140 | f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}" 141 | ] 142 | build_args += ["--config", cfg] 143 | 144 | if sys.platform.startswith("darwin"): 145 | # Cross-compile support for macOS - respect ARCHFLAGS if set 146 | archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) 147 | if archs: 148 | cmake_args += [ 149 | "-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] 150 | 151 | # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level 152 | # across all generators. 153 | if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: 154 | # self.parallel is a Python 3 only way to set parallel jobs by hand 155 | # using -j in the build_ext call, not supported by pip or PyPA-build. 156 | if hasattr(self, "parallel") and self.parallel: 157 | # CMake 3.12+ only. 158 | build_args += [f"-j{self.parallel}"] 159 | 160 | build_temp = os.path.join(self.build_temp, ext.name) 161 | if not os.path.exists(build_temp): 162 | os.makedirs(build_temp) 163 | 164 | subprocess.check_call(["cmake", ext.sourcedir] + 165 | cmake_args, cwd=build_temp) 166 | subprocess.check_call(["cmake", "--build", "."] + 167 | build_args, cwd=build_temp) 168 | 169 | 170 | with open('../README.md') as f: 171 | long_description = f.read() 172 | 173 | # The information here can also be placed in setup.cfg - better separation of 174 | # logic and declaration, and simpler if you include description/version in a file. 175 | setup( 176 | name="coloquinte", 177 | version="0.4.1", 178 | author="Gabriel Gouvine", 179 | author_email="gabriel.gouvine_git@m4x.org", 180 | url="https://github.com/Coloquinte/PlaceRoute", 181 | description="Python interface for the Coloquinte VLSI placer", 182 | long_description=long_description, 183 | long_description_content_type="text/markdown", 184 | license="MIT", 185 | classifiers=[ 186 | "License :: OSI Approved :: MIT License", 187 | "Programming Language :: Python :: 3", 188 | "Intended Audience :: Science/Research", 189 | "Intended Audience :: Developers", 190 | "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", 191 | ], 192 | ext_modules=[CMakeExtension("pycoloquinte_pybind", "..")], 193 | py_modules=["coloquinte"], 194 | cmdclass={"build_ext": CMakeBuild}, 195 | zip_safe=False, 196 | python_requires=">=3.8", 197 | entry_points={'console_scripts': ['coloquinte = coloquinte:main']}, 198 | ) 199 | -------------------------------------------------------------------------------- /src/place_detailed/detailed_placement.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "coloquinte.hpp" 4 | 5 | namespace coloquinte { 6 | /** 7 | * @brief Representation of the detailed placement state of a standard cells 8 | * design 9 | * 10 | * Each row is represented as a linked lists, so that accessing nearby cells is 11 | * cheap. 12 | */ 13 | class DetailedPlacement { 14 | public: 15 | /** 16 | * @brief Initialize the datastructure from a circuit 17 | */ 18 | static DetailedPlacement fromIspdCircuit(const Circuit &circuit); 19 | 20 | /** 21 | * @brief Initialize the datastructure from a region of a circuit 22 | */ 23 | static DetailedPlacement fromIspdCircuit(const Circuit &circuit, 24 | const Rectangle ®ion); 25 | 26 | /** 27 | * @brief Export the placement obtained to the circuit datastructure 28 | */ 29 | void exportPlacement(Circuit &circuit); 30 | 31 | /** 32 | * @brief Create the datastructure 33 | * @param rows: Available rows for placement; must all be the right 34 | * height for the cells 35 | * @param width: Width of the cells when placed in a row 36 | * @param posX: X coordinate (must be legal) 37 | * @param posY: Y coordinate (must be legal) 38 | */ 39 | static DetailedPlacement fromPos(const std::vector &rows, 40 | const std::vector &width, 41 | const std::vector &posX, 42 | const std::vector &posY); 43 | 44 | /** 45 | * @brief Initialize the datastructure 46 | * @param rows: Available rows for placement; must all be the right 47 | * height for the cells 48 | * @param width: Width of the cells when placed in a row 49 | * @param posX: X coordinate (must be legal) 50 | * @param posY: Y coordinate (must be legal) 51 | * @param orient: Orientation of the cells (must be legal) 52 | * @param pol: Polarity of the cells with respect to the rows 53 | * @param cellIndex: Cell index in the original circuit 54 | */ 55 | DetailedPlacement(const std::vector &rows, const std::vector &width, 56 | const std::vector &posX, const std::vector &posY, 57 | const std::vector &orient, 58 | const std::vector &pol, 59 | const std::vector &cellIndex); 60 | 61 | /** 62 | * @brief Return the number of rows 63 | */ 64 | int nbRows() const { return rows_.size(); } 65 | 66 | /** 67 | * @brief Return the number of cells 68 | */ 69 | int nbCells() const { return cellWidth_.size(); } 70 | 71 | /** 72 | * @brief Return all row geometries 73 | */ 74 | const std::vector &rows() const { return rows_; } 75 | 76 | /** 77 | * @brief Returns true if the cell is to be ignored by the detailed placement 78 | */ 79 | bool isIgnored(int cell) const { return cellWidth_[cell] == -1; } 80 | 81 | /** 82 | * @brief Returns true if the cell is assigned a placement 83 | */ 84 | bool isPlaced(int cell) const { return cellRow_[cell] != -1; } 85 | 86 | /** 87 | * @brief Return the width of the cell 88 | */ 89 | int cellWidth(int cell) const { return cellWidth_[cell]; } 90 | 91 | /** 92 | * @brief Return the row the cell is currently allocated to, -1 if it is not 93 | * placed 94 | */ 95 | int cellRow(int c) const { 96 | assert(c >= 0 && c < nbCells()); 97 | return cellRow_[c]; 98 | } 99 | /** 100 | * @brief Return the predecessor of the cell in its row, -1 if it is the first 101 | */ 102 | int cellPred(int c) const { 103 | assert(c >= 0 && c < nbCells()); 104 | return cellPred_[c]; 105 | } 106 | 107 | /** 108 | * @brief Return the successor of the cell in its row, -1 if it is the last 109 | */ 110 | int cellNext(int c) const { 111 | assert(c >= 0 && c < nbCells()); 112 | return cellNext_[c]; 113 | } 114 | 115 | /** 116 | * @brief Return the first cell in the row, -1 if there is none 117 | */ 118 | int rowFirstCell(int row) const { 119 | assert(row >= 0 && row < nbRows()); 120 | return rowFirstCell_[row]; 121 | } 122 | 123 | /** 124 | * @brief Return the last cell in the row, -1 if there is none 125 | */ 126 | int rowLastCell(int row) const { 127 | assert(row >= 0 && row < nbRows()); 128 | return rowLastCell_[row]; 129 | } 130 | 131 | /** 132 | * @brief Return all cells in the row 133 | */ 134 | std::vector rowCells(int row) const; 135 | 136 | /** 137 | * @brief Return all cells in the s, sorted by position 138 | */ 139 | std::vector rowCells(const std::vector &rows) const; 140 | 141 | /** 142 | * @brief Return all cells in the row strictly between two boundary cells 143 | */ 144 | std::vector cellsBetween(int row, int cellBefore, int cellAfter) const; 145 | 146 | /** 147 | * @brief Return the x position of the cell 148 | */ 149 | int cellX(int c) const { 150 | assert(c >= 0 && c < nbCells()); 151 | return cellX_[c]; 152 | } 153 | 154 | /** 155 | * @brief Return the y position of the cell 156 | */ 157 | int cellY(int c) const { 158 | assert(c >= 0 && c < nbCells()); 159 | return cellY_[c]; 160 | } 161 | 162 | /** 163 | * @brief Return the position of the cell 164 | */ 165 | Point cellPos(int c) const { 166 | assert(c >= 0 && c < nbCells()); 167 | return Point(cellX_[c], cellY_[c]); 168 | } 169 | 170 | /** 171 | * @brief Return the orientation of the cell 172 | */ 173 | CellOrientation cellOrientation(int c) const { 174 | assert(c >= 0 && c < nbCells()); 175 | return cellOrientation_[c]; 176 | } 177 | 178 | /** 179 | * @brief Return the polarity of the cell 180 | */ 181 | CellRowPolarity cellRowPolarity(int c) const { 182 | assert(c >= 0 && c < nbCells()); 183 | return cellRowPolarity_[c]; 184 | } 185 | 186 | /** 187 | * @brief Return the y position of the row 188 | */ 189 | int rowY(int r) const { 190 | assert(r >= 0 && r < nbRows()); 191 | return rows_[r].minY; 192 | } 193 | 194 | /** 195 | * @brief Return indices of the cells in the original circuit 196 | */ 197 | const std::vector &cellIndex() const { return cellIndex_; } 198 | 199 | /** 200 | * @brief Return the x boundary before the cell (beginning of row or end of 201 | * previous cell) 202 | */ 203 | int boundaryBefore(int c) const; 204 | 205 | /** 206 | * @brief Return the x boundary before the cell (beginning of row or end of 207 | * previous cell), or the end of the row is cell is -1 208 | */ 209 | int boundaryBefore(int row, int c) const; 210 | 211 | /** 212 | * @brief Return the x boundary after the cell (end of row or beginning of 213 | * next cell) 214 | */ 215 | int boundaryAfter(int c) const; 216 | 217 | /** 218 | * @brief Return the x boundary after the cell (end of row or beginning of 219 | * next cell), or the beginning of the row is cell is -1 220 | */ 221 | int boundaryAfter(int row, int c) const; 222 | 223 | /** 224 | * @brief Return the beginning position of the placement site after this cell 225 | * on this row 226 | */ 227 | int siteBegin(int row, int pred) const; 228 | 229 | /** 230 | * @brief Return the ending position of the placement site after this cell on 231 | * this row 232 | */ 233 | int siteEnd(int row, int pred) const; 234 | 235 | /** 236 | * @brief Return true if it is possible to place the cell here 237 | */ 238 | bool canPlace(int c, int row, int pred, int x) const; 239 | 240 | /** 241 | * @brief Return true if it is possible to insert the cell with this 242 | * predecessor 243 | */ 244 | bool canInsert(int c, int row, int pred) const; 245 | 246 | /** 247 | * @brief Return the position after inserting the cell here 248 | */ 249 | Point positionOnInsert(int c, int row, int pred) const; 250 | 251 | /** 252 | * @brief Return true if it is possible to swap the two cells 253 | */ 254 | bool canSwap(int c1, int c2) const; 255 | 256 | /** 257 | * @brief Return the positions after swapping the two cells 258 | */ 259 | std::pair positionsOnSwap(int c1, int c2) const; 260 | 261 | /** 262 | * @brief Do placement of a single cell 263 | */ 264 | void place(int c, int row, int pred, int x); 265 | 266 | /** 267 | * @brief Undo placement of a single cell 268 | */ 269 | void unplace(int c); 270 | 271 | /** 272 | * @brief Insert the cell with this predecessor 273 | */ 274 | void insert(int c, int row, int pred); 275 | 276 | /** 277 | * @brief Insert the cell at this position 278 | */ 279 | void insertAt(int c, int row, int pred, int x); 280 | 281 | /** 282 | * @brief Swap the two cells 283 | */ 284 | void swap(int c1, int c2); 285 | 286 | /** 287 | * @brief Swap the two cells with the given x position 288 | */ 289 | void swapAt(int c1, int c2, int x1, int x2); 290 | 291 | /** 292 | * @brief Run the algorithm 293 | */ 294 | void run(); 295 | 296 | /** 297 | * @brief Check consistency of the datastructure 298 | */ 299 | void check() const; 300 | 301 | private: 302 | std::vector rows_; 303 | std::vector rowFirstCell_; 304 | std::vector rowLastCell_; 305 | std::vector cellWidth_; 306 | std::vector cellPred_; 307 | std::vector cellNext_; 308 | std::vector cellRow_; 309 | std::vector cellX_; 310 | std::vector cellY_; 311 | std::vector cellOrientation_; 312 | std::vector cellRowPolarity_; 313 | std::vector cellIndex_; 314 | 315 | friend class DetailedPlacer; 316 | }; 317 | } // namespace coloquinte 318 | -------------------------------------------------------------------------------- /src/place_detailed/incr_net_model.cpp: -------------------------------------------------------------------------------- 1 | #include "place_detailed/incr_net_model.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace coloquinte { 9 | 10 | IncrNetModelBuilder::IncrNetModelBuilder(int nbCells) : nbCells_(nbCells) { 11 | netLimits_.push_back(0); 12 | } 13 | 14 | void IncrNetModelBuilder::addNet(const std::vector &cells, 15 | const std::vector &pinOffsets) { 16 | assert(cells.size() == pinOffsets.size()); 17 | if (cells.size() <= 1) { 18 | return; 19 | } 20 | netLimits_.push_back(netLimits_.back() + cells.size()); 21 | netCells_.insert(netCells_.end(), cells.begin(), cells.end()); 22 | netPinOffsets_.insert(netPinOffsets_.end(), pinOffsets.begin(), 23 | pinOffsets.end()); 24 | } 25 | 26 | IncrNetModel IncrNetModel::xTopology(const Circuit &circuit) { 27 | std::vector cells; 28 | cells.reserve(circuit.nbCells()); 29 | 30 | for (int c = 0; c < circuit.nbCells(); ++c) { 31 | cells.push_back(c); 32 | } 33 | return xTopology(circuit, cells); 34 | } 35 | 36 | IncrNetModel IncrNetModel::yTopology(const Circuit &circuit) { 37 | std::vector cells; 38 | cells.reserve(circuit.nbCells()); 39 | 40 | for (int c = 0; c < circuit.nbCells(); ++c) { 41 | cells.push_back(c); 42 | } 43 | return yTopology(circuit, cells); 44 | } 45 | 46 | namespace { 47 | std::unordered_map buildCellMapping(const Circuit &circuit, 48 | const std::vector &cells) { 49 | std::unordered_map cellMap; 50 | for (size_t i = 0; i < cells.size(); ++i) { 51 | int c = cells[i]; 52 | assert(c >= 0 && c < circuit.nbCells()); 53 | cellMap[c] = (int)i; 54 | } 55 | assert(cellMap.size() == cells.size()); 56 | return cellMap; 57 | } 58 | } // namespace 59 | 60 | IncrNetModel IncrNetModel::xTopology(const Circuit &circuit, 61 | const std::vector &cells) { 62 | std::unordered_map cellMap = buildCellMapping(circuit, cells); 63 | 64 | int fixedCell = cells.size(); 65 | std::vector cellX; 66 | cellX.reserve(cells.size()); 67 | 68 | for (int c : cells) { 69 | cellX.push_back(circuit.x(c)); 70 | } 71 | cellX.push_back(0); 72 | 73 | IncrNetModelBuilder ret(cellX.size()); 74 | for (int i = 0; i < circuit.nbNets(); ++i) { 75 | std::vector cells; 76 | std::vector offsets; 77 | bool hasFixed = false; 78 | int minFixed = std::numeric_limits::max(); 79 | int maxFixed = std::numeric_limits::min(); 80 | for (int j = 0; j < circuit.nbPinsNet(i); ++j) { 81 | int cell = circuit.pinCell(i, j); 82 | int offset = circuit.pinXOffset(i, j); 83 | if (cellMap.count(cell) != 0u) { 84 | cells.push_back(cellMap[cell]); 85 | offsets.push_back(offset); 86 | } else { 87 | int pos = circuit.x(cell) + offset; 88 | minFixed = std::min(pos, minFixed); 89 | maxFixed = std::max(pos, maxFixed); 90 | hasFixed = true; 91 | } 92 | } 93 | if (hasFixed) { 94 | cells.push_back(fixedCell); 95 | offsets.push_back(minFixed); 96 | if (minFixed != maxFixed) { 97 | cells.push_back(fixedCell); 98 | offsets.push_back(maxFixed); 99 | } 100 | } 101 | ret.addNet(cells, offsets); 102 | } 103 | return ret.build(cellX); 104 | } 105 | 106 | IncrNetModel IncrNetModel::yTopology(const Circuit &circuit, 107 | const std::vector &cells) { 108 | std::unordered_map cellMap = buildCellMapping(circuit, cells); 109 | 110 | int fixedCell = cells.size(); 111 | std::vector cellY; 112 | cellY.reserve(cells.size()); 113 | 114 | for (int c : cells) { 115 | cellY.push_back(circuit.y(c)); 116 | } 117 | cellY.push_back(0); 118 | 119 | IncrNetModelBuilder ret(cellY.size()); 120 | for (int i = 0; i < circuit.nbNets(); ++i) { 121 | std::vector cells; 122 | std::vector offsets; 123 | bool hasFixed = false; 124 | int minFixed = std::numeric_limits::max(); 125 | int maxFixed = std::numeric_limits::min(); 126 | for (int j = 0; j < circuit.nbPinsNet(i); ++j) { 127 | int cell = circuit.pinCell(i, j); 128 | int offset = circuit.pinYOffset(i, j); 129 | if (cellMap.count(cell) != 0u) { 130 | cells.push_back(cellMap[cell]); 131 | offsets.push_back(offset); 132 | } else { 133 | int pos = circuit.y(cell) + offset; 134 | minFixed = std::min(pos, minFixed); 135 | maxFixed = std::max(pos, maxFixed); 136 | hasFixed = true; 137 | } 138 | } 139 | if (hasFixed) { 140 | cells.push_back(fixedCell); 141 | offsets.push_back(minFixed); 142 | if (minFixed != maxFixed) { 143 | cells.push_back(fixedCell); 144 | offsets.push_back(maxFixed); 145 | } 146 | } 147 | ret.addNet(cells, offsets); 148 | } 149 | return ret.build(cellY); 150 | } 151 | 152 | void IncrNetModel::exportPlacementX(Circuit &circuit) const { 153 | assert(circuit.nbCells() == nbCells()); 154 | for (int i = 0; i < circuit.nbCells(); ++i) { 155 | if (!circuit.isFixed(i)) { 156 | circuit.cellX_[i] = cellPos_[i]; 157 | } 158 | } 159 | } 160 | 161 | void IncrNetModel::exportPlacementY(Circuit &circuit) const { 162 | assert(circuit.nbCells() == nbCells()); 163 | for (int i = 0; i < circuit.nbCells(); ++i) { 164 | if (!circuit.isFixed(i)) { 165 | circuit.cellY_[i] = cellPos_[i]; 166 | } 167 | } 168 | } 169 | 170 | IncrNetModel IncrNetModelBuilder::build() const { 171 | std::vector pos(nbCells(), 0); 172 | return build(pos); 173 | } 174 | 175 | IncrNetModel IncrNetModelBuilder::build(const std::vector &pos) const { 176 | assert((int)pos.size() == nbCells()); 177 | IncrNetModel ret; 178 | ret.netLimits_ = netLimits_; 179 | ret.netCells_ = netCells_; 180 | ret.netPinOffsets_ = netPinOffsets_; 181 | ret.cellPos_ = pos; 182 | ret.finalize(); 183 | return ret; 184 | } 185 | 186 | void IncrNetModel::finalize() { 187 | cellLimits_.assign(nbCells() + 1, 0); 188 | cellNets_.assign(nbPins(), -1); 189 | cellPinOffsets_.assign(nbPins(), 0); 190 | // Compute the cell limits 191 | for (int i = 0; i < nbNets(); ++i) { 192 | for (int j = 0; j < nbNetPins(i); ++j) { 193 | ++cellLimits_[pinCell(i, j) + 1]; 194 | } 195 | } 196 | std::partial_sum(cellLimits_.begin(), cellLimits_.end(), cellLimits_.begin()); 197 | // Setup the nets and offsets 198 | std::vector curIndex = cellLimits_; 199 | for (int i = 0; i < nbNets(); ++i) { 200 | for (int j = 0; j < nbNetPins(i); ++j) { 201 | int cell = pinCell(i, j); 202 | int ind = curIndex[cell]++; 203 | cellNets_[ind] = i; 204 | cellPinOffsets_[ind] = netPinOffset(i, j); 205 | } 206 | } 207 | 208 | // Setup the cost 209 | netMinMaxPos_ = computeNetMinMaxPos(); 210 | value_ = computeValue(); 211 | } 212 | 213 | std::pair IncrNetModel::computeNetMinMaxPos(int net) const { 214 | int minPos = std::numeric_limits::max(); 215 | int maxPos = std::numeric_limits::min(); 216 | for (int j = 0; j < nbNetPins(net); ++j) { 217 | int c = pinCell(net, j); 218 | int pinPos = cellPos_[c] + netPinOffset(net, j); 219 | minPos = std::min(pinPos, minPos); 220 | maxPos = std::max(pinPos, maxPos); 221 | } 222 | return std::make_pair(minPos, maxPos); 223 | } 224 | 225 | std::vector> IncrNetModel::computeNetMinMaxPos() const { 226 | std::vector> ret(nbNets()); 227 | for (int net = 0; net < nbNets(); ++net) { 228 | ret[net] = computeNetMinMaxPos(net); 229 | } 230 | return ret; 231 | } 232 | 233 | long long IncrNetModel::computeValue() const { 234 | long long ret = 0; 235 | for (int net = 0; net < nbNets(); ++net) { 236 | auto minMaxPos = netMinMaxPos_[net]; 237 | ret += minMaxPos.second - minMaxPos.first; 238 | } 239 | return ret; 240 | } 241 | 242 | void IncrNetModel::updateCellPos(int cell, int pos) { 243 | // TODO: optimize performance: do not recompute every connected net 244 | cellPos_[cell] = pos; 245 | for (int i = 0; i < nbCellPins(cell); ++i) { 246 | int net = pinNet(cell, i); 247 | recomputeNet(net); 248 | } 249 | } 250 | 251 | void IncrNetModel::recomputeNet(int net) { 252 | auto newMinMaxPos = computeNetMinMaxPos(net); 253 | auto oldMinMaxPos = netMinMaxPos_[net]; 254 | int oldValue = oldMinMaxPos.second - oldMinMaxPos.first; 255 | int newValue = newMinMaxPos.second - newMinMaxPos.first; 256 | netMinMaxPos_[net] = newMinMaxPos; 257 | value_ += newValue - oldValue; 258 | } 259 | 260 | void IncrNetModel::check() const { 261 | if ((int)netLimits_.size() != nbNets() + 1) { 262 | throw std::runtime_error("Net number mismatch"); 263 | } 264 | if (netLimits_.front() != 0 || netLimits_.back() != (int)netCells_.size()) { 265 | throw std::runtime_error("Pin number mismatch"); 266 | } 267 | if (netCells_.size() != netPinOffsets_.size()) { 268 | throw std::runtime_error("Pin number mismatch"); 269 | } 270 | for (int c : netCells_) { 271 | if (c < -1 || c >= nbCells()) { 272 | throw std::runtime_error("Invalid cell number"); 273 | } 274 | } 275 | for (int i = 0; i < nbNets(); ++i) { 276 | // At least one cell per net 277 | if (netLimits_[i] + 1 > netLimits_[i + 1]) { 278 | throw std::runtime_error("Invalid number of pins in nets"); 279 | } 280 | } 281 | if ((int)cellLimits_.size() != nbCells() + 1) { 282 | throw std::runtime_error("Cell number mismatch"); 283 | } 284 | if (cellLimits_.front() != 0 || cellLimits_.back() != (int)cellNets_.size()) { 285 | throw std::runtime_error("Pin number mismatch"); 286 | } 287 | if (cellLimits_.back() != nbPins()) { 288 | throw std::runtime_error("Pin number mismatch"); 289 | } 290 | if ((int)cellNets_.size() != nbPins()) { 291 | throw std::runtime_error("Pin number mismatch"); 292 | } 293 | assert((int)cellPinOffsets_.size() == nbPins()); 294 | for (int i = 0; i < nbCells(); ++i) { 295 | if (cellLimits_[i] > cellLimits_[i + 1]) { 296 | throw std::runtime_error("Invalid number of pins in cell"); 297 | } 298 | } 299 | for (int net : cellNets_) { 300 | if (net < 0 || net >= nbNets()) { 301 | throw std::runtime_error("Invalid net number"); 302 | } 303 | } 304 | auto minMaxPos = computeNetMinMaxPos(); 305 | for (int i = 0; i < nbNets(); ++i) { 306 | if (minMaxPos[i] != netMinMaxPos_[i]) { 307 | throw std::runtime_error("Mismatched net bound"); 308 | } 309 | } 310 | if (computeValue() != value()) { 311 | throw std::runtime_error("Wrong incremental value"); 312 | } 313 | } 314 | } // namespace coloquinte -------------------------------------------------------------------------------- /test/test_legalizer.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE ROW_LEGALIZER 2 | 3 | #include 4 | 5 | #include "place_detailed/legalizer.hpp" 6 | 7 | using namespace coloquinte; 8 | 9 | BOOST_AUTO_TEST_CASE(TestStats) { 10 | std::vector widths = {}; 11 | std::vector heights = {}; 12 | std::vector cellX = {}; 13 | std::vector cellY = {}; 14 | std::vector polarities = {}; 15 | std::vector orientations = {}; 16 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 17 | Row(0, 15, 20, 30, CellOrientation::N)}; 18 | 19 | ColoquinteParameters params; 20 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 21 | leg.check(); 22 | BOOST_CHECK_EQUAL(leg.rowHeight(), 10); 23 | } 24 | 25 | BOOST_AUTO_TEST_CASE(TestBasic1) { 26 | std::vector widths = {5}; 27 | std::vector heights = {10}; 28 | std::vector cellX = {0}; 29 | std::vector cellY = {0}; 30 | std::vector polarities = {CellRowPolarity::SAME}; 31 | std::vector orientations = {CellOrientation::N}; 32 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 33 | Row(0, 15, 20, 30, CellOrientation::N)}; 34 | 35 | ColoquinteParameters params; 36 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 37 | leg.run(params); 38 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 0); 39 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 10); 40 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::N); 41 | } 42 | 43 | BOOST_AUTO_TEST_CASE(TestBasic2) { 44 | std::vector widths = {5}; 45 | std::vector heights = {10}; 46 | std::vector cellX = {20}; 47 | std::vector cellY = {16}; 48 | std::vector polarities = {CellRowPolarity::SAME}; 49 | std::vector orientations = {CellOrientation::N}; 50 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 51 | Row(0, 15, 20, 30, CellOrientation::N)}; 52 | 53 | ColoquinteParameters params; 54 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 55 | leg.run(params); 56 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 10); 57 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 20); 58 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::N); 59 | } 60 | 61 | BOOST_AUTO_TEST_CASE(TestSameOrientation1) { 62 | std::vector widths = {5, 5}; 63 | std::vector heights = {10, 10}; 64 | std::vector cellX = {0, 0}; 65 | std::vector cellY = {0, 20}; 66 | std::vector polarities = {CellRowPolarity::SAME, 67 | CellRowPolarity::SAME}; 68 | std::vector orientations = {CellOrientation::N, 69 | CellOrientation::N}; 70 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::S), 71 | Row(0, 15, 20, 30, CellOrientation::N)}; 72 | 73 | ColoquinteParameters params; 74 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 75 | leg.run(params); 76 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 0); 77 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 10); 78 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::S); 79 | BOOST_CHECK_EQUAL(leg.cellLegalX()[1], 0); 80 | BOOST_CHECK_EQUAL(leg.cellLegalY()[1], 20); 81 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[1], CellOrientation::N); 82 | } 83 | 84 | BOOST_AUTO_TEST_CASE(TestOppositeOrientation1) { 85 | std::vector widths = {5, 5}; 86 | std::vector heights = {10, 10}; 87 | std::vector cellX = {0, 0}; 88 | std::vector cellY = {0, 20}; 89 | std::vector polarities = {CellRowPolarity::OPPOSITE, 90 | CellRowPolarity::OPPOSITE}; 91 | std::vector orientations = {CellOrientation::N, 92 | CellOrientation::N}; 93 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::S), 94 | Row(0, 15, 20, 30, CellOrientation::N)}; 95 | 96 | ColoquinteParameters params; 97 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 98 | leg.run(params); 99 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 0); 100 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 10); 101 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::FN); 102 | BOOST_CHECK_EQUAL(leg.cellLegalX()[1], 0); 103 | BOOST_CHECK_EQUAL(leg.cellLegalY()[1], 20); 104 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[1], CellOrientation::FS); 105 | } 106 | 107 | BOOST_AUTO_TEST_CASE(TestTwoRowCell1) { 108 | std::vector widths = {5}; 109 | std::vector heights = {20}; 110 | std::vector cellX = {0}; 111 | std::vector cellY = {0}; 112 | std::vector polarities = {CellRowPolarity::SAME}; 113 | std::vector orientations = {CellOrientation::N}; 114 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 115 | Row(0, 15, 20, 30, CellOrientation::S)}; 116 | 117 | ColoquinteParameters params; 118 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 119 | leg.run(params); 120 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 0); 121 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 10); 122 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::N); 123 | } 124 | 125 | BOOST_AUTO_TEST_CASE(TestTwoRowCell2) { 126 | std::vector widths = {5}; 127 | std::vector heights = {20}; 128 | std::vector cellX = {0}; 129 | std::vector cellY = {20}; 130 | std::vector polarities = {CellRowPolarity::SAME}; 131 | std::vector orientations = {CellOrientation::N}; 132 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 133 | Row(0, 15, 20, 30, CellOrientation::S)}; 134 | 135 | ColoquinteParameters params; 136 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 137 | leg.run(params); 138 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 0); 139 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 10); 140 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::N); 141 | } 142 | 143 | BOOST_AUTO_TEST_CASE(TestTwoRowCell3) { 144 | std::vector widths = {5}; 145 | std::vector heights = {20}; 146 | std::vector cellX = {0}; 147 | std::vector cellY = {20}; 148 | std::vector polarities = {CellRowPolarity::SAME}; 149 | std::vector orientations = {CellOrientation::N}; 150 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 151 | Row(0, 15, 20, 30, CellOrientation::S), 152 | Row(0, 15, 30, 40, CellOrientation::N)}; 153 | 154 | ColoquinteParameters params; 155 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 156 | leg.run(params); 157 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 0); 158 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 20); 159 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::S); 160 | } 161 | 162 | BOOST_AUTO_TEST_CASE(TestTwoRowCell4) { 163 | std::vector widths = {5}; 164 | std::vector heights = {20}; 165 | std::vector cellX = {0}; 166 | std::vector cellY = {20}; 167 | std::vector polarities = {CellRowPolarity::OPPOSITE}; 168 | std::vector orientations = {CellOrientation::N}; 169 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 170 | Row(0, 15, 20, 30, CellOrientation::S), 171 | Row(0, 15, 30, 40, CellOrientation::N)}; 172 | 173 | ColoquinteParameters params; 174 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 175 | leg.run(params); 176 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 0); 177 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 20); 178 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::FN); 179 | } 180 | 181 | BOOST_AUTO_TEST_CASE(TestTwoRowCell5) { 182 | std::vector widths = {5}; 183 | std::vector heights = {20}; 184 | std::vector cellX = {0}; 185 | std::vector cellY = {20}; 186 | std::vector polarities = {CellRowPolarity::NW}; 187 | std::vector orientations = {CellOrientation::N}; 188 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 189 | Row(0, 15, 20, 30, CellOrientation::S), 190 | Row(0, 15, 30, 40, CellOrientation::N)}; 191 | 192 | ColoquinteParameters params; 193 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 194 | leg.run(params); 195 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 0); 196 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 10); 197 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::N); 198 | } 199 | 200 | BOOST_AUTO_TEST_CASE(TestTwoRowCell6) { 201 | std::vector widths = {5}; 202 | std::vector heights = {20}; 203 | std::vector cellX = {0}; 204 | std::vector cellY = {20}; 205 | std::vector polarities = {CellRowPolarity::SE}; 206 | std::vector orientations = {CellOrientation::N}; 207 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 208 | Row(0, 15, 20, 30, CellOrientation::S), 209 | Row(0, 15, 30, 40, CellOrientation::N)}; 210 | 211 | ColoquinteParameters params; 212 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 213 | leg.run(params); 214 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 0); 215 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 20); 216 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::S); 217 | } 218 | 219 | BOOST_AUTO_TEST_CASE(TestThreeRowCell1) { 220 | std::vector widths = {5}; 221 | std::vector heights = {30}; 222 | std::vector cellX = {8}; 223 | std::vector cellY = {30}; 224 | std::vector polarities = {CellRowPolarity::SAME}; 225 | std::vector orientations = {CellOrientation::N}; 226 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 227 | Row(0, 15, 20, 30, CellOrientation::S), 228 | Row(0, 15, 30, 40, CellOrientation::N), 229 | Row(0, 15, 40, 50, CellOrientation::S)}; 230 | 231 | ColoquinteParameters params; 232 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 233 | leg.run(params); 234 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 8); 235 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 20); 236 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::S); 237 | } 238 | 239 | BOOST_AUTO_TEST_CASE(TestThreeRowCell2) { 240 | std::vector widths = {5}; 241 | std::vector heights = {30}; 242 | std::vector cellX = {8}; 243 | std::vector cellY = {0}; 244 | std::vector polarities = {CellRowPolarity::OPPOSITE}; 245 | std::vector orientations = {CellOrientation::N}; 246 | std::vector rows = {Row(0, 15, 10, 20, CellOrientation::N), 247 | Row(0, 15, 20, 30, CellOrientation::S), 248 | Row(0, 15, 30, 40, CellOrientation::N), 249 | Row(0, 15, 40, 50, CellOrientation::S)}; 250 | 251 | ColoquinteParameters params; 252 | Legalizer leg(rows, widths, heights, polarities, cellX, cellY, orientations); 253 | leg.run(params); 254 | BOOST_CHECK_EQUAL(leg.cellLegalX()[0], 8); 255 | BOOST_CHECK_EQUAL(leg.cellLegalY()[0], 10); 256 | BOOST_CHECK_EQUAL(leg.cellLegalOrientation()[0], CellOrientation::FS); 257 | } 258 | -------------------------------------------------------------------------------- /src/place_global/place_global.cpp: -------------------------------------------------------------------------------- 1 | #include "place_global.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "density_legalizer.hpp" 13 | #include "net_model.hpp" 14 | 15 | namespace coloquinte { 16 | 17 | namespace { 18 | /** 19 | * Mix two placements with a weight: 0 for the first, 1 for the second, or 20 | * in-between 21 | */ 22 | std::vector blendPlacement(const std::vector &v1, 23 | const std::vector &v2, 24 | float blending) { 25 | if (blending == 0.0f) { 26 | return v1; 27 | } 28 | if (blending == 1.0f) { 29 | return v2; 30 | } 31 | std::vector ret; 32 | assert(v1.size() == v2.size()); 33 | ret.reserve(v1.size()); 34 | for (size_t i = 0; i < v1.size(); ++i) { 35 | ret.push_back((1.0f - blending) * v1[i] + blending * v2[i]); 36 | } 37 | return ret; 38 | } 39 | } // namespace 40 | 41 | void GlobalPlacer::place(Circuit &circuit, const ColoquinteParameters ¶ms, 42 | const std::optional &callback) { 43 | params.check(); 44 | std::cout << "Global placement starting" << std::endl; 45 | auto startTime = std::chrono::steady_clock::now(); 46 | GlobalPlacer pl(circuit, params); 47 | pl.callback_ = callback; 48 | pl.run(); 49 | auto endTime = std::chrono::steady_clock::now(); 50 | std::chrono::duration duration = endTime - startTime; 51 | std::cout << std::fixed << std::setprecision(2) << "Global placement done in " 52 | << duration.count() << "s" << std::endl; 53 | pl.exportPlacement(circuit); 54 | } 55 | 56 | GlobalPlacer::GlobalPlacer(Circuit &circuit, const ColoquinteParameters ¶ms) 57 | : leg_(DensityLegalizer::fromIspdCircuit( 58 | circuit, params.global.roughLegalization.binSize, 59 | params.global.roughLegalization.sideMargin)), 60 | xtopo_(NetModel::xTopology(circuit)), 61 | ytopo_(NetModel::yTopology(circuit)), 62 | params_(params), 63 | circuit_(circuit) { 64 | circuit_.hasCellSizeUpdate_ = false; 65 | circuit_.hasNetUpdate_ = false; 66 | rgen_.seed(params_.seed); 67 | averageCellLength_ = computeAverageCellSize(); 68 | perCellPenalty_ = computePerCellPenalty(); 69 | auto rlp = params.global.roughLegalization; 70 | LegalizationModel m = rlp.costModel; 71 | DensityLegalizer::Parameters legParams; 72 | legParams.nbSteps = rlp.nbSteps; 73 | legParams.costModel = rlp.costModel; 74 | legParams.lineReoptSize = rlp.lineReoptSize; 75 | legParams.lineReoptOverlap = rlp.lineReoptOverlap; 76 | legParams.diagReoptSize = rlp.diagReoptSize; 77 | legParams.diagReoptOverlap = rlp.diagReoptOverlap; 78 | legParams.squareReoptSize = rlp.squareReoptSize; 79 | legParams.squareReoptOverlap = rlp.squareReoptOverlap; 80 | legParams.unidimensionalTransport = 81 | rlp.unidimensionalTransport && m == LegalizationModel::L1; 82 | legParams.coarseningLimit = rlp.coarseningLimit; 83 | if (m == LegalizationModel::L1 || m == LegalizationModel::L2 || 84 | m == LegalizationModel::LInf) { 85 | float dist = leg_.placementArea().width() + leg_.placementArea().height(); 86 | legParams.quadraticPenaltyFactor = rlp.quadraticPenalty / dist; 87 | } 88 | leg_.setParams(legParams); 89 | } 90 | 91 | void GlobalPlacer::exportPlacement(Circuit &circuit) const { 92 | assert(xtopo_.nbCells() == circuit.nbCells()); 93 | assert(ytopo_.nbCells() == circuit.nbCells()); 94 | assert(leg_.nbCells() == circuit.nbCells()); 95 | float w = params_.global.exportBlending; 96 | std::vector xplace = blendPlacement(xPlacementLB_, xPlacementUB_, w); 97 | std::vector yplace = blendPlacement(yPlacementLB_, yPlacementUB_, w); 98 | exportPlacement(circuit, xplace, yplace); 99 | } 100 | 101 | void GlobalPlacer::exportPlacement(Circuit &circuit, 102 | const std::vector &xplace, 103 | const std::vector &yplace) { 104 | assert((int)xplace.size() == circuit.nbCells()); 105 | assert((int)yplace.size() == circuit.nbCells()); 106 | 107 | for (int i = 0; i < circuit.nbCells(); ++i) { 108 | if (circuit.isFixed(i)) { 109 | continue; 110 | } 111 | circuit.cellX_[i] = std::round(xplace[i] - 0.5 * circuit.placedWidth(i)); 112 | circuit.cellY_[i] = std::round(yplace[i] - 0.5 * circuit.placedHeight(i)); 113 | } 114 | } 115 | 116 | float GlobalPlacer::computeAverageCellSize() const { 117 | float totalDemand = leg_.totalDemand(); 118 | float avgDemand = totalDemand == 0.0 ? 0.0 : totalDemand / leg_.nbCells(); 119 | return std::sqrt(avgDemand); 120 | } 121 | 122 | std::vector GlobalPlacer::computePerCellPenalty() const { 123 | int nbCells = leg_.nbNonEmptyCells(); 124 | float meanArea = leg_.totalDemand() / std::max(1, nbCells); 125 | std::vector ret; 126 | ret.reserve(leg_.nbCells()); 127 | 128 | for (int i = 0; i < leg_.nbCells(); ++i) { 129 | ret.push_back(std::pow(leg_.cellDemand(i) / meanArea, 130 | params_.global.penalty.areaExponent)); 131 | } 132 | return ret; 133 | } 134 | 135 | std::vector GlobalPlacer::computeIterationPerCellPenalty() { 136 | std::vector penalty = perCellPenalty_; 137 | for (float &s : penalty) { 138 | s *= penalty_; 139 | } 140 | if (params_.global.noise > 0.0) { 141 | std::uniform_real_distribution dist(0.0f, params_.global.noise); 142 | for (float &s : penalty) { 143 | s *= (1.0f + dist(rgen_)); 144 | } 145 | } 146 | return penalty; 147 | } 148 | 149 | void GlobalPlacer::run() { 150 | runInitialLB(); 151 | penalty_ = params_.global.penalty.initialValue; 152 | approximationDistance_ = initialApproximationDistance(); 153 | penaltyCutoffDistance_ = initialPenaltyCutoffDistance(); 154 | double nextPenaltyUpdateDistance = penaltyUpdateDistance(); 155 | 156 | float lb = valueLB(); 157 | float ub = std::numeric_limits::infinity(); 158 | int firstStep = params_.global.nbInitialSteps + 1; 159 | for (step_ = firstStep; step_ <= params_.global.maxNbSteps; ++step_) { 160 | std::cout << "#" << step_ << ":" << std::flush; 161 | runUB(); 162 | ub = valueUB(); 163 | std::cout << std::defaultfloat << std::setprecision(4) << "\tUB " << ub; 164 | 165 | float dist = leg_.meanDistance(); 166 | std::cout << std::fixed << std::setprecision(1) << "\tDist " << dist / averageCellLength_; 167 | std::cout << std::flush; 168 | 169 | float gap = (ub - lb) / ub; 170 | // Stop if distance or the difference between LB and UB is small enough 171 | if (gap < params_.global.gapTolerance || dist < distanceTolerance()) { 172 | std::cout << std::endl; 173 | break; 174 | } 175 | if (dist < nextPenaltyUpdateDistance) { 176 | callback(PlacementStep::PenaltyUpdate, xPlacementUB_, yPlacementUB_); 177 | nextPenaltyUpdateDistance /= params_.global.penaltyUpdateBackoff; 178 | } 179 | for (int i = 0; i < params_.global.nbStepsBeforeRoughLegalization; ++i) { 180 | if (i != 0) { 181 | std::cout << "#" << step_ << ":\t........\t........"; 182 | std::cout << std::flush; 183 | } 184 | runLB(); 185 | lb = valueLB(); 186 | std::cout << std::defaultfloat << std::setprecision(4) << "\tLB " << lb 187 | << std::endl; 188 | } 189 | penalty_ *= params_.global.penalty.updateFactor; 190 | penaltyCutoffDistance_ *= params_.global.penalty.cutoffDistanceUpdateFactor; 191 | approximationDistance_ *= 192 | params_.global.continuousModel.approximationDistanceUpdateFactor; 193 | } 194 | runUB(); 195 | } 196 | 197 | float GlobalPlacer::valueLB() const { 198 | return xtopo_.value(xPlacementLB_) + ytopo_.value(yPlacementLB_); 199 | } 200 | 201 | float GlobalPlacer::valueUB() const { 202 | return xtopo_.value(xPlacementUB_) + ytopo_.value(yPlacementUB_); 203 | } 204 | 205 | void GlobalPlacer::runInitialLB() { 206 | NetModel::Parameters params; 207 | params.netModel = params_.global.continuousModel.netModel; 208 | params.tolerance = 209 | params_.global.continuousModel.conjugateGradientErrorTolerance; 210 | params.maxNbIterations = 211 | params_.global.continuousModel.maxNbConjugateGradientSteps; 212 | xPlacementLB_ = xtopo_.solveStar(params); 213 | yPlacementLB_ = ytopo_.solveStar(params); 214 | std::cout << std::defaultfloat << std::setprecision(4) << "#0:\tLB " 215 | << valueLB() << std::endl; 216 | callback(PlacementStep::LowerBound, xPlacementLB_, yPlacementLB_); 217 | for (step_ = 1; step_ <= params_.global.nbInitialSteps; ++step_) { 218 | xPlacementLB_ = xtopo_.solve(xPlacementLB_, params); 219 | yPlacementLB_ = ytopo_.solve(yPlacementLB_, params); 220 | std::cout << std::defaultfloat << std::setprecision(4) << "#" << step_ 221 | << ":\tLB " << valueLB() << std::endl; 222 | callback(PlacementStep::LowerBound, xPlacementLB_, yPlacementLB_); 223 | } 224 | // Simplify blending solutions by having a UB immediately 225 | xPlacementUB_ = xPlacementLB_; 226 | yPlacementUB_ = yPlacementLB_; 227 | } 228 | 229 | void GlobalPlacer::runLB() { 230 | // Compute the parameters for the continuous model solver 231 | NetModel::Parameters params; 232 | params.netModel = params_.global.continuousModel.netModel; 233 | params.approximationDistance = approximationDistance_; 234 | params.penaltyCutoffDistance = penaltyCutoffDistance_; 235 | params.tolerance = 236 | params_.global.continuousModel.conjugateGradientErrorTolerance; 237 | params.maxNbIterations = 238 | params_.global.continuousModel.maxNbConjugateGradientSteps; 239 | 240 | // Compute the per-cell penalty with randomization 241 | std::vector penalty = computeIterationPerCellPenalty(); 242 | 243 | float w = params_.global.penalty.targetBlending; 244 | std::vector xTarget = blendPlacement(xPlacementLB_, xPlacementUB_, w); 245 | std::vector yTarget = blendPlacement(yPlacementLB_, yPlacementUB_, w); 246 | 247 | // Solve the continuous model (x and y independently) 248 | std::future > x = 249 | std::async(std::launch::async, &NetModel::solveWithPenalty, &xtopo_, 250 | xPlacementLB_, xTarget, penalty, params); 251 | std::future > y = 252 | std::async(std::launch::async, &NetModel::solveWithPenalty, &ytopo_, 253 | yPlacementLB_, yTarget, penalty, params); 254 | xPlacementLB_ = x.get(); 255 | yPlacementLB_ = y.get(); 256 | callback(PlacementStep::LowerBound, xPlacementLB_, yPlacementLB_); 257 | } 258 | 259 | void GlobalPlacer::runUB() { 260 | updateCellSizes(); 261 | float w = params_.global.roughLegalization.targetBlending; 262 | std::vector xTarget = blendPlacement(xPlacementLB_, xPlacementUB_, w); 263 | std::vector yTarget = blendPlacement(yPlacementLB_, yPlacementUB_, w); 264 | leg_.updateCellTargetX(xTarget); 265 | leg_.updateCellTargetY(yTarget); 266 | leg_.run(); 267 | xPlacementUB_ = leg_.spreadCoordX(xTarget); 268 | yPlacementUB_ = leg_.spreadCoordY(yTarget); 269 | callback(PlacementStep::UpperBound, xPlacementUB_, yPlacementUB_); 270 | } 271 | 272 | void GlobalPlacer::callback(PlacementStep step, 273 | const std::vector &xplace, 274 | const std::vector &yplace) { 275 | if (!callback_.has_value()) return; 276 | exportPlacement(circuit_, xplace, yplace); 277 | callback_.value()(step); 278 | } 279 | 280 | void GlobalPlacer::updateCellSizes() { 281 | if (circuit_.hasCellSizeUpdate_) { 282 | leg_.updateCellDemand(circuit_); 283 | averageCellLength_ = computeAverageCellSize(); 284 | perCellPenalty_ = computePerCellPenalty(); 285 | circuit_.hasCellSizeUpdate_ = false; 286 | } 287 | } 288 | 289 | void GlobalPlacer::updateNets() { 290 | if (circuit_.hasNetUpdate_) { 291 | xtopo_ = NetModel::xTopology(circuit_); 292 | ytopo_ = NetModel::yTopology(circuit_); 293 | circuit_.hasNetUpdate_ = false; 294 | } 295 | } 296 | } // namespace coloquinte 297 | -------------------------------------------------------------------------------- /src/place_detailed/legalizer.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "place_detailed/legalizer.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "place_detailed/abacus_legalizer.hpp" 10 | #include "place_detailed/tetris_legalizer.hpp" 11 | #include "utils/norm.hpp" 12 | 13 | namespace coloquinte { 14 | Legalizer Legalizer::fromIspdCircuit(const Circuit &circuit) { 15 | // Represent fixed cells with -1 width so they are not considered 16 | std::vector widths; 17 | std::vector heights; 18 | std::vector x; 19 | std::vector y; 20 | std::vector polarities; 21 | std::vector orient; 22 | for (int i = 0; i < circuit.nbCells(); ++i) { 23 | if (circuit.cellIsFixed_[i]) { 24 | continue; 25 | } 26 | widths.push_back(circuit.placedWidth(i)); 27 | heights.push_back(circuit.placedHeight(i)); 28 | polarities.push_back(circuit.cellRowPolarity_[i]); 29 | x.push_back(circuit.cellX_[i]); 30 | y.push_back(circuit.cellY_[i]); 31 | orient.push_back(circuit.cellOrientation_[i]); 32 | } 33 | return Legalizer(circuit.computeRows(), widths, heights, polarities, x, y, 34 | orient); 35 | } 36 | 37 | LegalizerBase::LegalizerBase( 38 | const std::vector &rows, const std::vector &width, 39 | const std::vector &height, 40 | const std::vector &polarities, 41 | const std::vector &targetX, const std::vector &targetY, 42 | const std::vector &targetOrientation) 43 | : cellWidth_(width), 44 | cellHeight_(height), 45 | cellRowPolarity_(polarities), 46 | cellTargetX_(targetX), 47 | cellTargetY_(targetY), 48 | cellTargetOrientation_(targetOrientation) { 49 | assert(width.size() == height.size()); 50 | assert(width.size() == polarities.size()); 51 | assert(width.size() == targetX.size()); 52 | assert(width.size() == targetY.size()); 53 | assert(width.size() == targetOrientation.size()); 54 | // Sort the rows 55 | rows_ = rows; 56 | std::stable_sort(rows_.begin(), rows_.end(), [](Row a, Row b) -> bool { 57 | return a.minY < b.minY || (a.minY == b.minY && a.minX < b.minX); 58 | }); 59 | cellToX_ = cellTargetX_; 60 | cellToY_ = cellTargetY_; 61 | cellToOrientation_ = cellTargetOrientation_; 62 | cellIsPlaced_.assign(width.size(), false); 63 | } 64 | 65 | void LegalizerBase::check() const { 66 | if ((int)cellWidth_.size() != nbCells()) { 67 | throw std::runtime_error("Number of cell widths does not match"); 68 | } 69 | if ((int)cellHeight_.size() != nbCells()) { 70 | throw std::runtime_error("Number of cell heights does not match"); 71 | } 72 | if ((int)cellTargetX_.size() != nbCells()) { 73 | throw std::runtime_error("Number of cell x targets does not match"); 74 | } 75 | if ((int)cellTargetY_.size() != nbCells()) { 76 | throw std::runtime_error("Number of cell y targets does not match"); 77 | } 78 | if ((int)cellTargetOrientation_.size() != nbCells()) { 79 | throw std::runtime_error( 80 | "Number of cell orientation targets does not match"); 81 | } 82 | if ((int)cellToX_.size() != nbCells()) { 83 | throw std::runtime_error("Number of cell x positions does not match"); 84 | } 85 | if ((int)cellToY_.size() != nbCells()) { 86 | throw std::runtime_error("Number of cell y positions does not match"); 87 | } 88 | if ((int)cellToOrientation_.size() != nbCells()) { 89 | throw std::runtime_error("Number of cell orientations does not match"); 90 | } 91 | for (Row r : rows_) { 92 | if (r.height() != rowHeight()) { 93 | throw std::runtime_error("Rows have different heights"); 94 | } 95 | } 96 | } 97 | 98 | void LegalizerBase::checkAllPlaced() const { 99 | for (int i = 0; i < nbCells(); ++i) { 100 | if (!isPlaced(i)) { 101 | throw std::runtime_error("Not all cells have been placed"); 102 | } 103 | } 104 | } 105 | 106 | void LegalizerBase::importLegalization(const LegalizerBase &leg, 107 | const std::vector &cells) { 108 | std::vector x = leg.cellLegalX(); 109 | std::vector y = leg.cellLegalY(); 110 | std::vector o = leg.cellLegalOrientation(); 111 | assert((int)cells.size() == leg.nbCells()); 112 | for (size_t i = 0; i < cells.size(); ++i) { 113 | int c = cells[i]; 114 | if (leg.cellIsPlaced_[i]) { 115 | cellToX_[c] = x[i]; 116 | cellToY_[c] = y[i]; 117 | cellToOrientation_[c] = o[i]; 118 | cellIsPlaced_[c] = true; 119 | } 120 | } 121 | } 122 | 123 | std::vector LegalizerBase::computeCellOrder(float weightX, 124 | float weightWidth, 125 | float weightY, 126 | float weightHeight) const { 127 | // Sort the cells by target X coordinate 128 | std::vector > sortedCells; 129 | for (int i = 0; i < nbCells(); ++i) { 130 | float val = weightX * cellTargetX_[i] + weightWidth * cellWidth_[i] + 131 | weightY * cellTargetY_[i] + weightHeight * cellHeight_[i]; 132 | sortedCells.emplace_back(val, i); 133 | } 134 | std::stable_sort(sortedCells.begin(), sortedCells.end()); 135 | std::vector cells; 136 | cells.reserve(sortedCells.size()); 137 | 138 | for (auto p : sortedCells) { 139 | cells.push_back(p.second); 140 | } 141 | return cells; 142 | } 143 | 144 | int LegalizerBase::closestRow(int y) const { 145 | auto it = std::lower_bound(rows_.begin(), rows_.end(), y, 146 | [](Rectangle r, int v) { return r.minY < v; }); 147 | if (it == rows_.end()) { 148 | return nbRows() - 1; 149 | } 150 | if (it == rows_.begin()) { 151 | return 0; 152 | } 153 | int row = it - rows_.begin(); 154 | if (row > 0 && rows_[row].minY - y > y - rows_[row - 1].minY) { 155 | return row - 1; 156 | } 157 | return row; 158 | } 159 | 160 | std::vector LegalizerBase::remainingRows() const { 161 | std::vector obstacles; 162 | for (int i = 0; i < nbCells(); ++i) { 163 | if (!isPlaced(i)) { 164 | continue; 165 | } 166 | obstacles.emplace_back(cellToX_[i], cellToX_[i] + cellWidth_[i], 167 | cellToY_[i], cellToY_[i] + cellHeight_[i]); 168 | } 169 | std::vector ret; 170 | for (Row r : rows_) { 171 | auto o = r.freespace(obstacles); 172 | ret.insert(ret.end(), o.begin(), o.end()); 173 | } 174 | return ret; 175 | } 176 | 177 | const std::vector &LegalizerBase::cellLegalX() const { return cellToX_; } 178 | 179 | const std::vector &LegalizerBase::cellLegalY() const { return cellToY_; } 180 | 181 | const std::vector &LegalizerBase::cellLegalOrientation() 182 | const { 183 | return cellToOrientation_; 184 | } 185 | 186 | void Legalizer::exportPlacement(Circuit &circuit) { 187 | std::vector cellX = cellLegalX(); 188 | std::vector cellY = cellLegalY(); 189 | std::vector cellOrient = cellLegalOrientation(); 190 | int j = 0; 191 | for (int i = 0; i < circuit.nbCells(); ++i) { 192 | if (circuit.cellIsFixed_[i]) { 193 | continue; 194 | } 195 | if (j >= nbCells()) { 196 | throw std::runtime_error("Circuit does not match legalizer for export"); 197 | } 198 | if (isPlaced(j)) { 199 | circuit.cellX_[i] = cellX[j]; 200 | circuit.cellY_[i] = cellY[j]; 201 | circuit.cellOrientation_[i] = cellOrient[j]; 202 | } 203 | ++j; 204 | } 205 | } 206 | 207 | std::vector LegalizerBase::allDistances(LegalizationModel model) const { 208 | std::vector cellX = cellLegalX(); 209 | std::vector cellY = cellLegalY(); 210 | std::vector targetX = cellTargetX_; 211 | std::vector targetY = cellTargetY_; 212 | std::vector distances; 213 | distances.reserve(nbCells()); 214 | for (int i = 0; i < nbCells(); ++i) { 215 | float dx = targetX[i] - cellX[i]; 216 | float dy = targetY[i] - cellY[i]; 217 | distances.push_back(norm(dx, dy, model)); 218 | } 219 | return distances; 220 | } 221 | 222 | float LegalizerBase::meanDistance(LegalizationModel model) const { 223 | std::vector dist = allDistances(model); 224 | float disp = 0.0f; 225 | for (int i = 0; i < nbCells(); ++i) { 226 | disp += dist[i] * cellWidth_[i] * cellHeight_[i]; 227 | } 228 | disp /= totalCellArea(); 229 | if (model == LegalizationModel::L1Squared || 230 | model == LegalizationModel::L2Squared || 231 | model == LegalizationModel::LInfSquared) { 232 | disp = std::sqrt(disp); 233 | } 234 | return disp; 235 | } 236 | 237 | float LegalizerBase::rmsDistance(LegalizationModel model) const { 238 | std::vector dist = allDistances(model); 239 | float disp = 0.0f; 240 | for (int i = 0; i < nbCells(); ++i) { 241 | disp += dist[i] * dist[i] * cellWidth_[i] * cellHeight_[i]; 242 | } 243 | disp = std::sqrt(disp / totalCellArea()); 244 | if (model == LegalizationModel::L1Squared || 245 | model == LegalizationModel::L2Squared || 246 | model == LegalizationModel::LInfSquared) { 247 | disp = std::sqrt(disp); 248 | } 249 | return disp; 250 | } 251 | 252 | float LegalizerBase::maxDistance(LegalizationModel model) const { 253 | std::vector dist = allDistances(model); 254 | return *std::max_element(dist.begin(), dist.end()); 255 | } 256 | 257 | long long LegalizerBase::totalCellArea() const { 258 | long long ret = 0; 259 | for (int c = 0; c < nbCells(); ++c) { 260 | ret += static_cast(cellWidth_[c]) * 261 | static_cast(cellHeight_[c]); 262 | } 263 | return ret; 264 | } 265 | 266 | CellOrientation LegalizerBase::getOrientation(int cell, int row) const { 267 | CellRowPolarity pol = cellRowPolarity_[cell]; 268 | CellOrientation rowOrientation = rows_[row].orientation; 269 | CellOrientation orient = cellOrientationInRow(pol, rowOrientation); 270 | if (orient == CellOrientation::UNKNOWN) { 271 | // Keep the same orientation 272 | return cellTargetOrientation_[cell]; 273 | } 274 | return orient; 275 | } 276 | 277 | int LegalizerBase::rowHeight() const { 278 | if (rows_.empty()) { 279 | throw std::runtime_error("No row present"); 280 | } 281 | return rows_.front().height(); 282 | } 283 | 284 | Legalizer::Legalizer(const std::vector &rows, 285 | const std::vector &width, 286 | const std::vector &height, 287 | const std::vector &polarities, 288 | const std::vector &targetX, 289 | const std::vector &targetY, 290 | const std::vector &targetOrientation) 291 | : LegalizerBase(rows, width, height, polarities, targetX, targetY, 292 | targetOrientation) {} 293 | 294 | void Legalizer::run(const ColoquinteParameters ¶ms) { 295 | std::vector cellOrder = computeCellOrder( 296 | 1.0, params.legalization.orderingWidth, params.legalization.orderingY, 297 | params.legalization.orderingHeight); 298 | // Run the Tetris legalizer on the macros and large cells 299 | runTetris(cellOrder); 300 | // Run the Abacus legalizer on the remaining cells 301 | runAbacus(cellOrder); 302 | // Check that everything is legalized 303 | checkAllPlaced(); 304 | } 305 | 306 | void Legalizer::runTetris(const std::vector &cells) { 307 | std::vector r = remainingRows(); 308 | std::vector w, h, x, y, remainingCells; 309 | std::vector p; 310 | std::vector o; 311 | for (int c : cells) { 312 | if (isPlaced(c)) { 313 | continue; 314 | } 315 | if (cellHeight_[c] <= rowHeight()) { 316 | continue; 317 | } 318 | remainingCells.push_back(c); 319 | w.push_back(cellWidth_[c]); 320 | h.push_back(cellHeight_[c]); 321 | p.push_back(cellRowPolarity_[c]); 322 | x.push_back(cellTargetX_[c]); 323 | y.push_back(cellTargetY_[c]); 324 | o.push_back(cellTargetOrientation_[c]); 325 | } 326 | 327 | TetrisLegalizer leg(r, w, h, p, x, y, o); 328 | leg.run(); 329 | importLegalization(leg, remainingCells); 330 | } 331 | 332 | void Legalizer::runAbacus(const std::vector &cells) { 333 | std::vector r = remainingRows(); 334 | std::vector w, h, x, y, remainingCells; 335 | std::vector p; 336 | std::vector o; 337 | for (int c : cells) { 338 | if (isPlaced(c)) { 339 | continue; 340 | } 341 | if (cellHeight_[c] != rowHeight()) { 342 | continue; 343 | } 344 | remainingCells.push_back(c); 345 | w.push_back(cellWidth_[c]); 346 | h.push_back(cellHeight_[c]); 347 | p.push_back(cellRowPolarity_[c]); 348 | x.push_back(cellTargetX_[c]); 349 | y.push_back(cellTargetY_[c]); 350 | o.push_back(cellTargetOrientation_[c]); 351 | } 352 | 353 | AbacusLegalizer leg(r, w, h, p, x, y, o); 354 | leg.run(); 355 | importLegalization(leg, remainingCells); 356 | } 357 | } // namespace coloquinte 358 | --------------------------------------------------------------------------------