/***********************************************************************

 HiSIM (Hiroshima University STARC IGFET Model)
 Copyright (C) 2014 Hiroshima University & STARC

 MODEL NAME : HiSIM
 ( VERSION : 2  SUBVERSION : 8  REVISION : 0 )
 
 FILE : hsm2noi.c

 Date : 2014.6.5

 released by 
                Hiroshima University &
                Semiconductor Technology Academic Research Center (STARC)
***********************************************************************/

/**********************************************************************

The following source code, and all copyrights, trade secrets or other
intellectual property rights in and to the source code in its entirety,
is owned by the Hiroshima University and the STARC organization.

All users need to follow the "HiSIM2 Distribution Statement and
Copyright Notice" attached to HiSIM2 model.

-----HiSIM2 Distribution Statement and Copyright Notice--------------

Software is distributed as is, completely without warranty or service
support. Hiroshima University or STARC and its employees are not liable
for the condition or performance of the software.

Hiroshima University and STARC own the copyright and grant users a perpetual,
irrevocable, worldwide, non-exclusive, royalty-free license with respect 
to the software as set forth below.   

Hiroshima University and STARC hereby disclaim all implied warranties.

Hiroshima University and STARC grant the users the right to modify, copy,
and redistribute the software and documentation, both within the user's
organization and externally, subject to the following restrictions

1. The users agree not to charge for Hiroshima University and STARC code
itself but may charge for additions, extensions, or support.

2. In any product based on the software, the users agree to acknowledge
Hiroshima University and STARC that developed the software. This
acknowledgment shall appear in the product documentation.

3. The users agree to reproduce any copyright notice which appears on
the software on any copy or modification of such made available
to others."


*************************************************************************/

#include "ngspice/ngspice.h"
#include "hsm2def.h"
#include "ngspice/cktdefs.h"
#include "ngspice/iferrmsg.h"
#include "ngspice/noisedef.h"
#include "ngspice/suffix.h"
#include "ngspice/const.h"  /* jwan */

/*
 * HSM2noise (mode, operation, firstModel, ckt, data, OnDens)
 *    This routine names and evaluates all of the noise sources
 *    associated with MOSFET's.  It starts with the model *firstModel and
 *    traverses all of its insts.  It then proceeds to any other models
 *    on the linked list.  The total output noise density generated by
 *    all of the MOSFET's is summed with the variable "OnDens".
 */

int HSM2noise (
     int mode, int operation,
     GENmodel *inModel,
     CKTcircuit *ckt,
     Ndata *data,
     double *OnDens)
{
  HSM2model *model = (HSM2model *)inModel;
  HSM2instance *here;
  double tempOnoise;
  double tempInoise;
  double noizDens[HSM2NSRCS];
  double lnNdens[HSM2NSRCS];
  int i;
  double G = 0.0 ;
  double TTEMP = 0.0 ;
 
  /* for induced gate noise calculation: */
  double omega = ckt->CKTomega;
  double sid, ci, sigrat, Qdrat;
  double realXds, imagXds, realXgs, imagXgs ;

  /* define the names of the noise sources */
  static char * HSM2nNames[HSM2NSRCS] = {
    /* Note that we have to keep the order
       consistent with the index definitions 
       in hsm2defs.h */
    ".rd",              /* noise due to rd */
    ".rs",              /* noise due to rs */
    ".id",              /* noise due to id */
    ".1ovf",            /* flicker (1/f) noise */
    ".igs",             /* shot noise due to Igs */
    ".igd",             /* shot noise due to Igd */
    ".igb",             /* shot noise due to Igb */
    ".ign",             /* induced gate noise component at the drain node */
    ""                  /* total transistor noise */
  };
  
  for ( ;model != NULL; model = model->HSM2nextModel ) {
    for ( here = model->HSM2instances; here != NULL;
	  here = here->HSM2nextInstance ) {
      switch (operation) {
      case N_OPEN:
	/* see if we have to to produce a summary report */
	/* if so, name all the noise generators */
	  
	if (((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0) {
	  switch (mode) {
	  case N_DENS:
	    for ( i = 0; i < HSM2NSRCS; i++ ) { 
	      NOISE_ADD_OUTVAR(ckt, data, "onoise.%s%s", here->HSM2name, HSM2nNames[i]);
	    }
	    break;
	  case INT_NOIZ:
	    for ( i = 0; i < HSM2NSRCS; i++ ) {
	      NOISE_ADD_OUTVAR(ckt, data, "onoise_total.%s%s", here->HSM2name, HSM2nNames[i]);
	      NOISE_ADD_OUTVAR(ckt, data, "inoise_total.%s%s", here->HSM2name, HSM2nNames[i]);
	    }
	    break;
	  }
	}
	break;
      case N_CALC:
	switch (mode) {
	case N_DENS:

	  /* temperature */
	  TTEMP = ckt->CKTtemp ;
	  if ( here->HSM2_temp_Given ) TTEMP = here->HSM2_ktemp ;
	  if ( here->HSM2_dtemp_Given ) {
	    TTEMP = TTEMP + here->HSM2_dtemp ;
	  }

         /* rs/rd thermal noise */
         if ( model->HSM2_corsrd < 0 ) {
           NevalSrc(&noizDens[HSM2RDNOIZ], NULL,
                    ckt, N_GAIN,
                    here->HSM2dNodePrime, here->HSM2dNode,
                    0.0);
           noizDens[HSM2RDNOIZ] *= 4 * CONSTboltz * TTEMP * here->HSM2drainConductance ;
	   lnNdens[HSM2RDNOIZ] = log( MAX(noizDens[HSM2RDNOIZ],N_MINLOG) ) ;

           NevalSrc(&noizDens[HSM2RSNOIZ], NULL,
                    ckt, N_GAIN,
                    here->HSM2sNodePrime, here->HSM2sNode,
                    0.0);
           noizDens[HSM2RSNOIZ] *= 4 * CONSTboltz * TTEMP * here->HSM2sourceConductance ;
	   lnNdens[HSM2RSNOIZ] = log( MAX(noizDens[HSM2RSNOIZ],N_MINLOG) ) ;

           /*
           NevalSrc(&noizDens[HSM2RDNOIZ], &lnNdens[HSM2RDNOIZ],
		     ckt, THERMNOISE,
		     here->HSM2dNodePrime, here->HSM2dNode,
		     here->HSM2_weff / model->HSM2_rsh);
	    
	    NevalSrc(&noizDens[HSM2RSNOIZ], &lnNdens[HSM2RSNOIZ], 
		     ckt, THERMNOISE,
		     here->HSM2sNodePrime, here->HSM2sNode,
		     here->HSM2_weff / model->HSM2_rsh);
	    */
	  } else {
	    noizDens[HSM2RDNOIZ] = 0e0 ;
	    lnNdens[HSM2RDNOIZ] = N_MINLOG ;
	    noizDens[HSM2RSNOIZ] = 0e0 ;
	    lnNdens[HSM2RSNOIZ] = N_MINLOG ;
	  }

	  /* channel thermal noise */
	  switch( model->HSM2_noise ) {
	  case 1:
           /* HiSIM2 model */
         if ( model->HSM2_corsrd <= 0 || here->HSM2internalGd <= 0.0 ) {
           G = here->HSM2_noithrml ;
         } else {
           if ( here->HSM2_noithrml * here->HSM2internalGd * here->HSM2internalGs > 0.0 ) {
              G = here->HSM2_noithrml * here->HSM2internalGd * here->HSM2internalGs
              / ( here->HSM2_noithrml * here->HSM2internalGd
                + here->HSM2internalGd * here->HSM2internalGs
                + here->HSM2_noithrml * here->HSM2internalGs );
           } else {
              G = 0.0;
           }
         }
           NevalSrc(&noizDens[HSM2IDNOIZ], NULL,
                    ckt, N_GAIN,
                    here->HSM2dNodePrime, here->HSM2sNodePrime,
                    0.0);
           noizDens[HSM2IDNOIZ] *= 4 * CONSTboltz * TTEMP * G ;
           lnNdens[HSM2IDNOIZ] = log( MAX(noizDens[HSM2IDNOIZ],N_MINLOG) );
           break;
         }

         /* flicker noise */
         NevalSrc(&noizDens[HSM2FLNOIZ], NULL,
                  ckt, N_GAIN,
                  here->HSM2dNodePrime, here->HSM2sNodePrime,
                  0.0);
         switch ( model->HSM2_noise ) {
         case 1:
           /* HiSIM model */
           noizDens[HSM2FLNOIZ] *= here->HSM2_noiflick / pow(data->freq, model->HSM2_falph) ;
           break;
         }
         lnNdens[HSM2FLNOIZ] = log(MAX(noizDens[HSM2FLNOIZ], N_MINLOG));

         /* shot noise */
	 NevalSrc(&noizDens[HSM2IGSNOIZ],
		  &lnNdens[HSM2IGSNOIZ], ckt, SHOTNOISE,
		  here->HSM2gNodePrime, here->HSM2sNodePrime,
		  here->HSM2_igs);
	 NevalSrc(&noizDens[HSM2IGDNOIZ],
		  &lnNdens[HSM2IGDNOIZ], ckt, SHOTNOISE,
		  here->HSM2gNodePrime, here->HSM2dNodePrime,
		  here->HSM2_igd);
	 NevalSrc(&noizDens[HSM2IGBNOIZ],
		  &lnNdens[HSM2IGBNOIZ], ckt, SHOTNOISE,
		  here->HSM2gNodePrime, here->HSM2bNodePrime,
		  here->HSM2_igb);

	  /* induced gate noise */
	  switch ( model->HSM2_noise ) {
	  case 1:
	    /* HiSIM model */
            sid = 4.0 * CONSTboltz * TTEMP * here->HSM2_noithrml ;
            ci  = here->HSM2_noicross ;
            sigrat = (sid > 0.0 && here->HSM2_noiigate > 0.0) ? sqrt(here->HSM2_noiigate/sid) : 0.0 ;
            Qdrat  = here->HSM2_Qdrat ;

            realXds = *(ckt->CKTrhs +here->HSM2dNodePrime) - *(ckt->CKTrhs +here->HSM2sNodePrime);
            imagXds = *(ckt->CKTirhs+here->HSM2dNodePrime) - *(ckt->CKTirhs+here->HSM2sNodePrime);
            realXgs = *(ckt->CKTrhs +here->HSM2gNodePrime) - *(ckt->CKTrhs +here->HSM2sNodePrime);
            imagXgs = *(ckt->CKTirhs+here->HSM2gNodePrime) - *(ckt->CKTirhs+here->HSM2sNodePrime);

            noizDens[HSM2IGNOIZ] = 2.0 * omega * ci * sigrat * sid * ( realXgs*imagXds - realXds*imagXgs ) 
                                  + omega*omega * sigrat*sigrat * sid * ( (realXgs-Qdrat*realXds) * (realXgs-Qdrat*realXds)
                                                                         +(imagXgs-Qdrat*imagXds) * (imagXgs-Qdrat*imagXds) ) ;
	    lnNdens[HSM2IGNOIZ] = log(MAX(noizDens[HSM2IGNOIZ], N_MINLOG));
	    break;
	  }

	  /* total */
	  noizDens[HSM2TOTNOIZ] = noizDens[HSM2RDNOIZ] + noizDens[HSM2RSNOIZ]
	    + noizDens[HSM2IDNOIZ] + noizDens[HSM2FLNOIZ]
	    + noizDens[HSM2IGSNOIZ] + noizDens[HSM2IGDNOIZ] + noizDens[HSM2IGBNOIZ]
	    + noizDens[HSM2IGNOIZ];
	  lnNdens[HSM2TOTNOIZ] = log(MAX(noizDens[HSM2TOTNOIZ], N_MINLOG));
/*       printf("f %e Sid %.16e Srd %.16e Srs %.16e Sflick %.16e Stherm %.16e Sign %.16e\n", */
/*              data->freq,noizDens[HSM2TOTNOIZ],noizDens[HSM2RDNOIZ],noizDens[HSM2RSNOIZ],noizDens[HSM2FLNOIZ],noizDens[HSM2IDNOIZ],noizDens[HSM2IGNOIZ]); */
	  
	  *OnDens += noizDens[HSM2TOTNOIZ];
	  
	  if ( data->delFreq == 0.0 ) {
	    /* if we haven't done any previous 
	       integration, we need to initialize our
	       "history" variables.
	    */
	    
	    for ( i = 0; i < HSM2NSRCS; i++ ) 
	      here->HSM2nVar[LNLSTDENS][i] = lnNdens[i];
	    
	    /* clear out our integration variables
	       if it's the first pass
	    */
	    if (data->freq == ((NOISEAN*) ckt->CKTcurJob)->NstartFreq) {
	      for (i = 0; i < HSM2NSRCS; i++) {
		here->HSM2nVar[OUTNOIZ][i] = 0.0;
		here->HSM2nVar[INNOIZ][i] = 0.0;
	      }
	    }
	  }
	  else {
	    /* data->delFreq != 0.0,
	       we have to integrate.
	    */
	    for ( i = 0; i < HSM2NSRCS; i++ ) {
	      if ( i != HSM2TOTNOIZ ) {
		tempOnoise = 
		  Nintegrate(noizDens[i], lnNdens[i],
			     here->HSM2nVar[LNLSTDENS][i], data);
		tempInoise = 
		  Nintegrate(noizDens[i] * data->GainSqInv, 
			     lnNdens[i] + data->lnGainInv,
			     here->HSM2nVar[LNLSTDENS][i] + data->lnGainInv,
			     data);
		here->HSM2nVar[LNLSTDENS][i] = lnNdens[i];
		data->outNoiz += tempOnoise;
		data->inNoise += tempInoise;
		if ( ((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0 ) {
		  here->HSM2nVar[OUTNOIZ][i] += tempOnoise;
		  here->HSM2nVar[OUTNOIZ][HSM2TOTNOIZ] += tempOnoise;
		  here->HSM2nVar[INNOIZ][i] += tempInoise;
		  here->HSM2nVar[INNOIZ][HSM2TOTNOIZ] += tempInoise;
		}
	      }
	    }
	  }
	  if ( data->prtSummary ) {
	    for (i = 0; i < HSM2NSRCS; i++) {
	      /* print a summary report */
	      data->outpVector[data->outNumber++] = noizDens[i];
	    }
	  }
	  break;
	case INT_NOIZ:
	  /* already calculated, just output */
	  if ( ((NOISEAN*)ckt->CKTcurJob)->NStpsSm != 0 ) {
	    for ( i = 0; i < HSM2NSRCS; i++ ) {
	      data->outpVector[data->outNumber++] = here->HSM2nVar[OUTNOIZ][i];
	      data->outpVector[data->outNumber++] = here->HSM2nVar[INNOIZ][i];
	    }
	  }
	  break;
	}
	break;
      case N_CLOSE:
	/* do nothing, the main calling routine will close */
	return (OK);
	break;   /* the plots */
      }       /* switch (operation) */
    }    /* for here */
  }    /* for model */
  
  return(OK);
}



