use vpi_sys::PLI_INT32;
#[must_use]
pub fn simulator_info() -> SimulatorInfo {
let mut vlog_info = vpi_sys::t_vpi_vlog_info {
argc: 0,
argv: std::ptr::null_mut(),
version: std::ptr::null_mut(),
product: std::ptr::null_mut(),
};
unsafe { vpi_sys::vpi_get_vlog_info(&raw mut vlog_info) };
let version = unsafe { std::ffi::CStr::from_ptr(vlog_info.version) }
.to_str()
.unwrap_or("Unknown")
.to_string();
let product = unsafe { std::ffi::CStr::from_ptr(vlog_info.product) }
.to_str()
.unwrap_or("Unknown")
.to_string();
let mut arguments = Vec::new();
for i in 0..vlog_info.argc {
let arg_ptr = unsafe { *vlog_info.argv.add(i as usize) };
let arg = unsafe { std::ffi::CStr::from_ptr(arg_ptr) }
.to_str()
.unwrap_or("Unknown")
.to_string();
arguments.push(arg);
}
SimulatorInfo {
arguments,
version,
product,
}
}
#[derive(Debug)]
pub struct SimulatorInfo {
pub arguments: Vec<String>,
pub version: String,
pub product: String,
}
#[must_use]
pub fn simulator_name() -> String {
let mut vlog_info = vpi_sys::t_vpi_vlog_info {
argc: 0,
argv: std::ptr::null_mut(),
version: std::ptr::null_mut(),
product: std::ptr::null_mut(),
};
unsafe { vpi_sys::vpi_get_vlog_info(&raw mut vlog_info) };
unsafe { std::ffi::CStr::from_ptr(vlog_info.product) }
.to_str()
.unwrap_or("Unknown")
.to_string()
}
#[must_use]
pub fn simulator_version() -> String {
let mut vlog_info = vpi_sys::t_vpi_vlog_info {
argc: 0,
argv: std::ptr::null_mut(),
version: std::ptr::null_mut(),
product: std::ptr::null_mut(),
};
unsafe { vpi_sys::vpi_get_vlog_info(&raw mut vlog_info) };
unsafe { std::ffi::CStr::from_ptr(vlog_info.version) }
.to_str()
.unwrap_or("Unknown")
.to_string()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Timescale {
pub unit: i32,
pub precision: i32,
}
impl Timescale {
pub unsafe fn from_module(module_handle: vpi_sys::vpiHandle) -> Option<Self> {
let unit =
unsafe { vpi_sys::vpi_get(crate::Property::TimeUnit as PLI_INT32, module_handle) };
let precision =
unsafe { vpi_sys::vpi_get(crate::Property::TimePrecision as PLI_INT32, module_handle) };
Some(Timescale { unit, precision })
}
#[must_use]
pub fn unit_str(&self) -> String {
power_of_10_to_time_str(self.unit)
}
#[must_use]
pub fn precision_str(&self) -> String {
power_of_10_to_time_str(self.precision)
}
}
impl std::fmt::Display for Timescale {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} / {}", self.unit_str(), self.precision_str())
}
}
fn power_of_10_to_time_str(power: i32) -> String {
match power {
2 => "100s".to_string(),
1 => "10s".to_string(),
0 => "1s".to_string(),
-1 => "100ms".to_string(),
-2 => "10ms".to_string(),
-3 => "1ms".to_string(),
-4 => "100us".to_string(),
-5 => "10us".to_string(),
-6 => "1us".to_string(),
-7 => "100ns".to_string(),
-8 => "10ns".to_string(),
-9 => "1ns".to_string(),
-10 => "100ps".to_string(),
-11 => "10ps".to_string(),
-12 => "1ps".to_string(),
-13 => "100fs".to_string(),
-14 => "10fs".to_string(),
-15 => "1fs".to_string(),
_ => format!("10^{power}s"),
}
}
#[must_use]
pub fn get_top_module_timescales() -> Vec<(String, Option<Timescale>)> {
let mut results = Vec::new();
unsafe {
let iter = vpi_sys::vpi_iterate(vpi_sys::vpiModule as i32, std::ptr::null_mut());
if iter.is_null() {
return results;
}
loop {
let module = vpi_sys::vpi_scan(iter);
if module.is_null() {
break;
}
let name_ptr = vpi_sys::vpi_get_str(crate::Property::Name as i32, module);
let name = if name_ptr.is_null() {
"Unknown".to_string()
} else {
std::ffi::CStr::from_ptr(name_ptr)
.to_str()
.unwrap_or("Unknown")
.to_string()
};
let timescale = Timescale::from_module(module);
results.push((name, timescale));
}
}
results
}
#[cfg(test)]
mod tests {
use super::{power_of_10_to_time_str, Timescale};
#[test]
fn maps_known_power_values_to_expected_units() {
assert_eq!(power_of_10_to_time_str(2), "100s");
assert_eq!(power_of_10_to_time_str(1), "10s");
assert_eq!(power_of_10_to_time_str(0), "1s");
assert_eq!(power_of_10_to_time_str(-1), "100ms");
assert_eq!(power_of_10_to_time_str(-2), "10ms");
assert_eq!(power_of_10_to_time_str(-3), "1ms");
assert_eq!(power_of_10_to_time_str(-6), "1us");
assert_eq!(power_of_10_to_time_str(-9), "1ns");
assert_eq!(power_of_10_to_time_str(-12), "1ps");
assert_eq!(power_of_10_to_time_str(-15), "1fs");
}
#[test]
fn timescale_unit_and_precision_helpers_use_power_mapping() {
let timescale = Timescale {
unit: -9,
precision: -12,
};
assert_eq!(timescale.unit_str(), "1ns");
assert_eq!(timescale.precision_str(), "1ps");
}
#[test]
fn timescale_display_formats_as_unit_slash_precision() {
let timescale = Timescale {
unit: -6,
precision: -15,
};
assert_eq!(timescale.to_string(), "1us / 1fs");
}
}