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
|
||||
|
||||
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:
|
||||
- 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)]
|
||||
pub enum ModelCmd {
|
||||
Gate(Gate),
|
||||
FF(FlipFlop),
|
||||
LibGate(LibGate),
|
||||
LibFF(LibFlipFlop),
|
||||
FSM(FSM),
|
||||
SubModel {
|
||||
name: Str<32>,
|
||||
map: Vec<(Str<16>, Str<16>)>,
|
||||
@@ -97,6 +127,31 @@ impl CommandConsumer for Model {
|
||||
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)]
|
||||
|
263
src/lib.rs
263
src/lib.rs
@@ -59,11 +59,25 @@ pub struct LibFlipFlop {
|
||||
pub init: FlipFlopInit,
|
||||
}
|
||||
|
||||
pub trait FSMConsumer {
|
||||
fn add_transition(&mut self, transition: FSMTransition);
|
||||
}
|
||||
|
||||
pub trait CommandConsumer {
|
||||
type Gate: GateLutConsumer;
|
||||
type FSM: FSMConsumer;
|
||||
|
||||
fn gate(&self, gate: GateMeta) -> 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 lib_gate(&mut self, gate: LibGate);
|
||||
fn lib_ff(&mut self, ff: LibFlipFlop);
|
||||
@@ -281,6 +295,24 @@ pub enum FlipFlopInit {
|
||||
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(
|
||||
name: &str,
|
||||
consumer: &mut impl ModelConsumer,
|
||||
@@ -292,7 +324,7 @@ fn parse_mod(
|
||||
let line = next_stmt(lines)?.unwrap();
|
||||
let line = line.as_ref();
|
||||
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())
|
||||
} else {
|
||||
@@ -306,7 +338,7 @@ fn parse_mod(
|
||||
let line = next_stmt(lines)?.unwrap();
|
||||
let line = line.as_ref();
|
||||
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())
|
||||
} else {
|
||||
@@ -320,7 +352,7 @@ fn parse_mod(
|
||||
let line = next_stmt(lines)?.unwrap();
|
||||
let line = line.as_ref();
|
||||
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()
|
||||
} else {
|
||||
@@ -374,15 +406,7 @@ fn parse_mod(
|
||||
let l = next_stmt(lines)?.unwrap();
|
||||
let (l, r) = l.as_ref().split_once(' ').ok_or(BlifParserError::Invalid)?;
|
||||
|
||||
let invs = l
|
||||
.bytes()
|
||||
.map(|x| {
|
||||
let x = [x];
|
||||
let x = unsafe { str::from_utf8_unchecked(&x) };
|
||||
x.parse()
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(|_| BlifParserError::Invalid)?;
|
||||
let invs = str_to_tristates(l).map_err(|_| BlifParserError::Invalid)?;
|
||||
let outvs = match r {
|
||||
"0" => false,
|
||||
"1" => true,
|
||||
@@ -529,7 +553,218 @@ fn parse_mod(
|
||||
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
|
||||
_ => Err(BlifParserError::UnknownKw(cmd.to_string()))?,
|
||||
};
|
||||
@@ -573,7 +808,7 @@ pub fn parse_blif(
|
||||
}
|
||||
|
||||
".model" => {
|
||||
let mod_name = args.next().ok_or(BlifParserError::MissingArgs)?;
|
||||
let mod_name = args.next().unwrap_or(file_name);
|
||||
if args.next().is_some() {
|
||||
Err(BlifParserError::TooManyArgs)?;
|
||||
}
|
||||
|
@@ -363,7 +363,7 @@ fn tech_ff() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tech_kiss_fsm() {
|
||||
fn kiss_fsm() {
|
||||
let ast = parse_str_blif_to_ast(
|
||||
"top.blif",
|
||||
r#"
|
||||
@@ -376,7 +376,6 @@ fn tech_kiss_fsm() {
|
||||
0 st1 st2 0
|
||||
1 st1 st1 0
|
||||
0 st2 st0 0
|
||||
8
|
||||
1 st2 st3 1
|
||||
0 st3 st2 0
|
||||
1 st3 st1 0
|
||||
@@ -390,7 +389,7 @@ fn tech_kiss_fsm() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tech_kiss_fsm2() {
|
||||
fn kiss_fsm2() {
|
||||
let ast = parse_str_blif_to_ast(
|
||||
"top.blif",
|
||||
r#"
|
||||
|
Reference in New Issue
Block a user