mirror of
https://github.com/alex-s168/turbo-blif.git
synced 2025-09-10 01:55:08 +02:00
kiss parse
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# turbo-blif
|
# turbo-blif
|
||||||
|
|
||||||
low-memory-usage BLIF (berkeley logic interchange format) and KISS (finite state-machine description format) parser and writer.
|
low-memory-usage BLIF (berkeley logic interchange format) parser and writer.
|
||||||
|
|
||||||
supports:
|
supports:
|
||||||
- the latest BLIF specification (dated July 28, 1992)
|
- the latest BLIF specification (dated July 28, 1992)
|
||||||
|
55
src/ast.rs
55
src/ast.rs
@@ -50,12 +50,42 @@ impl GateLutConsumer for Gate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
|
||||||
|
pub struct FSMTransitionAST {
|
||||||
|
pub input: SmallVec<[Tristate; 8]>,
|
||||||
|
pub current_state: String,
|
||||||
|
pub next_state: String,
|
||||||
|
pub output: SmallVec<[Tristate; 8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
|
||||||
|
pub struct FSM {
|
||||||
|
pub inputs: usize,
|
||||||
|
pub outputs: usize,
|
||||||
|
pub reset_state: Option<String>,
|
||||||
|
pub states: Vec<FSMTransitionAST>,
|
||||||
|
pub physical_latch_order: Option<Vec<String>>,
|
||||||
|
pub state_assignments: Option<Vec<(String, SmallVec<[bool; 8]>)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FSMConsumer for FSM {
|
||||||
|
fn add_transition(&mut self, transition: FSMTransition) {
|
||||||
|
self.states.push(FSMTransitionAST {
|
||||||
|
input: transition.input,
|
||||||
|
current_state: transition.current_state.to_string(),
|
||||||
|
next_state: transition.next_state.to_string(),
|
||||||
|
output: transition.output,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
|
||||||
pub enum ModelCmd {
|
pub enum ModelCmd {
|
||||||
Gate(Gate),
|
Gate(Gate),
|
||||||
FF(FlipFlop),
|
FF(FlipFlop),
|
||||||
LibGate(LibGate),
|
LibGate(LibGate),
|
||||||
LibFF(LibFlipFlop),
|
LibFF(LibFlipFlop),
|
||||||
|
FSM(FSM),
|
||||||
SubModel {
|
SubModel {
|
||||||
name: Str<32>,
|
name: Str<32>,
|
||||||
map: Vec<(Str<16>, Str<16>)>,
|
map: Vec<(Str<16>, Str<16>)>,
|
||||||
@@ -97,6 +127,31 @@ impl CommandConsumer for Model {
|
|||||||
map,
|
map,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FSM = FSM;
|
||||||
|
|
||||||
|
fn fsm(&self, inputs: usize, outputs: usize, reset_state: Option<&str>) -> Self::FSM {
|
||||||
|
FSM {
|
||||||
|
inputs,
|
||||||
|
outputs,
|
||||||
|
reset_state: reset_state.map(|x| x.to_string()),
|
||||||
|
states: vec![],
|
||||||
|
physical_latch_order: None,
|
||||||
|
state_assignments: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fsm_done(
|
||||||
|
&mut self,
|
||||||
|
fsm: Self::FSM,
|
||||||
|
physical_latch_order: Option<Vec<String>>,
|
||||||
|
state_assignments: Option<Vec<(String, SmallVec<[bool; 8]>)>>,
|
||||||
|
) {
|
||||||
|
let mut fsm = fsm;
|
||||||
|
fsm.physical_latch_order = physical_latch_order;
|
||||||
|
fsm.state_assignments = state_assignments;
|
||||||
|
self.commands.push(ModelCmd::FSM(fsm));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
263
src/lib.rs
263
src/lib.rs
@@ -59,11 +59,25 @@ pub struct LibFlipFlop {
|
|||||||
pub init: FlipFlopInit,
|
pub init: FlipFlopInit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait FSMConsumer {
|
||||||
|
fn add_transition(&mut self, transition: FSMTransition);
|
||||||
|
}
|
||||||
|
|
||||||
pub trait CommandConsumer {
|
pub trait CommandConsumer {
|
||||||
type Gate: GateLutConsumer;
|
type Gate: GateLutConsumer;
|
||||||
|
type FSM: FSMConsumer;
|
||||||
|
|
||||||
fn gate(&self, gate: GateMeta) -> Self::Gate;
|
fn gate(&self, gate: GateMeta) -> Self::Gate;
|
||||||
fn gate_done(&mut self, gate: Self::Gate);
|
fn gate_done(&mut self, gate: Self::Gate);
|
||||||
|
|
||||||
|
fn fsm(&self, inputs: usize, outputs: usize, reset_state: Option<&str>) -> Self::FSM;
|
||||||
|
fn fsm_done(
|
||||||
|
&mut self,
|
||||||
|
fsm: Self::FSM,
|
||||||
|
physical_latch_order: Option<Vec<String>>,
|
||||||
|
state_assignments: Option<Vec<(String, SmallVec<[bool; 8]>)>>,
|
||||||
|
);
|
||||||
|
|
||||||
fn ff(&mut self, ff: FlipFlop);
|
fn ff(&mut self, ff: FlipFlop);
|
||||||
fn lib_gate(&mut self, gate: LibGate);
|
fn lib_gate(&mut self, gate: LibGate);
|
||||||
fn lib_ff(&mut self, ff: LibFlipFlop);
|
fn lib_ff(&mut self, ff: LibFlipFlop);
|
||||||
@@ -281,6 +295,24 @@ pub enum FlipFlopInit {
|
|||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
|
||||||
|
pub struct FSMTransition<'s> {
|
||||||
|
pub input: SmallVec<[Tristate; 8]>,
|
||||||
|
pub current_state: &'s str,
|
||||||
|
pub next_state: &'s str,
|
||||||
|
pub output: SmallVec<[Tristate; 8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn str_to_tristates<C: FromIterator<Tristate>>(s: &str) -> Result<C, ()> {
|
||||||
|
s.bytes()
|
||||||
|
.map(|x| {
|
||||||
|
let x = [x];
|
||||||
|
let x = unsafe { str::from_utf8_unchecked(&x) };
|
||||||
|
x.parse()
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_mod(
|
fn parse_mod(
|
||||||
name: &str,
|
name: &str,
|
||||||
consumer: &mut impl ModelConsumer,
|
consumer: &mut impl ModelConsumer,
|
||||||
@@ -292,7 +324,7 @@ fn parse_mod(
|
|||||||
let line = next_stmt(lines)?.unwrap();
|
let line = next_stmt(lines)?.unwrap();
|
||||||
let line = line.as_ref();
|
let line = line.as_ref();
|
||||||
let mut args = line.split(' ');
|
let mut args = line.split(' ');
|
||||||
let cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
let _cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
Some(args.map(|x| x.into()).collect())
|
Some(args.map(|x| x.into()).collect())
|
||||||
} else {
|
} else {
|
||||||
@@ -306,7 +338,7 @@ fn parse_mod(
|
|||||||
let line = next_stmt(lines)?.unwrap();
|
let line = next_stmt(lines)?.unwrap();
|
||||||
let line = line.as_ref();
|
let line = line.as_ref();
|
||||||
let mut args = line.split(' ');
|
let mut args = line.split(' ');
|
||||||
let cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
let _cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
Some(args.map(|x| x.into()).collect())
|
Some(args.map(|x| x.into()).collect())
|
||||||
} else {
|
} else {
|
||||||
@@ -320,7 +352,7 @@ fn parse_mod(
|
|||||||
let line = next_stmt(lines)?.unwrap();
|
let line = next_stmt(lines)?.unwrap();
|
||||||
let line = line.as_ref();
|
let line = line.as_ref();
|
||||||
let mut args = line.split(' ');
|
let mut args = line.split(' ');
|
||||||
let cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
let _cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
args.map(|x| x.into()).collect()
|
args.map(|x| x.into()).collect()
|
||||||
} else {
|
} else {
|
||||||
@@ -374,15 +406,7 @@ fn parse_mod(
|
|||||||
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, r) = l.as_ref().split_once(' ').ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
let invs = l
|
let invs = str_to_tristates(l).map_err(|_| BlifParserError::Invalid)?;
|
||||||
.bytes()
|
|
||||||
.map(|x| {
|
|
||||||
let x = [x];
|
|
||||||
let x = unsafe { str::from_utf8_unchecked(&x) };
|
|
||||||
x.parse()
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()
|
|
||||||
.map_err(|_| BlifParserError::Invalid)?;
|
|
||||||
let outvs = match r {
|
let outvs = match r {
|
||||||
"0" => false,
|
"0" => false,
|
||||||
"1" => true,
|
"1" => true,
|
||||||
@@ -529,7 +553,218 @@ fn parse_mod(
|
|||||||
main_consumer.search(path);
|
main_consumer.search(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: .start_kiss
|
".start_kiss" => {
|
||||||
|
if args.next().is_some() {
|
||||||
|
Err(BlifParserError::TooManyArgs)?
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_ins: usize = {
|
||||||
|
parse_padding(lines);
|
||||||
|
let line = next_stmt(lines)?.unwrap();
|
||||||
|
let line = line.as_ref().trim();
|
||||||
|
let mut args = line.split(' ');
|
||||||
|
let cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
if cmd != ".i" {
|
||||||
|
Err(BlifParserError::UnknownKw(cmd.to_string()))?
|
||||||
|
}
|
||||||
|
|
||||||
|
let v = args
|
||||||
|
.next()
|
||||||
|
.ok_or(BlifParserError::MissingArgs)?
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
if args.next().is_some() {
|
||||||
|
Err(BlifParserError::TooManyArgs)?
|
||||||
|
}
|
||||||
|
|
||||||
|
v
|
||||||
|
};
|
||||||
|
|
||||||
|
let num_outs: usize = {
|
||||||
|
parse_padding(lines);
|
||||||
|
let line = next_stmt(lines)?.unwrap();
|
||||||
|
let line = line.as_ref().trim();
|
||||||
|
let mut args = line.split(' ');
|
||||||
|
let cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
if cmd != ".o" {
|
||||||
|
Err(BlifParserError::UnknownKw(cmd.to_string()))?
|
||||||
|
}
|
||||||
|
|
||||||
|
let v = args
|
||||||
|
.next()
|
||||||
|
.ok_or(BlifParserError::MissingArgs)?
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
if args.next().is_some() {
|
||||||
|
Err(BlifParserError::TooManyArgs)?
|
||||||
|
}
|
||||||
|
|
||||||
|
v
|
||||||
|
};
|
||||||
|
|
||||||
|
let _num_terms: Option<usize> = {
|
||||||
|
parse_padding(lines);
|
||||||
|
if is_kw(lines, ".p") {
|
||||||
|
let line = next_stmt(lines)?.unwrap();
|
||||||
|
let line = line.as_ref().trim();
|
||||||
|
|
||||||
|
let mut args = line.split(' ');
|
||||||
|
let _cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
let v = args
|
||||||
|
.next()
|
||||||
|
.ok_or(BlifParserError::MissingArgs)?
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
if args.next().is_some() {
|
||||||
|
Err(BlifParserError::TooManyArgs)?
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let _num_states: Option<usize> = {
|
||||||
|
parse_padding(lines);
|
||||||
|
if is_kw(lines, ".s") {
|
||||||
|
let line = next_stmt(lines)?.unwrap();
|
||||||
|
let line = line.as_ref().trim();
|
||||||
|
|
||||||
|
let mut args = line.split(' ');
|
||||||
|
let _cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
let v = args
|
||||||
|
.next()
|
||||||
|
.ok_or(BlifParserError::MissingArgs)?
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
if args.next().is_some() {
|
||||||
|
Err(BlifParserError::TooManyArgs)?
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let reset_state: Option<String> = {
|
||||||
|
parse_padding(lines);
|
||||||
|
if is_kw(lines, ".r") {
|
||||||
|
let line = next_stmt(lines)?.unwrap();
|
||||||
|
let line = line.as_ref().trim();
|
||||||
|
|
||||||
|
let mut args = line.split(' ');
|
||||||
|
let _cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
let v = args.next().ok_or(BlifParserError::MissingArgs)?;
|
||||||
|
|
||||||
|
if args.next().is_some() {
|
||||||
|
Err(BlifParserError::TooManyArgs)?
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(v.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fsm =
|
||||||
|
consumer.fsm(num_ins, num_outs, reset_state.as_ref().map(|x| x.as_str()));
|
||||||
|
|
||||||
|
while {
|
||||||
|
parse_padding(lines);
|
||||||
|
lines.peek().is_some_and(|x| x.as_ref() != ".end_kiss")
|
||||||
|
} {
|
||||||
|
let line = next_stmt(lines)?.unwrap();
|
||||||
|
let line = line.as_ref().trim();
|
||||||
|
|
||||||
|
let mut args = line.split(' ');
|
||||||
|
|
||||||
|
let input = str_to_tristates(args.next().ok_or(BlifParserError::Invalid)?)
|
||||||
|
.map_err(|_| BlifParserError::Invalid)?;
|
||||||
|
let current_state = args.next().ok_or(BlifParserError::Invalid)?.to_string();
|
||||||
|
let next_state = args.next().ok_or(BlifParserError::Invalid)?.to_string();
|
||||||
|
let output = str_to_tristates(args.next().ok_or(BlifParserError::Invalid)?)
|
||||||
|
.map_err(|_| BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
fsm.add_transition(FSMTransition {
|
||||||
|
input,
|
||||||
|
current_state: current_state.as_str(),
|
||||||
|
next_state: next_state.as_str(),
|
||||||
|
output,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
parse_padding(lines);
|
||||||
|
let line = next_stmt(lines)?.ok_or(BlifParserError::UnexpectedEnd)?;
|
||||||
|
let line = line.as_ref().trim();
|
||||||
|
if line != ".end_kiss" {
|
||||||
|
Err(BlifParserError::Invalid)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let latch_order: Option<Vec<String>> = {
|
||||||
|
parse_padding(lines);
|
||||||
|
if is_kw(lines, ".latch_order") {
|
||||||
|
let line = next_stmt(lines)?.unwrap();
|
||||||
|
let line = line.as_ref().trim();
|
||||||
|
|
||||||
|
let mut args = line.split(' ');
|
||||||
|
let _cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
Some(args.map(|x| x.to_string()).collect())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut code_mapping = vec![];
|
||||||
|
|
||||||
|
while {
|
||||||
|
parse_padding(lines);
|
||||||
|
is_kw(lines, ".code")
|
||||||
|
} {
|
||||||
|
let line = next_stmt(lines)?.unwrap();
|
||||||
|
let line = line.as_ref().trim();
|
||||||
|
let mut args = line.split(' ');
|
||||||
|
let _cmd = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
|
||||||
|
let state = args.next().ok_or(BlifParserError::Invalid)?;
|
||||||
|
let value = args
|
||||||
|
.next()
|
||||||
|
.ok_or(BlifParserError::Invalid)?
|
||||||
|
.chars()
|
||||||
|
.map(|x| match x {
|
||||||
|
'0' => Ok(false),
|
||||||
|
'1' => Ok(true),
|
||||||
|
_ => Err(BlifParserError::Invalid),
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
code_mapping.push((state.to_string(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
consumer.fsm_done(
|
||||||
|
fsm,
|
||||||
|
latch_order,
|
||||||
|
if code_mapping.len() == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(code_mapping)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: clock & delay cst
|
// TODO: clock & delay cst
|
||||||
_ => Err(BlifParserError::UnknownKw(cmd.to_string()))?,
|
_ => Err(BlifParserError::UnknownKw(cmd.to_string()))?,
|
||||||
};
|
};
|
||||||
@@ -573,7 +808,7 @@ pub fn parse_blif(
|
|||||||
}
|
}
|
||||||
|
|
||||||
".model" => {
|
".model" => {
|
||||||
let mod_name = args.next().ok_or(BlifParserError::MissingArgs)?;
|
let mod_name = args.next().unwrap_or(file_name);
|
||||||
if args.next().is_some() {
|
if args.next().is_some() {
|
||||||
Err(BlifParserError::TooManyArgs)?;
|
Err(BlifParserError::TooManyArgs)?;
|
||||||
}
|
}
|
||||||
|
@@ -363,7 +363,7 @@ fn tech_ff() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tech_kiss_fsm() {
|
fn kiss_fsm() {
|
||||||
let ast = parse_str_blif_to_ast(
|
let ast = parse_str_blif_to_ast(
|
||||||
"top.blif",
|
"top.blif",
|
||||||
r#"
|
r#"
|
||||||
@@ -376,7 +376,6 @@ fn tech_kiss_fsm() {
|
|||||||
0 st1 st2 0
|
0 st1 st2 0
|
||||||
1 st1 st1 0
|
1 st1 st1 0
|
||||||
0 st2 st0 0
|
0 st2 st0 0
|
||||||
8
|
|
||||||
1 st2 st3 1
|
1 st2 st3 1
|
||||||
0 st3 st2 0
|
0 st3 st2 0
|
||||||
1 st3 st1 0
|
1 st3 st1 0
|
||||||
@@ -390,7 +389,7 @@ fn tech_kiss_fsm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tech_kiss_fsm2() {
|
fn kiss_fsm2() {
|
||||||
let ast = parse_str_blif_to_ast(
|
let ast = parse_str_blif_to_ast(
|
||||||
"top.blif",
|
"top.blif",
|
||||||
r#"
|
r#"
|
||||||
|
Reference in New Issue
Block a user