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 tbAndNReg32Function
  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();
    tbAndNReg32Function tb = new tbAndNReg32Function( hws );

    if ( netlist ) {
      tm = tb.getDefaultTechMapper();
      ((byucc.jhdl.Xilinx.TechMapper)tm).setInsertPads( true );
      tm.netlist( tb.design, "andnff.edn" );
    } else if ( interactive ) {
      CLIJL jabLite = new CLIJL( tb );
      new CustomCosineView(jabLite.getInterp(),
			   hws);
    } else {
      tb.execute();
    }

  }

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

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

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

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

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

    /** This is where we do all of the Stimulator stuff.  This example
     * shows how we can programmatically add a custom function
     * generator to wires.  This can also be done using the put
     * command on the command line of the JHDL window. */
    Stimulator stimulator;
    stimulator = new Stimulator( this );
    stimulator.addWire( wireA, "Cosine -- 0 4" );
    stimulator.addWire( wireB, "Cosine -- 0 8" );
    stimulator.addWire( wireC, "Cosine -- 0 16" );
  }

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

  public void execute() {

    final int iterations = 34;
    boolean failed = false;
    boolean failedAtAll = 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 ) {
	  failedAtAll = true;
	  System.err.println("AndNRegNBit failed");
	  if ( !interactive )
	    System.exit( 1 );
	}
      }
    }
    if ( !failedAtAll )
      System.out.println("AndNRegNBit 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);
  }

}

