libertyparse 0.3.0

Liberty cell library parser
Documentation
//! A liberty cell library parser written in Rust
//!
//! See [Liberty::parse_str], [Liberty], [Lib], etc.

use compact_str::CompactString;
use indexmap::{IndexMap, IndexSet};
use std::ops::Range;

/// A liberty file with multiple libraries.
/// This is the main entry of parsing.
#[derive(Default, Debug)]
pub struct Liberty {
    pub libs: Vec<(CompactString, Lib)>,
}

/// A warning record of unparsed (but semantically valid)
/// Liberty content.
///
/// Because Liberty format is too complex to cover completely,
/// we have to allow some unknown structures inside it.
#[derive(Debug)]
#[allow(dead_code)]
#[readonly::make]
pub struct Unparsed {
    /// A warning reason description
    pub reason: &'static str,
    /// The keyword that is unsupported / encountered error.
    pub keyword: CompactString,
    /// The file bytes ranges of the unparsed content.
    pub span: Range<usize>
}

/// A liberty library wrapped inside `library(...) {...}`.
///
/// It consists of metadata (e.g., PVT, thresholds, derates, units) followed by cells.
#[derive(Debug)]
pub struct Lib {
    /// The cell entries.
    pub cells: Vec<(CompactString, Cell)>,
    /// The unit definition for all value types.
    ///
    /// Remember that it is NOT applied to values automatically
    /// (to avoid numerical issues).
    /// You should manually use it to transform the units
    /// before you can use the content in parsed results.
    pub units: Units,
    /// The templates of look-up tables.
    /// 
    /// You can use it to locate the axis orders, corresponding
    /// units, etc.
    pub lut_templates: IndexMap<CompactString, LUTTemplate>,
    /// used internally. the default input pin cap.
    ///
    /// It will be propagated to the pins if the pins lack
    /// such information. We will handle this.
    pub default_input_pin_cap: f32,
    /// used internally. the default output pin cap.
    ///
    /// It will be propagated to the pins if the pins lack
    /// such information. We will handle this.
    pub default_output_pin_cap: f32,
    /// The slew derate in `slew_derate_from_library`.
    ///
    /// remember to apply this to values before you use slew LUTs.
    pub slew_derate: f32,
    /// the input delay thresholds (R/F).
    ///
    /// E.g., [0.5, 0.5]
    pub input_delay_threshold: [f32; 2],
    /// the output delay thresholds (R/F).
    ///
    /// E.g., [0.5, 0.5]
    pub output_delay_threshold: [f32; 2],
    /// the slew thresholds (R/F -> lower/upper).
    ///
    /// E.g., [(0.2, 0.8), (0.2, 0.8)]
    pub slew_threshold: [(f32, f32); 2],
    /// The unparsed terms. See [Unparsed].
    pub unparsed: Vec<Unparsed>,
}

/// A library cell consisting of pin definitions and
/// sequential functionality definitions.
#[derive(Default, Debug)]
pub struct Cell {
    /// The cell pins (name, pin).
    pub pins: Vec<(CompactString, Pin)>,
    /// The sequential definition useful in logic simulation.
    pub sequential_def: Option<SequentialDef>,
    /// The unparsed terms. See [Unparsed].
    pub unparsed: Vec<Unparsed>,
}

/// A sequential element specification.
/// Optionally defines a number of internal states.
#[derive(Debug)]
pub enum SequentialDef {
    FF(FFDef),
    Latch(LatchDef),
    StateTable(StateTableDef)
}

#[derive(Debug)]
pub struct FFDef {
    pub pin_v1: CompactString,
    pub pin_v2: CompactString,
    pub clocked_on: LogicExpr,
    pub next_state: LogicExpr,
    pub clear: Option<LogicExpr>,
    pub preset: Option<LogicExpr>,
    pub clear_preset_var1: Option<ClearPresetVar>,
    pub clear_preset_var2: Option<ClearPresetVar>
}

#[derive(Debug)]
pub struct LatchDef {
    pub pin_v1: CompactString,
    pub pin_v2: CompactString,
    pub enable: Option<LogicExpr>,  // SR latch is None
    pub data_in: Option<LogicExpr>,  // SR latch is None
    pub clear: Option<LogicExpr>,
    pub preset: Option<LogicExpr>,
    pub clear_preset_var1: Option<ClearPresetVar>,
    pub clear_preset_var2: Option<ClearPresetVar>
}

/// Clear & preset behavior definition.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ClearPresetVar {
    L, H, N, T, X
}

#[derive(Debug)]
pub struct StateTableDef {
    pub inputs: Vec<CompactString>,
    pub internals: Vec<CompactString>,
    pub rows: Vec<Vec<StateTableValue>>
}

/// All possible state table items
/// Note that we merged the "-" and "N" in next internal
/// nodes into a single "N". According to the hints in
/// Liberty docs, they are actually different in
/// event generation? No difference in our applications yet.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum StateTableValue {
    L, H, N, X, LH, HL, R, F, NR, NF
}

#[derive(Debug, Clone)]
pub struct LogicExpr {
    /// A compiled stack language representing the logic.
    /// It is essentially a postfix expression.
    pub compiled: Vec<LogicExprInst>
}

/// The element of the stack language can be:
/// 1. Push an variable to stack
/// 2. An operation that pops some variables from stack,
///    compute one logic operation, and push result
///    to stack.
#[derive(Debug, Clone)]
pub enum LogicExprInst {
    /// Push a variable.
    PushVar(CompactString),
    /// Push a 0 or 1.
    PushConst(bool),
    /// Operation can be b'&', b'|', b'!'.
    Op(u8)
}

/// A library pin (macro pin) with metadata (direction, function,
/// caps, etc) and timing arcs (delay, slew & constraints).
#[derive(Debug)]
pub struct Pin {
    /// Pin direction.
    pub direction: PinDirection,
    /// Pin function, if supplied.
    pub function: Option<LogicExpr>,
    /// Pin capacitance (R/F).
    pub cap_rf: [f32; 2],
    /// Timing arcs.
    pub timings: Vec<Timing>,
    /// The unparsed terms. See [Unparsed].
    pub unparsed: Vec<Unparsed>,
}

/// A timing arc definition. It contains arc type, timing sense,
/// and delay/slew/constraint LUTs.
#[derive(Debug)]
pub struct Timing {
    /// The other pin name (typically corresponding cell input).
    pub related_pin: CompactString,
    /// Arc type
    pub typ: TimingType,
    /// Timing sense (unate).
    pub sense: TimingSense,
    pub cell_rise: Option<LUT>,
    pub cell_fall: Option<LUT>,
    pub rise_transition: Option<LUT>,
    pub fall_transition: Option<LUT>,
    pub rise_constraint: Option<LUT>,
    pub fall_constraint: Option<LUT>,
    /// The unparsed terms. See [Unparsed].
    pub unparsed: Vec<Unparsed>,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum LUTVariable {
    Empty,
    InputNetTransition,
    TotalOutputNetCapacitance,
    ConstrainedPinTransition,
    RelatedPinTransition
}

/// A LUT template.
///
/// The units are NOT applied to indices.
#[derive(Debug)]
pub struct LUTTemplate {
    pub variables: [LUTVariable; 3],
    pub units: [f32; 3],
    pub indices: [Vec<f32>; 3],
}

/// A LUT with associated template.
///
/// The indices can come from an ad-hoc definition, or
/// inherited from the template. We handle this for you.
///
/// The values are NOT normalized with the values_unit.
#[derive(Debug)]
pub struct LUT {
    /// The template name which can be used to look up
    /// in parent [Lib] structs.
    pub template: CompactString,
    /// The indices.
    ///
    /// Unused axes are empty vectors.
    pub indices: [Vec<f32>; 3],
    /// A flattened values list
    pub values: Vec<f32>,
    /// Unit of values.
    ///
    /// currently it is most likely the same as [Units::time].
    pub values_unit: f32,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TimingType {
    Combinational,
    RisingEdge,
    FallingEdge,
    SetupRising,
    HoldRising,
    SetupFalling,
    HoldFalling,
    Unsupported(CompactString)
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TimingSense {
    PositiveUnate,
    NegativeUnate,
    NonUnate
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PinDirection {
    I, O, 
    Unsupported(CompactString),
    Unspecified
}

/// All units are stored as ratio to the standard units (s, F, Ohm, A, ...)
#[derive(Debug)]
pub struct Units {
    pub time: f32,
    pub voltage: f32,
    pub current: f32,
    pub power: f32,
    pub cap: f32,
    pub res: f32,
}

mod libertypest;

impl Liberty {
    /// The parser.
    pub fn parse_str(s: &str) -> Result<Liberty, String> {
        libertypest::parse_liberty(s)
    }

    /// log to `stderr` all unparsed keywords, for debugging purpose.
    pub fn debug_report_unparsed(&self) -> (
        IndexSet<&str>, IndexSet<&str>, IndexSet<&str>, IndexSet<&str>
    ) {
        let libwise = self.libs.iter()
            .map(|(_, lib)| lib.unparsed.iter().map(|u| u.keyword.as_str()))
            .flatten().collect::<IndexSet<&str>>();
        if libwise.len() != 0 {
            clilog::warn!(
                W_LIB_UNPARSE, "{} unparsed library-wise items: {:?}",
                libwise.len(), libwise);
        }
        
        let cellwise = self.libs.iter().map(
            |(_, lib)| lib.cells.iter().map(|(_, c)| c.unparsed.iter().map(|u| u.keyword.as_str()))
                .flatten()).flatten().collect::<IndexSet<&str>>();
        if cellwise.len() != 0 {
            clilog::warn!(
                W_LIB_UNPARSE, "{} unparsed cell-wise items: {:?}",
                cellwise.len(), cellwise);
        }
        
        let pinwise = self.libs.iter().map(
            |(_, lib)| lib.cells.iter().map(
                |(_, c)| c.pins.iter().map(|(_, p)| p.unparsed.iter().map(|u| u.keyword.as_str()))
                    .flatten()).flatten()).flatten().collect::<IndexSet<&str>>();
        if pinwise.len() != 0 {
            clilog::warn!(
                W_LIB_UNPARSE, "{} unparsed pin-wise items: {:?}",
                pinwise.len(), pinwise);
        }

        let timingwise = self.libs.iter().map(
            |(_, lib)| lib.cells.iter().map(
                |(_, c)| c.pins.iter().map(
                    |(_, p)| p.timings.iter().map(
                        |t| t.unparsed.iter().map(|u| u.keyword.as_str()))
                .flatten()).flatten()).flatten()).flatten()
            .collect::<IndexSet<&str>>();
        if timingwise.len() != 0 {
            clilog::warn!(
                W_LIB_UNPARSE, "{} unparsed timing-wise items: {:?}",
                timingwise.len(), timingwise);
        }

        (libwise, cellwise, pinwise, timingwise)
    }
}

mod fmt;

// direction provider (optional).
#[cfg(feature = "direction_provider")]
mod direction_provider;
#[cfg(feature = "direction_provider")]
pub use direction_provider::LibDirectionProvider;