import java.io.FileNotFoundException;
import byucc.jhdl.apps.Stimulator.Stimulator;
import byucc.jhdl.base.Cell;
import byucc.jhdl.base.HWSystem;
import byucc.jhdl.base.Node;
import byucc.jhdl.base.ProgrammaticTestBench;
import byucc.jhdl.base.Wire;
import byucc.jhdl.Logic.Logic;
import byucc.jhdl.Logic.TechMapper;
import byucc.jhdl.Xilinx.Virtex2.Virtex2TechMapper;
import byucc.jhdl.apps.Viewers.JL.CLIJL;

/** This is an example of how you might use the interactive stimulator
 * to set up custom put schedules for port wires.
 * @author Anthony Slade*/
public class tbAndNReg3BitStimulator
  extends Logic
  implements ProgrammaticTestBench {
 
  public Cell design; 

  private static boolean verbose;
  private static boolean interactive;

  public static void main(String argv[]) {

    boolean netlist = false;
    interactive = false;

    TechMapper tm;

    for ( int i = 0; i < argv.length; ++i ) {
      if ( "--netlist".equals(argv[i]) )
	netlist = true;
      else if ( "-v".equals(argv[i]) )
	verbose = true;
      else if ( "-gui".equals(argv[i]) )
	interactive = true;
    }

    HWSystem hws = new HWSystem();
    tbAndNReg3BitStimulator tb = new tbAndNReg3BitStimulator( hws );

    if ( netlist ) {
      tm = tb.getDefaultTechMapper();
      ((byucc.jhdl.Xilinx.TechMapper)tm).setInsertPads( true );
      tm.netlist( tb.design, "andnff.edn" );
    } else if ( interactive ) {
      new CLIJL( tb );
      System.err.println("Returned from creation of JL");
    } else {
      tb.execute();
    }

  }

  private Wire wireA;
  private Wire wireB;
  private Wire wireC;
  private Wire wireO;
  
  public tbAndNReg3BitStimulator(Node parent) {
    this( parent, null );
  }

  public tbAndNReg3BitStimulator(Node parent, TechMapper tm) {
    super(parent);

    setDefaultTechMapper( (null == tm)? new Virtex2TechMapper(true) : tm);

    wireA = wire( 3, "wireA" );
    wireB = wire( 3, "wireB" );
    wireC = wire( 3, "wireC" );
    wireO = wire( 3, "wireO" );

    design = new AndNReg3Bit(this,
			     wireA,
			     wireB,
			     wireC,
			     wireO);

    /** This is where we do all of the Stimulator stuff.  This is only
     * one approach (not necessarily the most efficient). */
    Stimulator stimulator, stimulator2;
    stimulator = new Stimulator( this );
    Wire[] stim2Wires = { wireB };
    try { // this constructor will throw a FileNotFoundException if sched.props is not found
      stimulator2 = new Stimulator( this, stim2Wires, "sched.props" );
    } catch ( FileNotFoundException fnfe ) {
      throw new RuntimeException(fnfe.getMessage());
    }
    stimulator.addWire( wireA, "0 1 0 1 3 7" );
    stimulator2.addWire( wireC, "0 0 1 1 4 6 0" );
  }

  private int inValA;
  private int inValB;
  private int inValC;
  private int outVal;

  public void execute() {

    final int iterations = 20;
    boolean failed = false;

    getSystem().reset();

    for (int itr=0; itr<iterations; itr++) {

      getInValues();
      getSystem().cycle(1);
      getOutValues();

      int expected = inValA & inValB & inValC;
      failed = ( expected != outVal );
      if ( verbose || failed ) {
	String binaryA = Integer.toBinaryString(inValA);
	String binaryB = Integer.toBinaryString(inValB);
	String binaryC = Integer.toBinaryString(inValC);
	String binaryO = Integer.toBinaryString(outVal);
	String binaryCorrect = Integer.toBinaryString(expected);
	System.err.print("* input  = " + binaryA
			 + " & " + binaryB
			 + " & " + binaryC);
	System.err.print("; * output = " + binaryO);
	System.err.println("; * correct= " + binaryCorrect);

	if ( failed ) {
	  System.err.println("AndNReg3Bit failed");
	  if ( !interactive )
	    System.exit( 1 );
	}
      }
    }
    if ( !failed )
      System.out.println("AndNReg3Bit passed with flying colors.");
  }
  
  public void reset() {
    // Let the Stimulator do all this
    //wireA.put( this, 0 );
    //wireB.put( this, 0 );
  }
  static int cl=0;
  public void clock() {
    // Let the Stimulator do all this
    //wireA.put( this, inValA );
    //wireB.put( this, inValB );
  }

  public void getInValues() {
    inValA = wireA.get(this);
    inValB = wireB.get(this);
    inValC = wireC.get(this);
  }
  public void getOutValues() {
    outVal = wireO.get(this);
  }

}

