ah … yes … learning by reverse engineering. I love spending hours trying to understand something that could be explained with a view lines of text or a diagram. Also I’m not asking about how to use it, but how it is working, so I may have a better chance for debugging what is not working.
But ok, I try myself :
Schematic:
Code
// Automatically generated C++ file on Thu Aug 17 23:16:22 2023
//
// To build with Digital Mars C++ Compiler:
//
// dmc -mn -WD ctest_x1.cpp kernel32.lib
#include <stdio.h>
#include <malloc.h>
#include <stdarg.h>
#include <time.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 display(const char *fmt, ...)
{ // for diagnostic print statements
msleep(30);
fflush(stdout);
va_list args = { 0 };
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
fflush(stdout);
msleep(30);
}
void bzero(void *ptr, unsigned int count)
{
unsigned char *first = (unsigned char *) ptr;
unsigned char *last = first + count;
while(first < last)
*first++ = '\0';
display("bzero\n");
display("[sample time + count]\n");
}
// #undef pin names lest they collide with names in any header file(s) you might include.
#undef in
#undef out
struct sCTEST_X1
{
// declare the structure here
bool state;
bool trunc;
int n_pwm;
int n;
};
extern "C" __declspec(dllexport) void ctest_x1(struct sCTEST_X1 **opaque, double t, union uData *data)
{
double &out = data[0].d; // output
double &dbg = data[1].d; // output
if(!*opaque)
{
*opaque = (struct sCTEST_X1 *) malloc(sizeof(struct sCTEST_X1));
bzero(*opaque, sizeof(struct sCTEST_X1));
}
struct sCTEST_X1 *inst = *opaque;
// Implement module evaluation code here:
static int n = 0;
static int nt = 0;
bool toggle = false;
double T = 10e-6;
double tpwm = t - inst->n_pwm * T;
dbg = tpwm;
// sample point counter
if (t > 0) inst->n++; // don't count t=0 steps
if (inst->trunc) {
nt++; // call counter called from trunc
display("\t\t\t\t%4u\t\t\tmain called from Trunc (n=%u)\n", inst->n, nt);
}
else {
n++; // call counter called from ?
display("%8.3fns\t%4u\t\tmain called from Qspice? (n=%u)\n", t*1e9, inst->n, n);
display("\t\t\t\t\t\t\t\t=> outputs determined for t=%8.3fns\n", t*1e9);
}
// set high
if (!out && tpwm > 0.5*T) {
out = true;
toggle = true;
if (inst->trunc) display("\t\t\t\t\t\t\t\tout is LOW, but would be HIGH next step\n");
else display("\t\t\t\t\t\t\t\tout is HIGH\n");
}
// set low
if (tpwm >= T) {
inst->n_pwm++;
out = false;
toggle = true;
if (inst->trunc) display("\t\t\t\t\t\t\t\tout is HIGH, but would be LOW next step\n");
else display("\t\t\t\t\t\t\t\tout is LOW\n");
}
inst->state ^= toggle;
}
extern "C" __declspec(dllexport) double MaxExtStepSize(struct sCTEST_X1 *inst)
{
static int sn = 0;
sn++;
display("\n\t\t\t\t%4u\t\tMaxExtStepSize called from Qspice?\n", inst->n);
display("\t\t\t\t\t\t\t\t=> returns max_timestep=1e-6 (n=%u)\n", sn);
return 1e-6; // implement a good choice of max timestep size that depends on struct sCTEST_X1
}
extern "C" __declspec(dllexport) void Trunc(struct sCTEST_X1 *inst, double t, union uData *data, double *timestep)
{ // limit the timestep to a tolerance if the circuit causes a change in struct sCTEST_X1
const double ttol = 10e-9;
static int n = 0; // call counter
n++;
display("%8.3fns\t%4u\t\tTrunc called from Qspice? (n=%u)\n", t*1e9, inst->n, n);
if(*timestep > ttol) // *timestep seems to be always inf.
{
double &out = data[0].d; // output
double &dbg = data[1].d; // output
// Save output vector
const double _out = out;
const double _dbg = dbg;
struct sCTEST_X1 tmp = *inst;
tmp.trunc = true; // to signal it was called from trunc
ctest_x1(&(&tmp), t, data);
if(tmp.state != inst->state) {
*timestep = ttol;
display("\t\t\t\t\t\t\t\ttrunc: STATE CHANGE DETECTED, next timestep=%.1fns. Outputs restored.\n", (*timestep)*1e9);
}
else {
display("\t\t\t\t\t\t\t\ttrunc: no state change detected, next timestep=%.1fns. Outputs restored.\n", (*timestep)*1e9);
}
// Restore output vector
out = _out;
dbg = _dbg;
}
else {
display("*timestep <= ttol");
}
}
extern "C" __declspec(dllexport) void Destroy(struct sCTEST_X1 *inst)
{
display("Destroy.");
free(inst);
}
Waveform looks like this:
Output: (excerpt)
bzero
[sample time + count]
0.000ns 0 main called from Qspice? (n=1)
=> outputs determined for t= 0.000ns
0.000ns 0 main called from Qspice? (n=2)
=> outputs determined for t= 0.000ns
0.000ns 0 main called from Qspice? (n=3)
=> outputs determined for t= 0.000ns
0.000ns 0 main called from Qspice? (n=4)
=> outputs determined for t= 0.000ns
0.000ns 0 main called from Qspice? (n=5)
=> outputs determined for t= 0.000ns
0.000ns 0 main called from Qspice? (n=6)
=> outputs determined for t= 0.000ns
0 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=1)
1000.000ns 1 main called from Qspice? (n=7)
=> outputs determined for t=1000.000ns
1 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=2)
2000.000ns 1 Trunc called from Qspice? (n=1)
2 main called from Trunc (n=1)
trunc: no state change detected, next timestep=infns. Outputs restored.
2000.000ns 1 Trunc called from Qspice? (n=2)
2 main called from Trunc (n=2)
trunc: no state change detected, next timestep=infns. Outputs restored.
2000.000ns 2 main called from Qspice? (n=8)
=> outputs determined for t=2000.000ns
2 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=3)
3000.000ns 2 Trunc called from Qspice? (n=3)
3 main called from Trunc (n=3)
trunc: no state change detected, next timestep=infns. Outputs restored.
3000.000ns 2 Trunc called from Qspice? (n=4)
3 main called from Trunc (n=4)
trunc: no state change detected, next timestep=infns. Outputs restored.
3000.000ns 3 main called from Qspice? (n=9)
=> outputs determined for t=3000.000ns
3 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=4)
4000.000ns 3 Trunc called from Qspice? (n=5)
4 main called from Trunc (n=5)
trunc: no state change detected, next timestep=infns. Outputs restored.
4000.000ns 3 Trunc called from Qspice? (n=6)
4 main called from Trunc (n=6)
trunc: no state change detected, next timestep=infns. Outputs restored.
4000.000ns 4 main called from Qspice? (n=10)
=> outputs determined for t=4000.000ns
4 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=5)
5000.000ns 4 Trunc called from Qspice? (n=7)
5 main called from Trunc (n=7)
trunc: no state change detected, next timestep=infns. Outputs restored.
5000.000ns 5 main called from Qspice? (n=11)
=> outputs determined for t=5000.000ns
5 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=6)
6000.000ns 5 Trunc called from Qspice? (n=8)
6 main called from Trunc (n=8)
out is LOW, but would be HIGH next step
trunc: STATE CHANGE DETECTED, next timestep=10.0ns. Outputs restored.
5010.000ns 5 Trunc called from Qspice? (n=9)
6 main called from Trunc (n=9)
out is LOW, but would be HIGH next step
trunc: STATE CHANGE DETECTED, next timestep=10.0ns. Outputs restored.
5010.000ns 6 main called from Qspice? (n=12)
=> outputs determined for t=5010.000ns
out is HIGH
6 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=7)
5020.000ns 6 Trunc called from Qspice? (n=10)
7 main called from Trunc (n=10)
trunc: no state change detected, next timestep=infns. Outputs restored.
5020.000ns 7 main called from Qspice? (n=13)
=> outputs determined for t=5020.000ns
7 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=8)
5040.000ns 7 Trunc called from Qspice? (n=11)
8 main called from Trunc (n=11)
trunc: no state change detected, next timestep=infns. Outputs restored.
5040.000ns 8 main called from Qspice? (n=14)
=> outputs determined for t=5040.000ns
8 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=9)
5080.000ns 8 Trunc called from Qspice? (n=12)
9 main called from Trunc (n=12)
trunc: no state change detected, next timestep=infns. Outputs restored.
5080.000ns 9 main called from Qspice? (n=15)
=> outputs determined for t=5080.000ns
9 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=10)
5160.000ns 9 Trunc called from Qspice? (n=13)
10 main called from Trunc (n=13)
trunc: no state change detected, next timestep=infns. Outputs restored.
5160.000ns 10 main called from Qspice? (n=16)
=> outputs determined for t=5160.000ns
10 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=11)
5320.000ns 10 Trunc called from Qspice? (n=14)
11 main called from Trunc (n=14)
trunc: no state change detected, next timestep=infns. Outputs restored.
5320.000ns 11 main called from Qspice? (n=17)
=> outputs determined for t=5320.000ns
11 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=12)
5640.000ns 11 Trunc called from Qspice? (n=15)
12 main called from Trunc (n=15)
trunc: no state change detected, next timestep=infns. Outputs restored.
5640.000ns 12 main called from Qspice? (n=18)
=> outputs determined for t=5640.000ns
12 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=13)
6280.000ns 12 Trunc called from Qspice? (n=16)
13 main called from Trunc (n=16)
trunc: no state change detected, next timestep=infns. Outputs restored.
6280.000ns 13 main called from Qspice? (n=19)
=> outputs determined for t=6280.000ns
13 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=14)
7280.000ns 13 Trunc called from Qspice? (n=17)
14 main called from Trunc (n=17)
trunc: no state change detected, next timestep=infns. Outputs restored.
7280.000ns 14 main called from Qspice? (n=20)
=> outputs determined for t=7280.000ns
14 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=15)
8280.000ns 14 Trunc called from Qspice? (n=18)
15 main called from Trunc (n=18)
trunc: no state change detected, next timestep=infns. Outputs restored.
8280.000ns 15 main called from Qspice? (n=21)
=> outputs determined for t=8280.000ns
15 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=16)
9280.000ns 15 Trunc called from Qspice? (n=19)
16 main called from Trunc (n=19)
trunc: no state change detected, next timestep=infns. Outputs restored.
9280.000ns 16 main called from Qspice? (n=22)
=> outputs determined for t=9280.000ns
16 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=17)
10280.000ns 16 Trunc called from Qspice? (n=20)
17 main called from Trunc (n=20)
out is HIGH, but would be LOW next step
trunc: STATE CHANGE DETECTED, next timestep=10.0ns. Outputs restored.
9290.000ns 16 Trunc called from Qspice? (n=21)
17 main called from Trunc (n=21)
trunc: no state change detected, next timestep=infns. Outputs restored.
9290.000ns 17 main called from Qspice? (n=23)
=> outputs determined for t=9290.000ns
17 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=18)
9310.000ns 17 Trunc called from Qspice? (n=22)
18 main called from Trunc (n=22)
trunc: no state change detected, next timestep=infns. Outputs restored.
9310.000ns 18 main called from Qspice? (n=24)
=> outputs determined for t=9310.000ns
18 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=19)
9350.000ns 18 Trunc called from Qspice? (n=23)
19 main called from Trunc (n=23)
trunc: no state change detected, next timestep=infns. Outputs restored.
9350.000ns 19 main called from Qspice? (n=25)
=> outputs determined for t=9350.000ns
19 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=20)
9430.000ns 19 Trunc called from Qspice? (n=24)
20 main called from Trunc (n=24)
trunc: no state change detected, next timestep=infns. Outputs restored.
9430.000ns 20 main called from Qspice? (n=26)
=> outputs determined for t=9430.000ns
20 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=21)
9590.000ns 20 Trunc called from Qspice? (n=25)
21 main called from Trunc (n=25)
trunc: no state change detected, next timestep=infns. Outputs restored.
9590.000ns 21 main called from Qspice? (n=27)
=> outputs determined for t=9590.000ns
21 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=22)
9910.000ns 21 Trunc called from Qspice? (n=26)
22 main called from Trunc (n=26)
trunc: no state change detected, next timestep=infns. Outputs restored.
9910.000ns 22 main called from Qspice? (n=28)
=> outputs determined for t=9910.000ns
22 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=23)
10550.000ns 22 Trunc called from Qspice? (n=27)
23 main called from Trunc (n=27)
out is HIGH, but would be LOW next step
trunc: STATE CHANGE DETECTED, next timestep=10.0ns. Outputs restored.
9920.000ns 22 Trunc called from Qspice? (n=28)
23 main called from Trunc (n=28)
trunc: no state change detected, next timestep=infns. Outputs restored.
9920.000ns 23 main called from Qspice? (n=29)
=> outputs determined for t=9920.000ns
23 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=24)
9940.000ns 23 Trunc called from Qspice? (n=29)
24 main called from Trunc (n=29)
trunc: no state change detected, next timestep=infns. Outputs restored.
9940.000ns 24 main called from Qspice? (n=30)
=> outputs determined for t=9940.000ns
24 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=25)
9980.000ns 24 Trunc called from Qspice? (n=30)
25 main called from Trunc (n=30)
trunc: no state change detected, next timestep=infns. Outputs restored.
9980.000ns 25 main called from Qspice? (n=31)
=> outputs determined for t=9980.000ns
25 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=26)
10060.000ns 25 Trunc called from Qspice? (n=31)
26 main called from Trunc (n=31)
out is HIGH, but would be LOW next step
trunc: STATE CHANGE DETECTED, next timestep=10.0ns. Outputs restored.
9990.000ns 25 Trunc called from Qspice? (n=32)
26 main called from Trunc (n=32)
trunc: no state change detected, next timestep=infns. Outputs restored.
9990.000ns 26 main called from Qspice? (n=32)
=> outputs determined for t=9990.000ns
26 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=27)
10010.000ns 26 Trunc called from Qspice? (n=33)
27 main called from Trunc (n=33)
out is HIGH, but would be LOW next step
trunc: STATE CHANGE DETECTED, next timestep=10.0ns. Outputs restored.
10000.000ns 26 Trunc called from Qspice? (n=34)
27 main called from Trunc (n=34)
trunc: no state change detected, next timestep=infns. Outputs restored.
10000.000ns 27 main called from Qspice? (n=33)
=> outputs determined for t=10000.000ns
27 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=28)
10020.000ns 27 Trunc called from Qspice? (n=35)
28 main called from Trunc (n=35)
out is HIGH, but would be LOW next step
trunc: STATE CHANGE DETECTED, next timestep=10.0ns. Outputs restored.
10010.000ns 27 Trunc called from Qspice? (n=36)
28 main called from Trunc (n=36)
out is HIGH, but would be LOW next step
trunc: STATE CHANGE DETECTED, next timestep=10.0ns. Outputs restored.
10010.000ns 28 main called from Qspice? (n=34)
=> outputs determined for t=10010.000ns
out is LOW
28 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=29)
10020.000ns 28 Trunc called from Qspice? (n=37)
29 main called from Trunc (n=37)
trunc: no state change detected, next timestep=infns. Outputs restored.
10020.000ns 29 main called from Qspice? (n=35)
=> outputs determined for t=10020.000ns
29 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=30)
10040.000ns 29 Trunc called from Qspice? (n=38)
30 main called from Trunc (n=38)
trunc: no state change detected, next timestep=infns. Outputs restored.
10040.000ns 30 main called from Qspice? (n=36)
=> outputs determined for t=10040.000ns
30 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=31)
10080.000ns 30 Trunc called from Qspice? (n=39)
31 main called from Trunc (n=39)
trunc: no state change detected, next timestep=infns. Outputs restored.
10080.000ns 31 main called from Qspice? (n=37)
=> outputs determined for t=10080.000ns
31 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=32)
10160.000ns 31 Trunc called from Qspice? (n=40)
32 main called from Trunc (n=40)
trunc: no state change detected, next timestep=infns. Outputs restored.
10160.000ns 32 main called from Qspice? (n=38)
=> outputs determined for t=10160.000ns
32 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=33)
10320.000ns 32 Trunc called from Qspice? (n=41)
33 main called from Trunc (n=41)
trunc: no state change detected, next timestep=infns. Outputs restored.
10320.000ns 33 main called from Qspice? (n=39)
=> outputs determined for t=10320.000ns
33 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=34)
10640.000ns 33 Trunc called from Qspice? (n=42)
34 main called from Trunc (n=42)
trunc: no state change detected, next timestep=infns. Outputs restored.
10640.000ns 34 main called from Qspice? (n=40)
=> outputs determined for t=10640.000ns
34 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=35)
11280.000ns 34 Trunc called from Qspice? (n=43)
35 main called from Trunc (n=43)
trunc: no state change detected, next timestep=infns. Outputs restored.
11280.000ns 35 main called from Qspice? (n=41)
=> outputs determined for t=11280.000ns
35 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=36)
12280.000ns 35 Trunc called from Qspice? (n=44)
36 main called from Trunc (n=44)
trunc: no state change detected, next timestep=infns. Outputs restored.
12280.000ns 36 main called from Qspice? (n=42)
=> outputs determined for t=12280.000ns
36 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=37)
13280.000ns 36 Trunc called from Qspice? (n=45)
37 main called from Trunc (n=45)
trunc: no state change detected, next timestep=infns. Outputs restored.
13280.000ns 37 main called from Qspice? (n=43)
=> outputs determined for t=13280.000ns
37 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=38)
14280.000ns 37 Trunc called from Qspice? (n=46)
38 main called from Trunc (n=46)
trunc: no state change detected, next timestep=infns. Outputs restored.
14280.000ns 38 main called from Qspice? (n=44)
=> outputs determined for t=14280.000ns
38 MaxExtStepSize called from Qspice?
=> returns max_timestep=1e-6 (n=39)
15280.000ns 38 Trunc called from Qspice? (n=47)
39 main called from Trunc (n=47)
out is LOW, but would be HIGH next step
trunc: STATE CHANGE DETECTED, next timestep=10.0ns. Outputs restored.
...
So what I get is something like the following, starting at:
time4 = 4000ns, outpus are already calculated
- QSPICE calls MaxExtStepSize, this will limit the timestep to the specified 1000ns, if it is bigger.
- QSPICE calls the trunc function (
void Trunc(...)
)
- if timestep is bigger than a specified timestep of 10ns (which seems to be always the case?, timestep=inf)
3a. trunc saves the outputs and calls the main function (void ctest_x1(...)
) with a temporary struct with time5= time4 + 1000ns = 5000ns
3b. no state change is detected, temporary data is discarded
- QSPICE calls the main function a second time (seems unnecessary?) and the results will end up as a sample point for time5 = 5000ns ?
so for one sample point the main funcition gets called 2 times.
time5 = 5000ns
- like 1.
- like 2.
- like 3.
8a. like 3a. but with time6 = time5 + 1000ns = 6000ns
8b. like 3b, but now a change of the specified variable inst->state
is detected, temporary data is discarded, next timestep will be only 10ns
- like 4. but with time6 = time5 + 10ns = 5010ns, so calculations are repeated for an earlier time point. Again a change of the state is detected.
- like 5. but again with time6 = time5 + 10ns = 5010ns (seems unnecessary?)
time6=5010ns
for the next steps the timestep will get doubled until it gets limited to 1000ns again (if not reduced again in the meantime)
- So is this how it works?
- Does it always work like that?
- Any further advice/info besides: just look at the sparsely commented example?
- while the edges are with good precision, it seems to me that the main is called more often than it needs to be (or I don’t understand the reason).
- and it is not working very well for the falling edge, i.e. many ununnecessary calculations are made before. (this has nothing to do with the falling edge itself, but just with how the timeings are)
edit: updated code, output and text to make it clearer