From d5d6a07c346e2f720cb62c7f27be04364f54871a Mon Sep 17 00:00:00 2001 From: Lofty Date: Sun, 20 Nov 2022 03:20:09 +0000 Subject: [PATCH] awooter: highly, highly WIP --- CMakeLists.txt | 29 ++++ Cargo.lock | 16 ++ Cargo.toml | 4 + common/route/awooter.cc | 151 ++++++++++++++++++ common/route/awooter.h | 30 ++++ common/route/awooter/rust/Cargo.lock | 7 + common/route/awooter/rust/Cargo.toml | 14 ++ common/route/awooter/rust/src/lib.rs | 31 ++++ common/route/awooter/rust/src/npnr.rs | 218 ++++++++++++++++++++++++++ common/route/awooter/rust/src/part.rs | 6 + ecp5/arch.cc | 5 +- rust-toolchain.toml | 3 + 12 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 common/route/awooter.cc create mode 100644 common/route/awooter.h create mode 100644 common/route/awooter/rust/Cargo.lock create mode 100644 common/route/awooter/rust/Cargo.toml create mode 100644 common/route/awooter/rust/src/lib.rs create mode 100644 common/route/awooter/rust/src/npnr.rs create mode 100644 common/route/awooter/rust/src/part.rs create mode 100644 rust-toolchain.toml diff --git a/CMakeLists.txt b/CMakeLists.txt index adef332f..2e096fba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) + + # 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) diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..da0fd887 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "awooter" +version = "0.1.0" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..7bd93035 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = [ + "common/route/awooter/rust" +] diff --git a/common/route/awooter.cc b/common/route/awooter.cc new file mode 100644 index 00000000..39592d0b --- /dev/null +++ b/common/route/awooter.cc @@ -0,0 +1,151 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2022 Lofty + * + * 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 + 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; + } +} + +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 + ctx->setting + ctx->setting + 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()); } + + 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_delay_epsilon(const Context *const ctx) { return ctx->getDelayNS(ctx->getDelayEpsilon()); } + + void npnr_context_check(const Context *const ctx) { ctx->check(); } + bool npnr_context_debug(const Context *const ctx) { return ctx->debug; } + IdString npnr_context_id(const Context *const ctx, const char *const str) { return ctx->id(str); } + const char *npnr_context_name_of(const Context *const ctx, IdString str) { return ctx->nameOf(str); } + bool npnr_context_verbose(const Context *const ctx) { return ctx->verbose; } + + //NetInfo** npnr_context_nets(Context *ctx) { /* oh no */ } + + extern bool npnr_router_awooter(Context *ctx); +} + +NEXTPNR_NAMESPACE_BEGIN + +bool router_awooter(Context *ctx) { + static_assert(std::is_standard_layout::value == true, "IdString is not FFI-safe"); + + log_info("Running Awooter...\n"); + auto result = npnr_router_awooter(ctx); + log_info("Router returned: %d\n", result); + NPNR_ASSERT_FALSE_STR("I haven't implemented anything beyond this yet."); + return result; +} + +NEXTPNR_NAMESPACE_END diff --git a/common/route/awooter.h b/common/route/awooter.h new file mode 100644 index 00000000..0265e298 --- /dev/null +++ b/common/route/awooter.h @@ -0,0 +1,30 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2022 Lofty + * + * 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 diff --git a/common/route/awooter/rust/Cargo.lock b/common/route/awooter/rust/Cargo.lock new file mode 100644 index 00000000..b21cc6a2 --- /dev/null +++ b/common/route/awooter/rust/Cargo.lock @@ -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" diff --git a/common/route/awooter/rust/Cargo.toml b/common/route/awooter/rust/Cargo.toml new file mode 100644 index 00000000..383cf217 --- /dev/null +++ b/common/route/awooter/rust/Cargo.toml @@ -0,0 +1,14 @@ +[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" diff --git a/common/route/awooter/rust/src/lib.rs b/common/route/awooter/rust/src/lib.rs new file mode 100644 index 00000000..1278efd4 --- /dev/null +++ b/common/route/awooter/rust/src/lib.rs @@ -0,0 +1,31 @@ +use std::ptr::NonNull; + +#[macro_use] +mod npnr; +mod part; + +#[no_mangle] +pub extern "C" fn npnr_router_awooter(ctx: Option>) -> bool { + 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::() { + log_error!("caught panic: {}", x); + } + false + }) +} + +fn route(ctx: &mut npnr::Context) -> bool { + log_info!("Hello from Rust!\n"); + log_info!( + "Running on a {}x{} grid\n", + ctx.grid_dim_x(), + ctx.grid_dim_y() + ); + // let _belid = npnr::BelId::null(); + log_info!("Managed to survive BelId()\n"); + true +} \ No newline at end of file diff --git a/common/route/awooter/rust/src/npnr.rs b/common/route/awooter/rust/src/npnr.rs new file mode 100644 index 00000000..408555b5 --- /dev/null +++ b/common/route/awooter/rust/src/npnr.rs @@ -0,0 +1,218 @@ +use std::ffi::CStr; + +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], +} + +#[repr(C)] +pub struct NetInfo { + private: [u8; 0], +} + +#[repr(C)] +pub struct IdString { + index: libc::c_int, +} + +/// A type representing a bel name. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct BelId { + _private: u64, +} + +impl BelId { + pub fn null() -> Self { + unsafe { npnr_belid_null() } + } +} + +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct PipId { + _private: u64, +} + +impl PipId { + pub fn null() -> Self { + todo!() + } +} + +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct WireId { + _private: u64, +} + +impl WireId { + pub fn null() -> Self { + todo!() + } +} + +#[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) as i32 } + } + + /// 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) as i32 } + } + + /// 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) as f32 } + } + + 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 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_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) -> libc::c_float; + + 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_verbose(ctx: *const Context) -> bool; + + // fn npnr_context_nets(ctx: *const Context) -> *mut *mut NetInfo; +} + +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()); } + }; +} \ No newline at end of file diff --git a/common/route/awooter/rust/src/part.rs b/common/route/awooter/rust/src/part.rs new file mode 100644 index 00000000..daeec3f3 --- /dev/null +++ b/common/route/awooter/rust/src/part.rs @@ -0,0 +1,6 @@ +use crate::npnr; + +pub struct Partition { + parts: [Option>; 4], + borders: [[Vec; 4]; 4] +} diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 8bf5db91..b742e690 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -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 Arch::availablePlacers = {"sa", "heap", "static"}; const std::string Arch::defaultRouter = "router1"; -const std::vector Arch::availableRouters = {"router1", "router2"}; +const std::vector Arch::availableRouters = {"router1", "router2", "awooter"}; // ----------------------------------------------------------------------- diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..db583a01 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2022-08-06" +components = ["rust-src"] \ No newline at end of file