svf 0.3.0

Serial Vector Format (SVF) file parser
Documentation
// Copyright 2021 Adam Greig
// Licensed under the MIT license.

#![allow(clippy::upper_case_acronyms)]

//! # svf
//!
//! Parse and generate SVF files.
//!
//! Use [`parse_complete`] to parse a full SVF file into a vector of [`Command`],
//! [`parse_iter`] to create an iterator over [`Command`] which parses incrementally,
//! or [`parse_iter_bufread`] to create an iterator using a `BufRead` input, allowing
//! both the input and output to be processed piece-by-piece.
//!
//! Once parsed, or if you construct [`Command`] manually, the `Display` trait
//! implementation on [`Command`] can be used to generate SVF files.

mod display;
mod parser;
pub use parser::{parse_complete, parse_iter, parse_iter_bufread, SVFParseError as ParseError};

/// IEEE 1149.1 TAP states with SVF TAP state names.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum State {
    /// Test-Logic-Reset
    RESET,

    /// Run-Test/Idle
    IDLE,

    /// Select-DR-Scan
    DRSELECT,

    /// Capture-DR
    DRCAPTURE,

    /// Shift-DR
    DRSHIFT,

    /// Exit1-DR
    DREXIT1,

    /// Pause-DR
    DRPAUSE,

    /// Exit2-DR
    DREXIT2,

    /// Update-DR
    DRUPDATE,

    /// Select-IR-Scan
    IRSELECT,

    /// Capture-IR
    IRCAPTURE,

    /// Shift-IR
    IRSHIFT,

    /// Exit1-IR
    IREXIT1,

    /// Pause-IR
    IRPAUSE,

    /// Exit2-IR
    IREXIT2,

    /// Update-IR
    IRUPDATE,
}

impl State {
    /// Check if this state is one of the stable states IRPAUSE, DRPAUSE, RESET, or IDLE.
    pub fn is_stable(&self) -> bool {
        matches!(self, State::DRPAUSE | State::IRPAUSE | State::RESET | State::IDLE)
    }

    /// Return the default path to move from the current state `self` to an end state `end`,
    /// when both `self` and `end` are stable states.
    ///
    /// The returned path only includes the intermediate states and not the start or end state.
    /// If either `self` or `end` are not stable, None is returned.
    pub fn default_path(&self, end: &State) -> Option<Vec<State>> {
        use State::*;
        match (self, end) {
            (RESET, RESET)      => Some(vec![]),
            (RESET, IDLE)       => Some(vec![]),
            (RESET, DRPAUSE)    => Some(vec![IDLE, DRSELECT, DRCAPTURE, DREXIT1]),
            (RESET, IRPAUSE)    => Some(vec![IDLE, DRSELECT, IRSELECT, IRCAPTURE, IREXIT1]),
            (IDLE, RESET)       => Some(vec![DRSELECT, IRSELECT]),
            (IDLE, IDLE)        => Some(vec![]),
            (IDLE, DRPAUSE)     => Some(vec![DRSELECT, DRCAPTURE, DREXIT1]),
            (IDLE, IRPAUSE)     => Some(vec![DRSELECT, IRSELECT, IRCAPTURE, IREXIT1]),
            (DRPAUSE, RESET)    => Some(vec![DREXIT2, DRUPDATE, DRSELECT, IRSELECT]),
            (DRPAUSE, IDLE)     => Some(vec![DREXIT2, DRUPDATE]),
            (DRPAUSE, DRPAUSE)  => Some(vec![DREXIT2, DRUPDATE, DRSELECT, DRCAPTURE, DREXIT1]),
            (DRPAUSE, IRPAUSE)  => Some(vec![DREXIT2, DRUPDATE, DRSELECT, IRSELECT, IRCAPTURE,
                                             IREXIT1]),
            (IRPAUSE, RESET)    => Some(vec![IREXIT2, IRUPDATE, DRSELECT, IRSELECT]),
            (IRPAUSE, IDLE)     => Some(vec![IREXIT2, IRUPDATE]),
            (IRPAUSE, DRPAUSE)  => Some(vec![IREXIT2, IRUPDATE, DRSELECT, DRCAPTURE, DREXIT1]),
            (IRPAUSE, IRPAUSE)  => Some(vec![IREXIT2, IRUPDATE, DRSELECT, IRSELECT, IRCAPTURE,
                                             IREXIT1]),
            _                   => None,
        }
    }
}

/// Vector characters for a parallel test vector.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum VectorChar {
    /// Drive logical 1
    H,

    /// Drive logical 0
    L,

    /// Drive high impedance
    Z,

    /// Detect logical 1
    U,

    /// Detect logical 0
    D,

    /// Detect unknown
    X,
}

/// Data pattern used for HDR, HIR, SDR, SIR, TDR, and TIR commands.
#[derive(Clone, Debug, PartialEq)]
pub struct Pattern {
    /// Number of bits to be scanned.
    pub length: u32,

    /// Value to be scanned into the target.
    /// If not specified, the previously specified TDI for this command is used.
    /// Bits are packed into bytes least-significant-bit and least-significant-byte first,
    /// and the vector is automatically zero-padded to contain enough bits for the length.
    pub tdi: Option<Vec<u8>>,

    /// Value to compare against actual values scanned out of the target.
    /// If not specified, no comparison is performed.
    /// Bits are packed into bytes least-significant-bit and least-significant-byte first,
    /// and the vector is automatically zero-padded to contain enough bits for the length.
    pub tdo: Option<Vec<u8>>,

    /// Mask used when comparing TDO values against actual values.
    /// 1 indicates care, 0 indicates don't-care.
    /// If not specified, the previously specified MASK for this command is used.
    /// Bits are packed into bytes least-significant-bit and least-significant-byte first,
    /// and the vector is automatically zero-padded to contain enough bits for the length.
    pub mask: Option<Vec<u8>>,

    /// Mask TDI data.
    /// 1 indicates care, 0 indicates don't-care.
    /// If not specified, the previously specified SMASK for this command is used.
    /// Bits are packed into bytes least-significant-bit and least-significant-byte first,
    /// and the vector is automatically zero-padded to contain enough bits for the length.
    pub smask: Option<Vec<u8>>,
}

impl Pattern {
    /// Extend all non-None scan data to contain as many trailing 0s
    /// as required to make up the bit length.
    fn extend(mut self) -> Self {
        for data in [&mut self.tdi, &mut self.tdo, &mut self.mask, &mut self.smask].iter_mut() {
            if let Some(data) = data {
                while data.len() * 8 < self.length as usize {
                    data.push(0);
                }
            }
        }
        self
    }
}

/// Possible directions for a column in a PIOMAP command.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PIOMapDirection {
    /// Input to the unit under test, uses H, L, or Z characters.
    In,

    /// Output from the unit under test, uses U, D, or X characters.
    Out,

    /// Bidirectional, may use any characters.
    InOut,
}

/// Possible clocks for the `run_clk` argument to RUNTEST.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RunClock {
    /// Test clock
    TCK,

    /// System clock
    SCK,
}

/// Possible modes for the TRST signal.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TRSTMode {
    /// Active (logic 0)
    On,

    /// Active (logic 1)
    Off,

    /// High impedance
    Z,

    /// Not present
    Absent,
}

/// Minimum and optional maximum time to run a RunTest command for.
#[derive(Clone, Debug, PartialEq)]
pub struct RunTestTime {
    pub min: f64,
    pub max: Option<f64>,
}

/// Possible forms of the RunTest arguments.
#[derive(Clone, Debug, PartialEq)]
pub enum RunTestForm {
    Clocked {
        run_count: u32,
        run_clk: RunClock,
        time: Option<RunTestTime>,
    },
    Timed(RunTestTime),
}

/// SVF command and corresponding parsed data.
#[derive(Clone, Debug, PartialEq)]
pub enum Command {
    /// State for the bus after a DR scan.
    ///
    /// The state is checked to be a stable state when parsing.
    EndDR(State),

    /// State for the bus after an IR scan.
    ///
    /// The state is checked to be a stable state when parsing.
    EndIR(State),

    /// Maximum TCK frequency for subsequent scans, state changes, and test operations.
    Frequency(Option<f64>),

    /// Default header pattern to be shifted in before every data register scan operation.
    HDR(Pattern),

    /// Default header pattern to be shifted in before every instruction register scan operation.
    HIR(Pattern),

    /// Parallel input/output test vector.
    PIO(Vec<VectorChar>),

    /// Define I/O direction and name for each column in a PIO command.
    /// Each entry corresponds to a column with associated direction and logical name.
    PIOMap(Vec<(PIOMapDirection, String)>),

    /// Force the target to the specified state for a specified number of clocks
    /// or a specified length of time or both, then move to the specified end state.
    RunTest {
        /// State to hold during this command.
        /// Checked to be a stable state when parsing.
        /// If not specified, use the run_state specified in the previous RUNTEST command.
        run_state: Option<State>,

        /// The run_count, run_clk, min_time, and max_time parameters are stored
        /// in this RunTestForm enum which encodes the various ways in which they
        /// may be specified or omitted.
        form: RunTestForm,

        /// State to enter after completion of command.
        /// Checked to be a stable state when parsing.
        /// If not specified, use the default end state, which is the most
        /// recently specified end_state or run_state, or IDLE otherwise.
        end_state: Option<State>,
    },

    /// Scan data register with specified pattern.
    SDR(Pattern),

    /// Scan instruction register with specified pattern.
    SIR(Pattern),

    /// Force target to a stable state, optionally specifying the path to traverse.
    /// If no path is specified, use the default path between the current and final state.
    State {
        /// Path to take to reach the end state. States must be in an order that obeys the
        /// TAP state machine. If not specified, the default path from the current state to
        /// the end state is used.
        path: Option<Vec<State>>,

        /// Final state to reach. Checked to be a stable state when parsing.
        end: State,
    },

    /// Default trailer pattern to be shifted in before every data register scan operation.
    TDR(Pattern),

    /// Default trailer pattern to be shifted in before every instruction register scan operation.
    TIR(Pattern),

    /// Operation of TRST signal.
    TRST(TRSTMode),
}