Compare commits

...

73 Commits

Author SHA1 Message Date
SpaceCat-Chan
3eceee98bd fix partitioner crash when pips are unknown 2023-12-06 17:47:12 +00:00
Lofty
4b635cf68c infrastructure for pre-routing 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
0bc945fbc3 make partitioner handle arcs with no locations 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
da2892fc28 remove erroneous set_visited call 2023-12-06 17:47:12 +00:00
Lofty
eaf55a0b2e awooter: partitioner improvements 2023-12-06 17:47:12 +00:00
Lofty
181d76772a awooter: it compiles but is broken 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
8b8abc2aac tiny partitioner fixup 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
0a570f418d try to detect bad arcs in partitioner 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
000ad6d539 slightly better at recursive partitioning
still very fucked
2023-12-06 17:47:12 +00:00
SpaceCat-Chan
b38215062e majorly improve partitioner code quality 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
c22a45ae89 recursive partitioning WIP 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
3d2635f379 use approximate partitioning for binary search 2023-12-06 17:47:12 +00:00
Lofty
0bdadde9c0 awooter: refactor router
I was possessed by abstraction demons, I swear.
2023-12-06 17:47:12 +00:00
SpaceCat-Chan
a205d3b8dd fix suboptimal pathing 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
50aae5935d properly calculate final delay 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
3cec25db6c bidirectional router 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
2fe823cab9 rustfmt 2023-12-06 17:47:12 +00:00
Lofty
7c1449601d awooter: router improvements 2023-12-06 17:47:12 +00:00
Lofty
92ea4422f6 awooter: break out of pathfinder quicker 2023-12-06 17:47:12 +00:00
Lofty
0555a12974 awooter: set congestion parameters 2023-12-06 17:47:12 +00:00
SpaceCat-Chan
f1d51287ea don't route to sinks that have already been routed to 2023-12-06 17:47:12 +00:00
Lofty
7484bd34dc awooter: make congestion factors configurable 2023-12-06 17:47:10 +00:00
Lofty
d0231fc67e awooter: router correctness fixes 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
ec65ec3484 spread out partitioner pip selections 2023-12-06 17:45:20 +00:00
Lofty
3ac3a5407f awooter: (bad) router congestion heuristics 2023-12-06 17:45:20 +00:00
Lofty
55ccacdcb8 awooter: router improvements and a bugfix 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
4a51da0900 make pip selection cache 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
ccd454c1f8 split pip selection into seperate struct 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
0f9cf8775c move special casing out of partitioner 2023-12-06 17:45:20 +00:00
Lofty
c13e840e41 awooter: significantly speed up partitioner
Co-authored-by: SpaceCat-Chan <49094338+SpaceCat-Chan@users.noreply.github.com>
2023-12-06 17:45:20 +00:00
SpaceCat-Chan
838efaf2f1 make slight improvement to partitioner 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
6f940d29b9 make a theoretically correct partitioner 2023-12-06 17:45:20 +00:00
Lofty
483e42d5e7 awooter: retry partitioning if there are overused wires 2023-12-06 17:45:20 +00:00
Lofty
ac3ef9b0bb awooter: multithreaded routing 2023-12-06 17:45:20 +00:00
Lofty
290291cca6 awooter: fix multithreaded partitioning 2023-12-06 17:45:20 +00:00
Lofty
d9161d9142 awooter: single-thread pathfinding 2023-12-06 17:45:20 +00:00
Lofty
3408f532ec awooter: cargo fmt 2023-12-06 17:45:20 +00:00
Lofty
522dd3da7c awooter: partitioner improvements 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
8fd983af15 make pip exploration not cursed 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
035247ebbf add uphill pips iter 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
afaaff6b00 make find_best_pip take an arc instead of wires 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
f7fc239f75 improve partition sanity check quality of life 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
3694632aa4 use new arc type in partitioner 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
908d7ef69a only search pips inside partition bounds 2023-12-06 17:45:20 +00:00
Lofty
6213b13277 awooter: wip 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
b333080715 finalize downhill iterator 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
2ff491c717 create downhill iter wrapper 2023-12-06 17:45:20 +00:00
Lofty
bc7f7845d2 awooter: further clean up 2023-12-06 17:45:20 +00:00
Lofty
a16247d512 awooter: add Arc struct 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
319d1c4620 split partitioning code into seperate file 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
9b4c6966be split arc extraction into function 2023-12-06 17:45:20 +00:00
Lofty
2c1a9d18ed awooter: clean up unused code 2023-12-06 17:45:20 +00:00
Lofty
cb7dba6dd5 awooter: return of the progress bar 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
ae756e2d8c fix bug in split_line_over_x 2023-12-06 17:45:20 +00:00
Lofty
1cc00308dc awooter: simple time measurement 2023-12-06 17:45:20 +00:00
SpaceCat-Chan
2d3c05da85 make partitioner run in parallel over nets 2023-12-06 17:45:20 +00:00
Lofty
ba9067afaf awooter: atomics instead 2023-12-06 17:45:20 +00:00
Lofty
268906819d awooter: better locking 2023-12-06 17:45:20 +00:00
Lofty
1077cd3654 awooter: properly parallelise 2023-12-06 17:45:20 +00:00
Lofty
c00fba75e9 awooter: clamp pip indices 2023-12-06 17:45:20 +00:00
Lofty
2a18fe58c6 awooter: cargo fmt 2023-12-06 17:45:20 +00:00
Lofty
f1a4848c0f awooter: parallelise partitioning 2023-12-06 17:45:20 +00:00
Lofty
873a70ddd0 awooter: refactor and bugfix
Co-authored-by: Spacecat-Chan <49094338+SpaceCat-Chan@users.noreply.github.com>
2023-12-06 17:45:20 +00:00
Lofty
c8a2b842d6 awooter: further prettify 2023-12-06 17:45:20 +00:00
Lofty
e64cd668b2 awooter: prettify 2023-12-06 17:45:20 +00:00
Lofty
d50ab6139c awooter: net-based partitioner 2023-12-06 17:45:20 +00:00
Lofty
f64caaae24 awooter: wire storage and some cleanup 2023-12-06 17:45:20 +00:00
Lofty
4a0406420b awooter: 'better' FFI API 2023-12-06 17:45:20 +00:00
Lofty
b92a1bf52d awooter: fix an API soundness issue 2023-12-06 17:45:20 +00:00
Lofty
0cc3245d4b awooter: partition experiment code 2023-12-06 17:45:20 +00:00
Lofty
60cc6b7765 awooter: add net import code 2023-12-06 17:45:20 +00:00
Jubilee
195d344d9a Specify PROFILE when importing awooter
Otherwise CMake and Corrosion get confused about how to put things together.
2023-12-06 17:45:20 +00:00
Lofty
d5d6a07c34 awooter: highly, highly WIP 2023-12-06 17:45:20 +00:00
14 changed files with 4044 additions and 1 deletions

View File

@ -29,6 +29,7 @@ option(EXTERNAL_CHIPDB "Create build with pre-built chipdb binaries" OFF)
option(WERROR "pass -Werror to compiler (used for CI)" OFF)
option(PROFILER "Link against libprofiler" OFF)
option(USE_IPO "Compile nextpnr with IPO" ON)
option(AWOOTER "Build awooter router" ON)
if (USE_IPO)
if (ipo_supported)
@ -99,6 +100,29 @@ if (EXTERNAL_CHIPDB)
add_definitions("-DEXTERNAL_CHIPDB_ROOT=\"${EXTERNAL_CHIPDB_ROOT}\"")
endif()
if (AWOOTER)
include(FetchContent)
FetchContent_Declare(Corrosion GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git GIT_TAG v0.3.0)
FetchContent_MakeAvailable(Corrosion)
corrosion_import_crate(MANIFEST_PATH common/route/awooter/rust/Cargo.toml PROFILE "release")
# Enable cross-lang IPO if CXX is clang.
if (USE_IPO AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# rustc and clang disagree on what the triple of Linux should be:
# rustc: x86_64-unknown-linux-gnu
# clang: x86_64-pc-linux-gnu
# ask clang to use the rustc triple instead.
set(CMAKE_C_COMPILER_TARGET x86_64-unknown-linux-gnu)
set(CMAKE_CXX_COMPILER_TARGET x86_64-unknown-linux-gnu)
# If we don't do this, IPO fails. I don't know why.
corrosion_set_cargo_flags(awooter "-Zbuild-std")
corrosion_add_target_rustflags(awooter "-Clinker-plugin-lto")
message(STATUS "Enabled cross-language IPO")
endif()
endif()
set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables")
# List of families to build
@ -297,6 +321,11 @@ foreach (family ${ARCH})
install(TARGETS ${PROGRAM_PREFIX}nextpnr-${family} RUNTIME DESTINATION bin)
target_compile_definitions(${PROGRAM_PREFIX}nextpnr-${family} PRIVATE MAIN_EXECUTABLE)
# Link in awooter, if enabled.
if (AWOOTER)
target_link_libraries(${PROGRAM_PREFIX}nextpnr-${family} PRIVATE awooter)
endif()
# Add any new per-architecture targets here
if (BUILD_TESTS)
if (COVERAGE)

314
Cargo.lock generated Normal file
View File

@ -0,0 +1,314 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "awooter"
version = "0.1.0"
dependencies = [
"colored",
"enumflags2",
"indicatif",
"itertools",
"libc",
"rayon",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "colored"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
dependencies = [
"atty",
"lazy_static",
"winapi",
]
[[package]]
name = "console"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"terminal_size",
"unicode-width",
"winapi",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if",
]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "enumflags2"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb"
dependencies = [
"enumflags2_derive",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indicatif"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4295cbb7573c16d310e99e713cf9e75101eb190ab31fccd35f2d2691b4352b19"
dependencies = [
"console",
"number_prefix",
"portable-atomic",
"rayon",
"unicode-width",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "portable-atomic"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15eb2c6e362923af47e13c23ca5afb859e83d54452c55b0b9ac763b8f7c1ac16"
[[package]]
name = "proc-macro2"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
dependencies = [
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

4
Cargo.toml Normal file
View File

@ -0,0 +1,4 @@
[workspace]
members = [
"common/route/awooter/rust"
]

View File

@ -397,6 +397,9 @@ po::options_description CommandHandler::getGeneralOptions()
"enable experimental timing-driven ripup in router (deprecated; use --tmg-ripup instead)");
general.add_options()("router2-alt-weights", "use alternate router2 weights");
general.add_options()("awooter-pressure-factor", po::value<float>(), "pressure factor for awooter");
general.add_options()("awooter-history-factor", po::value<float>(), "history factor for awooter");
general.add_options()("report", po::value<std::string>(),
"write timing and utilization report in JSON format to file");
@ -536,8 +539,15 @@ void CommandHandler::setupContext(Context *ctx)
if (vm.count("tmg-ripup") || vm.count("router2-tmg-ripup"))
ctx->settings[ctx->id("router/tmg_ripup")] = true;
<<<<<<< HEAD
if (vm.count("router2-alt-weights"))
ctx->settings[ctx->id("router2/alt-weights")] = true;
=======
if (vm.count("awooter-pressure-factor"))
ctx->settings[ctx->id("awooter-pressure-factor")] = std::to_string(vm["awooter-pressure-factor"].as<float>());
if (vm.count("awooter-history-factor"))
ctx->settings[ctx->id("awooter-history-factor")] = std::to_string(vm["awooter-history-factor"].as<float>());
>>>>>>> a401db30 (awooter: make congestion factors configurable)
// Setting default values
if (ctx->settings.find(ctx->id("target_freq")) == ctx->settings.end())

329
common/route/awooter.cc Normal file
View File

@ -0,0 +1,329 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2022 Lofty <lofty@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "log.h"
#include "nextpnr.h"
namespace {
USING_NEXTPNR_NAMESPACE;
template<typename T>
uint64_t wrap(T thing) {
static_assert(sizeof(T) <= 8, "T is too big for FFI");
uint64_t b = 0;
memcpy(&b, &thing, sizeof(T));
return b;
}
BelId unwrap_bel(uint64_t bel) {
static_assert(sizeof(BelId) <= 8, "T is too big for FFI");
auto b = BelId();
memcpy(&b, &bel, sizeof(BelId));
return b;
}
PipId unwrap_pip(uint64_t pip) {
static_assert(sizeof(PipId) <= 8, "T is too big for FFI");
auto p = PipId();
memcpy(&p, &pip, sizeof(PipId));
return p;
}
WireId unwrap_wire(uint64_t wire) {
static_assert(sizeof(WireId) <= 8, "T is too big for FFI");
auto w = WireId();
memcpy(&w, &wire, sizeof(WireId));
return w;
}
}
using DownhillIter = decltype(Context(ArchArgs()).getPipsDownhill(WireId()).begin());
struct DownhillIterWrapper {
DownhillIter current;
DownhillIter end;
DownhillIterWrapper(DownhillIter begin, DownhillIter end) : current(begin), end(end) {}
};
using UphillIter = decltype(Context(ArchArgs()).getPipsUphill(WireId()).begin());
struct UphillIterWrapper {
UphillIter current;
UphillIter end;
UphillIterWrapper(UphillIter begin, UphillIter end) : current(begin), end(end) {}
};
extern "C" {
USING_NEXTPNR_NAMESPACE;
/*
DONE:
ctx->bindPip
ctx->bindWire
ctx->check
ctx->debug
ctx->estimateDelay
ctx->getDelayEpsilon
ctx->getPipDstWire
ctx->getPipSrcWire
ctx->getGridDimX
ctx->getGridDimY
ctx->id
ctx->nameOf
ctx->unbindWire
ctx->verbose
UNNECESSARY:
ctx->getDelayNS - all FFI calls go through it anyway.
TODO:
ctx->checkPipAvail
ctx->checkPipAvailForNet
ctx->checkWireAvail
ctx->getBelPinType
ctx->getBoundPipNet
ctx->getBoundWireNet
ctx->getNetinfoSinkWire
ctx->getNetinfoSinkWires
ctx->getNetinfoSourceWire
ctx->getPipDelay
ctx->getPipLocation
ctx->getPipsDownhill
ctx->getPipsUphill
ctx->getRouteBoundingBox
ctx->getWireBelPins
ctx->getWireDelay
ctx->getWires
ctx->getWireType
ctx->nameOfPip
ctx->nameOfWire
ctx->nets
ctx->nets.at
ctx->nets.size
ctx->rng64
ctx->setting<bool>
ctx->setting<float>
ctx->setting<int>
ctx->sorted_shuffle
*/
void npnr_log_info(const char *const format) { log_info("%s", format); }
void npnr_log_error(const char *const format) { log_error("%s", format); }
uint64_t npnr_belid_null() { return wrap(BelId()); }
uint64_t npnr_wireid_null() { return wrap(WireId()); }
uint64_t npnr_pipid_null() { return wrap(PipId()); }
int npnr_context_get_grid_dim_x(const Context *const ctx) { return ctx->getGridDimX(); }
int npnr_context_get_grid_dim_y(const Context *const ctx) { return ctx->getGridDimY(); }
void npnr_context_bind_bel(Context *ctx, uint64_t bel, CellInfo* cell, PlaceStrength strength) { return ctx->bindBel(unwrap_bel(bel), cell, strength); }
void npnr_context_unbind_bel(Context *ctx, uint64_t bel) { return ctx->unbindBel(unwrap_bel(bel)); }
bool npnr_context_check_bel_avail(Context *const ctx, uint64_t bel) { return ctx->checkBelAvail(unwrap_bel(bel)); }
void npnr_context_bind_wire(Context *ctx, uint64_t wire, NetInfo* net, PlaceStrength strength) { ctx->bindWire(unwrap_wire(wire), net, strength); }
void npnr_context_unbind_wire(Context *ctx, uint64_t wire) { ctx->unbindWire(unwrap_wire(wire)); }
void npnr_context_bind_pip(Context *ctx, uint64_t pip, NetInfo* net, PlaceStrength strength) { ctx->bindPip(unwrap_pip(pip), net, strength); }
void npnr_context_unbind_pip(Context *ctx, uint64_t pip) { ctx->unbindPip(unwrap_pip(pip)); }
uint64_t npnr_context_get_pip_src_wire(const Context *const ctx, uint64_t pip) { return wrap(ctx->getPipSrcWire(unwrap_pip(pip))); }
uint64_t npnr_context_get_pip_dst_wire(const Context *const ctx, uint64_t pip) { return wrap(ctx->getPipDstWire(unwrap_pip(pip))); }
float npnr_context_estimate_delay(const Context *const ctx, uint64_t src, uint64_t dst) { return ctx->getDelayNS(ctx->estimateDelay(unwrap_wire(src), unwrap_wire(dst))); }
float npnr_context_get_pip_delay(const Context *const ctx, uint64_t pip) { return ctx->getDelayNS(ctx->getPipDelay(unwrap_pip(pip)).maxDelay()); }
float npnr_context_get_wire_delay(const Context *const ctx, uint64_t wire) { return ctx->getDelayNS(ctx->getWireDelay(unwrap_wire(wire)).maxDelay()); }
float npnr_context_delay_epsilon(const Context *const ctx) { return ctx->getDelayNS(ctx->getDelayEpsilon()); }
Loc npnr_context_get_pip_location(const Context *const ctx, uint64_t pip) { return ctx->getPipLocation(unwrap_pip(pip)); }
bool npnr_context_check_pip_avail_for_net(const Context *const ctx, uint64_t pip, NetInfo *net) { return ctx->checkPipAvailForNet(unwrap_pip(pip), net); }
// This method's in C++ temporarily, while I figure out some better way of getting a pip iterator.
Loc npnr_context_get_pip_direction(const Context *const ctx, uint64_t _pip) {
auto pip = unwrap_pip(_pip);
auto src_loc = Loc{};
auto dst_loc = Loc{};
auto uh_pips = 0;
for (auto uh : ctx->getPipsUphill(ctx->getPipSrcWire(pip))) {
auto loc = ctx->getPipLocation(uh);
src_loc.x += loc.x;
src_loc.y += loc.y;
uh_pips++;
}
if (uh_pips > 1) {
src_loc.x /= uh_pips;
src_loc.y /= uh_pips;
}
auto dh_pips = 0;
for (auto dh : ctx->getPipsDownhill(ctx->getPipDstWire(pip))) {
auto loc = ctx->getPipLocation(dh);
dst_loc.x += loc.x;
dst_loc.y += loc.y;
dh_pips++;
}
if (dh_pips > 1) {
dst_loc.x /= dh_pips;
dst_loc.y /= dh_pips;
}
dst_loc.x -= src_loc.x;
dst_loc.y -= src_loc.y;
return dst_loc;
}
uint64_t npnr_context_get_pips_leak(const Context *const ctx, PipId **pips) {
auto pip_vec = std::vector<PipId>{};
for (auto pip : ctx->getPips()) {
pip_vec.push_back(pip);
}
pip_vec.shrink_to_fit();
auto size = pip_vec.size();
*pips = pip_vec.data();
auto dummy = std::vector<PipId>{};
// Yes, by memcpying over `pip_vec` we leak memory.
std::memcpy(&pip_vec, &dummy, sizeof(dummy));
return size;
}
uint64_t npnr_context_get_wires_leak(const Context *const ctx, WireId **wires) {
auto wire_vec = std::vector<WireId>{};
for (auto wire : ctx->getWires()) {
wire_vec.push_back(wire);
}
wire_vec.shrink_to_fit();
auto size = wire_vec.size();
*wires = wire_vec.data();
auto dummy = std::vector<WireId>{};
// Yes, by memcpying over `wire_vec` we leak memory.
std::memcpy(&wire_vec, &dummy, sizeof(dummy));
return size;
}
void npnr_context_check(const Context *const ctx) { ctx->check(); }
bool npnr_context_debug(const Context *const ctx) { return ctx->debug; }
int npnr_context_id(const Context *const ctx, const char *const str) { return ctx->id(str).hash(); }
const char *npnr_context_name_of(const Context *const ctx, IdString str) { return ctx->nameOf(str); }
const char *npnr_context_name_of_pip(const Context *const ctx, uint64_t pip) { return ctx->nameOfPip(unwrap_pip(pip)); }
const char *npnr_context_name_of_wire(const Context *const ctx, uint64_t wire) { return ctx->nameOfWire(unwrap_wire(wire)); }
bool npnr_context_verbose(const Context *const ctx) { return ctx->verbose; }
uint64_t npnr_context_get_netinfo_source_wire(const Context *const ctx, const NetInfo *const net) { return wrap(ctx->getNetinfoSourceWire(net)); }
uint64_t npnr_context_get_netinfo_sink_wire(const Context *const ctx, const NetInfo *const net, const PortRef *const sink, uint32_t n) { return wrap(ctx->getNetinfoSinkWire(net, *sink, n)); }
uint32_t npnr_context_nets_leak(const Context *const ctx, int **names, NetInfo ***nets) {
auto name_vec = std::vector<int>{};
auto nets_vec = std::vector<NetInfo*>{};
for (auto& item : ctx->nets) {
name_vec.push_back(item.first.hash());
nets_vec.push_back(item.second.get());
}
name_vec.shrink_to_fit();
nets_vec.shrink_to_fit();
auto size = name_vec.size();
*names = name_vec.data();
*nets = nets_vec.data();
// Yes, by memcpying over `name_vec` and `nets_vec` we leak memory.
auto dummy1 = std::vector<int>{};
auto dummy2 = std::vector<NetInfo*>{};
std::memcpy(&name_vec, &dummy1, sizeof(dummy1));
std::memcpy(&nets_vec, &dummy2, sizeof(dummy2));
return size;
}
DownhillIterWrapper *npnr_context_get_pips_downhill(Context *ctx, uint64_t wire_id) {
auto wire = unwrap_wire(wire_id);
auto range = ctx->getPipsDownhill(wire);
return new DownhillIterWrapper(range.begin(), range.end());
}
void npnr_delete_downhill_iter(DownhillIterWrapper *iter) {
delete iter;
}
UphillIterWrapper *npnr_context_get_pips_uphill(Context *ctx, uint64_t wire_id) {
auto wire = unwrap_wire(wire_id);
auto range = ctx->getPipsUphill(wire);
return new UphillIterWrapper(range.begin(), range.end());
}
void npnr_delete_uphill_iter(UphillIterWrapper *iter) {
delete iter;
}
PortRef* npnr_netinfo_driver(NetInfo *const net) {
if (net == nullptr) {
return nullptr;
}
return &net->driver;
}
uint32_t npnr_netinfo_users_leak(NetInfo *const net, PortRef ***users) {
auto x = std::vector<PortRef*>{};
for (auto& item : net->users) {
x.push_back(&item);
}
x.shrink_to_fit();
*users = x.data();
auto size = x.size();
// Yes, by memcpying over `x` we leak memory.
auto dummy = std::vector<PortRef*>{};
std::memcpy(&x, &dummy, sizeof(dummy));
return size;
}
#ifdef ARCH_ECP5
bool npnr_netinfo_is_global(NetInfo *const net) { return net->is_global; }
#else
bool npnr_netinfo_is_global(NetInfo *const net) { return false; }
#endif
int32_t npnr_netinfo_udata(NetInfo *const net) { return net->udata; }
void npnr_netinfo_udata_set(NetInfo *const net, int32_t value) { net->udata = value; }
CellInfo* npnr_portref_cell(const PortRef *const port) { return port->cell; }
Loc npnr_cellinfo_get_location(const CellInfo *const info) { return info->getLocation(); }
void npnr_inc_downhill_iter(DownhillIterWrapper *iter) {
++iter->current;
}
uint64_t npnr_deref_downhill_iter(DownhillIterWrapper *iter) {
return wrap(*iter->current);
}
bool npnr_is_downhill_iter_done(DownhillIterWrapper *iter) {
return !(iter->current != iter->end);
}
void npnr_inc_uphill_iter(UphillIterWrapper *iter) {
++iter->current;
}
uint64_t npnr_deref_uphill_iter(UphillIterWrapper *iter) {
return wrap(*iter->current);
}
bool npnr_is_uphill_iter_done(UphillIterWrapper *iter) {
return !(iter->current != iter->end);
}
extern bool npnr_router_awooter(Context *ctx, float pressure, float history);
}
NEXTPNR_NAMESPACE_BEGIN
bool router_awooter(Context *ctx) {
auto pressure = ctx->setting<float>("awooter-pressure-factor", 0.05);
auto history = ctx->setting<float>("awooter-history-factor", 0.04);
log_info("Running Awooter...\n");
auto result = npnr_router_awooter(ctx, pressure, history);
log_info("Router returned: %d\n", result);
NPNR_ASSERT_FALSE_STR("I haven't implemented anything beyond this yet.");
return result;
}
NEXTPNR_NAMESPACE_END

30
common/route/awooter.h Normal file
View File

@ -0,0 +1,30 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2022 Lofty <lofty@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef AWOOTER_H
#define AWOOTER_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
extern bool router_awooter(Context* ctx);
NEXTPNR_NAMESPACE_END
#endif // AWOOTER_H

7
common/route/awooter/rust/Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "rust"
version = "0.1.0"

View File

@ -0,0 +1,19 @@
[package]
name = "awooter"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "awooter"
path = "src/lib.rs"
crate-type = ["staticlib"]
[dependencies]
libc = "0.2"
indicatif = { version = "0.17", features = ["rayon"] }
rayon = "1.6"
colored = "2"
itertools = "0.10.5"
enumflags2 = "0.7.5"

View File

@ -0,0 +1,304 @@
use std::{ptr::NonNull, time::Instant};
use colored::Colorize;
use indicatif::MultiProgress;
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use crate::partition::Coord;
#[macro_use]
mod npnr;
mod partition;
mod route;
#[no_mangle]
pub extern "C" fn npnr_router_awooter(
ctx: Option<NonNull<npnr::Context>>,
pressure: f32,
history: f32,
) -> bool {
let ctx: &mut npnr::Context = unsafe { ctx.expect("non-null context").as_mut() };
route(ctx, pressure, history)
/*std::panic::catch_unwind(move || {
let ctx: &mut npnr::Context = unsafe { ctx.expect("non-null context").as_mut() };
route(ctx)
})
.unwrap_or_else(|x| {
if let Ok(x) = x.downcast::<String>() {
log_error!("caught panic: {}", x);
}
false
})*/
}
fn extract_arcs_from_nets(ctx: &npnr::Context, nets: &npnr::Nets) -> Vec<route::Arc> {
let mut arcs = vec![];
for (name, net) in nets.to_vec().iter() {
let net = unsafe { net.as_mut().unwrap() };
let str = ctx.name_of(**name).to_str().unwrap().to_string();
let verbose = false; //str == "soc0.processor.with_fpu.fpu_0.fpu_multiply_0.rin_CCU2C_S0_4$CCU2_FCI_INT";
if verbose {
dbg!(str, net.is_global());
}
if net.is_global() {
continue;
}
let port_ref = net.driver();
let port_ref = unsafe { port_ref.as_ref().unwrap() };
if let Some(cell) = port_ref.cell() {
let source = cell.location();
let source_wire = ctx.source_wire(net);
for sink_ref in nets.users_by_name(**name).unwrap().iter() {
let sink = sink_ref.cell().unwrap();
let sink = sink.location();
for sink_wire in ctx.sink_wires(net, *sink_ref) {
let arc = route::Arc::new(
source_wire,
Some(source),
sink_wire,
Some(sink),
net.index(),
nets.name_from_index(net.index()),
);
if !arcs.contains(&arc) && arc.source_wire() != arc.sink_wire()
{
arcs.push(arc);
}
if verbose {
let source_wire = ctx.name_of_wire(source_wire);
let sink_wire = ctx.name_of_wire(sink_wire);
dbg!(source_wire, sink_wire, net.index().into_inner());
}
}
}
}
}
arcs
}
fn route(ctx: &mut npnr::Context, pressure: f32, history: f32) -> bool {
log_info!(
"{}{}{}{}{}{} from Rust!\n",
"A".red(),
"w".green(),
"o".yellow(),
"o".blue(),
"o".magenta(),
"o".cyan()
);
log_info!(
"Running on a {}x{} grid\n",
ctx.grid_dim_x().to_string().bold(),
ctx.grid_dim_y().to_string().bold(),
);
let wires = ctx.wires_leaking();
log_info!("Found {} wires\n", wires.len().to_string().bold());
let pips = ctx.pips_leaking();
log_info!("Found {} pips\n", pips.len().to_string().bold());
let nets = npnr::Nets::new(ctx);
let nets_str = nets.len().to_string();
log_info!("Found {} nets\n", nets_str.bold());
let mut count = 0;
for (&name, net) in nets.to_vec().iter() {
let _src = ctx.source_wire(**net);
let net = unsafe { net.as_mut().unwrap() };
let users = nets.users_by_name(name).unwrap().iter();
for user in users {
count += ctx.sink_wires(net, *user).len();
}
}
log_info!("Found {} arcs\n", count.to_string().bold());
let binding = nets.to_vec();
let (name, net) = binding
.iter()
.max_by_key(|(name, net)| {
let net = unsafe { net.as_mut().unwrap() };
if net.is_global() {
0
} else {
nets.users_by_name(**name)
.unwrap()
.iter()
.fold(0, |acc, sink| acc + ctx.sink_wires(net, *sink).len())
}
})
.unwrap();
let net = unsafe { net.as_mut().unwrap() };
let count = nets
.users_by_name(**name)
.unwrap()
.iter()
.fold(0, |acc, sink| acc + ctx.sink_wires(net, *sink).len())
.to_string();
log_info!(
"Highest non-global fanout net is {}\n",
ctx.name_of(**name).to_str().unwrap().bold()
);
log_info!(" with {} arcs\n", count.bold());
let mut x0 = 0;
let mut y0 = 0;
let mut x1 = 0;
let mut y1 = 0;
for sink in nets.users_by_name(**name).unwrap().iter() {
let cell = sink.cell().unwrap().location();
x0 = x0.min(cell.x);
y0 = y0.min(cell.y);
x1 = x1.max(cell.x);
y1 = y1.max(cell.y);
}
let coords_min = format!("({}, {})", x0, y0);
let coords_max = format!("({}, {})", x1, y1);
log_info!(
" which spans from {} to {}\n",
coords_min.bold(),
coords_max.bold()
);
log_info!(
"rayon reports {} threads available\n",
rayon::current_num_threads().to_string().bold()
);
let start = Instant::now();
let arcs = extract_arcs_from_nets(ctx, &nets);
let router = route::Router::new(&nets, wires, pressure, history);
let progress = MultiProgress::new();
let mut thread = route::RouterThread::new(
Coord::new(0, 0),
Coord::new(ctx.grid_dim_x(), ctx.grid_dim_y()),
&arcs,
"pre-routing",
&progress
);
let arcs = router.find_general_routing(ctx, &nets, &mut thread);
let mut special_arcs = vec![];
let mut partitionable_arcs = Vec::with_capacity(arcs.len());
for arc in arcs {
let src_name = ctx.name_of_wire(arc.source_wire());
let dst_name = ctx.name_of_wire(arc.sink_wire());
if src_name.contains("FCO_SLICE")
|| src_name.contains("Q6_SLICE")
|| src_name.contains('J')
|| src_name.contains("DDR")
|| dst_name.contains("DDR")
|| dst_name.contains("X126/Y20/PADDOD_PIO")
{
special_arcs.push(arc);
} else {
partitionable_arcs.push(arc);
}
}
log_info!(
" {} arcs special-cased\n",
special_arcs.len().to_string().bold()
);
let mut partitions = vec![(
Coord::new(0, 0),
Coord::new(ctx.grid_dim_x(), ctx.grid_dim_y()),
partitionable_arcs,
String::from(""),
)];
for _ in 0..0 {
let mut new_partitions = Vec::with_capacity(partitions.len() * 4);
for (min, max, partition, name) in &partitions {
log_info!("partition {}:\n", name);
let (x_part, y_part, ne, se, sw, nw, special) =
partition::find_partition_point_and_sanity_check(
ctx, &nets, partition, pips, min.x, max.x, min.y, max.y,
);
special_arcs.extend(special.into_iter());
new_partitions.push((
Coord::new(x_part, min.y),
Coord::new(max.x, y_part),
se,
format!("{}_SE", name),
));
new_partitions.push((Coord::new(x_part, y_part), *max, sw, format!("{}_SW", name)));
new_partitions.push((*min, Coord::new(x_part, y_part), ne, format!("{}_NE", name)));
new_partitions.push((
Coord::new(min.x, y_part),
Coord::new(x_part, max.y),
nw,
format!("{}_NW", name),
));
}
partitions = new_partitions;
}
let time = format!("{:.2}", (Instant::now() - start).as_secs_f32());
log_info!("Partitioning took {}s\n", time.bold());
log_info!(
"now {} arcs special-cased\n",
special_arcs.len().to_string().bold()
);
log_info!(
"Using pressure factor {} and history factor {}\n",
pressure,
history
);
let start = Instant::now();
log_info!("Routing partitioned arcs\n");
let progress = MultiProgress::new();
let router = route::Router::new(&nets, wires, pressure, history);
partitions
.par_iter()
.for_each(|(box_ne, box_sw, arcs, id)| {
let mut thread = route::RouterThread::new(*box_ne, *box_sw, arcs, id, &progress);
router.route(ctx, &nets, &mut thread);
});
log_info!("Routing miscellaneous arcs\n");
let mut thread = route::RouterThread::new(
Coord::new(0, 0),
Coord::new(ctx.grid_dim_x(), ctx.grid_dim_y()),
&special_arcs,
"MISC",
&progress,
);
router.route(ctx, &nets, &mut thread);
let time = format!("{:.2}", (Instant::now() - start).as_secs_f32());
log_info!("Routing took {}s\n", time.bold());
//let mut router = route::Router::new(Coord::new(0, 0), Coord::new(x_part, y_part));
/*log_info!("=== level 2 NE:\n");
let _ = find_partition_point(&ne, x_start, x, y_start, y);
log_info!("=== level 2 SE:\n");
let _ = find_partition_point(&se, x, x_finish, y_start, y);
log_info!("=== level 2 SW:\n");
let _ = find_partition_point(&sw, x, x_finish, y, y_finish);
log_info!("=== level 2 NW:\n");
let _ = find_partition_point(&nw, x_start, x, y, y_finish);*/
true
}

View File

@ -0,0 +1,586 @@
use core::slice;
use std::{collections::HashMap, ffi::CStr, marker::PhantomData, sync::Mutex};
use libc::c_char;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum PlaceStrength {
None = 0,
Weak = 1,
Strong = 2,
Placer = 3,
Fixed = 4,
Locked = 5,
User = 6,
}
#[repr(C)]
pub struct CellInfo {
private: [u8; 0],
}
impl CellInfo {
pub fn location(&self) -> Loc {
unsafe { npnr_cellinfo_get_location(self) }
}
}
#[repr(C)]
pub struct NetInfo {
private: [u8; 0],
}
impl NetInfo {
pub fn driver(&mut self) -> *mut PortRef {
unsafe { npnr_netinfo_driver(self) }
}
pub fn is_global(&self) -> bool {
unsafe { npnr_netinfo_is_global(self) }
}
pub fn index(&self) -> NetIndex {
unsafe { npnr_netinfo_udata(self) }
}
}
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct NetIndex(i32);
impl NetIndex {
pub fn into_inner(self) -> i32 {
self.0
}
}
#[repr(C)]
pub struct PortRef {
private: [u8; 0],
}
impl PortRef {
pub fn cell(&self) -> Option<&CellInfo> {
// SAFETY: handing out &s is safe when we have &self.
unsafe { npnr_portref_cell(self).as_ref() }
}
pub fn cell_mut(&mut self) -> Option<&mut CellInfo> {
// SAFETY: handing out &mut is safe when we have &mut self
// as getting multiple &mut CellInfo would require multiple &mut PortRef.
unsafe { npnr_portref_cell(self).as_mut() }
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct IdString(libc::c_int);
/// A type representing a bel name.
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct BelId(u64);
impl BelId {
/// Return a sentinel value that represents an invalid bel.
pub fn null() -> Self {
// SAFETY: BelId() has no safety requirements.
unsafe { npnr_belid_null() }
}
/// Check if this bel is invalid.
pub fn is_null(self) -> bool {
self == Self::null()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct PipId(u64);
impl PipId {
pub fn null() -> Self {
unsafe { npnr_pipid_null() }
}
}
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct WireId(u64);
impl WireId {
/// Return a sentinel value that represents an invalid wire.
pub fn null() -> Self {
// SAFETY: WireId() has no safety requirements.
unsafe { npnr_wireid_null() }
}
/// Check if this wire is invalid.
pub fn is_null(self) -> bool {
self == Self::null()
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Loc {
pub x: libc::c_int,
pub y: libc::c_int,
pub z: libc::c_int,
}
impl From<(i32, i32)> for Loc {
fn from(pos: (i32, i32)) -> Self {
Self {
x: pos.0,
y: pos.1,
z: 0,
}
}
}
#[repr(C)]
pub struct Context {
_private: [u8; 0],
}
impl Context {
/// Get grid X dimension. All bels and pips must have X coordinates in the range `0 .. getGridDimX()-1` (inclusive).
pub fn grid_dim_x(&self) -> i32 {
unsafe { npnr_context_get_grid_dim_x(self) }
}
/// Get grid Y dimension. All bels and pips must have Y coordinates in the range `0 .. getGridDimY()-1` (inclusive).
pub fn grid_dim_y(&self) -> i32 {
unsafe { npnr_context_get_grid_dim_y(self) }
}
/// Bind a given bel to a given cell with the given strength.
pub fn bind_bel(&mut self, bel: BelId, cell: *mut CellInfo, strength: PlaceStrength) {
unsafe { npnr_context_bind_bel(self, bel, cell, strength) }
}
/// Unbind a bel.
pub fn unbind_bel(&mut self, bel: BelId) {
unsafe { npnr_context_unbind_bel(self, bel) }
}
/// Returns true if the bel is available. A bel can be unavailable because it is bound, or because it is exclusive to some other resource that is bound.
pub fn check_bel_avail(&self, bel: BelId) -> bool {
unsafe { npnr_context_check_bel_avail(self, bel) }
}
/// Return the cell the given bel is bound to, or nullptr if the bel is not bound.
pub fn bound_bel_cell(&self, bel: BelId) -> Option<&CellInfo> {
unsafe { npnr_context_get_bound_bel_cell(self, bel).as_ref() }
}
/// Bind a wire to a net. This method must be used when binding a wire that is driven by a bel pin. Use bindPip() when binding a wire that is driven by a pip.
pub fn bind_wire(&mut self, wire: WireId, net: *mut NetInfo, strength: PlaceStrength) {
unsafe { npnr_context_bind_wire(self, wire, net, strength) }
}
/// Unbind a wire. For wires that are driven by a pip, this will also unbind the driving pip.
pub fn unbind_wire(&mut self, wire: WireId) {
unsafe { npnr_context_unbind_wire(self, wire) }
}
/// Bid a pip to a net. This also bind the destination wire of that pip.
pub fn bind_pip(&mut self, pip: PipId, net: *mut NetInfo, strength: PlaceStrength) {
unsafe { npnr_context_bind_pip(self, pip, net, strength) }
}
/// Unbind a pip and the wire driven by that pip.
pub fn unbind_pip(&mut self, pip: PipId) {
unsafe { npnr_context_unbind_pip(self, pip) }
}
/// Get the source wire for a pip.
pub fn pip_src_wire(&self, pip: PipId) -> WireId {
unsafe { npnr_context_get_pip_src_wire(self, pip) }
}
/// Get the destination wire for a pip.
pub fn pip_dst_wire(&self, pip: PipId) -> WireId {
unsafe { npnr_context_get_pip_dst_wire(self, pip) }
}
// TODO: Should this be a Duration? Does that even make sense?
pub fn estimate_delay(&self, src: WireId, dst: WireId) -> f32 {
unsafe { npnr_context_estimate_delay(self, src, dst) }
}
pub fn pip_delay(&self, pip: PipId) -> f32 {
unsafe { npnr_context_get_pip_delay(self, pip) }
}
pub fn wire_delay(&self, wire: WireId) -> f32 {
unsafe { npnr_context_get_wire_delay(self, wire) }
}
pub fn delay_epsilon(&self) -> f32 {
unsafe { npnr_context_delay_epsilon(self) }
}
pub fn source_wire(&self, net: *const NetInfo) -> WireId {
unsafe { npnr_context_get_netinfo_source_wire(self, net) }
}
pub fn sink_wires(&self, net: *const NetInfo, sink: *const PortRef) -> Vec<WireId> {
let mut v = Vec::new();
let mut n = 0;
loop {
let wire = unsafe { npnr_context_get_netinfo_sink_wire(self, net, sink, n) };
if wire.is_null() {
break;
}
n += 1;
v.push(wire);
}
v
}
pub fn wires_leaking(&self) -> &[WireId] {
let mut wires = std::ptr::null_mut();
let len = unsafe { npnr_context_get_wires_leak(self, &mut wires as *mut *mut WireId) };
unsafe { std::slice::from_raw_parts(wires, len as usize) }
}
pub fn pips_leaking(&self) -> &[PipId] {
let mut pips = std::ptr::null_mut();
let len = unsafe { npnr_context_get_pips_leak(self, &mut pips as *mut *mut PipId) };
unsafe { std::slice::from_raw_parts(pips, len as usize) }
}
pub fn get_downhill_pips(&self, wire: WireId) -> DownhillPipsIter {
let iter = unsafe { npnr_context_get_pips_downhill(self, wire) };
DownhillPipsIter {
iter,
phantom_data: Default::default(),
}
}
pub fn get_uphill_pips(&self, wire: WireId) -> UphillPipsIter {
let iter = unsafe { npnr_context_get_pips_uphill(self, wire) };
UphillPipsIter {
iter,
phantom_data: Default::default(),
}
}
pub fn pip_location(&self, pip: PipId) -> Loc {
unsafe { npnr_context_get_pip_location(self, pip) }
}
pub fn pip_direction(&self, pip: PipId) -> Loc {
unsafe { npnr_context_get_pip_direction(self, pip) }
}
pub fn pip_avail_for_net(&self, pip: PipId, net: *mut NetInfo) -> bool {
unsafe { npnr_context_check_pip_avail_for_net(self, pip, net) }
}
pub fn check(&self) {
unsafe { npnr_context_check(self) }
}
pub fn debug(&self) -> bool {
unsafe { npnr_context_debug(self) }
}
pub fn id(&self, s: &str) -> IdString {
let s = std::ffi::CString::new(s).unwrap();
unsafe { npnr_context_id(self, s.as_ptr()) }
}
pub fn name_of(&self, s: IdString) -> &CStr {
unsafe { CStr::from_ptr(npnr_context_name_of(self, s)) }
}
pub fn name_of_pip(&self, pip: PipId) -> &CStr {
unsafe { CStr::from_ptr(npnr_context_name_of_pip(self, pip)) }
}
pub fn name_of_wire(&self, wire: WireId) -> &str {
static MUTEX: Mutex<()> = Mutex::new(());
let _lock = MUTEX.lock().unwrap();
unsafe { CStr::from_ptr(npnr_context_name_of_wire(self, wire)) }.to_str().unwrap()
}
pub fn verbose(&self) -> bool {
unsafe { npnr_context_verbose(self) }
}
}
extern "C" {
pub fn npnr_log_info(format: *const c_char);
pub fn npnr_log_error(format: *const c_char);
fn npnr_belid_null() -> BelId;
fn npnr_wireid_null() -> WireId;
fn npnr_pipid_null() -> PipId;
fn npnr_context_get_grid_dim_x(ctx: *const Context) -> libc::c_int;
fn npnr_context_get_grid_dim_y(ctx: *const Context) -> libc::c_int;
fn npnr_context_bind_bel(
ctx: *mut Context,
bel: BelId,
cell: *mut CellInfo,
strength: PlaceStrength,
);
fn npnr_context_unbind_bel(ctx: *mut Context, bel: BelId);
fn npnr_context_check_bel_avail(ctx: *const Context, bel: BelId) -> bool;
fn npnr_context_get_bound_bel_cell(ctx: *const Context, bel: BelId) -> *const CellInfo;
fn npnr_context_bind_wire(
ctx: *mut Context,
wire: WireId,
net: *mut NetInfo,
strength: PlaceStrength,
);
fn npnr_context_unbind_wire(ctx: *mut Context, wire: WireId);
fn npnr_context_bind_pip(
ctx: *mut Context,
pip: PipId,
net: *mut NetInfo,
strength: PlaceStrength,
);
fn npnr_context_unbind_pip(ctx: *mut Context, pip: PipId);
fn npnr_context_get_pip_src_wire(ctx: *const Context, pip: PipId) -> WireId;
fn npnr_context_get_pip_dst_wire(ctx: *const Context, pip: PipId) -> WireId;
fn npnr_context_estimate_delay(ctx: *const Context, src: WireId, dst: WireId) -> f32;
fn npnr_context_delay_epsilon(ctx: *const Context) -> f32;
fn npnr_context_get_pip_delay(ctx: *const Context, pip: PipId) -> f32;
fn npnr_context_get_wire_delay(ctx: *const Context, wire: WireId) -> f32;
fn npnr_context_get_wires_leak(ctx: *const Context, wires: *mut *mut WireId) -> u64;
fn npnr_context_get_pips_leak(ctx: *const Context, pips: *mut *mut PipId) -> u64;
fn npnr_context_get_pip_location(ctx: *const Context, pip: PipId) -> Loc;
fn npnr_context_get_pip_direction(ctx: *const Context, pip: PipId) -> Loc;
fn npnr_context_check_pip_avail_for_net(
ctx: *const Context,
pip: PipId,
net: *const NetInfo,
) -> bool;
fn npnr_context_check(ctx: *const Context);
fn npnr_context_debug(ctx: *const Context) -> bool;
fn npnr_context_id(ctx: *const Context, s: *const c_char) -> IdString;
fn npnr_context_name_of(ctx: *const Context, s: IdString) -> *const libc::c_char;
fn npnr_context_name_of_pip(ctx: *const Context, pip: PipId) -> *const libc::c_char;
fn npnr_context_name_of_wire(ctx: *const Context, wire: WireId) -> *const libc::c_char;
fn npnr_context_verbose(ctx: *const Context) -> bool;
fn npnr_context_get_netinfo_source_wire(ctx: *const Context, net: *const NetInfo) -> WireId;
fn npnr_context_get_netinfo_sink_wire(
ctx: *const Context,
net: *const NetInfo,
sink: *const PortRef,
n: u32,
) -> WireId;
fn npnr_context_nets_leak(
ctx: *const Context,
names: *mut *mut libc::c_int,
nets: *mut *mut *mut NetInfo,
) -> u32;
fn npnr_context_get_pips_downhill(ctx: *const Context, wire: WireId) -> *mut RawDownhillIter;
fn npnr_delete_downhill_iter(iter: *mut RawDownhillIter);
fn npnr_context_get_pips_uphill(ctx: *const Context, wire: WireId) -> *mut RawUphillIter;
fn npnr_delete_uphill_iter(iter: *mut RawUphillIter);
fn npnr_netinfo_driver(net: *mut NetInfo) -> *mut PortRef;
fn npnr_netinfo_users_leak(net: *mut NetInfo, users: *mut *mut *mut PortRef) -> u32;
fn npnr_netinfo_is_global(net: *const NetInfo) -> bool;
fn npnr_netinfo_udata(net: *const NetInfo) -> NetIndex;
fn npnr_netinfo_udata_set(net: *mut NetInfo, value: NetIndex);
fn npnr_portref_cell(port: *const PortRef) -> *mut CellInfo;
fn npnr_cellinfo_get_location(info: *const CellInfo) -> Loc;
fn npnr_inc_downhill_iter(iter: *mut RawDownhillIter);
fn npnr_deref_downhill_iter(iter: *mut RawDownhillIter) -> PipId;
fn npnr_is_downhill_iter_done(iter: *mut RawDownhillIter) -> bool;
fn npnr_inc_uphill_iter(iter: *mut RawUphillIter);
fn npnr_deref_uphill_iter(iter: *mut RawUphillIter) -> PipId;
fn npnr_is_uphill_iter_done(iter: *mut RawUphillIter) -> bool;
}
/// Store for the nets of a context.
pub struct Nets<'a> {
nets: HashMap<IdString, *mut NetInfo>,
users: HashMap<IdString, &'a [&'a mut PortRef]>,
index_to_net: Vec<IdString>,
net_to_index: HashMap<*mut NetInfo, i32>,
_data: PhantomData<&'a Context>,
}
unsafe impl Send for Nets<'_> {}
unsafe impl Sync for Nets<'_> {}
impl<'a> Nets<'a> {
/// Create a new store for the nets of a context.
///
/// Note that this leaks memory created by nextpnr; the intention is this is called once.
pub fn new(ctx: &'a Context) -> Nets<'a> {
let mut names: *mut libc::c_int = std::ptr::null_mut();
let mut nets_ptr: *mut *mut NetInfo = std::ptr::null_mut();
let size = unsafe {
npnr_context_nets_leak(
ctx,
&mut names as *mut *mut libc::c_int,
&mut nets_ptr as *mut *mut *mut NetInfo,
)
};
let mut nets = HashMap::new();
let mut users = HashMap::new();
let mut index_to_net = Vec::new();
let mut net_to_index = HashMap::new();
for i in 0..size {
let name = unsafe { IdString(*names.add(i as usize)) };
let net = unsafe { *nets_ptr.add(i as usize) };
let mut users_ptr = std::ptr::null_mut();
// SAFETY: net is not null because it's a &mut, and users is only written to.
// Leaking memory is the most convenient FFI I could think of.
let len =
unsafe { npnr_netinfo_users_leak(net, &mut users_ptr as *mut *mut *mut PortRef) };
let users_slice =
unsafe { slice::from_raw_parts(users_ptr as *mut &mut PortRef, len as usize) };
nets.insert(name, net);
users.insert(name, users_slice);
let index = index_to_net.len() as i32;
index_to_net.push(name);
net_to_index.insert(net, index);
unsafe {
npnr_netinfo_udata_set(net, NetIndex(index));
}
}
// Note: the contents of `names` and `nets_ptr` are now lost.
Self {
nets,
users,
index_to_net,
net_to_index,
_data: PhantomData,
}
}
/// Find net users given a net's name.
pub fn users_by_name(&self, net: IdString) -> Option<&&[&mut PortRef]> {
self.users.get(&net)
}
/// Return the number of nets in the store.
pub fn len(&self) -> usize {
self.nets.len()
}
pub fn name_from_index(&self, index: NetIndex) -> IdString {
self.index_to_net[index.0 as usize]
}
pub fn net_from_index(&self, index: NetIndex) -> *mut NetInfo {
*self.nets.get(&self.name_from_index(index)).unwrap()
}
pub fn to_vec(&self) -> Vec<(&IdString, &*mut NetInfo)> {
let mut v = Vec::new();
v.extend(self.nets.iter());
v.sort_by_key(|(name, _net)| name.0);
v
}
}
pub struct NetSinkWireIter<'a> {
ctx: &'a Context,
net: *const NetInfo,
sink: *const PortRef,
n: u32,
}
impl<'a> Iterator for NetSinkWireIter<'a> {
type Item = WireId;
fn next(&mut self) -> Option<Self::Item> {
let item =
unsafe { npnr_context_get_netinfo_sink_wire(self.ctx, self.net, self.sink, self.n) };
if item.is_null() {
return None;
}
self.n += 1;
Some(item)
}
}
#[repr(C)]
struct RawDownhillIter {
content: [u8; 0],
}
pub struct DownhillPipsIter<'a> {
iter: *mut RawDownhillIter,
phantom_data: std::marker::PhantomData<&'a PipId>,
}
impl<'a> Iterator for DownhillPipsIter<'a> {
type Item = PipId;
fn next(&mut self) -> Option<Self::Item> {
if unsafe { npnr_is_downhill_iter_done(self.iter) } {
None
} else {
let pip = unsafe { npnr_deref_downhill_iter(self.iter) };
unsafe { npnr_inc_downhill_iter(self.iter) };
Some(pip)
}
}
}
impl<'a> Drop for DownhillPipsIter<'a> {
fn drop(&mut self) {
unsafe { npnr_delete_downhill_iter(self.iter) };
}
}
#[repr(C)]
struct RawUphillIter {
content: [u8; 0],
}
pub struct UphillPipsIter<'a> {
iter: *mut RawUphillIter,
phantom_data: std::marker::PhantomData<&'a PipId>,
}
impl<'a> Iterator for UphillPipsIter<'a> {
type Item = PipId;
fn next(&mut self) -> Option<Self::Item> {
if unsafe { npnr_is_uphill_iter_done(self.iter) } {
None
} else {
let pip = unsafe { npnr_deref_uphill_iter(self.iter) };
unsafe { npnr_inc_uphill_iter(self.iter) };
Some(pip)
}
}
}
impl<'a> Drop for UphillPipsIter<'a> {
fn drop(&mut self) {
unsafe { npnr_delete_uphill_iter(self.iter) };
}
}
macro_rules! log_info {
($($t:tt)*) => {
let s = std::ffi::CString::new(format!($($t)*)).unwrap();
unsafe { crate::npnr::npnr_log_info(s.as_ptr()); }
};
}
macro_rules! log_error {
($($t:tt)*) => {
let s = std::ffi::CString::new(format!($($t)*)).unwrap();
unsafe { crate::npnr::npnr_log_error(s.as_ptr()); }
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,850 @@
use std::{
collections::{BinaryHeap, HashMap, HashSet},
sync::RwLock,
time::Instant,
};
use colored::Colorize;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use itertools::Itertools;
use crate::{
npnr::{self, IdString, Loc, NetIndex, PipId, WireId},
partition,
};
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct Arc {
source_wire: WireId,
source_loc: Option<Loc>,
sink_wire: WireId,
sink_loc: Option<Loc>,
net: NetIndex,
name: IdString,
}
impl Arc {
pub fn new(
source_wire: WireId,
source_loc: Option<Loc>,
sink_wire: WireId,
sink_loc: Option<Loc>,
net: NetIndex,
name: IdString,
) -> Self {
Self {
source_wire,
source_loc,
sink_wire,
sink_loc,
net,
name,
}
}
pub fn split(&self, ctx: &npnr::Context, pip: npnr::PipId) -> (Self, Self) {
// should this still set the sink and source using the pip? not sure
let pip_src = ctx.pip_src_wire(pip);
let pip_dst = ctx.pip_dst_wire(pip);
(
Self {
source_wire: self.source_wire,
source_loc: self.source_loc,
sink_wire: pip_src,
sink_loc: Some(ctx.pip_location(pip)),
net: self.net,
name: self.name,
},
Self {
source_wire: pip_dst,
source_loc: Some(ctx.pip_location(pip)),
sink_wire: self.sink_wire,
sink_loc: self.sink_loc,
net: self.net,
name: self.name,
},
)
}
pub fn source_wire(&self) -> npnr::WireId {
self.source_wire
}
pub fn sink_wire(&self) -> npnr::WireId {
self.sink_wire
}
pub fn net(&self) -> npnr::NetIndex {
self.net
}
pub fn get_source_loc(&self) -> Option<Loc> {
self.source_loc
}
pub fn get_sink_loc(&self) -> Option<Loc> {
self.sink_loc
}
}
#[derive(Copy, Clone)]
struct QueuedWire {
delay: f32,
congest: f32,
togo: f32,
criticality: f32,
wire: npnr::WireId,
from_pip: Option<npnr::PipId>,
}
impl QueuedWire {
pub fn new(
delay: f32,
congest: f32,
togo: f32,
criticality: f32,
wire: npnr::WireId,
from_pip: Option<npnr::PipId>,
) -> Self {
Self {
delay,
congest,
togo,
criticality,
wire,
from_pip,
}
}
fn score(&self) -> f32 {
(self.criticality * self.delay) + ((1.0 - self.criticality) * self.congest)
}
}
impl PartialEq for QueuedWire {
fn eq(&self, other: &Self) -> bool {
self.delay == other.delay
&& self.congest == other.congest
&& self.togo == other.togo
&& self.wire == other.wire
}
}
impl Eq for QueuedWire {}
impl Ord for QueuedWire {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let me = self.score() + self.togo;
let other = other.score() + other.togo;
other.total_cmp(&me)
}
}
impl PartialOrd for QueuedWire {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
struct PerNetData {
wires: HashMap<WireId, (PipId, u32)>,
done_sinks: HashSet<WireId>,
}
struct PerWireData {
wire: WireId,
curr_cong: u32,
hist_cong: f32,
unavailable: bool,
reserved_net: Option<NetIndex>,
pip_fwd: PipId,
visited_fwd: bool,
pip_bwd: PipId,
visited_bwd: bool,
}
pub struct RouterThread<'a> {
box_ne: partition::Coord,
box_sw: partition::Coord,
arcs: &'a [Arc],
id: &'a str,
progress: &'a MultiProgress,
dirty_wires: Vec<u32>,
}
impl<'a> RouterThread<'a> {
pub fn new(
box_ne: partition::Coord,
box_sw: partition::Coord,
arcs: &'a [Arc],
id: &'a str,
progress: &'a MultiProgress,
) -> Self {
Self {
box_ne,
box_sw,
arcs,
id,
progress,
dirty_wires: Vec::new(),
}
}
}
pub struct Router {
pressure: f32,
history: f32,
nets: RwLock<Vec<PerNetData>>,
wire_to_idx: HashMap<WireId, u32>,
flat_wires: Vec<RwLock<PerWireData>>,
}
impl Router {
pub fn new(nets: &npnr::Nets, wires: &[npnr::WireId], pressure: f32, history: f32) -> Self {
let mut net_vec = Vec::new();
let mut flat_wires = Vec::new();
let mut wire_to_idx = HashMap::new();
for _ in 0..nets.len() {
net_vec.push(PerNetData {
wires: HashMap::new(),
done_sinks: HashSet::new(),
});
}
for (idx, &wire) in wires.iter().enumerate() {
flat_wires.push(RwLock::new(PerWireData {
wire,
curr_cong: 0,
hist_cong: 0.0,
unavailable: false,
reserved_net: None,
pip_fwd: PipId::null(),
visited_fwd: false,
pip_bwd: PipId::null(),
visited_bwd: false,
}));
wire_to_idx.insert(wire, idx as u32);
}
Self {
pressure,
history,
nets: RwLock::new(net_vec),
wire_to_idx,
flat_wires,
}
}
pub fn find_general_routing(&self, ctx: &npnr::Context, nets: &npnr::Nets, this: &mut RouterThread) -> Vec<Arc> {
let is_general_routing = |wire: &str| {
wire.contains("H01")
|| wire.contains("V01")
|| wire.contains("H02")
|| wire.contains("V02")
|| wire.contains("H06")
|| wire.contains("V06")
};
let mut delay = HashMap::new();
for arc in this.arcs {
delay.insert(arc, 1.0_f32);
}
let start = Instant::now();
let mut max_delay = 1.0;
let mut least_overuse = usize::MAX;
let mut iters_since_improvement = 0;
let mut route_arcs = Vec::from_iter(this.arcs.iter());
let progress = this.progress.add(ProgressBar::new(0));
progress.set_style(
ProgressStyle::with_template("[{elapsed}] [{bar:40.green/green}] {msg:30!}")
.unwrap()
.progress_chars("━╸ "),
);
let mut new_arcs = Vec::new();
let mut iterations = 0;
loop {
iterations += 1;
progress.set_position(0);
progress.set_length(route_arcs.len() as u64);
for arc in route_arcs.iter().sorted_by(|&i, &j| {
(delay.get(j).unwrap() / max_delay).total_cmp(&(delay.get(i).unwrap() / max_delay))
}) {
let name = ctx.name_of(arc.name).to_str().unwrap();
progress.inc(1);
let criticality = (delay.get(arc).unwrap() / max_delay).min(0.99).powf(2.5) + 0.1;
progress.set_message(format!("{} @ {}: {}", this.id, iterations, name));
*delay.get_mut(arc).unwrap() = self.route_arc(ctx, nets, arc, criticality);
}
let mut overused = HashSet::new();
for wd in self.flat_wires.iter() {
let mut wd = wd.write().unwrap();
if wd.curr_cong > 1 && !is_general_routing(ctx.name_of_wire(wd.wire)) {
overused.insert(wd.wire);
wd.hist_cong += (wd.curr_cong as f32) * self.history;
}
}
if overused.is_empty() {
break;
} else if overused.len() < least_overuse {
least_overuse = overused.len();
iters_since_improvement = 0;
progress.println(format!(
"{} @ {}: {} wires overused {}",
this.id,
iterations,
overused.len(),
"(new best)".bold()
));
} else {
iters_since_improvement += 1;
progress.println(format!(
"{} @ {}: {} wires overused",
this.id,
iterations,
overused.len()
));
}
let mut next_arcs = HashSet::new();
for arc in this.arcs {
let nets = &*self.nets.read().unwrap();
for wire in nets[arc.net.into_inner() as usize]
.wires
.keys()
{
if overused.contains(wire) {
next_arcs.insert(arc);
}
}
}
for &arc in &next_arcs {
self.ripup_arc(ctx, arc);
}
{
let nets = &mut *self.nets.write().unwrap();
for net in nets.iter_mut() {
net.done_sinks.clear();
}
}
if iters_since_improvement > 50 {
iters_since_improvement = 0;
least_overuse = usize::MAX;
progress.println(format!(
"{} @ {}: {}",
this.id,
iterations,
"bored; rerouting everything".bold()
));
route_arcs = Vec::from_iter(this.arcs.iter());
} else {
route_arcs = Vec::from_iter(next_arcs.into_iter());
}
max_delay = this
.arcs
.iter()
.map(|arc| *delay.get(arc).unwrap())
.reduce(f32::max)
.unwrap();
}
let now = start.elapsed().as_secs_f32();
progress.println(format!(
"{} @ {}: {} in {:.0}m{:.03}s",
this.id,
iterations,
"pre-routing complete".green(),
(now / 60.0).floor(),
now % 60.0
));
progress.finish_and_clear();
for arc in this.arcs {
let (source_wire, sink_wire) = self.ripup_arc_general_routing(ctx, arc);
new_arcs.push(Arc::new(source_wire, None, sink_wire, None, arc.net, arc.name));
}
new_arcs
}
pub fn route(&self, ctx: &npnr::Context, nets: &npnr::Nets, this: &mut RouterThread) {
let mut delay = HashMap::new();
for arc in this.arcs {
delay.insert(arc, 1.0_f32);
}
let start = Instant::now();
let mut max_delay = 1.0;
let mut least_overuse = usize::MAX;
let mut iters_since_improvement = 0;
let mut route_arcs = Vec::from_iter(this.arcs.iter());
let progress = this.progress.add(ProgressBar::new(0));
progress.set_style(
ProgressStyle::with_template("[{elapsed}] [{bar:40.magenta/red}] {msg:30!}")
.unwrap()
.progress_chars("━╸ "),
);
let mut iterations = 0;
loop {
iterations += 1;
progress.set_position(0);
progress.set_length(route_arcs.len() as u64);
for arc in route_arcs.iter().sorted_by(|&i, &j| {
(delay.get(j).unwrap() / max_delay).total_cmp(&(delay.get(i).unwrap() / max_delay))
}) {
let name = ctx.name_of(arc.name).to_str().unwrap();
progress.inc(1);
let criticality = (delay.get(arc).unwrap() / max_delay);
progress.set_message(format!("{} @ {}: {}", this.id, iterations, name));
*delay.get_mut(arc).unwrap() = self.route_arc(ctx, nets, arc, criticality);
}
let mut overused = HashSet::new();
for wd in self.flat_wires.iter() {
let mut wd = wd.write().unwrap();
if wd.curr_cong > 1 {
overused.insert(wd.wire);
wd.hist_cong += (wd.curr_cong as f32) * self.history;
}
}
if overused.is_empty() {
break;
} else if overused.len() < least_overuse {
least_overuse = overused.len();
iters_since_improvement = 0;
progress.println(format!(
"{} @ {}: {} wires overused {}",
this.id,
iterations,
overused.len(),
"(new best)".bold()
));
} else {
iters_since_improvement += 1;
progress.println(format!(
"{} @ {}: {} wires overused",
this.id,
iterations,
overused.len()
));
}
let mut next_arcs = HashSet::new();
for arc in this.arcs {
let nets = &*self.nets.read().unwrap();
for wire in nets[arc.net.into_inner() as usize]
.wires
.keys()
{
if overused.contains(wire) {
next_arcs.insert(arc);
}
}
}
for &arc in &next_arcs {
self.ripup_arc(ctx, arc);
}
{
let nets = &mut *self.nets.write().unwrap();
for net in nets.iter_mut() {
net.done_sinks.clear();
}
}
if iters_since_improvement > 50 {
iters_since_improvement = 0;
least_overuse = usize::MAX;
progress.println(format!(
"{} @ {}: {}",
this.id,
iterations,
"bored; rerouting everything".bold()
));
route_arcs = Vec::from_iter(this.arcs.iter());
} else {
route_arcs = Vec::from_iter(next_arcs.into_iter());
}
max_delay = this
.arcs
.iter()
.map(|arc| *delay.get(arc).unwrap())
.reduce(f32::max)
.unwrap();
}
let now = (Instant::now() - start).as_secs_f32();
progress.println(format!(
"{} @ {}: {} in {:.0}m{:.03}s",
this.id,
iterations,
"routing complete".green(),
now / 60.0,
now % 60.0
));
progress.finish_and_clear();
}
fn can_visit_pip(&self, ctx: &npnr::Context, nets: &npnr::Nets, arc: &Arc, pip: PipId) -> bool {
let wire = ctx.pip_dst_wire(pip);
let sink = *self.wire_to_idx.get(&wire).unwrap();
let nd = &self.nets.read().unwrap()[arc.net().into_inner() as usize];
let nwd = &self.flat_wires[sink as usize].read().unwrap();
/*let pip_coord = partition::Coord::from(ctx.pip_location(pip));
if pip_coord.is_north_of(&self.box_ne) || pip_coord.is_east_of(&self.box_ne) {
return false;
}
if pip_coord.is_south_of(&self.box_sw) || pip_coord.is_west_of(&self.box_sw) {
return false;
}*/
if !ctx.pip_avail_for_net(pip, nets.net_from_index(arc.net())) {
return false;
}
if nwd.unavailable {
return false;
}
if let Some(net) = nwd.reserved_net && net != arc.net() {
return false;
}
// Don't allow the same wire to be bound to the same net with a different driving pip
if let Some((found_pip, _)) = nd.wires.get(&wire) && *found_pip != pip {
return false;
}
true
}
#[allow(clippy::too_many_arguments)]
fn step<'b, 'a: 'b, I>(
&'a self,
ctx: &'b npnr::Context,
nets: &npnr::Nets,
arc: &Arc,
criticality: f32,
queue: &mut BinaryHeap<QueuedWire>,
midpoint: &mut Option<u32>,
target: WireId,
dirty_wires: &mut Vec<u32>,
was_visited: impl Fn(&Self, u32) -> bool,
set_visited: impl Fn(&Self, u32, PipId, &mut Vec<u32>),
is_done: impl Fn(&Self, u32) -> bool,
pip_iter: impl Fn(&'b npnr::Context, WireId) -> I,
pip_wire: impl Fn(&npnr::Context, PipId) -> WireId,
) -> bool
where
I: Iterator<Item = PipId>,
{
if let Some(source) = queue.pop() {
let source_idx = *self.wire_to_idx.get(&source.wire).unwrap();
if was_visited(self, source_idx) {
return true;
}
if let Some(pip) = source.from_pip {
set_visited(self, source_idx, pip, dirty_wires);
}
if is_done(self, source_idx) {
*midpoint = Some(source_idx);
return false;
}
for pip in pip_iter(ctx, source.wire) {
if !self.can_visit_pip(ctx, nets, arc, pip) {
continue;
}
let wire = pip_wire(ctx, pip);
let sink = *self.wire_to_idx.get(&wire).unwrap();
if was_visited(self, sink) {
continue;
}
let nwd = &self.flat_wires[sink as usize].read().unwrap();
let node_delay = ctx.pip_delay(pip) + ctx.wire_delay(wire) + ctx.delay_epsilon();
let sum_delay = source.delay + node_delay;
let congest = source.congest
+ (node_delay + nwd.hist_cong) * (1.0 + (nwd.curr_cong as f32 * self.pressure));
let qw = QueuedWire::new(
sum_delay,
congest,
ctx.estimate_delay(wire, target),
criticality,
wire,
Some(pip),
);
queue.push(qw);
}
return true;
}
false
}
fn route_arc(
&self,
ctx: &npnr::Context,
nets: &npnr::Nets,
arc: &Arc,
criticality: f32,
) -> f32 {
if arc.source_wire == arc.sink_wire {
return 0.0;
}
let mut fwd_queue = BinaryHeap::new();
fwd_queue.push(QueuedWire::new(
0.0,
0.0,
ctx.estimate_delay(arc.source_wire, arc.sink_wire),
criticality,
arc.source_wire,
None,
));
let mut bwd_queue = BinaryHeap::new();
bwd_queue.push(QueuedWire::new(
0.0,
0.0,
ctx.estimate_delay(arc.source_wire, arc.sink_wire),
criticality,
arc.sink_wire,
None,
));
let mut midpoint = None;
let source_wire = *self.wire_to_idx.get(&arc.source_wire).unwrap();
let sink_wire = *self.wire_to_idx.get(&arc.sink_wire).unwrap();
let mut dirty_wires = Vec::new();
dirty_wires.push(source_wire);
dirty_wires.push(sink_wire);
while midpoint.is_none() {
// Step forward
if !self.step(
ctx,
nets,
arc,
criticality,
&mut fwd_queue,
&mut midpoint,
arc.sink_wire,
&mut dirty_wires,
Self::was_visited_fwd,
Self::set_visited_fwd,
Self::was_visited_bwd,
npnr::Context::get_downhill_pips,
npnr::Context::pip_dst_wire,
) {
break;
}
// Step backward
if !self.step(
ctx,
nets,
arc,
criticality,
&mut bwd_queue,
&mut midpoint,
arc.source_wire,
&mut dirty_wires,
Self::was_visited_bwd,
Self::set_visited_bwd,
Self::was_visited_fwd,
npnr::Context::get_uphill_pips,
npnr::Context::pip_src_wire,
) {
break;
}
self.flat_wires[source_wire as usize]
.write()
.unwrap()
.visited_fwd = true;
self.flat_wires[sink_wire as usize]
.write()
.unwrap()
.visited_bwd = true;
}
assert!(
midpoint.is_some(),
"didn't find sink wire for net {} between {} and {}",
ctx.name_of(arc.name).to_str().unwrap(),
ctx.name_of_wire(arc.source_wire),
ctx.name_of_wire(arc.sink_wire),
);
let mut wire = midpoint.unwrap();
let mut calculated_delay = 0.0;
while wire != source_wire {
let (pip, wireid) = {
let nwd = self.flat_wires[wire as usize].read().unwrap();
(nwd.pip_fwd, nwd.wire)
};
assert!(pip != PipId::null());
let node_delay = ctx.pip_delay(pip) + ctx.wire_delay(wireid) + ctx.delay_epsilon();
calculated_delay += node_delay;
self.bind_pip_internal(arc.net(), wire, pip);
wire = *self.wire_to_idx.get(&ctx.pip_src_wire(pip)).unwrap();
}
let mut wire = midpoint.unwrap();
while wire != sink_wire {
let (pip, wireid) = {
let nwd = self.flat_wires[wire as usize].read().unwrap();
(nwd.pip_bwd, nwd.wire)
};
assert!(pip != PipId::null());
// do note that the order is inverted from the fwd loop
wire = *self.wire_to_idx.get(&ctx.pip_dst_wire(pip)).unwrap();
let node_delay = ctx.pip_delay(pip) + ctx.wire_delay(wireid) + ctx.delay_epsilon();
calculated_delay += node_delay;
self.bind_pip_internal(arc.net(), wire, pip);
}
self.nets.write().unwrap()[arc.net().into_inner() as usize]
.done_sinks
.insert(arc.sink_wire);
self.reset_wires(&dirty_wires);
calculated_delay
}
fn was_visited_fwd(&self, wire: u32) -> bool {
self.flat_wires[wire as usize].read().unwrap().visited_fwd
}
fn was_visited_bwd(&self, wire: u32) -> bool {
self.flat_wires[wire as usize].read().unwrap().visited_bwd
}
fn set_visited_fwd(&self, wire: u32, pip: PipId, dirty_wires: &mut Vec<u32>) {
let mut wd = self.flat_wires[wire as usize].write().unwrap();
if !wd.visited_fwd {
dirty_wires.push(wire);
}
wd.pip_fwd = pip;
wd.visited_fwd = true;
}
fn set_visited_bwd(&self, wire: u32, pip: PipId, dirty_wires: &mut Vec<u32>) {
let mut wd = self.flat_wires[wire as usize].write().unwrap();
if !wd.visited_bwd {
dirty_wires.push(wire);
}
wd.pip_bwd = pip;
wd.visited_bwd = true;
}
fn bind_pip_internal(&self, netindex: NetIndex, wire: u32, pip: PipId) {
let wireid = self.flat_wires[wire as usize].read().unwrap().wire;
let net = &mut self.nets.write().unwrap()[netindex.into_inner() as usize];
if let Some((bound_pip, usage)) = net.wires.get_mut(&wireid) {
assert!(*bound_pip == pip);
*usage += 1;
} else {
net.wires.insert(wireid, (pip, 1));
self.flat_wires[wire as usize].write().unwrap().curr_cong += 1;
}
}
fn unbind_pip_internal(&self, net: NetIndex, wire: WireId) {
let net = net.into_inner() as usize;
let wireidx = *self.wire_to_idx.get(&wire).unwrap() as usize;
let nd = &mut self.nets.write().unwrap()[net];
let (_pip, usage) = nd.wires.get_mut(&wire).unwrap();
*usage -= 1;
if *usage == 0 {
self.flat_wires[wireidx].write().unwrap().curr_cong -= 1;
nd.wires.remove(&wire);
}
}
fn ripup_arc(&self, ctx: &npnr::Context, arc: &Arc) {
let net = arc.net().into_inner() as usize;
let source_wire = arc.source_wire;
let mut wire = arc.sink_wire;
while wire != source_wire {
let pip = self.nets.read().unwrap()[net].wires.get(&wire).unwrap().0;
assert!(pip != PipId::null());
self.unbind_pip_internal(arc.net(), wire);
wire = ctx.pip_src_wire(pip);
}
}
fn ripup_arc_general_routing(&self, ctx: &npnr::Context, arc: &Arc) -> (WireId, WireId) {
let is_general_routing = |wire: WireId| {
let wire = ctx.name_of_wire(wire);
wire.contains("H01")
|| wire.contains("V01")
|| wire.contains("H02")
|| wire.contains("V02")
|| wire.contains("H06")
|| wire.contains("V06")
};
let net = arc.net().into_inner() as usize;
let source_wire = arc.source_wire;
let mut wire = arc.sink_wire;
let mut last_was_general = false;
let mut w1 = arc.sink_wire;
let mut w2 = arc.source_wire;
while wire != source_wire {
let pip = self.nets.read().unwrap()[net].wires.get(&wire).expect("wire should have driving pip").0;
assert!(pip != PipId::null());
if is_general_routing(wire) {
if !last_was_general {
w1 = wire;
}
self.unbind_pip_internal(arc.net(), wire);
last_was_general = true;
} else {
if last_was_general {
w2 = wire;
}
last_was_general = false;
}
wire = ctx.pip_src_wire(pip);
}
(w2, w1)
}
fn reset_wires(&self, dirty_wires: &Vec<u32>) {
for &wire in dirty_wires {
let mut nwd = self.flat_wires[wire as usize].write().unwrap();
nwd.pip_fwd = PipId::null();
nwd.visited_fwd = false;
nwd.pip_bwd = PipId::null();
nwd.visited_bwd = false;
}
}
}

View File

@ -33,6 +33,7 @@
#include "placer_static.h"
#include "router1.h"
#include "router2.h"
#include "awooter.h"
#include "timing.h"
#include "util.h"
@ -696,6 +697,8 @@ bool Arch::route()
} else if (router == "router2") {
router2(getCtx(), Router2Cfg(getCtx()));
result = true;
} else if (router == "awooter") {
result = router_awooter(getCtx());
} else {
log_error("ECP5 architecture does not support router '%s'\n", router.c_str());
}
@ -1278,7 +1281,7 @@ const std::string Arch::defaultPlacer = "heap";
const std::vector<std::string> Arch::availablePlacers = {"sa", "heap", "static"};
const std::string Arch::defaultRouter = "router1";
const std::vector<std::string> Arch::availableRouters = {"router1", "router2"};
const std::vector<std::string> Arch::availableRouters = {"router1", "router2", "awooter"};
// -----------------------------------------------------------------------

3
rust-toolchain.toml Normal file
View File

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly-2022-08-06"
components = ["rust-src"]