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}