// 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 ISINE_VA
`else
`define ISINE_VA 1

/**
 * @brief sine waveform current source.
 *
 * Generic sine waveform current source, with options to have a regular sine
 * wave, a damped sine wave, an AM modulated waveform, an FM modulated
 * waveform, or even any combination thereof.
 *
 * @param dc		DC current level in [A].
 * @param mag		AC small-signal source magnitude in [A].
 * @param phase		AC small-signal source phase in [rad].
 * @param offset	sine waveform DC offset in [A].
 * @param ampl		sine waveform amplitude in [A].
 * @param freq		sine waveform frequency in [Hz].
 * @param td		start time of the first sine pulse in [s].
 * @param damp		sine waveform damping factor in [1/s], should be
 *			between 0 and 1.
 * @param sinephase	sine waveform phase shift in [rad].
 * @param ammodindex	index of AM modulation of the sine waveform, should
 *			be between 0 and 1, or 0 if no AM modulation.
 * @param ammodfreq	frequency of AM modulation of the sine waveform
 *			in [Hz].
 * @param ammodphase	phase of AM modulation of the sine waveform in [rad].
 * @param fmmodindex	index of FM modulation of the sine waveform, should
 *			be between 0 and 1, or 0 if no FM modulation.
 * @param fmmodfreq	frequency of FM modulation of the sine waveform
 *			in [Hz].
 */

module Isine (p, n);
inout p, n;
electrical p, n;
parameter dc = 0.0;
parameter mag = 1.0;
parameter phase = 0.0;
parameter offset = 0.0;
parameter ampl = 1.0 from (0:inf);
parameter freq = 1.0 from (0:inf);
parameter td = 0.0 from [0:inf);
parameter damp = 0.0 from [0:1);
parameter sinephase = 0.0;
parameter ammodindex = 0.0 from [0:1);
parameter ammodfreq = 1.0 from (0:inf);
parameter ammodphase = 0.0;
parameter fmmodindex = 0.0 from [0:1);
parameter fmmodfreq = 1.0 from (0:inf);

real dc_val, active;
real sin_freq, sin_ampl;

analog begin

  @(initial_step) begin

    if (td > 0)
      active = 0;
    else
      active = 1;

    sin_freq = freq;
    sin_ampl = ampl;

    if ((ammodindex > 0) && (ammodfreq > freq)) begin
      $strobe("ERROR: AM modulation signal frequency of %m is larger than carrier signal frequency.");
      $finish;
    end

    if ((fmmodindex > 0) && (fmmodfreq > freq)) begin
      $strobe("ERROR: FM modulation signal frequency of %m is larger than carrier signal frequency.");
      $finish;
    end

    // If the DC value is not given, we make it equal to the t=0 value of
    // the sine, so the DC solution is continuous with any subsequent
    // transient.
    // We assume the DC value not given if it is 0, and offset or sinephase
    // is not 0.
    //$strobe("dc = %f, offset = %f, sinephase = %f\n", dc, offset, sinephase);
    if ((dc == 0.0) && ((offset != 0.0) || (sinephase != 0.0)))
      dc_val = offset + ampl * cos(sinephase);
    else
      dc_val = dc;

  end

  I(p, n) <+ ac_stim("ac", mag, phase);
  
  if (analysis("dc","ic","static"))

    I(p, n) <+ dc_val;

  else if (analysis("tran","tr")) begin

    @(timer(td)) // This only runs if td > 0
      active = 1;

    if (fmmodindex > 0)
      sin_freq = freq * (1 - fmmodindex *
                             sin(2 * `M_PI * fmmodfreq * ($abstime - td)));
    if (ammodindex > 0)
      sin_ampl = ampl * (1 - ammodindex *
                             cos(2 * `M_PI * ammodfreq * ($abstime - td) +
                                 ammodphase));
    if (damp > 0)
      sin_ampl = sin_ampl * (1 - damp * ($abstime - td));

    I(p, n) <+ offset + active * sin_ampl *
                        cos(2 * `M_PI * sin_freq * ($abstime - td) +
                            sinephase);

  end

end

endmodule // Isine

`endif
