use crate::{value::decode_vpi_value, Handle, Time, Value, ValueType};
use num_traits::FromPrimitive;
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)]
pub enum CbReason {
ValueChange = vpi_sys::cbValueChange,
Stmt = vpi_sys::cbStmt,
Force = vpi_sys::cbForce,
Release = vpi_sys::cbRelease,
AtStartOfSimTime = vpi_sys::cbAtStartOfSimTime,
ReadWriteSynch = vpi_sys::cbReadWriteSynch,
ReadOnlySynch = vpi_sys::cbReadOnlySynch,
NextSimTime = vpi_sys::cbNextSimTime,
AfterDelay = vpi_sys::cbAfterDelay,
EndOfCompile = vpi_sys::cbEndOfCompile,
StartOfSimulation = vpi_sys::cbStartOfSimulation,
EndOfSimulation = vpi_sys::cbEndOfSimulation,
Error = vpi_sys::cbError,
TchkViolation = vpi_sys::cbTchkViolation,
StartOfSave = vpi_sys::cbStartOfSave,
EndOfSave = vpi_sys::cbEndOfSave,
StartOfRestart = vpi_sys::cbStartOfRestart,
EndOfRestart = vpi_sys::cbEndOfRestart,
StartOfReset = vpi_sys::cbStartOfReset,
EndOfReset = vpi_sys::cbEndOfReset,
EnterInteractive = vpi_sys::cbEnterInteractive,
ExitInteractive = vpi_sys::cbExitInteractive,
InteractiveScopeChange = vpi_sys::cbInteractiveScopeChange,
UnresolvedSystf = vpi_sys::cbUnresolvedSystf,
PLIError = vpi_sys::cbPLIError,
Assign = vpi_sys::cbAssign,
Deassign = vpi_sys::cbDeassign,
Disable = vpi_sys::cbDisable,
Signal = vpi_sys::cbSignal,
NBASynch = vpi_sys::cbNBASynch,
AtEndOfSimTime = vpi_sys::cbAtEndOfSimTime,
#[cfg(feature = "sv")]
StartOfThread = vpi_sys::cbStartOfThread,
#[cfg(feature = "sv")]
EndOfThread = vpi_sys::cbEndOfThread,
#[cfg(feature = "sv")]
EnterThread = vpi_sys::cbEnterThread,
#[cfg(feature = "sv")]
StartOfFrame = vpi_sys::cbStartOfFrame,
#[cfg(feature = "sv")]
EndOfFrame = vpi_sys::cbEndOfFrame,
#[cfg(feature = "sv")]
SizeChange = vpi_sys::cbSizeChange,
#[cfg(feature = "sv")]
AssertionStart = vpi_sys::cbAssertionStart,
#[cfg(feature = "sv")]
AssertionSuccess = vpi_sys::cbAssertionSuccess,
#[cfg(feature = "sv")]
AssertionFailure = vpi_sys::cbAssertionFailure,
#[cfg(feature = "sv")]
AssertionStepSuccess = vpi_sys::cbAssertionStepSuccess,
#[cfg(feature = "sv")]
AssertionStepFailure = vpi_sys::cbAssertionStepFailure,
#[cfg(feature = "sv")]
AssertionDisable = vpi_sys::cbAssertionDisable,
#[cfg(feature = "sv")]
AssertionEnable = vpi_sys::cbAssertionEnable,
#[cfg(feature = "sv")]
AssertionReset = vpi_sys::cbAssertionReset,
#[cfg(feature = "sv")]
AssertionKill = vpi_sys::cbAssertionKill,
#[cfg(feature = "sv")]
AssertionSysInitialized = vpi_sys::cbAssertionSysInitialized,
#[cfg(feature = "sv")]
AssertionSysOn = vpi_sys::cbAssertionSysOn,
#[cfg(feature = "sv")]
AssertionSysOff = vpi_sys::cbAssertionSysOff,
#[cfg(feature = "sv")]
AssertionSysEnd = vpi_sys::cbAssertionSysEnd,
#[cfg(feature = "sv")]
AssertionSysReset = vpi_sys::cbAssertionSysReset,
#[cfg(feature = "sv")]
AssertionVacuousSuccess = vpi_sys::cbAssertionVacuousSuccess,
#[cfg(feature = "sv")]
AssertionDisabledEvaluation = vpi_sys::cbAssertionDisabledEvaluation,
#[cfg(feature = "sv")]
AssertionSysLock = vpi_sys::cbAssertionSysLock,
#[cfg(feature = "sv")]
AssertionSysUnlock = vpi_sys::cbAssertionSysUnlock,
#[cfg(feature = "sv")]
AssertionLock = vpi_sys::cbAssertionLock,
#[cfg(feature = "sv")]
AssertionUnlock = vpi_sys::cbAssertionUnlock,
#[cfg(feature = "sv")]
AssertionEnablePassAction = vpi_sys::cbAssertionEnablePassAction,
#[cfg(feature = "sv")]
AssertionEnableFailAction = vpi_sys::cbAssertionEnableFailAction,
#[cfg(feature = "sv")]
AssertionDisablePassAction = vpi_sys::cbAssertionDisablePassAction,
#[cfg(feature = "sv")]
AssertionDisableFailAction = vpi_sys::cbAssertionDisableFailAction,
#[cfg(feature = "sv")]
AssertionEnableNonvacuousAction = vpi_sys::cbAssertionEnableNonvacuousAction,
#[cfg(feature = "sv")]
AssertionDisableVacuousAction = vpi_sys::cbAssertionDisableVacuousAction,
#[cfg(feature = "sv")]
AssertionSysEnablePassAction = vpi_sys::cbAssertionSysEnablePassAction,
#[cfg(feature = "sv")]
AssertionSysEnableFailAction = vpi_sys::cbAssertionSysEnableFailAction,
#[cfg(feature = "sv")]
AssertionSysDisablePassAction = vpi_sys::cbAssertionSysDisablePassAction,
#[cfg(feature = "sv")]
AssertionSysDisableFailAction = vpi_sys::cbAssertionSysDisableFailAction,
#[cfg(feature = "sv")]
AssertionSysEnableNonvacuousAction = vpi_sys::cbAssertionSysEnableNonvacuousAction,
#[cfg(feature = "sv")]
AssertionSysDisableVacuousAction = vpi_sys::cbAssertionSysDisableVacuousAction,
#[cfg(feature = "sv")]
CreateObj = vpi_sys::cbCreateObj,
#[cfg(feature = "sv")]
ReclaimObj = vpi_sys::cbReclaimObj,
#[cfg(feature = "sv")]
EndOfObject = vpi_sys::cbEndOfObject,
}
#[derive(Debug)]
pub struct CbData {
pub reason: CbReason,
pub obj: Handle,
pub time: Option<Time>,
pub value: Option<Value>,
pub value_type: Option<ValueType>,
pub index: i32,
}
fn time_from_cb_data(raw_time: vpi_sys::s_vpi_time) -> Option<Time> {
match raw_time.type_ as u32 {
vpi_sys::vpiSimTime => Some(Time::Sim(
u64::from(raw_time.high) << 32 | u64::from(raw_time.low),
)),
vpi_sys::vpiScaledRealTime => Some(Time::ScaledReal(raw_time.real)),
vpi_sys::vpiSuppressTime => Some(Time::Suppress),
_ => None,
}
}
struct CallbackState {
callback: Box<dyn Fn(&CbData)>,
time: Option<Box<vpi_sys::t_vpi_time>>,
value: Option<Box<vpi_sys::t_vpi_value>>,
}
fn default_cb_time() -> vpi_sys::t_vpi_time {
vpi_sys::t_vpi_time {
type_: vpi_sys::vpiSimTime as i32,
high: 0,
low: 0,
real: 0.0,
}
}
fn default_cb_value() -> vpi_sys::t_vpi_value {
vpi_sys::t_vpi_value {
format: vpi_sys::vpiObjTypeVal as i32,
value: vpi_sys::t_vpi_value__bindgen_ty_1 { integer: 0 },
}
}
fn cb_value_with_format(value_type: ValueType) -> vpi_sys::t_vpi_value {
vpi_sys::t_vpi_value {
format: value_type as i32,
value: vpi_sys::t_vpi_value__bindgen_ty_1 { integer: 0 },
}
}
fn register_with_state(
reason: CbReason,
obj: vpi_sys::vpiHandle,
state: Box<CallbackState>,
) -> Handle {
let state_ptr = Box::into_raw(state);
let state_ref = unsafe { &mut *state_ptr };
let handle = unsafe {
let mut cb_data = vpi_sys::s_cb_data {
reason: reason as i32,
cb_rtn: Some(trampoline),
obj,
time: std::ptr::from_mut(
state_ref
.time
.as_mut()
.expect("register_with_state requires time storage")
.as_mut(),
),
value: std::ptr::from_mut(
state_ref
.value
.as_mut()
.expect("register_with_state requires value storage")
.as_mut(),
),
index: 0,
user_data: state_ptr.cast::<vpi_sys::PLI_BYTE8>(),
};
vpi_sys::vpi_register_cb(&raw mut cb_data)
};
if handle.is_null() {
unsafe {
let _ = Box::from_raw(state_ptr);
}
}
Handle::from_raw(handle)
}
impl Handle {
pub fn register_cb<F>(&self, reason: CbReason, callback: F) -> Handle
where
F: Fn(&CbData) + 'static,
{
let user_data = Box::into_raw(Box::new(CallbackState {
callback: Box::new(callback),
time: None,
value: None,
}));
let handle = unsafe {
let mut cb_data = vpi_sys::s_cb_data {
reason: reason as i32,
cb_rtn: Some(trampoline),
obj: self.as_raw(),
time: std::ptr::null_mut(),
value: std::ptr::null_mut(),
index: 0,
user_data: user_data.cast::<vpi_sys::PLI_BYTE8>(),
};
vpi_sys::vpi_register_cb(&raw mut cb_data)
};
if handle.is_null() {
unsafe {
let _ = Box::from_raw(user_data);
}
}
Handle::from_raw(handle)
}
pub fn register_full_cb<F>(&self, reason: CbReason, callback: F) -> Handle
where
F: Fn(&CbData) + 'static,
{
let state = Box::new(CallbackState {
callback: Box::new(callback),
time: Some(Box::new(default_cb_time())),
value: Some(Box::new(default_cb_value())),
});
register_with_state(reason, self.as_raw(), state)
}
pub fn register_value_change_cb<F>(&self, value_type: ValueType, callback: F) -> Handle
where
F: Fn(&CbData) + 'static,
{
let state = Box::new(CallbackState {
callback: Box::new(callback),
time: Some(Box::new(default_cb_time())),
value: Some(Box::new(cb_value_with_format(value_type))),
});
register_with_state(CbReason::ValueChange, self.as_raw(), state)
}
}
unsafe extern "C" fn trampoline(cb_data: *mut vpi_sys::t_cb_data) -> i32 {
if cb_data.is_null() {
return 0; }
let user_data = unsafe { (*cb_data).user_data.cast::<CallbackState>() };
if user_data.is_null() {
return 0; }
let cb_data_ref = unsafe { &*cb_data };
let value = if cb_data_ref.value.is_null() {
None
} else {
Some(unsafe { *cb_data_ref.value })
};
let mut data = CbData {
reason: CbReason::from_u32(cb_data_ref.reason as u32)
.expect("received unknown callback reason from simulator"),
obj: Handle::from_raw(cb_data_ref.obj),
time: if cb_data_ref.time.is_null() {
None
} else {
time_from_cb_data(unsafe { *cb_data_ref.time })
},
value: value.and_then(|raw| decode_vpi_value(raw, cb_data_ref.obj)),
value_type: value.and_then(|raw| ValueType::from_u32(raw.format as u32)),
index: cb_data_ref.index,
};
let state = unsafe { &*user_data };
(state.callback)(&data);
data.obj.clear(); 0 }
pub fn register_cb<F>(reason: CbReason, callback: F) -> Handle
where
F: Fn(&CbData) + 'static,
{
let user_data = Box::into_raw(Box::new(CallbackState {
callback: Box::new(callback),
time: None,
value: None,
}));
let handle = unsafe {
let mut cb_data = vpi_sys::s_cb_data {
reason: reason as i32,
cb_rtn: Some(trampoline),
obj: std::ptr::null_mut(),
time: std::ptr::null_mut(),
value: std::ptr::null_mut(),
index: 0,
user_data: user_data.cast::<vpi_sys::PLI_BYTE8>(),
};
vpi_sys::vpi_register_cb(&raw mut cb_data)
};
if handle.is_null() {
unsafe {
let _ = Box::from_raw(user_data);
}
}
Handle::from_raw(handle)
}
pub fn register_full_cb<F>(reason: CbReason, callback: F) -> Handle
where
F: Fn(&CbData) + 'static,
{
let state = Box::new(CallbackState {
callback: Box::new(callback),
time: Some(Box::new(default_cb_time())),
value: Some(Box::new(default_cb_value())),
});
register_with_state(reason, std::ptr::null_mut(), state)
}
pub fn register_cb_with_time<F>(reason: CbReason, time: Time, callback: F) -> Handle
where
F: Fn(&CbData) + 'static,
{
let state = Box::new(CallbackState {
callback: Box::new(callback),
time: Some(Box::new(time.into())),
value: Some(Box::new(default_cb_value())),
});
register_with_state(reason, std::ptr::null_mut(), state)
}
pub fn remove_cb(handle: &Handle) {
if !handle.is_null() {
unsafe {
let mut cb_data = vpi_sys::s_cb_data {
reason: 0,
cb_rtn: None,
obj: std::ptr::null_mut(),
time: std::ptr::null_mut(),
value: std::ptr::null_mut(),
index: 0,
user_data: std::ptr::null_mut(),
};
vpi_sys::vpi_get_cb_info(handle.as_raw(), &raw mut cb_data);
vpi_sys::vpi_remove_cb(handle.as_raw());
let trampoline_ptr = trampoline as unsafe extern "C" fn(*mut vpi_sys::t_cb_data) -> i32;
let is_internal = cb_data
.cb_rtn
.map(|cb| (cb as usize) == (trampoline_ptr as usize))
.unwrap_or(false);
if is_internal && !cb_data.user_data.is_null() {
let _ = Box::from_raw(cb_data.user_data.cast::<CallbackState>());
}
}
}
}