C-Block Calling Bugs?

Hi, guys.

I’m trying to understand when the three C-Block code functions are called. I set up a basic C-Block component (“TestCmp”) and added display() statements to display the calls. See attachments. I get curious results:

  • testcmp() is called six times with t=0
  • MaxExtStepSize() is called
  • testcmp() is called with t=9.765625e-06
  • MaxExtStepSize() is called
  • Trunc() is called twice with t=1.953125e-05 and *timestep=infinity (I never assign a value so t is expected)
  • testcmp() is called once with t=1.953125e-05

The last three are then repeated with t incrementing. I assume that I’m missing something or maybe have a bug that I just don’t see. Does QSpice expect the code to respond differently when called repeatedly with the same parameter values?


I know that others have asked for an explanation for these functions and were pointed to the sample code (which I have reviewed). I respectfully submit that the sample code doesn’t really explain when/why/in what order the functions are called. I would very much appreciate an explanation rather than continuing this step-wise journey in self-discovery.


Next issue.

The code itself simply sets Out = 1.0 + (t * 10.0). The second data point in the waveform viewer is identical to the first which seems like a bug.

image

Note that the waveform window that opens with File | Export Data doesn’t seem to have the extra leading datapoint.


FYI, I updated QSpice earlier today so I should be current.

TIA

– robert

temp.txt (5.6 KB)
testcmp.cpp.txt (3.4 KB)

1 Like

OK, I think that I’m starting to get a handle on the three functions. I’d rather have a clear authoritative explanation but I’ll post what I think that I understand once, well, I think that I understand just in case I’m not the only lost soul.

In the meantime, the repeated calls with unchanged parameters seem unnecessary. Perhaps I’m missing something.

– robert

Hello everyone

I would also like to know how to make a function be called exactly once, before the actual simulation starts, to calculate parameters. As far as I’m concerned, even a simple check for t<0 (assuming a -1 “at rest”) would do.

Vlad

Hi, Vlad.

Maybe I misunderstand the question. For a component named “xxx”, the generated code would look like this:

struct sXXX {
// declare the structure here
[ per-instance data goes here ]
};

extern “C” __declspec(dllexport) void xxx (struct sXXX **opaque, double t, union uData *data) {

if (!*opaque) {
*opaque = (struct sXXX *)malloc(sizeof(struct sXXX));
bzero(*opaque, sizeof(struct sXXX));

[ put code here to initialize values in the per-instance sXXX structure ]

}
struct sXXX *inst = *opaque;

// Implement module evaluation code here:
}

Is that what you’re looking for?

Edit: To be clear, you must put something in the per-instance structure (i.e., the structure isn’t empty) even if it is never used. Otherwise, the allocation will (I think) return a null pointer to the per-instance memory which is how we detect that the instance is already allocated/initialized.

–robert

Hello Robert

What I have is a struct with equivalent .params and these are used with various custom functions in order to be calculated. But these functions are called – as you saw – many times over, in the body of the main function. So I was wondering how to make those functions be called only once, similar to how .param are treated in SPICE: only once, before the simulation starts. I can’t use .param because this is the sort of thing where C/C++ would be a million times better, or even possible. I hope I’ve made myself a bit less obscure.

Vlad

@archbugaboo

Sorry, Vlad, I’m lost. You say, “as you saw” – well, there were no attachments or links to anything that explain your problem further in the post that I responded to. Perhaps I missed something. If not, maybe share some code/links to explain the issue better?

–robert

The code snippet in Robert’s answer shows the exact point where the initalizations should be placed. Your need for code initalization is understandable.

I tried to lobby for a predefined Init function in the code block what could call only once by QSPICE right before the start of simulation and the user could place the initialization code there. It would be the clearest situation.

@RDunn

I was referring to your showing that testcmp() was called 6 times at t=0 (and at each timestep, really).

@WinterMime

Hello. Currently I managed to circumvent that by adding a dummy parameter, int callMeOnce, which is incremented by a function, onlyOnceDammit(), called right after the I/O and parameters definitions in the main function (... = data[n].x). Then I encompassed everything related to .param evaluation within a single if(s.callMeOnce == 1). Seems to work, and an int should hold enough points to avoid cycling back.

Vlad

Could you show the related parts of your solution? The using of the .param statement sounds interesting.

Earlier I have used similar solution as yours:

if (!s.initHappened)
{
    s.initHappened = 1;
    InitFunction();
}

The initHappened variable was cleared by bzero function. But why do you increment it? At this solution no incrementation is needed of this flag variable.

After this I dropped the initHappened variable and I simpliy put the InitFunction to the place what Robert showed and it worked well.

Hello WinterMime

There’s really nothing special: a struct holding all the required parameters (I spelled these as .params in my other replies – if this was a source of confusion, my apologies, just used to refer to them whenever I am in the SPICE world) and helper functions outside the main function. Then it’s very similar to what you’re showing (so, just another way of doing it):

static Params s;
++s.callMeOnce;
if(s.callMeOnce == 1)
{
   // helper functions for setting up .params
   // called only once
}
// "running" code goes here

I can’t know what’s going on under the hood but, the fact that the main function is called several times when t=0 is probably just the way it’s handled by Qspice (BTW: is it Qspice, QSPICE, …?), and t=0 is just showing that t is still at its default value.

Vlad

1 Like

It is clear now, thank you for clarification, Vlad.