vpi_export/
lib.rs

1#![no_std]
2#![allow(incomplete_features)]
3#![feature(generic_const_exprs)]
4#![warn(missing_docs)]
5
6//! Vpi-export
7//!
8//! Allows exporting of rust functions as VPI functions
9
10extern crate alloc;
11
12#[doc(hidden)]
13pub mod __hidden__;
14use core::{ffi::CStr, ptr::NonNull};
15
16pub use vpi_user;
17mod bitvec;
18mod clk;
19mod handle;
20mod impls;
21mod vpi_iter;
22pub use bitvec::BitVector;
23pub use clk::Clk;
24pub use vpi_export_macro::{bitvec, vpi_module, vpi_task, vpi_top};
25pub use vpi_user::vpi_printf;
26use vpi_user::{vpiSimTime, vpi_get_time};
27
28mod __private {
29    pub trait Sealed {}
30}
31
32pub use handle::Handle;
33pub use vpi_iter::VpiIter;
34
35/// Not null [vpi_user::vpiHandle]
36pub type RawHandle = NonNull<vpi_user::PLI_UINT32>;
37
38///Possible result of a vpi task
39pub trait VpiTaskResult: __private::Sealed {
40    ///Conversion into [VpiTaskResult]
41    fn into_vpi_result(self) -> Result<()>;
42}
43
44///Errors relating to VPI
45#[derive(Debug)]
46#[non_exhaustive]
47pub enum VpiError {
48    ///String conversion error from verilog to rust
49    Utf8Error(core::str::Utf8Error),
50    ///Module was not found
51    NoModule(&'static CStr),
52    ///Vector length missmat
53    BitVectorLengthMissMatch {
54        ///Expected length
55        expected: usize,
56        ///Obtained length
57        actual: usize,
58    },
59    ///Period too small
60    PeriodTooSmall,
61}
62
63///Result relating to a vpi result
64pub type Result<T> = core::result::Result<T, VpiError>;
65
66///Conversion trait from verilog to rust
67pub trait FromVpiHandle: Sized {
68    ///Conversion function from verilog to rust. In implementation, use the function
69    /// [crate::vpi_user::vpi_get_value] to obtain the value to convert.
70    /// # Safety
71    /// handle must NOT be dangling or null
72    unsafe fn from_vpi_handle(handle: RawHandle) -> Result<Self>;
73}
74
75///Conversion trait from rust to verilog
76pub trait StoreIntoVpiHandle: Sized {
77    ///Conversion function from rust to verilog. In implementation, use the function
78    /// [crate::vpi_user::vpi_put_value] to conver type to verilog.
79    /// # Safety
80    /// handle must NOT be dangling or null
81    unsafe fn store_into_vpi_handle(&self, handle: RawHandle) -> Result<()>;
82}
83
84unsafe extern "C" fn cb(data: *mut vpi_user::t_cb_data) -> i32 {
85    let data = unsafe { &mut *data };
86    let data = unsafe { &mut *(data.user_data as *mut CallbackData) };
87    let f = unsafe { &mut *data.callback };
88    f();
89    0
90}
91
92struct CallbackData {
93    raw_callback_pointer: *mut u8,
94    callback: *mut dyn FnMut(),
95    callback_layout: alloc::alloc::Layout,
96}
97
98///Callback handle wrapper
99pub struct VpiCallbackHandle(vpi_user::vpiHandle, *const CallbackData);
100
101///Register callback for on value change
102pub fn on_value_change<E: FromVpiHandle, F: FnMut() + Sized + 'static>(
103    value: Handle<E>,
104    f: F,
105) -> VpiCallbackHandle {
106    use alloc::alloc::{alloc, handle_alloc_error, Layout};
107    let callback_layout = Layout::new::<F>();
108    let raw_callback_pointer = unsafe { alloc(callback_layout) };
109    let callback = raw_callback_pointer as *mut F;
110    if callback.is_null() {
111        handle_alloc_error(callback_layout);
112    }
113    unsafe {
114        callback.write(f);
115    }
116    let data_layout = Layout::new::<CallbackData>();
117    let data = unsafe { alloc(data_layout) } as *mut CallbackData;
118    unsafe {
119        data.write(CallbackData {
120            raw_callback_pointer,
121            callback,
122            callback_layout,
123        });
124    }
125    let mut cb_data = vpi_user::t_cb_data {
126        reason: vpi_user::cbValueChange as i32,
127        cb_rtn: Some(cb),
128        obj: value.handle.as_ptr(),
129        user_data: data as *mut vpi_user::PLI_BYTE8,
130        ..Default::default()
131    };
132    VpiCallbackHandle(unsafe { vpi_user::vpi_register_cb(&mut cb_data) }, data)
133}
134
135fn on_delay_internal<F: FnMut() + Sized + 'static>(delay: u64, f: F) -> VpiCallbackHandle {
136    use alloc::alloc::{alloc, handle_alloc_error, Layout};
137    let callback_layout = Layout::new::<F>();
138    let raw_callback_pointer = unsafe { alloc(callback_layout) };
139    let callback = raw_callback_pointer as *mut F;
140    if callback.is_null() {
141        handle_alloc_error(callback_layout);
142    }
143    unsafe {
144        callback.write(f);
145    }
146    let data_layout = Layout::new::<CallbackData>();
147    let data = unsafe { alloc(data_layout) } as *mut CallbackData;
148    unsafe {
149        data.write(CallbackData {
150            raw_callback_pointer,
151            callback,
152            callback_layout,
153        });
154    }
155
156    let mut cb_data = vpi_user::t_cb_data {
157        reason: vpi_user::cbAfterDelay as i32,
158        cb_rtn: Some(cb),
159        time: &mut vpi_user::t_vpi_time {
160            type_: vpiSimTime as i32,
161            high: (delay >> 32) as u32,
162            low: (delay & ((!0u64) >> 32)) as u32,
163            ..Default::default()
164        },
165        user_data: data as *mut vpi_user::PLI_BYTE8,
166        ..Default::default()
167    };
168    VpiCallbackHandle(unsafe { vpi_user::vpi_register_cb(&mut cb_data) }, data)
169}
170
171///Callback after delay
172pub fn on_delay<F: FnOnce() + Sized + 'static>(delay: u64, f: F) -> VpiCallbackHandle {
173    let mut f = Some(f);
174    on_delay_internal(delay, move || {
175        if let Some(f) = core::mem::take(&mut f) {
176            f();
177        }
178    })
179}
180
181///Obtain simulation time
182pub fn get_time() -> u64 {
183    let mut t = vpi_user::t_vpi_time {
184        type_: vpiSimTime as i32,
185        ..Default::default()
186    };
187    unsafe { vpi_get_time(core::ptr::null_mut(), &mut t) };
188    let mut result = t.high as u64;
189    result <<= 32;
190    result |= t.low as u64;
191    result
192}
193
194/// Equivalent to $finish
195pub fn finish() {
196    unsafe {
197        vpi_user::vpi_control(vpi_user::vpiFinish as i32);
198    }
199}
200
201///Remove callback handle
202pub fn remove_cb(cb_handle: VpiCallbackHandle) {
203    //TODO free data even though there does not seem to be a sensible way of doing it
204    //Data is shared with the simulator
205    let a = unsafe { &*cb_handle.1 };
206    let _ = a.callback_layout;
207    let _ = a.raw_callback_pointer;
208    unsafe { vpi_user::vpi_remove_cb(cb_handle.0) };
209}
210
211///Print function that internally will use the simulator's print function.
212pub fn print(c: &core::ffi::CStr) {
213    unsafe {
214        vpi_user::vpi_printf(c.as_ptr() as *mut core::ffi::c_char);
215    }
216}
217
218///Print function that internally will use the simulator's print function with an appended new line.
219pub fn println(c: &core::ffi::CStr) {
220    print(c);
221    print(c"\n");
222}