surfer_translation_types/
lib.rs

1mod field_ref;
2pub mod plugin_types;
3#[cfg(feature = "pyo3")]
4pub mod python;
5mod result;
6mod scope_ref;
7pub mod translator;
8pub mod variable_index;
9mod variable_ref;
10
11use derive_more::Display;
12use ecolor::Color32;
13#[cfg(feature = "wasm_plugins")]
14use extism_convert::{FromBytes, Json, ToBytes};
15use num::BigUint;
16use serde::{Deserialize, Serialize};
17use std::collections::HashMap;
18
19pub use crate::field_ref::FieldRef;
20pub use crate::result::{
21    HierFormatResult, SubFieldFlatTranslationResult, SubFieldTranslationResult, TranslatedValue,
22    TranslationResult, ValueRepr,
23};
24pub use crate::scope_ref::ScopeRef;
25pub use crate::translator::{
26    BasicTranslator, Translator, VariableNameInfo, WaveSource, translates_all_bit_types,
27};
28pub use crate::variable_index::VariableIndex;
29pub use crate::variable_ref::VariableRef;
30
31#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
32#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
33#[derive(Deserialize, Serialize)]
34pub struct PluginConfig(pub HashMap<String, String>);
35
36/// Turn vector variable string into name and corresponding color if it
37/// includes values other than 0 and 1. If only 0 and 1, return None.
38pub fn check_vector_variable(s: &str) -> Option<(String, ValueKind)> {
39    if s.contains('x') {
40        Some(("UNDEF".to_string(), ValueKind::Undef))
41    } else if s.contains('z') {
42        Some(("HIGHIMP".to_string(), ValueKind::HighImp))
43    } else if s.contains('-') {
44        Some(("DON'T CARE".to_string(), ValueKind::DontCare))
45    } else if s.contains('u') {
46        Some(("UNDEF".to_string(), ValueKind::Undef))
47    } else if s.contains('w') {
48        Some(("UNDEF WEAK".to_string(), ValueKind::Undef))
49    } else if s.contains('h') || s.contains('l') {
50        Some(("WEAK".to_string(), ValueKind::Weak))
51    } else if s.chars().all(|c| matches!(c, '0' | '1')) {
52        None
53    } else {
54        Some(("UNKNOWN VALUES".to_string(), ValueKind::Undef))
55    }
56}
57
58/// VCD bit extension
59pub fn extend_string(val: &str, num_bits: u64) -> String {
60    if num_bits > val.len() as u64 {
61        let extra_count = num_bits - val.len() as u64;
62        let extra_value = match val.chars().next() {
63            Some('0') => "0",
64            Some('1') => "0",
65            Some('x') => "x",
66            Some('z') => "z",
67            // If we got weird characters, this is probably a string, so we don't
68            // do the extension
69            // We may have to add extensions for std_logic values though if simulators save without extension
70            _ => "",
71        };
72        extra_value.repeat(extra_count as usize)
73    } else {
74        String::new()
75    }
76}
77
78#[derive(Debug, PartialEq, Clone, Display, Serialize, Deserialize)]
79pub enum VariableValue {
80    #[display("{_0}")]
81    BigUint(BigUint),
82    #[display("{_0}")]
83    String(String),
84}
85
86impl VariableValue {
87    /// Utility function for handling the happy case of the variable value being only 0 and 1,
88    /// with default handling of other values.
89    ///
90    /// The value passed to the handler is guaranteed to only contain 0 and 1, but it is not
91    /// padded to the length of the vector, i.e. leading zeros can be missing. Use [extend_string]
92    /// on the result to add the padding.
93    pub fn handle_bits<E>(
94        self,
95        handler: impl Fn(String) -> Result<TranslationResult, E>,
96    ) -> Result<TranslationResult, E> {
97        let value = match self {
98            VariableValue::BigUint(v) => format!("{v:b}"),
99            VariableValue::String(v) => {
100                if let Some((val, kind)) = check_vector_variable(&v) {
101                    return Ok(TranslationResult {
102                        val: ValueRepr::String(val),
103                        subfields: vec![],
104                        kind,
105                    });
106                } else {
107                    v
108                }
109            }
110        };
111
112        handler(value)
113    }
114}
115
116#[derive(Clone, PartialEq, Copy, Debug, Serialize, Deserialize)]
117pub enum ValueKind {
118    Normal,
119    Undef,
120    HighImp,
121    Custom(Color32),
122    Warn,
123    DontCare,
124    Weak,
125    Error,
126}
127
128#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
129#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
130#[derive(PartialEq, Deserialize, Serialize, Debug)]
131pub enum TranslationPreference {
132    /// This translator prefers translating the variable, so it will be selected
133    /// as the default translator for the variable
134    Prefer,
135    /// This translator is able to translate the variable, but will not be
136    /// selected by default, the user has to select it
137    Yes,
138    No,
139}
140
141/// Static information about the structure of a variable.
142#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
143#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
144#[derive(Clone, Debug, Default, Deserialize, Serialize)]
145pub enum VariableInfo {
146    Compound {
147        subfields: Vec<(String, VariableInfo)>,
148    },
149    Bits,
150    Bool,
151    Clock,
152    // NOTE: only used for state saving where translators will clear this out with the actual value
153    #[default]
154    String,
155    Real,
156}
157
158#[derive(Debug, Display, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
159pub enum VariableType {
160    // VCD-specific types
161    #[display("event")]
162    VCDEvent,
163    #[display("reg")]
164    VCDReg,
165    #[display("wire")]
166    VCDWire,
167    #[display("real")]
168    VCDReal,
169    #[display("time")]
170    VCDTime,
171    #[display("string")]
172    VCDString,
173    #[display("parameter")]
174    VCDParameter,
175    #[display("integer")]
176    VCDInteger,
177    #[display("real time")]
178    VCDRealTime,
179    #[display("supply 0")]
180    VCDSupply0,
181    #[display("supply 1")]
182    VCDSupply1,
183    #[display("tri")]
184    VCDTri,
185    #[display("tri and")]
186    VCDTriAnd,
187    #[display("tri or")]
188    VCDTriOr,
189    #[display("tri reg")]
190    VCDTriReg,
191    #[display("tri 0")]
192    VCDTri0,
193    #[display("tri 1")]
194    VCDTri1,
195    #[display("wand")]
196    VCDWAnd,
197    #[display("wor")]
198    VCDWOr,
199    #[display("port")]
200    Port,
201    #[display("sparse array")]
202    SparseArray,
203    #[display("realtime")]
204    RealTime,
205
206    // System Verilog
207    #[display("bit")]
208    Bit,
209    #[display("logic")]
210    Logic,
211    #[display("int")]
212    Int,
213    #[display("shortint")]
214    ShortInt,
215    #[display("longint")]
216    LongInt,
217    #[display("byte")]
218    Byte,
219    #[display("enum")]
220    Enum,
221    #[display("shortreal")]
222    ShortReal,
223
224    // VHDL (these are the types emitted by GHDL)
225    #[display("boolean")]
226    Boolean,
227    #[display("bit_vector")]
228    BitVector,
229    #[display("std_logic")]
230    StdLogic,
231    #[display("std_logic_vector")]
232    StdLogicVector,
233    #[display("std_ulogic")]
234    StdULogic,
235    #[display("std_ulogic_vector")]
236    StdULogicVector,
237}
238
239#[derive(Clone, Display, Copy, PartialOrd, Debug, Eq, PartialEq, Serialize, Deserialize)]
240pub enum VariableDirection {
241    // Ordering is used for sorting variable list
242    #[display("input")]
243    Input,
244    #[display("output")]
245    Output,
246    #[display("inout")]
247    InOut,
248    #[display("buffer")]
249    Buffer,
250    #[display("linkage")]
251    Linkage,
252    #[display("implicit")]
253    Implicit,
254    #[display("unknown")]
255    Unknown,
256}
257
258#[cfg_attr(feature = "wasm_plugins", derive(FromBytes, ToBytes))]
259#[cfg_attr(feature = "wasm_plugins", encoding(Json))]
260#[derive(Clone, Debug, Serialize, Deserialize)]
261pub struct VariableMeta<VarId, ScopeId> {
262    pub var: VariableRef<VarId, ScopeId>,
263    pub num_bits: Option<u32>,
264    /// Type of the variable in the HDL (on a best effort basis).
265    pub variable_type: Option<VariableType>,
266    /// Type name of variable, if available
267    pub variable_type_name: Option<String>,
268    pub index: Option<VariableIndex>,
269    pub direction: Option<VariableDirection>,
270    pub enum_map: HashMap<String, String>,
271    /// Indicates how the variable is stored. A variable of "type" boolean for example
272    /// could be stored as a String or as a BitVector.
273    pub encoding: VariableEncoding,
274}
275
276#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
277pub enum VariableEncoding {
278    String,
279    Real,
280    BitVector,
281}
282
283impl<VarId1, ScopeId1> VariableMeta<VarId1, ScopeId1> {
284    pub fn map_ids<VarId2, ScopeId2>(
285        self,
286        var_fn: impl FnMut(VarId1) -> VarId2,
287        scope_fn: impl FnMut(ScopeId1) -> ScopeId2,
288    ) -> VariableMeta<VarId2, ScopeId2> {
289        VariableMeta {
290            var: self.var.map_ids(var_fn, scope_fn),
291            num_bits: self.num_bits,
292            variable_type: self.variable_type,
293            index: self.index,
294            direction: self.direction,
295            enum_map: self.enum_map,
296            encoding: self.encoding,
297            variable_type_name: self.variable_type_name,
298        }
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use super::{ValueKind, check_vector_variable, extend_string};
305
306    #[test]
307    fn binary_only_returns_none() {
308        for s in ["0", "1", "0101", "1111", "000000", "101010"].iter() {
309            assert_eq!(check_vector_variable(s), None, "{s}");
310        }
311    }
312
313    #[test]
314    fn x_marks_undef() {
315        let res = check_vector_variable("10x01").unwrap();
316        assert_eq!(res.0, "UNDEF");
317        assert_eq!(res.1, ValueKind::Undef);
318    }
319
320    #[test]
321    fn u_marks_undef() {
322        for s in ["u", "10u", "uuuu"].iter() {
323            let res = check_vector_variable(s).unwrap();
324            assert_eq!(res.0, "UNDEF");
325            assert_eq!(res.1, ValueKind::Undef);
326        }
327    }
328
329    #[test]
330    fn z_marks_highimp() {
331        let res = check_vector_variable("zz01").unwrap();
332        assert_eq!(res.0, "HIGHIMP");
333        assert_eq!(res.1, ValueKind::HighImp);
334    }
335
336    #[test]
337    fn dash_marks_dont_care() {
338        let res = check_vector_variable("-01--").unwrap();
339        assert_eq!(res.0, "DON'T CARE");
340        assert_eq!(res.1, ValueKind::DontCare);
341    }
342
343    #[test]
344    fn w_marks_undef_weak() {
345        let res = check_vector_variable("w101").unwrap();
346        assert_eq!(res.0, "UNDEF WEAK");
347        assert_eq!(res.1, ValueKind::Undef); // intentionally Undef per implementation
348    }
349
350    #[test]
351    fn h_or_l_marks_weak() {
352        let res_h = check_vector_variable("h110").unwrap();
353        assert_eq!(res_h.0, "WEAK");
354        assert_eq!(res_h.1, ValueKind::Weak);
355
356        let res_l = check_vector_variable("l001").unwrap();
357        assert_eq!(res_l.0, "WEAK");
358        assert_eq!(res_l.1, ValueKind::Weak);
359    }
360
361    #[test]
362    fn unknown_values_fallback() {
363        for s in ["2", "a", "?", " "] {
364            let res = check_vector_variable(s).unwrap();
365            assert_eq!(res.0, "UNKNOWN VALUES");
366            assert_eq!(res.1, ValueKind::Undef);
367        }
368    }
369
370    #[test]
371    fn precedence_is_respected() {
372        // contains both x and z -> x handled first (UNDEF)
373        let res = check_vector_variable("xz").unwrap();
374        assert_eq!(res.0, "UNDEF");
375        assert_eq!(res.1, ValueKind::Undef);
376
377        // contains w and h -> w handled before h, yielding UNDEF WEAK not WEAK
378        let res = check_vector_variable("wh").unwrap();
379        assert_eq!(res.0, "UNDEF WEAK");
380        assert_eq!(res.1, ValueKind::Undef);
381    }
382
383    // ---------------- extend_string tests ----------------
384
385    #[test]
386    fn extend_string_zero_extend_from_0_and_1() {
387        // Leading '0' extends with '0'
388        assert_eq!(extend_string("001", 5), "00");
389        assert_eq!(extend_string("0", 3), "00");
390
391        // Leading '1' also extends with '0' per current implementation
392        assert_eq!(extend_string("101", 5), "00");
393        assert_eq!(extend_string("1", 4), "000");
394    }
395
396    #[test]
397    fn extend_string_x_and_z() {
398        // Leading 'x' extends with 'x'
399        assert_eq!(extend_string("x1", 4), "xx");
400        assert_eq!(extend_string("x", 3), "xx");
401
402        // Leading 'z' extends with 'z'
403        assert_eq!(extend_string("z0", 3), "z");
404        assert_eq!(extend_string("z", 5), "zzzz");
405    }
406
407    #[test]
408    fn extend_string_same_or_smaller_returns_empty() {
409        assert_eq!(extend_string("101", 3), "");
410        assert_eq!(extend_string("101", 2), "");
411        assert_eq!(extend_string("", 0), "");
412    }
413
414    #[test]
415    fn extend_string_weird_char_and_empty_input() {
416        // Unknown leading char results in no extension (empty), even if num_bits is larger
417        assert_eq!(extend_string("h101", 6), "");
418        assert_eq!(extend_string("?", 10), "");
419
420        // Empty input yields empty extension as there is no leading char to guide
421        assert_eq!(extend_string("", 5), "");
422    }
423}