Using Electric 11-2: The Lisp Interface

Chapter 11: INTERPRETERS

| ## 11-2: The Lisp Interface |

This section explains the Lisp interpretive language interface in the Electric VLSI design system. This interface is built on top of ELK Lisp 3.0, a Scheme dialect (see http://www-rn.informatik.uni-bremen.de/software/elk/elk.html).

Throughout this section, examples of Lisp code will appear underlined. For example, the "getarcproto" predicate takes the name of an arc prototype and returns a pointer to that object. This is coded as (getarcproto 'Metal-1) which evaluates to the pointer of the form #[arcproto Metal-1].

This section assumes that the reader is very familiar with the use of Electric, and somewhat familiar with the internals of the system. The Internals Manual (a document that is available from Static Free Software) provides a broad, C-oriented view of the information described here. For users of Lisp, however, this section summarizes the relevant aspects of the Internals Manual. In general, the best way to understand this section is to try each command as it is explained.

### Session Control

To invoke the Lisp interpreter, use the LISP... subcommand of the Language Interpreter command of the Tools menu. On some systems it may be necessary to move the cursor into the messages window (the text window) in order for the interpreter to "hear" you.

If you have a disk file with Lisp code in it, you can read it into the interpreter by typing:
(load 'FILENAME)

To get back to Electric from Lisp, type ^D (hold the Control key and type a "D"). On Windows, you must type the ESC key instead.

### Database Structure

The entire Electric database is a collection of objects, each of which has an arbitrary number of attributes. This section briefly outlines the object types and shows how they are related. Further detail can be found in the Internals Manual. See Section 11-5 for a list of attributes on these objects.

Individual components inside of circuits are described with NODEINST objects (instances of nodes), and individual wires are described with ARCINST objects (instances of arcs). Connections between components and wires are described with PORTARCINST objects (instances of ports that connect to arcs). Because both components and wires have geometry, each one also has an associated GEOM object, and all of the GEOM objects in a facet are organized spatially into an R-tree with a collection of RTNODE objects.

Class objects also exist to describe all individuals of a given type. The NODEPROTO object describes the prototypical component, which may have many individual NODEINST objects associated with it. For example, the CMOS P-Transistor is described with a single NODEPROTO object, and many NODEINST objects for each instance of such a transistor in any circuit. Hierarchy is implemented by having complex components, better known as facets, represented in the same way as the primitive components such as transistors. For example, the ALU circuit is described with a single NODEPROTO object, and each instance of that circuit higher up the hierarchy is described with a NODEINST object.

The CELL object aggregates different views and versions of a circuit. Each of these is called a "facet" (represented with a NODEPROTO object) and a facet has both a VIEW pointer and a version number.

In addition to component prototypes, the ARCPROTO describes classes of wires and the PORTPROTO describes classes of component-wire connections. An additional object, the PORTEXPINST, exists for exports. The NETWORK object describes electrically connected ARCINST and PORTPROTO objects within a FACET.

As a further aggregation of objects, the LIBRARY is a collection of cells and facets. The TECHNOLOGY is a collection of primitive components (NODEPROTOs) and all wire classes (ARCPROTOs).

In addition to the above object pointers, there are some standard types of values that can be accessed through getval:

integer | 32-bit integer
address | 32-bit integer
char | 8-bit byte
string | null-terminated string of bytes
float | 32-bit floating point number
double | 64-bit floating point number
fract | fractional integer number that is multiplied by 120
short | 16-bit integer
window | window partition object
window-frame | display window object
constraint | constraint system object
graphics | graphical attributes object

Also, there is the ability to have displayable variables (those whose values appear on the object) with the keyword: displayable.

### Database Examination

To begin a search through the database, it is important to know the current library. This is done with:
(curlib)
which returns a pointer to a LIBRARY object (for example #[library noname]). From here, the current facet can be obtained with:
(getval (curlib) 'firstnodeproto)

Essentially, any attribute can be examined with getval, and new attributes can be created with setval. Getval has the following format:
(getval OBJECT 'ATTRIBUTE)
where OBJECT is the object being accessed and ATTRIBUTE is the attribute being requested. A list of all existing attributes on the Electric objects is given at the end of this document.

New attributes can be created on any object with setval. In general, many of the existing attributes that are described at the end of this document cannot be set with setval, but rather are controlled with special database modification predicates. The format for setval is:
(setval OBJECT 'ATTRIBUTE VALUE OPTIONS)
Where the OPTIONS are either 0 or 'displayable to show this attribute when displaying the object. For example, to add a new attribute called "power-consumption" to the transistor component "t1", and give it the value 75, use:
(setval t1 'power-consumption 75 0)
To add a displayed name to node "t1", use:
(setval t1 'NODE_name "Q1" 'displayable)
To set an array of values, use vectors. For example, to set the shape of pure-layer node "metal" to be a diamond, use:
(setval metal 'trace (vector -1000 0 0 1000 1000 0 0 -1000) 0)

Single entries in array attributes can be set, with:
(setind OBJECT 'ATTRIBUTE INDEX VALUE)
where INDEX is the 0-based entry in the array.

Finally, attributes can be deleted with:
(delval OBJECT 'ATTRIBUTE)
However, only those attributes that have been created with setval can be deleted in this way. The other attributes are protected.

### Basic Synthesis

To create a new facet in the current library, use:
(newnodeproto 'FACETNAME (curlib))
which returns a NODEPROTO pointer that can be used in subsequent calls which place components and wires in that facet.

To get the address of an existing NODEPROTO, use:
(getnodeproto 'FACETNAME)
which returns the same type of value as newnodeproto. Thus, the code:
(define myfacet (newnodeproto 'adder{lay} (curlib)))
is the same as the code:
(newnodeproto 'adder{lay} (curlib))
(define myfacet (getnodeproto 'adder{lay}))
and both deal with the "layout" view of the cell called "adder".

As an aside, the predicate getcell can be used to get a CELL object, such that:
(getcell 'adder)
returns the same thing as:
(getval myfacet 'cell)

To create a component in a facet, use:
(newnodeinst PROTO LOWX HIGHX LOWY HIGHY TRANSPOSE ANGLE FACET)
where PROTO is a NODEPROTO of the component that is to be created, LOWX, HIGHX, LOWY, and HIGHY are the bounds of the component, ANGLE is the number of tenth-degrees of rotation for the component, TRANSPOSE is nonzero to transpose the component's orientation (after rotation), and FACET is the NODEPROTO in which to place the component.

The four bounds values are somewhat confusing to compute. For primitive components (such as Transistors), any value is acceptable and the component will scale. However, it is still nice to know the default value, which can be obtained from the NODEPROTO with getval as follows:
(define tran (getnodeproto 'P-Transistor))
(define lowx (getval tran 'lowx))
(define highx (getval tran 'highx))
(define lowy (getval tran 'lowy))
(define highy (getval tran 'highy))
When complex components (facets) are placed, the bounds MUST be exactly the same as the bounding box of the facet's contents. This information is available in the above manner. As an example of newnodeinst, and given the above bounds calculations, a default size P-Transistor is created in facet "adder" with:
(define t1 (newnodeinst tran lowx highx lowy highy 0 0 myfacet))
The returned pointer to the transistor component will be used later when wiring.

To wire two components, it is necessary to know these four things:

- The component objects on the two ends, returned by newnodeinst
- The PORTPROTO values of the connection sites on the components
- The X and Y coordinates of the connection sites
- The type, width, and other characteristics of the wire being created

Connection sites are called PORTPROTOs and are associated with NODEPROTOs. To get the address, use:
(getportproto NODEPROTO 'PORTNAME)
For example, to get the polysilicon port on the left side of the MOSIS CMOS P-Transistor, use:
(define polyleft (getportproto tran 'p-trans-poly-left))
Unfortunately, there is no good way to get a list of port names on the primitive components. There are, however, some simplifications. For example, if there is only one port (as is the case with most contacts and pins) then its name is not necessary:
(define port (getval tran 'firstportproto))
This will obtain the first port on the P-Transistor component. To obtain the coordinates of a port for wiring, use:
(portposition NODE PORT)
This returns a vector with the coordinates. For example:
(define portpos (portposition t1 polyleft))
will obtain the coordinate of the "p-trans-poly-left" port on the newly created P-Transistor, t1. The X value will be (vector-ref portpos 0) and the Y value will be (vector-ref portpos 1).

The final piece of information necessary is the type of arc and the width of the arc. Given an arc name, the type can be obtained with:
(getarcproto 'ARCNAME)
Given an ARCPROTO, its default width can be obtained with:
(getval ARCTYPE 'nominalwidth)
When all of the information is ready, the call:
(newarcinst ARCTYPE WIDTH BITS NODEA PORTA XA YA NODEB PORTB XB YB FACET)
places the wire. You can ignore the value of BITS and set it to zero.

Here is a complete example of placing a transistor, a contact, and running a wire between them (the result is shown here).

; create a facet called "tran-contact" in the current library (define myfacet (newnodeproto 'tran-contact (curlib))) ; get pointers to primitives (define tran (getnodeproto 'P-Transistor)) (define contact (getnodeproto 'Metal-1-Polysilicon-1-Con)) ; get default sizes of these primitives (define tlowx (getval tran 'lowx)) (define thighx (getval tran 'highx)) (define tlowy (getval tran 'lowy)) (define thighy (getval tran 'highy)) (define clowx (getval contact 'lowx)) (define chighx (getval contact 'highx)) (define clowy (getval contact 'lowy)) (define chighy (getval contact 'highy)) ; get pointer to Polysilicon arc and its default width (define arctype (getarcproto 'Polysilicon-1)) (define width (getval arctype 'nominalwidth)) ; create the transistor and the contact to its left (define c1 (newnodeinst contact clowx chighx clowy chighy 0 0 myfacet)) (define t1 (newnodeinst tran (+ tlowx 8000) (+ thighx 8000) tlowy thighy 0 0 myfacet)) ; get the transistor's left port coordinates (define tport (getportproto tran 'p-trans-poly-left)) (define tpos (portposition t1 tport)) ; get the contacts's only port coordinates (define cport (getval contact 'firstportproto)) (define cpos (portposition c1 cport)) ; run a wire between the primitives (newarcinst arctype width 0 t1 tport (vector-ref tpos 0) (vector-ref tpos 1) c1 cport (vector-ref cpos 0) (vector-ref cpos 1) myfacet) |

### Hierarchy

Facets, as created by newnodeproto, can be placed in other facets with newnodeinst. The instances simply use complex NODEPROTO fields rather than primitive NODEPROTOs as in the above example. For example, the following code creates a new facet called "two-trans" and places two instances of the above "tran-contact" facet, one above the other.

; create a facet called "two-trans" (define higherfacet (newnodeproto 'two-trans (curlib))) ; get pointer to the "tran-contact" facet (define t-c (getnodeproto 'tran-contact)) ; get size of this facet (define lowx (getval t-c 'lowx)) (define highx (getval t-c 'highx)) (define lowy (getval t-c 'lowy)) (define highy (getval t-c 'highy)) ; create the two facet instances, one above the other (define o1 (newnodeinst t-c lowx highx lowy highy 0 0 higherfacet)) (define o2 (newnodeinst t-c lowx highx (+ lowy 10000) (+ highy 10000) 0 0 higherfacet)) |

Another necessary feature, when making hierarchy, is the ability to place wires between connection sites on facet instances. To do this, it is necessary to create exports. This takes a port on a primitive component (for example, the transistor or contact in the "tran-contact" facet) and makes it into an export on the current facet. This is done with:
(newportproto FACET NODE-IN-FACET PORT-ON-NODE 'PORTNAME)
where FACET is the facet containing the component whose port is being exported, NODE-IN-FACET is that component, and PORT-ON-NODE is the particular port on that node being exported. For example, to export the top and bottom diffusion ports in the "tran-contact" facet (as shown here), the following code can be added:

(newportproto myfacet t1 (getportproto tran 'p-trans-diff-top) 'topdiff) (newportproto myfacet t1 (getportproto tran 'p-trans-diff-bottom) 'botdiff) |

And then, the components "o1" and "o2" in the facet "two-trans" can be wired, using the ports called "topdiff" and "botdiff":

; get pointer to P-Active arc and its default width (define darctype (getarcproto 'P-Active)) (define dwidth (getval darctype 'nominalwidth)) ; get the bottom facet's top port (define lowport (getportproto myfacet 'topdiff)) (define lowpos (portposition o1 lowport)) ; get the top facet's bottom port (define highport (getportproto myfacet 'botdiff)) (define highpos (portposition o2 highport)) ; run a wire between the primitives (newarcinst darctype dwidth 0 o1 lowport (vector-ref lowpos 0) (vector-ref lowpos 1) o2 highport (vector-ref highpos 0) (vector-ref highpos 1) higherfacet) |

### Modification

Two types of modification can be done to existing objects: deletion and change. To delete a facet, use:
(killnodeproto FACET)

To make a copy of a facet (within the same library or from one library to another), use:
(copynodeproto FROM-FACET TO-LIBRARY 'TO-FACET-NAME)
where FROM-FACET is the original facet (NODEPROTO) and TO-LIBRARY is the destination library. Use (curlib) to copy to the same library. The new facet name is the last parameter. The predicate returns the address of the new facet (NODEPROTO).

To delete a component, use:
(killnodeinst NODE)
Before a component can be deleted, all wires and exports must be removed.

To change the size or orientation of a component, use:
(modifynodeinst NODE DLOWX DLOWY DHIGHX DHIGHY DROTATION DTRN)
where DLOWX, DLOWY, DHIGHX, and DHIGHY are the changes to position and size. DROTATION and DTRN are changes to the orientation.

To change the prototype of a component, use:
(replacenodeinst OLD-NODE NEW-PROTOTYPE)
where the old component is OLD-NODE, and the new NODEPROTO that should be in its place is NEW-PROTOTYPE. This new prototype must be able to connect to all existing arcs. The predicate returns the address of the new component.

To delete a wire, use:
(killarcinst ARC)

To change the width or position of a wire, use:
(modifyarcinst ARC DWIDTH DX1 DY1 DX2 DY2)
where DWIDTH, DX1, DY1, DX2, and DY2 are the changes to the width, X/Y position of end 1, and X/Y position of end 2. Note that position changes cannot cause the connecting nodes to move, so the changes may only be small ones that work within the ports.

To change the prototype of a wire, use:
(replacearcinst OLD-ARC NEW-PROTOTYPE)
where OLD-ARC is the former wire and NEW-PROTOTYPE is the new ARCPROTO to use. The nodes on either end must be able to accept this new type of wire. The predicate returns the address of the new wire.

To delete an export, use:
(killportproto FACET PORT)
which will remove port PORT on facet FACET.

To move an export from one component to another (keeping connected wires), use:
(moveportproto FACET OLD-PORT NEW-NODE PORT-ON-NEW-NODE)
where the old port is OLD-PORT in facet FACET, and it is now moved to component NEW-NODE (which is also in facet FACET), port PORT-ON-NEW-NODE of that component.

### Search

A common operation is a search of all components in a facet. The following code prints the name of all components in the facet "myfacet":

(do
(
(node (getval myfacet 'firstnodeinst)
(getval node 'nextnodeinst))
)
((null? node))
(format #t "Found ~s node~%" (describenode node))
)

Where describenode is defined as follows (the name of a node is found in different places depending on whether it is a primitive or complex NODEPROTO):

(define describenode
(lambda (node)
(define proto (getval node 'proto))
(if (= (getval proto 'primindex) 0)
(getval (getval proto 'cell) 'cellname)
(getval proto 'primname)
)
)
)

And the following code prints the name of all wires in the facet "myfacet":

(do
(
(arc (getval myfacet 'firstarcinst)
(getval arc 'nextarcinst))
)
((null? arc))
(format #t "Found ~s arc~%"
(getval (getval arc 'proto) 'protoname))
)

To do a search of all nodes and arcs in a rectangular area of a facet, first call:
(initsearch LOWX HIGHX LOWY HIGHY FACET)
where LOWX, HIGHX, LOWY, and HIGHY are the coordinates to search in facet FACET (a NODEPROTO). This predicate will return a search key that can then be passed repeatedly to:
(nextobject SEARCHKEY)
which will return GEOM objects of each node and arc in the search area. When this predicate returns Null, the search is complete. GEOM objects can point to either nodes or arcs, depending on their "entrytype" attribute: 1 for nodes and 2 for arcs. Then, the "entryaddr" attribute will point to the actual NODEINST or ARCINST. Here is an example of code that prints the names of all nodes and arcs in the area (2000 <= X <= 10000, -3000 <= Y <= 3000). The selected area is shown as a black box here.

(define key (initsearch 2000 10000 -3000 3000 myfacet)) (do ( (object (nextobject key) (nextobject key)) ) ((null? object)) (define type (getval object 'entrytype)) (if (= type 1) (format t "Found ~s node~%" (describenode (getval object 'entryaddr)) ) (format t "Found ~s arc~%" (getval (getval (getval object 'entryaddr) 'proto) 'protoname) ) ) ) |

### Views

A view is an object that describes a cell. There are many standard views: Layout, Schematic, Icon, Simulation-snapshot, Skeleton, VHDL, Verilog, Document, Unknown, and many flavors of Netlist. In addition, new views can be created with "newview":
(newview 'VIEWNAME 'ABBREVIATION)
and views can be deleted with killview (the standard views cannot be deleted):
(killview VIEW)
To get a view object, use getview on its name.

To associate different views of a cell, the predicates iconview and contentsview obtain different facets. For example:
(iconview myfacet)
finds the associated icon facet of the cell in which "myfacet" resides.

### Libraries

In the above examples, the current library was always used. This is determined by calling:
(curlib)
However, there can be other libraries. To get a specific named library, use:
(getlibrary 'LIBNAME)

To create a new library, use:
(newlibrary 'LIBRARYNAME 'LIBRARYFILE)
where LIBRARYNAME is the name to use, and LIBRARYFILE is the path name where this library will be saved. This predicate returns the address of a new library object that can then be used when creating facets.

Only one library is the current one, and to switch, you must use:
(selectlibrary LIBRARY)

A library can be deleted with:
(killlibrary LIBRARY)

A library can be erased (its facets deleted, but not the library) with:
(eraselibrary LIBRARY)

### Technologies

A technology is an environment of design that includes primitive components and wire prototypes. The current technology can be obtained with:
(curtech)

A specific technology can be obtained from its name with:
(gettechnology 'TECHNAME)

All technologies can be found by traversing a linked list, the head of which is a technology named "Generic".

### Tools

A tool is a piece of synthesis or analysis code that can operate upon the database. A particular tool object can be obtained with:
(getaid 'TOOLNAME)
where the possible names of tools are:

compaction | circuit compaction
compensation | geometry compensation
drc | design-rule checking
erc | electrical-rule checking
io | input/output control
logeffort | logical effort analysis
network | network maintenance
pla | programmable logic array generator
project | project management
routing | automatic wire routing
silicon-compiler | netlist-to-layout silicon assembler
simulation | simulation
user | the user interface
vhdl-compiler | VHDL-to-netlist compiler

The number of tools is available with:
(maxaid)
And a particular tool, indexed from 0 to (maxaid)-1 can be obtained with:
(indexaid INDEX)

A tool can be switched on with:
(aidturnon TOOL NO-CATCH-UP)
where TOOL is a tool object, and NO-CATCH-UP is nonzero to suppress the catch-up activity normally provided when a tool has been off.

A tool can be turned off with:
(aidturnoff AID)

A tool can be given a specific instruction with:
(tellaid AID PARAMETERS)
For example, to list all technologies, use this code:
(tellaid (getaid 'user) 'show 'technologies)
These commands are from the low-level command interpreter, which is documented fully in the Internals Manual.

### Miscellaneous

Every change to the database is queued internally in a "batch" which includes the change and any constrained side-effects of that change. A new batch is created for each Lisp session with the interpreter (also for each Electric command that is issued from the keyboard/mouse). To reverse the last batch of changes, use:
(undoabatch)

Multiple calls to this predicate in a single batch will undo multiple batches. To erase the list of change batches, use:
(noundoallowed)

If you are creating a wire that makes many bends, it is necessary to create special nodes called "pins" at each bend. To find out what kind of pin to use for a given wire type, use:
(getpinproto ARC-PROTO)
where ARC-PROTO is the wire type, and the predicate returns the component type (NODEPROTO) of the pin.

Network objects can be obtained by name with the predicate getnetwork which takes a name and a facet in which to search. For example, the code:
(getnetwork 'insig myfacet)
obtains the address of the network called "insig" in facet myfacet.

The generic function of a node instance can be determined with:
(nodefunction NODE)
which returns a value from the list of constants in the C header file "efunction.h". This value is essentially the same one as would be obtained by looking at the "userbits" field of the node's prototype. However, certain components that have generic prototypes will be made more specific by this predicate.

To get an attribute value from an instance above this in the hierarchy, use:
(getparentval 'name DEFAULT HEIGHT)
where name is the attribute name, DEFAULT is the default value to return if the attribute is not found, and HEIGHT is the number of levels of hierarchy to climb when looking for the attribute (0 for infinite). As a shortcut for finding parameter values, there are four macros which use this routine:

- (P 'xx) obtains the value of parameter xx from the parent instance in the hierarchy.
- (PD 'xx def) obtains the value of parameter xx from the parent instance in the hierarchy and uses the value def if the parameter cannot be found.
- (PAR 'xx) obtains the value of parameter xx from any higher instance, anywhere in the hierarchy.
- (PARD 'xx def) obtains the value of parameter xx from any higher instance, anywhere in the hierarchy and uses the value def if the parameter cannot be found.

| Previous |  |  | Table of Contents |  | Next |