From 2e4ef6f71fc81d70a05971e1bd590b7ec8fd3a41 Mon Sep 17 00:00:00 2001 From: Lofty Date: Wed, 24 Sep 2025 12:59:30 +0100 Subject: [PATCH] rust: small updates (#1560) --- rust/Cargo.toml | 2 +- rust/nextpnr/Cargo.toml | 2 +- rust/nextpnr/src/lib.rs | 101 +++++++++++++++++++++++++++++++--------- 3 files changed, 82 insertions(+), 23 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index edf09dfe..65586697 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,5 +1,5 @@ [workspace] - +resolver = "2" members = [ "nextpnr", "example_printnets", diff --git a/rust/nextpnr/Cargo.toml b/rust/nextpnr/Cargo.toml index aa0fb488..8052d132 100644 --- a/rust/nextpnr/Cargo.toml +++ b/rust/nextpnr/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "nextpnr" version = "0.1.0" -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/rust/nextpnr/src/lib.rs b/rust/nextpnr/src/lib.rs index 92427579..e8bae386 100644 --- a/rust/nextpnr/src/lib.rs +++ b/rust/nextpnr/src/lib.rs @@ -21,6 +21,7 @@ pub struct CellInfo { } impl CellInfo { + #[must_use] pub fn location(&self) -> Loc { unsafe { npnr_cellinfo_get_location(self) } } @@ -36,10 +37,12 @@ impl NetInfo { unsafe { npnr_netinfo_driver(self) } } + #[must_use] pub fn is_global(&self) -> bool { unsafe { npnr_netinfo_is_global(self) } } + #[must_use] pub fn index(&self) -> NetIndex { unsafe { npnr_netinfo_udata(self) } } @@ -50,6 +53,7 @@ impl NetInfo { pub struct NetIndex(i32); impl NetIndex { + #[must_use] pub fn into_inner(self) -> i32 { self.0 } @@ -61,6 +65,7 @@ pub struct PortRef { } impl PortRef { + #[must_use] pub fn cell(&self) -> Option<&CellInfo> { // SAFETY: handing out &s is safe when we have &self. unsafe { npnr_portref_cell(self) } @@ -76,14 +81,21 @@ pub struct IdString(libc::c_int); #[repr(transparent)] pub struct BelId(u64); +impl Default for BelId { + fn default() -> Self { + Self::null() + } +} + impl BelId { /// Return a sentinel value that represents an invalid bel. + #[must_use] pub fn null() -> Self { - // SAFETY: BelId() has no safety requirements. - unsafe { npnr_belid_null() } + npnr_belid_null() } /// Check if this bel is invalid. + #[must_use] pub fn is_null(self) -> bool { self == Self::null() } @@ -93,9 +105,16 @@ impl BelId { #[repr(transparent)] pub struct PipId(u64); +impl Default for PipId { + fn default() -> Self { + Self::null() + } +} + impl PipId { + #[must_use] pub fn null() -> Self { - unsafe { npnr_pipid_null() } + npnr_pipid_null() } } @@ -103,14 +122,22 @@ impl PipId { #[repr(transparent)] pub struct WireId(u64); +impl Default for WireId { + fn default() -> Self { + Self::null() + } +} + impl WireId { /// Return a sentinel value that represents an invalid wire. + #[must_use] pub fn null() -> Self { // SAFETY: WireId() has no safety requirements. - unsafe { npnr_wireid_null() } + npnr_wireid_null() } /// Check if this wire is invalid. + #[must_use] pub fn is_null(self) -> bool { self == Self::null() } @@ -144,11 +171,13 @@ pub struct Context { impl Context { /// 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 { 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). + #[must_use] pub fn grid_dim_y(&self) -> i32 { 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. + #[must_use] 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. + /// 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) { let _lock = ARCH_MUTEX.lock().unwrap(); unsafe { npnr_context_bind_wire(self, wire, net, strength) } @@ -195,36 +225,44 @@ impl Context { } /// Get the source wire for a pip. + #[must_use] 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. + #[must_use] 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? + #[must_use] pub fn estimate_delay(&self, src: WireId, dst: WireId) -> f32 { unsafe { npnr_context_estimate_delay(self, src, dst) } } + #[must_use] pub fn pip_delay(&self, pip: PipId) -> f32 { unsafe { npnr_context_get_pip_delay(self, pip) } } + #[must_use] pub fn wire_delay(&self, wire: WireId) -> f32 { unsafe { npnr_context_get_wire_delay(self, wire) } } + #[must_use] pub fn delay_epsilon(&self) -> f32 { unsafe { npnr_context_delay_epsilon(self) } } + #[must_use] pub fn source_wire(&self, net: &NetInfo) -> WireId { unsafe { npnr_context_get_netinfo_source_wire(self, net) } } + #[must_use] pub fn sink_wires(&self, net: &NetInfo, sink: &PortRef) -> Vec { let mut v = Vec::new(); let mut n = 0; @@ -239,41 +277,48 @@ impl Context { v } - pub fn bels(&self) -> BelIter { + #[must_use] + pub fn bels(&self) -> BelIter<'_> { let iter = unsafe { npnr_context_get_bels(self) }; 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) }; 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) }; 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) }; DownhillPipsIter { 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) }; UphillPipsIter { iter, - phantom_data: Default::default(), + phantom_data: PhantomData, } } + #[must_use] pub fn pip_location(&self, pip: PipId) -> Loc { unsafe { npnr_context_get_pip_location(self, pip) } } + #[must_use] pub fn pip_direction(&self, pip: PipId) -> Loc { let mut src = 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} } + #[must_use] pub fn pip_avail_for_net(&self, pip: PipId, net: &mut NetInfo) -> bool { unsafe { npnr_context_check_pip_avail_for_net(self, pip, net) } } @@ -313,42 +359,48 @@ impl Context { unsafe { npnr_context_check(self) } } + #[must_use] pub fn debug(&self) -> bool { unsafe { npnr_context_debug(self) } } + #[must_use] pub fn id(&self, s: &str) -> IdString { let s = std::ffi::CString::new(s).unwrap(); unsafe { npnr_context_id(self, s.as_ptr()) } } + #[must_use] pub fn name_of(&self, s: IdString) -> &CStr { let _lock = RINGBUFFER_MUTEX.lock().unwrap(); unsafe { CStr::from_ptr(npnr_context_name_of(self, s)) } } + #[must_use] pub fn name_of_pip(&self, pip: PipId) -> &CStr { let _lock = RINGBUFFER_MUTEX.lock().unwrap(); unsafe { CStr::from_ptr(npnr_context_name_of_pip(self, pip)) } } + #[must_use] pub fn name_of_wire(&self, wire: WireId) -> &CStr { let _lock = RINGBUFFER_MUTEX.lock().unwrap(); unsafe { CStr::from_ptr(npnr_context_name_of_wire(self, wire)) } } + #[must_use] pub fn verbose(&self) -> bool { unsafe { npnr_context_verbose(self) } } } -extern "C" { +unsafe 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; + safe fn npnr_belid_null() -> BelId; + safe fn npnr_wireid_null() -> WireId; + safe fn npnr_pipid_null() -> PipId; fn npnr_context_get_grid_dim_x(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. /// /// 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 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, + &raw mut names, + &raw mut nets_ptr, ) }; 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. // 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 *const PortRef) }; + unsafe { npnr_netinfo_users_leak(net, &raw mut users_ptr) }; 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; index_to_net.push(name); unsafe { @@ -502,27 +555,33 @@ impl<'a> Nets<'a> { } /// Find net users given a net's name. + #[must_use] pub fn users_by_name(&self, net: IdString) -> Option<&&[&PortRef]> { self.users.get(&net) } /// Return the number of nets in the store. + #[must_use] pub fn len(&self) -> usize { self.nets.len() } + #[must_use] pub fn is_empty(&self) -> bool { self.nets.len() == 0 } + #[must_use] pub fn name_from_index(&self, index: NetIndex) -> IdString { self.index_to_net[index.0 as usize] } + #[must_use] pub fn net_from_index(&self, index: NetIndex) -> &NetInfo { self.nets.get(&self.name_from_index(index)).unwrap() } + #[must_use] pub fn to_vec(&self) -> Vec<(&IdString, &&mut NetInfo)> { let mut v = Vec::new(); v.extend(self.nets.iter());