You are on page 1of 36

How to write an optimum Verilog-A model

Peter Grove

Dialog Semiconductor
Edinburgh, United Kingdom

www.diasemi.com

ABSTRACT

In general Verilog-A has not been used to its full potential by analogue designers in
simulations. Some of this is down to the language support level in the various tools versus the
language reference manual. However, the main part is down to designers not having the correct
skill-set or training to write Verilog-A code in a style that makes it run fast without convergence
issues. Due to this, often block models are drawn schematics made of ideal components, which
has its own set of issues. This paper explains how to write Verilog-A models in a way that run
fast and allows the block to be modelled without ideal components. It also demonstrates how
simple Verilog-A checkers can be used to check top level hook-up. This technique can be applied
on the analogue part of Verilog-A/MS files.
Table of Contents
1 Introduction .............................................................................................................................. 4
2 Generic Structure...................................................................................................................... 4
2.1 ANALOGUE SIMULATOR INTERNALS................................................................................... 5
2.2 MONITORED EVENTS VS. CONTINUOUS TIME ...................................................................... 6
2.2.1 cross/above alternative ............................................................................................... 7
2.2.2 timer alternative .......................................................................................................... 8
3 Supporting Modules/Files ........................................................................................................ 9
3.1 MESSAGING SYSTEM .......................................................................................................... 9
3.2 VOLTAGE/CURRENT CHECKERS ....................................................................................... 10
3.3 DIGITAL INPUT CHECKER ................................................................................................. 13
3.4 GENERIC FUNCTIONS ........................................................................................................ 16
3.4.1 Smoothing Limiter ..................................................................................................... 16
3.4.2 Logical Comparators ................................................................................................ 18
3.4.3 Range Checkers ........................................................................................................ 19
3.4.4 Control Messages...................................................................................................... 20
4 Electrical Drivers.................................................................................................................... 21
4.1 OUTPUT DRIVER ............................................................................................................... 21
4.2 CURRENT OUTPUTS .......................................................................................................... 22
4.3 VOLTAGE OUTPUTS .......................................................................................................... 22
5 Electrical Receivers ................................................................................................................ 23
5.1 VOLTAGE INPUTS ............................................................................................................. 23
5.2 CURRENT INPUTS .............................................................................................................. 24
6 Model internals ....................................................................................................................... 25
6.1 SUPPLIED SIGNAL ............................................................................................................. 25
6.2 LOGIC CONTROLS ............................................................................................................. 25
6.3 FILTERS ............................................................................................................................ 26
6.4 STATIC CURRENTS ............................................................................................................ 29
7 Other tips ................................................................................................................................ 29
7.1 SWITCHES ......................................................................................................................... 29
7.2 COMMON SUB EXPRESSION ............................................................................................... 29
7.3 EXPRESSIONS .................................................................................................................... 30
7.4 MULTIPLE ANALOG BLOCKS ............................................................................................. 30
7.5 PARAMETER RANGES........................................................................................................ 30
7.6 TRIMS ............................................................................................................................... 30
7.7 VARIABLE CAPACITORS.................................................................................................... 30
8 Conclusions ............................................................................................................................ 31
9 References .............................................................................................................................. 31
10 Appendices .......................................................................................................................... 32
10.1 VOLTAGE/CURRENT CHECKER CODE ............................................................................. 32
10.2 DIGITAL INPUT CHECKER CODE ..................................................................................... 34

SNUG 2014 2 How to Write an optimum Verilog-A model


Table of Figures
Figure 1 Overview of a Verilog-A model 5
Figure 2 cross/above timing of event relative to threshold[1] 7
Figure 3 if statement version of cross/above event 8
Figure 4 if statement version of timer event 8
Figure 5 Voltage/Current operating regions 10
Figure 6 Internal models of checkers. 11
Figure 7 Digital Input Threshold levels 13
Figure 8 Input model of Digital Input Checker 15
Figure 9 Power Limiter response for different n values (MAX=1) 17
Figure 10 Generic Output Driver Stage 21
Figure 11 Current outputs 22
Figure 12 Voltage outputs 23
Figure 13 Voltage input model structure 24
Figure 14 Current input model structure 24
Figure 15 Example filter network. 28
Figure 16 Filter feedback limiting 29

Table of Tables
Table 1 Suggested port list for Voltage/Current checker.............................................................. 12
Table 2 Suggested Instance parameters for Voltage/Current checker .......................................... 12

SNUG 2014 3 How to Write an optimum Verilog-A model


1 Introduction
SoC’s are becoming more complex and trying to simulate the whole design at transistor level
is not always possible. Even at block level sometimes the time for the simulation to run is
dominated not by the block under test (DUT) but by an auxiliary block required for simulation.
To speed up these auxiliary blocks, they can be swapped for other models. There are many
methods for creating these models, such as ideal components drawn on a schematic, SPICE or
Spectre® models. However, they all have their own issues and limitations: not least of which is
the languages SPICE/Spectre® have not been an open standard.
Verilog-A was introduced to overcome the language limitation and to allow mixed mode
simulations in a single, open standard language such as Verilog-A/MS. It also tried to address the
issue that there were 2 main PDK modelling methods, by having a common language only one
would need to be supported by the vendors. The other advantage was to remove the requirement
for the simulators to have the base MOS model set compiled in, by compiling a Verilog-A MOS
model written by the foundry. However what it has allowed is designers to write Verilog-A code
that causes convergence issues, because they have not understood how the code they write is
used by the simulator. Discontinuity is now a major issue for the tools with Verilog-A, which
weren’t present beforehand due to the controlled set of base components in tools, such as piece
wise linear (PWL) sources. Verilog-A linters are starting to appear in the tool-set to try to
indicate where there might be coding issues to help improve Verilog-A’s usage. Another key a
mistake it to model cells using Verilog-A as too low a level. For example A Verilog-A model of
an inverter is likely to take more CPU time than the transistors to simulate. Verilog-A should be
used at a high level, where the model is still simple to write and the benefit is still there.

2 Generic Structure
Both XA© and HSPICE© support the Verilog-A language, by compiling the modules at run
time to be part of the main netlist. These tools only recompile the Verilog-A modules, if the file
has changed from run to run, thus eliminating the time spent recompiling the modules on
repeated simulation runs of the same netlist. These tools also concatenate all Verilog-A files so
only a single call to the compiler is needed. This minor optimization helps reduce the time
needed to setup a simulation. However often this is not the bottleneck in run time, but rather
poorly written Verilog-A files.
The most optimum approach to writing a Verilog-A model is to use real numbers within the
model itself and only have the electrical nodes on the ports. This cuts down the number of
electrical nodes for the simulator to solve each time step and should simplify the model. Using
electrical nodes internally would make the simulator maintain KCL, KVL for each node and
store the voltage and current natures for those nodes. Using real numbers remove this and allows
simpler calculation of the values, which can be optimised by the compiler. It is up to the model
writer to say whether each real number variable is a current value or a voltage. One could use a
variable naming standard of I<name> or V<name> to help others understand the formulas.
The first step in this approach is to categorise the input ports into supplies, voltage/current
references, digital signals, and normal signals. The relevant checkers can then be used on all but
normal signal inputs. Each checker should have the ports impedance modelled and matched as
close as possible. The outputs of the checkers can be used to control the models function. Normal

SNUG 2014 4 How to Write an optimum Verilog-A model


input signals need to have an input receiver attached, depending upon whether the input stage is
current steered or voltage.
The next step is to define the output driver type, whether it is current or voltage, and apply
limiters and/or slew rate control. Even logic outputs should use the same modelling system.
The final step is to use the real numbers from the checker signal inputs to generate the real
output value to apply to the output driver(s), based on the transfer function of the block. Figure 1
shows this structure, where the red indicates the section that is using real numbers and the black
for anything kept as electrical.

Figure 1 Overview of a Verilog-A model

2.1 Analogue Simulator Internals


At the basic level analogue simulators work by forming a matrix, based on Kirchhoff’s
Current Law (KCL) that the current into every node should sum to 0, [G] * [V] = [I]. The I
matrix should be 0 unless there are current sources actively injecting current into the node.
Forming the matrix the other way around, [R] * [I] = [V], is not possible as the voltages at each
node are not known. Kirchhoff’s Voltage Law (KVL) requires the voltage loops to sum to 0.
With any circuit there could be many loops to solve making KVL harder to implement. Writing
equations in the format G*V=I helps the simulator as this allows nodal analysis using KCL to
take place, which is one of the first steps the simulators take when solving the matrix. However
nodal analysis using KCL would not work with voltage sources, so a modified nodal analysis
technique is used where the I matrix contains either the fixed value, voltage or current, and the V
matrix contains the variable part.
Most circuits contain non-linear elements and using the modified nodal analysis is not enough
on its own to solve the equations. To solve non-linear elements the Newton-Raphson algorithm
is used to solve a DC operating point or the next time step. The Newton-Raphson algorithm
requires that the non-linear device has a first derivate that it can use. Discontinuities happen
when this derivative has steps in it function. The discontinuities around these steps can cause
convergence issues in the simulator. Making sure that any model has a first derivative is
paramount to getting the performance out of an analogue simulator. A piece wise linear (PWL)
source is just such a component that has discontinuities. However the simulator is aware of these
so deals with it in a different way. It is the unknown discontinuities that it can’t deal with
gracefully. As soon as extra convergence aids such as source stepping, GMIN stepping, pseudo-
transient analysis and others are invoked it should cause the user to look at the circuit to see what
is causing the issues and change them to help the simulator.

SNUG 2014 5 How to Write an optimum Verilog-A model


More detail on how, in principle analogue simulators work is available in [3]. Even though
this is an old book is gives a good overview on how most of the modern simulator work.

2.2 Monitored events vs. Continuous time


A lot of literature and training courses recommend the use of the monitoring events available
in Verilog-A such as cross, above, and timer. However, they rarely explain the pitfalls of using
such methods and what they do to the simulator. For example they don’t all execute for each
simulation analysis type or at circuit initialisation. A good example is the difference between the
cross and above events.
The main pitfall, even if they are used correctly, is that simulator time-step is affected by these
events. The time-step algorithm within the simulators tends to constrain the next time step to be
up to twice that of the previous one [6], unless the matrix does not converge whereby it reduces
the time step till it does. (The actual upper limit on the next time-step is simulator and algorithm
specific, but as a rule of thumb, twice the previous time step is a good value to use.) The
algorithms that do this are clever enough to try and reduce the number of attempts at different
time-steps for convergence, as each convergence operation take CPU time. So once one has the
simulation running with a large time-step, one wants to avoid causing anything that will drop the
time-step. The cross, above, and timer events have tolerances around the event trigger-point
time. For the cross and above there is an expression tolerance as well. Without care using these
statements, can cause the simulation to run slowly, by causing the time-step to drop to a small
value again. The default of these tolerances is simulator-dependent unless they are specified,
which is also not mentioned and hard to find. Within the simulator it works out the next time step
and if a timer condition has been trigger it then has to recalculate the matrix to a time-step that
meets the timer tolerances. For the cross and above it not only has to change the time, but
constantly evaluates the condition so that it is within its tolerance. This recalculation is an
expensive CPU operation, which makes the simulation appear to run slow [5].
The cross, above, and timer events can also cause hidden states, where the value is only
updated when an event happens. For most cases this does not cause any harm, but is best
avoided, so that every variable is defined at every time-step, including circuit initialisation.
Changing the cross, above, and timer to an if-else will not change the effects of any
discontinuity generated by the code it then executes, but it will allow the simulator more
flexibility on the choice of time-step. As a by-product of using the if-else structure there is no
need for an initial_step event to define any variable(s) that the cross, above, and timer would
require. Where possible an if-else, cross, above or timer should not be used for any direct
electrical signal changes as these will cause discontinuities as there are other methods that can be
used as mentioned in 6.2. They should be used for any log file messages. The improvement in
simulation runtime using an if-else method will depend on the number of event that happen. The
smaller the number of events been triggered the smaller the difference one would notice in
runtime. However the more event based statements in the netlist the higher the change that the
events will increase the runtime. Given a model could be used in a larger system it is best to
avoid event based code.
The cross statement is best used in Verilog-AMS files for interface elements between the
analogue and digital simulator engines (connect modules or equivalent modules), where a change
in the analogue signal causes an event in the digital domain. When used in this situation correct
tolerances based on the interface timing needs to be applied. A solution is to use the if-else

SNUG 2014 6 How to Write an optimum Verilog-A model


solution and have a single cross statement in the file, so that a time-step at the cross point
happens. The cross statement in this method is not gating any code that is then executed, it is just
telling the simulator a time point need to be generated near the cross event. This method is
preferred as the rest there is no hidden state. (The $bound_step function to set the maximum
time-step for that module could also be used.)

2.2.1 cross/above alternative


The cross and above events have 2 tolerances: one is the time; the other is the expression as
shown in Figure 2. With these events it is guaranteed that the expression has crossed the value.

Figure 2 cross/above timing of event relative to threshold[1]


A better method is to use an if statement, where one could add one’s own tolerance if required
and allow the simulator to change the time-step if necessary. It does mean writing more code but
this is worth it for the decrease in simulation run time.
integer falling_triggered, rising_triggered;

if(V(IN)>=THREHOLD) begin
if(!rising_triggered) begin
//V(IN) has just gone above THREHOLD

end
falling_triggered=0;
rising_triggered =1;
end
else begin
if(!falling_triggered) begin
//V(IN) has just gone below THREHOLD

end
falling_triggered=1;
rising_triggered =0;
end

SNUG 2014 7 How to Write an optimum Verilog-A model


Even cells such as flip-flops and latches can be defined in such a manner giving a big
simulator speed-up on blocks that have clocked inputs; the penalty being that they are slightly
more complex to write. The Verilog-A code below shows a reset dominant, rising-edge clocked
flip-flop. The code is not straight forward, though it could easily be wrapped up into a Verilog-A
function for simple reuse.

//Keep everything as integer so we have 0,1 values.


integer clk_int, rising_clk, clk_old, rst_an_int, q_int;

//Get reset value


rst_an_int = V(RST_AN)<=THREHOLD;

//Create clock pulse


clk_int = V(CLK)>=THREHOLD;
rising_clk = clk_int && !clk_old;
Figure 3 if statement version of cross/above event
//Model FF using if statement
if(rst_an_int) begin
//Setup outputs in Reset state
q_int = 0;
end
else if(rising_clk) begin
//Setup outputs when clock toggles.
q_int = ...;
end
else begin
//Hold state
q_int = q_int;
end

//Updated old clock value so the FF is not triggered


//again until the rising edge condition is reached.
clk_old = clk_int;

2.2.2 timer alternative


The timer event has only one tolerance which is how close to the actual time the event needs
to be. This could be either positive or negative delta time of the actual time specified, which
users often don’t realise. The default tolerance on some simulators is 1second, so they can
actually happen at any time, making the event function useless without a tolerance being set. A
better way to implement a timer event is using a continuous if statement and using a variable for
the event_time. Again, one could add one’s own tolerance if needed by adding extra conditions
on the if statement. Using the method in Figure 4 will guarantee the event is triggered at or after
the event_time and that it is executed for every simulation analysis type. The conditional
expression in the if statement must use either >= or <= and not == or != otherwise you will cause
the simulator to change the time-step to meet that condition, which is the opposite effect one
wants. After the condition has been meet, the event_time is set to -1.0 so that the code does not
trigger again.

if(($abstime >= event_time) && (event_time >= 0.0)) begin


//Do something based on this event time.

//Clear this event call or schedule another one.


event_time = -1.0;
end

Figure 4 if statement version of timer event

SNUG 2014 8 How to Write an optimum Verilog-A model


3 Supporting Modules/Files

3.1 Messaging System


In the digital environment like UVM, there are standard macros used for displaying messages:
`uvm_report_info, `uvm_report_warning, `uvm_report_error and `uvm_report_fatal. Normally
these standard message calls are not used directly, but wrapped up in a macro by each design
group or company, so they can be refined easily. (Doing it this at the outset allows a system to be
used in any environment just by a change to the macro definition.)
//Sets the messages that will be displayed, errors and warnings by default
`ifndef VERBOSITY
`define VERBOSITY 1
`endif

//Standard messaging system


`ifndef my_debug
`define my_debug(msg)if(`VERBOSITY >= 3) begin $strobe("DEBUG: %10.3fns : %m : %s",$abstime/1n, msg); end
`endif

`ifndef my_info
`define my_info(msg) if(`VERBOSITY >= 2) begin $strobe("INFO : %10.3fns : %m : %s",$abstime/1n, msg); end
`endif

`ifndef my_warn
`define my_warn(msg) if(`VERBOSITY >= 1) begin $strobe("WARN : %10.3fns : %m : %s",$abstime/1n, msg); end
`endif

`ifndef my_error
`define my_error(msg) begin $strobe("ERROR: %10.3fns : %m : %s",$abstime/1n, msg); end
`endif

`ifndef my_msg
`define my_msg(msg) begin $strobe("MSG : %10.3fns : %m : %s",$abstime/1n, msg); end
`endif

Currently there are no standard macros in the analogue environment using Verilog-A. This has
meant each model-writer invents his or her own method and formatting. What is proposed is to
define macros in Verilog-A to create a standard messaging system, each with a verbosity level to
control the information level that can then be used in every model. Using a standard messaging
format makes it easier for the log files to be parsed. In the code, the output message format is
<DEBUG|INFO|WARN|ERROR|MSG>, time in ns, instance name and finally the string passed
into the macro. This maybe whatever is decided as the standard for that design group or
company. The file containing all these definitions would then be included in every Verilog-A file
like the other standard include files: constants.va, disciplines.va.
For the my_debug macro one may want to use the $debug task instead of $strobe, as this will
display simulation data while the simulator is solving the equations, whereas $strobe displays
once the simulator has converged on a solution for all nodes.
Verilog-A does not have the $sformatf() system task which returns a formatted string, only the
$sformat() system task, which puts a formatted string into a declared string variable. Therefore,
to use the messaging macros a locally defined string variable, such as my_message, is needed.
This variable can be used multiple times in the module, so there is no need to use a string
variable for every $sformat() statement. (The call to the macros does not need a semi-colon as
they have been defined with begin and end around them.)

SNUG 2014 9 How to Write an optimum Verilog-A model


string my_message;

$sformat(mj_message, " Value is %0g", value);


`my_warn(my_message)

The Verilog-A/MS LRM has a restriction that the $sformat() statement may not be used within
a function. This means that there is no way to build up a string to be displayed. (This is most
likely due to the fact the $sformat() is a system task, and a function can’t call tasks since tasks
can take simulation time whereas functions can’t.)

3.2 Voltage/Current Checkers


Every block will have some supply rails, maybe a voltage reference and bias current. Rather
than every model having its own method of detection to see if its pins are valid for the block to
function, a wrapper module can be used. Instances of this cell can then be used in the main block
model, simplifying the amount of code needed in the model. The main advantage of this is that
this makes the method of checking uniform across a design.
When looking at any Voltage/Current input one can slice the range up into 3 different regions:
Active, InActive and InValid, as shown in Figure 5. The region boundaries can be defined as

InValid (-1)
INVALID_HIGH
InActive (0)
ACTIVE_HIGH

Active (1)

ACTIVE_LOW
InActive (0)
INVALID_LOW
InValid (-1)

Figure 5 Voltage/Current operating regions


parameters. These regions allow some basic checking of supply and input connections.
Each input must have impedance that matches approximately what it would see in reality. For
currents, they normally either come from a P-Source and connect to an N-Diode device, (Figure
6a) or come from an N-Source and connect to a P-Diode device (Figure 6b). These MOS diode
connections will have a body diode for reverse conduction and a capacitance associated with the
MOS diode. (The diode is mainly there to help the simulator get an operating point if the RX
node is driven by an ideal current source; without it, the voltage would have to go to infinity and
the simulator would fail to converge.) For voltages, they could go into any sort of load (Figure
6c), though there might need to be a current limit for small loads. The capacitance is there to
model any sort of filtering the real device will have between the RAIL and RX node. This
capacitance will help reduce the amount of re-evaluation equations by filtering out any noise on
the nodes.

SNUG 2014 10 How to Write an optimum Verilog-A model


Figure 6 Internal models of checkers.
To avoid, having to create an individual model for each type of checker, which is harder to
maintain, a simple if-else statement is used to select the correct input impedance model and
probe to use. The MOS diode-connected model does have a discontinuity around V(RX,RAIL)-
Vth=0, however the model should never sit around this region unless there is a system error in
the current source or sink. If this does cause a concern, a transition or discontinuity statement
could be used, though this will slow the simulation down at this point. Another solution would be
to use a modified power smoothing function as mentioned in 3.4.1, but if one is sure that the
node will not sit in this region under normal usage then the discontinuity might be easier to
understand.

if(chk_type == "ND") begin


I(RX,RAIL) <+ -1e-15 * (limexp(V(RAIL,RX)/$vt ) - 1.0); //Diode protection
I(RX,RAIL) <+ (V(RX,RAIL)>=vth) * beta * pow(V(RX,RAIL)-vth,2); //MOS diode
I(RX,RAIL) <+ c * ddt(V(RX,RAIL)); //MOS Bias Cap
value = I(RX,RAIL);
end
else if(chk_type == "PD") begin
I(RAIL,RX) <+ -1e-15 * (limexp(V(RX,RAIL)/$vt ) - 1.0); //Diode protection
I(RAIL,RX) <+ (V(RAIL,RX)>=vth) * beta * pow(V(RAIL,RX)-vth,2); //MOS Diode
I(RX,RAIL) <+ c * ddt(V(RX,RAIL)); //MOS Bias Cap
value = I(RAIL,RX);
end
else if(chk_type == "V") begin
I(RX,RAIL) <+ g * V(RX,RAIL); //Voltage Load
I(RX,RAIL) <+ c * ddt(V(RX,RAIL)); //Voltage Cap
value = V(RX,RAIL);
end
else begin
$sformat(my_message, "Invalid parameter chk_type %s.",chk_type);
`my_error(my_message)
$finish(0);
end

Port Direction Function


RX Inout Signal to measure.
RAIL Inout Rail the measurement is made against. For
voltages this could be to the node 0 using
ground statement. For currents is has to be
the supply for which the current is
sourced/sunk.
FLAG Output Voltage representing the region the input
RX is in.
1v – Active, 0v – Inactive, -1v – Invalid

SNUG 2014 11 How to Write an optimum Verilog-A model


VAL Output Voltage output of the value measured.
Table 1 Suggested port list for Voltage/Current checker

Parameter Recommended Function


Defaults
tol 1n Tolerance used when
INACTIVE_LOW/HIGH parameter are
not used.
active_low 0 Active high limit
active_high 0 Active low limit
inactive_low (active_low < 0.0) ? InActive high limit
active_low -tol:-tol
inactive_high (active_high > 0.0) ? InActive low limit.
active_high+tol:tol
td 0 FLAG signal output delay
tr 0 FLAG signal output rise time
tf 0 FLAG signal output fall time
inactive_dt 10n Debounce time on entering inactive
region before output FLAG changes.
invalid_dt 10n Debounce time on entering invalid
region before output FLAG changes.
RX_g 0 Conductance RX to RAIL
(Voltage checks only.)
RX_c 100f Capacitance RX to RAIL.
vth 0.40 MOS Vth value.
(Current checks only.)
beta 45.0u MOS beta value.
(Current checks only.)
chk_type “” Defines the check type ND, PD or V.
Table 2 Suggested Instance parameters for Voltage/Current checker
These blocks take in a number of signals (Table 1) and parameters (Table 2) and output a
status flag (FLAG) and a signal value (VAL) as a voltage only. As the outputs have to be of
electrical type the status flag could change between the values -1, 0 or 1 in no time at all. As
there is no load added to this node apart from maybe GMIN within the main model, if the
simulator has added that in, then this is fine as there would not be any electrical equation to
solve. It would mean on that node there is infinite dV/dT. Ideally this port would stay as a real
number but the language does not support real port types. The complete code for the checker
implemented is in 10.1.

SNUG 2014 12 How to Write an optimum Verilog-A model


if((value <= ACTIVE_HIGH) && (value >= ACTIVE_LOW)) begin
if(flag_int != ACTIVE) begin
$sformat(my_message, "%0s at Active level of %0g",(chk_type=="V" ? "Voltage":"Bias"),value);
`my_info(my_message)
end
flag_int = ACTIVE;
warn_time = -1.0;
error_time = -1.0;
end
else if((value <= INACTIVE_HIGH) && (value >= INACTIVE_LOW)) begin
if((warn_time < 0.0) && (flag_int!=INACTE)) begin
warn_time = $abstime + warn_dt;
error_time = -1.0;
end
if((warn_time > 0.0) && (flag_int==INACTE)) warn_time = -1.0;
end
else begin
if((error_time < 0.0) && (flag_int!=INVALI)) begin
error_time = $abstime + error_dt;
warn_time = -1.0;
end
if((error_time > 0.0) && (flag_int==INVALI)) error_time = -1.0;
end

//Warning timer event using if statement rather than timer as quicker.


if(($abstime >= warn_time) && (warn_time > 0)) begin
flag_int = INACTE;
$sformat(my_message, "%0s at InActive level of %0g",(chk_type=="V" ? "Voltage":"Bias"),value);
`my_warn(my_message)
warn_time = -1.0;
end

//Error timer event using if statement rather than timer as quicker.


if(($abstime >= error_time) && (error_time > 0)) begin
flag_int = INVALI;
$sformat(my_message, "%0s at InValid level of %0g",(chk_type=="V" ? "Voltage":"Bias"),value);
`my_error(my_message)
error_time = -1.0;
end

This is by no means a complete list of checks that could be used, but explain the basics on how
a base-level checker module could be used. For example, on currents, an extra check could be
put on the maximum voltage drop across the RAIL and RX pin, to catch issues with biases going
from a high to low voltage domain. For biases, these checkers would make sure that there is only
one bias source and sink per net.

3.3 Digital Input Checker


A majority of analogue blocks will have some control input that should be a voltage logic
level relative to some supply. A lot of models written by designers just check if the voltage on

Figure 7 Digital Input Threshold levels

SNUG 2014 13 How to Write an optimum Verilog-A model


these pins is greater than a certain amount, normally ½ the supply. However, this fails to check
that it has been driven to the right level and that the transition time from a logic high to low is
within bounds. Spice simulations may even pass with the wrong level being used, and only the
use of tools such as XA/HSIM Circuit Checker may catch these. Using a simple Verilog-A
model, it is possible to perform some better checking to catch these simple mistakes early on in
the design phase.
Similar to the Voltage/Current checkers, a logical input can be split into 3 regions Figure 7:
the logic high (1), the logic low (0), and the logic unknown (-1). Unlike the Voltage/Current
checker, the digital input checker has to check the supply relative to the input for overstress. This
means the logic unknown area is also affected by the supply.

Port Direction Function


A Input Signal to check.
VDD Inout Supply the signal should be relative to.
VSS Inout Ground the signal should be relative to.
D Output Voltage representing the logic level. Logic
X (-1V), Logic 0 (0V) and Logic 1 (1V)

Parameter Recommended Function


Defaults
active_low 0.0 Minimum value of VDD before the input
can be checked.
vgsmax 0.0 Maximum Voltage A can be relative to
VSS or VDD.
art_vdd 0.0 Arterial VDD value if the input goes only
to an N-MOS device. (A_cvdd/gvdd
should be 0.)
art_vss 0.0 Artificial VSS value if the input goes to
only to a P-MOS device. (A_cvss/gvss
should be 0.)
a_cvdd 0 Input capacitance of A pin to VDD.
a_gvdd 0 Input impedance of A pin to VDD.
a_cvss 1p Input capacitance of A pin to VSS.
a_gvss 0 Input impedance of A pin to VSS.
a_dt 10n Debounce time A can be when moving
from a logic 1/0 state.
a_vh 0.7 A Voltage high threshold
a_vl 0.3 A Voltage Low threshold
d_low_sup -1 Output value of D while VDD <
active_low value.
td 0 FLAG output delay time
tr 0 FLAG output rise time
tf 0 FLAG output fall time

SNUG 2014 14 How to Write an optimum Verilog-A model


As with the Voltage/Current checkers the input impedance should match approximately what
it would see in reality. Figure 8 shows a model of the input structure one could use. By using a
current contribution statement rather than a voltage the capacitances and conductance can be 0
without causing any divide by 0 errors.

Figure 8 Input model of Digital Input Checker


Three voltage measurements are made to allow the logic level to be evaluated correctly. VDD-
VSS is used to see if the supply to the block is even at a level where the input could be useful
(ACTIVE_LOW parameter). RX-VSS is checked to see where in the supply range VDD-VSS
the RX node is sitting for the logic 0 check, and to check that the RX-VSS is not greater than
VGSMAX. Similarly VDD-RX is used to do the same for the logic 1 check. Since there could be
ripple on the supply or A node, there is a debounce time when leaving the ACTIVE region. The
complete code for the checker implemented is in 10.2.

//Model input impedance between the rails


I(VDD,A) <+ A_cvdd * ddt(V(VDD,A));
I(VDD,A) <+ A_gvdd * V(VDD,A) ;
I(A,VSS) <+ A_cvss * ddt(V(A,VSS));
I(A,VSS) <+ A_gvss * V(A,VSS) ;

//Continious time output evalution with debounce on X region.


if(vdd_vss >= ACTIVE_LOW) begin
if( (V(A,VSS) >= (A_vh*vdd_vss)) && (V(A,VSS) < VGSMAX)) begin
a_logic_int = LOGIC_HIGH;
error_time = -1.0;
end
else if( (V(A,VSS) <= (A_vl*vdd_vss)) && (vdd_a < VGSMAX)) begin
a_logic_int = LOGIC_LOW;
error_time = -1.0;
end
else begin
//In an unknown region.
//a_logic_int = a_logic_int; <- Not needed.
if((error_time < 0.0) && (a_logic_int!=LOGIC_X)) error_time = $abstime + A_dt;
if((error_time > 0.0) && (a_logic_int==LOGIC_X)) error_time = -1.0;
end
end
else begin
//Port supply too low.
a_logic_int = D_LOW_SUP;
error_time = -1.0;
end

//Timer for setting the output to the X region.


if(($abstime>=error_time) && (error_time > 0)) begin
a_logic_int = LOGIC_X;
$sformat(my_message, "Input is in the Logic 1'bx region, value set to -1.");
`my_error(my_message)
end

SNUG 2014 15 How to Write an optimum Verilog-A model


3.4 Generic Functions
Often there are a group of repeated features needed in every block, yet everyone tends to
create their own local version. It is best to have a file that includes all reusable functions that can
be included in each model. This would allow each model-writer to have access to repeatable
functions, which can be maintained centrally. This include file needs to be within the body of the
module, since functions are only accessible within the module - endmodule boundary. (It is
worth noting that a function can only access variables within the scope of the analog function
and endfunction; this is different to Verilog-D.) Checks should always be placed in functions for
invalid input values to help with model debugging. These checks would happen every time the
function is called. For example an input that can cause a divide by zero error should be
highlighted, as each simulator may react differently as it is unspecified in the LRM [1].

3.4.1 Smoothing Limiter


Models tend to be written forgetting to include limits on the internal values, or if they do they
use hard limits causing discontinuity by using if-else statements. The tanh() function is quite
often used as a smoothing limiter, but the problem with this method is that it is CPU intensive
and non-linear, except at small values. Other methods proposed use the max() and min()
functions, but these still are discontinuous. Using a power smoothing function it is possible to
have a function that is linear for most of the range and asymmetric at the limits. The function in
Equation 1 Power does this, and for different values of n, one gets varying smoothing effects as
shown in Figure 9. (n should be an integer to make the equation easier for the simulator to
evaluate.) It does have the limitation of been symmetrical around the axis, so max = -min. As the
function is linear it does not require any techniques in the simulator to try and linearize it, so its
execution time is fast. Unlike the other methods it derivative is easily calculated, and for a
majority of the range (–MAX to MAX), equates to 1, but has no discontinuity. Using this limiter
will help speed up simulations by removing a possible discontinuity.
X
Y=
  X 2n 
2 n 1 +  
  MAX  
 
Equation 1 Power Limiter (-MAX <-> MAX)

SNUG 2014 16 How to Write an optimum Verilog-A model


Figure 9 Power Limiter response for different n values (MAX=1)

analog function real pwr_smooth;


input in;
real in;
input factor;
integer factor;
input low;
real low;
input high;
real high;

begin
if((low!=high) || (high<=0)) begin
`my_error("high !=low or low>high. Function output set to 0.0.")
pwr_smooth = 0.0;
end
else if(factor < 1) begin
`my_error("Input 'factor' >= 1. Function output set to 0.0.")
pwr_smooth = 0.0;
end
else begin
pwr_smooth = in/( pow(1.0+pow(in/high,2.0*factor),1.0/(2.0*factor)));
end
end
endfunction

With some minor adjustments, the equation can be made to vary from 0 to MAX rather than –
MAX to MAX. The main use for this is on the voltage feedback for current outputs, as explained
later.

SNUG 2014 17 How to Write an optimum Verilog-A model


 
 
 
1 2 X − MAX 
Y = MAX +
2 2n 
  2 X − MAX  
 2 n 1 +  
   MAX   
  
Equation 2 Power Limiter (0 -> MAX)

analog function real pwr_ilimit;


input in;
real in;
input factor;
integer factor;
input high;
real high;

begin
if(high<=0) begin
`my_error("high input <=0.0. Function output set to 0.0.")
pwr_ilimit = 0.0;
end
else if(factor < 1) begin
`my_error("Input 'factor' >= 1. Function output set to 0.0.")
pwr_ilimit = 0.0;
end
else begin
pwr_ilimit = (high+((2.0*in)-high)/(pow(1.0+pow(((2.0*in)-high)/high,2.0*factor),1.0/(2.0*factor))))/2.0;
end
end
endfunction

3.4.2 Logical Comparators


As the status flag output from the checkers has to be a voltage, it makes sense to have some
function to check if the value matched the expected value, rather than write the code each time.
Common comparisons are greater than, less than or equal to. As the input is a real number >=

SNUG 2014 18 How to Write an optimum Verilog-A model


and <= operands must be used. A tolerance feature is added to try and remove rounding error
issues. To save probing the voltage FLAG output from the checker every time it is needed, one
should convert the voltage to an integer once and use that within the code. (Every probe to a
voltage is a lookup in the matrix which one wants to avoid.)

analog function integer check_eq;


input value;
real value;
input expected_value;
real expected_value;
real tol;
begin
tol = 1e9;
check_eq = (value <= expected_value+tol) && (value >= expected_value-tol;
end
endfunction

3.4.3 Range Checkers


Whenever a limiter is used it is likely that you will want to know it has been limited. The
simple approach is to use a cross/above statement to monitor the input to the limiting function,
but as discussed earlier this causes the time-step to change, in this case only to show a message.
A better approach is to use a function that uses an if-else routine. So that the same message only
appears once, rather than every time the function is evaluated at each time-step, the call to the
function returns a variable that is passed back to the function as the limited input. The code
below is an example of how this may be achieved.

analog function integer check_range;


input value;
input high;
input low;
input limited;
input msg;
input ena;

real value;
real high;
real low;
integer limited;
string msg;
integer ena;

begin
if((limted != -1) && (low > high) begin
`my_error("Min > Max to the function. Function output set to -1.")
check_range = -1;
end
else if(ena==1) begin
if((in <= high) && (in >= low)) begin
if(limited && (msg != "")) `my_info({"Output within limits for ",msg})
check_range = 0;
end
else begin
if(!limited && (msg != "")) `my_warn({"Output outwith limits for ",msg})
check_range = 1;
end
end
else begin
check_range = 0;
end
end

endfunction

SNUG 2014 19 How to Write an optimum Verilog-A model


The use of the function is then very simple, and cuts down the amount of repeated code
needed in every model. It also removes the complexity for the analogue designer to code, so he
or she can focus on the actual model internals.

integer op_limted;

op_limted = check_range(out,out_max,out_min,op_limted,"output current",enabled);

3.4.4 Control Messages


Most blocks will have an enable and other digital control that it would be useful to know when
it has changed. The blocks will also have a set of criteria to say when the supplies, voltage
reference or current are at a level sufficient for the block to function. The classic approach to this
would be to use a cross/above statement, but, as discussed earlier, this causes time-step changes.
Using a similar approach to 3.4.3, a nice wrapper function can be used. Simple changes to this
could be done for detecting only rising or falling edges.

analog function integer check_msg;


input value;
input status;
input ena;
input high_string;
input low_string;

integer value;
integer status;
integer ena;
string high_string;
string low_string;

begin
if(ena==1) begin
if(status!=value) begin
if(value==1) begin
`my_info(high_string)
check_ msg = 1;
end
else if(value==0) begin
`my_info(low_string)
check_ msg = 0;
end
else check_sig = -1;
end
else check_ msg = check_ msg;
end
else check_ msg = -1;
end

endfunction

The use of the function is then very simple as shown below

integer supplied_msg;

supplied_msg = check_msg(supplied,supplied_msg,1,"Block is now supplied","Block is not supplied");

SNUG 2014 20 How to Write an optimum Verilog-A model


4 Electrical Drivers
The output drivers should take the internal real number that has had limiters, slew rates and
any other adjustments applied to it and convert this back into an electrical node driven out as a
pin on the block. These are then the interface elements back into the simulation matrix. It is
important that the input to the interface elements are continuous in time and don’t have any
discontinuities that the simulator is not made aware of.
Unless there is no other way, all contribution statements should have a current branch on the
right hand side of the equation. This then allows the simulator to map this directly into the
internal matrix. Using a current output also allows the output driver to be tri-stated. Models
should be written sinking or sourcing the current from the right supply so Iq measurements can
be made realistic.
A common mistake when writing contribution statements is to forget to include some sort of
feedback so the output is not ideal. Without this feedback, it is possible to write Verilog-A code
that will never converge or gives a difficult convergence point to meet. Imagine that you have 2
blocks that have ideal voltage source on the outputs, which are connected together. Without any
feedback, there is no solution unless the two sources are the same value, because of the hard
voltage loop. Even if the 2 are the same a simple rounding error in the simulator could cause it to
fail to converge part way though a transient simulation.

4.1 Output Driver


The output driver chosen and shown in Figure 10 is an ideal Class-AB output stage using
current sources, however the modelling style is applicable to other output stages, such as class
A,AB, C, G and H, or even single sided outputs. The model could be enhanced to include non-
linarites of such an output stage, but this is out with the scope of the paper. When adding non-
linarites, it is best to make the mathematical function continuous so there are no discontinuities
for the simulator to deal with, so to smooth the first derivative. For example power supply
rejection might be important to add.

Figure 10 Generic Output Driver Stage

The diode equation uses the limexp() function rather than the exp() function as the limexp()
function coordinates with the simulator to prevent it from declaring convergence while the

SNUG 2014 21 How to Write an optimum Verilog-A model


function is limiting. (Externally the limexp() appears the same as exp(), except it is more
expensive in terms of memory and CPU.) The 1e-15 is an arbutrary saturation current and could
easily be another value. (If the output is not connected to any load, the stauration current might
need to be different for the top and bottom diodes. This will depend upon whether the simulator
can choose an operating point for the output, as the voltage could any solution from VDD to
VSS.) The pwr_limit function 3.4.1 is used to model the effects of the Vds vs Ids of a normal
device as Vds < Vdsat, and the if-else, using the ? notation, is to make sure the current is drawn
from the correct supply rail. Although this might look like there is discontinity, there is always a
current path from the out pin, and the discontinity can only happened around out_slew=0.0, as
the current direction changes.

//High side output current and diode


I(vdd,out) <+ ((out_slew>=0.0) ? out_slew:0.0) * pwr_ilimit(V(vdd,out),7,0.2,0.2);
I(vdd,out) <+ -(1e-15 * (limexp(V(out,vdd)/$vt) - 1.0));
//Low side output current and diode
I(out,vss) <+ ((out_slew>=0.0) ? 0.0:-out_slew) * pwr_ilimit(V(out,vss),7,0.2,0.2);
I(out,vss) <+ -(1e-15 * (limexp(V(vss,out)/$vt) - 1.0));

One could wrap the whole of the output driver into a macro to try and simplify the model code. It
could not be a module due to the limitation that port types can’t be a real number. However the
macro definition would not be the simplest to read, hence it is not shown here.

4.2 Current Outputs


By defining the output stage as a class-AB current , the only extra steps in sinking or sourcing
an output current is a limiter and slew rate control, as shown in Figure 11. The slew rate control
is optional as the control on the out_value might do the slew rate limiting, and this extra
mathematical step is an overhead. The limiter is the power smoothing limiter mentioned in 3.4.1.
out_lim = pwr_smooth(out_value,7,out_min,out_max);
out_slew = slew(out_lim,out_sr);

//High side output current and diode


I(vdd,out) <+ ((out_slew>=0.0) ? out_slew:0.0) * pwr_ilimit(V(vdd,out),7,0.2,0.2);
I(vdd,out) <+ -(1e-15 * (limexp(V(out,vdd)/$vt) - 1.0));
//Low side output current and diode
I(out,vss) <+ ((out_slew>=0.0) ? 0.0:-out_slew) * pwr_ilimit(V(out,vss),7,0.2,0.2);
I(out,vss) <+ -(1e-15 * (limexp(V(vss,out)/$vt) - 1.0));

Figure 11 Current outputs

4.3 Voltage Outputs


Voltage outputs are an extension to the current outputs by the simple effect of adding an extra
loop in the equation as shown in Figure 12. The output voltage is measured and compared to the
desired output voltage. The difference is converted to a current to control the current steered
class-AB output stage. The gm should be very big and the current limiter should be used for all

SNUG 2014 22 How to Write an optimum Verilog-A model


but very small delta voltage difference, that way the gain is uniform for all but small deltas.
Depending on how the out_value is calculated it may need some voltage limiter added on it so
that it stays within the range VDD-VSS as determined by the output driver.
One could add a timer check to display a message, using an if-else statement 2.2.2, to make
sure that the current output is not at the limit for too long, as this could indicate a short on the
output node.

out_raw = (out_value-V(out,vss)) * gm;


out_lim = pwr_smooth(out_raw,7,out_min,out_max);
out_slew = slew(out_lim,out_sr);

//High side output current and diode


I(vdd,out) <+ ((out_slew>=0.0) ? out_slew:0.0) * pwr_ilimit(V(vdd,out),7,0.2,0.2);
I(vdd,out) <+ -(1e-15 * (limexp(V(out,vdd)/$vt) - 1.0));
//Low side output current and diode
I(out,vss) <+ ((out_slew>=0.0) ? 0.0:-out_slew) * pwr_ilimit(V(out,vss),7,0.2,0.2);
I(out,vss) <+ -(1e-15 * (limexp(V(vss,out)/$vt) - 1.0));

Figure 12 Voltage outputs

5 Electrical Receivers
The inputs receivers should in general have some impedance on the node. For a voltage input
this could be as simple as an RC network, for currents there needs to be feedback to steer the
current to the right place. Without any impedance the simulator will add GMIN to those nodes.

5.1 Voltage Inputs


Voltage inputs are probably the easiest to model as they will just have impedance to other
nodes, with the voltage probe on the node. If the node voltage is not used directly, but some tap
off on a resistor string, used instead, the real number in_value can be scaled or filtered to model
this. Figure 13 shows a basic structure that could be used, where Z1 and Z2 are the impendence to
the respective supplies. Some simple checks could be added to the in_value to make sure it is
within the expected ranges.

I(vdd,in) <+ V(vdd,in) * Z1;


I(in,vss) <+ V(in,vss) * Z2;
in_value = V(in,vss);

SNUG 2014 23 How to Write an optimum Verilog-A model


vdd

Z1

in

Z2 V in_value

vss
Figure 13 Voltage input model structure

5.2 Current Inputs


Current inputs tend to need some sort of feedback to direct where the current should go, and
what voltage to try and regulate the pin to. This is true for current and trans impedance inputs.
Often one sees code with I(port) used to probe the current, but this connects the port to the node
0 making the voltage on the port 0. Even the probe I(port1,port2) puts a short between the 2
ports, which might not be what one wants. Figure 14 shows how a current input might be
arranged. It uses the class-AB output driver to direct the current to the correct supply by trying to
regulate the voltage on the pin to a reference. The loop will have finite gain (gm) and slew rate.
By directing the current to an appropriate supply helps the simulation matrix formation by
avoiding any ideal voltage source to sink or source current to be added by the simulator. For this
loop to work the reference voltage must be between the supplies VDD, VSS.

in_raw = V(in,vref) * gm;


in_lim = pwr_smooth(in_raw,7,in_min,in_max);
in_slew = slew(in_lim,in_sr);

//High side input current and diode


I(vdd,in) <+ ((in_slew>=0.0) ? in_slew:0.0) * pwr_ilimit(V(vdd,in),7,0.2,0.2);
I(vdd,in) <+ -(1e-15 * (limexp(V(out,vdd)/$vt) - 1.0));
//Low side input current and diode
I(in,vss) <+ ((in_slew>=0.0) ? 0.0:-in_slew) * pwr_ilimit(V(in,vss),7,0.2,0.2);
I(in,vss) <+ -(1e-15 * (limexp(V(vss,in)/$vt) - 1.0));

Figure 14 Current input model structure

SNUG 2014 24 How to Write an optimum Verilog-A model


6 Model internals
Most models tend to be written in an electrical way causing extra electrical nodes in the
simulation matrix. This is because the model writer is thinking how the circuit would be or has
been designed, rather than taking a step back, and looking at the transfer function from input to
output. If the designer looks at the transfer function, then there is no need for models to use
internal electrical nodes as everything can be done using real numbers that can either represent a
voltage or a current. These real numbers in the model can have discontinuities as long as they
don’t directly affect the simulation matrix, though it is best to avoid this as discussed in section
6.2. The checkers described above allow the checking of what mode the block should be working
in based on a logic input, and if the supplies/reference are present. These signals have to be
treated as digital logic controls that may need filtering when affecting the model internals. For
example if a block is suddenly enabled the output may ramp slowly to the desired value rather
than step to it. This ramp would be done on a real number that is then applied to either a voltage
or current output driver described above, probably using a transition statement on the enable
logic value. The electrical drivers and receivers are the interface elements from the model back
into the simulation matrix that must not have discontinuities in the first derivative.

6.1 Supplied Signal


Using the checkers mentioned in 3.2, it is possible to use the FLAG outputs to create an
integer that logically represents the block’s supply status. It will be either 0 (not supplied) or 1
(supplied). As the FLAG port of the checkers has to be of electrical type, the voltage needs to be
checked against a value. This is best done using the function in 3.4.2, as this returns an integer on
which the && operator can be used. The supplied signal can then be used in the model to set the
output/input values.

integer supplied;

analog supplied = check_eq(V(vdd_ok),1) && check_eq(V(vss_ok),1);

6.2 Logic Controls


The outputs from the logic inputs and voltage/current checkers are logic levels, that using
3.4.2 are converted to a discrete value of either 0 or 1. If these signals are used directly on any
internal signal that affect the input receivers or output drivers they would cause discontinuity.
The transition statement should be used to smooth these out, as it is function is to smooth out
piecewise waveforms. This function deals with the discontinuities in the piecewise signal by
notifying the simulator. The output of the transition statement would now be a continuous real
number that can be used as a multiplier on an equation, making it smooth. Modelling the control
signals this way means there is no need for if-else statement and thus the equation from input to
output is continuous. The logical expression that any if-else could create is then the input to the
transition filter. Thus the transition filters output can only every change between 0.0 or 1.0.
Multiple transition statements could be used on the same variable assignment allowing dead time
or overlaps. The rise (tr) and fall (td) times of the transition filter must not be 0 and should be
chosen appropriately not to cause too much deviation in the time-step.

SNUG 2014 25 How to Write an optimum Verilog-A model


//1. Using discrete if-else code.
if(supplied && check_eq(ena,1)) begin
if(check_eq(mode,0)) out = in_value * gain1;
else out = in_value * gain2;
end
else out = 0.0;

//2. Transition between gain1 and gain in a linear fashion.


out = transition(supplied && check_eq(ena,1),td,tr,tf) * in_value *
transition(check_eq(mode,0) ? gain1:gain2,td,tr,tf);

//3. Allow crossover of gain1 and gain2 by controlling the


//independent transition statements td,tr,tf.
out = transition(supplied && check_eq(ena,1),td,tr,tf) *
((in_value * gain1 * transition(check_eq(mode,0),td,tr,tf)) +
(in_value * gain2 * transition(check_eq(mode,1),td,tr,tf)));

In the example code above, the block is a simple amplifier with a gain control. Using the if-
else approach, (1), the real number out would have discrete steps as the control signals changed.
(These could be smoothed out with a slew statement, but the simulator reports when the slew rate
is reached, which would be unwanted information.) In (2) the supplied and ena signals control
the outer if-else loop from (1) using on transition statement. The gain adjustment is done via a
second transition statement. This would allow a linear change from one gain to another, but
means the second transition statement is not varying between 0.0 and 1.0, but between gain
values. In (3) better control on how the gains cross over is done, using two separate transition
statements that both vary between 0.0 and 1.0. Using this approach all model discontinuities can
be dealt with in a controlled manor that the simulator is aware of. However equations can
become hard to read, so one may wrap them up into macros.

It is best to put the supplied and enable logic control signals on any current input receivers and
any voltage output drivers as a transition statement, so those loops are disabled when the block
should not be operating. (It is not an issue for voltage input receivers or current output drivers.)
The gm multiplication stage on the loops is the most obvious location to apply this to.

6.3 Filters
Most blocks will have some filters in them, and the Laplace statement is the easiest way to do
this. Laplace statements do have a restriction that the poles and zero have to be defined at
compile time. Dynamic changes to the poles or zero’s are not allowed. There are ways around
this using for loops to generate all the possible filter combinations, but this tends to make the
model very slow, or use too much memory if the number of filters is >100. Where possible either
use a parameter to select the filter values, or use the Laplace Integration theorem, Equation 3, to
create a time domain integral idt() operator that allows dynamic changes to the pole’s and zero’s.
The Laplace approach also suffers from the fact you can’t reset the filter, which is what a block
may do when it is disabled. The idt() approach does not suffer from this as the function has an
option input <assert>.

SNUG 2014 26 How to Write an optimum Verilog-A model


t
F (S )
∫ f (t )dt ← →
Lapace

0
s
Equation 3 Laplace Integration Theorem

If one takes the polynomial form of the Laplace transform and re-arranges it it is possible to
create a generic equation that can be used to apply the Laplace Integration Theorem to. This
method only works if there are more pole’s (N) than zero’s (M), and that the pole filter order
does not change as that would change the number of integration stages required. i.e. PN must be
non-zero all the time. The later could be dealt with, but the code would become very complex,
and still have a restriction that a finite order would have to be defined at compile time.

N M
V0ut ∑ Pn S n = VIn ∑ Z m S m
n =0 m=0

 (VIn Z m − VOut Pm )S m− N  N −1
P S n− N
= ∑m=0   − VOut ∑ n
M
VOut
 PN  n = m +1 PN
Equation 4 Polynomial form for Laplace integration theorem

As an example if we have a 2 pole, P1, P2, and a 2 zero Z1, Z2 filter, using the Laplace
Integration theorem we can convert this to a time domain integral. Equation 5 shows how a
Laplace zero-pole form is translated into a time domain integral. Finally the equation is mapped
to the Verilog-A code. The 0’s are there to give the integrators an initial condition, and the rst is
to reset the filters back to this initial condition whenever it has a non-zero value. This could be
done with other zero-pole combinations, but care has to be taken to make sure the final Verilog-
A code is correct. A simple, but effective test, is to check the ac phase and magnitude of the time
domain integral form against the laplace_np() function for a specific value of zero’s and pole’s.

Vout ( S )  (1 + sZ1)(1 + sZ 2)  1 + s ( Z1 + Z 2) + s 2 ( Z1 * Z 2)
H (s) = = =
Vin( s )  (1 + sP1)(1 + sP 2)  1 + s ( P1 + P 2) + s 2 ( P1 * P 2)
Vout ( s ) * (1 + s ( P1 + P 2) + s 2 ( P1 * P 2)) = Vin( s ) * (1 + s ( Z1 + Z 2) + s 2 ( Z1 * Z 2))
 1 ( P1 + P 2)   1 ( Z1 + Z 2) 
Vout ( s ) *  2 + + ( P1 * P 2)  = Vin( s ) *  2 + + ( Z1 * Z 2) 
s s  s s 
1  Vin( s ) − Vout ( s ) Vin( s ) * ( Z 2 + Z1) − Vout ( s ) * ( P 2 + P1) 
Vout ( s ) =  + + Vin( s ) * Z 2 * Z1
P1 * P 2  s2 s 
Vout (t ) =
1
(
P1 * P 2 ∫∫
)
(Vin(t ) − Vout (t ))dtdt + ∫ (Vin(t ) * ( Z 2 + Z1) − Vout (t ) * ( P 2 + P1))dt + Vin(t ) * Z 2 * Z1

Vout (t ) =
1
(
P1 * P 2 ∫∫
(Vin(t ) − Vout (t ))dt +(Vin(t ) * ( Z 2 + Z1) − Vout (t ) * ( P 2 + P1))dt + Vin(t ) * Z 2 * Z1)
Equation 5 2-pole/2-zero Laplace to time integral example

V(out)<+(1.0/(P1*P2))*(idt(idt(V(in,out),0,rst)+(V(in)*(Z2+Z1)-V(out)*(P1+P2)),0,rst)+(V(in)*(Z1*Z2)));

SNUG 2014 27 How to Write an optimum Verilog-A model


Whichever filter implementation is used another common mistake users make is not to put
limiters on the filters. Without limiter the filters outputs can take any value which in reality is
unrealistic, even going outside the supplies of the block. Although the output of the filter can be
limited this does not solve the issue, as the filter itself needs to have limits so that it can’t go any
higher than the limits. Allowing the filters to grow would mean recovery from an over-limit
event will take longer than reality. One solution, for the integral form, is to use the initial
condition and assert fields to create a hard limit by resetting the filters to a fixed value, until the
limiting is no longer needed. This is discontinuous, but mathematically quicker to execute when
limiting. Figure 15 shows a simple filter with a current steered input, Itop and Ibottom, to
generate an output voltage. The code shows how limiting could be done for the integral form of a
filter.

Figure 15 Example filter network.

in = res_value * (Itop - Ibottom);

if((out >= V(vdd)) && (in>=out)) begin


ic = V(vdd);
rst = 1;
end
else if (out <= V(vss)) && (in<=out)) begin
ic = V(vss);
rst = 1;
end
else begin
ic = 0;
rst = 0;
end

out = idt((1.0/(cap_value*res_value))*(in - out),ic,rst);

Another solution, which works on the integral and Laplace form of a filter, is to use feedback
from the filters output to limit the input. Care need to be taken on both approaches, so that the
result is what one expects. It is recommended to use the feedback option as this is likely to yield
the correct result over different scenarios. Figure 16 shows the implementation, where G is the
limiter whose output varies from 0 to 1 that is multiplied against the input. Although analogue
simulators appear to be continuous in time, they actually take time steps, which on the feedback

SNUG 2014 28 How to Write an optimum Verilog-A model


system will appear as sampled system. This means when the limits are reached the output will
oscillate around the limit at each time-step.

Figure 16 Filter feedback limiting

6.4 Static Currents


As the model interface uses current steering to interface with the simulator matrix, and the
model itself is all implemented using real modelling, the complete current usage of the cell will
not be correct. So that the model matches the current used by the real cell, a current taken from a
relevant supply might be needed to model the internals of the actual design. This current could
then be input dependant to allow better power estimations.

7 Other tips
There are many other optimizations that can be used to help speed up the Verilog-A models.
However not all are generic and some should really be done by the simulators themselves. This
section just focuses on some common tips for helping create optimal Verilog-A models.

7.1 Switches
Many people use the known if-else statement to model a switch, where one statement is a 0v
Voltage source between the nodes, and on another it is a 0A current source. This sort of
statement creates a discontinuity at the switching point and also changes the structure of the
circuit. A better method is to use a transition statement with a conductance, with an rdsoff value
of say 10G ohm. A diode could be added in parallel to mimic a real device and allow the node to
find an operating point.

I(OUT,IN) <+ V(OUT,IN) * transition(switch ? (1.0/rdson):(1.0/rdsoff),td,tr,tf);

7.2 Common sub expression


It appears that compilers for Verilog-A are not as optimal as they could be, and simple things
like the same sub-expression being used multiple times is not optimised into one calculation and
reused. Therefore, it is best to do some of this leg work yourself as it will speed up the
simulation by not having to re-evaluate the same expression multiple times. The downside of this
is it can make the code harder to read.

SNUG 2014 29 How to Write an optimum Verilog-A model


7.3 Expressions
Never use absolute values for comparison of reals (== or !=) as they may not trigger, or they
could cause the simulator to force a time-step change. It is always better to use greater than (>=)
or less than (<=), so that any rounding does not cause odd effects. Integers do not suffer from
this rounding error.

7.4 Multiple Analog blocks


Verilog-A supports multiple analog blocks and most simulators now have implemented this
feature. Multiple analog blocks are merged together in the simulator, so their main uses are to
space blocks of code to make them more readable. In the context of this paper, 3 separate analog
blocks are used, the first is to create the supplied variable, the second is for any messages, and
the final is for the actual functional model.

7.5 Parameter Ranges


Verilog-A allows integer and real number parameters to be specified with ranges, where
possible these should be used as the check is applied before any simulation happens. String
parameter can have specific values they can be set to, which is often not mentioned in literature.
These sorts of checks just help remove any minor mistake in the netlist before any long
simulation is run.

parameter string param1 = "default" from {"option1", ... , "option1"};

7.6 Trims
Often blocks will have some trim or adjustment capability that affects the models. If this is
mult-bit wide quite often complex lines are code are created, whereas a simple for loop would
achieve the same value and is simpler to read. For example if you had a bus port called ota_gain
which was 4 bits wide. Using a digital input checker it could be converted to an internal integer
bus ota_gain_logic. Using the lines of code below this could be turned into a real number called
gain. Clearly if the step value is not unitised then it could use a parameter array to select the step.

//Min gain of 0.6 and gain step value of 0.05


gain = 0.6;
for(i=0;i<4;i=i+1) gain = gain + (check_eq(V(ota_gain_logic[i]),1) * pow(2,i) * 0.05);

7.7 Variable Capacitors


Some circuit might have variable capacitors whose value can be changed during a transient
simulation, or a capacitor model that tries to model a realistic device where the capacitance value
is a function of the voltage across its nodes. In both cases it is important that the model is written
so charge conservation in taken into account. Without this the simulator has to deal with odd
events, that can’t be done in the real world. [3] Explains a good way of modelling a variable
capacitor or you can use Equation 6.

SNUG 2014 30 How to Write an optimum Verilog-A model


dV dC
I =C +V
dt dV
Equation 6 Capacitor Equation with charge conservation

8 Conclusions
Using the methods mentioned in this paper it is possible to write an optimal Verilog-A for
most blocks with no discontinuities that the simulator is not aware of. Each block can have an
input to output transfer function that maps into the simulator matrix with feedback so none of the
sources are ideal. Using the wrapper cells and structures described one can focus on the model
internals that are key to get functional and parametric equivalence rather than the interface
elements. This methodology does require the model writer to approach the model in a different
way to normal, but the speed improvement will mean more test cases can be run in less time,
improving confidence in the tape out. Cutting the number of electrical nodes down in the
simulation matrix reduces the need for multi-threaded simulations and thus licence usage.
There is still a lot of ambiguity as to what should be supported in the analogue simulator
verses the LRM [1], because this describes the mixed mode language rather than the analogue
specific one. Some vendors have added features in that help their customers, but appear not to
part of the Verilog-A language. There are some areas of the language and implementation that
would help;
1. Firstly real ports would help hierarchy in a Verilog-A models. This would save
unnecessary electrical nodes, when wrapping block up, like the checkers.
2. Functions should be able to access variables within the module scope they are defined
in.
3. Compiler optimisations so the model writer doesn’t have to do them.
4. A language linter similar to the level currently available for System Verilog.
5. Laplace support for variable poles/zero’s with checks.

9 References

[1] http://www.accellera.org/downloads/standards/v-ams/VAMS-LRM-2-3-1.pdf.
[2] “An Alternative approach to connect modules in Verilog-AMS”, Peter Grove SNUG 2012
[3] “The Designer's Guide to SPICE and Spectre®”, ISBN-13: 978-0792395713
[4] http://www.designers-guide.org/modeling/varactors.pdf
[5] “An Efficient Method to Simulate Threshold-Crossing Events”, G. Peter Fang IEEE 2008
[6] “The Mixed-Signal Methodology Guide”, ISBN-13: 978-1300035206

SNUG 2014 31 How to Write an optimum Verilog-A model


10 Appendices

10.1 Voltage/Current checker code


`include "constants.vams"
`include "disciplines.vams"
`include "messages.va"

module VI_CHECK(RX,RAIL,FLAG,VAL);

/**********************************************************************
*** Ports ***
*********************************************************************/
inout RX;
inout RAIL;
output FLAG;
output VAL;

/**********************************************************************
*** Port Types ***
*********************************************************************/
electrical RX;
electrical RAIL;
voltage FLAG;
voltage VAL;

/**********************************************************************
*** Parameters ***
*********************************************************************/
parameter real tol = 1.0e-9 from [0:inf];
parameter real ACTIVE_HIGH = 0.0;
parameter real ACTIVE_LOW = 0.0;
parameter real INACTIVE_HIGH = (ACTIVE_HIGH > 0.0) ? ACTIVE_HIGH+tol:tol;
parameter real INACTIVE_LOW = (ACTIVE_LOW < 0.0) ? ACTIVE_LOW -tol:-tol;

parameter real td = 0.0n from [0:10n];


parameter real tr = 0.0n from [0:10n];
parameter real tf = tr from [0:10n];

parameter real warn_dt = 10n from [0:inf];


parameter real error_dt = 10n from [0:inf];

parameter real RX_c = 0.0;


parameter real RX_g = 0.0;

parameter real vth = 0.40 from [0:inf];


parameter real beta = 45.0u from [0:inf];

parameter string chk_type = "ND" from {"ND", "PD", "V"};

/**********************************************************************
*** Local Parameters ***
*********************************************************************/
localparam ACTIVE = 1;
localparam INACTE = 0;
localparam INVALI = -1;

/**********************************************************************
*** Varibale declarations ***
*********************************************************************/
integer flag_int;

real value;

real warn_time;
real error_time;

SNUG 2014 32 How to Write an optimum Verilog-A model


string my_message;

/**********************************************************************
*** Model ***
*********************************************************************/
analog begin
if(chk_type == "ND") begin
I(RX,RAIL) <+ -1e-15 * (limexp(V(RAIL,RX)/$vt ) - 1.0); //Diode protection
I(RX,RAIL) <+ (V(RX,RAIL)>vth) * beta * pow(V(RX,RAIL)-vth,2); //MOS diode
I(RX,RAIL) <+ RX_c * ddt(V(RX,RAIL)); //MOS Bias Cap
value = I(RX,RAIL);
end
else if(chk_type == "PD") begin
I(RAIL,RX) <+ -1e-15 * (limexp(V(RX,RAIL)/$vt ) - 1.0); //Diode protection
I(RAIL,RX) <+ (V(RAIL,RX)>vth) * beta * pow(V(RAIL,RX)-vth,2); //MOS Diode
I(RX,RAIL) <+ RX_c * ddt(V(RX,RAIL)); //MOS Bias Cap
value = I(RAIL,RX);
end
else if(chk_type == "V") begin
I(RX,RAIL) <+ RX_g * V(RX,RAIL); //Voltage Load
I(RX,RAIL) <+ RX_c * ddt(V(RX,RAIL)); //Voltage Cap
value = V(RX,RAIL);
end
else begin
$sformat(my_message, "Invalid parameter option %0s passed into cell. Valid options are ND, PD,
V.",chk_type);
`my_error(my_message)
$finish(0);
end

`ifdef NO_CHECKS
flag_int = ACTIVE;
`else
@(initial_step) begin
error_time =-1;
warn_time =-1;
end

if((value <= ACTIVE_HIGH) && (value >= ACTIVE_LOW)) begin


if(flag_int != ACTIVE) begin
$sformat(my_message, "%0s at Active level of %0g",(chk_type=="V" ? "Voltage":"Bias"),value);
`my_info(my_message)
end
flag_int = ACTIVE;
warn_time = -1.0;
error_time = -1.0;
end
else if((value <= INACTIVE_HIGH) && (value >= INACTIVE_LOW)) begin
if((warn_time < 0.0) && (flag_int!=INACTE)) begin
warn_time = $abstime + warn_dt;
error_time = -1.0;
end
if((warn_time > 0.0) && (flag_int==INACTE)) warn_time = -1.0;
end
else begin
if((error_time < 0.0) && (flag_int!=INVALI)) begin
error_time = $abstime + error_dt;
warn_time = -1.0;
end
if((error_time > 0.0) && (flag_int==INVALI)) error_time = -1.0;
end

//Warning timer event using if statement rather than timer as quicker.


if(($abstime >= warn_time) && (warn_time > 0)) begin
flag_int = INACTE;
$sformat(my_message, "%0s at InActive level of %0g",(chk_type=="V" ? "Voltage":"Bias"),value);
`my_warn(my_message)
warn_time = -1.0;
end

SNUG 2014 33 How to Write an optimum Verilog-A model


//Error timer event using if statement rather than timer as quicker.
if(($abstime >= error_time) && (error_time > 0)) begin
flag_int = INVALI;
$sformat(my_message, "%0s at InValid level of %0g",(chk_type=="V" ? "Voltage":"Bias"),value);
`my_error(my_message)
error_time = -1.0;
end
`endif

//Drive out signals


if(td == 0.0) begin
if(tr == 0.0 || tf == 0.0) V(FLAG) <+ flag_int;
else V(FLAG) <+ transition(flag_int,td,tr,tf);
end
else V(FLAG) <+ absdelay(flag_int,td);

V(VAL) <+ value;

end

endmodule

10.2 Digital input checker code


`include "constants.vams"
`include "disciplines.vams"
`include "messages.va"

module DIGINCHECK(A,VDD,VSS,D);

/**********************************************************************
*** Ports ***
*********************************************************************/
input A;
input VDD;
input VSS;

output D;

/**********************************************************************
*** Port Types ***
*********************************************************************/
electrical A;

electrical VDD;
electrical VSS;

voltage D;

/**********************************************************************
*** Parameters ***
*********************************************************************/
parameter real ACTIVE_LOW = 0.0;
parameter real VGSMAX = 0.0;
parameter real ART_VDD = 0.0;
parameter real ART_VSS = 0.0;

parameter real A_cvdd = 0.0 from [0:100p];


parameter real A_gvdd = 1e-15 from [0:inf];
parameter real A_cvss = 1.0p from [0:100p];
parameter real A_gvss = 1e-15 from [0:inf];

parameter real A_dt = 10n from (0:inf);


parameter real A_vh = 0.7 from (0:1);

SNUG 2014 34 How to Write an optimum Verilog-A model


parameter real A_vl = 0.3 from (0:1);

parameter integer D_LOW_SUP = 0 from [-1:1];

parameter real td = 0.0 from [0:10n];


parameter real tr = 0.0 from [0:10n];
parameter real tf = tr from [0:10n];

/**********************************************************************
*** Local Parameters ***
*********************************************************************/
localparam LOGIC_HIGH = 1;
localparam LOGIC_LOW = 0;
localparam LOGIC_X = -1;

/**********************************************************************
*** Varibale declarations ***
*********************************************************************/
integer a_logic_int;

real error_time;

real vdd_vss;
real vdd_a;

string my_message;

/**********************************************************************
*** Model ***
*********************************************************************/
analog begin

//Define some variables at circuit setup.


@(initial_step) begin
error_time= -1;
end

//Model input impedance between the rails


I(VDD,A) <+ A_cvdd * ddt(V(VDD,A));
I(VDD,A) <+ A_gvdd * V(VDD,A);
I(A,VSS) <+ A_cvss * ddt(V(A,VSS));
I(A,VSS) <+ A_gvss * V(A,VSS);

//Cope with artificial VDD


if(ART_VDD > 0.0) begin
vdd_vss = ART_VDD - V(VSS);
vdd_a = ART_VDD - V(A);
end
else begin
vdd_vss = V(VDD,VSS);
vdd_a = V(VDD,A);
end

//Continuous time output evaluation with debounce on X region.


if(vdd_vss >= ACTIVE_LOW) begin
if( (V(A,VSS) >= (A_vh*vdd_vss)) && (V(A,VSS) < VGSMAX)) begin
a_logic_int = LOGIC_HIGH;
error_time = -1.0;
end
else if( (V(A,VSS) <= (A_vl*vdd_vss)) && (vdd_a < VGSMAX)) begin
a_logic_int = LOGIC_LOW;
error_time = -1.0;
end
else begin
//In an unknown region, before we set the output unknown we should
//give some debounce time to make sure it is not transitioning
//from Low to high.
//a_logic_int = a_logic_int; <- Not needed.

SNUG 2014 35 How to Write an optimum Verilog-A model


if((error_time < 0.0) && (a_logic_int!=LOGIC_X)) error_time = $abstime + A_dt;
if((error_time > 0.0) && (a_logic_int==LOGIC_X)) error_time = -1.0;
end
end
else begin
//Port supply too low.
a_logic_int = D_LOW_SUP;
error_time = -1.0;
end

//Timer for setting the output to the X region.


if(($abstime>=error_time) && (error_time > 0)) begin
a_logic_int = LOGIC_X;
$sformat(my_message, "Input is in the Logic 1'bx region, value set to -1.");
`my_error(my_message)
end

//Drive out logic value


if(td == 0.0) begin
if(tr == 0.0 || tf == 0.0) V(D) <+ a_logic_int;
else V(D) <+ transition(a_logic_int,td,tr,tf);
end
else V(D) <+ absdelay(a_logic_int,td);
end

endmodule

SNUG 2014 36 How to Write an optimum Verilog-A model

You might also like