rust: small updates (#1560)

This commit is contained in:
Lofty 2025-09-24 12:59:30 +01:00 committed by GitHub
parent 22041ed5df
commit 2e4ef6f71f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 82 additions and 23 deletions

View File

@ -1,5 +1,5 @@
[workspace] [workspace]
resolver = "2"
members = [ members = [
"nextpnr", "nextpnr",
"example_printnets", "example_printnets",

View File

@ -1,7 +1,7 @@
[package] [package]
name = "nextpnr" name = "nextpnr"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -21,6 +21,7 @@ pub struct CellInfo {
} }
impl CellInfo { impl CellInfo {
#[must_use]
pub fn location(&self) -> Loc { pub fn location(&self) -> Loc {
unsafe { npnr_cellinfo_get_location(self) } unsafe { npnr_cellinfo_get_location(self) }
} }
@ -36,10 +37,12 @@ impl NetInfo {
unsafe { npnr_netinfo_driver(self) } unsafe { npnr_netinfo_driver(self) }
} }
#[must_use]
pub fn is_global(&self) -> bool { pub fn is_global(&self) -> bool {
unsafe { npnr_netinfo_is_global(self) } unsafe { npnr_netinfo_is_global(self) }
} }
#[must_use]
pub fn index(&self) -> NetIndex { pub fn index(&self) -> NetIndex {
unsafe { npnr_netinfo_udata(self) } unsafe { npnr_netinfo_udata(self) }
} }
@ -50,6 +53,7 @@ impl NetInfo {
pub struct NetIndex(i32); pub struct NetIndex(i32);
impl NetIndex { impl NetIndex {
#[must_use]
pub fn into_inner(self) -> i32 { pub fn into_inner(self) -> i32 {
self.0 self.0
} }
@ -61,6 +65,7 @@ pub struct PortRef {
} }
impl PortRef { impl PortRef {
#[must_use]
pub fn cell(&self) -> Option<&CellInfo> { pub fn cell(&self) -> Option<&CellInfo> {
// SAFETY: handing out &s is safe when we have &self. // SAFETY: handing out &s is safe when we have &self.
unsafe { npnr_portref_cell(self) } unsafe { npnr_portref_cell(self) }
@ -76,14 +81,21 @@ pub struct IdString(libc::c_int);
#[repr(transparent)] #[repr(transparent)]
pub struct BelId(u64); pub struct BelId(u64);
impl Default for BelId {
fn default() -> Self {
Self::null()
}
}
impl BelId { impl BelId {
/// Return a sentinel value that represents an invalid bel. /// Return a sentinel value that represents an invalid bel.
#[must_use]
pub fn null() -> Self { pub fn null() -> Self {
// SAFETY: BelId() has no safety requirements. npnr_belid_null()
unsafe { npnr_belid_null() }
} }
/// Check if this bel is invalid. /// Check if this bel is invalid.
#[must_use]
pub fn is_null(self) -> bool { pub fn is_null(self) -> bool {
self == Self::null() self == Self::null()
} }
@ -93,9 +105,16 @@ impl BelId {
#[repr(transparent)] #[repr(transparent)]
pub struct PipId(u64); pub struct PipId(u64);
impl Default for PipId {
fn default() -> Self {
Self::null()
}
}
impl PipId { impl PipId {
#[must_use]
pub fn null() -> Self { pub fn null() -> Self {
unsafe { npnr_pipid_null() } npnr_pipid_null()
} }
} }
@ -103,14 +122,22 @@ impl PipId {
#[repr(transparent)] #[repr(transparent)]
pub struct WireId(u64); pub struct WireId(u64);
impl Default for WireId {
fn default() -> Self {
Self::null()
}
}
impl WireId { impl WireId {
/// Return a sentinel value that represents an invalid wire. /// Return a sentinel value that represents an invalid wire.
#[must_use]
pub fn null() -> Self { pub fn null() -> Self {
// SAFETY: WireId() has no safety requirements. // SAFETY: WireId() has no safety requirements.
unsafe { npnr_wireid_null() } npnr_wireid_null()
} }
/// Check if this wire is invalid. /// Check if this wire is invalid.
#[must_use]
pub fn is_null(self) -> bool { pub fn is_null(self) -> bool {
self == Self::null() self == Self::null()
} }
@ -144,11 +171,13 @@ pub struct Context {
impl Context { impl Context {
/// Get grid X dimension. All bels and pips must have X coordinates in the range `0 .. getGridDimX()-1` (inclusive). /// Get grid X dimension. All bels and pips must have X coordinates in the range `0 .. getGridDimX()-1` (inclusive).
#[must_use]
pub fn grid_dim_x(&self) -> i32 { pub fn grid_dim_x(&self) -> i32 {
unsafe { npnr_context_get_grid_dim_x(self) } 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). /// Get grid Y dimension. All bels and pips must have Y coordinates in the range `0 .. getGridDimY()-1` (inclusive).
#[must_use]
pub fn grid_dim_y(&self) -> i32 { pub fn grid_dim_y(&self) -> i32 {
unsafe { npnr_context_get_grid_dim_y(self) } unsafe { npnr_context_get_grid_dim_y(self) }
} }
@ -166,11 +195,12 @@ impl Context {
} }
/// 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. /// 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.
#[must_use]
pub fn check_bel_avail(&self, bel: BelId) -> bool { pub fn check_bel_avail(&self, bel: BelId) -> bool {
unsafe { npnr_context_check_bel_avail(self, bel) } 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. /// 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) { pub fn bind_wire(&mut self, wire: WireId, net: &mut NetInfo, strength: PlaceStrength) {
let _lock = ARCH_MUTEX.lock().unwrap(); let _lock = ARCH_MUTEX.lock().unwrap();
unsafe { npnr_context_bind_wire(self, wire, net, strength) } unsafe { npnr_context_bind_wire(self, wire, net, strength) }
@ -195,36 +225,44 @@ impl Context {
} }
/// Get the source wire for a pip. /// Get the source wire for a pip.
#[must_use]
pub fn pip_src_wire(&self, pip: PipId) -> WireId { pub fn pip_src_wire(&self, pip: PipId) -> WireId {
unsafe { npnr_context_get_pip_src_wire(self, pip) } unsafe { npnr_context_get_pip_src_wire(self, pip) }
} }
/// Get the destination wire for a pip. /// Get the destination wire for a pip.
#[must_use]
pub fn pip_dst_wire(&self, pip: PipId) -> WireId { pub fn pip_dst_wire(&self, pip: PipId) -> WireId {
unsafe { npnr_context_get_pip_dst_wire(self, pip) } unsafe { npnr_context_get_pip_dst_wire(self, pip) }
} }
// TODO: Should this be a Duration? Does that even make sense? // TODO: Should this be a Duration? Does that even make sense?
#[must_use]
pub fn estimate_delay(&self, src: WireId, dst: WireId) -> f32 { pub fn estimate_delay(&self, src: WireId, dst: WireId) -> f32 {
unsafe { npnr_context_estimate_delay(self, src, dst) } unsafe { npnr_context_estimate_delay(self, src, dst) }
} }
#[must_use]
pub fn pip_delay(&self, pip: PipId) -> f32 { pub fn pip_delay(&self, pip: PipId) -> f32 {
unsafe { npnr_context_get_pip_delay(self, pip) } unsafe { npnr_context_get_pip_delay(self, pip) }
} }
#[must_use]
pub fn wire_delay(&self, wire: WireId) -> f32 { pub fn wire_delay(&self, wire: WireId) -> f32 {
unsafe { npnr_context_get_wire_delay(self, wire) } unsafe { npnr_context_get_wire_delay(self, wire) }
} }
#[must_use]
pub fn delay_epsilon(&self) -> f32 { pub fn delay_epsilon(&self) -> f32 {
unsafe { npnr_context_delay_epsilon(self) } unsafe { npnr_context_delay_epsilon(self) }
} }
#[must_use]
pub fn source_wire(&self, net: &NetInfo) -> WireId { pub fn source_wire(&self, net: &NetInfo) -> WireId {
unsafe { npnr_context_get_netinfo_source_wire(self, net) } unsafe { npnr_context_get_netinfo_source_wire(self, net) }
} }
#[must_use]
pub fn sink_wires(&self, net: &NetInfo, sink: &PortRef) -> Vec<WireId> { pub fn sink_wires(&self, net: &NetInfo, sink: &PortRef) -> Vec<WireId> {
let mut v = Vec::new(); let mut v = Vec::new();
let mut n = 0; let mut n = 0;
@ -239,41 +277,48 @@ impl Context {
v v
} }
pub fn bels(&self) -> BelIter { #[must_use]
pub fn bels(&self) -> BelIter<'_> {
let iter = unsafe { npnr_context_get_bels(self) }; let iter = unsafe { npnr_context_get_bels(self) };
BelIter { iter, phantom_data: PhantomData } BelIter { iter, phantom_data: PhantomData }
} }
pub fn pips(&self) -> PipIter { #[must_use]
pub fn pips(&self) -> PipIter<'_> {
let iter = unsafe { npnr_context_get_pips(self) }; let iter = unsafe { npnr_context_get_pips(self) };
PipIter { iter, phantom_data: PhantomData } PipIter { iter, phantom_data: PhantomData }
} }
pub fn wires(&self) -> WireIter { #[must_use]
pub fn wires(&self) -> WireIter<'_> {
let iter = unsafe { npnr_context_get_wires(self) }; let iter = unsafe { npnr_context_get_wires(self) };
WireIter { iter, phantom_data: PhantomData } WireIter { iter, phantom_data: PhantomData }
} }
pub fn get_downhill_pips(&self, wire: WireId) -> DownhillPipsIter { #[must_use]
pub fn get_downhill_pips(&self, wire: WireId) -> DownhillPipsIter<'_> {
let iter = unsafe { npnr_context_get_pips_downhill(self, wire) }; let iter = unsafe { npnr_context_get_pips_downhill(self, wire) };
DownhillPipsIter { DownhillPipsIter {
iter, iter,
phantom_data: Default::default(), phantom_data: PhantomData,
} }
} }
pub fn get_uphill_pips(&self, wire: WireId) -> UphillPipsIter { #[must_use]
pub fn get_uphill_pips(&self, wire: WireId) -> UphillPipsIter<'_> {
let iter = unsafe { npnr_context_get_pips_uphill(self, wire) }; let iter = unsafe { npnr_context_get_pips_uphill(self, wire) };
UphillPipsIter { UphillPipsIter {
iter, iter,
phantom_data: Default::default(), phantom_data: PhantomData,
} }
} }
#[must_use]
pub fn pip_location(&self, pip: PipId) -> Loc { pub fn pip_location(&self, pip: PipId) -> Loc {
unsafe { npnr_context_get_pip_location(self, pip) } unsafe { npnr_context_get_pip_location(self, pip) }
} }
#[must_use]
pub fn pip_direction(&self, pip: PipId) -> Loc { pub fn pip_direction(&self, pip: PipId) -> Loc {
let mut src = Loc{x: 0, y: 0, z: 0}; let mut src = Loc{x: 0, y: 0, z: 0};
let mut dst = Loc{x: 0, y: 0, z: 0}; let mut dst = Loc{x: 0, y: 0, z: 0};
@ -305,6 +350,7 @@ impl Context {
Loc{x: dst.x - src.x, y: dst.y - src.y, z: 0} Loc{x: dst.x - src.x, y: dst.y - src.y, z: 0}
} }
#[must_use]
pub fn pip_avail_for_net(&self, pip: PipId, net: &mut NetInfo) -> bool { pub fn pip_avail_for_net(&self, pip: PipId, net: &mut NetInfo) -> bool {
unsafe { npnr_context_check_pip_avail_for_net(self, pip, net) } unsafe { npnr_context_check_pip_avail_for_net(self, pip, net) }
} }
@ -313,42 +359,48 @@ impl Context {
unsafe { npnr_context_check(self) } unsafe { npnr_context_check(self) }
} }
#[must_use]
pub fn debug(&self) -> bool { pub fn debug(&self) -> bool {
unsafe { npnr_context_debug(self) } unsafe { npnr_context_debug(self) }
} }
#[must_use]
pub fn id(&self, s: &str) -> IdString { pub fn id(&self, s: &str) -> IdString {
let s = std::ffi::CString::new(s).unwrap(); let s = std::ffi::CString::new(s).unwrap();
unsafe { npnr_context_id(self, s.as_ptr()) } unsafe { npnr_context_id(self, s.as_ptr()) }
} }
#[must_use]
pub fn name_of(&self, s: IdString) -> &CStr { pub fn name_of(&self, s: IdString) -> &CStr {
let _lock = RINGBUFFER_MUTEX.lock().unwrap(); let _lock = RINGBUFFER_MUTEX.lock().unwrap();
unsafe { CStr::from_ptr(npnr_context_name_of(self, s)) } unsafe { CStr::from_ptr(npnr_context_name_of(self, s)) }
} }
#[must_use]
pub fn name_of_pip(&self, pip: PipId) -> &CStr { pub fn name_of_pip(&self, pip: PipId) -> &CStr {
let _lock = RINGBUFFER_MUTEX.lock().unwrap(); let _lock = RINGBUFFER_MUTEX.lock().unwrap();
unsafe { CStr::from_ptr(npnr_context_name_of_pip(self, pip)) } unsafe { CStr::from_ptr(npnr_context_name_of_pip(self, pip)) }
} }
#[must_use]
pub fn name_of_wire(&self, wire: WireId) -> &CStr { pub fn name_of_wire(&self, wire: WireId) -> &CStr {
let _lock = RINGBUFFER_MUTEX.lock().unwrap(); let _lock = RINGBUFFER_MUTEX.lock().unwrap();
unsafe { CStr::from_ptr(npnr_context_name_of_wire(self, wire)) } unsafe { CStr::from_ptr(npnr_context_name_of_wire(self, wire)) }
} }
#[must_use]
pub fn verbose(&self) -> bool { pub fn verbose(&self) -> bool {
unsafe { npnr_context_verbose(self) } unsafe { npnr_context_verbose(self) }
} }
} }
extern "C" { unsafe extern "C" {
pub fn npnr_log_info(format: *const c_char); pub fn npnr_log_info(format: *const c_char);
pub fn npnr_log_error(format: *const c_char); pub fn npnr_log_error(format: *const c_char);
fn npnr_belid_null() -> BelId; safe fn npnr_belid_null() -> BelId;
fn npnr_wireid_null() -> WireId; safe fn npnr_wireid_null() -> WireId;
fn npnr_pipid_null() -> PipId; safe fn npnr_pipid_null() -> PipId;
fn npnr_context_get_grid_dim_x(ctx: &Context) -> libc::c_int; fn npnr_context_get_grid_dim_x(ctx: &Context) -> libc::c_int;
fn npnr_context_get_grid_dim_y(ctx: &Context) -> libc::c_int; fn npnr_context_get_grid_dim_y(ctx: &Context) -> libc::c_int;
@ -461,14 +513,15 @@ impl<'a> Nets<'a> {
/// Create a new store for the nets of a context. /// 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. /// Note that this leaks memory created by nextpnr; the intention is this is called once.
pub fn new(ctx: &'a Context) -> Nets<'a> { #[must_use]
pub fn new(ctx: &'a Context) -> Self {
let mut names: *mut libc::c_int = std::ptr::null_mut(); let mut names: *mut libc::c_int = std::ptr::null_mut();
let mut nets_ptr: *mut *mut NetInfo = std::ptr::null_mut(); let mut nets_ptr: *mut *mut NetInfo = std::ptr::null_mut();
let size = unsafe { let size = unsafe {
npnr_context_nets_leak( npnr_context_nets_leak(
ctx, ctx,
&mut names as *mut *mut libc::c_int, &raw mut names,
&mut nets_ptr as *mut *mut *mut NetInfo, &raw mut nets_ptr,
) )
}; };
let mut nets = HashMap::new(); let mut nets = HashMap::new();
@ -481,9 +534,9 @@ impl<'a> Nets<'a> {
// SAFETY: net is not null because it's a &mut, and users is only written to. // 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. // Leaking memory is the most convenient FFI I could think of.
let len = let len =
unsafe { npnr_netinfo_users_leak(net, &mut users_ptr as *mut *mut *const PortRef) }; unsafe { npnr_netinfo_users_leak(net, &raw mut users_ptr) };
let users_slice = let users_slice =
unsafe { slice::from_raw_parts(users_ptr as *mut &PortRef, len as usize) }; unsafe { slice::from_raw_parts(users_ptr.cast::<&PortRef>(), len as usize) };
let index = index_to_net.len() as i32; let index = index_to_net.len() as i32;
index_to_net.push(name); index_to_net.push(name);
unsafe { unsafe {
@ -502,27 +555,33 @@ impl<'a> Nets<'a> {
} }
/// Find net users given a net's name. /// Find net users given a net's name.
#[must_use]
pub fn users_by_name(&self, net: IdString) -> Option<&&[&PortRef]> { pub fn users_by_name(&self, net: IdString) -> Option<&&[&PortRef]> {
self.users.get(&net) self.users.get(&net)
} }
/// Return the number of nets in the store. /// Return the number of nets in the store.
#[must_use]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.nets.len() self.nets.len()
} }
#[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.nets.len() == 0 self.nets.len() == 0
} }
#[must_use]
pub fn name_from_index(&self, index: NetIndex) -> IdString { pub fn name_from_index(&self, index: NetIndex) -> IdString {
self.index_to_net[index.0 as usize] self.index_to_net[index.0 as usize]
} }
#[must_use]
pub fn net_from_index(&self, index: NetIndex) -> &NetInfo { pub fn net_from_index(&self, index: NetIndex) -> &NetInfo {
self.nets.get(&self.name_from_index(index)).unwrap() self.nets.get(&self.name_from_index(index)).unwrap()
} }
#[must_use]
pub fn to_vec(&self) -> Vec<(&IdString, &&mut NetInfo)> { pub fn to_vec(&self) -> Vec<(&IdString, &&mut NetInfo)> {
let mut v = Vec::new(); let mut v = Vec::new();
v.extend(self.nets.iter()); v.extend(self.nets.iter());