Add an analog delay source for transient simulation

Internal circular memory of size tstop/tstep or user defined.
(aprox.) every tstep a value is stored.
Delay time in multiples of tstep, by control voltage or user defined.
Dc or ac sim will simply connect input to output.
This commit is contained in:
Holger Vogt 2021-05-25 17:51:07 +02:00
parent 0d2d034eb2
commit fbab9a0fe2
4 changed files with 430 additions and 0 deletions

View File

@ -0,0 +1,344 @@
/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================
FILE delay/cfunc.mod
3-clause BSD
Copyright 2021
The ngspice team
AUTHORS
21 May 2021 Holger Vogt
MODIFICATIONS
SUMMARY
This file contains the functional description of the analog
delay code model.
INTERFACES
FILE ROUTINE CALLED
CMutil.c void cm_smooth_corner();
CM.c void *cm_analog_alloc()
void *cm_analog_get_ptr()
int cm_analog_integrate()
cm_delay_callback()
REFERENCED FILES
Inputs from and outputs to ARGS structure.
NON-STANDARD FEATURES
NONE
===============================================================================*/
/*=== INCLUDE FILES ====================*/
#include <stdlib.h>
/*=== CONSTANTS ========================*/
/*=== MACROS ===========================*/
/*=== LOCAL VARIABLES & TYPEDEFS =======*/
typedef struct {
double *buffer; /* the storage array for input values */
int buffer_size; /* size of buffer */
int buff_write; /* buffer write index */
int buff_del; /* buffer index, delayed */
int step_count; /* steps processed */
double tdelmin; /* min delay, if controlled */
double tdelmax; /* max delay, if controlled */
double tdelay; /* time delay */
double tstep; /* tran step size */
double tstop; /* tran stop value */
double tprev; /* previous time value */
double prev_val; /* previous data value */
double start_val; /* signal time 0 value */
} mLocal_Data_t;
struct CKTcircuitmin {
/* This is a minimum re-definition of the circuit structure defined in
cktdefs.h. We are interested in TSTEP and TSTOP */
GENmodel **CKThead;
STATistics *CKTstat; /* The STATistics structure */
double *CKTstates[8]; /* Used as memory of past steps ??? */
double CKTtime; /* Current transient simulation time */
double CKTdelta; /* next time step in transient simulation */
double CKTdeltaOld[7]; /* Memory for the 7 most recent CKTdelta */
double CKTtemp; /* Actual temperature of CKT, initialzed to 300.15 K in cktinit.c*/
double CKTnomTemp; /* Reference temperature 300.15 K set in cktinit.c */
double CKTvt; /* Thernmal voltage at CKTtemp */
double CKTag[7]; /* the gear variable coefficient matrix */
#ifdef PREDICTOR
double CKTagp[7]; /* the gear predictor variable coefficient matrix */
#endif /*PREDICTOR*/
int CKTorder; /* the integration method order */
int CKTmaxOrder; /* maximum integration method order */
int CKTintegrateMethod; /* the integration method to be used */
double CKTxmu; /* for trapezoidal method */
int CKTindverbosity; /* control check of inductive couplings */
SMPmatrix *CKTmatrix; /* pointer to sparse matrix */
int CKTniState; /* internal state */
double *CKTrhs; /* current rhs value - being loaded */
double *CKTrhsOld; /* previous rhs value for convergence testing */
double *CKTrhsSpare; /* spare rhs value for reordering */
double *CKTirhs; /* current rhs value - being loaded imag) */
double *CKTirhsOld; /* previous rhs value (imaginary)*/
double *CKTirhsSpare; /* spare rhs value (imaginary)*/
#ifdef PREDICTOR
double *CKTpred; /* predicted solution vector */
double *CKTsols[8]; /* previous 8 solutions */
#endif /* PREDICTOR */
double *CKTrhsOp; /* opearating point values */
double *CKTsenRhs; /* current sensitivity rhs values */
double *CKTseniRhs; /* current sensitivity rhs values (imag)*/
int CKTmaxEqNum; /* And this ? */
int CKTcurrentAnalysis; /* the analysis in progress (if any) */
CKTnode *CKTnodes; /* ??? */
CKTnode *CKTlastNode; /* ??? */
CKTnode *prev_CKTlastNode; /* just before model setup */
int CKTnumStates; /* Number of sates effectively valid ??? */
long CKTmode; /* Mode of operation of the circuit ??? */
int CKTbypass; /* bypass option, how does it work ? */
int CKTdcMaxIter; /* iteration limit for dc op. (itl1) */
int CKTdcTrcvMaxIter; /* iteration limit for dc tran. curv (itl2) */
int CKTtranMaxIter; /* iteration limit for each timepoint for tran*/
int CKTbreakSize; /* ??? */
int CKTbreak; /* ??? */
double CKTsaveDelta; /* ??? */
double CKTminBreak; /* ??? */
double *CKTbreaks; /* List of breakpoints ??? */
double CKTabstol; /* --- */
double CKTpivotAbsTol; /* --- */
double CKTpivotRelTol; /* --- */
double CKTreltol; /* --- */
double CKTchgtol; /* --- */
double CKTvoltTol; /* --- */
/* What is this define for ? */
#ifdef NEWTRUNC
double CKTlteReltol;
double CKTlteAbstol;
#endif /* NEWTRUNC */
double CKTgmin; /* .options GMIN */
double CKTgshunt; /* .options RSHUNT */
double CKTcshunt; /* .options CSHUNT */
double CKTdelmin; /* minimum time step for tran analysis */
double CKTtrtol; /* .options TRTOL */
double CKTfinalTime; /* TSTOP */
double CKTstep; /* TSTEP */
double CKTmaxStep; /* TMAX */
double CKTinitTime; /* TSTART */
/* struct is truncated here */
};
typedef struct CKTcircuitmin CKTcircuitmin;
/*=== FUNCTION PROTOTYPE DEFINITIONS ===*/
static void cm_delay_callback(ARGS, Mif_Callback_Reason_t reason);
/*=== CM_DELAY ROUTINE ===*/
void cm_delay(ARGS)
{
int buffer_size, delay_step;
double delay;
double delmin, delmax;
double *ins, *ins_old;
mLocal_Data_t *loc; /* Pointer to local static data, not to be included
in the state vector */
CKTcircuitmin *ckt;
if (ANALYSIS != MIF_AC) { /**** only Transient Analysis and dc ****/
/** INIT: allocate storage **/
if (INIT==1) {
CALLBACK = cm_delay_callback;
ckt = (CKTcircuitmin*)cm_get_circuit();
if (PARAM_NULL(buffer_size)) {
/* size depends on TSTOP/TSTEP, if no parameter given */
buffer_size = (int) (ckt->CKTfinalTime / ckt->CKTstep) + 1;
}
else {
buffer_size = PARAM(buffer_size);
if (buffer_size < 0) {
cm_message_send("Negative buffer size is not allowed "
"in a delay code model");
return;
}
}
delay = PARAM(delay);
if (delay < 0.0) {
delay = 0.0;
}
/*** allocate static storage for *loc ***/
if ((loc = (mLocal_Data_t *) (STATIC_VAR(locdata) = calloc(1,
sizeof(mLocal_Data_t)))) == (mLocal_Data_t *) NULL) {
cm_message_send("Unable to allocate Local_Data_t "
"in cm_delay()");
return;
}
/*** allocate static storage for the delay buffer ***/
loc->buffer = (double *) calloc((size_t)buffer_size, sizeof(double));
if (loc->buffer == (double *) NULL) {
cm_message_send("Unable to allocate delay buffer "
"in cm_delay()");
return;
}
loc->buffer_size = buffer_size;
cm_analog_alloc(TRUE,sizeof(double));
ins = (double *) cm_analog_get_ptr(TRUE,0); /* Set out pointer to current
time storage */
ins_old = (double *) cm_analog_get_ptr(TRUE,1); /* Set old-output-state pointer
to previous time storage */
*ins = *ins_old = loc->start_val = INPUT(in);
/* The delay is controlled by input delay_cnt */
if (PARAM(has_delay_cnt) == MIF_TRUE) {
if (PARAM_NULL(delmin))
loc->tdelmin = 0.0;
else
loc->tdelmin = PARAM(delmin);
if (PARAM_NULL(delmax))
loc->tdelmax = ckt->CKTfinalTime;
else
loc->tdelmax = PARAM(delmax);
}
loc->buff_write = 0;
loc->buff_del = 0;
loc->step_count = 0;
loc->tdelay = delay;
loc->tstop = ckt->CKTfinalTime;
loc->tstep = ckt->CKTstep;
loc->tprev = 0.0;
loc->prev_val = 0.0;
}
/* retrieve previous values */
loc = STATIC_VAR (locdata);
ins = (double *) cm_analog_get_ptr(TRUE,0); /* Set out pointer to current
time storage */
ins_old = (double *) cm_analog_get_ptr(TRUE,1); /* Set old-output-state pointer
to previous time storage */
delay = loc->tdelay;
if (delay == 0.0) {
OUTPUT(out) = INPUT(in);
return;
}
if (TIME == 0.0)
loc->start_val = INPUT(in);
/* input, simply interpolated to TSTEP */
double tacct = loc->step_count * loc->tstep;
if (TIME >= tacct) {
double curr_in;
/* double tdiff = TIME - tacct;
// interpolate
if (tdiff > 0) {
curr_in = loc->prev_val + (INPUT(in) - loc->prev_val) / (TIME - loc->tprev) * tacct;
}
else*/
curr_in = INPUT(in);
loc->buffer[loc->buff_write] = *ins = curr_in;
/* next buffer location, circular, for writing */
loc->buff_write = (loc->buff_write + 1) % loc->buffer_size;
loc->step_count++;
loc->tprev = tacct;
loc->prev_val = curr_in;
}
delmin = loc->tdelmin;
delmax = loc->tdelmax;
if (PARAM(has_delay_cnt) == MIF_TRUE) {
delay = (delmax - delmin) * INPUT(cntrl) + delmin;
}
/* time not yet advanced for delay output */
if (TIME < delay) {
OUTPUT(out) = loc->start_val;
}
else
OUTPUT(out) = loc->buffer[loc->buff_del];
/* delay in steps */
delay_step = (int)(delay / loc->tstep);
/* FIXME: For whatever reason the model is assessed two times per time step */
/* next readout location, delayed after writing */
if ((loc->buff_write - delay_step) >= 0)
loc->buff_del = loc->buff_write - delay_step;
else
loc->buff_del = loc->buff_write - delay_step + loc->buffer_size;
}
else { /**** all others ****/
OUTPUT(out) = INPUT(in);
}
}
/* free the memory created locally */
static void cm_delay_callback(ARGS, Mif_Callback_Reason_t reason)
{
switch (reason) {
case MIF_CB_DESTROY: {
mLocal_Data_t *loc = (mLocal_Data_t *) STATIC_VAR(locdata);
if (loc == (mLocal_Data_t *) NULL) {
break;
}
if (loc->buffer != (double *) NULL) {
free(loc->buffer);
}
free(loc);
STATIC_VAR(locdata) = NULL;
break;
}
}
} /* end of function cm_delay_callback */

View File

@ -0,0 +1,81 @@
/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================
-------------------------------------------------------------------------
Copyright 2011 Thomas Sailer
3 - Clause BSD license
(see COPYING or https://opensource.org/licenses/BSD-3-Clause)
-------------------------------------------------------------------------
AUTHORS
19 May 2011 Thomas Sailer
SUMMARY
This file contains the interface specification file for the
delay code model.
===============================================================================*/
NAME_TABLE:
C_Function_Name: cm_delay
Spice_Model_Name: delay
Description: "analog delay line"
PORT_TABLE:
Port_Name: in out cntrl
Description: "input" "output" "control"
Direction: in out in
Default_Type: v v v
Allowed_Types: [v,vd,vnam] [v,vd] [v,vd,i,id]
Vector: no no no
Vector_Bounds: - - -
Null_Allowed: no no yes
PARAMETER_TABLE:
Parameter_Name: delay buffer_size
Description: "time delay" "size of delay buffer"
Data_Type: real int
Default_Value: 0.0 1024
Limits: - [1 -]
Vector: no no
Vector_Bounds: - -
Null_Allowed: yes yes
PARAMETER_TABLE:
Parameter_Name: has_delay_cnt
Description: "controlled delay"
Data_Type: boolean
Default_Value: FALSE
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: delmin delmax
Description: "min delay" "max delay"
Data_Type: real real
Default_Value: 0 0
Limits: [0 -] [0 -]
Vector: no no
Vector_Bounds: - -
Null_Allowed: yes yes
STATIC_VAR_TABLE:
Static_Var_Name: locdata
Description: "local static data"
Data_Type: pointer

View File

@ -17,3 +17,4 @@ summer
s_xfer
triangle
file_source
delay

View File

@ -250,6 +250,8 @@
<ClCompile Include="icm\analog\triangle\triangle-ifspec.c" />
<ClCompile Include="icm\analog\file_source\file_source-cfunc.c" />
<ClCompile Include="icm\analog\file_source\file_source-ifspec.c" />
<ClCompile Include="icm\analog\delay\delay-cfunc.c" />
<ClCompile Include="icm\analog\delay\delay-ifspec.c" />
<ClCompile Include="..\..\src\xspice\icm\dlmain.c" />
</ItemGroup>
<ItemGroup>
@ -291,6 +293,8 @@
<None Include="..\..\src\xspice\icm\analog\triangle\ifspec.ifs" />
<None Include="..\..\src\xspice\icm\analog\file_source\cfunc.mod" />
<None Include="..\..\src\xspice\icm\analog\file_source\ifspec.ifs" />
<None Include="..\..\src\xspice\icm\analog\delay\cfunc.mod" />
<None Include="..\..\src\xspice\icm\analog\delay\ifspec.ifs" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\include\ngspice\dstring.h" />