// This is -*- Verilog-A -*-

// ============================================================================
//
// (c) Copyright 2005, All Rights Reserved, Philips Electronics N.V.
//
//
// Version: August 22, 2005
//
// ============================================================================

// Spice primitives
// Verilog-AMS LRM 2.0 Annex E "SPICE compatibility"

`include "disciplines.vams"
`include "constants.vams"

`ifdef VEXP_VA
`else
`define VEXP_VA 1

/**
 * @brief exponential waveform voltage source.
 *
 * A one-off pulse waveform in which the rising and falling edges have an
 * exponential decay whose start times and time constants are adjustable for
 * each edge individually.
 *
 * @param dc		DC voltage level in [V].
 * @param mag		AC small-signal source magnitude in [V].
 * @param phase		AC small-signal source phase in [rad].
 * @param val0		exponential waveform initial value in [V].
 * @param val1		exponential waveform second value in [V].
 * @param td0		rise start time for exponential wave in [s].
 * @param tau0		rise time constant for exponential wave in [s].
 * @param td1		fall start time for exponential wave in [s].
 * @param tau1		fall time constant for exponential wave in [s].
 */

module Vexp (p, n);
inout p, n;
electrical p, n;
parameter dc = 0.0;
parameter mag = 1.0;
parameter phase = 0.0;
parameter val0 = 0.0;
parameter val1 = 0.0;
parameter td0 = 0.0 from [0:inf);
parameter tau0 = 0.0 from [0:inf);
parameter td1 = 0.0 from [0:inf);
parameter tau1 = 0.0 from [0:inf);

real t;
real dc_val, rise_val, fall_val;
real exp_rise, exp_fall;

analog begin

  @(initial_step) begin

    if (td1 < td0) begin
      $strobe("ERROR: fall start time of %m is earlier than rise start time.");
      $finish;
    end

    // If the DC value is not given, we make it equal to the val0 value,
    // so the DC solution is continuous with any subsequent transient.
    // We assume the DC value not given if it is 0, and val0 is not 0.
    if ((dc == 0.0) && (val0 != 0.0))
      dc_val = val0;
    else
      dc_val = dc;

  end

  V(p, n) <+ ac_stim("ac", mag, phase);
  
  if (analysis("dc","ic","static")) begin

    V(p, n) <+ dc_val;

  end
  else if (analysis("tran")) begin

    @(timer(td0))
      $discontinuity;

    @(timer(td1))
      $discontinuity;

    t = $abstime;
    if (t < td0) begin
      V(p, n) <+ dc_val;
    end
    else if ((t > td0) && (t < td1)) begin
      rise_val = dc_val + (val1 - dc_val) * (1 - exp((td0 - t)/tau0));
      V(p, n) <+ rise_val;
    end
    else if (t > td1) begin
      fall_val = rise_val + (val0 - rise_val) * (1 - exp((td1 - t)/tau1));
      V(p, n) <+ fall_val;
    end

  end

end

endmodule // Vexp

`endif
