CppSim Reference Manual
CppSim Reference Manual
Michael H. Perrott
http://www-mtl.mit.edu/∼perrott
Copyright
c 2002 by Michael H. Perrott
All rights reserved
2
3
Isaiah 53:2,5
4
Contents
1 Foreword 9
2 Introduction 11
2.1 Comparison to Other Simulation Packages . . . . . . . . . . . . . . . . . . . 11
2.2 Object Oriented Simulation Code . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3 The Issue of Ordering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.4 Outline of Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4 Overview 23
4.1 Schematic Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.2 Netlist Format (netlist) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.3 Module Description File (modules.par) . . . . . . . . . . . . . . . . . . . . . 27
4.4 Simulation Description File (test.par) . . . . . . . . . . . . . . . . . . . . . . 30
4.5 CppSim Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.6 Viewing Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5
6 CONTENTS
5.6 probe64: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.7 global nodes: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.8 global param: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.9 top param: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.10 alter: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.11 inp timing: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.12 inp dig: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.13 mex prototype: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.14 simulink prototype: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.15 add top verilog libary file statements: . . . . . . . . . . . . . . . . . . . . . . 44
5.16 add bottom verilog libary file statements: . . . . . . . . . . . . . . . . . . . 45
5.17 add verilog test file statements: . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.18 add verilog test module statements: . . . . . . . . . . . . . . . . . . . . . . . 46
Foreword
As an IC designer, I often found myself frustrated by existing behavioral simulation tools, and
would typically go down the road of writing my own C or C++ code to examine architectural
issues. Now that I’ve entered the academic realm, I find myself wanting to pass on the ‘tricks
of the trade’ I have learned over the years, and thereby speed up the progress of my students.
Also, I have observed a general need for system simulation tools that are fast and flexible
and also integrated within current CAD tool frameworks for IC design.
The CppSim simulation package is my response to those needs. My hope is that it will
allow my students, and others, to quickly assess architectural ideas and then seamlessly move
on to circuit design within the same CAD framework, and to leverage each others system
designs through the existence of a common framework for behavioral simulation.
C++ was chosen as the simulator language do its powerful features and fast execution
speed. It turns out that C++ is a fantastic language for representing high level systems
due to its support for object oriented descriptions. Indeed, systems can be described in a
hierarchical manner, object code can be executed in a multi-rate manner, and signals can
be stored in binary format compatible with other simulators.
A significant problem with C++ is that most circuit designers do not like to program,
and the learning curve for C++ is perceived as formidable. Also, complex system descrip-
tions quickly become unrecognizable in the form of text, and are much better specified in a
graphical manner to allow the designer to ‘see’ signal paths and topological structures such
as feedback loops.
The CppSim package makes two contributions to the behavioral simulation of systems.
First, it provides a netlist to C++ translator that allows the C++ simulation code to be au-
tomatically written based on a SPICE-compatible netlist produced by a graphical schematic
9
10 CHAPTER 1. FOREWORD
capture program. In doing so, the designer can quickly piece together a system in a graphical
manner based on a library of system primitives with corresponding code descriptions, and
benefit from the power and speed of running compiled C++ code. Second, the CppSim
package provides a set of C++ classes that allow fast and convenient implementation of
system primitives. Common system blocks such as filters, VCO’s, nonlinear amplifiers, and
signal generators are easily realized using these classes, so that the creation of new system
primitives is typically fast and straightforward. Also, special blocks, which are based on
the area-conservation approach described in the paper referenced below, are included which
allow fast and accurate behavioral simulation of phase locked loop and delay locked loop
systems.
Currently, the CppSim package is license-free software that may be used for either aca-
demic or commercial use. The source code for the C++ classes is provided, and binary files
for implementing the netlist to C++ translator are included for the SUN Solaris operating
system. It is possible that future versions of the CppSim package will be commercialized,
but that is not a path that is being pursued at the time of this writing. If you benefit from
the use of this package, it would be appreciated if you would tell others, and also include a
reference to the package in any papers you publish for which the software proved useful. For
general simulation of systems, an appropriate reference would be:
Perrott, M.H., “CppSim Behavioral Simulator Package,”
http://www-mtl.mit.edu/∼perrott
If you apply the package to the simulation of phase locked loop or delay locked loop systems,
it would be appreciated if you would also include the reference:
Perrott, M.H., “Fast and Accurate Behavioral Simulation of
Fractional-N Frequency Synthesizers and other PLL/DLL Circuits,”
Design Automation Conference, June, 2002
Michael Perrott
Massachusetts Institute of Technology
August 2002
Chapter 2
Introduction
This chapter introduces the CppSim package in a broad manner so that the reader can
develop a sense of how it fits in with other simulation packages, understand its overall
framework, and be aware of the assumptions it makes. We begin by comparing CppSim to
other simulation packages, discussing its object oriented framework and the issue of execution
order, and then providing a summary of the rest of the book.
This section compares the SPICE, Simulink, and Verilog AMS simulation packages to the
CppSim package. This information will hopefully allow the user to understand the strengths
and weaknesses of CppSim, and to see how it fits in with other simulators used in the current
IC design flow.
SPICE
The SPICE simulation environment determines the solution to a set of simultaneous equa-
tions that are specified through a netlist describing the interaction between the system nodes.
This ‘fine-grain’ simulator is required when attempting to estimate the performance of ana-
log circuits implemented with transistors and passive elements. However, the solution of
simultaneous equations is a slow process, and the resulting simulation times are too long to
allow characterization of the behavior of medium to large systems.
11
12 CHAPTER 2. INTRODUCTION
Simulink
Many systems are designed in a block diagram manner in which there is little interaction
between elements contained in different blocks. In this case, there is no need to solve simul-
taneous equations, but, rather, the system can be viewed as a set of expressions relating the
outputs of each block to its inputs and internal state. The overall outputs of the system can
be calculated in terms of its inputs by properly assembling the individual block relationships
into a large computation structure.
Simulink assumes such a block level view of the system, and assembles the individual
block relationships into one giant state-space description of the overall system. The response
of the overall system outputs to variations of its inputs is then computed using standard
matrix computations. Since the level of detail of the system is much lower than would be
encountered in SPICE, and there is no need to solve simultaneous equations, the computation
runs much faster than SPICE and the behavior of medium size systems can be explored.
Also, the graphical interface of Simulink allows beginners to quickly come up to speed in
doing simulations of a desired system.
Verilog AMS
Verilog AMS is one of the most promising simulation environments to appear on the IC
CAD scene for some time. This simulator combines SPICE and Verilog simulators into
a common simulator core, and therefore allows analog blocks to be described in terms of
coupling relationships between nodes, and digital blocks to be described in terms of Verilog
code. Therefore, analog and digital circuits can be co-simulated, and the overall behavior,
and possibly even performance, of the system can be investigated.
Unfortunately, Verilog AMS currently has some deficiencies when trying to investigate
systems at an architectural level. Specifically, it lacks a set of fast, high level macromodels to
describe analog blocks at a behavioral level. The approach of using SPICE representations
to represent such blocks has two major drawbacks — the resulting simulation times are too
long, and the level of detail that needs to be supplied by the user is too great. A follow on
project to CppSim is to develop fast, high level macromodels for analog blocks within the
Verilog language. The resulting simulation times are expected to be orders of magnitude
faster than their SPICE counterparts, so that the overall behavior of a large system with
analog and digital sections can be investigated in a timely manner.
CppSim
The C++ language offers the flexibility of computing system behavior in any manner desired
— it can based on the solution of simultaneous equations as assumed in SPICE or on the
solution of input/state/output relationships as assumed in Simulink. It is indeed a powerful
language, and allows you to quickly perform low level computation while also offering high
level structural constructs such as classes. The ability to represent systems in an object
oriented manner allows an elegant framework for their simulation. These facts make C++
the language of choice for designers that want the maximum freedom in developing simulation
code for an investigated system.
Unfortunately, C++ has the drawbacks that it requires a large amount of effort to develop
simulation code, and that the resulting text description of the system is much less intuitive
than a graphical representation. The CppSim package removes these issues by supplying
classes that allow easy representation of system building blocks such as filters, amplifiers,
VCO’s, etc., and by supplying a netlist to C++ conversion utility that enables automatic
code generation from a graphical description using any mainstream schematic editor package.
The resulting environment provides both beginners and advanced users a powerful tool for
14 CHAPTER 2. INTRODUCTION
simulating large systems, and also enables the tool to be completely integrated within all
mainstream IC CAD tools that support SPICE netlisting.
The simulation approach taken with CppSim is to represent blocks in the system based
on input/state/output relationships as done with Simulink. However, unlike Simulink, these
relationships do not need to be placed in state-space form, and conditional loops are sup-
ported rather than vectorized expressions. The blocks are represented in an object oriented
manner, and the simulation code calculates the overall system behavior by computing the
output of each block one at a time for each sample point in the simulation. This approach
carries the advantage of allowing straightforward description of blocks, fast computation,
and the ability to easily support multi-rate operation of different blocks in the system.
The primary disadvantages to CppSim are that it is limited to architectural investigation
since Verilog and SPICE descriptions of blocks are not supported, and that the user needs
to pay attention to the computation order of blocks in some cases. Note that the latter
issue will be discussed in more detail below. All things considered, however, I believe that
CppSim will offer designers an extremely fast and powerful framework for investigating the
behavior of large systems.
A B C
In the above code, we see that the simulation consists of the following structure:
1. Declaration statements
As stated above, the behavior of objects is specified in the declaration section — examples of
declaration statements for various classes are given in Chapters 7 and 8. A main simulation
loop executes the ‘inp’ routine for all simulation blocks according to the order they are placed
within the loop. The ‘inp’ routine updates the current outputs of the object based on inputs
entering the routine. If there are no inputs, the output value is updated solely on its current
state and declared behavior. As revealed in the above code, blocks can be placed within
16 CHAPTER 2. INTRODUCTION
conditional statements so that their output value is updated only when the statement is
true. By doing so, blocks can be executed in multi-rate fashion according to, for instance,
clock signals generated by other blocks in the system. The outputs of each block, as well as
important state variables, can be easily accessed for each block by using the notation
block name.signal
The code above illustrates this point for the output signals of various blocks. Finally, signals
associated with different blocks are saved to a file using the ‘probe’ function.
Execution Order A B C
A
E
B Delay D
C
D E
(3)
Execution Order A B C
A
B
C Delay D
D
E E Delay
(4)
Execution Order A Delay B C
E
B
C Delay D
D
A E
other regardless of the higher level portion of the system. Therefore, it is a good strategy to
encapsulate blocks that are order-sensitive with respect to each other into the same cell so
that their order remains constant regardless of changes you make to the rest of the system.
code examples using the CppSim classes, and Appendix B provides documentation for the
Hspice Toolbox for Matlab, which is useful for viewing and postprocessing simulation results.
Chapter 3
This chapter outlines basic instructions for installing and running CppSim.
3.1 Installation
It is assumed that you are currently in possession of a file called setup_cppsim3.exe. This
self-extracting file supports operation in Windows Vista/XP/2000, and includes Sue2, Cpp-
Sim, and the MinGW and MSYS packages which provide g++, make, sh, and other routines.
Other installations of CppSim support use of Cadence for design entry — see the CAD tools
link at
http://www.cppsim.com
for details.
Install the CppSim package by running
setup_cppsim3.exe
in Windows (i.e., simply double-click on it in Windows Explorer). You may place the
main CppSim directory at any desired location, though it is advised that you place it at a
convenient place for editing files that are contained within it. The self-extracting file will
not only extract all the required files (which will all be contained with the CppSim main
directory), but will also automatically add the appropriate directories to your Windows Path
variable to allow seamless execution of CppSim. Note that a Windows environment variable
called CppSimHome will also be created during the installation. In order for Windows to
recognize the updated Path and CppSimHome variables, you should restart your machine
after completing the installation.
19
20 CHAPTER 3. SETUP AND USE (WINDOWS VERSION)
addpath(’c:/CppSim/CppSimShared/HspiceToolbox’)
at the Matlab prompt, where c:/CppSim should be replaced by the actual path you chose
for CppSim during the installation. Note that you must repeat the above operation each
time you start Matlab, or you may also include the above statement in a startup.m file that
Matlab automatically executes during startup.
As an example of running CppSim, go to the simulation directory for the cell sd_synth_fast
by typing
cd c:/CppSim/SimRuns/Synthesizer_Examples/sd_synth_fast
within Matlab. Again, you must substitute the proper path that you chose for CppSim in
place of c:/CppSim. If you type ls at the Matlab prompt, you will find three files: test.par,
comp_psd.m, and netlist.
Once you are in that directory, simply type
cppsim
at the Matlab prompt. CppSim should run and generate a bunch of warning messages (just
ignore them in this case). Once the run has completed, load the signals into Matlab by
typing
x = loadsig(’test.tr0’);
You can then view which signals have been probed by typing
lssig(x);
plotsig(x,’sd_in;vin’);
See the Hspice Toolbox manual for more commands related to viewing and post-processing.
3.3. CPPSIMSHARED DIRECTORY CONTENTS 21
• CommonCode
– Contains the CppSim classes, an example my classes and functions.h and .cpp
file, and an example modules.par file that matches the CppModules Sue2 library.
• Doc
– Contains this document, the Sue2 manual, and an expanded DAC paper describ-
ing techniques to achieve fast and accurate PLL simulations. These techniques
are implemented in the CppSim classes provided for PLL/DLL simulations.
• HspiceToolbox
– Contains the Hspice Toolbox for Matlab, which allows a convenient and powerful
waveform viewer and postprocessor for simulated signals from the Hspice and
CppSim simulators.
• MatlabCode
– Contains Matlab code useful for plotting the simulated phase noise of the syn-
thesizer examples and the simulated phase error of the clock and data recovery
examples.
• NonNetlistExamples
– Contains example C++ code based on the CppSim classes for frequency synthe-
sizers and clock and data recovery circuits.
• SimRuns
– Contains the test.par files and netlists for two example systems, sd synth and
sd synth fast, contained in the Sue2 library CppExamples.
• bin
– Contains the Windows binary files net2cpp.exe, sue spice netlister.exe, and the
shell script cppsim. The net2cpp.exe program performs netlist to C++ conver-
sion, while the sue spice netlister.exe program generates Spice netlists from Sue2
22 CHAPTER 3. SETUP AND USE (WINDOWS VERSION)
schematic files. The cppsim script is a wrapper program that coordinates auto-
matic netlisting, netlist to C++ conversion, compilation, and executation of a
given simulation run.
• Sue2
– Contains the Sue2 schematic editor package, which is also available as a stand-
alone application at the CAD tools link found at http://www-mtl.mit.edu/ per-
rott.
• MinGW
• msys
Overview
This section provides an overview of the steps involved in running simulations within the
CppSim framework. The intention is to provide the reader with a feeling of the issues
involved — more detailed explanations will be covered in the following chapters. As such,
we will look at an example simulation system that is provided with the CppSim package,
namely the sd synth cell included in Sue2 library CppExamples. We will examine schematic
views associated with this cell, the corresponding netlist and modules.par file, the main
simulator description file test.par used to set the key simulator parameters, a description
of the UNIX commands required to perform the simulation, and a brief overview of how to
view the results.
23
24 CHAPTER 4. OVERVIEW
also be specified in terms of expressions that include higher level parameters (see the next
paragraph) or global variables. For instance, the step in symbol in Figure 4.1 contains
expressions involving global parameters. Here we have added the suffix ‘ gl’ to alert us to
the fact that the parameter is global, but this notation is not necessary.
The CppSim simulator allows schematics to be hierarchical in nature, so that symbols
at any level can be represented by schematics consisting of other symbols. As an example,
in Figure 4.1, the PFD symbol has an associated schematic that is shown in Figure 4.2. In
turn, the nand2 symbol within the PFD schematic also has an associated schematic, which
is not shown here for the sake of brevity. It is important to note that parameters can be
passed between levels of hierarchy, so that symbols contained in the schematic view of a
higher level symbol can inherit parameters values from the higher level symbol.
At the lowest level in the schematic hierarchy, symbols must correspond to C++ code
that implements the function associated with the symbol. These symbols are referred to as
primitives, and have an associated schematic that typically does not contain other symbols.
Such a schematic will often look like the one shown in Figure 4.3, which corresponds to
an XOR gate that has inputs a and b, and an output y. However, the schematic view of
primitives can also contain other instances, transistors, resistors, etc. — it simply ignores
everything within it when code is specified for it in the ‘modules.par’ file. Therefore, the
module definitions within the ‘modules.par’ file control the level of hierarchy that is de-
scended to in any cell. For instance, if you desired to go deeper into the hierarchy of a
cell that you already defined code for in ‘modules.par’, simply comment out the module
4.2. NETLIST FORMAT (NETLIST) 25
definition in ‘modules.par’ and add in new definitions for the instances within the cell. Note
that in all cases, whether code for a module is defined or not, all non-instance elements
contained in the netlist, such as transistors, capacitors, and resistors, are completely ignored
by CppSim.
A few more words are in order with respect to parameters associated with instances
in the netlist. The netlist above shows parameters associated with instances but not their
corresponding module definitions. In practice, one may also specify parameters in the module
definition to specify default values for parameters. For instance, the module definition for
the lead/lag filter could be specified as:
∗∗∗∗∗∗∗∗∗∗∗∗∗∗ Module leadlagfilter ∗∗∗∗∗∗∗∗∗∗∗∗∗∗
.subckt leadlagfilter out in fp=100e3 gain=1/100 fz=10e3
.ends leadlagfilter
In this case, if any instances of the lead/lag filter declared in other modules omit any
of the parameters declared in the above lead/lag filter module definition, the value of those
omitted parameters will be set to the default value specified in the above module definition.
module: constant
description: constant for input to other blocks
parameters: double consval
28 CHAPTER 4. OVERVIEW
inputs:
outputs: double out
static variables:
classes:
init:
code: 20
out = consval;
module: noise
description: Gaussian noise source
parameters: double var
inputs:
outputs: double out
static variables:
classes: Rand randg("gauss")
init: 30
code:
out = sqrt(var/Ts)∗randg.inp();
module: step in
description: step input
parameters: double vend double vstart double tstep
inputs:
outputs: double step
classes:
static variables: double i 40
init: i=0.0;
code:
step = i∗Ts > tstep ? vend : vstart;
i++;
module: vco
description: voltage controlled oscillator
parameters: double freq double kvco
inputs: double vctrl
outputs: double squareout double sineout 50
static variables:
classes: Vco vco("fc + Kv*x","fc,Kv,Ts",freq,kvco,Ts);
init:
code:
vco.inp(vctrl);
4.3. MODULE DESCRIPTION FILE (MODULES.PAR) 29
squareout = vco.out;
sineout = sin(vco.phase);
module: leadlagfilter
description: lead/lag filter 60
module: sd modulator
description: Sigma−Delta modulator with multi−bit output
parameters: int order
inputs: double in double clk
outputs: double out
classes: SdMbitMod sd mod("1 - z^-1"), EdgeDetect clkedge()
static variables:
init:
if (order == 1)
sd mod.set("1 - z^-1"); 80
else if (order == 2)
sd mod.set("1 -2z^-1 + z^-2");
else
sd mod.set("1 -3z^-1 + 3z^-2 - z^-3");
out = 2.0;
code:
if (clkedge.inp(clk))
{
sd mod.inp(in);
out = sd mod.out; 90
As seen in the above file, each module is described by a list of items that includes its
inputs, outputs, and parameters along with a specification of their respective types (i.e., int,
double, etc.). The input, output, and parameter names must match those in the correspond-
ing module definition in the netlist (i.e., module ‘gain’ must have nodes a and y in its module
30 CHAPTER 4. OVERVIEW
definition along with parameter gain in either the module definition or the corresponding
instance calls). It should be noted that the netlist is converted to lowercase text, so that the
input, output, and parameter names must all be lowercase in any module definition in the
module.par file.
Each module definition must also specify all classes that are used for its code implemen-
tation, along with initialization and main code descriptions. Initialization code is run only
once at the beginning of a simulation, while the main code is run each time step of the
simulation. Note that any variables declared in the main code section will lose their value
each time the time step is incremented in the simulation. If variables are required which
must retain their values between time steps, they should be declared as static variables using
the ‘static variables:’ command.
Please see Chapter 6 for more information on writing module description files, which
includes issues related to syntax and information on the various commands that are available.
Please see Chapter 5 for more information on writing simulation description files, which
includes issues related to syntax and information on the various commands that are available.
97
96
95
sd_in
94
93
92
0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2
−4
x 10
2
vin
−1
0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2
TIME −4
x 10
Figure 4.4 Synthesizer response to a step input — top plot: input to sd modulator, bottom
plot: closed loop response of VCO input voltage.
Chapter 5
This chapter discusses the various commands available for use in the simulation description
file, which we refer to as the ‘test.par’ file. We begin by covering general parsing issues,
including the notation for commenting out lines, and will then discuss the various commands
in detail.
33
34 CHAPTER 5. SPECIFYING SIMULATION PARAMETERS (TEST.PAR)
As a rule of thumb, one should never separate an expression into multiple lines. As an
example, you should NOT write
global param: a gl=
3.3
Either ‘*’ or ‘%’ or ‘//’ characters can be used to comment out lines provided that they are
the first characters encountered on a line. As an example
* global param: a gl=3.3 b gl=-5 c gl=.7e6*Ts
*** global param: a gl=3.3 b gl=-5 c gl=.7e6*Ts
% global param: a gl=3.3 b gl=-5 c gl=.7e6*Ts
%%% global param: a gl=3.3 b gl=-5 c gl=.7e6*Ts
// global param: a gl=3.3 b gl=-5 c gl=.7e6*Ts
///// global param: a gl=3.3 b gl=-5 c gl=.7e6*Ts
are all valid ways of commenting out a line.
Descriptions of the individual commands used in a ‘test.par’ file are presented below:
5.3 Ts:
The value of the simulation period, Ts, is specified with this statement. Note that Ts
becomes a global variable in the C++ simulation code, and is often used in module parameter
statements as well as module code. An example of the syntax of this command is:
Ts: 1/10e9
5.4 output:
The name of the Hspice-compatible binary output file for signals specified in the following
‘probe:’ or ‘probe64:’ statement. Nominally, the specified signals are stored every time step
of the simulator. Optional parameters allow one to store the signal values only on the rising
5.4. OUTPUT: 35
edges of a trigger signal, when an enable signal is above zero, or when the time sample or
time value is greater than or equal to a given value.
Example: save signals every time step in the binary file ‘signals.tr0’
output: signals
probe: a b y xi12.net12
Example: save signals only when the signal ‘xi1.clk’ has a rising edge in the binary file
‘signals2.tr0’
output: signals2 trigger=xi1.clk
probe: a2 b2 xi3.xi1.sd out
Example: save signals only when the signal ‘clk’ has a falling edge in the binary file ‘sig-
nals3.tr0’
output: signals3 trigger=-xi1.clk
probe: a2 b2 xi3.xi1.sd out
Example: save signals only when the signal ‘clk’ is greater than zero in the binary file
‘signals3.tr0’
output: signals3 enable=xi1.clk
probe: a2 b2 xi3.xi1.sd out
Example: save signals only when the signal ‘clk’ is less than or equal to zero in the binary
file ‘signals3.tr0’
output: signals3 enable=-xi1.clk
probe: a2 b2 xi3.xi1.sd out
Example: save signals only when the simulation time step is greater than or equal to 1000
output: signals3 start sample=1000
probe: a2 b2 xi3.xi1.sd out
Example: save signals only when the simulation time value is greater than or equal to 1e-6
output: signals3 start time=1e-6
probe: a2 b2 xi3.xi1.sd out
Example: save signals only when the simulation time step is less than or equal to 3000
output: signals3 end sample=3000
probe: a2 b2 xi3.xi1.sd out
Example: save signals only when the simulation time value is less than or equal to 3e-6
36 CHAPTER 5. SPECIFYING SIMULATION PARAMETERS (TEST.PAR)
5.5 probe:
The signals specified with this statement are saved in an Hspice-compatible binary file whose
name is specified by the last ‘output:’ command encountered before this statement. Signals
contained in the top level of the schematic are specified by their name, and signals at lower
levels of hierarchy are specified using standard SPICE notation by their name and by the
chain of instances that lead to the cell view that the signal is contained in. An example of
the syntax of this command is:
probe: vin pfdout sd in div val xi12.xor out xi12.xi7.y
where xi12.xor out corresponds to signal xor out contained within instance xi12 in the top
level of hierarchy, and xi12.xi7.y corresponds to signal y contained within instance xi7 that
is within instance xi12. Note that the number of levels of hierarchy within the system can
be as large as the user desires.
5.6 probe64:
The same as ‘probe:’, except that values are stored as 64-bit values rather than 32-bit values.
Files created with ‘probe64:’ are roughly twice as large as those created with ‘probe:’, but
provide double-precision rather than single-precision accuracy for signals.
An example of the syntax of this command is:
probe64: vin pfdout sd in div val xi12.xor out xi12.xi7.y
5.7. GLOBAL NODES: 37
In practice, it is inappropriate for global nodes to correspond to the output node of any
instance — they should always correspond to input nodes. No checking is done to insure
this is the case.
5.10 alter:
You can alter global parameters several ways using the ‘alter:’ statement, which will now be
explained through a series of examples. In all of the examples, the ‘alter:’ statement(s) are
assumed to be placed after the ‘global param:’ statement that defines the global parameters
being altered.
Example: do simulations over all combinations of a gl = 15,18 and b gl = 1e3,2e3
alter: a gl = 15 18
alter: b gl = 1e3 2e3
Example: do simulations where a gl and b gl are altered together,
i.e., a gl,b gl = 15,1e3 and a gl,b gl = 18,2e3
alter: a gl = 15 18 b gl = 1e3 2e3
Example: do combinations where a gl and b gl are altered together in combination with
values of c gl = 1,2,3,4,5
alter: a gl = 15 18 b gl = 1e3 2e3
alter: c gl = 1 2 3 4 5
Example: an easier way to do the above is to use Matlab notation:
alter: a gl = 15 18 b gl = 1e3 2e3
alter: c gl = 1:5
Example: suppose you want to increment c gl by 0.1 instead of 1
alter: c gl = 1:0.1:5
Example: combine Matlab notation with individual specifications
alter: c gl = 1e3 5e3:1e3:10e3 20e3 50e3 100e3:100e3:1e6 2e6
The resulting output of performing such ‘alter:’ operations is to produce a separate
output file for each global parameter combination. As an example, if
output: signals
is specified in the test.par file, then a a series of files
signals.tr0 signals.tr1 signals.tr2 . . .
will be produced. If you have multiple ‘output:’ statements, then a series of files will be
produced for each of those output files.
To see how the global variable combinations match up to their corresponding transient
runs in this case, you can load test globals.tr0 in Matlab. Note that the prefix ’test’ was
determined by the name of the simulation description file, ’test.par’. If you instead, as an
example, named this file ’test2.par’, you would load test2 globals.tr0. Each altered global
variable will be a signal in that file whose value for each simulation run is specified.
5.11. INP TIMING: 39
Example: create two clock signals at different frequencies along with accompanying signals
inp timing: 0 0 1/1e9 0 1
inp dig: clk slow [1 0 R]
inp dig: data slow [1 0 1 1 0 R]
inp timing: 0 0 1/2e9 0 1
inp dig: clk fast [1 0 R]
inp dig: data fast [0 1 1 1 0 0 1 R]
In the above example, signals vin and xi12.xor_out correspond to signals which have been
placed within ‘probe:’ statements elsewhere in the simulation file. These signals will be
output by the above Matlab mex call upon its completion — all conditions imposed by the
associated ‘probe:’ statements for each signal will be observed (such as triggering or enabling
by other signals, start and stop times, etc.) so that the vector lengths of each of these signals
may differ from each other.
The name of the mex function, which in the above case is ‘sd synth fast’, must be the
same as the associated CppSim top level cell.
5.13. MEX PROTOTYPE: 41
The input signals in and in2 can have different lengths in the above case since num_sim_steps
is specified, but must have the same length if num_sim_steps is not present in the prototype
definition. Both global and top cell parameters, such as param1 in the above example, can
be specified. The specification of num_sim_steps and Ts are optional. If num_sim_steps is
not specified, then the number of simulation steps per mex call will be set by the length of
the input signals. If num_sim_steps is not specified and no input signals are present, then
the number of simulation steps per mex call will be the same as specified in the simulation
file (i.e., test.par). If Ts is not specified, then the simulation step size will also be as specifed
in the simulation file.
Repeated calls to the CppSim mex function will cause the simulation to continue from
its last stopping point. To reset the simulation to its starting point, you must specify ‘end’
within the mex call. For the above example, you would run
sd synth fast(‘end’);
within Matlab. Resetting the simulation allows parameters (including num_sim_steps and
Ts) to be updated. In contrast, input signals can be changed for each mex call regardless of
whether the simulation has been reset.
In Windows, you need to specify a C++ compiler rather than the C compiler that comes
with Matlab. Thus far, only the Microsoft C++ compiler has been verified to work — a free
version of this compiler is available at
http://msdn.microsoft.com/vstudio/express/visualc/
After downloading this package, you’ll need to add a few missing library files to the directory
which, for convenience, have been placed within the CppSim package (Windows version) in
the directory
c:/CppSim/CommonCode/msft_SDK
mex -setup
Choose the Template Options file for buiding gcc MEX-files (i.e., gccopts.sh).
To generate the mex code, you simply need to include the ‘mex prototype:’ statement
within the simulation file (i.e., test.par) and then run cppsim. To do this in Matlab, begin by
changing the working directory to that of the desired SimRun cellview and then run cppsim
within Matlab. To access the cppsim script in Matlab, recall that you need to first add the
HspiceToolbox path to Matlab using the Matlab command:
addpath c:/cppsim/HspiceToolbox
cd c:/CppSim/SimRuns/Synthesizer_Examples/sd_synth_fast
compile_sd_synth_fast
where you should replace the above with compile_cellname for a cellname other than
sd_synth_fast. Upon completion of the above, you can then run the mex function in
Matlab as specified by the prototype format.
Additional Examples
Here are additional examples of the syntax of the ‘mex prototype:’ command:
In the above case, num_sim_steps and Ts are set according to the simulation file (i.e.,
test.par).
In the above case, Ts is set according to the simulation file (i.e., test.par), and num_sim_steps
is set equal to the lengths of input signals in and in2 (which must have matching lengths).
In the above case, Ts is set according to the simulation file (i.e., test.par), num_sim_steps is
set according to the above input value, and in and in2 can be input signals of any length. As
an example, consider the case where num_sim_steps was set to 1000, the length of in to 10,
and the length of in2 to 20. In this case, each mex call with such settings will progressively
simulate the system 1000 steps at a time, where the first 10 steps will progress through the
first 10 input values of in and in2, the next 10 steps will retain the last value of in while
it progresses through the next 10 values of in2, and the remaining steps will retain the last
values of in and in2. Therefore, by declaring num_sim_steps as one of the input arguments
to the mex function, one can avoid long input vector lengths for the case where input signals
are constant.
This chapter discusses the various commands available for use in the module code description
file, which we refer to as the ’modules.par’ file. We begin by covering general parsing issues,
including the notation for commenting out lines, and will then discuss the various commands
in detail.
or as
inputs:
double a
double b
47
48 CHAPTER 6. WRITING CODE FOR PRIMITIVES (MODULES.PAR)
As a rule of thumb, one should never separate a specific description into multiple lines. As
an example, you should NOT write
inputs: double
a
Either the ‘*’ or ‘%’ or ‘//’ characters can be used to comment out lines provided that they
are the first characters encountered on a line.
Example: comment out specific lines in a module description:
module: xor2
description: two-input xor gate
// parameters: double w
inputs: double a, double b
outputs: double y
classes: Xor xor1()
// init:
y = -1.0;
code:
xor1.inp(a,b);
y = xor1.out;
// module: xor2
description: two-input xor gate
parameters: double w
inputs: double a, double b
outputs: double y
classes: Xor xor1()
init:
y = -1.0;
code:
xor1.inp(a,b);
y = xor1.out;
Descriptions of the individual commands used to describe module code are presented
below:
6.2. MODULE: 49
6.2 module:
The name of the module specified here must match the associated module it represents in
the netlist. An example of the syntax of this command is:
module: and2
6.3 description:
A text description of the module function. This is ignored by CppSim, but is useful for
sharing the code with others. An example of the syntax of this command is:
description:
implements a two input ‘and’ function whose signals conform
to the area conservation protocol for representing transitions
6.5 parameters:
The type and name of all parameters associated with the module are specified with this
command. The parameter names must match those in the associated module in the netlist
— since CppSim converts the netlist text to lowercase, lowercase parameter names must be
specified here. An example of the syntax of this command is:
parameters: double gain double fc int order
One should note that expressions for parameter values given in the netlist will be con-
verted to lowercase, so that it is good practice to define lowercase variables in the test.par
file. A related issue is that the global variable ’Ts’ has an uppercase letter — if you desire
to use it in expressions for parameter values within the netlist, then you should define a
lowercase version in the test.par file using
50 CHAPTER 6. WRITING CODE FOR PRIMITIVES (MODULES.PAR)
6.6 inputs:
The type and name of all inputs associated with the module are specified with this command.
The input names must match those in the associated module in the netlist — since CppSim
converts the netlist text to lowercase, lowercase input names must be specified here. An
example of the syntax of this command is:
inputs: double a double b int c
6.7 outputs:
The type and name of all outputs associated with the module are specified with this com-
mand. The output names must match those in the associated module in the netlist — since
CppSim converts the netlist text to lowercase, lowercase output names must be specified
here. An example of the syntax of this command is:
outputs: double y double yb int yd
6.8 classes:
The declaration statement of all classes used in the module code must be placed here. Any
number of classes can be specified, and parameters specified in the ‘parameters:’ statement
can be passed to class declaration expressions. A few examples are in order.
Example: declare two classes for use in the module code
classes: Reg reg1() Xor xor1()
or
classes:
Reg reg1()
Xor xor1()
Example: declare a class using parameters ‘freq’ and ‘kvco’ that have been specified with
the ‘parameters:’ command, and the global parameter ‘Ts’
classes: Vco vco(”fc + Kv*x”,”fc,Kv,Ts”,freq,kvco,Ts)
6.9. STATIC VARIABLES: 51
Note that a few words are in order with regards to syntax. First, all class objects must
have an associated set of parenthesis, as seen above with the class objects reg1() and xor1().
Second, class declarations cannot be broken up between lines, i.e., do NOT write:
classes: Vco vco(”fc + Kv*x”,”fc,Kv,Ts”,freq,
kvco,Ts)
Note that static variable names can freely use uppercase letters.
which allows the length of input vector signals to be checked by code written in the ‘init:’
section.
To gain a better understanding of how to use this command, it is recommended that you
go to the modules.par file and do a search on set output vector lengths: to see examples of
its use. However, here is an example of setting the output vector ’out vec’ to have length 8,
and output vector ’out vec2’ to have length 12:
set output vector lengths:
out vec=8
out vec2=12
6.13 init:
The ‘init:’ command allows you to run initialization code that is executed only once at the
beginning of a simulation run (note that if you use the ‘alter:’ statement in the test.par file,
the initialization code will be run once for each global parameter combination). Therefore,
you can use this command to initialize variables and also to redefine class behavior based
on module parameters specified with the ‘parameters:’ command. For instance, the code
below redefines the noise shaping transfer function of a Σ-Δ modulator object based on the
module parameter ‘order’, and initializes the variable ‘out’ to the value of 2:
init:
if (order == 1)
sd mod.set(”1 - z-̂1”);
else if (order == 2)
sd mod.set(”1 -2z-̂1 + z-̂2”);
else
6.14. END: 53
You can see the full module definition corresponding to the above ’init:’ command in Chapter
4, section 3.
An important issue in writing code in the ‘init:’ section is that it is not parsed by CppSim,
but rather is passed straight to the C++ simulation code. Therefore, the syntax of the code
must conform to the C++ language — if it does not, the C++ compiler will generate error
messages.
6.14 end:
The ‘end:’ command allows you to run terminating code that is executed only once at the
end of a simulation run (note that if you use the ‘alter:’ statement in the test.par file, the
terminating code will be run once for each global parameter combination). An example of
using the end: statement is found in the ascii store module within modules.par — there it
saves the contents of a list to a file at the end of a given simulation run.
An important issue in writing code in the ‘end:’ section is that it is not parsed by CppSim,
but rather is passed straight to the C++ simulation code. Therefore, the syntax of the code
must conform to the C++ language — if it does not, the C++ compiler will generate error
messages.
6.15 code:
The ‘code:’ section contains the C++ code that implements the desired function of the
module. This code is run every time the simulator time step is incremented, and can include
variable declarations, functions, class objects, and all the standard C++ directives such as
for loops and if-else condition statements. One thing to be aware of is that if the user wants
to use their own user defined classes or functions, the class/function declarations must be
placed in the my classes and functions.h file and the class/function code must be placed in
the my classes and functions.cpp file, both of which must be located in the same directory as
cppsim classes.h and cppsim classes.cpp. The need for user defined classes/functions should
be minimal given the flexibility of the CppSim classes. Another issue is that variables
declared in the ‘code:’ section will lose their value from time step to time step, so that the
54 CHAPTER 6. WRITING CODE FOR PRIMITIVES (MODULES.PAR)
‘static variables:’ command should be used in some cases as discussed above. Finally, you
should be aware of the fact that the code specified in the ‘code:’ section is not parsed by
CppSim, but rather is passed straight to the C++ simulation code. Therefore, the syntax
of the code must conform to the C++ language — if it does not, the C++ compiler will
generate error messages.
Some examples are in order — each of the ones to follow are taken from the modules.par
file that is shown in Chapter 4, section 3.
Example: implement a ‘gain’ module by setting its output, ‘y’, equal to its input, ‘a’, times
a gain parameter ‘gain’:
code:
y=a*gain;
Example: implement a ‘noise’ module using a class object ‘randg’ (which implements a Gaus-
sian random sequence with variance one) whose output is scaled by the variance parameter
‘var’ and global variable ‘Ts’:
code:
out = sqrt(var/Ts)*randg.inp();
The reader is invited to look at more examples of ‘code:’ sections listed in Chapter 4,
Section 3, as well as the module descriptions contained in the example modules.par file that
is included in the CppSim package.
6.16 functions:
The ‘functions:’ section allows you to specify functions local to the module being de-
fined. By doing so, the module becomes self-contained and easy to pass on to others (the
user otherwise needs to add such functions to my classes and functions.cpp/h in the Cpp-
Sim/CommonCode directory). To use the function within several different modules, you’ll
need to copy the function into the functions: section of each module (unless you put it into
my classes and functions.cpp/h).
As an example, let us assume that you wanted to use two functions within the module
called ‘add(a,b)’ and ‘mul(a,b)’. Such functions would be specified as:
functions:
double add(double a, double b)
{
double out;
6.17. CUSTOM CLASSES DEFINITION: AND CUSTOM CLASSES CODE: 55
out = a + b;
return(out);
}
double mul(double a, double b)
{
double out;
out = a*b;
return(out);
}
double And6 example::inp(double in0, double in1, double in2, double in3, double in4, double in5)
{
out = and2.inp(and5.inp(in0,in1,in2,in3,in4),in5);
return(out);
}
Note that if you want to specify the order of instance execution in the top module in the
netlist (i.e., the highest cellview in the hierarchy), but your netlist does not include the
top module within a .subckt definition, then simply use the same description technique but
specify the module name as ‘top’, i.e.,
module: top
sim order: xi8 xi6 . . .
Note that the default behavior for the Sue2 setup is to include the top module within a
.subckt definition, in which case you refer to the top module by its schematic cell name
rather than by ‘top’.
CppSim will check that all instances are included in your list, and also alert you to any
instance names not being present in the associated netlist description of the module.
Note that there is an alternative method of specifying the order of execution of the
instances within a cell that does not involve changes to the modules.par file. Specifically,
CppSim reserves the parameter ‘sim order’ to specify the order of execution of instances
within a cell, so that the user may add this parameter to each instance and set the execution
order in their schematic editor. If this method is used, all instances in the cell must have
‘sim order’ as a parameter, and its value for a given instance should be set lower than the
values of other instances that should be executed after it. In other words, CppSim orders
the instances from lowest to highest ‘sim order’ value. One can use decimal values for the
‘sim order’ value, so that an instance with ‘sim order=1.5’ executes after an instance with
‘sim order=1’ and before an instance with ‘sim order=2’.
if (in1 == in2)
{ 10
printf("Stop alter run asserted by 'stop_alter_run_on_match' module\n");
stop current alter run = 1;
}
Chapter 7
This chapter provides reference information on the general purpose classes available in the
CppSim package. Most of these classes update the output one sample at a time as new
input values are fed in. Exceptions to this rule are the Vector, IntVector, List and Clist
classes, which offer the ability to perform operations on sequences. These classes prove
very useful when simulating the behavior of communication systems that require algorithms
to be performed on a sequence of data values. For instance, in an OFDM communication
system, FFT and inverse FFT operations must be performed on sequences of data in order
to implement the modulation and demodulation functions. The Vector, IntVector, and Clist
classes offer support of such operations, as well as a variety of other processing functions.
The description of each class is partitioned into several different sections:
In each section, an attempt has been made to use a large number of examples to convey the
fundamental concepts associated with the respective class.
59
60 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
Declaration
// Basic method (use within CppSim modules.par files):
Vector vec1,vec2;
IntVector ivec1,ivec2;
// For debugging in standalone code (but ∗not∗ within modules.par files):
Vector vec1("module_name","vec1"),vec2("module_name","vec2");
IntVector vec1("module_name","ivec1"),vec2("module_name","ivec2");
Variables
// Elements within Vector or IntVector classes should NOT be directly accessed!
// — For a given vector A, always use the class functions such as
// — A.get length(), A.get elem(index), A.set elem(index,val), etc. . . . .
Functions
/////////////// Functions within Vector and IntVector classes ///////////////
//// i.e., for vector A: length = A.get length(), A.set elem(0,5.0), . . . ////
// Copy contents of list to vector (note: copy from vector to list using list1.copy(vec1);)
void copy(const List &from, const Vector &to);
void copy(const List &from, const IntVector &to);
30
// Create a vector with Gaussian entries of standard deviation std dev
void gauss ran vector(double std dev, int length, const Vector &A);
void fft(const IntVector &real, const IntVector &imag, const Vector &fft real, const Vector &fft imag);
void ifft(const Vector &real, const Vector &imag, const Vector &ifft real, const Vector &ifft imag);
void ifft(const IntVector &real, const IntVector &imag, const Vector &ifft real, const Vector &ifft imag); 60
void real fft(const Vector &in, const Vector &fft real, const Vector &fft imag);
void real fft(const IntVector &in, const Vector &fft real, const Vector &fft imag);
Example of Usage
#include "cppsim_classes.h"
void my sum of squares func(const Vector &a, const Vector &b, const Vector &c);
void my sum of squares func2(const Vector &a, const Vector &b, const Vector &c);
main()
{
// declarations - include module name and vector name for debugging
Vector vec1("main_func","vec1");
Vector vec2("main_func","vec2");
Vector vec3("main_func","vec3"); 10
Vector vec4("main_func","vec4");
IntVector ivec1("main_func","ivec1");
IntVector ivec2("main_func","ivec2");
IntVector ivec3("main_func","ivec3");
/// Note: you don’t do the above for vectors contained with modules defined
/// in module.par files! CppSim automatically takes care of providing
/// such names
///
/// Alternate way of declaring the above vectors (i.e., don’t name vectors): 20
/// Vector vec1,vec2,vec3,vec4;
/// IntVector ivec1,ivec2,ivec3;
double val;
int i,length, int val;
// set length of integer vector ivec1 to length 5 and fill with entries 0 to 8
ivec1.set length(5);
length = ivec1.get length();
30
for (i = 0; i < length; i++)
ivec1.set elem(i,i∗2);
// fill vector vec2 with random numbers generated from a Guassian distribution
// with standard deviation = 0.31, and have its length match that of vec1
gauss ran vector(0.31,vec1.get length(),vec2);
// take the inverse FFT of vec1 (real part) and vec3 (imag part) and
// save the result in vec2 (real part of ifft) and vec4 (imag part of ifft)
ifft(vec1,vec3,vec2,vec4);
// take the FFT of vec1 (real part) and vec2 (imag part) and 70
// save the result in vec3 (real part) and vec4 (imag part)
fft(vec1,vec2,vec3,vec4);
// The following example function illustrates how one can great their own custom functions 90
// using the Vector class. This example illustrates the preferred method for most
// applications, which is to use the Vector class routines to perform all operations.
// For time critical applications (in which you want to avoid the extensive error checking done
// in the Vector class routines), one might want to consider using the Vector
// structure method shown below this one.
void my sum of squares func(const Vector &a, const Vector &b, const Vector &c)
{
int i;
double val;
100
if (a.get length() != b.get length())
{
printf("error in 'my_sum_of_squares_func':\n");
printf(" length of vectors 'a' and 'b' must match!\n");
printf(" in this case, vector 'a' has length %d\n",a.get length());
printf(" vector 'b' has length %d\n",b.get length());
printf(" vector 'a' is named '%s' (originating module: '%s')\n",
a.name, a.module name);
printf(" vector 'b' is named '%s' (originating module: '%s')\n",
b.name, b.module name); 110
exit(1);
}
// This example function makes use of the vector structures embedded within Vector classes.
// The advantage of extracting the vector structures is that you can directly operate on
// the vector element values and avoid the time required to do error checking.
// This should only be done for time intensive functions where you are committed to do upfront
// error checking to save computation time.
// BEWARE: it’s easy to create segmentation faults if you use this method - you must be careful!!
// (for those lazy at doing error checking, use the method above - segmentation faults are hard to debug) 130
void my sum of squares func2(const Vector &a, const Vector &b, const Vector &c)
{
int i;
vector struct ∗a vec,∗b vec,∗c vec;
Declaration
// Basic method (use within CppSim modules.par files):
Matrix mat1,mat2;
IntMatrix imat1,imat2;
// For debugging in standalone code (but ∗not∗ within modules.par files):
Matrix mat1("module_name","mat1"),mat2("module_name","mat2");
IntMatrix mat1("module_name","imat1"),mat2("module_name","imat2");
Variables
// Elements within Matrix or IntMatrix classes should NOT be directly accessed!
// — For a given matrix A, always use the class functions such as
// — A.get rows(), A.get elem(row,col), A.set elem(row,col,val), etc. . . . .
Functions
/////////////// Functions within Matrix and IntMatrix classes ///////////////
//// i.e., for matrix A: rows = A.get rows(), A.set elem(0,1,5.0), . . . ////
//////////// Functions that take both matrix and vector arguments /////////////
// Copy contents of vector to matrix – vector must replace an existing row or column at given index in matrix
void copy(int index, char ∗row or col, Vector &from, Matrix &to);
void copy(int index, char ∗row or col, IntVector &from, IntMatrix &to);
80
// Copy contents of row or column vector within matrix to a vector – vector is automatically sized
void copy(int index, char ∗row or col, Matrix &from, Vector &to);
void copy(int index, char ∗row or col, IntMatrix &from, IntVector &to);
// Compute least square estimate of x where: A∗x = b + error (x and b are vectors)
void least sq(const Matrix &A, const Vector &B, const Vector &X);
Example of Usage
#include "cppsim_classes.h"
void my sum of squares func(const Matrix &a, const Matrix &b, const Matrix &c);
void my sum of squares func2(const Matrix &a, const Matrix &b, const Matrix &c);
main()
{
70 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
/// Note: you don’t do the above for matrices contained with modules defined
/// in module.par files! CppSim automatically takes care of providing
/// such names
/// 20
/// Alternate way of declaring the above matrices (i.e., don’t name matrices):
/// Matrix mat1,mat2,mat3,mat4;
/// IntMatrix imat1,imat2,imat3;
/// Vector vec1;
double val;
int i,j,rows,cols, int val;
// set size of integer matrix imat1 to 5 by 7 and fill with entries i∗j
imat1.set size(5,7); 30
rows = imat1.get rows();
cols = imat1.get cols();
mul(int val,imat2,imat3);
// convert integer elements of imat3 into double values and store in mat1
int to real(imat3,mat1);
// multiply vec1 by 3
mul elem(3,vec1,vec1);
}
80
// The following example function illustrates how one can great their own custom functions
// using the Matrix class. This example illustrates the preferred method for most
// applications, which is to use the Matrix class routines to perform all operations.
// For time critical applications (in which you want to avoid the extensive error checking done
// in the Matrix class routines), one might want to consider using the Matrix
// structure method shown below this one.
void my sum of squares func(const Matrix &a, const Matrix &b, const Matrix &c)
72 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
{
int i,j; 90
double val;
// This example function makes use of the matrix structures embedded within Matrix classes.
// The advantage of extracting the matrix structures is that you can directly operate on
// the matrix element values and avoid the time required to do error checking.
// This should only be done for time intensive functions where you are committed to do upfront 120
// error checking to save computation time.
// BEWARE: it’s easy to create segmentation faults if you use this method - you must be careful!!
// (for those lazy at doing error checking, use the method above - segmentation faults are hard to debug)
void my sum of squares func2(const Matrix &a, const Matrix &b, const Matrix &c)
{
int i,j;
matrix struct ∗a mat,∗b mat,∗c mat;
7.3 List
A linked list of double values that provides storage for sequences of numbers.
Declaration
List list1;
Variables
double out; // current entry value
int length; // number of entries in list
int notdone; // notdone = 0 or 1 depending if current entry is last value or not last value, respectively
Functions
double read(); // returns current element value and increments pointer
// recycles back to first element after last is read
void reset(); // reset read pointer to first element
void flush(); // delete all elements from list
int inp(double in); // add a new element of value ’in’ to the end of the list (returns list length)
double mean(); // returns mean of list element values
double var(); // returns variance of list element values
void add(const List &other); // adds, element by element, other list values to list values
void add(double in); // adds constant ’in’ to all element values in list
void mul(const List &other); // multiplies, element by element, other list values to list values 10
void mul(double in); // multiplies all element values in list by constant ’in’
void conv(const List &other); // convolves list with other list
void cat(const List &other); // concatonates other list to list
int load(char ∗filename); // loads values from file into list (previous elements destroyed, returns list length)
void save(char ∗filename); // saves element values to file
void copy(const List &other); // copy other list into list (previous elements destroyed)
void copy(const List &Vector); // copy other real-valued vector into list (previous elements destroyed)
void copy(const List &IntVector); // copy other integer-valued vector into list (previous elements destroyed)
void print(char ∗name); // print list to stdout using ’name’ as label
20
// New functions as of 6/5/04
// Note: see Delay class (inp function) in cppsim classes.cpp to see how to use these
read without incrementing(); // reads current element but does not increment pointer
write(double in); // writes to the current element value and increments pointer
// recycles back to first element after last one is written
write without incrementing(double in); // writes to the current element value but does not increment pointer
remove first entry(); // removes first entry in the list - this is useful
7.3. LIST 75
Example of Usage
#include "com_blocks.h"
main()
{
// declarations
List list1,list2,list3,list4;
double val;
int i;
// copy all entries of list3 into list4 (all previous list4 entries deleted)
// (list3 unchanged)
list4.copy(list3);
// concatonate list2 and list4, store result in list4 (list2 unchanged)
list4.cat(list2);
30
// illustrate read() by printing out entries of list4 ‘by hand’
list4.reset(); // resets read pointer to first entry
i = 1;
while(list4.notdone)
{
val = list4.read(); // read current entry, increment read pointer
printf("list4[%d] = %5.3f\n",i++,val);
}
76 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
// add list1 and list2 elements, store result in list1 (list2 unchanged)
list1.add(list2);
// add the constant 5.0 to all elements in list2
list2.add(5.0);
60
// multiply list1 and list2 elements, store result in list2 (list1 unchanged)
list2.mul(list1);
// muliply list1 by the constant -3.0
list1.mul(−3.0);
// convolve list1 and list3 elements, store result in list3 (list1 unchanged)
list3.conv(list1);
7.4 Clist
A grouping of two linked lists of double values that form a complex sequence.
Declaration
Clist clist1;
Variables
List real; // sequence of real element values
List imag; // sequence of imag element values
double outr; // current real entry value
double outi; // current imag entry value
int length; // number of entries in clist
int notdone; // notdone = 0 or 1 depending if current complex entry is last value or not last value, respectively
Functions
void read(); // read current element value into outr and outi and increment pointer
// recycles back to first complex element after last is read
void reset(); // reset read pointer to first complex element
void flush(); // delete all elements from clist
int inp(double rin, double iin); // add a new element to the end of the list (returns list length)
// real part of new element is ’rin’, and imag part of new element is ’iin’
int inp(const List &rin, const List &iin); // create a complex sequence using list ’rin’ as
// real part and list ’iin’ for imag part (all previous entries destroyed, returns list length)
void cat(const Clist &other); // concatonates other clist to clist
void add(const Clist &other); // adds, element by element, other clist values to clist values 10
void add(double rin, double iin); // adds constant ’rin + j∗iin’ to all element values in clist
void mul(const Clist &other); // multiplies, element by element, other clist values to clist values
void mul(double rin, double iin); // multiplies all element values in clist by constant ’rin + j∗iin’
void conv(const Clist &other); // convolves clist with other clist
void conv(const List &other); // convolves clist with other list (other list assumed to be real)
void copy(const Clist &other); // copy other list into list (previous elements destroyed)
void fft(const List &data); // calculate fft of list ’data’ (’data’ assumed to be real)
void fft(const Clist &data); // calculate fft of clist ’data’ (data is complex)
void fft(const List &data real, const List &data imag); // calculate fft of sequence with real part
//’data real’ and imag part ’data imag’ 20
void ifft(const Clist &fft in); // calculate inverse fft of clist ’fft in’
void ifft(const List &fft real, const List &fft imag); // calculate inverse fft of sequence with real part
//’fft real’ and imag part ’fft imag’
void print(char ∗name); // print clist to stdout using ’name’ as label
78 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
Example of Usage
#include "com_blocks.h"
main()
{
// declarations
List list1,list2;
Clist clist1,clist2;
int i;
// create a complex list with list1 as real part, list2 as imag part
clist1.inp(list1,list2);
// add elements of clist1 and clist2 and store in clist2 (clist1 unchanged)
clist2.add(clist1);
clist2.add(2,−7);
7.5 Probe
Save data in single-precision format to a binary file which can be read with loadsig cppsim
in Matlab.
Declaration
Probe probe1("test.tr0"); // save probed data in file ’test.tr0’, sample period = 1
Probe probe2("test2.tr0",1e−6); // save probed data in file ’test2.tr0’, sample period = 1e-6
Probe probe3("test3.tr0",1e−6,10); // save probed data in file ’test3.tr0’, sample period = 1e-6
// subsample data by a factor of 10 before saving to file
Variables
None
Functions
void inp(double node value, char ∗node name); // send double value to probe file with label ’node name’
void inp(int int node value, char ∗node name); // send integer value to probe file with label ’node name’
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double sig1,sig2;
Ts = 1e−6;
Probe probe1("test.tr0"); 10
Probe probe2("test2.tr0",Ts);
Probe probe3("test3.tr0",Ts,10);
Probe probe4("test4.tr0",Ts);
// Probe statements used in previous loops must follow same order of signal names probed 40
probe2.inp(sig1,"sine"); // acceptable, but not recommended
probe2.inp(sig2,"cosine"); // acceptable, but not recommended
// probe2.inp(sig1+sig2,“sum”); // will produce an error since ’sum’ not probed in loop above
// The following probe statements are fine sinece probe4 was not used above
probe4.inp(sig1,"sine"); // recommended approach - new probe statement for this loop
probe4.inp(sig2,"cosine"); // ditto - this is fine
probe4.inp(sig1+sig2,"sum"); // ditto - this is fine
}
} 50
7.6. PROBE64 83
7.6 Probe64
Save data in double-precision format to a binary file which can be read with loadsig cppsim
in Matlab.
Declaration
Probe64 probe1("test.tr0"); // save probed data in file ’test.tr0’, sample period = 1
Probe64 probe2("test2.tr0",1e−6); // save probed data in file ’test2.tr0’, sample period = 1e-6
Probe64 probe3("test3.tr0",1e−6,10); // save probed data in file ’test3.tr0’, sample period = 1e-6
// subsample data by a factor of 10 before saving to file
Variables
None
Functions
void inp(double node value, char ∗node name); // send double value to probe file with label ’node name’
void inp(int int node value, char ∗node name); // send integer value to probe file with label ’node name’
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double sig1,sig2;
Ts = 1e−6;
Probe64 probe1("test.tr0"); 10
Probe64 probe2("test2.tr0",Ts);
Probe64 probe3("test3.tr0",Ts,10);
Probe64 probe4("test4.tr0",Ts);
// Probe statements used in previous loops must follow same order of signal names probed 40
probe2.inp(sig1,"sine"); // acceptable, but not recommended
probe2.inp(sig2,"cosine"); // acceptable, but not recommended
// probe2.inp(sig1+sig2,“sum”); // will produce an error since ’sum’ not probed in loop above
// The following probe statements are fine sinece probe4 was not used above
probe4.inp(sig1,"sine"); // recommended approach - new probe statement for this loop
probe4.inp(sig2,"cosine"); // ditto - this is fine
probe4.inp(sig1+sig2,"sum"); // ditto - this is fine
}
} 50
7.7. FILTER 85
7.7 Filter
Continuous-time and discrete-time filters.
Declaration
Filter difference("1 - z^-1","1"); // discrete-time first difference
Filter accum("1","1 - z^-1"); // discrete-time accumulator
Filter zfilt("1 - a*z^-1","1 - b*z^-1","a,b",.8,.9); // general discrete-time
// filter consisting of one pole and one zero
Filter accum lim("1","1 - z^-1","Max,Min",2.5,0.0); // discrete-time accumulator
// with max and min limits set on its output
Filter delay("z^-no","1","no",10); // delay of 10 samples (must use integer sample delay)
Filter diff("K*s","1","Ts,K",1e−6,2.0); // continuous-time differentiator K∗s
// (Ts is simulation sample period)
Filter integ("K","s","K,Ts",1,1e−6); // continuous-time integrator K/s 10
Filter RC fil("K","1 + 1/(2*pi*fo)*s","K,fo,Ts",1.0,1e3,1e−6); // continuous-time filter
// corresponding to an RC network with transfer function K/(1 + s/(2∗pi∗fo))
Filter LC fil("K","1 + 1/(wo*Q)*s + 1/(wo^2)*s^2","Ts,K,wo,Q",1e−6,1.0,1e3∗2∗PI,1.2);
// continuous-time filter corresponding to an LC network with
// transfer function K/(1 + 1/(wo∗Q)∗s + (s/wo)^2)
List list1;
list1.load("list1.dat"); // load data from file into list
Filter(list1,"1"); // create z-domain FIR filter whose numerator z-polynomial is created from
// list1 elements and whose denominator is 1 (see Usage example below)
Filter("1",list1); // create z-domain IIR filter whose denominator z-polynomial is created from 20
// list1 elements and whose numerator is 1
List list2;
list2.load("list2.dat");
Filter(list1,list2); // create z-domain IIR filter whose denominator z-polynomial is created from
// list1 elements and whose numerator z-polynomial is created from list2 elements
Vector vec1;
vec1.load("list1.dat"); // load data from file into real-valued vector
Filter(vec1,"1"); // create z-domain FIR filter whose numerator z-polynomial is created from
// vec1 elements and whose denominator is 1
// Redefine
86 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
filt1.set("1","1 - z^-1");
// Redefine again
filt1.set("K","1 + 1/(2*pi*fo)*s","K,fo,Ts",1.0,1e3,1e−6);
Variables
double out; // output of filter
char ivar; // independent variable of transfer function - either ’z’ or ’s’
int num a coeff; // number of denominator coefficients
int num b coeff; // number of numerator coefficients
Functions
double inp(double in); // input new sample to filter, resulting output is returned
void reset(double value); // set output and all state information of filter to value
Example of Usage
#include "com_blocks.h"
main()
{
List list1;
Probe probe1("test.tr0");
int i;
double in;
// incrementally load values into list1 to later set z-polynomial of a0 + a1∗z^-1 + a2∗z^-2 10
list1.inp(1.0); // set a0
list1.inp(−2.0); // set a1
list1.inp(1.0); // set a2
in = 1.0; 20
for (i = 0; i < 1000; i++)
{
if (i == 200)
in = −1.0;
else if (i == 400)
in = 2.0;
7.7. FILTER 87
Probe probe2("test2.tr0",sample per); // need a new probe statement for new iteration loop
in = 1.0;
for (i = 0; i < 1000; i++)
{
if (i == 200)
in = −1.0;
else if (i == 400)
in = 2.0;
else if (i == 700) // zero out filters at the 700th sample 60
{
double integ.reset(0.0);
diff.reset(0.0);
}
diff.inp(double integ.out);
7.8 Amp
General amplifier with nonlinear characteristic specified by a polynomial and saturating
characteristic specified by Min, Max values.
Declaration
Amp amp("off + A*x","off,A",1.0,5.0); // amp offset of 1 V, gain of 5
Amp amp2("off + A*x + A1*x^2 + A2*x^(1/2)","off,A,A1,A2",.5,10.0,1,.1); // nonlinear
// characterstic described by a polynmomial
Amp amp("off + A*x","off,A,Min,Max",1.0,5.0,0.5,2.0); // Min output is 0.5, Max output is 2.0
// Redefine
amp.set("off + A*x + A1*x^2 + A2*x^(1/2)","off,A,A1,A2",.5,10.0,1,.1);
// Redefine again
amp.set("off + A*x","off,A,Min,Max",1.0,5.0,0.5,2.0);
Variables
double out; // output of amplifier
Functions
double inp(double in); // input voltage to amp, returns resulting value of out
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in;
Ts = 1e−6;
Probe probe1("test.tr0",Ts); 10
// create an amplifier with offset -0.5, gain 3.0, and saturation at 0 (min) and 2.0 (max)
Amp amp1("off + A*x","off,A,Max,Min",−0.5,3.0,2.0,0.0);
90 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
7.9 EdgeDetect
Output is 0 except at the rising edge of its input, at which point the output is 1. The input
must be a square wave that alternates between -1 and 1, 0 and 1, or -1 and 0.
Declaration
EdgeDetect edge1;
Variables
int out; // output is 1 if input is rising edge, 0 otherwise
Functions
int inp(double in); // returns out, input can alternate between -1 and 1,
// 0 and 1, or -1 and 0
int inp(int in); // same as previous function, but with integer input
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in,sig1;
int vco pos count, div pos count;
int vco neg count, div neg count;
10
Ts = 1e−9;
Probe probe1("test.tr0",Ts);
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Divider divider;
EdgeDetect pedge vco, pedge div;
EdgeDetect nedge vco, nedge div;
in = (1.0/10000.0)∗i;
vco.inp(in); // frequency will gradually increase since in is a ramp
divider.inp(vco.out,10.0); // divide down VCO frequency by a factor of 10
7.10 SdMbitMod
Implements a multi-bit Σ-Δ modulator with a signal transfer function (STF) of 1.0 and a
noise transfer function (NTF) that is specified as a z polynomial.
Declaration
SdMbitMod sd mod1("1 - 2*z^-1 + z^-2"); // sd modulator with second order noise shaping
SdMbitMod sd mod2("1 - 3z^-1 + 3z^-2 - 1z^-3"); // sd modulator with third order noise shaping
// Redefine
sd mod1.set("1 - 3z^-1 + 3z^-2 - 1z^-3");
// Redefine again
sd mod1.set("1 - z^-1");
Variables
double out; // output of sd modulator
Functions
double inp(double in); // returns sd modulator ’out’ given input ’in’
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Filter lowpass("1","1 + 1/(2*pi*fo)*s","fo,Ts",100e3,Ts);
SdMbitMod sdmod("1 - 2z^-1 + z^-2");
94 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
7.11 Rand
Produces a random, white sequence whose sample values are chosen according to three
different probability distributions:
Declaration
Rand rand1("gauss"); // Gaussian distribution
Rand rand2("uniform"); // Uniform distribution
Rand rand3("bernoulli"); // Bernouilli distribution with p = 1/2
Rand rand4("bernoulli",.25); // Bernoulli distribution with p = 1/4
Variables
double out; // sequence sample chosen according to specified probability distribution
Functions
void reset(); // resets seed to -1 for random sequence (impacts all random number generators)
void set seed(int in); // sets seed to value in (impacts all random number generators)
double inp(); // returns sequence output ’out’
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in,noise1;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Rand rand1("gauss");
96 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
7.12 Quantizer
Quantizes input according to five parameters: levels, step size, in center, out min, and
out max as shown in Figure 7.1
levels=5 levels=4
in in
in_center
in_center step_size step_size
0 t 0 t
out out
out_max out_max
out_min
out_min
0 t 0 t
Declaration
Quantizer quant1(2,.5,0.0,−1,1); // levels=2, step size=0.5, in center=0, out min=-1, out max=1
Quantizer quant2(2,.5,0.5,−1,1);
Quantizer quant3(2,.5,−0.5,0,1);
Quantizer quant4(3,.5,0.0,−1,1);
Quantizer quant5(5,.5,0.0,−1,1);
Quantizer quant6(5,.5,0.0,0,4);
Quantizer quant7(16,.3,0.0,0,15);
Variables
double out; // output of quantizer
Functions
double inp(double in); // returns quantized out for given in
double inp(double in, double clk); // returns quantized out for given in on rising edge of clk
Example of Usage
#include "com_blocks.h"
98 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
main()
{
double Ts=1;
Quantizer quant1(2,.5,0.0,−1,1);
Quantizer quant2(2,.5,0.5,−1,1);
Quantizer quant3(2,.5,−0.5,0,1);
Quantizer quant4(3,.5,0.0,−1,1);
Quantizer quant5(5,.5,0.0,−1,1); 10
Quantizer quant6(5,.5,0.0,0,4);
Quantizer quant7(16,.3,0.0,0,15);
Vco vco("fc + Kv*x","Ts,fc,Kv",Ts,1/150.0,1.0);
Probe probe1("test.tr0");
double in;
int i;
in = −2.0;
for (i = 0; i < 4000; i++)
{ 20
// generate input signal
if (i % 2000 < 1000)
in += .004;
else
in −= .004;
probe1.inp(quant3.out,"quant3");
probe1.inp(quant4.out,"quant4");
probe1.inp(quant5.out,"quant5");
probe1.inp(quant6.out,"quant6");
probe1.inp(quant7.out,"quant7");
}
} 50
1
in,quant1
−1
−2
0 500 1000 1500 2000 2500 3000 3500 4000
1
in,quant2
−1
−2
0 500 1000 1500 2000 2500 3000 3500 4000
1
in,quant3
−1
−2
0 500 1000 1500 2000 2500 3000 3500 4000
TIME
Figure 7.2 Plot of quant1, quant2, and quant3 from example simulation.
100 CHAPTER 7. GENERAL PURPOSE CPPSIM CLASSES
in,quant4
0
−1
−2
0 500 1000 1500 2000 2500 3000 3500 4000
1
in,quant5
−1
−2
0 500 1000 1500 2000 2500 3000 3500 4000
4
in,quant6
−2
0 500 1000 1500 2000 2500 3000 3500 4000
TIME
Figure 7.3 Plot of quant4, quant5, and quant6 from example simulation.
1
in
−1
−2
0 500 1000 1500 2000 2500 3000 3500 4000
0.5
clk
−0.5
−1
0 500 1000 1500 2000 2500 3000 3500 4000
15
10
quant7
0
0 500 1000 1500 2000 2500 3000 3500 4000
TIME
Figure 7.4 Plot of quant7 and associated signals from example simulation.
Chapter 8
These classes are used in the same manner as the general purpose ones described in the
previous chapter, but are specialized for PLL/DLL simulation in that they implement the
area conservation approach for digital signal transitions that is described in the paper
Perrott, M.H., “Fast and Accurate Behavioral Simulation of
Fractional-N Frequency Synthesizers and other PLL/DLL Circuits,”
Design Automation Conference, June, 2002
An expanded version of the above paper is included in the CppSim package — the included
version more explicitly relates the described techniques to the classes provided in this library.
101
102 CHAPTER 8. CPPSIM CLASSES FOR PLL/DLL SIMULATION
8.1 SigGen
Produces a waveform of a specified frequency that corresponds to one of four different wave-
form types:
Declaration
double Ts=1e−9; // simulation sample period
SigGen siggen1("square",1.0e6,Ts); //square wave of frequency 1 MHz
SigGen siggen2("sine",1.0e6,Ts); // sine wave of frequency 1 MHz
SigGen siggen3("prbs",1.0e6,Ts); // prbs sequence of period 1/(1 MHz), data chosen randomly
List list1;
list1.load("list1.dat"); // load data sequence from file, which must have values of either 1 or -1
SigGen siggen4("prbs",1.0e6,Ts,list1); // prbs sequence of period 1/(1 MHz), data repeats through list1
SigGen siggen4("prbs",1.0e6,Ts,list1,5); // prbs sequence of period 1/(1 MHz),
// data repeats through list1, start with 5th entry in list
SigGen siggen5("impulse",1.0e6,Ts); // impulse sequence of period 1/(1 MHz), data chosen randomly 10
SigGen siggen6("impulse",1.0e6,Ts,list1); // impulse sequence of period 1/(1 MHz), data repeats through list1
SigGen siggen6("impulse",1.0e6,Ts,list1,3); // impulse sequence of period 1/(1 MHz),
// data repeats through list1, start with 3rd entry in list
// Redefine
siggen1.set("sine",1.0e6,Ts);
// Redefine again
siggen1.set("prbs",1.0e6,Ts);
Variables
double out; // waveform output
double phase; // normalized phase of waveform (ramps between 0.0 and 1.0)
double square; // square wave (clk) that output is derived from
8.1. SIGGEN 103
Functions
double inp(double in); // return waveform output, input specifies phase offset
double reset(); // reset waveform (useful when SigGen initialized with a List)
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts,freq;
Ts = 1e−9;
freq = 1e6;
Probe probe1("test.tr0",Ts); 10
SigGen sine1("sine",freq,Ts);
SigGen sine2("sine",freq,Ts);
List list1;
list1.inp(1);
list1.inp(−1);
list1.inp(−1);
SigGen prbs1("prbs",freq,Ts,list1,2); // sequence repeats through value 1, -1, -1; starts with entry 2 of list
SigGen prbs2("prbs",freq,Ts); // sequence randomly takes on values of 1 or -1
Rand rand1("gauss");
20
for (i = 0; i < 10000; i++)
{
if (i == 5000) // reset sine1 at i = 5000
sine1.reset();
// note: phase shifts specified at input are lowpassed filtered by discrete-time filter
// freq∗Ts/(1 - (1-freq∗Ts)∗z^-1)
sine1.inp(0.0); // sine wave with 0 radian phase shift
sine2.inp(0.25); // sine wave with 1/4∗(2∗pi) radian phase shift (i.e. cosine wave)
prbs1.inp(0.0); // 1, -1, -1 sequence with 0 degree phase shift 30
prbs2.inp(0.0001∗rand1.inp()); // prbs sequence with random phase shift (mean 0, var (.0001∗2∗pi)^2)
}
}
8.2. VCO 105
8.2 Vco
Voltage controlled oscillator. Output is a square wave that alternates between -1 and 1. At
edges, value of square wave is between -1 and 1 according to the time occurance of the edge
within the sample period.
Declaration
Vco vco("1.84e9 + 30e6*x","Ts",Ts); // center frequency 1.84 GHz, gain 30 MHz/V,
// simulation sample period of Ts
Vco vco2("fc + Kv*x","fc,Kv,Ts",1.84e9,30e6,Ts); // center freqency fc, gain of Kv
Vco vco3("fc + Kv*x + Kv2*x^2 + Kv3*x^(1/2)","fc,Kv,Kv2,Kv3,Ts",1.84e9,30e6,5e6,1e6,Ts);
// Nonlinear VCO characteristic specified by a polynomial
Vco vco4("fc + Kv*x","fc,Kv,Ts,Max,Min",1.84e9,30e6,Ts,2e9,1.7e9); // Max frequency 2e9,
// Min frequency 1.7e9
// Redefine
vco1.set("fc + Kv*x","fc,Kv,Ts",1.84e9,30e6,Ts);
// Redefine again
vco1.set("fc + Kv*x + Kv2*x^2 + Kv3*x^(1/2)","fc,Kv,Kv2,Kv3,Ts",1.84e9,30e6,5e6,1e6,Ts);
Variables
double out; // square wave alternating between 1 and -1
double phase; // phase wraps so that it varies between 0 and 2pi
Functions
double inp(double in); // input voltage to vco, returns resulting value of out
double inp(double in, int divide val); // vco divided down by divide value, out returned
double inp(double in, double divide val); // divide value can be double or integer
Example of Usage
#include "com_blocks.h"
main()
{
int i;
106 CHAPTER 8. CPPSIM CLASSES FOR PLL/DLL SIMULATION
double Ts;
double in,sig1;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Vco vco2("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
8.3 Delay
Variable delay element for inputs that follow the interpolation convention (i.e. they must
alternate between -1.0 and 1.0, with transition values taking on a value in the range of -1.0
to 1.0 depending on the location of the transition relative to the sample period). Output
alternates between -1.0 and 1.0, with edge values between -1.0 and 1.0 according to their
actual time position within the sample period.
Declaration
Delay delay1(5.4); // nominal delay of 5.4 simulator time samples when varying delay
Delay delay2(3.6); // fixed delay of 3.6 simulator time samples for fixed delay
Variables
double out; // output of delay element (alternates between 1.0 and -1.0)
Functions
double inp(double in); // input ’in’ signal to delay, keep the delay value fixed
double inp(double in, double delay val); // adjust delay value about
// nominal value according to ’delay val’ signal
Example of Usage
#include "cppsim_classes.h"
main()
{
double Ts = 1/20e9;
Probe probe("test.tr0",Ts);
Vco vco("fc + Kv*x","fc,Kv,Ts",1e9,30e6,Ts);
Delay delay(2.0); // nominal delay is 2.0 simulation time samples
Delay delay2(2.5); // delay2 will be 2.5 simulation time samples
EdgeMeasure vco period,delay period; 10
EdgeDetect rising edge;
double in;
int i,count;
count = 0;
for (i = 0; i < 700; i++)
{
// delay will be varied about its nominal value by
108 CHAPTER 8. CPPSIM CLASSES FOR PLL/DLL SIMULATION
// 1.5 - 1.0 = 0.5 time samples to 1.5 + 1.0 = 2.5 time samples
if (rising edge.inp(delay.out)) 20
in = 1.5 + sin(2.0∗pi∗(1.0/20.0)∗((double) count++));
// VCO
vco.inp(0.0);
// Delay elements
// vary delay from vco.out to delay.out about its nominal value of 2.0
// simulation time samples as specified by signal ’in’
// -> for above ’in’, overall delay range spans from 2.5 to 4.5 time samples
delay.inp(vco.out,in); 30
// delay from vco.out to delay2.out is fixed at 2.5 time samples
delay2.inp(vco.out);
8.4 Divider
Divides down input square wave (which much alternate between -1 and 1) according to a
specified divide value. Output also alternates between -1 and 1.
Declaration
Divider divider1;
Variables
double out; // output square wave alternating between 1 and -1
Functions
double inp(double in, int divide value); // returns output given specified input and divide value
double inp(double in, double divide value); // divide value can be double or integer valued
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in,sig1;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Divider divider;
8.5 Latch
Performs latch function with input, clock, set and reset that alternate between -1 and 1.
Declaration
Latch latch1;
Variables
double out; // output is 1 or -1 depending on latch state
Functions
// for all functions, in, clock, set, reset must be -1 or 1, values between -1 and 1 correspond to transitions
double inp(double in, double clk); // returns out based on in and clk
double inp(double in, double clk, double set, double reset); // includes set and reset
void init(double in); // initializes latch to value in (must be -1 or 1)
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in,sig1;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Divider divider;
Latch latch1, latch2, latch3, latch4;
// implement a register with divider output as its input, VCO output as its clock
// reset the register according to above register output
latch3.inp(divider.out,vco.out,−1.0,latch2.out); // first latch of register
latch4.inp(latch3.out,−vco.out,−1.0,latch2.out); // second latch of register
8.6 Reg
Performs register function with input, clock, set and reset that alternate between -1 and 1.
Declaration
Reg reg1;
Variables
double out; // output is 1 or -1 depending on register state
Latch lat1; // first latch in register
Latch lat2; // second latch in register
Functions
// for all functions, in, clock, set, reset must be -1 or 1, values between -1 and 1 correspond to transitions
double inp(double in, double clk); // returns out based on in and clk
double inp(double in, double clk, double set, double reset); // includes set and reset
void init(double in); // initializes both register latches to value in (must be -1 or 1)
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in,sig1;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Divider divider;
Reg reg1, reg2;
reg1.inp(divider.out,vco.out);
// implement a register with divider output as its input, VCO output as its clock
// reset the register according to above register output
reg2.inp(divider.out,vco.out,−1.0,reg1.out);
8.7 Xor
Performs ‘xor’ function with 2 inputs that alternate between -1 and 1.
Declaration
Xor xor1;
Variables
double out; // output is 1 or -1 depending on xor of inputs
Functions
// inputs must be either -1 or 1
// values between -1 and 1 correspond to transitions
double inp(double in0, double in1); // returns out = -in0∗in1; (the xor function)
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in,sig1;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Divider divider;
Reg reg1;
Xor xor1, xor2, xor3;
8.8 And
Performs ‘and’ function with 2 to 5 inputs that alternate between -1 and 1.
Declaration
And and1;
Variables
double out; // output is 1 or -1 depending on ‘and’ of inputs
Functions
// inputs must be either -1 or 1, values between -1 and 1 correspond to transitions
double inp(double in0, double in1); // returns out = 1 if and(in0,in1)=1, -1 if and(in0,in1)=0
double inp(double in0, double in1, double in2); // three inputs
double inp(double in0, double in1, double in2, double in3); // four inputs
double inp(double in0, double in1, double in2, double in3, double in4); // five inputs
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in,sig1;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Divider divider;
Reg reg1;
And and1, and2, and3;
8.9 Or
Performs ‘or’ function with 2 to 5 inputs that alternate between -1 and 1.
Declaration
Or or1;
Variables
double out; // output is 1 or -1 depending on ‘or’ of inputs
Functions
// inputs must be either -1 or 1, values between -1 and 1 correspond to transitions
double inp(double in0, double in1); // returns out = 1 if or(in0,in1)=1, -1 if or(in0,in1)=0
double inp(double in0, double in1, double in2); // three inputs
double inp(double in0, double in1, double in2, double in3); // four inputs
double inp(double in0, double in1, double in2, double in3, double in4); // five inputs
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in,sig1;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Divider divider;
Reg reg1;
Or or1, or2, or3;
8.10 EdgeMeasure
Measures time between rising edges of its input. Normalized to a sample time equal to one.
The output is zero except at the location of edges.
Declaration
EdgeMeasure edge time1;
Variables
double out; // output is time since last edge if input is rising edge, 0 otherwise
Functions
double inp(double in); // returns out, input must be a square wave alternating between -1 and 1
Example of Usage
#include "com_blocks.h"
main()
{
int i;
double Ts;
double in;
Ts = 1e−9;
Probe probe1("test.tr0",Ts); 10
Vco vco("fc + Kv*x","fc,Kv,Ts",10e6,1e6,Ts);
Divider divider;
EdgeMeasure edge time1,edge time2;
probe1.inp(edge time1.inp(vco.out),"vco_time");
// note: a given EdgeMeasure module cannot be used twice in one loop
8.10. EDGEMEASURE 121
probe1.inp(edge time2.inp(divider.out),"divide_time");
}
}
122 CHAPTER 8. CPPSIM CLASSES FOR PLL/DLL SIMULATION
Appendix A
This appendix provides four different examples illustrating the ability of the CppSim classes
to quickly and accurately simulate the behavior of PLL systems ranging from frequency
synthesizers to clock and data recovery circuits. These examples are NOT generated from
netlists, but rather are directly implemented in C++ code using the CppSim classes. Al-
though the netlist driven method should be used whenever possible, these examples provide
the user with a straightforward presentation of the structure and issues associated with doing
C++ simulations with the provided classes.
123
124 APPENDIX A. EXAMPLE SIMULATION CODE (NOT AUTO-GENERATED)
T
average
div(t) Divider
N[k] = Nnom
A key decision when designing the synthesizer is the choice of PFD structure. Two main
styles are available — the Tristate PFD and the XOR-based PFD. The Tristate PFD is
the most popular of the two, and allows charge pump noise to be minimized due to the
small pulse widths it achieves. The XOR-based PFD has advantages for Σ-Δ frequency
synthesizers since it avoids small pulses, and thereby achieves better linearity. For this
example, we will assume the Tristate PFD shown in Figure A.2 is used.
Example C++ code to achieve simulation of the above system is shown below.
#include "com_blocks.h"
main()
{
double Ts = 1/200e6;
Probe probe("test.tr0",Ts);
Vco vco("fc + Kv*x","fc,Kv,Ts",1.84e9,30e6,Ts);
SigGen ref clk("square",20e6,Ts);
Reg reg1,reg2;
And and1; 10
Filter rc filt("1.0","1 + 1/(2*pi*fp)*s","fp,Ts",127.2e3,Ts);
Filter int filt("2*pi*fp/10","s","fp,Ts",127.2e3,Ts);
double chp out,vco in;
A.1. CLASSICAL SYNTHESIZER 125
reg1 reg1.out
1 D Q
ref_clk.out
Q -and1.out
R and1
and1.out reg2.out
1 DRQ
vco.out
Q
reg2
1
ref_clk.out
-1
1
vco.out
-1
1
reg1.out
-1
1
reg2.out
-1
int i,N;
N = 90;
for (i = 0; i < 200000; i++)
{
// step desired VCO frequency by 1.0∗Kv at sample 160000
if (i == 160000) 20
N += 1;
// reference oscillator
ref clk.inp(0.0);
// PFD
reg1.inp(1.0,vco.out,−1.0,and1.out);
reg2.inp(1.0,ref clk.out,−1.0,and1.out);
and1.inp(reg1.out,reg2.out);
30
// Charge Pump
chp out = (reg2.out−reg1.out)∗.1989∗PI;
// Loop Filter
rc filt.inp(chp out);
int filt.inp(chp out);
126 APPENDIX A. EXAMPLE SIMULATION CODE (NOT AUTO-GENERATED)
probe.inp(N,"N");
probe.inp(vco in,"vco");
}
}
Inspection of the above code reveals that is quite straightforward to represent the system
using the CppSim classes.
Simulated results are shown in Figure A.3. The initial part of the response corresponds
to the PLL cycle slipping before it becomes locked in frequency. The right portion of the
plot illustrates the step response of the PLL for the case where it remains frequency locked.
−0.2
−0.4
−0.6
vco
−0.8
−1
−1.2
−1.4
−1.6
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
TIME −3
x 10
div(t) Divider
Example C++ code to achieve simulation of the Σ-Δ synthesizer is shown below. As in
the case of the classical synthesizer, the resulting code is compact and straightforward to
implement. Simulated results are shown in Figure A.5.
#include "com_blocks.h"
main()
{
double Ts = 1/200e6;
SdMbitMod sd mod("1 - 3z^-1 + 3z^-2 - 1z^-3");
Probe probe("test.tr0",Ts);
Vco vco("fc + Kv*x","fc,Kv,Ts",1.84e9,30e6,Ts);
SigGen ref clk("square",20e6,Ts);
Reg reg1,reg2; 10
And and1;
Filter rc filt("1.0","1 + 1/(2*pi*fp)*s","fp,Ts",127.2e3,Ts);
Filter int filt("2*pi*fp/10","s","fp,Ts",127.2e3,Ts);
Edge vco edge;
double chp out,vco in,in;
int i;
in = 90.3;
for (i = 0; i < 100000; i++)
{ 20
128 APPENDIX A. EXAMPLE SIMULATION CODE (NOT AUTO-GENERATED)
// reference oscillator
ref clk.inp(0.0);
30
// PFD
reg1.inp(1.0,vco.out,−1.0,and1.out);
reg2.inp(1.0,ref clk.out,−1.0,and1.out);
and1.inp(reg1.out,reg2.out);
// Charge Pump
chp out = (reg2.out−reg1.out)∗.1989∗PI;
// Loop Filter
rc filt.inp(chp out); 40
int filt.inp(chp out);
probe.inp(sd mod.out,"sd");
probe.inp(int filt.out,"int");
probe.inp(chp out,"chp");
probe.inp(vco in,"vco"); 50
probe.inp(reg1.out,"reg1");
probe.inp(reg2.out,"reg2");
probe.inp(vco.out,"out");
probe.inp(vco.phase,"phase");
probe.inp(ref clk.out,"ref");
}
}
A.3. LINEAR CDR 129
−0.2
−0.4
vco
−0.6
−0.8
−1
−1.2
−1.4
0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5
TIME −4
x 10
Retimed
Data
In this section, we will examine the simulation of a CDR that has linear dynamics by
virtue of using a Hogge phase detector. The Hogge structure is illustrated in Figure A.7.
Error
Data In
Clk
B C
A
Retimed Data
B
Reg Latch Retimed
Data In A Data C
D Q D Q
Clk Error
C++ simulation code for a linear CDR system is given below. As with the synthesizer
examples, one can see that the CppSim classes allow straightforward implementation of this
system in code.
#include "com_blocks.h"
main()
{
double Ts = 1/15e9;
Probe probe("test.tr0",Ts);
Vco vco("fc + Kv*x","fc,Kv,Ts",2.5e9,30e6,Ts);
SigGen prbs data("prbs",2.502e9,Ts);
Reg reg1;
Latch latch1; 10
Xor xor1,xor2;
Filter int filt("1","C*s","C,Ts",2e−9,Ts);
Filter rc filt("R","1 + 1/(2*pi*fp)*s","R,fp,Ts",1.07e3,40e6,Ts);
EdgeMeasure vco period,in period;
Rand randg("gauss");
double chp out,vco in,pd out,in;
double N dBc,f off,noise var,Kv;
int i;
/∗ VCO noise ∗/ 20
A.3. LINEAR CDR 131
// Charge Pump
chp out = 150e−6∗pd out; 40
// Loop filter
vco in = int filt.inp(chp out) + rc filt.inp(chp out);
vco in += sqrt(noise var/Ts)∗randg.inp(); // add VCO noise
// VCO
vco.inp(vco in);
Simulated results generated by the above simulation code are shown in Figure A.8. The
plot reveals an exponential decay of the phase error over time (or, equivalently, over VCO
cycle number). For reference, the Matlab code used to generate this plot is given below (this
code is contained in the CppSim/MatlabCode directory).
132 APPENDIX A. EXAMPLE SIMULATION CODE (NOT AUTO-GENERATED)
x = loadsig('test.tr0');
% phase = phase(30000:length(phase));
plot(phase,'-k');
grid on;
xlabel('VCO rising edge number');
ylabel('Instantaneous Jitter (U.I.)');
title str = sprintf ('Instantaneous Jitter of VCO in CDR: \n Steady-state RMS jitter = %5.4f mUI',1e3∗std(phase(30
title(title str);
%axis([-1e3 5e4 -.2 .05])
20
A.4. BANG-BANG CDR 133
0.5
Instantaneous Phase Error (U.I.)
0.4
0.3
0.2
0.1
0.1
0 1 2 3 4 5 6 7 8 9
VCO rising edge number 4
x 10
main()
{
double Ts = 1/15e9;
Probe probe("test.tr0",Ts);
Vco vco("fc + Kv*x","fc,Kv,Ts",2.5e9,50e6,Ts);
SigGen prbs data("prbs",2.502e9,Ts);
Reg reg1,reg2,reg3;
Latch latch1; 10
134 APPENDIX A. EXAMPLE SIMULATION CODE (NOT AUTO-GENERATED)
Xor xor1,xor2;
Filter int filt("2*pi*40e9","s","Ts",Ts);
EdgeMeasure vco period,in period;
Rand randg("gauss");
double chp out,vco in,pd out,in;
double N dBc,f off,noise var,Kv;
int i;
/∗ VCO noise ∗/
N dBc = −90; // -90 dBc/Hz at 1 MHz offset 20
f off = 1e6;
Kv = 50e6;
noise var = pow(10,N dBc/10.0)∗pow(f off/Kv,2);
// Charge Pump 40
chp out = 1e−6∗pd out;
// Loop filter
vco in = int filt.inp(chp out) + chp out∗125.0e3;
vco in += sqrt(noise var/Ts)∗randg.inp(); // add VCO noise
// VCO
vco.inp(vco in);
Retimed Data
Reg Reg Data In
Data In A
D Q D Q
Clk
Clk Error A
Retimed Data
Reg Latch
D Q D Q B
B
1
Clk
Error 0
-1
0
Instantaneous Phase Error (U.I.)
0.05
0.1
0.15
0.2
0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5
VCO rising edge number 4
x 10
The Hspice toolbox for Matlab is a collection of Matlab routines that allow you to
manipulate and view signals generated by Hspice simulations. The primary routine is a mex
program called loadsig.mexsol that reads binary output files generated by Hspice transient,
DC, and AC sweeps into Matlab. The remaining routines are used to extract particular
signals and view them.
We will begin this document by explaining how to include the Hspice toolbox in your
Matlab session. A list of each of the current functions will then be presented. Finally, we
will provide examples of using these routines to view and postprocess signals from Hspice
output files.
B.1 Setup
To use the Hspice toolbox, simply place the included files into a directory of your choice,
and then add that directory to your Matlab path. For example, inclusion of the path
’/home/username/CppSim/HspiceToolbox’ in Matlab can be done by adding the line
addpath(’/home/username/Cppsim/HspiceToolbox’)
to the file startup.m located in your home directory. In addition, you can specify the plot
background to be black (similar to the look of Awaves) by adding another line to startup.m:
colordef none;
137
138 APPENDIX B. HSPICE TOOLBOX FOR MATLAB
Once you’ve made the above changes to startup.m, start Matlab as you normally would.
Matlab will automatically read startup.m from your home directory and execute its com-
mands.
• x = loadsig(’hspice_output_filename’);
– Returns a Matlab structure into variable x that includes all of the signals that
are present in the Hspice binary output file, hspice_output_filename.
• lssig(x)
• y = evalsig(x,’nodename’);
– Pulls out the signal nodename from the structure x and places into variable y.
The string nodename can be an expression involving several Hspice signals. If you
only performed one sweep in the simulation (as is common), then y will contain
one column. If you performed several sweeps, y will contain several columns
that correspond to the data for each sweep. If you have set the global Matlab
variable sweep to a nonzero number, however, then y will contain only one column
corresponding to the value of sweep. If sweep equals zero, all the sweep columns
are included in y.
• plotsig(x,’plot_expression’,’optional_plotspec’)
– Plots signals from the structure x according to the expression given in plot_expression.
The string optional_plotspec is used to create logscale plots; it can be specified
as logx, logy, or logxy. The string plot_expression specifies the nodenames,
and corresponding mathematical operations, that you would like to view. In this
expression, commas delimit curves to be overlayed and semicolons delimit sepa-
rate subplots on the same figure. All numeric node names should be prepended
by ‘@’ to distinguish them from constants. Some examples of using plotsig are:
B.3. EXAMPLES 139
• xlima
– Sets the x-limits of all subplots in a figure. Three options are possible:
∗ xlima: sets all subplots to the same x-axis as the last subplot that was zoomed
into,
∗ xlima([xs xe]): sets all subplots to the x-axis limits specified,
∗ xlima(’auto’): resets all subplots back to autoscaling.
– Note: ylima and xylima functions are also provided. See comments in ylima.m
and xylima.m for proper usage.
• eyesig(x,period,start_off,’nodename’)
– Creates an eye diagram for nodename contained in x with the specified period.
All data samples prior to start_off are ignored when creating the diagram (useful
for removing the influence of transient effects from the eye diagram). The string
nodename can be an expression involving several variables.
B.3 Examples
Viewing Signals
Use the Matlab command cd to go to a directory containing a binary transient, DC, or AC
sweep file generated from Hspice. We will assume a filename of test.tr0, and now list a
series of Matlab commands that will be used to display nodes q and qb in that file.
140 APPENDIX B. HSPICE TOOLBOX FOR MATLAB