d_pwm: Add a hybrid oscillator (analg control in, digital out) with PWM

(pulse width modulation) capability, oscillation frequency is a parameter.

The model has been derived from the d_osc example.
This commit is contained in:
Holger Vogt 2022-03-04 11:43:18 +01:00
parent e61c92af5a
commit 31a0f3eaf6
5 changed files with 662 additions and 0 deletions

View File

@ -0,0 +1,485 @@
/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================
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
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 */
/*=== CONSTANTS ========================*/
/*=== MACROS ===========================*/
/*=== LOCAL VARIABLES & TYPEDEFS =======*/
/*=== 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
==============================================================================*/
/*=== 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 *
* *
*************************************************************/
#include <stdlib.h>
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 */
/**** 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);
}
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;
}
/* Allocate storage for breakpoint domain & duty cycle range values */
x = (double *) calloc((size_t) cntl_size, sizeof(double));
if (!x) {
cm_message_send(d_pwm_allocation_error);
return;
}
y = (double *) calloc((size_t) dc_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]);
}
/* 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;
}
if(x) free(x);
if(y) free(y);
}
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;
}
}

View File

@ -0,0 +1,93 @@
/*.......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 ====================================*/
/*=======================================================================*/

View File

@ -0,0 +1,77 @@
/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================
Public Domain
Georgia Tech Research Corporation
Atlanta, Georgia 30332
The ngspice team
AUTHORS
30 Sept 1991 Jeffrey P. Murray
02 Mar 2022 Holger Vogt
SUMMARY
This file contains the interface specification file for the
hybrid d_pwm code model.
===============================================================================*/
NAME_TABLE:
Spice_Model_Name: d_pwm
C_Function_Name: cm_d_pwm
Description: "duty cycle controlled digital oscillator"
PORT_TABLE:
Port_Name: cntl_in out
Description: "control input" "output"
Direction: in out
Default_Type: v d
Allowed_Types: [v,vd,i,id] [d]
Vector: no no
Vector_Bounds: - -
Null_Allowed: no no
PARAMETER_TABLE:
Parameter_Name: cntl_array dc_array
Description: "control array" "duty cycle array"
Data_Type: real real
Default_Value: 0.0 0.5
Limits: - [0 1]
Vector: yes yes
Vector_Bounds: [2 -] [2 -]
Null_Allowed: no no
PARAMETER_TABLE:
Parameter_Name: frequency init_phase
Description: "oscillator frequency" "initial phase of output"
Data_Type: real real
Default_Value: 1e6 0
Limits: [1e-6 -] [-180.0 +360.0]
Vector: no no
Vector_Bounds: - -
Null_Allowed: yes yes
PARAMETER_TABLE:
Parameter_Name: rise_delay fall_delay
Description: "rise delay" "fall delay"
Data_Type: real real
Default_Value: 1e-9 1e-9
Limits: [0 -] [0 -]
Vector: no no
Vector_Bounds: - -
Null_Allowed: yes yes

View File

@ -17,6 +17,7 @@ d_or
d_osc
d_pulldown
d_pullup
d_pwm
d_ram
d_source
d_srff

View File

@ -276,6 +276,10 @@
<AdditionalIncludeDirectories>..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="icm\digital\d_pullup\d_pullup-ifspec.c" />
<ClCompile Include="icm\digital\d_pwm\d_pwm-cfunc.c">
<AdditionalIncludeDirectories>..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="icm\digital\d_pwm\d_pwm-ifspec.c" />
<ClCompile Include="icm\digital\d_ram\d_ram-cfunc.c">
<AdditionalIncludeDirectories>..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
@ -353,6 +357,8 @@
<None Include="..\..\src\xspice\icm\digital\d_pulldown\ifspec.ifs" />
<None Include="..\..\src\xspice\icm\digital\d_pullup\cfunc.mod" />
<None Include="..\..\src\xspice\icm\digital\d_pullup\ifspec.ifs" />
<None Include="..\..\src\xspice\icm\digital\d_pwm\cfunc.mod" />
<None Include="..\..\src\xspice\icm\digital\d_pwm\ifspec.ifs" />
<None Include="..\..\src\xspice\icm\digital\d_ram\cfunc.mod" />
<None Include="..\..\src\xspice\icm\digital\d_ram\ifspec.ifs" />
<None Include="..\..\src\xspice\icm\digital\d_source\cfunc.mod" />