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