use pest::{Parser, Span};
use pest_derive::Parser;
use compact_str::CompactString;
use std::str::FromStr;
use super::*;
#[derive(Parser)]
#[grammar = "liberty.pest"]
struct LibertyParser;
type Pair<'i> = pest::iterators::Pair<'i, Rule>;
macro_rules! unpack {
($inner:expr, $($f:expr),+, @..) => {{
let mut inner = $inner;
($($f(inner.next().unwrap())),+, inner)
}};
($inner:expr, $($f:expr),+) => {{
let mut inner = $inner;
let ret = ($($f(inner.next().unwrap())),+);
assert!(inner.next().is_none());
ret
}};
}
#[inline]
fn unescape(s: &str) -> CompactString {
if s.chars().all(|c| c != '\\') {
return s.into();
}
let mut cs = CompactString::with_capacity(s.len());
let mut s = s.chars();
while let Some(c) = s.next() {
if c == '\\' { cs.push(s.next().unwrap()); }
else { cs.push(c); }
}
cs
}
#[inline]
fn parse_str(p: Pair) -> CompactString {
assert_eq!(p.as_rule(), Rule::str);
let substr = p.as_str();
let substr = &substr[1..substr.len() - 1];
unescape(substr)
}
#[inline]
fn parse_ident(p: Pair) -> CompactString {
assert_eq!(p.as_rule(), Rule::ident);
let substr = p.as_str();
unescape(substr)
}
#[inline]
fn parse_str_or_ident(p: Pair) -> CompactString {
match p.as_rule() {
Rule::str => parse_str(p),
Rule::ident => parse_ident(p),
_ => panic!("{:?}", p.as_rule())
}
}
#[inline]
fn parse_int(p: Pair) -> i32 {
assert_eq!(p.as_rule(), Rule::int);
i32::from_str(p.as_str()).unwrap()
}
#[inline]
fn parse_float(p: Pair) -> f32 {
assert_eq!(p.as_rule(), Rule::float);
f32::from_str(p.as_str()).unwrap()
}
#[inline]
fn parse_ident_group(p: Pair) -> Vec<CompactString> {
assert_eq!(p.as_rule(), Rule::ident_group);
p.into_inner().map(parse_ident).collect()
}
fn parse_logic_expr(p: Pair) -> LogicExpr {
assert_eq!(p.as_rule(), Rule::logic_expr);
let mut r = Vec::new();
for (i, expr_and) in p.into_inner().enumerate() {
for (i, expr_xor) in expr_and.into_inner().enumerate() {
for (i, expr_tn) in expr_xor.into_inner().enumerate() {
let (expr_term, neg) = match expr_tn.as_rule() {
Rule::logic_expr_neg => {
(expr_tn.into_inner().next().unwrap(), true)
},
Rule::logic_expr_term => {
(expr_tn, false)
},
_ => unreachable!()
};
let expr_term = unpack!(expr_term.into_inner(),
|p| p);
match expr_term.as_rule() {
Rule::logic_expr_term_paren => {
r.extend(parse_logic_expr(
expr_term.into_inner().next().unwrap()
).compiled.into_iter());
},
Rule::int => {
let a = parse_int(expr_term);
r.push(LogicExprInst::PushConst(match a {
0 => false,
1 => true,
_ => unreachable!()
}));
},
Rule::ident => {
r.push(LogicExprInst::PushVar(
parse_ident(expr_term)
));
},
_ => unreachable!(
"unexpected logic expr term rule {:?}",
expr_term.as_rule())
}
if neg {
r.push(LogicExprInst::Op(b'!'));
}
if i != 0 {
r.push(LogicExprInst::Op(b'^'));
}
}
if i != 0 {
r.push(LogicExprInst::Op(b'&'));
}
}
if i != 0 {
r.push(LogicExprInst::Op(b'|'));
}
}
LogicExpr { compiled: r }
}
#[inline]
fn parse_lut_array(p: Pair) -> Vec<f32> {
assert_eq!(p.as_rule(), Rule::lut_array);
p.into_inner().map(parse_float).collect()
}
#[inline]
fn parse_unit(unit: &mut Units, p: Pair) {
assert_eq!(p.as_rule(), Rule::units);
let p = unpack!(p.into_inner(), |p| p);
let unit_kw = p.as_rule();
let (num, name) = unpack!(
p.into_inner().flatten(),
parse_float, parse_str_or_ident);
macro_rules! unit_match {
($({$rule:ident, $name:ident, $($map:tt)+}),+) => {
match unit_kw {
$(Rule::$rule => {
unit.$name = num * match name.as_str() {
$($map)+,
_ => panic!()
};
})+
_ => panic!()
}
}
}
unit_match!{
{time_unit, time, "ns" => 1e-9, "ps" => 1e-12},
{voltage_unit, voltage, "mV" => 1e-3, "V" => 1.0},
{current_unit, current, "uA" => 1e-6, "mA" => 1e-3, "A" => 1.0},
{leakage_power_unit, power, "mW" => 1e-3, "uW" => 1e-6, "nW" => 1e-9, "pW" => 1e-12},
{capacitive_load_unit, cap, "pf" => 1e-12, "ff" => 1e-15},
{pulling_resistance_unit, res, "ohm" => 1.0, "kohm" => 1e3}
}
}
#[inline]
fn parse_properties_library(lib: &mut Lib, p: Pair) {
assert_eq!(p.as_rule(), Rule::properties_library);
let p = unpack!(p.into_inner(), |p| p);
let prop_kw = p.as_rule();
let val = unpack!(p.into_inner(), parse_float);
match prop_kw {
Rule::default_inout_pin_cap => {
lib.default_input_pin_cap = val;
lib.default_output_pin_cap = val;
}
Rule::default_input_pin_cap => {
lib.default_input_pin_cap = val;
}
Rule::default_output_pin_cap => {
lib.default_output_pin_cap = val;
}
Rule::slew_derate_from_library => {
lib.slew_derate = val;
}
Rule::input_threshold_pct_fall => {
lib.input_delay_threshold[1] = val / 100.;
}
Rule::input_threshold_pct_rise => {
lib.input_delay_threshold[0] = val / 100.;
}
Rule::output_threshold_pct_fall => {
lib.output_delay_threshold[1] = val / 100.;
}
Rule::output_threshold_pct_rise => {
lib.output_delay_threshold[0] = val / 100.;
}
Rule::slew_lower_threshold_pct_fall => {
lib.slew_threshold[1].0 = val / 100.;
}
Rule::slew_lower_threshold_pct_rise => {
lib.slew_threshold[0].0 = val / 100.;
}
Rule::slew_upper_threshold_pct_fall => {
lib.slew_threshold[1].1 = val / 100.;
}
Rule::slew_upper_threshold_pct_rise => {
lib.slew_threshold[0].1 = val / 100.;
}
_ => panic!()
}
}
#[inline]
fn unparsed(reason: &'static str, keyword: CompactString, span: Span) -> Unparsed {
let span = span.start()..span.end();
Unparsed { reason, keyword, span }
}
#[inline]
fn unparsed_tokenitem(p: Pair) -> Unparsed {
let span = p.as_span();
let span = span.start()..span.end();
let (keyword, _) = unpack!(p.into_inner(), parse_ident, @..);
Unparsed { reason: "Unparsed token item",
keyword, span }
}
fn parse_lut_template(u: &Units, p: Pair)
-> Result<(CompactString, LUTTemplate), Unparsed>
{
assert_eq!(p.as_rule(), Rule::lut_template);
use LUTVariable::*;
let span = p.as_span();
let (lt_type, lt_kw, lt_items) =
unpack!(p.into_inner(), parse_ident, parse_str_or_ident, @..);
let mut lt = LUTTemplate {
variables: [Empty; 3],
units: [f32::NAN; 3],
indices: Default::default(),
};
for p in lt_items {
match p.as_rule() {
Rule::lut_variable => {
let (id, typ) = unpack!(p.into_inner(), parse_int, parse_str_or_ident);
assert!(id <= 3 && id >= 1);
lt.variables[(id - 1) as usize] = match typ.as_str() {
"input_net_transition" => InputNetTransition,
"total_output_net_capacitance" => TotalOutputNetCapacitance,
"constrained_pin_transition" => ConstrainedPinTransition,
"related_pin_transition" => RelatedPinTransition,
_ => {
return Err(unparsed("unrecognized lut variable type, ignoring this table", lt_type, span));
}
};
lt.units[(id - 1) as usize] = match typ.as_str() {
"input_net_transition"
| "constrained_pin_transition"
| "related_pin_transition" => u.time,
"total_output_net_capacitance" => u.current,
_ => panic!()
};
}
Rule::lut_index => {
let (id, v) = unpack!(p.into_inner(), parse_int, parse_lut_array);
assert!(id <= 3 && id >= 1);
lt.indices[(id - 1) as usize] = v;
}
_ => panic!()
}
}
Ok((lt_kw, lt))
}
fn parse_lut(lib: &Lib, p: Pair) -> (CompactString, LUT) {
assert_eq!(p.as_rule(), Rule::lut_instance);
let (typ, template, items) = unpack!(
p.into_inner(), parse_ident, parse_str_or_ident, @..);
let lut_template = lib.lut_templates.get(&template).unwrap();
let values_unit = match typ.as_str() {
"cell_fall" |
"cell_rise" |
"fall_transition" |
"rise_transition" |
"fall_constraint" |
"rise_constraint" => lib.units.time,
_ => panic!()
};
let mut lut = LUT {
template,
indices: lut_template.indices.clone(),
values: Vec::new(),
values_unit
};
for p in items {
match p.as_rule() {
Rule::lut_index => {
let (id, v) = unpack!(p.into_inner(), parse_int, parse_lut_array);
assert!(id <= 3 && id >= 1);
lut.indices[(id - 1) as usize] = v;
}
Rule::lut_values => {
let v = unpack!(p.into_inner(), parse_lut_array);
lut.values = v;
}
_ => panic!()
}
}
(typ, lut)
}
fn parse_timing(lib: &Lib, p: Pair) -> Timing {
assert_eq!(p.as_rule(), Rule::timing);
let mut timing = Timing {
related_pin: CompactString::with_capacity(0),
typ: TimingType::Combinational, sense: TimingSense::NonUnate,
cell_rise: None,
cell_fall: None,
rise_transition: None,
fall_transition: None,
rise_constraint: None,
fall_constraint: None,
unparsed: Vec::new()
};
for p in p.into_inner() {
match p.as_rule() {
Rule::timing_related_pin => {
timing.related_pin = unpack!(p.into_inner(), parse_str_or_ident);
}
Rule::timing_sense => {
let p = unpack!(p.into_inner(), |p| p);
timing.sense = match p.as_str() {
"positive_unate" => TimingSense::PositiveUnate,
"negative_unate" => TimingSense::NegativeUnate,
"non_unate" => TimingSense::NonUnate,
_ => panic!()
};
}
Rule::timing_type => {
let p = unpack!(p.into_inner(), |p| p);
timing.typ = match p.as_str() {
"combinational" |
"combinational_rise" |
"combinational_fall" => TimingType::Combinational,
"rising_edge" => TimingType::RisingEdge,
"falling_edge" => TimingType::FallingEdge,
"setup_rising" => TimingType::SetupRising,
"hold_rising" => TimingType::HoldRising,
"setup_falling" => TimingType::SetupFalling,
"hold_falling" => TimingType::HoldFalling,
_ => TimingType::Unsupported(p.as_str().into())
};
}
Rule::nldm_lut => {
let (typ, lut) = parse_lut(lib, unpack!(p.into_inner(), |p| p));
match typ.as_str() {
"cell_rise" => timing.cell_rise = Some(lut),
"cell_fall" => timing.cell_fall = Some(lut),
"rise_transition" => timing.rise_transition = Some(lut),
"fall_transition" => timing.fall_transition = Some(lut),
"rise_constraint" => timing.rise_constraint = Some(lut),
"fall_constraint" => timing.fall_constraint = Some(lut),
_ => panic!()
}
}
Rule::unparsed_tokenitem => {
timing.unparsed.push(unparsed_tokenitem(p));
}
_ => panic!()
}
}
timing
}
fn parse_pin(lib: &Lib, p: Pair) -> (CompactString, Pin) {
assert_eq!(p.as_rule(), Rule::pin);
use PinDirection::*;
let mut pin = Pin {
direction: Unspecified,
function: None,
cap_rf: [f32::NAN, f32::NAN],
timings: Vec::new(),
unparsed: Vec::new()
};
let (name, items) =
unpack!(p.into_inner(), parse_str_or_ident, @..);
for p in items {
match p.as_rule() {
Rule::pin_direction => {
let d = unpack!(p.into_inner(), parse_str_or_ident);
pin.direction = match d.as_str() {
"input" => I,
"output" => O,
_ => Unsupported(d.clone())
};
let default_cap = match d.as_str() {
"input" => lib.default_input_pin_cap,
"output" => lib.default_output_pin_cap,
_ => f32::NAN
};
for rf in 0..2 {
if pin.cap_rf[rf].is_nan() {
pin.cap_rf[rf] = default_cap;
}
}
}
Rule::pin_function => {
let f = unpack!(p.into_inner(), parse_logic_expr);
pin.function = Some(f);
}
Rule::pin_cap => {
let (typ, val) = unpack!(p.into_inner(), |p| p, parse_float);
match typ.as_str() {
"capacitance" => {
pin.cap_rf[0] = val;
pin.cap_rf[1] = val;
}
"rise_capacitance" => {
pin.cap_rf[0] = val;
}
"fall_capacitance" => {
pin.cap_rf[1] = val;
}
_ => panic!()
}
}
Rule::timing => {
pin.timings.push(parse_timing(lib, p));
}
Rule::unparsed_tokenitem => {
pin.unparsed.push(unparsed_tokenitem(p));
}
_ => panic!()
}
}
(name, pin)
}
macro_rules! seq_def_parse_rule {
($p:ident, $cpv1:ident, $cpv2:ident;
$($rule:ident, $var:ident;)+) => {
match $p.as_rule() {
$(Rule::$rule => {
$var = Some(unpack!($p.into_inner(),
parse_logic_expr));
})+,
Rule::seq_def_clear_preset_var1 => {
$cpv1 = Some(unpack!($p.into_inner(),
parse_clear_preset_var))
},
Rule::seq_def_clear_preset_var2 => {
$cpv2 = Some(unpack!($p.into_inner(),
parse_clear_preset_var))
},
_ => unreachable!()
}
}
}
fn parse_clear_preset_var(p: Pair) -> ClearPresetVar {
assert_eq!(p.as_rule(), Rule::seq_def_clear_preset_var_val);
use ClearPresetVar::*;
match p.as_str() {
"L" => L, "H" => H, "N" => N, "T" => T, "X" => X,
_ => unreachable!()
}
}
fn parse_ff_def(p: Pair) -> FFDef {
assert_eq!(p.as_rule(), Rule::ff_def);
let (mut clocked_on, mut next_state, mut clear, mut preset,
mut clear_preset_var1, mut clear_preset_var2) =
(None, None, None, None, None, None);
let (pin_v1, pin_v2, inner) = unpack!(
p.into_inner(),
parse_str_or_ident,
parse_str_or_ident,
@..
);
for p in inner {
seq_def_parse_rule! {
p, clear_preset_var1, clear_preset_var2;
seq_def_clocked_on, clocked_on;
seq_def_next_state, next_state;
seq_def_clear, clear;
seq_def_preset, preset;
}
}
let clocked_on = clocked_on.unwrap();
let next_state = next_state.unwrap();
FFDef { pin_v1, pin_v2, clocked_on, next_state,
clear, preset, clear_preset_var1, clear_preset_var2 }
}
fn parse_latch_def(p: Pair) -> LatchDef {
assert_eq!(p.as_rule(), Rule::latch_def);
let (mut enable, mut data_in, mut clear, mut preset,
mut clear_preset_var1, mut clear_preset_var2) =
(None, None, None, None, None, None);
let (pin_v1, pin_v2, inner) = unpack!(
p.into_inner(),
parse_str_or_ident,
parse_str_or_ident,
@..
);
for p in inner {
seq_def_parse_rule! {
p, clear_preset_var1, clear_preset_var2;
seq_def_enable, enable;
seq_def_data_in, data_in;
seq_def_clear, clear;
seq_def_preset, preset;
}
}
assert!(!(enable.is_some() ^ data_in.is_some()));
assert!(enable.is_some() || clear.is_some());
LatchDef { pin_v1, pin_v2, enable, data_in,
clear, preset, clear_preset_var1, clear_preset_var2 }
}
fn parse_statetable_val(p: Pair) -> StateTableValue {
use StateTableValue::*;
match p.as_str() {
"L" => L,
"H" => H,
"-" | "N" => N,
"L/H" => LH,
"H/L" => HL,
"R" => R,
"F" => F,
"~R" => NR,
"~F" => NF,
"X" => X,
_ => unreachable!()
}
}
fn parse_statetable_def(p: Pair) -> StateTableDef {
assert_eq!(p.as_rule(), Rule::statetable_def);
let (inputs, internals, inner) = unpack!(
p.into_inner(),
parse_ident_group,
parse_ident_group,
@..
);
let expect_len = inputs.len() + internals.len() * 2;
let rows = inner.map(|p| {
assert_eq!(p.as_rule(), Rule::statetable_line);
let ret = p.into_inner().map(parse_statetable_val)
.collect::<Vec<_>>();
assert_eq!(ret.len(), expect_len);
ret
}).collect();
StateTableDef { inputs, internals, rows }
}
fn parse_cell(lib: &Lib, p: Pair) -> (CompactString, Cell) {
assert_eq!(p.as_rule(), Rule::cell);
let (name, items) =
unpack!(p.into_inner(), parse_str_or_ident, @..);
let mut pins = Vec::new();
let mut unparsed = Vec::new();
let mut sequential_def = None;
for p in items {
match p.as_rule() {
Rule::pin => {
let (name, pin) = parse_pin(lib, p);
pins.push((name, pin));
}
Rule::sequential_def => {
assert!(
sequential_def.is_none(),
"multiple sequential defs in one cell {}.",
name);
let p = p.into_inner().next().unwrap();
use SequentialDef::*;
sequential_def = Some(match p.as_rule() {
Rule::ff_def => FF(parse_ff_def(p)),
Rule::latch_def => Latch(parse_latch_def(p)),
Rule::statetable_def => StateTable(parse_statetable_def(p)),
_ => unreachable!()
});
}
Rule::unparsed_tokenitem => {
unparsed.push(unparsed_tokenitem(p));
}
_ => panic!()
}
}
(name, Cell { pins, sequential_def, unparsed })
}
pub fn parse_lib(p: Pair) -> (CompactString, Lib) {
assert_eq!(p.as_rule(), Rule::library);
let mut inner = p.into_inner();
let name = parse_str_or_ident(inner.next().unwrap());
let mut ret = Lib {
cells: Vec::new(),
units: Units { time: 1e-9, voltage: 1.0, current: f32::NAN, power: f32::NAN, cap: f32::NAN, res: f32::NAN, },
lut_templates: IndexMap::from([
("scalar".into(),
LUTTemplate {
variables: [LUTVariable::Empty; 3],
units: [f32::NAN; 3],
indices: Default::default(),
})]),
default_input_pin_cap: f32::NAN,
default_output_pin_cap: f32::NAN,
slew_derate: 1.0,
input_delay_threshold: [0.5, 0.5],
output_delay_threshold: [0.5, 0.5],
slew_threshold: [(0.2, 0.8), (0.2, 0.8)],
unparsed: Vec::new()
};
for p in inner {
match p.as_rule() {
Rule::units => {
parse_unit(&mut ret.units, p);
}
Rule::properties_library => {
parse_properties_library(&mut ret, p);
}
Rule::lut_template => {
match parse_lut_template(&ret.units, p) {
Ok((name, lt)) => {
assert!(ret.lut_templates.insert(name, lt).is_none());
}
Err(e) => { ret.unparsed.push(e); }
}
}
Rule::cell => {
let (name, cell) = parse_cell(&ret, p);
ret.cells.push((name, cell));
}
Rule::unparsed_tokenitem => {
ret.unparsed.push(unparsed_tokenitem(p));
}
_ => panic!()
}
}
(name, ret)
}
pub fn parse_liberty(s: &str) -> Result<Liberty, String> {
let parse_result = match LibertyParser::parse(Rule::main, &s) {
Ok(mut r) => r.next().unwrap(),
Err(e) => return Err(format!("{e}")),
};
Ok(Liberty{ libs:
parse_result.into_inner()
.map(parse_lib).collect() })
}