/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: BERT.java
 * Written by Jonathan Gainsley, Sun Microsystems.
 *
 * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.simulation.test;

import java.util.Comparator;
import java.util.Iterator;

/**
 * Interface for a HP81250 Bit Error Rate Tester.
 * A BERT is composed of several test modules. Each Module
 * is composed of one or more terminals.  A terminal drives a
 * single waveform (and it's complement for differential signals)
 * to the chip.
 * <P>
 * The BERT interface tries to hide the details from the user -
 * handles names, port locations, terminal locations, etc. Rather
 * than having to know what the address and port location of a Data Generator
 * is on the BERT, the user instead calls BERT.createDataGenerator().
 * This will return a DataGenerator for the user to use.
 * <P>
 * It is up to the implementation of the BERT to return a DataGenerator
 * and keep track of its address and port.  Or, to issue an error if
 * there are no (more) DataGenerators available on the BERT.
 */
public interface BERT {

    /**
     * Create a Data Module that is part of the BERT for use
     * by the user. May return null if not more data modules
     * can be created (i.e. are present in the real BERT).
     * @return a new DataModule
     */
    public DataModule createDataModule();

    /**
     * Run the BERT and its configured modules for the specified
     * time in nanoseconds. This is a replacement for doing
     * start, wait(nanoseconds), stop.
     * @param nanoseconds
     */
    public void run(double nanoseconds);

    /**
     * Same as {@link #run(double)}, but in minutes for
     * very long runs.
     * @param minutes number of minutes to run
     */
    public void runMinutes(double minutes);

    // ======================================================================

    /**
     * A Data Generator/Analyzer Module.
     */
    public interface DataModule {

        /**
         * Create a Data Generator from this module.
         * Returns null if not possible.  It is up
         * to the implementation of the Module to
         * know what is the number of data generators
         * available on this module.
         * @return a Data Generator.
         */
        public DataGenerator createDataGenerator();

        /**
         * Create a Data Analyzer from this module.
         * Returns null if not possible.  It is up
         * to the implementation of the Module to
         * know what is the number of data analyzers
         * available on this module.
         * @return a Data Analyzer.
         */
        public DataAnalyzer createDataAnalyzer();

        /**
         * This sets the frequency of the module,
         * which also dictates the frequency of all its terminals
         * @param freqKHz the frequency in Kilohertz
         */
        public void setFrequency(double freqKHz);

        /**
         * Get the frequency set for this module
         * @return the frequency in Kilohertz
         */
        public double getFrequency();

        /**
         * Return the frequency as a period, in nanoseconds.
         * @return the period in nanoseconds
         */
        public double getPeriod();
    }

    /**
     * A Terminal drives or reads a signal to or from a
     * chip.
     */
    public interface Terminal {

        /**
         * Get the parent Module that this Terminal is part of.
         * @return the parent Module
         */
        public DataModule getParentModule();

        /**
         * Enable or disable this Terminal
         * @param enable true to enable (should be default),
         * false to disable
         */
        public void setEnabled(boolean enable);

        /**
         * Get whether or not this Terminal is enabled.
         * @return true if it is enabled, false otherwise.
         */
        public boolean isEnabled();
    }

    /**
     * A Data Generator is a terminal that generates data
     */
    public interface DataGenerator extends Terminal {

        /**
         * Set the pattern that this generator will generate.
         * The frequency is set by the module this generator is in.
         * @param start the start of the pattern
         * @param repeat the pattern that will repeat after the start pattern
         */
        public void setPattern(BitVector start, BitVector repeat);

        /**
         * Set the pattern that this generator will generate.
         * The frequency is set by the module this generator is in.
         * @param start the start of the pattern
         * @param repeat the pattern that will repeat after the start pattern
         */
        public void setPattern(String start, String repeat);

        /**
         * Set the delay before the pattern starts. Defaults to zero.
         * @param ns the time delay, in nanoseconds
         */
        public void setDelay(double ns);

        /**
         * Set the way data is generated.
         * @param mode which mode to use.
         */
        public void setSignalMode(SignalMode mode);

        /**
         * Configure this terminal to generate a clock. This sets pattern
         * to be always 1, and mode to be Return to Zero. The clock
         * transitions high at time 0 and every period multiple thereafter.
         * You will still need to set the voltage and delay (if any).
         */
        public void setClock();

        /**
         * Configure this terminal to generate a DDR clock. This sets
         * the pattern to be alternating 1's and 0's, and mode to be
         * Non-return to zero. The clock transistions to high at time 0,
         * and then transistions to the opposite voltage every period
         * multiple thereafter.
         * You will still need to set the voltage and delay (if any).
         */ 
        public void setDDRClock();

        /**
         * Add the name of a pin this terminal connects to.
         * A terminal can connect to several pins by shorting them together.
         * @param pinName the pin name
         */
        public void addPinName(String pinName);

        /**
         * Many terminal outputs both True and Complement.
         * This adds the name of a the complement terminal connects to.
         * A terminal can connect to several pins by shorting them together.
         * @param pinName the pin name
         */
        public void addPinNameComplement(String pinName);

        /**
         * Get the names of pins connected to the true output
         * of this terminal (String iterator).
         * @return Iterator over pin names.
         */
        public Iterator getPinNames();

        /**
         * Get the names of pins connected to the complement output
         * of this terminal (String iterator).
         * @return Iterator over pin names in complement output.
         */
        public Iterator getPinNamesComplement();

        /**
         * Clear all pin assignments (both true and complement).
         */
        public void clearPins();

        /**
         * Set the voltage driven by Data Generator.  Note the complement
         * is only a complement value, so the voltage is never negative.
         * @param voltageLow the voltage for a logic low value
         * @param voltageHigh the voltage for a logic high value
         */
        public void setVoltage(double voltageLow, double voltageHigh);

        /**
         * Enable or disable complement signal
         * @param enable true to enable, false to disable
         */
        public void enableComplement(boolean enable);
    }

    /**
     * A Data Analyzer is a terminal that acquires/analyzes data.
     * It can be used in conjunction with a {@link DataGenerator}
     * to do bit error rate testing.
     */
    public interface DataAnalyzer extends Terminal {

        /**
         * Set the delay before starting to acquire data. The delay
         * is the sum of a number of periods (frequency set by the parent Module)
         * plus some number of absolute nanoseconds.
         * @param periods the number of periods
         * @param ns the number of nanoseconds
         */
        public void setDelay(int periods, double ns);

        /**
         * Measure the Bit Error Rate using expected data from the
         * given data generator. Any delay set on the generator is
         * added to the delay set on this Analyzer.
         * @param expectedData
         */
        public void measureBER(DataGenerator expectedData);

        /**
         * Get the Bit Error Rate measured the last time
         * the BERT was run.  If the analyzer was not
         * configured to measure the BER from a DataGenerator,
         * this returns zero.
         * @return the Bit Error Rate (num failed / num tested)
         */
        public double getMeasuredBER();

        /**
         * Set the name of the pin this terminal connects to.
         * @param pinName the pin name
         */
        public void setPinName(String pinName);

        /**
         * Get the name of the pin this terminal connects to.
         * @return the pin this analyzer is connected to
         */
        public String getPinName();

        /**
         * Set the voltage thresholds for distinguishing a logic low
         * and logic high value.
         * @param voltageLowThreshold voltages below this value will be considered logic low
         * @param voltageHighThreshold voltages above this value will be considered logic high
         */
        public void setVoltageThreshold(double voltageLowThreshold, double voltageHighThreshold);

        /**
         * Return a BitVector representing the captured data
         * @return the BitVector of the captured data.
         */
        public BitVector getCapturedData();
    }


    // ----------------------------------------------------------

    public static class StringComparator implements Comparator {
        public int compare(Object o1, Object o2) {
            String s1 = (String)o1;
            String s2 = (String)o2;
            return s1.compareTo(s2);
        }
    }

    public static class SignalMode {
        private final String name;
        private SignalMode(String n) { name = n; }
        public String toString() { return name; }
    }
    /**
     * All data tends to be Non-return to zero. The signal does not
     * change until the next bit in the pattern.
     */
    public static final SignalMode NONRETURNTOZERO = new SignalMode("Non-Return to Zero");
    /**
     * Return to Zero signals return to zero halfway through the period.
     * A clock signal can be generated with a pattern of all 1's, if it
     * uses return to zero.
     */
    public static final SignalMode RETURNTOZERO = new SignalMode("Return to Zero");

}
