yosys extensions

This commit is contained in:
2025-08-23 22:57:04 +02:00
parent e4ad3f29d7
commit 3f172debe0
4 changed files with 316 additions and 114 deletions

View File

@@ -1,11 +1,11 @@
# turbo-blif # turbo-blif
low-memory-usage BLIF (berkeley logic interchange format) parser and writer. low-memory-usage BLIF (berkeley logic interchange format) parser.
supports: supports:
- the latest BLIF specification (dated July 28, 1992) - the latest BLIF specification (dated July 28, 1992)
- all yosys BLIF extensions - all yosys BLIF extensions
(supports reading of BLIF files generated with `write_blif -iname -iattr -param -cname -blackbox -attr -conn -icells`) (supports reading of BLIF files generated with `write_blif -iname -iattr -param -cname -blackbox -attr -conn`)
- KISS state machines (which yosys doesn't even support) - KISS state machines (which yosys doesn't even support)
- clock and delay constraints (yosys just ignores those) - clock and delay constraints (yosys just ignores those)

View File

@@ -80,7 +80,23 @@ impl FSMConsumer for FSM {
} }
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)] #[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
pub enum ModelCmd { pub enum CellAttrAst {
/// non-standard; emitted by: yosys
CellName(String),
/// non-standard; possibly emitted by: yosys
///
/// example: Attr { key: "src", val: "\"some/file.v:320.20-320.28\"" }
Attr { key: Str<8>, val: String },
/// non-standard; emitted by: yosys
///
/// example: Param { key: "A_WIDTH", val: "00000000000000000000000000000001" }
Param { key: Str<16>, val: String },
}
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
pub enum ModelCmdKind {
Gate(Gate), Gate(Gate),
FF(FlipFlop), FF(FlipFlop),
LibGate(LibGate), LibGate(LibGate),
@@ -90,6 +106,25 @@ pub enum ModelCmd {
name: Str<32>, name: Str<32>,
map: Vec<(Str<16>, Str<16>)>, map: Vec<(Str<16>, Str<16>)>,
}, },
Connect {
from: Str<16>,
to: Str<16>,
},
}
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
pub struct ModelCmd {
pub kind: ModelCmdKind,
pub attrs: Vec<CellAttrAst>,
}
impl From<ModelCmdKind> for ModelCmd {
fn from(value: ModelCmdKind) -> Self {
Self {
kind: value,
attrs: vec![],
}
}
} }
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)] #[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
@@ -106,26 +141,29 @@ impl CommandConsumer for Model {
} }
fn gate_done(&mut self, gate: Self::Gate) { fn gate_done(&mut self, gate: Self::Gate) {
self.commands.push(ModelCmd::Gate(gate)); self.commands.push(ModelCmdKind::Gate(gate).into());
} }
fn ff(&mut self, ff: FlipFlop) { fn ff(&mut self, ff: FlipFlop) {
self.commands.push(ModelCmd::FF(ff)); self.commands.push(ModelCmdKind::FF(ff).into());
} }
fn lib_gate(&mut self, gate: LibGate) { fn lib_gate(&mut self, gate: LibGate) {
self.commands.push(ModelCmd::LibGate(gate)); self.commands.push(ModelCmdKind::LibGate(gate).into());
} }
fn lib_ff(&mut self, ff: LibFlipFlop) { fn lib_ff(&mut self, ff: LibFlipFlop) {
self.commands.push(ModelCmd::LibFF(ff)); self.commands.push(ModelCmdKind::LibFF(ff).into());
} }
fn sub_model(&mut self, model: &str, map: Vec<(Str<16>, Str<16>)>) { fn sub_model(&mut self, model: &str, map: Vec<(Str<16>, Str<16>)>) {
self.commands.push(ModelCmd::SubModel { self.commands.push(
name: model.into(), ModelCmdKind::SubModel {
map, name: model.into(),
}); map,
}
.into(),
);
} }
type FSM = FSM; type FSM = FSM;
@@ -150,7 +188,31 @@ impl CommandConsumer for Model {
let mut fsm = fsm; let mut fsm = fsm;
fsm.physical_latch_order = physical_latch_order; fsm.physical_latch_order = physical_latch_order;
fsm.state_assignments = state_assignments; fsm.state_assignments = state_assignments;
self.commands.push(ModelCmd::FSM(fsm)); self.commands.push(ModelCmdKind::FSM(fsm).into());
}
fn attr(&mut self, attr: CellAttr) {
self.commands.last_mut().unwrap().attrs.push(match attr {
CellAttr::CellName(n) => CellAttrAst::CellName(n.into()),
CellAttr::Attr { key, val } => CellAttrAst::Attr {
key: key.into(),
val: val.into(),
},
CellAttr::Param { key, val } => CellAttrAst::Param {
key: key.into(),
val: val.into(),
},
});
}
fn connect(&mut self, from: &str, to: &str) {
self.commands.push(
ModelCmdKind::Connect {
from: from.into(),
to: to.into(),
}
.into(),
)
} }
} }

View File

@@ -63,6 +63,22 @@ pub trait FSMConsumer {
fn add_transition(&mut self, transition: FSMTransition); fn add_transition(&mut self, transition: FSMTransition);
} }
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
pub enum CellAttr<'a> {
/// non-standard; emitted by: yosys
CellName(&'a str),
/// non-standard; possibly emitted by: yosys
///
/// example: Attr { key: "src", val: "\"some/file.v:320.20-320.28\"" }
Attr { key: &'a str, val: &'a str },
/// non-standard; emitted by: yosys
///
/// example: Param { key: "A_WIDTH", val: "00000000000000000000000000000001" }
Param { key: &'a str, val: &'a str },
}
pub trait CommandConsumer { pub trait CommandConsumer {
type Gate: GateLutConsumer; type Gate: GateLutConsumer;
type FSM: FSMConsumer; type FSM: FSMConsumer;
@@ -84,6 +100,12 @@ pub trait CommandConsumer {
/// copies the whole circuit of the referenced model, and maps the ins/outs/clocks according to /// copies the whole circuit of the referenced model, and maps the ins/outs/clocks according to
/// [map] /// [map]
fn sub_model(&mut self, model: &str, map: Vec<(Str<16>, Str<16>)>); fn sub_model(&mut self, model: &str, map: Vec<(Str<16>, Str<16>)>);
/// attach attr to last gate / fsm / ff / libgate / libff / sub_model
fn attr(&mut self, attr: CellAttr);
/// non-standard
fn connect(&mut self, from: &str, to: &str);
} }
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)] #[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
@@ -404,7 +426,13 @@ fn parse_mod(
lines.peek().is_some_and(|x| !x.as_ref().starts_with(".")) lines.peek().is_some_and(|x| !x.as_ref().starts_with("."))
} { } {
let l = next_stmt(lines)?.unwrap(); let l = next_stmt(lines)?.unwrap();
let (l, r) = l.as_ref().split_once(' ').ok_or(BlifParserError::Invalid)?; let l = l.as_ref();
let (l, r) = if l.contains(' ') {
l.split_once(' ').unwrap()
} else {
("", l)
};
let invs = str_to_tristates(l).map_err(|_| BlifParserError::Invalid)?; let invs = str_to_tristates(l).map_err(|_| BlifParserError::Invalid)?;
let outvs = match r { let outvs = match r {
@@ -765,7 +793,59 @@ fn parse_mod(
); );
} }
".cname" => {
let arg = args.next().ok_or(BlifParserError::MissingArgs)?.into();
if args.next().is_some() {
Err(BlifParserError::TooManyArgs)?
}
consumer.attr(CellAttr::CellName(arg));
}
".attr" => {
let key = args.next().ok_or(BlifParserError::MissingArgs)?.into();
let val = args.fold(String::new(), |acc, x| {
let mut acc = acc;
acc.push_str(x);
acc
});
consumer.attr(CellAttr::Attr {
key,
val: val.as_str(),
});
}
".param" => {
let key = args.next().ok_or(BlifParserError::MissingArgs)?.into();
let val = args.fold(String::new(), |acc, x| {
let mut acc = acc;
acc.push_str(x);
acc
});
consumer.attr(CellAttr::Param {
key,
val: val.as_str(),
});
}
".conn" => {
let from = args.next().ok_or(BlifParserError::MissingArgs)?.into();
let to = args.next().ok_or(BlifParserError::MissingArgs)?.into();
if args.next().is_some() {
Err(BlifParserError::TooManyArgs)?
}
consumer.connect(from, to);
}
// TODO: clock & delay cst // TODO: clock & delay cst
// TODO: .blackblox
_ => Err(BlifParserError::UnknownKw(cmd.to_string()))?, _ => Err(BlifParserError::UnknownKw(cmd.to_string()))?,
}; };
} }

View File

@@ -24,17 +24,20 @@ fn simple_named() {
outputs: Some(vec!["c".into()]), outputs: Some(vec!["c".into()]),
clocks: vec![], clocks: vec![],
}, },
commands: vec![ModelCmd::Gate(Gate { commands: vec![
meta: GateMeta { ModelCmdKind::Gate(Gate {
inputs: vec!["a".into(), "b".into(),], meta: GateMeta {
output: "c".into(), inputs: vec!["a".into(), "b".into(),],
external_dc: false, output: "c".into(),
}, external_dc: false,
lut: LUT(vec![( },
[Tristate::True, Tristate::True].into_iter().collect(), lut: LUT(vec![(
true [Tristate::True, Tristate::True].into_iter().collect(),
)]), true
})], )]),
})
.into()
],
})], })],
); );
} }
@@ -60,17 +63,20 @@ fn simple_unnamed() {
outputs: Some(vec!["c".into()]), outputs: Some(vec!["c".into()]),
clocks: vec![], clocks: vec![],
}, },
commands: vec![ModelCmd::Gate(Gate { commands: vec![
meta: GateMeta { ModelCmdKind::Gate(Gate {
inputs: vec!["a".into(), "b".into(),], meta: GateMeta {
output: "c".into(), inputs: vec!["a".into(), "b".into(),],
external_dc: false, output: "c".into(),
}, external_dc: false,
lut: LUT(vec![( },
[Tristate::True, Tristate::True].into_iter().collect(), lut: LUT(vec![(
true [Tristate::True, Tristate::True].into_iter().collect(),
)]), true
})], )]),
})
.into()
],
})], })],
); );
} }
@@ -95,17 +101,20 @@ c
outputs: None, outputs: None,
clocks: vec![], clocks: vec![],
}, },
commands: vec![ModelCmd::Gate(Gate { commands: vec![
meta: GateMeta { ModelCmdKind::Gate(Gate {
inputs: vec!["a".into(), "b".into(),], meta: GateMeta {
output: "c".into(), inputs: vec!["a".into(), "b".into(),],
external_dc: false, output: "c".into(),
}, external_dc: false,
lut: LUT(vec![( },
[Tristate::True, Tristate::True].into_iter().collect(), lut: LUT(vec![(
true [Tristate::True, Tristate::True].into_iter().collect(),
)]), true
})], )]),
})
.into()
],
})], })],
); );
} }
@@ -131,17 +140,20 @@ c
outputs: None, outputs: None,
clocks: vec![], clocks: vec![],
}, },
commands: vec![ModelCmd::Gate(Gate { commands: vec![
meta: GateMeta { ModelCmdKind::Gate(Gate {
inputs: vec!["a".into(), "b".into(),], meta: GateMeta {
output: "c".into(), inputs: vec!["a".into(), "b".into(),],
external_dc: true, output: "c".into(),
}, external_dc: true,
lut: LUT(vec![( },
[Tristate::True, Tristate::True].into_iter().collect(), lut: LUT(vec![(
true [Tristate::True, Tristate::True].into_iter().collect(),
)]), true
})], )]),
})
.into()
],
})], })],
); );
} }
@@ -167,48 +179,51 @@ fn lut() {
outputs: None, outputs: None,
clocks: vec![], clocks: vec![],
}, },
commands: vec![ModelCmd::Gate(Gate { commands: vec![
meta: GateMeta { ModelCmdKind::Gate(Gate {
inputs: vec!["v3".into(), "v6".into(), "j".into(), "u78".into()], meta: GateMeta {
output: "v13.15".into(), inputs: vec!["v3".into(), "v6".into(), "j".into(), "u78".into()],
external_dc: false, output: "v13.15".into(),
}, external_dc: false,
lut: LUT(vec![ },
( lut: LUT(vec![
[ (
Tristate::True, [
Tristate::Ignored, Tristate::True,
Tristate::Ignored, Tristate::Ignored,
Tristate::False Tristate::Ignored,
] Tristate::False
.into_iter() ]
.collect(), .into_iter()
true .collect(),
), true
( ),
[ (
Tristate::Ignored, [
Tristate::True, Tristate::Ignored,
Tristate::Ignored, Tristate::True,
Tristate::True Tristate::Ignored,
] Tristate::True
.into_iter() ]
.collect(), .into_iter()
true .collect(),
), true
( ),
[ (
Tristate::False, [
Tristate::Ignored, Tristate::False,
Tristate::True, Tristate::Ignored,
Tristate::True Tristate::True,
] Tristate::True
.into_iter() ]
.collect(), .into_iter()
true .collect(),
) true
]), )
})], ]),
})
.into()
],
})], })],
); );
} }
@@ -248,15 +263,16 @@ fn submod() {
clocks: vec![] clocks: vec![]
}, },
commands: vec![ commands: vec![
ModelCmd::SubModel { ModelCmdKind::SubModel {
name: "b".into(), name: "b".into(),
map: vec![ map: vec![
("x".into(), "x".into()), ("x".into(), "x".into()),
("y".into(), "y".into()), ("y".into(), "y".into()),
("j".into(), "j".into()) ("j".into(), "j".into())
] ]
}, }
ModelCmd::Gate(Gate { .into(),
ModelCmdKind::Gate(Gate {
meta: GateMeta { meta: GateMeta {
inputs: vec!["x".into()], inputs: vec!["x".into()],
output: "j".into(), output: "j".into(),
@@ -264,6 +280,7 @@ fn submod() {
}, },
lut: LUT(vec![([Tristate::True].into_iter().collect(), true)]) lut: LUT(vec![([Tristate::True].into_iter().collect(), true)])
}) })
.into()
] ]
}), }),
BlifEntry::Model(Model { BlifEntry::Model(Model {
@@ -273,17 +290,20 @@ fn submod() {
outputs: Some(vec!["j".into()]), outputs: Some(vec!["j".into()]),
clocks: vec![] clocks: vec![]
}, },
commands: vec![ModelCmd::Gate(Gate { commands: vec![
meta: GateMeta { ModelCmdKind::Gate(Gate {
inputs: vec!["x".into(), "y".into()], meta: GateMeta {
output: "j".into(), inputs: vec!["x".into(), "y".into()],
external_dc: false output: "j".into(),
}, external_dc: false
lut: LUT(vec![( },
[Tristate::True, Tristate::True].into_iter().collect(), lut: LUT(vec![(
true [Tristate::True, Tristate::True].into_iter().collect(),
)]) true
})] )])
})
.into()
]
}) })
] ]
); );
@@ -434,7 +454,7 @@ fn kiss_fsm2() {
} }
#[test] #[test]
fn tech_clock_cst() { fn clock_cst() {
let ast = parse_str_blif_to_ast( let ast = parse_str_blif_to_ast(
"top.blif", "top.blif",
r#" r#"
@@ -447,3 +467,43 @@ fn tech_clock_cst() {
// TODO: assert_eq // TODO: assert_eq
} }
#[test]
fn yosys_attrs() {
let ast = parse_str_blif_to_ast(
"top.blif",
r#"
.model MAC
.inputs clock reset io_rmii_r_RXD[0] io_rmii_r_RXD[1] io_rmii_r_RXER io_rmii_r_CRS_DV io_rmii_clk io_is10mbits io_full_duplex xmit_io_transmit xmit_io_byte[0] xmit_io_byte[1] xmit_io_byte[2] xmit_io_byte[3] xmit_io_byte[4] xmit_io_byte[5] xmit_io_byte[6] xmit_io_byte[7]
.outputs io_rmii_t_TXD[0] io_rmii_t_TXD[1] io_rmii_t_TXEN xmit_io_byte_sent recv_io_frame_start recv_io_abort_frame recv_io_valid_byte recv_io_byte[0] recv_io_byte[1] recv_io_byte[2] recv_io_byte[3] recv_io_byte[4] recv_io_byte[5] recv_io_byte[6] recv_io_byte[7] recv_io_info_carrier_lost_during_packet
.names $false
.names $true
1
.names $undef
.subckt $add A[0]=xmit_preamble_counter[0] A[1]=xmit_preamble_counter[1] A[2]=xmit_preamble_counter[2] B[0]=$true B[1]=$false B[2]=$false Y[0]=$add$build/out.v:213$277_Y[0] Y[1]=$add$build/out.v:213$277_Y[1] Y[2]=$add$build/out.v:213$277_Y[2]
.cname $add$build/out.v:213$277
.attr src "build/out.v:213.31-213.59"
.param A_SIGNED 00000000000000000000000000000000
.param A_WIDTH 00000000000000000000000000000011
.param B_SIGNED 00000000000000000000000000000000
.param B_WIDTH 00000000000000000000000000000011
.param Y_WIDTH 00000000000000000000000000000011
.subckt $and A=$not$build/out.v:170$224_Y B=$eq$build/out.v:170$225_Y Y=$and$build/out.v:170$226_Y
.cname $and$build/out.v:170$226
.attr src "build/out.v:170.31-170.103"
.param A_SIGNED 00000000000000000000000000000000
.param A_WIDTH 00000000000000000000000000000001
.param B_SIGNED 00000000000000000000000000000000
.param B_WIDTH 00000000000000000000000000000001
.param Y_WIDTH 00000000000000000000000000000001
"#,
)
.unwrap();
// TODO: assert_eq
}