1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
use crate::ObjectType;
use vpi_sys::{vpiHandle, PLI_INT32};
/// Wrapper around a raw VPI object handle.
///
/// This type provides convenience helpers for common handle operations and
/// iteration over child objects.
#[derive(Debug, Clone)]
pub struct Handle {
/// Underlying simulator-owned VPI handle pointer.
handle: vpiHandle,
}
impl Default for Handle {
fn default() -> Self {
Self::null()
}
}
impl PartialEq for Handle {
fn eq(&self, other: &Self) -> bool {
unsafe { vpi_sys::vpi_compare_objects(self.handle, other.handle) != 0 }
}
}
impl Drop for Handle {
fn drop(&mut self) {
#[cfg(feature = "release_handle")]
if !self.is_null() {
unsafe {
vpi_sys::vpi_release_handle(self.handle);
}
}
}
}
impl Handle {
/// Creates a null handle.
#[must_use]
pub fn null() -> Self {
Self {
handle: std::ptr::null_mut(),
}
}
/// Returns `true` if this handle is null.
#[must_use]
pub fn is_null(&self) -> bool {
self.handle.is_null()
}
/// Returns the underlying raw VPI handle.
#[must_use]
pub fn as_raw(&self) -> vpiHandle {
self.handle
}
/// Replaces the current handle with null.
///
/// This is used when ownership of the raw handle is not held by this type.
pub fn clear(&mut self) {
self.handle = std::ptr::null_mut();
}
/// Constructs a [`Handle`] from a raw VPI handle pointer.
pub fn from_raw(raw: vpiHandle) -> Self {
Self { handle: raw }
}
/// Looks up a handle by hierarchical name.
///
/// Returns a null handle when the object is not found.
#[must_use]
pub fn handle_by_name(name: &str) -> Self {
let c_name = std::ffi::CString::new(name).expect("CString::new failed");
let handle = unsafe {
vpi_sys::vpi_handle_by_name(c_name.as_ptr().cast_mut(), std::ptr::null_mut())
};
Self::from_raw(handle)
}
/// Returns an iterator handle for objects of `typ` under this handle.
#[must_use]
pub fn iterator(&self, typ: ObjectType) -> HandleIterator {
let raw = unsafe { vpi_sys::vpi_iterate(typ as PLI_INT32, self.as_raw()) };
HandleIterator {
iter: Handle::from_raw(raw),
}
}
/// Returns a related object handle selected by `typ`.
///
/// Returns a null handle when the relation is unavailable.
#[must_use]
pub fn get(&self, typ: ObjectType) -> Self {
let handle = unsafe { vpi_sys::vpi_handle(typ as PLI_INT32, self.as_raw()) };
Self::from_raw(handle)
}
/// Returns a child handle by index.
///
/// Returns a null handle when `index` is out of range.
#[must_use]
pub fn handle_by_index(&self, index: i32) -> Self {
let handle = unsafe { vpi_sys::vpi_handle_by_index(self.as_raw(), index) };
Self::from_raw(handle)
}
/// Iterates across multiple object kinds and flattens all resulting handles.
pub fn iterators<'a>(&'a self, typ: &'a [ObjectType]) -> impl Iterator<Item = Handle> + 'a {
typ.iter().copied().flat_map(move |t| self.iterator(t))
}
/// Returns a related object handle selected by `typ` using two reference handles.
///
/// This wraps `vpi_handle_multi` for APIs that require two source handles.
/// Returns a null handle when this handle or `other` is null, or when the
/// relation is unavailable.
#[must_use]
pub fn get_multi(&self, typ: ObjectType, other: &Handle) -> Self {
if self.is_null() || other.is_null() {
return Self::null();
}
let handle =
unsafe { vpi_sys::vpi_handle_multi(typ as PLI_INT32, self.as_raw(), other.as_raw()) };
Self::from_raw(handle)
}
/// Returns a child handle by multiple indices.
///
/// This wraps `vpi_handle_by_multi_index` and is used for
/// multidimensional arrays. Returns a null handle when this handle is null,
/// when `indices` is empty, when the index count exceeds VPI limits, or
/// when no object exists at the requested index tuple.
#[must_use]
pub fn handle_by_multi_index(&self, indices: impl AsRef<[i32]>) -> Self {
let indices = indices.as_ref();
if self.is_null() || indices.is_empty() {
return Self::null();
}
let Ok(num_index) = i32::try_from(indices.len()) else {
return Self::null();
};
let handle = unsafe {
vpi_sys::vpi_handle_by_multi_index(
self.as_raw(),
num_index as PLI_INT32,
indices.as_ptr().cast_mut(),
)
};
Self::from_raw(handle)
}
/// Convenience helper for multi-handle traversal.
///
/// First traverses to `typ` via `vpi_handle`, then resolves a
/// multidimensional element with `vpi_handle_by_multi_index`.
#[must_use]
pub fn multi_handle_traversal(&self, typ: ObjectType, indices: impl AsRef<[i32]>) -> Self {
self.get(typ).handle_by_multi_index(indices)
}
}
/// Iterator over VPI scan results from `vpi_iterate`/`vpi_scan`.
pub struct HandleIterator {
/// Internal iterator handle consumed by successive `vpi_scan` calls.
pub(crate) iter: Handle,
}
impl Iterator for HandleIterator {
type Item = Handle;
fn next(&mut self) -> Option<Self::Item> {
if self.iter.is_null() {
return None;
}
let next = Handle::from_raw(unsafe { vpi_sys::vpi_scan(self.iter.as_raw()) });
if next.is_null() {
// The handle is automatically released when the iterator is exhausted
self.iter.clear();
None
} else {
Some(next)
}
}
}