From e4ad3f29d77d3f70053f548359b4f15d9de40da3 Mon Sep 17 00:00:00 2001 From: Alexander Nutz Date: Sat, 23 Aug 2025 20:08:40 +0200 Subject: [PATCH] kiss parse --- README.md | 2 +- src/ast.rs | 55 +++++++++++ src/lib.rs | 263 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/tests.rs | 5 +- 4 files changed, 307 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ae09ed1..68c3286 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/src/ast.rs b/src/ast.rs index 3b7324a..e4ade96 100644 --- a/src/ast.rs +++ b/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, + pub states: Vec, + pub physical_latch_order: Option>, + pub state_assignments: Option)>>, +} + +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>, + state_assignments: Option)>>, + ) { + let mut fsm = fsm; + fsm.physical_latch_order = physical_latch_order; + fsm.state_assignments = state_assignments; + self.commands.push(ModelCmd::FSM(fsm)); + } } #[derive(Debug)] diff --git a/src/lib.rs b/src/lib.rs index 807a588..906d065 100644 --- a/src/lib.rs +++ b/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>, + state_assignments: Option)>>, + ); + 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>(s: &str) -> Result { + s.bytes() + .map(|x| { + let x = [x]; + let x = unsafe { str::from_utf8_unchecked(&x) }; + x.parse() + }) + .collect::>() +} + 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::>() - .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 = { + 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 = { + 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 = { + 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> = { + 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::>()?; + + 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)?; } diff --git a/src/tests.rs b/src/tests.rs index 2bfbca5..14c5545 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -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#"