Compare commits
73 Commits
master
...
lofty/awoo
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3eceee98bd | ||
![]() |
4b635cf68c | ||
![]() |
0bc945fbc3 | ||
![]() |
da2892fc28 | ||
![]() |
eaf55a0b2e | ||
![]() |
181d76772a | ||
![]() |
8b8abc2aac | ||
![]() |
0a570f418d | ||
![]() |
000ad6d539 | ||
![]() |
b38215062e | ||
![]() |
c22a45ae89 | ||
![]() |
3d2635f379 | ||
![]() |
0bdadde9c0 | ||
![]() |
a205d3b8dd | ||
![]() |
50aae5935d | ||
![]() |
3cec25db6c | ||
![]() |
2fe823cab9 | ||
![]() |
7c1449601d | ||
![]() |
92ea4422f6 | ||
![]() |
0555a12974 | ||
![]() |
f1d51287ea | ||
![]() |
7484bd34dc | ||
![]() |
d0231fc67e | ||
![]() |
ec65ec3484 | ||
![]() |
3ac3a5407f | ||
![]() |
55ccacdcb8 | ||
![]() |
4a51da0900 | ||
![]() |
ccd454c1f8 | ||
![]() |
0f9cf8775c | ||
![]() |
c13e840e41 | ||
![]() |
838efaf2f1 | ||
![]() |
6f940d29b9 | ||
![]() |
483e42d5e7 | ||
![]() |
ac3ef9b0bb | ||
![]() |
290291cca6 | ||
![]() |
d9161d9142 | ||
![]() |
3408f532ec | ||
![]() |
522dd3da7c | ||
![]() |
8fd983af15 | ||
![]() |
035247ebbf | ||
![]() |
afaaff6b00 | ||
![]() |
f7fc239f75 | ||
![]() |
3694632aa4 | ||
![]() |
908d7ef69a | ||
![]() |
6213b13277 | ||
![]() |
b333080715 | ||
![]() |
2ff491c717 | ||
![]() |
bc7f7845d2 | ||
![]() |
a16247d512 | ||
![]() |
319d1c4620 | ||
![]() |
9b4c6966be | ||
![]() |
2c1a9d18ed | ||
![]() |
cb7dba6dd5 | ||
![]() |
ae756e2d8c | ||
![]() |
1cc00308dc | ||
![]() |
2d3c05da85 | ||
![]() |
ba9067afaf | ||
![]() |
268906819d | ||
![]() |
1077cd3654 | ||
![]() |
c00fba75e9 | ||
![]() |
2a18fe58c6 | ||
![]() |
f1a4848c0f | ||
![]() |
873a70ddd0 | ||
![]() |
c8a2b842d6 | ||
![]() |
e64cd668b2 | ||
![]() |
d50ab6139c | ||
![]() |
f64caaae24 | ||
![]() |
4a0406420b | ||
![]() |
b92a1bf52d | ||
![]() |
0cc3245d4b | ||
![]() |
60cc6b7765 | ||
![]() |
195d344d9a | ||
![]() |
d5d6a07c34 |
@ -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
314
Cargo.lock
generated
Normal 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
4
Cargo.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"common/route/awooter/rust"
|
||||
]
|
@ -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
329
common/route/awooter.cc
Normal 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
30
common/route/awooter.h
Normal 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
7
common/route/awooter/rust/Cargo.lock
generated
Normal 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"
|
19
common/route/awooter/rust/Cargo.toml
Normal file
19
common/route/awooter/rust/Cargo.toml
Normal 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"
|
304
common/route/awooter/rust/src/lib.rs
Normal file
304
common/route/awooter/rust/src/lib.rs
Normal 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
|
||||
}
|
586
common/route/awooter/rust/src/npnr.rs
Normal file
586
common/route/awooter/rust/src/npnr.rs
Normal 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()); }
|
||||
};
|
||||
}
|
1555
common/route/awooter/rust/src/partition.rs
Normal file
1555
common/route/awooter/rust/src/partition.rs
Normal file
File diff suppressed because it is too large
Load Diff
850
common/route/awooter/rust/src/route.rs
Normal file
850
common/route/awooter/rust/src/route.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
3
rust-toolchain.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2022-08-06"
|
||||
components = ["rust-src"]
|
Loading…
Reference in New Issue
Block a user