Add Rust FFI bindings
This commit is contained in:
parent
4a4025192a
commit
d2297b1ba0
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,3 +7,6 @@
|
||||
[submodule "himbaechel/uarch/xilinx/meta"]
|
||||
path = himbaechel/uarch/xilinx/meta
|
||||
url = https://github.com/gatecat/nextpnr-xilinx-meta
|
||||
[submodule "3rdparty/corrosion"]
|
||||
path = 3rdparty/corrosion
|
||||
url = https://github.com/corrosion-rs/corrosion
|
||||
|
1
3rdparty/corrosion
vendored
Submodule
1
3rdparty/corrosion
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit be76480232216a64f65e3b1d9794d68cbac6c690
|
@ -29,6 +29,13 @@ 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(USE_RUST "Enable Rust bindings" OFF)
|
||||
|
||||
if (USE_RUST)
|
||||
add_subdirectory(3rdparty/corrosion)
|
||||
corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml PROFILE "release" IMPORTED_CRATES RUST_CRATES)
|
||||
aux_source_directory(rust RUST_FILES)
|
||||
endif()
|
||||
|
||||
if (USE_IPO)
|
||||
if (ipo_supported)
|
||||
@ -249,7 +256,10 @@ aux_source_directory(3rdparty/json11 EXT_JSON11_FILES)
|
||||
aux_source_directory(3rdparty/oourafft EXT_OOURAFFT_FILES)
|
||||
aux_source_directory(frontend/ FRONTEND_FILES)
|
||||
|
||||
aux_source_directory(rust/ RUST_FILES)
|
||||
|
||||
set(COMMON_FILES ${KERNEL_SRC_FILES} ${PLACE_SRC_FILES} ${ROUTE_SRC_FILES} ${EXT_JSON11_FILES} ${EXT_OOURAFFT_FILES} ${JSON_PARSER_FILES} ${FRONTEND_FILES})
|
||||
|
||||
if( NOT CMAKE_BUILD_TYPE )
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
@ -337,7 +347,7 @@ foreach (family ${ARCH})
|
||||
endforeach()
|
||||
|
||||
# Include family-specific source files to all family targets and set defines appropriately
|
||||
target_include_directories(${target} PRIVATE ${family}/ ${CMAKE_CURRENT_BINARY_DIR}/generated/)
|
||||
target_include_directories(${target} PRIVATE ${family}/ ${CMAKE_CURRENT_BINARY_DIR}/generated/ rust/)
|
||||
target_compile_definitions(${target} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family})
|
||||
target_link_libraries(${target} LINK_PUBLIC ${Boost_LIBRARIES} ${link_param})
|
||||
if (NOT MSVC)
|
||||
@ -360,6 +370,13 @@ foreach (family ${ARCH})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if (USE_RUST)
|
||||
target_sources(${target} PRIVATE ${RUST_FILES})
|
||||
target_compile_definitions(${target} PRIVATE USE_RUST)
|
||||
foreach(crate ${RUST_CRATES})
|
||||
target_link_libraries(${target} PRIVATE ${crate})
|
||||
endforeach(crate)
|
||||
endif()
|
||||
endforeach (target)
|
||||
endforeach (family)
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "placer_static.h"
|
||||
#include "router1.h"
|
||||
#include "router2.h"
|
||||
#include "rust.h"
|
||||
#include "timing_opt.h"
|
||||
#include "util.h"
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
@ -642,6 +643,12 @@ std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
|
||||
|
||||
bool Arch::place()
|
||||
{
|
||||
#ifdef USE_RUST
|
||||
log_info("Calling Rust\n");
|
||||
example_printnets(getCtx());
|
||||
log_info("Returned from Rust\n");
|
||||
#endif
|
||||
|
||||
std::string placer = str_or_default(settings, id_placer, defaultPlacer);
|
||||
if (placer == "heap") {
|
||||
PlacerHeapCfg cfg(getCtx());
|
||||
|
6
rust/Cargo.toml
Normal file
6
rust/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"nextpnr",
|
||||
"example_printnets",
|
||||
]
|
14
rust/example_printnets/Cargo.toml
Normal file
14
rust/example_printnets/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "example_printnets"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
name = "example_printnets"
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
nextpnr = { path = "../nextpnr" }
|
12
rust/example_printnets/src/lib.rs
Normal file
12
rust/example_printnets/src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use nextpnr::{Context, Nets};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_example_printnets(ctx: &mut Context) {
|
||||
let nets = Nets::new(ctx);
|
||||
let nets_vec = nets.to_vec();
|
||||
|
||||
println!("Nets in design:");
|
||||
for (&name, _net) in nets_vec {
|
||||
println!(" {}", ctx.name_of(name).to_str().unwrap());
|
||||
}
|
||||
}
|
14
rust/nextpnr/Cargo.toml
Normal file
14
rust/nextpnr/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "nextpnr"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
name = "nextpnr"
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["rlib"]
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
581
rust/nextpnr/src/lib.rs
Normal file
581
rust/nextpnr/src/lib.rs
Normal file
@ -0,0 +1,581 @@
|
||||
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) }
|
||||
}
|
||||
|
||||
/// 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) -> &CStr {
|
||||
static MUTEX: Mutex<()> = Mutex::new(());
|
||||
let _lock = MUTEX.lock().unwrap();
|
||||
unsafe { CStr::from_ptr(npnr_context_name_of_wire(self, wire)) }
|
||||
}
|
||||
|
||||
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_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()); }
|
||||
};
|
||||
}
|
||||
|
323
rust/rust.cc
Normal file
323
rust/rust.cc
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
void rust_example_printnets(Context *ctx);
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void example_printnets(Context *ctx) {
|
||||
rust_example_printnets(ctx);
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
34
rust/rust.h
Normal file
34
rust/rust.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 RUST_H
|
||||
#define RUST_H
|
||||
|
||||
#ifdef USE_RUST
|
||||
|
||||
#include "nextpnr.h"
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void example_printnets(Context* ctx);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif // RUST_H
|
Loading…
Reference in New Issue
Block a user