1
0
mirror of https://github.com/YosysHQ/nextpnr.git synced 2026-01-11 23:53:21 +00:00

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]
resolver = "2"
members = [
"nextpnr",
"example_printnets",

View File

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

View File

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