Skip to main content

vpi/
systf.rs

1//! Helpers for registering and implementing VPI system tasks/functions.
2//!
3//! This module provides typed wrappers around `vpi_register_systf` and
4//! convenience helpers for reading system task/function arguments inside
5//! `calltf` callbacks.
6//!
7//! # Example
8//!
9//! ```no_run
10//! use std::ffi::CStr;
11//! use std::os::raw::c_char;
12//! use vpi::{
13//!     current_systf_call, get_systf_arg, register_systf, SysFuncType, SystfKind, Value,
14//!     ValueType,
15//! };
16//!
17//! static FUNC_NAME: &CStr = c"$add_one";
18//!
19//! unsafe extern "C" fn compiletf(_user_data: *mut c_char) -> i32 {
20//!     0
21//! }
22//!
23//! unsafe extern "C" fn calltf_add_one(_user_data: *mut c_char) -> i32 {
24//!     let arg = match get_systf_arg(0, ValueType::Int) {
25//!         Some(Value::Int(v)) => v,
26//!         _ => 0,
27//!     };
28//!     let result = arg + 1;
29//!
30//!     let call = current_systf_call();
31//!     let _ = call.put_value(&Value::Int(result));
32//!     0
33//! }
34//!
35//! let _func_handle = register_systf(
36//!     SystfKind::Func,
37//!     FUNC_NAME,
38//!     Some(calltf_add_one),
39//!     Some(compiletf),
40//!     None,
41//!     std::ptr::null_mut(),
42//!     Some(SysFuncType::Sized),
43//! );
44//! ```
45//!
46//! In Verilog/SystemVerilog, the registered function is called using its `$`
47//! name from expressions like a built-in system function:
48//!
49//! ```verilog
50//! module tb;
51//!   integer x;
52//!   initial begin
53//!     x = $add_one(41);
54//!     $display("x=%0d", x); // expected: x=42
55//!   end
56//! endmodule
57//! ```
58
59use std::ffi::CStr;
60use std::os::raw::c_char;
61
62use crate::{Handle, ObjectType, SysFuncType, Value, ValueType};
63
64/// Raw VPI registration record type.
65pub type RawSystfData = vpi_sys::s_vpi_systf_data;
66
67/// Function pointer type for VPI system task/function callbacks.
68pub type SystfCallback = unsafe extern "C" fn(*mut c_char) -> i32;
69
70/// Owned view of data returned by `vpi_get_systf_info`.
71#[derive(Debug, Clone)]
72pub struct SystfInfo {
73    /// Raw system task/function kind (`vpiSysTask` or `vpiSysFunc`).
74    pub kind: i32,
75    /// Raw system-function return classification from VPI.
76    pub sys_func_type: i32,
77    /// Registered system task/function name, if present.
78    pub name: Option<String>,
79    /// `calltf` callback pointer returned by VPI.
80    pub calltf: Option<SystfCallback>,
81    /// `compiletf` callback pointer returned by VPI.
82    pub compiletf: Option<SystfCallback>,
83    /// `sizetf` callback pointer returned by VPI.
84    pub sizetf: Option<SystfCallback>,
85    /// User-data pointer returned by VPI.
86    pub user_data: *mut c_char,
87}
88
89/// System task/function registration kind.
90#[repr(u32)]
91#[derive(Debug, Copy, Clone, PartialEq, Eq)]
92pub enum SystfKind {
93    /// Register a system task.
94    Task = vpi_sys::vpiSysTask,
95    /// Register a system function.
96    Func = vpi_sys::vpiSysFunc,
97}
98
99/// Registers a raw `s_vpi_systf_data` record with the simulator.
100///
101/// This is a thin wrapper around `vpi_register_systf`.
102#[must_use]
103pub fn register_systf_raw(data: &mut RawSystfData) -> Handle {
104    let handle = unsafe { vpi_sys::vpi_register_systf(data) };
105    Handle::from_raw(handle)
106}
107
108/// Registers a system task or function with typed inputs.
109///
110/// `name` must be static and begin with `$` to satisfy VPI requirements.
111///
112/// For [`SystfKind::Task`], `sys_func_type` is ignored and set to `vpiSysTask`.
113/// For [`SystfKind::Func`], `sys_func_type` defaults to `vpiIntFunc` when omitted.
114#[must_use]
115pub fn register_systf(
116    kind: SystfKind,
117    name: &'static CStr,
118    calltf: Option<SystfCallback>,
119    compiletf: Option<SystfCallback>,
120    sizetf: Option<SystfCallback>,
121    user_data: *mut c_char,
122    sys_func_type: Option<SysFuncType>,
123) -> Handle {
124    let sysfunctype = match kind {
125        SystfKind::Task => vpi_sys::vpiSysTask as i32,
126        SystfKind::Func => sys_func_type.map_or(vpi_sys::vpiIntFunc as i32, |t| t as i32),
127    };
128
129    let mut data = RawSystfData {
130        type_: kind as i32,
131        sysfunctype,
132        tfname: name.as_ptr().cast_mut(),
133        calltf,
134        compiletf,
135        sizetf,
136        user_data,
137    };
138
139    register_systf_raw(&mut data)
140}
141
142/// Returns the current system task/function call handle.
143///
144/// This is typically used inside `calltf` callbacks to access arguments or
145/// assign a return value for system functions.
146#[must_use]
147pub fn current_systf_call() -> Handle {
148    Handle::null().get(ObjectType::SysTfCall)
149}
150
151/// Retrieves raw system task/function metadata for a VPI object.
152///
153/// This is a thin wrapper around `vpi_get_systf_info`.
154///
155/// Returns `None` when `object` is null.
156#[must_use]
157pub fn get_systf_info_raw(object: &Handle) -> Option<RawSystfData> {
158    if object.is_null() {
159        return None;
160    }
161
162    let mut data = RawSystfData {
163        type_: 0,
164        sysfunctype: 0,
165        tfname: std::ptr::null_mut(),
166        calltf: None,
167        compiletf: None,
168        sizetf: None,
169        user_data: std::ptr::null_mut(),
170    };
171
172    unsafe {
173        vpi_sys::vpi_get_systf_info(object.as_raw(), &raw mut data);
174    }
175
176    Some(data)
177}
178
179/// Retrieves owned system task/function metadata for a VPI object.
180///
181/// This calls [`get_systf_info_raw`] and converts the returned C string name
182/// into an owned Rust [`String`] when present.
183///
184/// Returns `None` when `object` is null.
185#[must_use]
186pub fn get_systf_info(object: &Handle) -> Option<SystfInfo> {
187    let data = get_systf_info_raw(object)?;
188    let name = if data.tfname.is_null() {
189        None
190    } else {
191        Some(
192            unsafe { CStr::from_ptr(data.tfname) }
193                .to_string_lossy()
194                .into_owned(),
195        )
196    };
197
198    Some(SystfInfo {
199        kind: data.type_,
200        sys_func_type: data.sysfunctype,
201        name,
202        calltf: data.calltf,
203        compiletf: data.compiletf,
204        sizetf: data.sizetf,
205        user_data: data.user_data,
206    })
207}
208
209/// Retrieves owned system task/function metadata for the current `calltf`.
210///
211/// Returns `None` when called outside a `calltf` context.
212#[must_use]
213pub fn current_systf_info() -> Option<SystfInfo> {
214    let call = current_systf_call();
215    get_systf_info(&call)
216}
217
218/// Returns one system task/function argument by index in a requested format.
219///
220/// The index is zero-based (`0` is the first argument).
221/// Returns `None` when called outside a `calltf` context, when the index is
222/// out of range, or when the simulator cannot provide the requested value
223/// format.
224#[must_use]
225pub fn get_systf_arg(index: u32, format: ValueType) -> Option<Value> {
226    let call = current_systf_call();
227    if call.is_null() {
228        return None;
229    }
230
231    call.iterator(ObjectType::Argument)
232        .nth(index as usize)
233        .and_then(|arg| arg.get_value(format))
234}
235
236/// Returns system task/function arguments using per-argument value formats.
237///
238/// Each entry in `formats` corresponds to one argument position and controls
239/// the `vpi_get_value` format requested for that argument.
240///
241/// The returned vector has the same length as `formats`:
242///
243/// - `Some(Value)` when the argument exists and can be decoded in the
244///   requested format.
245/// - `None` when called outside a `calltf` context, when the argument is
246///   missing, or when value retrieval fails.
247#[must_use]
248pub fn get_systf_args(formats: impl AsRef<[ValueType]>) -> Vec<Option<Value>> {
249    let call = current_systf_call();
250    if call.is_null() {
251        return std::iter::repeat_with(|| None)
252            .take(formats.as_ref().len())
253            .collect();
254    }
255
256    let mut args = call.iterator(ObjectType::Argument);
257    formats
258        .as_ref()
259        .iter()
260        .map(|format| args.next().and_then(|arg| arg.get_value(*format)))
261        .collect()
262}
263
264#[cfg(test)]
265mod tests {
266    use super::{get_systf_info, get_systf_info_raw};
267    use crate::Handle;
268
269    #[test]
270    fn get_systf_info_on_null_handle_returns_none() {
271        let h = Handle::null();
272        assert!(get_systf_info_raw(&h).is_none());
273        assert!(get_systf_info(&h).is_none());
274    }
275}