use bitvec::{bitvec, field::BitField, order::Lsb0, vec::BitVec};
use std::collections::{HashMap, HashSet};
use crate::{
circuit::Instantiable,
logic::Logic,
netlist::{NetRef, Netlist},
};
pub type AttributeKey = String;
pub type AttributeValue = Option<String>;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Attribute {
k: AttributeKey,
v: AttributeValue,
}
impl Attribute {
pub fn new(k: AttributeKey, v: AttributeValue) -> Self {
Self { k, v }
}
pub fn key(&self) -> &AttributeKey {
&self.k
}
pub fn value(&self) -> &AttributeValue {
&self.v
}
pub fn from_pairs(
iter: impl Iterator<Item = (AttributeKey, AttributeValue)>,
) -> impl Iterator<Item = Self> {
iter.map(|(k, v)| Self::new(k, v))
}
}
impl std::fmt::Display for Attribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(value) = &self.v {
write!(f, "(* {} = {} *)", self.k, value)
} else {
write!(f, "(* {} *)", self.k)
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Parameter {
Integer(u64),
Real(f32),
BitVec(BitVec),
Logic(Logic),
}
impl Eq for Parameter {}
impl std::fmt::Display for Parameter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Parameter::Integer(i) => write!(f, "{i}"),
Parameter::Real(_r) => todo!(),
Parameter::BitVec(bv) => {
if bv.len() >= 4 && bv.len() % 4 == 0 {
write!(f, "{}'h", bv.len())?;
for n in bv.chunks(4).rev() {
let val: u8 = n.load();
write!(f, "{:x}", val)?;
}
Ok(())
} else {
write!(
f,
"{}'b{}",
bv.len(),
bv.iter()
.rev()
.map(|b| if *b { '1' } else { '0' })
.collect::<String>()
)
}
}
Parameter::Logic(l) => write!(f, "{l}"),
}
}
}
impl Parameter {
pub fn integer(i: u64) -> Self {
Self::Integer(i)
}
pub fn real(r: f32) -> Self {
Self::Real(r)
}
pub fn bitvec(size: usize, val: u64) -> Self {
if size > 64 {
panic!("BitVec parameter size cannot be larger than 64");
}
let mut bv: BitVec = bitvec!(usize, Lsb0; 0; 64);
bv[0..64].store::<u64>(val);
bv.truncate(size);
Self::BitVec(bv)
}
pub fn logic(l: Logic) -> Self {
Self::Logic(l)
}
pub fn from_bool(b: bool) -> Self {
Self::Logic(Logic::from_bool(b))
}
}
pub struct AttributeFilter<'a, I: Instantiable> {
_netlist: &'a Netlist<I>,
keys: Vec<AttributeKey>,
map: HashMap<AttributeKey, HashSet<NetRef<I>>>,
full_set: HashSet<NetRef<I>>,
}
impl<'a, I> AttributeFilter<'a, I>
where
I: Instantiable,
{
fn new(netlist: &'a Netlist<I>, keys: Vec<AttributeKey>) -> Self {
let mut map = HashMap::new();
let mut full_set = HashSet::new();
for nr in netlist.objects() {
for attr in nr.attributes() {
if keys.contains(attr.key()) {
map.entry(attr.key().clone())
.or_insert_with(HashSet::new)
.insert(nr.clone());
full_set.insert(nr.clone());
}
}
}
Self {
_netlist: netlist,
keys,
map,
full_set,
}
}
pub fn has(&self, n: &NetRef<I>) -> bool {
self.map.values().any(|s| s.contains(n))
}
pub fn keys(&self) -> &[AttributeKey] {
&self.keys
}
}
impl<'a, I> IntoIterator for AttributeFilter<'a, I>
where
I: Instantiable,
{
type Item = NetRef<I>;
type IntoIter = std::collections::hash_set::IntoIter<NetRef<I>>;
fn into_iter(self) -> Self::IntoIter {
self.full_set.into_iter()
}
}
pub fn dont_touch_filter<'a, I>(netlist: &'a Netlist<I>) -> AttributeFilter<'a, I>
where
I: Instantiable,
{
AttributeFilter::new(netlist, vec!["dont_touch".to_string()])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn attribute_iter() {
let attributes: [(AttributeKey, AttributeValue); 2] = [
("dont_touch".to_string(), Some("true".to_string())),
("synthesizable".to_string(), None),
];
let real_attrs: Vec<Attribute> = Attribute::from_pairs(attributes.into_iter()).collect();
assert_eq!(real_attrs.len(), 2);
assert_eq!(
real_attrs.first().unwrap().to_string(),
"(* dont_touch = true *)"
);
assert_eq!(real_attrs.first().unwrap().key(), "dont_touch");
assert_eq!(
real_attrs.last().unwrap().to_string(),
"(* synthesizable *)"
);
assert!(real_attrs.last().unwrap().value().is_none());
}
#[test]
fn test_parameter_fmt() {
let p1 = Parameter::Integer(42);
let p2 = Parameter::BitVec(bitvec![0, 0, 0, 0, 0, 0, 0, 1]);
let p3 = Parameter::Logic(Logic::from_bool(true));
let p4 = Parameter::from_bool(true);
assert_eq!(p1.to_string(), "42");
assert_eq!(p2.to_string(), "8'h80");
assert_eq!(p3.to_string(), "1'b1");
assert_eq!(p4.to_string(), "1'b1");
}
#[test]
fn test_parameter_hex() {
let p = Parameter::BitVec(bitvec![1, 1, 1, 0, 1, 0, 0, 0]);
assert_eq!(p.to_string(), "8'h17");
let p = Parameter::BitVec(bitvec![1, 1, 1, 1, 1, 0, 0, 0]);
assert_eq!(p.to_string(), "8'h1f");
}
}