Thank you for your suggestion. However, in the case of stepping, this will not work.
You recommendation uses the fact that when this C-block is first called, *opaque == 0. Then the instance memory is allocated and then zeroed out giving the inst->state parameter a 0 setting.
Your recommendation works for non-stepped sims because *opaque = 0 happens only once.
In stepped sims, *opaque = 0 happens at the beginning of each step executed. Therefore, I still cannot detect the first step of the sim. From what I can tell, there is no variable that is or can be made persistent across steps.
I wonder if we could put together a full API somewhere for the QSpice code system. Would be nice to have a good reference for more complex issues like this.
I’m interested in contributing public IP. I think there are others who are contributing.
In general, I find a PSpice tool very useful but can be limited to basic building components (mostly because it started as an analog simulator). Most sophisticated models can be created using the basic building components but can be very complex to create, modify or reverse-engineer to find unpredicted issues.
C-blocks allow for easy and elaborate creation of extremely sophisticated models. It also allow for easy access to computer system resources where .subcir models don’t provide that easily or at all. I’ve created C-blocks to access the filesystem and perform string parsing.
Suggestion:
Propose a common version controlled repository (such as GitHub) that others can access with some rules for submission.
That’s a good idea and I’d be happy to set something like that up. I expect I’ll have to find some free time to learn quite a bit before I get it started, but when I do, I’ll post a new thread here. I’m definitely not a “software guy.”
In my opinion, you have options to put the global_count in Destroy() or in if(!opaque){}. Both of them are only called once throughout the whole transient simulation.
An direct way to let the .DLL to know the step number it to tell it with a parameter:
The problem with using global data is that it is shared between all instances that call that .DLL from the simulation. That’s why the template generator offers to declare a structure for you to store per-instance data.
Well, this isn’t exactly true. A generic DLL component should anticipate multiple schematic component instances. Try putting two instances of the C-Block on a schematic and both get called twice.
To solve that, you’ll need to implement reference counting in the instance structure and only increment the global step counter when allocating the first instance. And for that, you’ll need to allocate/delete the structure instances using new/delete (not malloc/free).
I’ll try to post some project code to demonstrate this later today.
OK, I’ve posted code for a reliable way for a C-Block to determine the current simulation step (even with multiple schematic instances) on the dev branch of my GitHub repo here.
Mike is correct that a dll global variable persists across steps. He is also correct that the global variable occurs only once in the same component. Therefore, if you have multiple instances of the same component in the sim, this global var is shared across instances.
I believe I solved the latest issue in my particular situation.
// Implement module evaluation code here:
if(t == 0)
{ // Make sure we’re at the beginning of the sim/step.
if(started == false)
{ // Has not started yet
… Code to initialize at the first step
}
}
else
{ // t != 0
started = true; // signal the sim has started. This prevents reinitialization
}
… More code …
}
This method allows all instances of the same component to go through the initialization phase at t=0.
Once t no longer = 0 then the started var is set to true.
Depending upon what you’re doing, there could be an issue with the code. Unless something changed in recent releases, each component instance is called multiple times with t==0 before being called with t>0. I believe the above will execute “Code to initialize at the first step” something like six times for each instance.
If you want some initialization to occur exactly once for each instance at the beginning of the sim/step, put that code in the instance initialization (right after the bzero() call).
Of course, it all depends on your use case. Just something to be aware of.
You are correct. Once a sim step is started, t=0 occurs 5 to 6 times
In the code frag I listed above, the fopen(filename,“w”) occurs every time t=0 and when started = false. Therefore the file gets opened in write mode multiple times. Redundant. True.
Let me try your suggestion of doing file initialization in the instance initialization.
Len
Update: It does appear to work. Potentially less fopen()s.
Len, if you’re comfortable with C++ code, you might look at the code I posted yesterday on the dev branch of my GitHub repo.
FWIW, I’m in the process of writing a tutorial that might also help but I don’t expect to complete it for a couple of days. However, it will also assume an understanding of C++ so it may or may not help depending on your skill set.
I’ll be very glad to look at your code example. Although I may wait for your tutorial first.
I’ve coded in embedded C for ‘eons’ (30+ years in computer-relative terms) but have little direct experience in C++ (which supports the C-subset). Most embedded systems compilers haven’t advanced to C++.
I’ve gotten my C-based solution to work well but it’s always good to learn new things any possibly find better solutions.
I’ve developed both C & C++ code to reliably handle multiple component instances, multiple simulation steps, and shared resources (such as a data logging file). The C code version is heavily commented and, I hope, is clear.
What the code doesn’t explain is exactly why QSpice requires this approach. I’ve been working on a tutorial-style PDF but, well, code is easy, coherent explanations are hard. Maybe I’ll get that finished tomorrow.