Internal Circuit Representation Reference

This document covers only how a circuit is represented after a netlist is parsed. This representation attempts to be independent of how the circuit is analyzed. Any specific analysis approach should be implemented separately in the analysis package.

UML Class Diagram

UML class diagram for circuit representation classes

All device model classes are derived from the Element class.

Modules and classes

circuit – Classes for internal circuit representation

Example of how to use this module

The following example was originally taken from analyses/nodal.py. The objective of this code is to assign row/column numbers to all non-reference nodes and -1 to all reference nodes in a Circuit instance (ckt):

# make a list of all non-reference terminals in circuit 
ckt.nD_termList = ckt.termDict.values() + ckt.get_internal_terms()
# get reference node (if any)
if ckt.has_term('gnd'):
    ckt.nD_ref = ckt.get_term('gnd')
    # remove ground node from terminal list
    ckt.nD_termList.remove(ckt.nD_ref)
# remove ground node from terminal list
ckt.nD_termList.remove(ckt.nD_ref)
# For reference nodes assign -1
ckt.nD_ref.nD_namRC = -1
# Make a list of all elements
ckt.nD_elemList = ckt.elemDict.values()
# Set RC number of reference terminals to -1
for elem in ckt.nD_elemList:
    if elem.localReference:
        elem.connection[elem.localReference].nD_namRC = -1
# Assign a number (starting from 0) to all nodes.
for i, term in enumerate(ckt.nD_termList):
    term.nD_namRC = i

The following example shows how to create and add devices from the point of view of a parser:

import circuit as cir
from devices import devClass

# Create circuit:
mainckt = cir.Circuit()

# Add elements with private model

# creates element type 'ind'
dev = devClass['ind']('Lchoke')

# override 'l' parameter value (otherwise default value is used)
dev.set_param('l', 1e-9) 

# add to circuit 
mainckt.add_elem(dev)

# connect to terminals 'vdd' and and 'drain', automatically created
mainckt.connect(dev, ['vdd', 'drain'])

# add element with shared (public) model: model 'my_nmos' is created
# if it is not already in circuit
m1 = devClass['mosacm']('mos1n')
mainckt.add_elem(m1, 'my_nmos')

# To later retrieve model
model = cir.get_model('my_nmos')
# if model not found returns None, use cir.add_model() if needed

# Subcircuits: create and add subcircuit instance
x1 = cir.Xsubckt('x1', 'LM741')
mainckt.add_subckt(x1)
# Connect subcircuit instance with cir.connect()

# Subcircuit definition: tl2 are the external connections
tl2 = ['vdd', 'vee', 'in', 'out']
amp1 = cir.SubCircuit('LM741', tl2)
# now you can treat amp1 as a regular circuit instance
dev = devClass['ind']('L2')
dev.override = [('l', 2e-9)] 
amp1.add_elem(dev)
amp1.connect(dev, ['in', 'n1'])

Notes

  • If anything goes wrong a CircuitError exception is thrown
  • The device type is automatically added to Element names. This prevents name conflicts from devices of different type. Example: ‘mosacm:mos1n’
  • When all circuits and subcircuits are entered, you can flatten() the main circuit to remove subcircuit hierarchy (optional).
  • Before elements can be used, the circuit should be initialized with init(). This performs some simple checks and initializes all elements in circuit (and sub-circuits if not flattened).
  • .model statements have global scope: they can be used and referred from any circuit/subcircuit.
  • The same applies to subcircuit definitions. They are global and can be referred from any circuit/subcircuit.
class cardoon.circuit.Circuit(name)

Holds a circuit.

There are 2 global dictionaries defined at the class level:

  • cktDict: Contains references to all circuit/subcircuit definitions
  • modelDict: References to all models in any circuit. Thus .model statements are global and can be defined and referred anywhere

Element, Xsubckt and (external) Terminal references are stored in dictionaries, empty by default:

  • elemDict
  • subcktDict
  • termDict

Internal terminals must be accessed directly from the parent Element instance.

Ground node: terminals ‘0’ and ‘gnd’ are considered to be the same reference node. If a circuit does not contain a ground node then it is up to the user to set a reference.

Plot/Save requests are stored in lists: plotReqList/saveReqList

In the future we may implement topology checking utilities here.

add_elem(elem, modelName=None)

Adds an element to a circuit.

If modelName is not given it is assumed that no global model will be used

Otherwise the model is retrieved (or created if necessary) and assigned to elem.dotModel. This model can be shared with other elements.

A check is made to make sure the instance name is unique

add_subckt(xsubckt)

Adds a Xsubckt instance to circuit

check_sanity()

This is slow for large circuits. Use for debugging to make sure that all elements and terminals have been added to the circuit. Also checks for floating terminals.

For now only works for unflattened circuits

connect(element, termList)

Connect an Element (or subckt) instance to terminals

Terminals are specified by a list of terminal names (termList). If a terminal does not exist in the circuit it is created and added.

Important notes:

  • Element/subckt should be in circuit dictionary. No attempt to check this is made (use add_elem()).

  • Order in the adjacency list is important for Elements

    For example, for a MOSFET the first node corresponds to the drain, the second to the gate, etc.

copy(newName)

Returns a copy of an uninitialized circuit

newName is the circuit name for the copy

The elements in the copy point to the ParamSet of the original element (models are not cloned). Elements are shallow-copied, but terminal connections are re-generated.

Example:

ckt2 = ckt1.copy('ckt1copy')

If the circuit has been flattened, the copy does not include subcircuit instances, otherwise a shallow copy of subcircuit instances are generated.

find_term(termName)

Find a terminal (external/internal) by name

termName: full name, including container Element name for
internal terminals

Returns terminal instance if found or raises CircuitError exception if not

flatten()

Expand all subcircuit instances in place, assuming the circuit and subcircuits have not been initialized.

The flattened elements point to the ParamSet of the original element (models are not cloned). Elements are shallow-copied, but terminal connections are re-generated.

get_internal_terms()

Returns a list with all non-reference internal terminals

Circuit must be initialized first. Note that the same effect can be achieved by directly polling the elements.

get_requested_terms(reqtype)

Returns a set with terminals to be plotted or saved

Set generated from all plot and save requests of the specified type.

get_term(termName)

Returns an external terminal instance with the given name.

A new instance is created if necessary

globals_to_str()

Convert all global stuff to netlist format

This includes: models, netlist variables and .options variables

has_term(termName)

Returns True if terminal present in circuit

init()

To be used after all elements/terminals have been created. Can be called multiple times but only has an effect if self._initialized is False

  1. Initialize all elements. This includes a check for terminal connections and parameter processing. Elements may add internal terminals at this point.
  2. If not flattened, checks that subcircuit definitions exist and checks number of terminal connections.
  3. Checks that output requests contain valid terminal names
netlist_string()

Generates a ‘netlist-like’ description of the circuit

remove_elem(elemName)

Disconnect and remove an element from a circuit. Internal terminals are also removed.

remove_term(termName)

Removes a terminal from a circuit. Links are not removed, you must take care of that.

exception cardoon.circuit.CircuitError

Used for exceptions raised in this module

class cardoon.circuit.Element(instanceName)

Base class for circuit Elements.

Default flags are set here.

add_internal_term(name, unit)

Create and connect one internal terminal

name: internal terminal name unit: internal variable unit

Returns internal terminal index

add_reference_term()

Create and connect one internal terminal to be used as local reference

check_terms()

Checks terminal connections

If numTerms is not zero, checks that the number of connected terminals is equal to numTerms. Raises an exception if they do not match.

Else sets numTerms = number of connected terminals

clean_internal_terms()

Disconnect any internal terms,

Normally used before calling process_params() for a second time or when an element is removed from circuit.

disconnect(terminal)

Disconnect a terminal from an element. Arguments are Element and Terminal instances.

Assumption is that if terminal is in element’s list, then the nodes are linked and element should be on terminal’s list. If nodes not connected an exception is raised.

get_internal_terms()

Returns a list of internal terms (if any) excluding local references

init()

General initialization function

Set the attribute values, check basic terminal connectivity and process parameters.

is_set(paramName)

Returns True if paramName is valid and manually set

netlist_string()

Output netlist-formatted string in netlist format

print_vars()

Nicely print parameter list and OP (if any) with values and units

set_attributes()

Set parameters as attributes.

Priority is as follows: first manually set parameters, then manually set parameters in model (if any) and finally default values

class cardoon.circuit.GraphNode(instanceName)

Simple graph node base class (not circuit node)

Used for circuit Elements, subcircuits and terminals. Links are stored using lists.

class cardoon.circuit.InternalTerminal(element, name)

Represent terminals that are internal to one Element instance

They only have one connection (the parent Element instance)

get_label()

Return label for plots/tables in a formatted string

class cardoon.circuit.OutRequest(reqtype, varlist)

Holds a set of output variables requests

Output request consist in:

  1. Type of of request (type): dc, ac_*, tran, etc.
  2. List of variables (varlist): for external terminals, these are strings with terminal name. For internal terminals, a list with device and terminal names.

After initialization the circuit adds a list of terminals in the termlist attribute.

class cardoon.circuit.SubCircuit(name, termList)

Almost identical to Circuit but adds support for external connections.

External subcircuit terminals have an additional attribute set: subCKTconnection. This attribute is set to the subcircuit’s connection number. Example:

.subckt  inverter in out vplus vminus

in.subCKTconnection = 0
out.subCKTconnection = 1
vplus.subCKTconnection = 2
vminus.subCKTconnection = 3

The reference node (‘gnd’ or ‘0’) is global , i.e., a terminal named ‘gnd’ in a subcircuit is assumed to be connected to the same ground node in all other circuits/subcircuits. To avoid problems, the ground terminal can not be included in the external terminal list. One of the possible problems is short-circuiting a node to ground when a subcircuit is connected. Example of invalid code:

vdc:vcc 1 0 vdc=10V
vdc:vee 2 0 vdc=-10V
xamp1 1 2 in out amplifier

.subckt amplifier 1 gnd in out
res:rc 1 out r=5k
cap:cin in 2 c=1uF
bjt:q1 out 2 gnd type=npn
.ends

Possible solutions:

  1. Rename ‘gnd’ node in subcircuit to something else
  2. Remove ‘gnd’ node (implicitly connected to terminal ‘0’ in main circuit) from subcircuit external terminal list
copy(newName)

Returns a copy of an uninitialized circuit

newName is the circuit name for the copy

Similar as Circuit.copy() but also takes care of subcircuit connections

get_circuit(xsubckt, target)

Dump copy of circuit into target (for hierarchy flattening)

Subcircuit instances are ignored

get_connections()

Returns a list of subcircuit terminals

netlist_string()

Ganerates a ‘netlist-like’ description of subcircuit.

Produces the same output as the Circuit class plus extra .subckt and .ends lines.

class cardoon.circuit.Terminal(instanceName)

Represent circuit terminals (i.e., nodes)

This class should used only for ‘external’ terminals (i.e., terminals that appear in the netlist). See also InternalTerminal

get_label()

Return label for plots/tables in a formatted string

class cardoon.circuit.Xsubckt(instanceName, cktName)

Represent subcircuit instances (not definitions, use SubCircuit for those)

netlist_string()

Convert to string in netlist format

cardoon.circuit.add_model(model)

Adds a public model (parameter set) to a circuit.

A check is made to make sure the instance name is unique

cardoon.circuit.get_mainckt()

Used to get the circuit called ‘main’ weather it has been already created or not

cardoon.circuit.get_model(modelName)

Returns model reference if present in circuit, otherwise returns None.

cardoon.circuit.reset_allckt()

Erases all existing circuits

Used mostly for debugging purposes

paramset – Classes for parameter handling

Handles sets of parameters used for Elements, Models and Analyses

Netlist variables are also handled here. They are stored in the ParamSet.netVar dictionary, accesible to all derived classes.

To reset parameters to netlist values, use the following:

obj.clean_attributes()
obj.set_attributes()
class cardoon.paramset.Model(name, modelType, paramDict)

Provides ‘.model’ functionality (used for Elements)

Provides some extra functionality in addition to ParamSet methods

netlist_string()

Output netlist-formatted string using pset definitions and attributes values from var

set_missing_attributes(target)

Set attributes not present in target with values in stored in self

exception cardoon.paramset.ParamError

Used for exceptions raised in this module

class cardoon.paramset.ParamSet(paramDict)

Handles a set of parameters with values.

Useful for devices, analyses and anything that accepts parameters.

Parameter definitions given in a dictionary with the following format:

paramDict = dict(
    w = ('Channel width', 'm', float, 10e-6),
    l = ('Channel length', 'm', float, 10e-6),
    vt0 = ('Threshold Voltage', 'V', float, 0.532),
    vsat = ('Velocity Saturation', 'm/s', float, 8e4),
    tox = ('Oxide Thickness', 'm', float, 7.5e-9)
    )
clean_attributes()

Delete parameter attributes

describe_parameters(paramName=None)

Returns a string with parameter information

If no parameter is specified all parameters are listed

format(paramName)

Returns a string describing parameter named param

get_float_attributes()

Returns a list with the names and values of ‘float’ attributes

This is handy for sensitivity calculation. Each item in the list is a tuple: (<name>, <value>)

The set is assumed to be already initialized. Values are taken directly from instance attributes.

get_type(paramName)

Returns type if parameter exists

is_set(paramName)

Returns True if paramName is valid and manually set

list_parameters()

Briefly list parameter names

netlist_string()

Output netlist-formatted string listing only non-default parameters

reset()

Reset to default state

set_attributes(useDefaults=True)

Set attributes named after parameters in self.valueDict

Parameter values may refer to netlist variables. If a netlist variable is referenced but not defined, an exception is raised here.

If useDefaults is True, set defaults from self.paramDict

set_param(paramName, value)

Set parameter given with paramName to value

Note that actual attribute is not set until set_attributes() is executed. A check is made to ensure the parameter name is valid.

If value type is a string and does not match with parameter type it is assumed that the value is a netlist variable name. The netlist variable may be assigned a value at a later time before set_attributes() is called.

set_params(**kwargs)

Set all parameter values given in kwargs

globalVars – Global simulator options and physical constants

Physical constants are from http://physics.nist.gov/cuu/Constants/

Usage example:

from globalVars import const, glVar

# Calculate thermal voltage (Vt) at default temperature
vt = const.k * glVar.temp / const.q

Important Note: The glVar name really refers to the variables in the .options line, not the netlist variables defined in .vars. Those are kept in a dictionary in ParamSet.

devices – Device Library

The devices package contains a library with device models. Device classes are imported into a dictionary. Keys are the device types. To create a new device use the following:

devices.devClass['devType']('instancename')

Example (from python):

from devices import devClass
# Create device instance
m1 = devClass['mosacm']('m1n')

Example (from netlist):

mosacm:m1n <nodes> <parameters>

Making a device model to be recognized by this package

Suppose the new model is implemented in a file named newmodel.py. Save this file in the devices directory and edit devices/__init__.py. Add your module name to netElemList as shown below:

# Regular 'netlist' elements must be listed here
netElemList = ['mosACM', 'resistor', 'capacitor', 'inductor', 'idc', 'vdc', 
               'diode', 'svdiode', 'mosEKV', 'bjt', 'svbjt', 'newelem']

That’s all!

UML diagrams generated using pyreverse (http://www.logilab.org/2560)