kiss parse

This commit is contained in:
2025-08-23 20:08:40 +02:00
parent 0fafa4c95b
commit e4ad3f29d7
4 changed files with 307 additions and 18 deletions

View File

@@ -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)

View File

@@ -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)]

View File

@@ -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)?;
} }

View File

@@ -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#"