Best way to get delta time in Verilog-A?

585 views
Skip to first unread message

Tim Molter

unread,
Dec 8, 2016, 7:45:57 AM12/8/16
to xyce-users
For my Joglekar memristor simulation I'm working on, I need to know the time elapsed since the last simulated time step. I hacked a way by storing the last update time and subtracting it from the current time using `$realtime`. Is there a more of a direct way to get this such as something like `$deltaT` or anything? Is the way I'm doing it acceptable?

I also have a few more quick questions that would be nice to have answered. I've attached some relevant files: the memristor model and the simulation file.

1. If I don't set `time_last` in the `@(initial_instance)` block, it always resets to 0 for each time step. Why is that?
2. In`@(initial_instance)` I cannot write `time_last = 0;`. Compiling gives the error: cannot convert 'Sacado::Fad::Expr<Sacado::Fad::SubtractionOp<Sacado::Fad::ConstExpr<double>, Sacado::Fad::Expr<Sacado::Fad::SFadExprTag<double, 1>, Sacado::Fad::ExprSpecDefault> >, Sacado::Fad::ExprSpecDefault>' to 'double' in assignment
 time_delta = (time-instanceVar_time_last). So to get around it I set it via `time_last = D * 0;`, using D just so it won't throw the compile error. What am I doing wrong or is that a weird bug?

Thanks in advance!

Memristor_I-V.cir
memristor1.va

xyce-users

unread,
Dec 8, 2016, 11:21:41 AM12/8/16
to xyce-users


On Thursday, December 8, 2016 at 5:45:57 AM UTC-7, Tim Molter wrote:
For my Joglekar memristor simulation I'm working on, I need to know the time elapsed since the last simulated time step. I hacked a way by storing the last update time and subtracting it from the current time using `$realtime`. Is there a more of a direct way to get this such as something like `$deltaT` or anything? Is the way I'm doing it acceptable?


There is no "best" way to get this done, and the way you're doing it is about as good as it gets. 

There is a deeper issue related to creating models that depend on the simulation time --- not only do these often lead to non-physical models that can have convergence issues when put into a simulator like Xyce, but using the simulation time precludes performing other types of analysis such as small-signal AC or Harmonic Balance.

A good discussion of the pitfalls of modeling memristors this way can be found in  https://arxiv.org/abs/1605.04897.  This paper also has some good suggestions for writing Verilog-A models for memristors that avoid those pitfalls.

That said, your method for getting at the time difference since the last call is something that could work.

I also have a few more quick questions that would be nice to have answered. I've attached some relevant files: the memristor model and the simulation file.

1. If I don't set `time_last` in the `@(initial_instance)` block, it always resets to 0 for each time step. Why is that?

It's because when you don't set a variable in the @(initial_instance) block, it becomes a local variable to the "updateIntermediateVars" function in Xyce.  That means it's just a temporary that is reset every time that function is called.  By setting the variable in the @(initial_instance) block, it becomes a variable that is kept around, so that it can  be shared between two different methods of the instance class of your model.

Generally, the @(initial_instance) block is meant to be used to set variables that are bias-independent  functions of model or instance parameters.  By setting them in @(initial_instance) the computation is done once, at the time the device is created, rather than repeatedly every time the model is evaluated.  The stuff in @(initial_instance) is only called again if parameters are changed during the run, such as via a ".STEP" analysis statement.

2. In`@(initial_instance)` I cannot write `time_last = 0;`. Compiling gives the error: cannot convert 'Sacado::Fad::Expr<Sacado::Fad::SubtractionOp<Sacado::Fad::ConstExpr<double>, Sacado::Fad::Expr<Sacado::Fad::SFadExprTag<double, 1>, Sacado::Fad::ExprSpecDefault> >, Sacado::Fad::ExprSpecDefault>' to 'double' in assignment
 time_delta = (time-instanceVar_time_last). So to get around it I set it via `time_last = D * 0;`, using D just so it won't throw the compile error. What am I doing wrong or is that a weird bug?


This is basically a weird bug in the code generation for sensitivities, because your use case is an unusual one that is not properly being handled.  When you specify "last_time=0" ADMS knows that your time_last variable is independent of any model parameter, and because of the mishandling of one special case, the resulting code generated is wrong.  If you were using "--disable-adms_sensitivities" then that code wouldn't even be getting compiled.  So you can either use your workaround, or make sure that you are disabling compilation of the sensitivity code.



Thanks in advance!

Tim Molter

unread,
Dec 20, 2016, 7:06:30 AM12/20/16
to xyce-users, xyce-...@googlegroups.com
Using the `--disable-adms_sensitivities` option did the trick.

Thanks for the link to the well-posed model paper. There's some very good info there.

Marcel Hendrix

unread,
Dec 21, 2016, 5:18:36 AM12/21/16
to xyce-users
Tim wrote:
> I need to know the time elapsed since the last simulated time step.
> I hacked a way by storing the last update time and subtracting it
> from the current time using `$realtime`. Is there a more of a direct
> way to get this such as something like `$deltaT` or anything?
> Is the way I'm doing it acceptable?

This assumes that time advances linearly. Did you consider the
possibility that some models do not converge during the present
time step? In that case the simulator will restart after the last successful
step. Unless you do something special you will then see occasional "future
times" from your stored variable.

E.g. XSPICE gets around this by providing managed storage (STATIC_VAR)
for these type of variables (not simply "static"). It could be that this is transparent
in AMS.

BTW, In XSPICE you can simply use TIME T(1) T(2) ... etc. to get all past
time steps.

-marcel

xyce-users

unread,
Dec 21, 2016, 12:42:11 PM12/21/16
to xyce-users
Marcel raises an important point, and this point is one of the things I eluded to when I said "there is no best way" to do this from Verilog-A in Xyce, and one of the reasons I pointed you at the Wang/Roychowdhury paper (https://arxiv.org/abs/1605.04897) in the hopes that you might try reformulating your model to be well-posed and without memory states instead.  By applying the guidance in that paper, you'd avoid this issue entirely and get a model that is robust and applicable to all analysis types rather than being tied to transient analysis.

Xyce could, in fact, take multiple nonlinear solver steps in the course of a single time step.  Each solver step will call the main analog block of your model, each of which would get the same value from "$realtime".  Since your analog block saves the previous time value at the end of the block, that means your deltat  values for all nonlinear solver steps other than the first step of a timestep will be zero.  So in fact, there is not only not a "best" way to do this from Verilog-A, there isn't even really a good way.

While we continue to assert that the best approach for you is to use a different formulation of your model, since you have decided to go ahead and code your model in C++ directly, there is a way to handle this issue using the Xyce device package capabilities.

Rather than saving "time_last" at the end of the "updateIntermediateVars" block (which is where Xyce/ADMS would put all of the code it generates from your Verilog-A main analog block, and which is called for each nonlinear solve step), you can instead save this state in the "acceptStep" method of the model. acceptStep is only called after a time step is accepted by the time integrator, so it is only called once per time step, and only when that step succeeds.  This is the place to save the current time into time_last.

Very few devices in Xyce use this method, because only a few devices store time-dependent state like this (the lossless transmission line uses it, and the code may be found in src/DeviceModelPKG/OpenModels/src/N_DEV_TRA.C and its associated header file).  acceptStep is defined in the Device::Instance base class as a no-op virtual method, but specific devices can override the base class method and cause it to do something.

So in your case, should you choose to implement your existing model as-is in C++ without reformulating it, you would declare time_last as a variable in the instance class, initialize it to zero in the Instance::processParams function (where Xyce/ADMS would have put your @(initial_instance) code), use time_last without resetting it in updateIntermediateVars, and implement acceptStep in your device model.  Save the current time into time_last in your models acceptStep method.

There is no way in Xyce/ADMS to specify that some computations should be performed in acceptStep rather than in updateIntermediateVars (or even a way of specifying it in Verilog-A, which is deliberately designed to hide simulator details like this from the model developer), so this sort of thing can't be handled well from Verilog-A models. 
Reply all
Reply to author
Forward
0 new messages