logiclib 0.2.3

VLSI compiled logic library for sequential and combinational cells.
Documentation
//! Unit tests for logiclib.
//! We test sky130 A211OI, FF, and statetable-based latch here.

use logiclib::*;
use LogicVal::*;

const LIBERTY: &str = include_str!("sky130_dff_simplify.lib");

/// Compute the outcome of one assignment.
fn eval<'i>(
    table: &'i[LogicVal],
    logicpin: &LogicOutputPin,
    input: &[LogicVal]
) -> &'i[LogicVal] {
    let n_inputs = logicpin.related_inputs.len();
    let n_internals = logicpin.num_internals as usize;
    assert_eq!(input.len(), n_inputs + n_internals);
    let mut index = 0;
    for (i, v) in input.iter().enumerate().rev() {
        let base = if i < n_inputs {
            match logicpin.related_inputs.get_index(i).unwrap().1 {
                true => 7,
                false => 5
            }
        }
        else { 4 };
        let v = *v as u8 as usize;
        assert!(v < base);
        index = index * base + v;
    }
    // println!("index of {input:?} is {index}");
    let i = logicpin.table_start + index * (1 + n_internals);
    &table[i..i + n_internals + 1]
}

macro_rules! check_tb {
    ($tb:ident;
     $($lp:ident => {
        $([$($inp:ident),+] => [$($out:ident),+]),+
     }),+) => {
        $($(assert_eq!(
            eval($tb, &$lp, &[$($inp),+]), &[$($out),+],
            concat!("Pin ", stringify!($lp),
                    " failed on case ", stringify!([$($inp),+])));)+
        )+
    }
}

#[test]
#[allow(non_snake_case)]
fn test_sky130() {
    clilog::init_stderr_color_debug();
    let parsed = libertyparse::Liberty::parse_str(LIBERTY)
        .expect("parse error");

    let logiclib = LogicLib::from(&parsed);
    let tb = logiclib.truthtable.as_ref();

    clilog::info!("Table size: {} = sum({:?})", tb.len(),
                  logiclib.logic_cells.iter()
                  .map(|(_, lc)| lc.output_pins.iter()
                       .filter_map(|(_, lp)| match lp.as_ref() {
                           Ok(lp) => Some(lp.table_size),
                           Err(_) => None
                       }))
                  .flatten().collect::<Vec<_>>());

    let a211oi = logiclib.logic_cells
        .get("sky130_fd_sc_hd__a211oi_1").unwrap();
    let a211oi_Y = a211oi.output_pins
        .get("Y").unwrap().as_ref().unwrap();

    assert_eq!(format!("{:?}", a211oi_Y.related_inputs),
               r#"{"A1": false, "A2": false, "B1": false, "C1": false}"#);

    let ff = logiclib.logic_cells
        .get("sky130_fd_sc_hd__dfbbn_1").unwrap();
    let ff_Q = ff.output_pins
        .get("Q").unwrap().as_ref().unwrap();

    assert_eq!(format!("{:?}", ff_Q.related_inputs),
               r#"{"CLK_N": true, "D": false, "RESET_B": false, "SET_B": false}"#);

    let latch_precontrol = logiclib.logic_cells
        .get("sky130_fd_sc_hd__sdlclkp_4").unwrap();
    let latch_precontrol_GCLK = latch_precontrol.output_pins
        .get("GCLK").unwrap().as_ref().unwrap();

    assert_eq!(format!("{:?}", latch_precontrol_GCLK.related_inputs),
               r#"{"CLK": false, "GATE": false, "SCE": false}"#);

    check_tb! {
        tb;
        a211oi_Y => {
            [L, L, L, L] => [H],
            [L, H, L, L] => [H],
            [H, H, L, L] => [L],
            [H, H, H, H] => [L],
            [L, L, H, L] => [L],
            [L, H, L, H] => [L],
            [X, L, H, L] => [L],
            [X, Z, L, L] => [X],

            [U, U, L, L] => [U],
            [U, U, U, H] => [L],
            [H, H, U, U] => [L],
            [L, U, L, L] => [H],
            [U, L, X, L] => [X],
            [X, L, L, U] => [U]
        },
        ff_Q => {
            // low-activated async clear and preset.
            [L, U, H, H, L, H] => [L, L, H],
            [L, U, H, H, H, L] => [H, H, L],
            [U, U, L, H, H, L] => [L, L, H],
            [U, U, H, L, L, L] => [H, H, L],
            [U, U, L, L, L, H] => [H, H, L],
            // negative clock (fall trigger).
            [R, U, H, H, H, L] => [H, H, L],
            [F, L, H, H, H, L] => [L, L, H],
            [F, H, H, H, H, L] => [H, H, L]
        },
        latch_precontrol_GCLK => {
            // latch_posedge_precontrol. see synopsys pwcug.pdf.
            // !clk, (se|gate) -> latch -> (&clk) output
            [H, U, U, H] => [H, H],
            [H, U, U, L] => [L, L],
            [L, H, L, L] => [L, H],
            [L, H, H, L] => [L, H],
            [L, H, U, L] => [U, U], // should be [L, H], but our statetable approach cannot recognize it yet.
            [L, L, L, H] => [L, L]
        }
    };
}