#!/usr/bin/env perl

## $Id: wave2hsp.pl,v 1.18 2006/10/30 17:18:48 lchhabra Exp $
## wave2hsp.pl - version 0.1
## Copyright 2001-2007, Lalit Chhabra (LC1178(at)yahoo.com)
## Usage: wave2hsp.pl <cmd file>
## Type perldoc wave2hsp.pl for more info.
## ------------------------------------------------------------------

@commLine=split(/\//, $0);
$commFile=$commLine[$#commLine];
die "Usage: $commFile [-v] <cmd file>\n".
    "Type perldoc $commFile for documentation.\n"
        if ($#ARGV < 0);

## Modules
use Convert::SciEng;
use Getopt::Long;

my($verilog) = 0;
GetOptions("v" => \$verilog);

## Global variables
my(@keyWords) = qw(INPUTS PARAMS STIMULUS MEASURE INIT CAPS);
my(%file, $unitTime);
my($subStart, $subEnd) = ("[", "]");
###################### BEGIN PROGRAM #######################
&parseFile($ARGV[0]);

for(@{$file{"HEADERS"}}) {print;}

for(@keyWords) {
  if($verilog) { s/STIMULUS/VSTIM/; }
  eval "process$_ ($_);";
  die $@ if $@;
}
for(@{$file{"FOOTERS"}}) {print;}

if(!$verilog) { print ".end\n";      }
else          { print "endmodule\n"; }
###################### END   PROGRAM #######################
###################### READ INPUT FILE ###########################
sub parseFile {
  my($cmdFile) = @_;
  open(CMDFILE, $cmdFile) || die "Cant open file $cmdFile: $!\n";
  my($line);
  while($line = <CMDFILE>) {
    if($line =~ /^\s*#/) {next};
    if($line =~ /^\s*$/) {next};
    $line =~ s/#.*$//g;
    for(@keyWords) {
      if($line =~ /^\s*\$$_/) {
	&parseBlock($_, \*CMDFILE);
      }
    }
    if($line =~ /^\s*\$HEADERS\s+(\S+)/) {
      open(HEADERFILE, $1) || die "Cant open header file $1: $!\n";
      push(@{$file{"HEADERS"}}, <HEADERFILE>);
      close(HEADERFILE);
    }
    elsif($line =~ /^\s*\$FOOTERS\s+(\S+)/) {
      open(FOOTERFILE, $1) || die "Cant open footer file $1: $!\n";
      push(@{$file{"FOOTERS"}},  <FOOTERFILE>);
      close(FOOTERFILE);
    }
    elsif($line =~ /^\s*\$CELL\s+(\S+)/) {
      my($netlist) = "$1.hsp";
      push(@{$file{"HEADERS"}}, "** Include the netlist\n");
      push(@{$file{"HEADERS"}}, ".include \"$netlist\"\n");
      push(@{$file{"HEADERS"}}, makeInstance($1, $netlist));
    }
  }
  close(CMDFILE);
}

sub parseBlock {
  my($block) = shift;
  my($filePtr) = shift;
  my($myArray) = [];
  $myArray =  $file{$block} if($file{$block});
  while($aa = <$filePtr>) {
    if($aa =~ /^\s*#/) {next};
    if($aa =~ /^\s*$/) {next};
    $aa =~ s/#.*$//g;
    $aa =~ s/^\s*//g;  ## Remove leading blanks
    if($aa !~ m/^\s*\$END_${block}/) {
      push(@$myArray, $aa)
    }
    else {
      $file{$block} = $myArray;
      return;
    }
  }
}

####################### Some utilities #####################
sub convertToNS {
  my($value) = @_;
  # Remove trailing 's' from $value
  $value =~ s/^(.*)[sS]\s*$/$1/;
  my $c = Convert::SciEng->new('spice');
  ## Convert into nano secs
  return($c->unfix($value)*1.0e9);
}

## Make an instance of the cell name from $CELL by looking for it in
## the netlist file.
sub makeInstance {
  my($cell, $netlist) = @_;
  my($instString) = "X$cell ";          ### The return value.
  open(NETLIST, "$netlist") || die "Cannot open netlist file $netlist: $!\n";

  my($inSubCkt) = 0;
  while(<NETLIST>) {
    if(/^\s*.subckt\s+$cell\s+(.*)/i) {
      $instString .= $1."\n";
      $inSubCkt = 1;
    } elsif ($inSubCkt && /^\s*\+/) {
      $instString .=$_;
    } elsif ($inSubCkt && /^\s*[^+]/) {
      $instString .= "+ $cell\n\n\n";
      return($instString);
    }
  }
  close(NETLIST);
}


####################### End of  utilities ##################

###################### PROCESS VARIOUS BLOCKS ##############
sub processINPUTS {
  my($blockName) = shift;
  if($verilog) {
    my(@arrayRef) = @{$file{$blockName}};
    for(@arrayRef) {
      chomp; s/\[.*//g;
      my($start, $end) = isBus($_);
      if((($start, $end) = isBus($_))) {
        print "  reg [$start:$end] $_;\n";
      } else {
        print "  reg $_;\n";
      }
    }
    print "\n\n";
  }
}

sub processCAPS {
  my($blockName) = shift;
  my(@arrayRef) = @{$file{$blockName}};
  my($capFunction) = "integ";
  my($calcMeasure);
  for(@arrayRef) {
    if(/function/i) {
      (undef, $capFunction) = split;
      next;
    }
    my($signal, $timePoints) = split;
    $signal =~ s/\s//g;
    $signal =~ s/\[.*//g;
    # Make uppercase so it becomes case insensitive.
    $timePoints = uc($timePoints);
    my($startIndex) = index($timePoints, "F");
    my($endIndex)   = index($timePoints, "T");
    my($from) = ($startIndex == -1)? "" : "from='$startIndex*unittime'";
    my($to) = ($endIndex == -1)? "" : "to='$endIndex*unittime'";
    my($busStart, $busEnd) = isBus($signal);
    my($busStart, $busEnd) = sort {$a <=> $b} ($busStart, $busEnd);
    my($subs); ### The bus subscript
    if(!($busStart, $busEnd)) { # Its not a bus
      $busStart = $busEnd = 0;
    }
    foreach $i ($busStart..$busEnd) {
      if($busStart == 0 && $busEnd == 0) {$subs = "";}
      else {$subs = $i}
      my($measure) = ".measure tran val_${signal}${subs} $capFunction".
	" i(V${signal}${subs}) $from $to\n".
	  ".measure cap_${signal}${subs} param=";
      if($capFunction eq "avg") {
	if($startIndex == -1 || $endIndex == -1) {
	  die "Need to give start and end points for averaging".
	    " in cap measurements\n";
	}
	else {  ### We have good start and end indices
	  my($avgTime) = $endIndex - $startIndex;
	  $calcMeasure = "'val_${signal}${subs}/high*$avgTime*unittime'\n";
	}
      } elsif($capFunction eq "integ") { ### capFunction is integ
        $calcMeasure = "'val_${signal}${subs}/high'\n";
      } else { ## Signal an error.
        die "ERROR: Cap function $capFunction not supported.\n";
      }
      print $measure.$calcMeasure."\n" if (!$verilog);
    }
  }
}


sub processPARAMS {
  my($blockName) = shift;
  my($arrayRef) = $file{$blockName};
  ## Some error checking
  my(@params);
  for(@$arrayRef) {
    my($param, $value) = split;
    push(@params, $param);
  SWITCH: {
      $unitTime=convertToNS($value), if($param eq "unittime");
      print ".param $param=$value\n" if (!$verilog);
    }
  }
  my(@reqdParams) = qw/unittime measvalue rftime high low ttlow tthigh/;
  for(@reqdParams) {
    my($check) = $_;
    if(! grep(/^$check$/, @params)) {
      warn "WARNING: Param $_ needs to be defined in the PARAMS block.\n";
    }
  }
  ## Unittime should be an integer for verilog waves.
  die "ERROR: Param unittime needs to be <integer> ns ".
      "instead of ${unitTime}ns.\n\n\n"
      if((int($unitTime) != $unitTime) && $verilog);
}

sub processSTIMULUS {
  my($blockName) = shift;
  my($arrayRef) = $file{$blockName};
  my(@inputArray) = @{$file{"INPUTS"}};

  ## Return if there is no stimulus block.
  if(!scalar(@$arrayRef)) { return; }

  for(@$arrayRef) {
    my($signal, $value) = split;
    if((($start, $end) = isBus($signal))) {
      parseBus($start, $end, $signal, $value);
    }
    else {
      ## Fixed a bug for powermill. Powermill wants
      ## supplies to be supplied with DC voltage sources
      ## and not PWLs, otherwise it confuses the dc
      ## initialization.
      if($value =~ m/^\-+$/ || $value =~ m/^\_+$/) {
        my($stim) = ($value =~ /\-/)?"high":"low";
        ## Put out DC waves
        print "V$signal $signal 0 dc $stim\n";
      } else {
        my($wave) = parseWaves($value);
        print "V$signal $signal 0 pwl($wave)\n";
      }
    }
    ## Remove $signal from @inputArray
    foreach $i (0..$#inputArray) {
      $inputArray[$i] =~ s/\[.*$//g; # Remove bus brackets
      $inputArray[$i] =~ s/^\s*(\S+)\s*$/$1/g; # Remove leading/trailing spaces
      if($inputArray[$i] =~ /^\s*$signal\s*$/) {
	splice(@inputArray, $i, 1);
	last;
      }
    }
  }
  ## Now process the rest of the inputs
  for(@inputArray) {
    if((($start, $end) = isBus($_))) {
      parseBus($start, $end, $_, "[0]");
    }
    else {
      ## Put a DC low value for unspecified single bit
      ## inputs.
      #my($wave) = parseWaves("_");
      print "V$_ $_ 0 dc low\n";
    }
  }
}

## Process verilog stimulus
sub processVSTIM {
  my($blockName)  = shift;
  my($arrayRef)   = $file{"STIMULUS"};

  ## Return if there is no stimulus block.
  if(!scalar(@$arrayRef)) { return; }

  my(@inputArray) = @{$file{"INPUTS"}};
  for(@$arrayRef) {
    my($signal, $value) = split;
    if((($start, $end) = isBus($signal))) {
      parseVerilogBus($start, $end, $signal, $value);
    }
    else {
      ## Fixed a bug for powermill. Powermill wants supplies to be
      ## supplied with DC voltage sources and not PWLs, otherwise it
      ## confuses the dc initialization.
      if($value =~ m/^\-+$/ || $value =~ m/^\_+$/) {
        my($stim) = ($value =~ /\-/)?"1'b1":"1'b0";
        ## Put out DC waves
        print "  initial begin $signal = $stim ; end\n";
      } else {
        my($wave) = parseVerilogWaves($signal, $value);
        print "  initial begin $wave;\n  end\n";
      }
    }
    ## Remove $signal from @inputArray
    foreach $i (0..$#inputArray) {
      $inputArray[$i] =~ s/\[.*$//g; # Remove bus brackets
      $inputArray[$i] =~ s/^\s*(\S+)\s*$/$1/g; # Remove leading/trailing spaces
      if($inputArray[$i] =~ /^\s*$signal\s*$/) {
	splice(@inputArray, $i, 1);
	last;
      }
    }
  }
  ## Now process the rest of the inputs
  for(@inputArray) {
    if((($start, $end) = isBus($_))) {
      parseVerilogBus($start, $end, $_, "[0]");
    }
    else {
      ## Put a DC low value for unspecified single bit
      ## inputs.
      #my($wave) = parseWaves("_");
      print "  initial begin". parseVerilogWaves($_, "_").
        ";\n  end\n";
    }
  }
}

sub processMEASURE {
  my($blockName) = shift;
  my(@arrayRef) = @{$file{$blockName}};
  my($i)=0;
  while($i <= $#arrayRef) {
    my($measureName) = split(/\s/, $arrayRef[$i]);;
    my($trig, $trigValue) = split(/\s+/,$arrayRef[$i+1]);
    my($targ, $targValue) = split(/\s+/,$arrayRef[$i+2]);

    my($riseTrigIndex) = index($trigValue, "R");
    my($trigIndex) = ($riseTrigIndex == -1)?
      index($trigValue, "F"):$riseTrigIndex;
    my($riseTargIndex) = index($targValue, "R");
    my($targIndex) = ($riseTargIndex == -1)?
      index($targValue, "F"):$riseTargIndex;
    my($trigSlope) = ($riseTrigIndex == -1)? "fall":"rise";
    my($targSlope) = ($riseTargIndex == -1)? "fall":"rise";

    my($td) = $trigIndex*$unitTime;
    my($measureLine);
    if($trig ne $targ) {
      #### Measure propagation times
      $measureLine = ".measure tran $measureName ";
      $measureLine .= "trig v($trig) val='measvalue' td=${td}ns $trigSlope=1 ";
      $measureLine .= "targ v($targ) val='measvalue' $targSlope=1 ";
    }
    else {
      #### Measure rise/fall times. The measure time point in the first
      #### measure statement is used. The second one is ignored.
      my($startVal) = ($riseTrigIndex == -1)? "tthigh": "ttlow" ;
      my($endVal)   = ($riseTrigIndex == -1)? "ttlow" : "tthigh";
      $measureLine = ".measure tran $measureName ";
      $measureLine .= "trig v($trig) val='$startVal' td=${td}ns $trigSlope=1 ";
      $measureLine .= "targ v($trig) val='$endVal' $trigSlope=1";
    }
    print "$measureLine\n" if (!$verilog);
    $i +=3;
  }
}
sub processINIT {
  my($blockName) = shift;
  my(@arrayRef) = @{$file{$blockName}};
  for(@arrayRef) {
    my($signal, $value) = split;
    print ".ic v($signal)=$value\n";
  }
}

sub parseBus {
  my($start, $end, $signal, $value) = @_;
  my(@waves) = split("", $value);

  my($data, $accumulateValue);

  # This variable indicates whether the bus is
  # MSB first or LSB first.
  my($reverse) = ($start>$end)?1:0;
  ### Define and initialize the array for the bus
  my(@busArray);
  my($start, $end) = sort {$a <=> $b} ($start, $end);
  my($initTime)=0;
  foreach $i (0..$#waves) {
    my($time) = $unitTime*$i."ns";
    if($waves[$i] eq "[") {$accumulateValue = 1; next;}
    elsif($waves[$i] eq "]") {$accumulateValue = 0;}
    elsif($waves[$i] eq "X" || $waves[$i] eq "x") {
      @busArray =
	generateBus($start, $end, $reverse, $time, $data, @busArray);
      $data="";
      $initTime = "'$time + rftime'";
      next; #<-------  Hey, dont forget to read this line.
    }
    if($accumulateValue == 1) {$data = $data."$waves[$i]";}
    else {
      @busArray =
	generateBus($start, $end, $reverse, $initTime, $data, @busArray);
    }
  }
  ## Now print the waves
  foreach $i ($start..$end) {
    print "V$signal$i ${signal}${subStart}${i}${subEnd} ".
          "0 pwl ( $busArray[$i] )\n";
  }
}

sub generateBus {
  my($start, $end, $reverse, $initTime, $data, @busArray)= @_;
  my($numBits) = $end - $start + 1;
  my($numHex)  = int($numBits/4) + 1;
  my(%logicValues) = ( "0" => "low", "1" => "high");
  if($data eq "" ) { return(@busArray); }
  $data = join ("", reverse(split("", $data)));
  # The following bit data is reversed.
  my(@binData) = split("", unpack("b$numBits", pack("h$numHex", $data)));
  if($reverse != 1) {
    # Reverse it again to get the correct order.
    @binData = reverse(@binData);
  }
  foreach $i ($start..$end) {
    $busArray[$i] = $busArray[$i].$initTime.
      " $logicValues{$binData[$i-$start]}\n+   ";
  }
  return(@busArray);
}


sub isBus {
  my($signal) = shift;
  my(@inputArray) = @{$file{"INPUTS"}};
  for(@inputArray) {
    if(/^\s*$signal\s*\[(\d+):(\d+)\]/) {
      return($1, $2);
    }
  }
  return;
}

sub parseWaves {
  my($arg) = shift;
  my(@waves) = split("", $arg);
  my($parsed);
  foreach $i (0..$#waves) {
    my($time) = $unitTime*$i;
  SWITCH: {
      $parsed=$parsed.$time."ns high \n+    ", 
	last SWITCH if($waves[$i] eq "-");
      $parsed=$parsed.$time."ns low \n+    ",
	last SWITCH if($waves[$i] eq "_");
      $parsed=$parsed.$time."ns low '$time"."ns + rftime'"." high \n+    ",
	last SWITCH if($waves[$i] eq "/");
      $parsed=$parsed.$time."ns high '$time"."ns + rftime'"." low \n+    ",
	last SWITCH if($waves[$i] eq "\\");
    }
  }
  return($parsed);
}

## Create verilog stimuli.
sub parseVerilogWaves {
  my($signal, $value) = @_;
  my(@waves) = split("", $value);
  my($parsed);
  foreach $i (0..$#waves) {
    my($time) = $unitTime;
  SWITCH: {
      $parsed=$parsed." $signal = 1'b1;\n    \# $unitTime",
        last SWITCH if($waves[$i] eq "-");
      $parsed=$parsed." $signal = 1'b0;\n    \# $unitTime",
        last SWITCH if($waves[$i] eq "_");
      $parsed=$parsed." $signal = 1'b1;\n    \# $unitTime",
        last SWITCH if($waves[$i] eq "/");
      $parsed=$parsed." $signal = 1'b0;\n    \# $unitTime",
        last SWITCH if($waves[$i] eq "\\");
    }
  }
  return($parsed);
}

sub parseVerilogBus {
  my($start, $end, $signal, $value) = @_;
  my($busStim);
  my(@waves) = split(/x/i, $value);
  for(0..$#waves) {
    my($val)=$waves[$_];
    $val =~ s/[\[\]]//g;
    # $val = ($wavesBlock{alias}{$val}) || $val;
    my($busLen) = $start - $end + 1;
    $busStim  .= "$signal = ${busLen}'h${val};\n";
    my($splitter) = $_? 1 : 0; ## Ignore the 'x' for the first seq.
    ## This is the length of the seq.
    $time = $unitTime*(length($waves[$_])+$splitter);
    $busStim .= "    # $time ";
  }
  print "  initial begin $busStim;\n  end\n";
}

__END__
########################## DOCUMENTATION ####################

=head1 NAME

wave2hsp.pl - An ascii waveform to spice/verilog stimuli converter for digital circuits.

=head1 SYNOPSIS

wave2hsp.pl  [-v] I<command_file>

=head1 DESCRIPTION

This program takes an ascii file consisting of various stimuli and
converts them into the spice/verilog format. It can also generate a bunch
of measure statements for propagation time and cap measurements. The
format of the ascii file is described below.

The -v option causes it to generate stimuli in verilog.

=head1 THE COMMAND FILE

The ascii command file consists of two types of keywords. B<Inline>
and B<Block>. All keywords begin with the B<$> sign. Comments can be
specified in the usual shell like fashion with the B<#> character.

B<Inline keywords>: The purpose of these keywords is to provide the
ability to generate a complete spice file which can be directly fed
into the simulator. Currently there are two keywords of this type.

=over 4

=item I<$HEADERS> I<file_name>

This keyword can be used to include a spice header file. The contents
of this file will be printed I<verbatim> to the STDOUT before the
command file is processed. This file can be used to include the
circuit being simulated, and set up the various conditions like
temperature and simulation options. Please see the EXAMPLE section
below for a possible use.

=item I<$FOOTERS> I<file_name>

Similar to the I<$HEADER> keyword, the contents of the file name are
printed I<verbatim> to the STDOUT after the command file is
processed. This file could be used to specify the types of analyses to
be carried out, cap loads on the output pins, any weird .measure
statements or .data blocks needed by your analysis. Please see the
EXAMPLE section below for a possible use.

=back

It is allowed to have multiple instances of the keywords mentioned
above and in such a case, the files are concatenated.

B<Block keywords>: As is evident, this set of keywords define various
blocks in the command file. Each I<KEYWORD> block begins with
I<$KEYWORD> and ends with I<$END_KEYWORD>. It is allowed to have
multiple blocks of any single I<KEYWORD>. The keywords in this
category are:

=over 4

=item I<$INPUTS>

This block defines the inputs of the circuit, one on each line of the
command file. Busses are specified by adding '[s:e]' to the
inputs. For example a 20 bit bus named memread can specified as:
memread[19:0]. The range can be specified in either decreasing or
increasing order and need not start (or end) with a 0.

=item I<$PARAMS>

Each line in this block is of the form
       <paramname>   <value>
 and gets translated into the form.
       .param <paramname>=<value>

However there are a few parameters used internally by the program and
these parameters carry special meaning.

     unittime:     Represents the time value given to each symbol 
                   specified in the ascii waveform representation.
     rftime:       The rise/fall time.
     high:         High voltage.
     low:          Low voltage.
     measvalue:    Value which triggers the measure statement.
                   Typically this is 'high/2'.
     ttlow,tthigh: Voltage values for measuring transition times.
                   For 10%-90%, ttlow is 'high*0.1' and tthigh is
                   'high*0.9'

=item I<$STIMULUS>

Each line in this block is of the form
       <signal name>   <value>

<signal name> is the name of an input taken from the $INPUTS block
with the bus specification stripped off. Value is an ascii
representation of the waveform specified either by using the symbols
'_', '/', '\' and '-' for single bits, or '[', ']' and 'X' for busses.

All stimuli specifications get converted into piecewise linear
waveforms in spice.  Please see the B<EXAMPLE> section at the end of
this document for more information.

=item I<$MEASURE>

Each measure block must consist of exactly three lines. The first line
should specify the measure name. The second line specifies the name, time
and direction of the trigger signal while the third specifies that for 
the target statement. 

The format of the second and third statements is
       <signal name>   <value>

Dashes ('-') in <value> are used to align times with the corresponding
stimuli statements.  An 'R' specifies that the measure should look for
the signal rising, while an 'F' specifies a falling event. The times
are measured from 'measvalue' of the trigger to the 'measvalue' of the
target.

=item I<$CAPS>

It is possible to automatically generate measure statements for
measuring input caps by specifying a I<$CAPS> block. Input caps are
measured in spice by integrating the current through an input node
caused by a voltage change, divided by the voltage change. The syntax
of statements in this block is

  <Input pin or bus>       -------F---------------T----

Here 'F' is the 'from time' and 'T' is the 'to time' for the
integration.

B<Note:> Some simulators dont support the 'integ' function in the
.measure statements. As a workaround, you can specify a

function avg

statement as the first line inside this block and this will use the
'avg' function to calculate the integral.

=item I<$INIT>

Finally, it is possible to specify initial conditions for nodes in the
circuit by placing the <node name> and <value> for each node in this
block. The syntax for this block is:

  <Node name in the spice file>       <value>

This gets directly translated into spice statements of the form:

.ic <Node name in the spice file>     <value>

=back

=head1 EXAMPLE


 ------------------- EXAMPLE OF A HEADER FILE -------------------
 * My cool spice circuit.
 * Some options below:
 .options nomod acct list accurate autostop
 
 * Whats the weather like?
 .temp 120
 
 * Globally incorrect...
 .global vdd gnd
 
 .include "/my/home/dir/spice_subckt_file.sp"
 --------------------- END  OF THE HEADER FILE ------------------


 ------------------- EXAMPLE OF A FOOTER FILE -------------------
 * Its a huge load for my back...
 Cout1 out<0> 0 cload
 Cout2 out<1> 0 cload
 Cout3 out<2> 0 cload
 
 * And he slew the outputs....
 .measure tran tt_out1_f trig v(out1) val='high*0.8' td=8ns
 + fall=1 targ v(out1) val='high*0.2' fall=1
 
 .measure tran tt_out2_f trig v(out2) val='high*0.8' td=8ns
 + fall=1 targ v(out2) val='high*0.2' fall=1
 
 * Analysis and data points
 * The simtime parameter comes from the $PARAMS block.
 .tran 1n simtime sweep data=dataModel
 
 .data dataModel
 rftime cload
 150p    10f
 300p    10f
 150p    30f
 300p    30f
 150p    60f
 300p    60f
 .enddata
 --------------------- END  OF THE FOOTER FILE ------------------

 ------------------- EXAMPLE OF A COMMAND FILE ------------------

 ## This command file is an example for a memory circuit.

 $HEADERS headers.txt        ## These files correspond to the header
 $FOOTERS footers.txt        ## and footer files defined above.

 $INPUTS
   address[4:0]              ## Bus ranges can be given in any order.
   data_in[3:0]
   read_control
   write_control
   cp
   vdd
   gnd
 $END_INPUTS

 $PARAMS
   ###### SYSTEM PARAMETERS: THEY NEED TO BE DEFINED  #######
   rftime      0.250n     ## The rise/fall time
   unittime    0.5n
   high        1.08
   low         0.00
   measvalue  'high/2'    ## For propagation measures
   ttlow      'high*0.1'  ## For transition time measures
   tthigh     'high*0.9'  ## For transition time measures

   ###### USER DEFINED PARAMETERS ##########################
   simtime     12n      ## Used in the footer file
 $END_PARAMS

 ## All bus stimuli are in hex. MSB's beyond the max bus size are
 ## ignored.
 ## All unspecified inputs get stimuli of low

 ## This is an example of a RAM write followed by 2 reads at
 ## different addresses.
 $STIMULUS
   vdd            ---------------------------------------
   gnd            _______________________________________
   read_control   ________/---\___/---\__________________
   cp             _/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-
   write_control  ____/--\_______________________________
   address        [000000000ff]X[0]
   data_in        [ff]
 $END_STIMULUS

 $CAPS
   function       avg   ## Use the avg function to calculate the integ.

   address        ---------F-------T------
   cp             ---------F---T----------
   read_control   -----F----------T-------
   write_control  --F-------T-------------
 $END_CAPS

 $MEASURE
   cp_r_hit_f     ### Measure name
   cp             ----------------R-------
   hit_out        -------------------F------
 $END_MEASURE
 $MEASURE
   cp_r_out_r
   cp             --------R-------
   out<1>         ---------R------
 $END_MEASURE

 $INIT
   XI11.vdata     high
 $END_INIT


 --------------------- END  OF THE COMMAND FILE -----------------


=head1 AUTHOR

Please contact Lalit Chhabra E<lt>LC1178(at)yahoo.com<gt> for any bug
fixes, typos, comments, suggestions, questions, encouragements...

=head1 LICENCE

Copyright (c) 2001 Lalit Chhabra. All rights reserved.  This program
is free software; you can redistribute it and/or modify it under the
same terms as Perl itself.  IN NO EVENT SHALL THE AUTHOR OR
DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE
AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND
THE AUTHOR AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE,
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=head1 BUGS/NOTES/TODO

There is no syntax check done on the command file. Therefore users
have to be very careful and inspect the resulting spice file and look
for inconsistencies.

=head1 SEE ALSO

The B<bitgen> spice waveform generator at http://www.opencollector.org

=cut

