/*
   Copyright (C) 1999 T. Scott Dattalo

This file is part of gpsim.

gpsim 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 2, or (at your option)
any later version.

gpsim 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 gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

#include "command.h"
#include "cmd_stimulus.h"
#include "../src/pic-processor.h"
#include "../src/stimulus_orb.h"
#include "../src/stimuli.h"


static ValueStimulus *last_stimulus=0;
cmd_stimulus c_stimulus;

#define ASYNCHRONOUS_STIMULUS    1
#define SYNCHRONOUS_STIMULUS     2

#define  STIM_NOTHING          0
#define  STIM_PERIOD          (1 << 0)
#define  STIM_PHASE           (1 << 1)
#define  STIM_HIGH_TIME       (1 << 2)
#define  STIM_INITIAL_STATE   (1 << 3)
#define  STIM_START_CYCLE     (1 << 4)
#define  STIM_DATA            (1 << 5)

#define  STIM_ASY             (1 << 7)
#define  STIM_SQW             (1 << 8)
#define  STIM_NAME            (1 << 9)
#define  STIM_TRI             (1 << 10)
#define  STIM_ATTRIBUTE       (1 << 11)
#define  STIM_ANALOG          (1 << 12)
#define  STIM_DIGITAL         (1 << 13)
#define  STIM_DUMP            (1 << 14)

const unsigned int
SQW_OPTIONS = STIM_SQW | STIM_PERIOD | STIM_PHASE | STIM_HIGH_TIME | STIM_START_CYCLE;
const unsigned int
ASY_OPTIONS = STIM_ASY | STIM_PERIOD | STIM_PHASE | STIM_HIGH_TIME | STIM_START_CYCLE | STIM_DATA;
const unsigned int
TRI_OPTIONS = STIM_TRI | STIM_PERIOD | STIM_PHASE | STIM_HIGH_TIME | STIM_START_CYCLE;
const unsigned int
ATTR_OPTIONS = STIM_ATTRIBUTE | STIM_PERIOD | STIM_PHASE | STIM_HIGH_TIME | STIM_START_CYCLE | STIM_DATA;

static cmd_options cmd_stimulus_options[] =
{
  {"asy",                   STIM_ASY,           OPT_TT_SUBTYPE},
  {"asynchronous_stimulus", STIM_ASY,           OPT_TT_SUBTYPE},
  {"attr",                  STIM_ATTRIBUTE,     OPT_TT_SUBTYPE},
  {"attribute_stimulus",    STIM_ATTRIBUTE,     OPT_TT_SUBTYPE},
  {"period",                STIM_PERIOD,        OPT_TT_NUMERIC},
  {"phase",                 STIM_PHASE,         OPT_TT_NUMERIC},
  {"high_time",             STIM_HIGH_TIME,     OPT_TT_NUMERIC},
  {"initial_state",         STIM_INITIAL_STATE, OPT_TT_NUMERIC},
  {"start_cycle",           STIM_START_CYCLE,   OPT_TT_NUMERIC},
  {"start",                 STIM_START_CYCLE,   OPT_TT_NUMERIC},
  {"name",                  STIM_NAME,          OPT_TT_STRING},
  {"digital",               STIM_DIGITAL,       OPT_TT_BITFLAG},
  {"analog",                STIM_ANALOG,        OPT_TT_BITFLAG},
  {"d",                     STIM_DUMP,          OPT_TT_BITFLAG},
  {"dump",                  STIM_DUMP,          OPT_TT_BITFLAG},
  {"sqw",                   STIM_SQW,           OPT_TT_SUBTYPE},
  {"square_wave",           STIM_SQW,           OPT_TT_SUBTYPE},
  {"tri",                   STIM_TRI,           OPT_TT_SUBTYPE},
  {"triangle_wave",         STIM_TRI,           OPT_TT_SUBTYPE},

 { 0,0,0}
};


cmd_stimulus::cmd_stimulus(void)
{ 
  name = "stimulus";
  abbreviation = "stim";

  brief_doc = string("Create a stimulus");

  long_doc = string ("\nstimulus [[type] options]\n"
		     "\tstimulus will create a signal that can be tied to a node or an\n"
		     "\attribute. Note that in most cases it is easier to create a\n"
		     "\tstimulus file then to type this by hand.\n"
		     "\n"
		     "\t  Supported stimuli:\n"
		     "\n"
		     //"\tsquare_wave | sqw  [period p] [high_time h] [phase ph] [initial_state i]\n"
		     //"\t  port port_name bit_pos end\n"
		     //"\t\t  creates a square wave with a period of \"p\" cpu cycles.\n"
		     //"\t\t  If the high time is specified then that's the number of cycles\n"
		     //"\t\t  the square wave will be high.\n"
		     //"\t\t  The phase is with respect to the cpu's cycle counter.\n"
		     //"\t\t  The \"port_name\" and \"bit_pos\" describe where the stimulus\n"
		     //"\t\t  will be attached.\n"
		     "\tasynchronous_stimulus | asy  [period p] [phase ph]  [initial_state i]\n"
		     "\t  { c0,e0 [,c1, e1, c2, e2, ... ,cn,en] } [name stim_name] end\n"
		     "\t\t  creates an asynchronous square wave with a period of \"p\" cpu\n"
		     "\t\t  cycles.  The phase is with respect to the cpu's cycle counter.\n"
		     "\t\t  The data is specified as a pair of expressions. The first expression\n"
		     "\t\t  is for the cycle time and the second is the data. "
		     "\n"
		     "\texamples:\n"
		     "\n"
		     //"\t  stimulus sqw period 200 high_time 20 phase 60 port portb 0 end\n"
		     //"\t  create a square wave stimulus that repeats every 200 cpu cycles,\n"
		     //"\t  is high for 20 cpu cycles (and low for 200-20=180 cycles). The\n"
		     //"\t  first rising edge will occur at cycle\n"
		     //"\t  60, the second at 260, . . . Bit 0 of portb will receive the stimulus.\n"

		     "\t  # define a stimulus to generate two pulses every 1000 cycles\n"
		     "\t  \n"
		     "\t  stimulus asynchronous_stimulus \n"
		     "\t  \n"
		     "\t  # The initial state AND the state the stimulus is when\n"
		     "\t  # it rolls over\n"
		     "\t  \n"
		     "\t  initial_state 0\n"
		     "\t  start_cycle 0\n"
		     "\t  \n"
		     "\t  # the asynchronous stimulus will roll over in 'period'\n"
		     "\t  # cycles. Delete this line if you don't want a roll over.\n"
		     "\t  \n"
		     "\t  period 1000\n"
		     "\t  \n"
		     "\t  { 100, 1,\n"
		     "\t  200, 0,\n"
		     "\t  300, 1,\n"
		     "\t  400, 0\n"
		     "\t  }\n"
		     "\t  \n"
		     "\t  # Give the stimulus a name:\n"
		     "\t  \n"
		     "\t  name two_pulse_repeat\n"
		     "\t  \n"
		     "\t  end\n"
		     "\n");

  op = cmd_stimulus_options;

  options_entered = 0;

}

void cmd_stimulus::stimulus(void)
{
  dump_stimulus_list();
}

//------------------------------------------------------------------
// stimulus(int bit_flag)
//
// For the bit_flags of SQW, ASY, TRI:
//   A new stimulus is dynamically created and a pointer to it is
// is assigned to 'last_stimulus'. The last_stimulus also acts like
// a flag. If it is non-null then a stimulus is in the process of
// being created. When the stimulus 'end' option is specified at the
// cli, then 'last_stimulus' is set to NULL. Note the memory for 
// used by the last stimulus is created here, but destroyed by the
// stimulus code in ../src/stimuli.cc .
//
void cmd_stimulus::stimulus(int bit_flag)
{

  switch(bit_flag)
    {
    case STIM_SQW:
      if(verbose)
	cout << "creating sqw stimulus\n";
      if(!last_stimulus) {
	//create_stimulus(NEW_SQW,stim_name);
	valid_options = SQW_OPTIONS;
	options_entered = STIM_SQW;
	//last_stimulus = new square_wave;
      } else
	cout << "warning: ignoring sqw stim creation";
      break;

    case STIM_ASY:
      if(verbose)
	cout << "creating asy stimulus\n";

      if(!last_stimulus) {
	//create_stimulus(NEW_ASY,stim_name);
	last_stimulus = new ValueStimulus;
	valid_options = ASY_OPTIONS;
	options_entered = STIM_ASY;
      }else
	cout << "warning: ignoring asy stim creation";
      break;

    case STIM_ATTRIBUTE:
      if(verbose)
	cout << "creating asy stimulus\n";

      if(!last_stimulus) {
	last_stimulus = new AttributeStimulus;
	valid_options = ATTR_OPTIONS;
	options_entered = STIM_ATTRIBUTE;
      }else
	cout << "warning: ignoring asy stim creation";
      break;

    case STIM_TRI:
      if(verbose)
	cout << "creating tri stimulus\n";

      if(!last_stimulus) {
	//create_stimulus(NEW_TRI,stim_name);
	//last_stimulus = new triangle_wave;
	valid_options = TRI_OPTIONS;
	options_entered = STIM_TRI;
      } else
	cout << "warning: ignoring tri stim creation";
      break;

    case STIM_DUMP:
      stimulus();      // Display the list of stimuli.
      return;

    case STIM_DIGITAL:
      //if(last_stimulus)
      //  last_stimulus->set_digital();
      return;

    case STIM_ANALOG:
      //if(last_stimulus)
      //  last_stimulus->set_analog();
      return;

    default:
      cout << " Invalid stimulus option\n";
      return;
    }

}


void cmd_stimulus::stimulus(cmd_options_expr *coe)
{
  /*
  double dvalue = 0.0;
  if(coe->expr)
    dvalue = evaluate(coe->expr);

  int value = (int) dvalue;
  */

  if (!coe || !coe->expr)
    return;

  Value *value = toValue(coe->expr);

  switch(coe->co->value)
    {
    case STIM_PHASE:
      if(verbose)
	cout << "stimulus command got the phase " << value << '\n';

      if(last_stimulus)
	last_stimulus->put_phase(value);

      break;

    case STIM_PERIOD:
      if(verbose)
	cout << "stimulus command got the period " << value << '\n';

      if(last_stimulus)
	last_stimulus->put_period(value);

      break;

    case STIM_HIGH_TIME:
      if(verbose)
	cout << "stimulus command got the high_time " << value << '\n';

      if(last_stimulus)
	last_stimulus->put_duty(value);

      break;

    case STIM_INITIAL_STATE:
      if(verbose)
	cout << "stimulus command got the initial_state " << value << '\n';

      if(last_stimulus)
	last_stimulus->put_initial_state(value);

      break;

    case STIM_START_CYCLE:
      if(verbose)
	cout << "stimulus command got the start_cycle " << value << '\n';

      if(last_stimulus)
	last_stimulus->put_start_cycle(value);

      break;

    default:
      cout << " Invalid stimulus option\n";
      return;
    }

  options_entered |= coe->co->value;
  delete coe->expr;
  delete value;

}


void cmd_stimulus::stimulus(cmd_options_str *cos)
{

  if(!last_stimulus) {
    cout << "warning: Ignoring stimulus (string) option because there's no stimulus defined.\n";
    return;
  }


  switch(cos->co->value)
    {
    case STIM_NAME:
      if(verbose)
	cout << "stimulus command got the name " << cos->str << '\n';

      last_stimulus->new_name(cos->str);

      break;
    }

  options_entered |= cos->co->value;
}

void cmd_stimulus::stimulus(ExprList_t *eList)
{
  ExprList_itor ei;

  bool bHaveSample=false;
  ValueStimulusData sample;
  sample.time = 0;
  sample.v = 0;

  if(last_stimulus) {
    for(ei = eList->begin(); ei != eList->end(); ++ei) {



      try {

	Value *v = (*ei)->evaluate();

	if(!bHaveSample) {
	  v->get(sample.time);
	  delete v;
	  bHaveSample = true;
	} else {
	  sample.v = v;
	  last_stimulus->put_data(sample);
	  bHaveSample = false;
	  have_data = 1;
	}

      }

      catch (Error *err) {
	if(err)
	  cout << "ERROR:" << err->toString() << endl;
	delete err;
      }

    }

  }

  eList->clear();
  delete eList;
}

//-----------------
// end()
// All of the stimulus' options have been entered. Now it's time
// to create the stimulus.

void cmd_stimulus::end(void)
{
  if(!last_stimulus) {
    cout << "warning: Ignoring stimulus (string) option because there's no stimulus defined.";
    return;
  }

  switch( options_entered & (STIM_SQW | STIM_TRI | STIM_ASY | STIM_ATTRIBUTE))
    {
    case STIM_SQW:
      if(verbose)
	cout << "created sqw stimulus\n";
      break;

    case STIM_ASY:
      if(verbose)
	cout << "created asy stimulus\n";
      last_stimulus->start();
      break;

    case STIM_ATTRIBUTE:
      if(verbose)
	cout << "created attribute stimulus\n";
      last_stimulus->start();
      break;

    case STIM_TRI:
      if(verbose)
	cout << "creating tri stimulus\n";
      break;

    }

  last_stimulus = 0;
}


syntax highlighted by Code2HTML, v. 0.9.1