awooter: highly, highly WIP

This commit is contained in:
Lofty 2022-11-20 03:20:09 +00:00
parent 6d9322457e
commit d5d6a07c34
12 changed files with 513 additions and 1 deletions

View File

@ -29,6 +29,7 @@ option(EXTERNAL_CHIPDB "Create build with pre-built chipdb binaries" OFF)
option(WERROR "pass -Werror to compiler (used for CI)" OFF) option(WERROR "pass -Werror to compiler (used for CI)" OFF)
option(PROFILER "Link against libprofiler" OFF) option(PROFILER "Link against libprofiler" OFF)
option(USE_IPO "Compile nextpnr with IPO" ON) option(USE_IPO "Compile nextpnr with IPO" ON)
option(AWOOTER "Build awooter router" ON)
if (USE_IPO) if (USE_IPO)
if (ipo_supported) if (ipo_supported)
@ -99,6 +100,29 @@ if (EXTERNAL_CHIPDB)
add_definitions("-DEXTERNAL_CHIPDB_ROOT=\"${EXTERNAL_CHIPDB_ROOT}\"") add_definitions("-DEXTERNAL_CHIPDB_ROOT=\"${EXTERNAL_CHIPDB_ROOT}\"")
endif() 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") set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables")
# List of families to build # List of families to build
@ -297,6 +321,11 @@ foreach (family ${ARCH})
install(TARGETS ${PROGRAM_PREFIX}nextpnr-${family} RUNTIME DESTINATION bin) install(TARGETS ${PROGRAM_PREFIX}nextpnr-${family} RUNTIME DESTINATION bin)
target_compile_definitions(${PROGRAM_PREFIX}nextpnr-${family} PRIVATE MAIN_EXECUTABLE) 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 # Add any new per-architecture targets here
if (BUILD_TESTS) if (BUILD_TESTS)
if (COVERAGE) if (COVERAGE)

16
Cargo.lock generated Normal file
View File

@ -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"

4
Cargo.toml Normal file
View File

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

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

@ -0,0 +1,151 @@
/*
* 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;
}
}
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()); }
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<IdString>::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

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

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

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

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

View File

@ -0,0 +1,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"

View File

@ -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<NonNull<npnr::Context>>) -> 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::<String>() {
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
}

View File

@ -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()); }
};
}

View File

@ -0,0 +1,6 @@
use crate::npnr;
pub struct Partition {
parts: [Option<Box<Partition>>; 4],
borders: [[Vec<npnr::WireId>; 4]; 4]
}

View File

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

3
rust-toolchain.toml Normal file
View File

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