Hi All,
@RDunn @KSKelvin @Engelhardt
As with my journey to study the C-code implementation in Qspice, I am now learning the Trunc() and what will be the advantage of using this Trunc() and how it may be useful to improve the accuracy of the simulation.
// Automatically generated C++ file on Thu Oct 19 16:11:47 2023
//
// To build with Digital Mars C++ Compiler:
//
// dmc -mn -WD study_pcmc.cpp kernel32.lib
union uData
{
bool b;
char c;
unsigned char uc;
short s;
unsigned short us;
int i;
unsigned int ui;
float f;
double d;
long long int i64;
unsigned long long int ui64;
char *str;
unsigned char *bytes;
};
// int DllMain() must exist and return 1 for a process to load the .DLL
// See https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain for more information.
int __stdcall DllMain(void *module, unsigned int reason, void *reserved) { return 1; }
// #undef pin names lest they collide with names in any header file(s) you might include.
#undef CLK
#undef OUT
#undef IL
double pwm;
double clk_prev;
extern "C" __declspec(dllexport) void study_pcmc(void **opaque, double t, union uData *data)
{
double CLK = data[0].d; // input
double IL = data[1].d; // input
double &OUT = data[2].d; // output
// Implement module evaluation code here:
if((clk_prev<=0.5)&&(CLK>=0.5))
{
pwm = 5;
}
if(IL>1)
{
pwm = 0;
}
clk_prev = CLK;
OUT = pwm;
}
study_pcmc.cpp (1.2 KB)
// Automatically generated C++ file on Thu Oct 19 16:07:55 2023
//
// To build with Digital Mars C++ Compiler:
//
// dmc -mn -WD study_pcmc_trunc.cpp kernel32.lib
#include <malloc.h>
union uData
{
bool b;
char c;
unsigned char uc;
short s;
unsigned short us;
int i;
unsigned int ui;
float f;
double d;
long long int i64;
unsigned long long int ui64;
char *str;
unsigned char *bytes;
};
// int DllMain() must exist and return 1 for a process to load the .DLL
// See https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain for more information.
int __stdcall DllMain(void *module, unsigned int reason, void *reserved) { return 1; }
void bzero(void *ptr, unsigned int count)
{
unsigned char *first = (unsigned char *) ptr;
unsigned char *last = first + count;
while(first < last)
*first++ = '\0';
}
// #undef pin names lest they collide with names in any header file(s) you might include.
#undef CLK
#undef OUT
#undef IL
struct sSTUDY_PCMC_TRUNC
{
// declare the structure here
double pwm;
double clk_prev;
};
extern "C" __declspec(dllexport) void study_pcmc_trunc(struct sSTUDY_PCMC_TRUNC **opaque, double t, union uData *data)
{
double CLK = data[0].d; // input
double IL = data[1].d; // input
double &OUT = data[2].d; // output
if(!*opaque)
{
*opaque = (struct sSTUDY_PCMC_TRUNC *) malloc(sizeof(struct sSTUDY_PCMC_TRUNC));
bzero(*opaque, sizeof(struct sSTUDY_PCMC_TRUNC));
}
struct sSTUDY_PCMC_TRUNC *inst = *opaque;
// Implement module evaluation code here:
if((inst->clk_prev<=0.5)&&(CLK>=0.5))
{
inst->pwm = 5;
}
if(IL>1)
{
inst->pwm = 0;
}
inst->clk_prev = CLK;
OUT = inst->pwm;
}
extern "C" __declspec(dllexport) double MaxExtStepSize(struct sSTUDY_PCMC_TRUNC *inst)
{
return 1e308; // implement a good choice of max timestep size that depends on struct sSTUDY_PCMC_TRUNC
}
extern "C" __declspec(dllexport) void Trunc(struct sSTUDY_PCMC_TRUNC *inst, double t, union uData *data, double *timestep)
{ // limit the timestep to a tolerance if the circuit causes a change in struct sSTUDY_PCMC_TRUNC
const double ttol = 1e-9;
if(*timestep > ttol)
{
double &OUT = data[2].d; // output
// Save output vector
const double _OUT = OUT;
struct sSTUDY_PCMC_TRUNC tmp = *inst;
study_pcmc_trunc(&(&tmp), t, data);
if(tmp.pwm != inst->pwm) // implement a meaningful way to detect if the state has changed
*timestep = ttol;
// Restore output vector
OUT = _OUT;
}
}
extern "C" __declspec(dllexport) void Destroy(struct sSTUDY_PCMC_TRUNC *inst)
{
free(inst);
}
study_pcmc_trunc.cpp (2.6 KB)
Please refer to the schematic above. Here we can see, that the code that is implemented with Trunc() can force the solver to reduce the timestep to the timestep tolerance when switching event is detected.
While, the comparator+SR latch block and simple C-block implementation completely failed to detect the accurate switching event.
I hope somebody can give a better insight why is it the case?
how does the C-code and the analog circuit are solved for each timestep?
I have a guess that the Analog Circuit solver first followed by C-code solver following the time step as suggested by the Spice solver.
However, with Trunc(), it can force the the Spice solver to revert to the previous step then use a very small time step when a change in any of the any of the data Instance is detected.
Looking for suggestions⦠Thanks,
Arief,