Merge branch 'pre-master-42' into bt_dev

This commit is contained in:
Brian Taylor 2023-10-11 14:42:29 -07:00
commit dee0500140
8 changed files with 364 additions and 628 deletions

View File

@ -0,0 +1,33 @@
A simple bipolar circuit
* Tadej Tuma, Árpád Burmen "Circuit Simulation with SPICE OPUS: Theory and Practice"
* Boston 2009
v1 1 0 dc=5
r1 1 2 r=1k
q1 2 3 0 t2n2222
r2 3 4 r=1k
vin 4 0 dc=0.68 ac 1
.model t2n2222 npn
+ is=19f bf=150 vaf=100 ikf=0.18 ise=50p
+ ne=2.5 br=7.5 var=6.4 ikr=12m isc=8.7p
+ nc=1.2 rb=50 re=0.4 rc=0.3 cje=26p tf=0.5n
+ cjc=11p tr=7n xtb=1.5 kf=0.032f af=1
.control
noise v(2) vin dec 10 0.5 100meg 1
setplot noise1
set xbrushwidth=3
plot loglog
+ onoise_q1_1overf
+ onoise_q1_ib onoise_q1_ic
+ onoise_q1_rb onoise_q1_rc onoise_q1_re
+ onoise_q1
plot loglog onoise_q1 onoise_r1 onoise_r2 onoise_spectrum
plot loglog onoise_spectrum inoise_spectrum
.endc
.end

View File

@ -17,7 +17,7 @@ R22 22 0 10k
C22 22 0 1u
.control
noise v(2) V1 dec 10 1 100k
noise v(2) V1 dec 10 1 100k 1
echo **resistive divider**
print onoise_total inoise_total
setplot noise1

View File

@ -1353,8 +1353,14 @@ inp_dodeck(
if (p == dd->error) {
if (strstr(dd->line, ".model"))
fprintf(stderr, "Warning: Model issue on line %d :\n %.*s ...\n%s\n",
dd->linenum_orig, 72, dd->line, dd->error);
if (dd->linenum_orig == 0) { /* new line, e.g. in subcircuit */
fprintf(stderr, "Warning: Model issue on line:\n %.*s ...\n%s\n",
72, dd->line, dd->error);
}
else {
fprintf(stderr, "Warning: Model issue on line %d :\n %.*s ...\n%s\n",
dd->linenum_orig, 72, dd->line, dd->error);
}
else if (dd->linenum_orig == 0) {
fprintf(stderr, "Error on line:\n %s\n%s\n",
dd->line, dd->error);

View File

@ -2306,7 +2306,7 @@ getisrcval(double time, char *iname)
delmin minimum delta CKTdelmin
redostep if 0, converged,
if 1, either no convergence, need to redo with new ckt->CKTdelta
or ckt->CKTdelta has been reduced by tuncation errors too large.
or ckt->CKTdelta has been reduced by truncation errors too large.
rejected pointer to ckt->CKTstat->STATrejected, counts rejected time points.
loc location of function call in dctran.c: 0: after breakpoint handling, 1: at end of for loop
*/

View File

@ -21,9 +21,9 @@ Author: 1987 Gary W. Ng
int
CKTnoise (CKTcircuit *ckt, int mode, int operation, Ndata *data)
CKTnoise(CKTcircuit* ckt, int mode, int operation, Ndata* data)
{
NOISEAN *job = (NOISEAN *) ckt->CKTcurJob;
NOISEAN* job = (NOISEAN*)ckt->CKTcurJob;
double outNdens;
int i;
@ -35,10 +35,10 @@ CKTnoise (CKTcircuit *ckt, int mode, int operation, Ndata *data)
/* let each device decide how many and what type of noise sources it has */
for (i=0; i < DEVmaxnum; i++) {
if ( DEVices[i] && DEVices[i]->DEVnoise && ckt->CKThead[i] ) {
error = DEVices[i]->DEVnoise (mode, operation, ckt->CKThead[i],
ckt,data, &outNdens);
for (i = 0; i < DEVmaxnum; i++) {
if (DEVices[i] && DEVices[i]->DEVnoise && ckt->CKThead[i]) {
error = DEVices[i]->DEVnoise(mode, operation, ckt->CKThead[i],
ckt, data, &outNdens);
if (error) return (error);
}
}
@ -47,102 +47,102 @@ CKTnoise (CKTcircuit *ckt, int mode, int operation, Ndata *data)
case N_OPEN:
/* take care of the noise for the circuit as a whole */
/* take care of the noise for the circuit as a whole */
switch (mode) {
switch (mode) {
case N_DENS:
case N_DENS:
data->namelist = TREALLOC(IFuid, data->namelist, data->numPlots + 1);
data->namelist = TREALLOC(IFuid, data->namelist, data->numPlots + 1);
SPfrontEnd->IFnewUid (ckt, &(data->namelist[data->numPlots++]),
NULL, "onoise_spectrum", UID_OTHER, NULL);
SPfrontEnd->IFnewUid(ckt, &(data->namelist[data->numPlots++]),
NULL, "onoise_spectrum", UID_OTHER, NULL);
data->namelist = TREALLOC(IFuid, data->namelist, data->numPlots + 1);
data->namelist = TREALLOC(IFuid, data->namelist, data->numPlots + 1);
SPfrontEnd->IFnewUid (ckt, &(data->namelist[data->numPlots++]),
NULL, "inoise_spectrum", UID_OTHER, NULL);
SPfrontEnd->IFnewUid(ckt, &(data->namelist[data->numPlots++]),
NULL, "inoise_spectrum", UID_OTHER, NULL);
/* we've added two more plots */
/* we've added two more plots */
data->outpVector =
TMALLOC(double, data->numPlots);
data->squared_value =
data->squared ? NULL : TMALLOC(char, data->numPlots);
data->outpVector =
TMALLOC(double, data->numPlots);
data->squared_value =
data->squared ? NULL : TMALLOC(char, data->numPlots);
break;
case INT_NOIZ:
case INT_NOIZ:
data->namelist = TREALLOC(IFuid, data->namelist, data->numPlots + 1);
SPfrontEnd->IFnewUid (ckt, &(data->namelist[data->numPlots++]),
NULL, "onoise_total", UID_OTHER, NULL);
data->namelist = TREALLOC(IFuid, data->namelist, data->numPlots + 1);
SPfrontEnd->IFnewUid(ckt, &(data->namelist[data->numPlots++]),
NULL, "onoise_total", UID_OTHER, NULL);
data->namelist = TREALLOC(IFuid, data->namelist, data->numPlots + 1);
SPfrontEnd->IFnewUid (ckt, &(data->namelist[data->numPlots++]),
NULL, "inoise_total", UID_OTHER, NULL);
/* we've added two more plots */
data->namelist = TREALLOC(IFuid, data->namelist, data->numPlots + 1);
SPfrontEnd->IFnewUid(ckt, &(data->namelist[data->numPlots++]),
NULL, "inoise_total", UID_OTHER, NULL);
/* we've added two more plots */
data->outpVector =
TMALLOC(double, data->numPlots);
data->squared_value =
data->squared ? NULL : TMALLOC(char, data->numPlots);
break;
data->outpVector =
TMALLOC(double, data->numPlots);
data->squared_value =
data->squared ? NULL : TMALLOC(char, data->numPlots);
break;
default:
return (E_INTERN);
return (E_INTERN);
}
break;
break;
case N_CALC:
switch (mode) {
switch (mode) {
case N_DENS:
case N_DENS:
if ((job->NStpsSm == 0)
|| data->prtSummary)
{
data->outpVector[data->outNumber++] = outNdens;
data->outpVector[data->outNumber++] =
(outNdens * data->GainSqInv);
|| data->prtSummary)
{
data->outpVector[data->outNumber++] = outNdens;
data->outpVector[data->outNumber++] =
(outNdens * data->GainSqInv);
refVal.rValue = data->freq; /* the reference is the freq */
if (!data->squared)
for (i = 0; i < data->outNumber; i++)
if (data->squared_value[i])
data->outpVector[i] = sqrt(data->outpVector[i]);
outData.v.numValue = data->outNumber; /* vector number */
outData.v.vec.rVec = data->outpVector; /* vector of outputs */
SPfrontEnd->OUTpData (data->NplotPtr, &refVal, &outData);
refVal.rValue = data->freq; /* the reference is the freq */
if (!data->squared)
for (i = 0; i < data->outNumber; i++)
if (data->squared_value[i])
data->outpVector[i] = sqrt(data->outpVector[i]);
outData.v.numValue = data->outNumber; /* vector number */
outData.v.vec.rVec = data->outpVector; /* vector of outputs */
SPfrontEnd->OUTpData(data->NplotPtr, &refVal, &outData);
}
break;
case INT_NOIZ:
data->outpVector[data->outNumber++] = data->outNoiz;
data->outpVector[data->outNumber++] = data->inNoise;
if (!data->squared)
for (i = 0; i < data->outNumber; i++)
if (data->squared_value[i])
data->outpVector[i] = sqrt(data->outpVector[i]);
outData.v.vec.rVec = data->outpVector; /* vector of outputs */
outData.v.numValue = data->outNumber; /* vector number */
SPfrontEnd->OUTpData (data->NplotPtr, &refVal, &outData);
break;
case INT_NOIZ:
data->outpVector[data->outNumber++] = data->outNoiz;
data->outpVector[data->outNumber++] = data->inNoise;
if (!data->squared)
for (i = 0; i < data->outNumber; i++)
if (data->squared_value[i])
data->outpVector[i] = sqrt(data->outpVector[i]);
outData.v.vec.rVec = data->outpVector; /* vector of outputs */
outData.v.numValue = data->outNumber; /* vector number */
SPfrontEnd->OUTpData(data->NplotPtr, &refVal, &outData);
break;
default:
return (E_INTERN);
default:
return (E_INTERN);
}
break;
case N_CLOSE:
SPfrontEnd->OUTendPlot (data->NplotPtr);
FREE(data->namelist);
FREE(data->outpVector);
FREE(data->squared_value);
SPfrontEnd->OUTendPlot(data->NplotPtr);
FREE(data->namelist);
FREE(data->outpVector);
FREE(data->squared_value);
break;
default:
return (E_INTERN);
return (E_INTERN);
}
return (OK);
}

View File

@ -2,6 +2,7 @@
Copyright 1990 Regents of the University of California. All rights reserved.
Author: 1985 Thomas L. Quarles
Modified: 2000 AlansFixes
Modified: 2023 XSPICE breakpoint fix for shared ngspice by Vyacheslav Shevchuk
**********/
/* subroutine to do DC TRANSIENT analysis
@ -106,6 +107,25 @@ DCtran(CKTcircuit *ckt,
double ipc_last_time = 0.0;
double ipc_last_delta = 0.0;
/* gtri - end - wbk - 12/19/90 - Add IPC stuff */
// Fix for sharedsync olddelta: When DCTran processes
// either analog or XSPICE breakpoint, then it subtracts delta from
// ckt->CKTtime. It sends 0 as olddelta after analog breakpoint
// processing. Still, for XSPICE breakpoints it subtracts delta (see code
// 'else if(g_mif_info.breakpoint.current < ckt->CKTtime)' branch) and
// then sends non zero olddelta to sharedsync at the end of the function
// (see chkStep: label). Thus olddelta is subtracted twice. Then
// ckt->CKTtime becomes less than last_accepted_time.
// xspice_breakpoints_processed 0:
// XSPICE models didn't have breakpoints in [last_accepted_time, CKTtime].
// xspice_breakpoints_processed 1:
// convergence criteria are satisfied but XSPICE breakpoint(s) is in the
// time interval [last_accepted_time, CKTtime].
int xspice_breakpoints_processed = 0;
#ifdef SHARED_MODULE
double olddelta_for_shared_sync = 0.0;
#endif // SHARED_MODULE
#endif
#if defined CLUSTER || defined SHARED_MODULE
int redostep;
@ -680,7 +700,7 @@ resume:
} /* end if there are event instances */
/* gtri - end - wbk - Do event solution */
#else
#else /* no XSPICE */
#ifdef CLUSTER
if(!CLUsync(ckt->CKTtime,&ckt->CKTdelta,0)) {
@ -697,7 +717,7 @@ resume:
ckt->CKTdelmin, 0, &ckt->CKTstat->STATrejected, 0);
#endif
#endif
#endif /* no XSPICE */
for(i=5; i>=0; i--)
ckt->CKTdeltaOld[i+1] = ckt->CKTdeltaOld[i];
ckt->CKTdeltaOld[0] = ckt->CKTdelta;
@ -722,6 +742,7 @@ resume:
ckt->CKTcurrentAnalysis = DOING_TRAN;
/* gtri - end - wbk - 4/17/91 - Fix Berkeley bug */
xspice_breakpoints_processed = 0;
#endif
olddelta=ckt->CKTdelta;
/* time abort? */
@ -815,6 +836,7 @@ resume:
ckt->CKTtime -= ckt->CKTdelta;
ckt->CKTdelta = g_mif_info.breakpoint.current - ckt->CKTtime;
g_mif_info.breakpoint.last = ckt->CKTtime + ckt->CKTdelta;
xspice_breakpoints_processed = 1;
if(firsttime) {
ckt->CKTmode = (ckt->CKTmode&MODEUIC)|MODETRAN | MODEINITTRAN;
@ -945,8 +967,24 @@ resume:
#ifdef XSPICE
/* gtri - begin - wbk - Do event backup */
if(ckt->evt->counts.num_insts > 0)
if(ckt->evt->counts.num_insts > 0) {
#ifdef SHARED_MODULE
double discard_start_time = ckt->CKTtime + ckt->CKTdelta;
// ngspice in executable mode subtracts olddelta from the time
// before new delta calculation, but it keeps delta in CKTtime and
// postpones subtraction in library mode. Delayed subtraction leads
// to incorrect points dropping because ckt->CKTdelta is almost always
// less than olddelta if there are convergence issues, and EVTbackup
// may drop valid events that need to be processed within
// [last_accepted_time, last_accepted_time + ckt->CKTdelta] range
// after delta adjustment.
if (redostep && xspice_breakpoints_processed == 0)
discard_start_time -= olddelta;
EVTbackup(ckt, discard_start_time);
#else
EVTbackup(ckt, ckt->CKTtime + ckt->CKTdelta);
#endif
}
/* gtri - end - wbk - Do event backup */
#endif
@ -973,10 +1011,25 @@ resume:
function.
*/
chkStep:
#ifdef XSPICE
// There is no need to subtract olddelta from ckt->CKTtime one more time
// if it has been subtracted during XSPICE breakpoint processing.
// olddelta will be reinitialized on
// the new iteration, so it reassigning here should be safe. It can't be
// zeroed during breakpoint processing because it takes part in the
// "timestep too small" check.
olddelta_for_shared_sync = olddelta;
if (xspice_breakpoints_processed)
olddelta_for_shared_sync = 0.0;
if(sharedsync(&ckt->CKTtime, &ckt->CKTdelta, olddelta_for_shared_sync, ckt->CKTfinalTime,
ckt->CKTdelmin, redostep, &ckt->CKTstat->STATrejected, 1) == 0)
goto nextTime;
#else
if(sharedsync(&ckt->CKTtime, &ckt->CKTdelta, olddelta, ckt->CKTfinalTime,
ckt->CKTdelmin, redostep, &ckt->CKTstat->STATrejected, 1) == 0)
goto nextTime;
#endif
#endif // XSPICE
#endif // SHARED_MODULE
}
/* NOTREACHED */

View File

@ -1,477 +1,214 @@
/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================
/* XSPICE code model for the Controlled PWM Oscillator.
* This is a complete redesign of the original version,
* according to the d_osc model provided by G. Atkinson
*/
FILE d_pwm/cfunc.mod
Public Domain
Georgia Tech Research Corporation
Atlanta, Georgia 30332
PROJECT A-8503-405
The ngspice team
AUTHORS
24 Jul 1991 Jeffrey P. Murray
02 Mar 2022 Holger Vogt
MODIFICATIONS
23 Aug 1991 Jeffrey P. Murray
30 Sep 1991 Jeffrey P. Murray
06 Oct 2022 Holger Vogt
05 Jan 2023 Robert Turnbull
SUMMARY
This file contains the model-specific routines used to
functionally describe the d_pwm code model.
INTERFACES
FILE ROUTINE CALLED
CMmacros.h cm_message_send();
CM.c void *cm_analog_alloc()
void *cm_analog_get_ptr()
CMevt.c void cm_event_queue()
REFERENCED FILES
Inputs from and outputs to ARGS structure.
NON-STANDARD FEATURES
NONE
===============================================================================*/
/*=== INCLUDE FILES ====================*/
#include "d_pwm.h" /* ...contains macros & type defns.
for this model. 7/24/91 - JPM */
#include <stdlib.h>
#define FACTOR 0.75 // Controls timing of next scheduled call. */
/* PWL table entry. */
/*=== CONSTANTS ========================*/
struct pwl {
double ctl, dc;
};
/* Called at end to free memory. */
/*=== MACROS ===========================*/
/*=== LOCAL VARIABLES & TYPEDEFS =======*/
typedef struct {
double *x;
double *y;
} Local_Data_t;
/*=== FUNCTION PROTOTYPE DEFINITIONS ===*/
/*==============================================================================
FUNCTION cm_d_pwm()
AUTHORS
24 Jul 1991 Jeffrey P. Murray
02 Mar 2022 Holger Vogt
MODIFICATIONS
30 Sep 1991 Jeffrey P. Murray
SUMMARY
This function implements the d_pwm code model.
INTERFACES
FILE ROUTINE CALLED
CMmacros.h cm_message_send();
CM.c void *cm_analog_alloc()
void *cm_analog_get_ptr()
CMevt.c void cm_event_queue()
RETURNED VALUE
Returns inputs and outputs via ARGS structure.
GLOBAL VARIABLES
NONE
NON-STANDARD FEATURES
NONE
==============================================================================*/
static void cm_d_pwm_callback(ARGS,
Mif_Callback_Reason_t reason)
static void cm_d_pwm_callback(ARGS, Mif_Callback_Reason_t reason)
{
switch (reason) {
case MIF_CB_DESTROY: {
Local_Data_t *loc = STATIC_VAR(locdata);
if (loc) {
if (loc->x)
free(loc->x);
if(loc->y)
free(loc->y);
free(loc);
STATIC_VAR(locdata) = loc = NULL;
}
break;
} /* end of case MIF_CB_DESTROY */
} /* end of switch over reason being called */
} /* end of function cm_d_pwm_callback */
if (reason == MIF_CB_DESTROY) {
struct pwl *table = STATIC_VAR(locdata);
/*=== CM_D_PWM ROUTINE ===*/
/*************************************************************
* The following is the model for a duty cycle controlled *
* digital oscillator, derived from the controlled digital *
* oscillator d_osc. *
* *
* Created 3/02/2022 H. Vogt *
*************************************************************/
/*************************************************************
* *
* *
* <-----duty_cycle-----> *
* I *
* I t2 t3 *
* I \______________/_____ *
* I | | *
* I | | | | *
* I | | *
* I | | | | *
* I | | *
* I | | | | *
* I-----------------*-----* - - - - - - - - - -*--------- *
* t1 t4 *
* *
* *
* t2 = t1 + rise_delay *
* t4 = t3 + fall_delay *
* *
* Note that for the digital model, unlike for the *
* analog "square" model, t1 and t3 are stored and *
* adjusted values, but t2 & t4 are implied by the *
* rise and fall delays of the model, but are otherwise *
* not stored values. JPM *
* *
*************************************************************/
void cm_d_pwm(ARGS)
{
double *x, /* analog input value control array */
*y, /* frequency array */
cntl_input, /* control input value */
*phase, /* instantaneous phase of the model */
*phase_old, /* previous phase of the model */
*t1, /* pointer to t1 value */
*t3, /* pointer to t3 value */
/*time1,*/ /* variable for calculating new time1 value */
/*time3,*/ /* variable for calculating new time3 value */
dc = 0.5, /* instantaneous duty cycle value */
dphase, /* fractional part into cycle */
frequency, /* frequency value */
test_double, /* testing variable */
slope; /* slope value...used to extrapolate
freq values past endpoints. */
int i, /* generic loop counter index */
cntl_size, /* control array size */
dc_size; /* duty cycle array size */
Local_Data_t *loc; /* Pointer to local static data, not to be included
in the state vector (save memory!) */
/**** Retrieve frequently used parameters... ****/
cntl_size = PARAM_SIZE(cntl_array);
dc_size = PARAM_SIZE(dc_array);
frequency = PARAM(frequency);
/* check and make sure that the control array is the
same size as the frequency array */
if(cntl_size != dc_size){
cm_message_send(d_pwm_array_error);
return;
}
if (INIT) { /*** Test for INIT == TRUE. If so, allocate storage, etc. ***/
/* Allocate storage for internal variables */
cm_analog_alloc(0, sizeof(double));
cm_analog_alloc(1, sizeof(double));
cm_analog_alloc(2, sizeof(double));
/* assign internal variables */
phase = phase_old = (double *) cm_analog_get_ptr(0,0);
t1 = (double *) cm_analog_get_ptr(1,0);
t3 = (double *) cm_analog_get_ptr(2,0);
/*** allocate static storage for *loc ***/
STATIC_VAR (locdata) = calloc (1 , sizeof ( Local_Data_t ));
loc = STATIC_VAR (locdata);
CALLBACK = cm_d_pwm_callback;
x = loc->x = (double *) calloc((size_t) cntl_size, sizeof(double));
if (!x) {
cm_message_send(d_pwm_allocation_error);
return;
}
y = loc->y = (double *) calloc((size_t) cntl_size, sizeof(double));
if (!y) {
cm_message_send(d_pwm_allocation_error);
if(x)
free(x);
return;
}
/* Retrieve x and y values. */
for (i=0; i<cntl_size; i++) {
x[i] = PARAM(cntl_array[i]);
y[i] = PARAM(dc_array[i]);
}
}
else { /*** This is not an initialization pass...retrieve storage
addresses and calculate new outputs, if required. ***/
/** Retrieve previous values... **/
/* assign internal variables */
phase = (double *) cm_analog_get_ptr(0,0);
phase_old = (double *) cm_analog_get_ptr(0,1);
t1 = (double *) cm_analog_get_ptr(1,0);
t3 = (double *) cm_analog_get_ptr(2,0);
}
switch (CALL_TYPE) {
case ANALOG: /** analog call **/
test_double = TIME;
if ( AC == ANALYSIS ) { /* this model does not function
in AC analysis mode. */
return;
}
else {
if ( 0.0 == TIME ) { /* DC analysis */
/* retrieve & normalize phase value */
*phase = PARAM(init_phase);
if ( 0 > *phase ) {
*phase = *phase + 360.0;
}
*phase = *phase / 360.0;
/* set phase value to init_phase */
*phase_old = *phase;
/* preset time values to harmless values... */
*t1 = -1;
*t3 = -1;
}
loc = STATIC_VAR (locdata);
x = loc->x;
y = loc->y;
/* Retrieve cntl_input value. */
cntl_input = INPUT(cntl_in);
/* Determine segment boundaries within which cntl_input resides */
/*** cntl_input below lowest cntl_voltage ***/
if (cntl_input <= x[0]) {
slope = (y[1] - y[0])/(x[1] - x[0]);
dc = y[0] + (cntl_input - x[0]) * slope;
}
else
/*** cntl_input above highest cntl_voltage ***/
if (cntl_input >= x[cntl_size-1]) {
slope = (y[cntl_size-1] - y[cntl_size-2]) /
(x[cntl_size-1] - x[cntl_size-2]);
dc = y[cntl_size-1] + (cntl_input - x[cntl_size-1]) * slope;
}
else { /*** cntl_input within bounds of end midpoints...
must determine position progressively & then
calculate required output. ***/
for (i=0; i<cntl_size-1; i++) {
if ( (cntl_input < x[i+1]) && (cntl_input >= x[i]) ) {
/* Interpolate to the correct duty cycle value */
dc = ( (cntl_input - x[i]) / (x[i+1] - x[i]) ) *
( y[i+1]-y[i] ) + y[i];
}
}
}
/*** If dc < 0.0, clamp to 0 & issue a warning ***/
if ( 0.0 > dc ) {
dc = 0;
// cm_message_send(d_pwm_negative_dc_error);
}
/*** If dc > 1.0, clamp to 1 & issue a warning ***/
if ( 1.0 < dc ) {
dc = 1;
// cm_message_send(d_pwm_positive_dc_error);
}
/* calculate the instantaneous phase */
*phase = *phase_old + frequency * (TIME - T(1));
/* dphase is the percent into the cycle for
the period */
dphase = *phase_old - floor(*phase_old);
/* Calculate the time variables and the output value
for this iteration */
if((*t1 <= TIME) && (TIME <= *t3)) { /* output high */
*t3 = T(1) + (1 - dphase)/frequency;
if(TIME < *t3) {
cm_event_queue(*t3);
}
}
else
if((*t3 <= TIME) && (TIME <= *t1)) { /* output low */
if(dphase > (1.0 - dc) ) {
dphase = dphase - 1.0;
}
*t1 = T(1) + ( (1.0 - dc) - dphase)/frequency;
if(TIME < *t1) {
cm_event_queue(*t1);
}
}
else {
if(dphase > (1.0 - dc) ) {
dphase = dphase - 1.0;
}
*t1 = T(1) + ( (1.0 - dc) - dphase )/frequency;
if((TIME < *t1) || (T(1) == 0)) {
cm_event_queue(*t1);
}
*t3 = T(1) + (1 - dphase)/frequency;
}
cm_analog_set_temp_bkpt(*t1);
cm_analog_set_temp_bkpt(*t3);
}
break;
case EVENT: /** discrete call...lots to do **/
test_double = TIME;
if ( 0.0 == TIME ) { /* DC analysis...preset values,
as appropriate.... */
/* retrieve & normalize phase value */
*phase = PARAM(init_phase);
if ( 0 > *phase ) {
*phase = *phase + 360.0;
}
*phase = *phase / 360.0;
/* set phase value to init_phase */
*phase_old = *phase;
/* preset time values to harmless values... */
*t1 = -1;
*t3 = -1;
}
/* Calculate the time variables and the output value
for this iteration */
/* Output is always set to STRONG */
OUTPUT_STRENGTH(out) = STRONG;
if( *t1 == TIME ) { /* rising edge */
OUTPUT_STATE(out) = ONE;
OUTPUT_DELAY(out) = PARAM(rise_delay);
}
else {
if ( *t3 == TIME ) { /* falling edge */
OUTPUT_STATE(out) = ZERO;
OUTPUT_DELAY(out) = PARAM(fall_delay);
}
else { /* no change in output */
if ( TIME != 0.0 ) {
OUTPUT_CHANGED(out) = FALSE;
}
if ( (*t1 < TIME) && (TIME < *t3) ) {
OUTPUT_STATE(out) = ONE;
}
else {
OUTPUT_STATE(out) = ZERO;
}
}
}
break;
if (table)
free(table);
STATIC_VAR(locdata) = NULL;
}
}
/* Get the current duty cycle. */
static double get_dc(double ctl, struct pwl *table, int csize)
{
double d;
int i;
for (i = 0; i < csize; ++i) {
if (table[i].ctl > ctl)
break;
}
/* Interpolation outside input range continues slope. */
if (i > 0) {
if (i == csize)
i -= 2;
else
i--;
}
d = table[i].dc +
(ctl - table[i].ctl) * ((table[i + 1].dc - table[i].dc) /
(table[i + 1].ctl - table[i].ctl));
/* limit duty cycle d to 0.01 <= d <= 0.99 */
if (d > 0.99)
d = 0.99;
else if (d < 0.01)
d = 0.01;
return d;
}
/* The state data. */
struct state {
double last_time; // Time of last output change.
Digital_State_t last; // Last value output.
};
/* The code-model function. */
void cm_d_pwm(ARGS)
{
struct pwl *table;
struct state *state;
double ctl, delta, when, ddc;
int csize, i;
CALLBACK = cm_d_pwm_callback;
csize = PARAM_SIZE(cntl_array);
delta = 1.0 / PARAM(frequency);
if (INIT) {
/* Validate PWL table. */
for (i = 0; i < csize - 1; ++i) {
if (PARAM(cntl_array[i]) >= PARAM(cntl_array[i + 1]))
break;
}
if (i < csize - 1 || csize != PARAM_SIZE(dc_array)) {
cm_message_send("Badly-formed control table");
STATIC_VAR(locdata) = NULL;
return;
}
/* Allocate PWL table. */
table = malloc(csize * sizeof (struct pwl));
STATIC_VAR(locdata) = table;
if (!table)
return;
for (i = 0; i < csize; ++i) {
table[i].ctl = PARAM(cntl_array[i]);
table[i].dc = PARAM(dc_array[i]);
if (table[i].dc <= 0) {
cm_message_printf("Error: duty cycle %g is not positve, "
"value replaced by 0.01.",
table[i].dc);
table[i].dc = 0.01;
}
else if (table[i].dc >= 1) {
cm_message_printf("Error: duty cycle %g is 1 or larger, "
"value replaced by 0.99.",
table[i].dc);
table[i].dc = 0.99;
}
}
/* Allocate state data. */
cm_event_alloc(0, sizeof (struct state));
return;
}
table = STATIC_VAR(locdata);
if (!table)
return;
state = (struct state *)cm_event_get_ptr(0, 0);
if (CALL_TYPE != EVENT) {
if (TIME == 0.0) {
double phase;
/* Set initial output and state data. */
ctl = INPUT(cntl_in);
ddc = get_dc(ctl, table, csize);
phase = PARAM(init_phase);
phase /= 360.0;
if (phase < 0.0)
phase += 1.0;
/* When would a hypothetical previous transition have been? */
state->last_time = delta * (1.0 - ddc - phase);
if (state->last_time < 0.0) {
state->last = ONE;
} else {
state->last = ZERO;
state->last_time = -delta * phase;
}
}
return;
}
/* Event call; either one requested previously or just before
* a time-step is accepted.
*/
if (TIME == 0.0) {
OUTPUT_STATE(out) = state->last;
OUTPUT_STRENGTH(out) = STRONG;
return;
}
/* When is the next transition due? */
ctl = INPUT(cntl_in);
ddc = get_dc(ctl, table, csize);
if (state->last)
delta *= ddc;
else
delta *= (1.0 - ddc);
when = state->last_time + delta;
if (TIME >= when) {
// If the frequency rose rapidly, the transition has been missed.
// Force a shorter time-step and schedule then.
cm_analog_set_temp_bkpt(state->last_time + FACTOR * delta);
OUTPUT_CHANGED(out) = FALSE;
return;
}
if (TIME >= state->last_time + FACTOR * delta) {
/* TIME is reasonably close to transition time. Request output. */
state->last_time = when;
state->last ^= ONE;
OUTPUT_STATE(out) = state->last;
OUTPUT_STRENGTH(out) = STRONG;
OUTPUT_DELAY(out) = when - TIME;
/* Request a call in the next half-cycle. */
cm_event_queue(when + FACTOR * delta);
} else {
OUTPUT_CHANGED(out) = FALSE;
if (TIME < state->last_time) {
/* Output transition pending, nothing to do. */
return;
} else {
/* Request a call nearer to transition time. */
cm_event_queue(state->last_time + FACTOR * delta);
}
}
}

View File

@ -1,93 +0,0 @@
/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================
FILE d_pwm/d_pwm.h
Public Domain
Georgia Tech Research Corporation
Atlanta, Georgia 30332
PROJECT A-8503-405
The ngspice team
AUTHORS
25 Jul 1991 Jeffrey P. Murray
02 Mar 2022 Holger Vogt
MODIFICATIONS
30 Sept 1991 Jeffrey P. Murray
SUMMARY
This file contains the header information for the d_pwm
code model.
INTERFACES
FILE ROUTINE CALLED
N/A N/A
REFERENCED FILES
N/A
NON-STANDARD FEATURES
NONE
===============================================================================*/
/*
Structures, etc. for d_pwm oscillator model.
7/25/90
Last Modified 7/25/91 J.P.Murray
3/02/22 H. Vogt */
/*=======================================================================*/
/*=== INCLUDE FILES =====================================================*/
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
/*=== CONSTANTS =========================================================*/
/**** Error Messages ****/
char *d_pwm_allocation_error = "\n**** Error ****\nD_PWM: Error allocating VCO block storage \n";
char *d_pwm_array_error = "\n**** Error ****\nD_PWM: Size of control array different than duty cycle array \n";
char *d_pwm_negative_dc_error = "\n**** Error ****\nD_PWM: The extrapolated value for duty cycle\nhas been found to be negative... \n Lower duty cycle level has been clamped to 0.0 \n";
char *d_pwm_positive_dc_error = "\n**** Error ****\nD_PWM: The extrapolated value for duty cycle\nhas been found to be > 1... \n Upper duty cycle level has been clamped to 1.0 \n";
/*=== MACROS ============================================================*/
/*=== LOCAL VARIABLES & TYPEDEFS ========================================*/
/*=== FUNCTION PROTOTYPE DEFINITIONS ====================================*/
/*=======================================================================*/