1use std::{collections::HashMap, ffi, fmt, slice};
10
11use libloading::Library;
12use snafu::Snafu;
13
14use crate::{
15 PortDirection, WideOut, compute_approx_width_from_wdata_word_count, types,
16};
17
18#[derive(PartialEq, Eq, Hash, Clone, Debug)]
20pub enum VerilatorValue<'a> {
21 CData(types::CData),
22 SData(types::SData),
23 IData(types::IData),
24 QData(types::QData),
25 WDataInP(&'a [types::WData]),
26 WDataOutP(Box<[types::WData]>),
27}
28
29impl VerilatorValue<'_> {
30 pub fn width(&self) -> usize {
32 match self {
33 Self::CData(_) => 8,
34 Self::SData(_) => 16,
35 Self::IData(_) => 32,
36 Self::QData(_) => 64,
37 Self::WDataInP(values) => {
38 compute_approx_width_from_wdata_word_count(values.len())
39 }
40 Self::WDataOutP(values) => {
41 compute_approx_width_from_wdata_word_count(values.len())
42 }
43 }
44 }
45}
46
47impl fmt::Display for VerilatorValue<'_> {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 VerilatorValue::CData(cdata) => cdata.fmt(f),
51 VerilatorValue::SData(sdata) => sdata.fmt(f),
52 VerilatorValue::IData(idata) => idata.fmt(f),
53 VerilatorValue::QData(qdata) => qdata.fmt(f),
54 Self::WDataInP(_values) => "wide (fmt is todo)".fmt(f),
55 Self::WDataOutP(_values) => "wide (fmt is todo)".fmt(f),
56 }
57 }
58}
59
60impl From<types::CData> for VerilatorValue<'_> {
61 fn from(value: types::CData) -> Self {
62 Self::CData(value)
63 }
64}
65
66impl From<types::SData> for VerilatorValue<'_> {
67 fn from(value: types::SData) -> Self {
68 Self::SData(value)
69 }
70}
71impl From<types::IData> for VerilatorValue<'_> {
72 fn from(value: types::IData) -> Self {
73 Self::IData(value)
74 }
75}
76
77impl From<types::QData> for VerilatorValue<'_> {
78 fn from(value: types::QData) -> Self {
79 Self::QData(value)
80 }
81}
82
83impl<'a, const WORDS: usize> From<&'a [types::WData; WORDS]>
84 for VerilatorValue<'a>
85{
86 fn from(value: &'a [types::WData; WORDS]) -> Self {
87 Self::WDataInP(value)
88 }
89}
90
91impl<const WORDS: usize> From<[types::WData; WORDS]> for VerilatorValue<'_> {
92 fn from(value: [types::WData; WORDS]) -> Self {
93 Self::WDataOutP(value.into())
94 }
95}
96
97impl<const WORDS: usize> From<WideOut<WORDS>> for VerilatorValue<'_> {
98 fn from(value: WideOut<WORDS>) -> Self {
99 Self::WDataOutP(value.inner.into())
100 }
101}
102
103pub trait AsDynamicVerilatedModel<'ctx>: 'ctx {
105 fn eval(&mut self);
107
108 fn read(
111 &self,
112 port: impl Into<String>,
113 ) -> Result<VerilatorValue<'_>, DynamicVerilatedModelError>;
114
115 fn pin(
118 &mut self,
119 port: impl Into<String>,
120 value: impl Into<VerilatorValue<'ctx>>,
121 ) -> Result<(), DynamicVerilatedModelError>;
122}
123
124#[derive(Clone, Copy)]
125pub(crate) struct DynamicPortInfo {
126 pub(crate) width: usize,
127 pub(crate) direction: PortDirection,
128}
129
130pub struct DynamicVerilatedModel<'ctx> {
133 pub(crate) ports: HashMap<String, DynamicPortInfo>,
135 pub(crate) name: String,
136 pub(crate) main: *mut ffi::c_void,
137 pub(crate) eval_main: extern "C" fn(*mut ffi::c_void),
138 pub(crate) library: &'ctx Library,
139}
140
141#[derive(Debug, Snafu)]
143pub enum DynamicVerilatedModelError {
144 #[snafu(display(
145 "Port {port} not found on verilated module {top_module}: did you forget to specify it in the runtime `create_dyn_model` constructor?: {source:?}"
146 ))]
147 NoSuchPort {
148 top_module: String,
149 port: String,
150 #[snafu(source(false))]
151 source: Option<libloading::Error>,
152 },
153 #[snafu(display(
154 "Port {port} on verilated module {top_module} has width {width}, but used as if it was in the {attempted_lower} to {attempted_higher} width range"
155 ))]
156 InvalidPortWidth {
157 top_module: String,
158 port: String,
159 width: usize,
160 attempted_lower: usize,
161 attempted_higher: usize,
162 },
163 #[snafu(display(
164 "Port {port} on verilated module {top_module} is an {direction} port, but was used as an {attempted_direction} port"
165 ))]
166 InvalidPortDirection {
167 top_module: String,
168 port: String,
169 direction: PortDirection,
170 attempted_direction: PortDirection,
171 },
172}
173
174impl<'ctx> AsDynamicVerilatedModel<'ctx> for DynamicVerilatedModel<'ctx> {
175 fn eval(&mut self) {
176 (self.eval_main)(self.main);
177 }
178
179 fn read(
180 &self,
181 port: impl Into<String>,
182 ) -> Result<VerilatorValue<'_>, DynamicVerilatedModelError> {
183 let port: String = port.into();
184 let DynamicPortInfo { width, direction } = *self
185 .ports
186 .get(&port)
187 .ok_or(DynamicVerilatedModelError::NoSuchPort {
188 top_module: self.name.clone(),
189 port: port.clone(),
190 source: None,
191 })?;
192
193 if !matches!(direction, PortDirection::Output | PortDirection::Inout,) {
194 return Err(DynamicVerilatedModelError::InvalidPortDirection {
195 top_module: self.name.clone(),
196 port,
197 direction,
198 attempted_direction: PortDirection::Output,
199 });
200 }
201
202 macro_rules! read_value {
203 ($self:ident, $port:expr, $value_type:ty) => {{
204 let symbol: libloading::Symbol<
205 extern "C" fn(*mut ffi::c_void) -> $value_type,
206 > = unsafe {
207 self.library.get(
208 format!("ffi_V{}_read_{}", self.name, $port).as_bytes(),
209 )
210 }
211 .map_err(|source| {
212 DynamicVerilatedModelError::NoSuchPort {
213 top_module: $self.name.to_string(),
214 port: $port.clone(),
215 source: Some(source),
216 }
217 })?;
218
219 Ok((*symbol)($self.main).into())
220 }};
221 }
222
223 if width <= 8 {
224 read_value!(self, port, types::CData)
225 } else if width <= 16 {
226 read_value!(self, port, types::SData)
227 } else if width <= 32 {
228 read_value!(self, port, types::IData)
229 } else if width <= 64 {
230 read_value!(self, port, types::QData)
231 } else {
232 let value: types::WDataOutP =
233 read_value!(self, port, types::WDataOutP)?;
234 assert!(
235 !value.is_null() && value.is_aligned(),
236 "The pointer should be a valid reference to an array in the design class"
237 );
238 let length = width.div_ceil(types::WData::BITS as usize);
239 let (total_bytes, did_overflow) =
240 length.overflowing_mul(size_of::<types::WData>());
241 assert!(!did_overflow && total_bytes < isize::MAX as usize);
242 Ok(VerilatorValue::WDataOutP(
243 unsafe { slice::from_raw_parts(value, length) }.into(),
265 ))
266 }
267 }
268
269 fn pin(
270 &mut self,
271 port: impl Into<String>,
272 value: impl Into<VerilatorValue<'ctx>>,
273 ) -> Result<(), DynamicVerilatedModelError> {
274 macro_rules! pin_value {
275 ($self:ident, $port:expr, $value:expr, $value_type:ty, $low:literal, $high:expr) => {{
276 let symbol: libloading::Symbol<
277 extern "C" fn(*mut ffi::c_void, $value_type),
278 > = unsafe {
279 self.library.get(
280 format!("ffi_V{}_pin_{}", self.name, $port).as_bytes(),
281 )
282 }
283 .map_err(|source| {
284 DynamicVerilatedModelError::NoSuchPort {
285 top_module: $self.name.to_string(),
286 port: $port.clone(),
287 source: Some(source),
288 }
289 })?;
290
291 let DynamicPortInfo { width, direction } = $self
292 .ports
293 .get(&$port)
294 .ok_or(DynamicVerilatedModelError::NoSuchPort {
295 top_module: $self.name.clone(),
296 port: $port.clone(),
297 source: None,
298 })?
299 .clone();
300
301 if width > $high {
302 return Err(DynamicVerilatedModelError::InvalidPortWidth {
303 top_module: $self.name.clone(),
304 port: $port.clone(),
305 width,
306 attempted_lower: $low,
307 attempted_higher: $high,
308 });
309 }
310
311 if !matches!(
312 direction,
313 PortDirection::Input | PortDirection::Inout,
314 ) {
315 return Err(
316 DynamicVerilatedModelError::InvalidPortDirection {
317 top_module: $self.name.clone(),
318 port: $port,
319 direction,
320 attempted_direction: PortDirection::Input,
321 },
322 );
323 }
324
325 (*symbol)($self.main, $value);
326 Ok(())
327 }};
328 }
329
330 let port: String = port.into();
331 match value.into() {
332 VerilatorValue::CData(cdata) => {
333 pin_value!(self, port, cdata, types::CData, 0, 8)
334 }
335 VerilatorValue::SData(sdata) => {
336 pin_value!(self, port, sdata, types::SData, 9, 16)
337 }
338 VerilatorValue::IData(idata) => {
339 pin_value!(self, port, idata, types::IData, 17, 32)
340 }
341 VerilatorValue::QData(qdata) => {
342 pin_value!(self, port, qdata, types::QData, 33, 64)
343 }
344 VerilatorValue::WDataInP(values) => {
345 let values_ptr = values.as_ptr();
346 pin_value!(
347 self,
348 port,
349 values_ptr,
350 types::WDataInP,
351 65,
352 usize::MAX
353 )
354 }
355 VerilatorValue::WDataOutP(_) => {
356 panic!(
357 "Cannot pin with WDataOutP: did you accidently use the From<vec![]> instead of From<&[]>?"
358 )
359 }
360 }
361 }
362}