Functional Modeling
One can broadly classify the models written in Verilog-A into two categories: device models and functional models. Device models generally try to capture the detailed behavior of some physical device. There are many examples of device models such as resistors, capacitors, diodes, transistors, motors, etc. Device models are generally combined into a circuit and simulated. Functional models are higher level models; models of the circuit themselves. For example, one might contain tens to hundreds of device models into a circuit that represents an amplifier. Then, to allow efficient simulation of systems that contain that amplifier, one might create a more abstract model that represents the amplifier. This model is a functional model. It represents the inputs and outputs in rather ideal ways and does not contain the internal structure constructed from device models.
The abstract nature of functional models brings new requirements on the model itself. There are certain things that the simulator needs to know in order to render the behavior of the model efficiently and accurately beyond the basic input-output relationship. In this case, the model must include hints that the simulator can use to adapt its behavior to the needs of the model. For example, functional models often exhibit discontinuous behavior, and the simulator needs the hints to identify the discontinuity and deal with it efficiently. Consider a digital inverter. In the simplest model of an inverter there is a threshold voltage at the input. When the input voltage is below the threshold the output is high, and when it is above the threshold the output is low. One could write that as follows:
Warning
This is an example of a bad model. It is discontinuous and does not accurately resolve the time of the threshold crossing. Do not use it.
module inverter(out, in): output out; input in; electrical out, in; analog begin if (V(in) > 0.5*V(vdd)) V(out) = 0; else V(out) = V(vdd); end endmodule
This is a very simple description of an inverter model. It has, however, two problems related to the discontinuous nature of the model. The first is that the behavior of the model changes abruptly when the input voltage crosses a threshold, and the simulator is unaware of the threshold and so can do little to control the simulation time steps to accurately resolve the crossing. Second, the output voltage changes discontinuously, which can cause substantial convergence and accuracy problems.
Resolving the Threshold
The model as given changes its behavior abruptly as the input voltage crosses
the threshold of 0.5*V(vdd)
, however the simulator is unaware of this
threshold. As such, the simulator can make no attempt to resolve the time of
the threshold crossing. Instead the output changes at the first time point
after the threshold is crossed. When that occurs depends on the time step the
simulator has chosen. This represents a problem. A significant aspect of the
behavior is dependent on the simulator’s time step. Since time steps exist only
in a simulation and not in the real world, the resulting behavior of the model
is non-physical. In effect, it is as if a substantial and seemingly random
amount of delay has been added to the behavior of the inverter.
The way to avoid this problem is to employ an event statement that contains a cross function:
Warning
This is an example of a bad model. It is discontinuous. Do not use it.
module inverter(out, in): output out; input in; electrical out, in; analog begin @(cross(V(in) - 0.5*V(vdd))) ; if (V(in) > 0.5*V(vdd)) V(out) <+ 0; else V(out) <+ V(vdd); end endmodule
Notice that the statement associated with the event statement (the statement that is executed when the event occurs) is empty. In Verilog-A, event statements do two things. They evaluate an event expression that detects events and acts to control the simulator’s choice of time points; and they execute the statement that immediately follows them, but only when a event occur. In this case, the cross function forces the simulator to place a time point close to, but just after, the threshold crossing. This is the desired effect of the event statement; nothing else is needed so an empty statement follows the event statement.
It is tempting to include the if statement in the event statement:
Warning
This is an example of a bad model. It is discontinuous and its value can be wrong at the beginning of the simulation. Do not use it.
module inverter(out, in): output out; input in; electrical out, in; integer d_out; analog begin @(cross(V(in) - 0.5*V(vdd))) if (V(in) > 0.5*V(vdd)) d_out = 0; else d_out = 1; V(out) <+ d_out*V(vdd); end endmodule
Notice that the contribution was moved out of the event statement. This is because event statements are only evaluated at discrete points in time, and contributions must be made continuously. In fact, it generally considered poor style to have contributions in if statements unless it is essential to the behavior of the model. It avoids the possibility that a contribution shows up in one side of the side of the if statement and not the other, which might inadvertently create a switch branch, which can cause simulation problems.
This version of the model is problematic in that the value of d_out is simply left at its initial value until the first threshold crossing. In Verilog-A variables are initialized to 0, so the output of the inverter will be zero regardless of the input value until the first time the input crosses the threshold. Thus, this version of the model should be avoided.
Smoothing the Output
The second issue with our first attempt to model an inverter was that the output jumped discontinuously between low and high values. That issue is resolved with the use of a transition function:
module inverter(out, in):
output out;
input in;
electrical out, in;
integer d_out;
analog begin
@(cross(V(in) - 0.5*V(vdd)))
;
if (V(in) > 0.5*V(vdd))
d_out = 0;
else
d_out = 1;
V(out) <+ transition(d_out, 0, 10n)*V(vdd);
end
endmodule
The transition takes three or four arguments. The first is the signal to be
smoothed. It is expected to be piecewise constant. This is an important
restriction. If you were to pass value that changed continuously as the first
argument, it conceptually generates a continuous stream of transitions, which
would slow the simulator and generate a ratty looking smoothed output. Ideally,
the simulator would prevent you from passing a continuous-varying signal as the
first argument. You cannot count on that. This restriction is why V(vdd)
is placed outside the transition function. This is important even if you
believe that vdd is constant because to the simulator vdd is an electrical
node and as such has a value that is computed to within the simulator’s
tolerances. Thus even a ‘constant’ supply voltage may include microscopic
variations that would be picked up by the transition function. See
Modeling Multiplexers for another example of how continuous and discrete behavior
can be combined in an analog block.
The second argument to the transition function is a time delay. Generally you want to keep this a zero unless modeling the delay is important. The reason being that specifying a non-zero delay will force an extra time step to resolve the beginning of the transition, and so would slow the simulator to some degree. Extremely short delay times are especially problematic.
If four arguments are passed, the last two are the rise and fall time of the transition. If only three arguments are passed, the last is the time of both rising and falling transitions. The transition time should always be non-zero and should be as large as you are comfortable making them.
Short delays and transition times require small time steps in order to render the delay or transition. This can be problematic as the simulator cannot recover immediately from a small time step; it must grow the time step slowly. Consider a 1 MHz clock that is rendered with a 1ps delay or transition time. If 1 MHz is the highest frequency in the circuit, then time steps needed by the simulation probably fall in the 10-100 ns range. However, when a clock transition occurs, the simulator needs a time step no greater than 1 ps. Then it will have to grow the step slowly back to the 10 ns range (1 ps, 2 ps, 4 ps, …). The result will be needlessly many extra time points, perhaps 10-20 needless time points per clock transition. Better to set the delay time to 0 and the transition time to 1 ns, or even better 10 ns. It may result in signals that are somewhat unrepresentative for your circuit, but if the those delays and transition times are not important to the behavior of your circuit, the zero delays and longer transition times will reward you with a faster simulation without degrading the accuracy of the result.
Simplified Model
The model can be shortened a bit by replacing the if statement with a simple comparison operation. Comparison operations return 1 if true and 0 if false:
module inverter(out, in):
output out;
input in;
electrical out, in;
analog begin
@(cross(V(in) - 0.5*V(vdd)))
;
V(out) <+ transition(V(in) < 0.5*V(vdd), 0, 10n)*V(vdd);
end
endmodule
Notice that the comparison operation was switched from >
to <
in order
to implement the inversion. It is also possible to do it as follows:
module inverter(out, in):
output out;
input in;
electrical out, in;
integer d_in;
analog begin
@(cross(V(in) - 0.5*V(vdd)))
;
d_in = V(in) > 0.5*V(vdd);
V(out) <+ transition(!d_in, 0, 10n)*V(vdd);
end
endmodule
The model is slightly preferred to the previous model as it is a bit more obvious what is intended.
However, there is a user trap to be avoided here. Consider the following version:
Warning
This is an example of a bad model. It generates extremely large output voltages. Do not use it.
module inverter(out, in): output out; input in; electrical out, in; integer d_in; analog begin @(cross(V(in) - 0.5*V(vdd))) ; d_in = V(in) > 0.5*V(vdd); V(out) <+ transition(~d_in, 0, 10n)*V(vdd); end endmodule
In this version the logical inversion operator was replaced by the bitwise inversion operator. The value of the logical inversion operator is either 0 or 1, but the bitwise inversion operator returns the value of its argument with all bits inverted. Here the argument is a 32-bit integer. So in this case the value returned by the bitwise inversion operator is either ‘hFFFF_FFFE ‘hFFFF_FFFF. In decimal numbers, that is either 4,294,967,294 or 4,294,967,295. Thus, the output of this model is 4.3 GV × V(vdd) regardless of the value of d_in.
Best Model
Given these rules, as well as a desire to make the behavior of the model as obvious as possible, the following represents the best of the above models:
module inverter(out, in):
output out;
input in;
electrical out, in;
integer d_in;
analog begin
@(cross(V(in) - 0.5*V(vdd)))
;
d_in = V(in) > 0.5*V(vdd);
V(out) <+ transition(!d_in, 0, 10n)*V(vdd);
end
endmodule
Digital Control of Analog Behavior
This is another situation that comes up in mixed-signal models that requires that the model provide hints to the simulator as before. Consider the following charge pump model:
module cp (out, u, d):
output out; electrical out;
input u, d; logic u, d;
analog begin
@(u or d)
;
I(out) <+ 10u*(transition(d, 0, 1n) - transition(u, 0, 1n));
end
The charge pump is expected to deliver charge to the output in proportion to to the amount of time either the u or d inputs are high, and the charge should be positive if the u input is high and negative if the d input is high.
There are two things to notice about this model. First is the presence of the event statement. It assures that the analog kernel places a time point at the instant when either input changes value. Second, transition functions are used to control the transition times of the output pulses. Both are very important for this model. Lack of time point control would result in high level of jitter in the output. Lack of transition functions would result in uncontrolled transition times, with the result being either much more or much less charge being delivered than is expected. This type of charge pump is used in phase-locked loops, and a significant amount of error in the charge delivered can dramatically alter the effective gain the loop, which can cause the loop to become unstable.
Unfortunately, the Cadence simulator places unreasonable restrictions on event
expressions in the analog block. Specifically, any digital signals used in an
analog event expression must be preceded by either the posedge
or
negedge
qualifiers. Thus, the model must be modified when intended for the
Cadence simulator:
module cp (out, u, d):
output out; electrical out;
input u, d; logic u, d;
reg sync = 0;
always @(u or d)
sync <= !sync;
analog begin
@(posedge sync or negedge sync)
;
I(out) <+ 10u*(transition(d, 0, 1n) - transition(u, 0, 1n));
end
With only two values, u
and d
, it is not difficult to add the required
posedge
and negedge
to the event expression. However, with more than
two values it can become tedious. Also, posedge
and negedge
cannot be
applied to buses or real values. Instead, one must follow this pattern is used
where the event expression is implemented outside the analog block and
a register variable is toggled to trigger the event statement within the analog
block.
General Rules
This inverter and charge pump examples illustrates three important rules that must always be kept in mind when creating functional models in Verilog-A or Veriog-AMS:
Thresholds require a event statement that contains a cross function so that the time of the threshold crossing is accurately resolved.
Output with discrete levels require transition functions to provide smooth transitions between the levels.
Digital control of analog behavior requires an analog event statement to synchronize the kernels when the controlling behavior changes.
These rules hold true for both Verilog-A and Verilog-AMS.