Browse Source
Add an analog delay source for transient simulation
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.pre-master-46
4 changed files with 430 additions and 0 deletions
-
344src/xspice/icm/analog/delay/cfunc.mod
-
81src/xspice/icm/analog/delay/ifspec.ifs
-
1src/xspice/icm/analog/modpath.lst
-
4visualc/xspice/analog.vcxproj
@ -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 */ |
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue