Skip to main content

safety_net/
attribute.rs

1/*!
2
3  Attributes and parameters for nets and node (gates) in the netlist.
4
5*/
6
7use bitvec::{bitvec, field::BitField, order::Lsb0, vec::BitVec};
8use std::collections::{HashMap, HashSet};
9
10use crate::{
11    circuit::Instantiable,
12    logic::Logic,
13    netlist::{NetRef, Netlist},
14};
15
16/// A Verilog attribute assigned to a net or gate in the netlist: (* dont_touch *)
17pub type AttributeKey = String;
18/// A Verilog attribute can be assigned a string value: bitvec = (* dont_touch = true *)
19pub type AttributeValue = Option<Parameter>;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23/// An attribute can add information to instances and wires in string form, like 'dont_touch'
24pub struct Attribute {
25    k: AttributeKey,
26    v: AttributeValue,
27}
28
29impl Attribute {
30    /// Create a new attribute pair
31    pub fn new(k: AttributeKey, v: AttributeValue) -> Self {
32        Self { k, v }
33    }
34
35    /// Get the key of the attribute
36    pub fn key(&self) -> &AttributeKey {
37        &self.k
38    }
39
40    /// Get the value of the attribute
41    pub fn value(&self) -> &AttributeValue {
42        &self.v
43    }
44
45    /// Map a attribute key-value pairs to the Attribute struct
46    pub fn from_pairs(
47        iter: impl Iterator<Item = (AttributeKey, AttributeValue)>,
48    ) -> impl Iterator<Item = Self> {
49        iter.map(|(k, v)| Self::new(k, v))
50    }
51}
52
53impl std::fmt::Display for Attribute {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        if let Some(value) = &self.v {
56            write!(f, "(* {} = {} *)", self.k, value)
57        } else {
58            write!(f, "(* {} *)", self.k)
59        }
60    }
61}
62
63#[derive(Debug, Clone, PartialEq)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
65/// A dedicated type to parameters for instantiables
66pub enum Parameter {
67    /// An unsigned integer parameter
68    Integer(u64),
69    /// A floating-point parameter
70    Real(f32),
71    /// A bit vector parameter, like for a truth table
72    BitVec(BitVec),
73    /// A four-state logic parameter
74    Logic(Logic),
75    /// String parameter, like for attributes
76    String(String),
77}
78
79impl Eq for Parameter {}
80
81impl std::fmt::Display for Parameter {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            Parameter::Integer(i) => write!(f, "{i}"),
85            Parameter::Real(_r) => todo!(),
86            Parameter::BitVec(bv) => {
87                if bv.len() >= 4 && bv.len() % 4 == 0 {
88                    write!(f, "{}'h", bv.len())?;
89                    for n in bv.chunks(4).rev() {
90                        let val: u8 = n.load();
91                        write!(f, "{:x}", val)?;
92                    }
93                } else {
94                    write!(f, "{}'b", bv.len())?;
95                    for v in bv.iter().rev() {
96                        write!(f, "{}", if *v { '1' } else { '0' })?;
97                    }
98                }
99                Ok(())
100            }
101            Parameter::Logic(l) => write!(f, "{l}"),
102            Parameter::String(s) => write!(f, "\"{s}\""),
103        }
104    }
105}
106
107impl Parameter {
108    /// Create a new integer parameter
109    pub fn integer(i: u64) -> Self {
110        Self::Integer(i)
111    }
112
113    /// Create a new real parameter
114    pub fn real(r: f32) -> Self {
115        Self::Real(r)
116    }
117
118    /// Create a new bitvec parameter
119    pub fn bitvec(size: usize, val: u64) -> Self {
120        if size > 64 {
121            panic!("BitVec parameter size cannot be larger than 64");
122        }
123        let mut bv: BitVec = bitvec!(usize, Lsb0; 0; 64);
124        bv[0..64].store::<u64>(val);
125        bv.truncate(size);
126        Self::BitVec(bv)
127    }
128
129    /// Create a new Logic parameter
130    pub fn logic(l: Logic) -> Self {
131        Self::Logic(l)
132    }
133
134    /// Create a new Logic parameter from bool
135    pub fn from_bool(b: bool) -> Self {
136        Self::Logic(Logic::from_bool(b))
137    }
138
139    /// Create a new String parameter
140    pub fn string(s: String) -> Self {
141        Self::String(s)
142    }
143
144    /// Create a String parameter from a &str
145    pub fn get_str(s: &str) -> Self {
146        Self::String(s.to_string())
147    }
148}
149
150/// Filter nodes/nets in the netlist by some attribute, like "dont_touch"
151pub struct AttributeFilter<'a, I: Instantiable> {
152    // A reference to the underlying netlist
153    _netlist: &'a Netlist<I>,
154    // The keys to filter by
155    keys: Vec<AttributeKey>,
156    /// The mapping of netrefs that have this attribute
157    map: HashMap<AttributeKey, HashSet<NetRef<I>>>,
158    /// Contains a dedup collection of all filtered nodes
159    full_set: HashSet<NetRef<I>>,
160}
161
162impl<'a, I> AttributeFilter<'a, I>
163where
164    I: Instantiable,
165{
166    /// Create a new filter for the netlist
167    fn new(netlist: &'a Netlist<I>, keys: Vec<AttributeKey>) -> Self {
168        let mut map = HashMap::new();
169        let mut full_set = HashSet::new();
170        for nr in netlist.objects() {
171            for attr in nr.attributes() {
172                if keys.contains(attr.key()) {
173                    map.entry(attr.key().clone())
174                        .or_insert_with(HashSet::new)
175                        .insert(nr.clone());
176                    full_set.insert(nr.clone());
177                }
178            }
179        }
180        Self {
181            _netlist: netlist,
182            keys,
183            map,
184            full_set,
185        }
186    }
187
188    /// Check if an node matches any of the filter keys
189    pub fn has(&self, n: &NetRef<I>) -> bool {
190        self.map.values().any(|s| s.contains(n))
191    }
192
193    /// Return a slice to the keys that were used for filtering
194    pub fn keys(&self) -> &[AttributeKey] {
195        &self.keys
196    }
197}
198
199impl<'a, I> IntoIterator for AttributeFilter<'a, I>
200where
201    I: Instantiable,
202{
203    type Item = NetRef<I>;
204
205    type IntoIter = std::collections::hash_set::IntoIter<NetRef<I>>;
206
207    fn into_iter(self) -> Self::IntoIter {
208        self.full_set.into_iter()
209    }
210}
211
212/// Returns a filtering of nodes and nets that are marked as 'dont_touch'
213pub fn dont_touch_filter<'a, I>(netlist: &'a Netlist<I>) -> AttributeFilter<'a, I>
214where
215    I: Instantiable,
216{
217    AttributeFilter::new(netlist, vec!["dont_touch".to_string()])
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223
224    #[test]
225    fn attribute_iter() {
226        let attributes: [(AttributeKey, AttributeValue); 2] = [
227            ("dont_touch".to_string(), Some(Parameter::get_str("true"))),
228            ("synthesizable".to_string(), None),
229        ];
230        let real_attrs: Vec<Attribute> = Attribute::from_pairs(attributes.into_iter()).collect();
231        assert_eq!(real_attrs.len(), 2);
232        assert_eq!(
233            real_attrs.first().unwrap().to_string(),
234            "(* dont_touch = \"true\" *)"
235        );
236        assert_eq!(real_attrs.first().unwrap().key(), "dont_touch");
237        assert_eq!(
238            real_attrs.last().unwrap().to_string(),
239            "(* synthesizable *)"
240        );
241        assert!(real_attrs.last().unwrap().value().is_none());
242    }
243
244    #[test]
245    fn test_parameter_fmt() {
246        let p1 = Parameter::Integer(42);
247        // Lsb first
248        let p2 = Parameter::BitVec(bitvec![0, 0, 0, 0, 0, 0, 0, 1]);
249        let p3 = Parameter::Logic(Logic::from_bool(true));
250        let p4 = Parameter::from_bool(true);
251        assert_eq!(p1.to_string(), "42");
252        assert_eq!(p2.to_string(), "8'h80");
253        assert_eq!(p3.to_string(), "1'b1");
254        assert_eq!(p4.to_string(), "1'b1");
255    }
256
257    #[test]
258    fn test_parameter_hex() {
259        let p = Parameter::BitVec(bitvec![1, 1, 1, 0, 1, 0, 0, 0]);
260        assert_eq!(p.to_string(), "8'h17");
261        let p = Parameter::BitVec(bitvec![1, 1, 1, 1, 1, 0, 0, 0]);
262        assert_eq!(p.to_string(), "8'h1f");
263    }
264}