mirror of
https://github.com/alex-s168/turbo-blif.git
synced 2025-09-10 01:55:08 +02:00
yosys extensions
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
# turbo-blif
|
||||
|
||||
low-memory-usage BLIF (berkeley logic interchange format) parser and writer.
|
||||
low-memory-usage BLIF (berkeley logic interchange format) parser.
|
||||
|
||||
supports:
|
||||
- the latest BLIF specification (dated July 28, 1992)
|
||||
- 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)
|
||||
- clock and delay constraints (yosys just ignores those)
|
||||
|
||||
|
82
src/ast.rs
82
src/ast.rs
@@ -80,7 +80,23 @@ impl FSMConsumer for FSM {
|
||||
}
|
||||
|
||||
#[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),
|
||||
FF(FlipFlop),
|
||||
LibGate(LibGate),
|
||||
@@ -90,6 +106,25 @@ pub enum ModelCmd {
|
||||
name: Str<32>,
|
||||
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)]
|
||||
@@ -106,26 +141,29 @@ impl CommandConsumer for Model {
|
||||
}
|
||||
|
||||
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) {
|
||||
self.commands.push(ModelCmd::FF(ff));
|
||||
self.commands.push(ModelCmdKind::FF(ff).into());
|
||||
}
|
||||
|
||||
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) {
|
||||
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>)>) {
|
||||
self.commands.push(ModelCmd::SubModel {
|
||||
name: model.into(),
|
||||
map,
|
||||
});
|
||||
self.commands.push(
|
||||
ModelCmdKind::SubModel {
|
||||
name: model.into(),
|
||||
map,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
type FSM = FSM;
|
||||
@@ -150,7 +188,31 @@ impl CommandConsumer for Model {
|
||||
let mut fsm = fsm;
|
||||
fsm.physical_latch_order = physical_latch_order;
|
||||
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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
82
src/lib.rs
82
src/lib.rs
@@ -63,6 +63,22 @@ pub trait FSMConsumer {
|
||||
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 {
|
||||
type Gate: GateLutConsumer;
|
||||
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
|
||||
/// [map]
|
||||
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)]
|
||||
@@ -404,7 +426,13 @@ fn parse_mod(
|
||||
lines.peek().is_some_and(|x| !x.as_ref().starts_with("."))
|
||||
} {
|
||||
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 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: .blackblox
|
||||
_ => Err(BlifParserError::UnknownKw(cmd.to_string()))?,
|
||||
};
|
||||
}
|
||||
|
262
src/tests.rs
262
src/tests.rs
@@ -24,17 +24,20 @@ fn simple_named() {
|
||||
outputs: Some(vec!["c".into()]),
|
||||
clocks: vec![],
|
||||
},
|
||||
commands: vec![ModelCmd::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["a".into(), "b".into(),],
|
||||
output: "c".into(),
|
||||
external_dc: false,
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)]),
|
||||
})],
|
||||
commands: vec![
|
||||
ModelCmdKind::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["a".into(), "b".into(),],
|
||||
output: "c".into(),
|
||||
external_dc: false,
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)]),
|
||||
})
|
||||
.into()
|
||||
],
|
||||
})],
|
||||
);
|
||||
}
|
||||
@@ -60,17 +63,20 @@ fn simple_unnamed() {
|
||||
outputs: Some(vec!["c".into()]),
|
||||
clocks: vec![],
|
||||
},
|
||||
commands: vec![ModelCmd::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["a".into(), "b".into(),],
|
||||
output: "c".into(),
|
||||
external_dc: false,
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)]),
|
||||
})],
|
||||
commands: vec![
|
||||
ModelCmdKind::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["a".into(), "b".into(),],
|
||||
output: "c".into(),
|
||||
external_dc: false,
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)]),
|
||||
})
|
||||
.into()
|
||||
],
|
||||
})],
|
||||
);
|
||||
}
|
||||
@@ -95,17 +101,20 @@ c
|
||||
outputs: None,
|
||||
clocks: vec![],
|
||||
},
|
||||
commands: vec![ModelCmd::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["a".into(), "b".into(),],
|
||||
output: "c".into(),
|
||||
external_dc: false,
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)]),
|
||||
})],
|
||||
commands: vec![
|
||||
ModelCmdKind::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["a".into(), "b".into(),],
|
||||
output: "c".into(),
|
||||
external_dc: false,
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)]),
|
||||
})
|
||||
.into()
|
||||
],
|
||||
})],
|
||||
);
|
||||
}
|
||||
@@ -131,17 +140,20 @@ c
|
||||
outputs: None,
|
||||
clocks: vec![],
|
||||
},
|
||||
commands: vec![ModelCmd::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["a".into(), "b".into(),],
|
||||
output: "c".into(),
|
||||
external_dc: true,
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)]),
|
||||
})],
|
||||
commands: vec![
|
||||
ModelCmdKind::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["a".into(), "b".into(),],
|
||||
output: "c".into(),
|
||||
external_dc: true,
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)]),
|
||||
})
|
||||
.into()
|
||||
],
|
||||
})],
|
||||
);
|
||||
}
|
||||
@@ -167,48 +179,51 @@ fn lut() {
|
||||
outputs: None,
|
||||
clocks: vec![],
|
||||
},
|
||||
commands: vec![ModelCmd::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["v3".into(), "v6".into(), "j".into(), "u78".into()],
|
||||
output: "v13.15".into(),
|
||||
external_dc: false,
|
||||
},
|
||||
lut: LUT(vec![
|
||||
(
|
||||
[
|
||||
Tristate::True,
|
||||
Tristate::Ignored,
|
||||
Tristate::Ignored,
|
||||
Tristate::False
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
true
|
||||
),
|
||||
(
|
||||
[
|
||||
Tristate::Ignored,
|
||||
Tristate::True,
|
||||
Tristate::Ignored,
|
||||
Tristate::True
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
true
|
||||
),
|
||||
(
|
||||
[
|
||||
Tristate::False,
|
||||
Tristate::Ignored,
|
||||
Tristate::True,
|
||||
Tristate::True
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
true
|
||||
)
|
||||
]),
|
||||
})],
|
||||
commands: vec![
|
||||
ModelCmdKind::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["v3".into(), "v6".into(), "j".into(), "u78".into()],
|
||||
output: "v13.15".into(),
|
||||
external_dc: false,
|
||||
},
|
||||
lut: LUT(vec![
|
||||
(
|
||||
[
|
||||
Tristate::True,
|
||||
Tristate::Ignored,
|
||||
Tristate::Ignored,
|
||||
Tristate::False
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
true
|
||||
),
|
||||
(
|
||||
[
|
||||
Tristate::Ignored,
|
||||
Tristate::True,
|
||||
Tristate::Ignored,
|
||||
Tristate::True
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
true
|
||||
),
|
||||
(
|
||||
[
|
||||
Tristate::False,
|
||||
Tristate::Ignored,
|
||||
Tristate::True,
|
||||
Tristate::True
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
true
|
||||
)
|
||||
]),
|
||||
})
|
||||
.into()
|
||||
],
|
||||
})],
|
||||
);
|
||||
}
|
||||
@@ -248,15 +263,16 @@ fn submod() {
|
||||
clocks: vec![]
|
||||
},
|
||||
commands: vec![
|
||||
ModelCmd::SubModel {
|
||||
ModelCmdKind::SubModel {
|
||||
name: "b".into(),
|
||||
map: vec![
|
||||
("x".into(), "x".into()),
|
||||
("y".into(), "y".into()),
|
||||
("j".into(), "j".into())
|
||||
]
|
||||
},
|
||||
ModelCmd::Gate(Gate {
|
||||
}
|
||||
.into(),
|
||||
ModelCmdKind::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["x".into()],
|
||||
output: "j".into(),
|
||||
@@ -264,6 +280,7 @@ fn submod() {
|
||||
},
|
||||
lut: LUT(vec![([Tristate::True].into_iter().collect(), true)])
|
||||
})
|
||||
.into()
|
||||
]
|
||||
}),
|
||||
BlifEntry::Model(Model {
|
||||
@@ -273,17 +290,20 @@ fn submod() {
|
||||
outputs: Some(vec!["j".into()]),
|
||||
clocks: vec![]
|
||||
},
|
||||
commands: vec![ModelCmd::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["x".into(), "y".into()],
|
||||
output: "j".into(),
|
||||
external_dc: false
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)])
|
||||
})]
|
||||
commands: vec![
|
||||
ModelCmdKind::Gate(Gate {
|
||||
meta: GateMeta {
|
||||
inputs: vec!["x".into(), "y".into()],
|
||||
output: "j".into(),
|
||||
external_dc: false
|
||||
},
|
||||
lut: LUT(vec![(
|
||||
[Tristate::True, Tristate::True].into_iter().collect(),
|
||||
true
|
||||
)])
|
||||
})
|
||||
.into()
|
||||
]
|
||||
})
|
||||
]
|
||||
);
|
||||
@@ -434,7 +454,7 @@ fn kiss_fsm2() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tech_clock_cst() {
|
||||
fn clock_cst() {
|
||||
let ast = parse_str_blif_to_ast(
|
||||
"top.blif",
|
||||
r#"
|
||||
@@ -447,3 +467,43 @@ fn tech_clock_cst() {
|
||||
|
||||
// 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
|
||||
}
|
||||
|
Reference in New Issue
Block a user