Quantcast
Channel: Cadence Custom IC Design Blogs
Viewing all 826 articles
Browse latest View live

SKILL for the Skilled: Many Ways to Sum a List (Part 7)

$
0
0

In this episode of SKILL for the Skilled I'll introduce a feature of the let primitive that Scheme programmers will find familiar, but other readers may have never seen before. The feature is called named let, and I'll show you how to use it to sum the numbers in a given list.

Named LET

There is a feature of let available in SKILL++ which is not available in traditional SKILL, called named let. Here is an example of how it can be used to sum a given list of numbers.
(defun sumlist_7a (numbers)
  (let REPEAT
    ((sum_so_far 0)
     (rest       numbers))
    (if rest
        (REPEAT (plus sum_so_far (car rest))
                (cdr rest))
        sum_so_far)))
In this example, the named let defines a local function named, REPEAT and calls it once with the two arguments, 0 and numbers. Of course, REPEAT is by no means a reserved word; you can name the local function any valid variable name. Within the body of the named let you may call the local function with two arguments. You may recursively call the function zero, one, or more times as your algorithm requires.

Testing the function

If we enable tracing of sumlist_7a and REPEAT, we can see what happens when calling sumlist_7a. We can see that the local function, REPEAT is called recursively several times, and that the implementation does in fact return the correct sum of the given list of numbers.
(trace sumlist_7a)
(trace REPEAT)
(sumlist_7a '(1 2 3 4 5))
|sumlist_7a((1 2 3 4 5))
||(REPEAT 0 (1 2 3 4 5))
|||(REPEAT 1 (2 3 4 5))
||||(REPEAT 3 (3 4 5))
|||||(REPEAT 6 (4 5))
||||||(REPEAT 10 (5))
|||||||(REPEAT 15 nil)
|||||||REPEAT --> 15
||||||REPEAT --> 15
|||||REPEAT --> 15
||||REPEAT --> 15
|||REPEAT --> 15
||REPEAT --> 15
|sumlist_7a --> 15
15

Equivalent to labels

The named let is more or less equivalent to a declaration and call of a local function as if by using labels. If you recall, this is exactly what was shown in sumlist_3b in SKILL for the Skilled: Many Ways to Sum a List (Part 3).

(defun sumlist_3b (numbers)
  (labels ((sum (sum_so_far rest)
             (if rest
                 (sum (plus (car rest) sum_so_far)
                      (cdr rest))
                 sum_so_far)))
    (sum 0 numbers)))

If you trace sumlist_3b and sum you'll see that it executes pretty much the same thing as sumlist_7a.

(trace sumlist_3b)
(trace sum)
(sumlist_3b '(1 2 3 4 5))
|sumlist_3b((1 2 3 4 5))
||(sum 0 (1 2 3 4 5))
|||(sum 1 (2 3 4 5))
||||(sum 3 (3 4 5))
|||||(sum 6 (4 5))
||||||(sum 10 (5))
|||||||(sum 15 nil)
|||||||sum --> 15
||||||sum --> 15
|||||sum --> 15
||||sum --> 15
|||sum --> 15
||sum --> 15
|sumlist_3b --> 15
15

Illusion of jumping to the top

The illusion (or abstraction) presented by the named let is that of being able to jump back to the top of the let form, and evaluate it again with different initialization values.

Consider this simple let example.

(let loop
   ((a 10)
    (b 0))
 (println (list a b))
 (when (plusp a)
   (loop (sub1 a) 
         (add1 b))))
Which prints the following output.
(10 0)
(9 1)
(8 2)
(7 3)
(6 4)
(5 5)
(4 6)
(3 7)
(2 8)
(1 9)
(0 10)

Doesn't work in traditional SKILL

If you try to evaluate a named let in traditional SKILL (e.g., with a .il file extension), you'll get an error something like the following, which basically means that SKILL let expects a list as its first operand and you have given the symbol loop instead.

*Error* let: local bindings must be a proper list - loop

let is syntactic sugar for lambda

In SKILL++ the normal let has the same semantics as calling an unnamed function with particular parameter values. For example:

(let ((a X)
      (b Y)
      (c Z))
  (expr1 a b)
  (expr2 b c))
Is semantically the same as the following arguably less readable expression. The expression uses funcall to call a nameless function, defined by the (lambda (a b c) ...); in particular to call it with the three values X, Y, and Z. In fact the two code snippets are simply syntactical transforms of each other.
(funcall (lambda (a b c)
           (expr1 a b)
           (expr2 b c))
         X
         Y
         Z)
When you look at the equivalent lambda form of let it is immediately clear that the expressions within the lambda are not able to make recursive calls to this unnamed function. This limitation is solved by the named let.

Named let and tail-call optimization

The illusion of jumping back to the top in sort of a goto fashion is indeed what happens if tail-call-elimination is enabled via the optimizeTailCall status flag explained in SKILL for the Skilled: Many Ways to Sum a List (Part 4).

Summary

In this post I've shown some examples of how to used the named let construct of SKILL++. This construct converts the conventional let into a loop --- a loop which can be repeated by calling the label as a function, providing the next iteration's variable values.

More to come

In upcoming posts we'll continue to survey the SKILL++ language using the example of summing a list.

See Also

SKILL for the Skilled: Many Ways to Sum a List (Part 3)
SKILL for the Skilled: Many Ways to Sum a List (Part 4)
Scheme In particular see the discussion of named let


Virtuosity: 10 Things I Learned in March by Browsing Cadence Online Support

$
0
0

Topics in March include advanced analysis in ADE GXL, taking advantage of lots of features for doing statistical analysis in ADE XL, defining bindkeys in ADE L (yes, you can do that!), plus a variety of useful details in the areas of routing and advanced custom layout.

Enjoy!

Application Notes

1. Design Tuning with Analog Design Environment GXL: Interactive and Automated Flows

Walks through a detailed example using several different advanced analysis tools, including Sensitivity Analysis, Manual Tuning, and Optimization.  Includes demo database.

2. Statistical Analysis Quick Start (ADE XL)

Overview and quick reference covering the statistical analysis features in ADE XL, including setup options for Monte Carlo Sampling, defining confidence levels, using auto-stop, post-processing data using histograms, quantile plots and waveforms, and creating statistical corners.

3. Mixed-Signal PSL Assertions in AMS Designer

Provides an overview of writing mixed-signal PSL assertions to handle Verilog-AMS, analog values, etc.  Includes a detailed case study, accompanying example tarkit and exercises.

Rapid Adoption Kit

4. Power, Ground and Structured Routing Implementation Flow

The flow leverages a combination of existing Virtuoso functionality, Virtuoso Routing TCL commands, and custom SKILL scripts to enable users to efficiently build custom power, ground, and structured signal routes.  The flow also supports creation of power and ground rough-in during early floorplanning. This flow is intended for analog mixed-signal designs at the chip, block, and device level in Virtuoso.  Includes demo database and detailed documentation providing an overview of the flow, guidelines for use in your designs and step-by-step walkthrough using the demo database.

Videos

5. Mark Nets

This video describes the Mark Net command including Mark, Save, Unmark, Options, and Retaining Via Info.

6. Generating Clones in Virtuoso

This is a demo that describes generating clones in Virtuoso. It includes topics such as creating clones as Free Objects, Group Objects, and Synchronized Family.

Solutions

7. VLS-XL: Modgen: Abutting Modgen members using SKILL (Solution 20033396)

Skill routine to use to abut all the devices in a given Modgen.

8. How to query directional spacing tables from techfile with SKILL? (Solution 20034969)

Some example Skill commands showing you how to access horizontal and vertical spacing rules from the techfile.

9. How to change nport file names for corner simulation (Solution 20040165)

You have an nport with an S-parameter file.  For each corner, the nport uses a different S-parameter file.  This solution shows you how to wrap up the nport as a subcircuit library with sections you can reference just like any other model file sections.

10. How to define bindkeys for ADE L window (Solution 20051691)

Shows you how to register bindkeys which will be active in the ADE L window.  Examples provided to create bindkeys for "Netlist and Run" and Direct Plot commands.

SKILL for the Skilled: Many Ways to Sum a List (Part 8): Closures -- Functions with State

$
0
0

In the past several postings to this blog, we've looked at various ways to sum a given list of numbers. In this posting I'll present yet another way to do this. This time the technique will be markedly different than the previous ways, and will take advantage of a powerful feature of SKILL++, namely lexical closures. These closures will be used to implement data encapsulation, and we'll also use lexical closures to capture computation state and continue the computation later.

Put the CIWindow into SKILL++ mode

Before proceeding, we need to change the listening mode of the CIWindow. We would like the CIWindow to interpret input expressions as SKILL++ rather than traditional SKILL.

Normally, when you type SKILL expressions into the CIWindow that defines functions or defines variables, the semantics of your code is taken as traditional SKILL. If, however, you would like to have SKILL++ (Scheme) semantics, you can put the CIWindow into SKILL++ Mode by calling the function (toplevel 'ils). This function does not return immediately, but rather puts the CIWindow into a different listening mode until you call the function resume, causing the (toplevel 'ils) to return.

You can find out which listening mode the CIWindow is in either by looking at the indicate in the button left-hand corner of the CIW. If in SKILL listening mode > will be inconspicuously displayed. The > is a little difficult to notice because it is so inconspicuous.

FILE UNREADABLE

However, if in SKILL++ listening mode ILS-> will be displayed.

FILE UNREADABLE

You may also determine the listening mode by calling the function theEnvironment which will return either nil if in SKILL mode, or non-nil, if in SKILL++ mode.

 

(theEnvironment)  ==> nil
(toplevel 'ils)
(theEnvironment)  ==> envobj@0x18fa2020
(resume)  ==> nil
(theEnvironment)  ==> nil

Defining an adder

With the CIWindow in SKILL++ mode we can proceed to define a SKILL++ function.
(unless (theEnvironment)
   (toplevel 'ils))

(defun make_adder_8a ()   ; 1.1
  (let ((sum 0))          ; 1.2
    (lambda (n)           ; 1.3
      sum = sum + n)))    ; 1.4

This definition of make_adder_8a is a 4 line function, yet does a lot in its 4 lines. It is a higher-order function, as seen in SKILL for the Skilled: What is SKILL++? it is a function which returns another function. It is a function which creates and returns a special kind of function called a lexical closure. In particular the lambda on line 1.3 creates a unary function which when called will evaluate the expression on line 1.4. However, the expression on line 1.4, references a variable, sum defined on line 1.2 and which is external to the lambda. In this case sum is called a free variable.

The important feature of SKILL++ which makes this magic work is that when make_adder_8a gets called, the let on line 1.2 creates a new binding for the variable named sum. A binding is a newly allocated memory location which is associated with a named variable. The initial value stored in this memory location is 0 as indicated on line 1.2. SKILL++ then proceeds to line 1.3 where it creates an anonymous function, attaching it to this binding. The two occurrences of the name, sum, on line 1.4 (within this anonymous function) are compiled as references to the binding.

The make_adder_8a function returns the anonymous function object created on line 1.3, without evaluating the code on line 1.4. Critically this function object maintains a link to the sum binding. Thereafter when the anonymous is called, the expression on line 1.4 is evaluated and its value returned. In evaluating this expression, the value of sum (in this allocated binding) is referenced and updated by the expression sum = sum + n, n being the value passed to the anonymous function when it is called.

Testing make_adder_8a

Let's experiment with make_adder_8a. Keep in mind that make_adder_8a does not actually add anything, rather it creates a function which is capabile of adding.
A = (make_adder_8a)       ; 2.1  ==> funobj:A
(A 1)                     ; 2.2  ==> 1
(A 2)                     ; 2.3  ==> 3
(A 3)                     ; 2.4  ==> 6
(A 4)                     ; 2.5  ==> 10
(A 5)                     ; 2.6  ==> 15

Arduous line-by-line explanation

On line 2.1, make_adder_8a is called, and as described above a SKILL++ anonymous function object (a closure) is created and returned. This closure is stored in the global variable A.

The CIWindow prints this value as funobj:A. As mentioned before the initial value of sum is 0. Note that sum is not a global variable. Rather it is a variable which is visible only to the code on lines 1.3 and 1.4. Furthermore, notice that we have no immediate access to the variable sum. We cannot query the value of code and we cannot modify its value, except by calling the function we have just stored in the global variable A.

When line 2.2, (A 1) is evaluated, the expression on line 1.4 is evaluated: sum = sum + n with n=1; sum is updated from 0 to 1, and 1 is returned and printed into the CIWindow.

When line 2.3, (A 2) is evaluated, again the expression on line 1.4 is evaluated: sum = sum + n with n=2. This time, sum is updated from 1 to 3 and 3 is returned.

When lines 2.4, 2.5, and 2.6 are evaluated, sum = sum + n is evaluated three times with n=3, n=4, and n=5 respectively; thus sum is updated to 6, 10, and finally to 15.

Summing a list with an adder

You can use an adder as created by make_adder_8a to add the elements of a list incrementally.
A = (make_adder_8a)          ; 3.1  ==> funobj:A
(mapcar A '(1 2 3 4 5))      ; 3.2  ==> (1 3 6 10 15)
This call to mapcar iterates across the given list (1 2 3 4 5) and calls the function A on each iteration, each time with the successive element of the list. Since each call to A returns the current value of the partial sum, mapcar returns not he final sum, but rather the list of partial sums computed at each step of the iteration.

  

Multiple SKILL++ adders in parallel

You can create several adders which work independent of each other. In the following example, we create two adders, A and B. Each one internally maintains its own partial sum of the arguments given to successive calls.
B1 = (make_adder_8a)       ; 4.1  ==> funobj:B1
B2 = (make_adder_8a)       ; 4.2  ==> funobj:B2
B3 = (make_adder_8a)       ; 4.3  ==> funobj:B3
(B1 1)                     ; 4.4  ==> 1
(B2 10)                    ; 4.5  ==> 10
(B3 100)                   ; 4.6  ==> 100
(B1 2)                     ; 4.7  ==> 3
(B2 20)                    ; 4.8  ==> 30
(B3 200)                   ; 4.9  ==> 300
(B1 3)                     ; 4.10  ==> 6
(B2 3)                     ; 4.11  ==> 60
(B3 30)                    ; 4.12  ==> 600

This works because each call to make_adder_8a on lines 4.1, 4.2, and 4.3, each allocate a new closure (three in all), assign each of them respectively turn to the global variables B1, B2, andB3. Each of these three function has its own independent binding of sum, each of which is initialized to code. When lines 4.4, 4.7, and 4.10 are evaluated, the sum binding of B1 is updated, but the sum bindings of B2 and B3 are not effected. Similarly when 4.5, 4.8, and 4.11 are evaluated, the sum binding of B2 is effected. And similarly for lines 4.6, 4.9, and 4.12.

Using flet as an alternative to lambda

If you find the use of (lambda ...) to be confusing in the definition of make_adder_8a, you can, as an alternative, define the same functionality using flet.

 

(defun make_adder_8b ()       ; 5.1
  (let ((sum 0))              ; 5.2
    (flet ((add (n)           ; 4.3
             sum = sum + n))  ; 5.4
      add)))                  ; 5.5

This implementation of make_adder_8b uses flet to define a local function named add. The normal pattern of using flet which you've seen in previous posts of SKILL for the Skilled such as Continued Introduction to SKILL++, is to define a local function and call it. The pattern used by make_adder_8b is to define a local function and return it, allowing the function which called make_adder_8b to call it, or likewise return it to its caller.

Data Encapsulation and Object Orientation

Some programming languages present the ability to encapsulate data as part of the object model. In these languages private variables are member variables within classes, and methods in/on those classes awkwardly manipulate and access these private variables.

This unholy marriage of private data to object model is limiting in practice because it is not only object oriented programs which need to hide data. In fact, a program written in any style may need to take advantage of encapsulate. In SKILL++ (and other dialects of Scheme), data encapsulation is independent from the object system, as it should be.

In SKILL++ the let and lambda primitives behave differently than in traditional SKILL. They behave in a way which allows a function to create lexical closures. These lexical closures are then able to manipulate the state of private variables which they encapsulate.

Summary

In this article, we looked at how to use lexical closures which maintain their internal state to implement counters. We looked very lightly and abstractly into how this is implemented within the SKILL++. And we traced step by step though a couple of examples of how this works in practice.

In this way SKILL++ provides data encapsulation completely independent from the object system. While SKILL++ does indeed have an extensive, full-featured, and powerful object system, in SKILL++ you are not forced to write a program in an object oriented way just to encapsulate/hide data. Encapsulation is a feature of SKILL++ which is available to you whether you are using OO, imperative, declarative, functional, or any other style you choose.

More to come

In the next post we'll look more at the differences you'll see when defining these types of functions in SKILL++ vs in SKILL.

See Also

SKILL for the Skilled: What is SKILL++?
as Continued Introduction to SKILL++

Jim Newton

Things You Didn't Know About Virtuoso: Delta Markers in ViVA

$
0
0

This article is dedicated to the gentleman I sat next to at lunch at CDNLive a while back who Is a CAD engineer busily supporting a large user community, but had been stumped by the question "How do I create a delta marker in VIVA?"

I'm sure he is not alone.  Delta markers in IC6.1.5 ViVA (Virtuoso Visualization and Analysis Tool) are very powerful, but they can be a bit hard to find and unless you read the documentation (or this blog), you may never unlock all their useful capabilities.  That's what I'm here for...

Note that the method for creating delta markers changed in IC6.1.5 ISR8, so if the instructions below doesn't work for you, you may need to update your Virtuoso version.

First, I want to direct your attention to a handy little document I put together a while ago which provides a 1-page Quick Reference to the most useful bindkeys in ViVA.  Markers in ViVA are all about bindkeys, and for delta markers, you have to know just where and in what combination to use those bindkeys.

The basic summary goes like this:

  • M for point marker
  • V for vertical marker 
  • H for horizontal marker
  • A/B for a/b marker (special kind of delta marker--see below)
  • Shift-D for delta's amongst selected markers (details below)
  • D for create 2nd marker and delta (details below)

A/B Markers

This is a special kind of delta marker.  To create it, press A somewhere, then B somewhere else.  You'll get 2 point markers labelled A and B and a delta label between them.  Now, wherever you press A or B again, the original A or B marker will move to that spot and the delta values will update.  Only one A/B marker allowed per window, but you can click the right mouse button (RMB) on the delta dimension line and select "Convert A/B Marker to Delta" so you can keep that information on the graph and continue playing the A/B game at another location.

Shift-D

If you have put down a bunch of markers (point, vertical, horizontal), you can select 2 or more of them (using Ctrl-click to select multiple markers) and thenpress Shift-D to get delta markers between all the selected markers.  The cool thing about this is that you can mix point markers with vertical or horizontal markers to get delta values from a point to a line.

Bindkey D

This will probably be your most commonly-used method of creating delta markers.  Simply select a point, vertical or horizontal marker (M, V, H) and then wherever you press the D key, you will get a 2nd marker of that type and the delta between.  Since when you create a point, vertical or horizontal marker, it remains selected, you can use the sequence M, D, D, D... or V, D, D, D... or H, D, D, D... to get multiple markers with delta values between them with just a few keystrokes.

A few extra tips

Use the menu pick Marker->Show Delta Child Labels (or RMB in a blank area of the graph->Show Delta Child Labels) to toggle the visibility of the point marker labels at either end of the delta and only display the delta values. (IC6.1.5 ISR12)  Helps reduce clutter on the graph.

Delta values between vertical markers now show up in the Vertical Marker table (IC6.1.5ISR12).  Choose Window->Assistants->Vert Marker Table or use the built-in MarkerTable workspace to open the marker table.  You can use Marker->Export->Vertical Markers to create a CSV file with all the marker values.

Watch them in action

If any of the above is not clear, watching this video clip should help.  Note that this short clip is part of a longer video available here, covering everything you need to know about using all types of markers in ViVA. If video fails to open, click here.

Stacy Whiteman

Virtuosity: 10 Things I Learned in April by Browsing Cadence Online Support

$
0
0

I'll confess: I didn't learn all of this strictly by browsing http://support.cadence.com (Cadence Online Support).  I also wandered over onto http://www.cadence.com/community/blogs/ii (Industry Insights blog) and http://www.cadence.com/cadence/events (Cadence Events), which were well worth a look.

ApplicationNote

1. Demystifying NCELAB

You've gotta love any technical document that begins with the word "demystifying".  Explains typical causes of and solutions for elaboration errors frequently encountered in running a digital or mixed-signal design using AMS Designer.  Organized by error code.  Includes descriptions, examples, solutions, and accompanying database.

Videos

2. Running Monte-Carlo Simulations with AMS in ADE-XL

Computer-narrated video describing how to set up and run Monte Carlo simulations using the AMS simulator in ADE XL

3. Virtuoso Connectivity-Driven Layout Solution Segment--Connectivity Extraction from Labels

Video segment from a Cadence physical design training course demonstrating extracting connectivity from labels

Blog

4. CDNLive Silicon Valley 2013 Proceedings Available for Download

Over 80 downloadable presentations from customers and partners discussing how they have used Cadence tools to solve real problems.  Custom IC, mixed signal, low power, advanced node--you name it, you can find something interesting to learn about.

Rapid Adoption Kit

5. Digital Mixed-Signal (DMS) Implementation Using EDI and Virtuoso

Reaching out to our colleagues in the digital world.  Learn about design import, early timing analysis, pin optimization, AoT block design, and top-level timing analysis.  Includes database and detailed workshop instructions.

Webinars

6. TSMC-Cadence Webinars for Advanced Node Design: Addressing Layout-Dependent Effects

Archived webinar discussing the TSMC Custom Design Reference Flow 3.0, which provides a complete layout-dependent effect (LDE) flow for circuit and layout designers working at 28nm and below.

7. A Completely Validated Solution for Designing to the TSMC 20nm Process Using Cadence Tools

Upcoming webinar scheduled for May 23, 2013.  Learn about how in-design design rule checking (DRC) and double patterning technology (DPT) checking can improve productivity; how to efficiently manage coloring data in front-to-back custom design flows; how local interconnect layers are supported within the Cadence Virtuoso platform, and how TSMC’s 20nm process technology and the Cadence methodology support this process.

Solutions

8. Recommended platform patches for systems running Cadence products

Not much description needed for this one.  Always a handy table of information to have.

9. Fluid Guardring changes shape with newer version of Virtuoso

How to prevent Fluid Guardrings from changing shapes and spacings with changes to your Virtuoso version.

10. How to keep ADE XL jobs running even if ADE XL GUI crashes

New environment variable in ADE XL, which allows you to close the ADE XL or Virtuoso session without killing jobs that are currently running.  Also works if Virtuoso crashes.

 

SKILL for the Skilled: Part 9, Many Ways to Sum a List

$
0
0
In the previous postings of SKILL for the Skilled, we've looked at different ways to sum the elements of a list of numbers. In this posting, we'll look at at least one way to NOT sum a list.

In my most recent posting, the particular subject was how to use SKILL++ to define a make_adder function. I commented in that article that the same thing would not work in traditional SKILL. In this posting, I'd like to walk through some of the issues you would encounter if you actually tried to implement this in traditional SKILL. Understanding why it does not work in the traditional SKILL dialect may shed some light on why it does work in the SKILL++ dialect.

Remember, these are not bugs in any sense in SKILL. The fact simply is that in some subtle ways traditional SKILL and SKILL++ behave differently. You can exploit the differences to your advantage, depending on your particular application.

Recall, here is the definition of make_adder_8a as you might type it into the CIWindow.

(unless (theEnvironment)
   (toplevel 'ils))

(defun make_adder_8a ()   ; 1.1
  (let ((sum 0))          ; 1.2
    (lambda (n)           ; 1.3
      sum = sum + n)))    ; 1.4

Try it in traditional SKILL

What happens if you try to define the make_adder_9a in traditional SKILL? Here is what will happen when you try to call the function.
(when (theEnvironment)
  (resume))

(defun make_adder_9a ()  ; 2.1
  (let ((sum 0))         ; 2.2
    (lambda (n)          ; 2.3
      sum = sum + n)))   ; 2.4

You see immediately that something very subtle is different.

C = (make_adder_9a)  ==> funobj@0x1e666390

The make_adder_9a function, when defined in traditional SKILL, returns an object which is printed something like funobj@0x1e666390. Contrast that with the SKILL++ version of the function make_adder_8a defined above, which returned an object which was printed as funobj:A. What happens if we try to call the function we just created?

(C 1)
*Error* eval: undefined function - C<<< Stack Trace >>>
C(1)
A drastic difference is obvious when you try to call the function, C. You have an error message indicating that C is not a function. This can be confusing to the beginner SKILL++ programmer. The reason for this is that the CIWindow is in SKILL listening mode. A similar thing happens if loading code from a file with a .il file name extension as opposed to a .ils extension.

For a discussion of changing the CIWindow listening mode between SKILL and SKILL++, see SKILL for the Skilled: Part 8, Many Ways to Sum a List, or consult the Cadence SKILL documentation for the toplevel and resume.

Setting a variable in SKILL

When the CIWindow is in SKILL listening mode an expression such as
C = (make_adder_9a)
modifies (or initially defines) a global variable named C to be the value returned from the make_adder_9a function. An expression such as
(defun C ()
  (println '(1 2 3 4)))
defines a global function named C. Critically important to how traditional SKILL works is that the variable C and the function C are different and independent. Either one can be referenced, or modified independent of the other. This is both a feature of SKILL as well as a point of confusion.

In SKILL mode the following expressions are semantically equivalent in setting the value of the variable C.

C = (make_adder_9a)
(setq C (make_adder_9a))
(set 'C (make_adder_9a))

The following expressions are equivalent in defining the function C.

(defun C ()
  (println '(1 2 3 4)))

(procedure (C)
  (println '(1 2 3 4)))

(putd 'C
  (lambda ()
    (println '(1 2 3 4))))

Setting a variable in SKILL++

By contrast when the CIWindow is in SKILL++ mode, an expression such as A = (make_adder_8a) (shown above) not only sets the global variable named A, but also defines a global function named A. If you then proceed to evaluate an expression such as
(defun A ()
  (println '(1 2 3 4)))

the global variable, A and the global function A will be modified to something new, and the old value will be lost (unless of course you have saved a reference to it).

SKILL++ does not support having a global variable and global function of the same name with different values.

In SKILL++ mode all the following expressions are equivalent in setting the global variable and simultaneously defining the global function named C.

(defun C ()
  (println '(1 2 3 4)))

(procedure (C)
  (println '(1 2 3 4)))

(define (C)
  (println '(1 2 3 4)))

(define C
  (lambda ()
    (println '(1 2 3 4))))

(setq C
  (lambda ()
    (println '(1 2 3 4))))

(putd 'C
  (lambda ()
    (println '(1 2 3 4))))

LISP-1 vs LISP-2

Another way to think about the different semantics of variables and functions between SKILL and SKILL++ is that SKILL++ is a LISP-1 and traditional SKILL is a LISP-2. LISP-1 means that there is one single name space for variables and functions. LISP-2 means that there are two different names spaces, one for variables and one for functions.

Use funcall to call a function indirectly

To call a function, in SKILL mode, whose value is stored in a variable (global or otherwise), you need to use funcall. If you use funcall to call the function whose value is in the global variable D, the function is indeed called but quickly encounters an error.
D = (make_adder_9a)  ==> funobj@0x1e666390
(funcall D 1)
*Error* eval: unbound variable - sum<<< Stack Trace >>>
(sum + n)
(sum = (sum + n))
funobj@0x1e666390()
funcall(D 1)

For some reason, the definition of the adder created by make_adder_9a, tries to reference a global variable named sum. Let's ignore this error momentarily, and assume we already accidentally had a global variable named sum whose value happened to be 0.

sum = 0  ==> 0
D = (make_adder_9a)  ==> funobj@0x1e666390
(funcall D 1)  ==> 1
(funcall D 2)  ==> 3
(funcall D 3)  ==> 6
(funcall D 4)  ==> 10
(funcall D 5)  ==> 15

This requirement of having to use funcall when calling a function indirectly is inherent to the way traditional SKILL works.

From the above experiment it deceptively seems that the SKILL function make_adder_9a behaves much the same as make_adder_8a with the exception of the calling syntax: (funcall D 1) vs (A 1).

A second SKILL adder fails

In the previous post, we used make_adder_8a to define adders B1, B2, and B3. We saw that all three adders kept track of their partial sum independently. What happens if we try to define even one additional adder using make_adder_8b?
E = (make_adder_9a)  ==> funobj@0x1e666390
(funcall E 1)  ==> 16
(funcall E 2)  ==> 18
(funcall E 3)  ==> 21

Hey wait! Why did (funcall E 1) return 16? Let's set the global sum back to 0 and try it again.

sum = 0  ==> 0
(funcall E 1)  ==> 1
(funcall E 2)  ==> 3
(funcall E 3)  ==> 6

OK, setting sum back to 0 fixes it. Now, look again at D.

(funcall D 1)  ==> 7
(funcall E 2)  ==> 9
(funcall D 3)  ==> 12

It seems that D and E are both using the same variable to keep track of the partial sum. In fact, if you print the value of the variable sum you'll see that its value is now 12.

Why does this fail?

In SKILL mode, the two adders, D and E, created by two different calls to make_adder_9a do not create two independent adders as did make_adder_8a in SKILL++ mode. The reason for this is critical to an important difference between SKILL and SKILL++. Look at lines 1.2, 1.3, and 1.4 of make_adder_8a and 2.1, 2.3, and 2.4 of make_adder_9a. While these lines are textually identical, they are semantically different in that make_adder_8a is SKILL++ and make_adder_9a is traditional SKILL.

Low level details of calling a SKILL function

Suppose make_adder_8a is called twice on line 3.1 and 3.4. Each time it is called, line 1.2 allocates a new binding which associates the variable sum with a location to store the value. The two functions created on line 1.3 each time make_adder_8a is called (lines 3.1 and 3.2), reference those respective bindings. You can see from line 3.3, that each time make_adder_8a is called, a different function is returned.

The textual reference to sum on line 1.4 references and modifies the value in the location of binding depending on which of the functions it is running from. When lines 3.4, and 3.5 are evaluted, line 1.4 references and modifies the first binding which was created as a result of evaluating line 3.1. Likewise, when lines 3.6, and 3.7 are evaluted, the same line 1.4 references and modifies the second binding which was created as a result of evaluating line 3.2.

A = (make_adder_8a)      ; 3.1  ==> funobj:A
B = (make_adder_8a)      ; 3.2  ==> funobj:B
(eq A B)                 ; 3.3  ==> nil
(A 1)                    ; 3.4  ==> 1
(A 2)                    ; 3.5  ==> 3
(B 11)                   ; 3.6  ==> 11
(B 22)                   ; 3.7  ==> 33

Low level details of calling a SKILL++ function

Constrast that with the very different scenaraio which what happens in make_adder_9a. In this case when make_adder_9a is called (lines 4.1 and 4.2), each time a binding is created (because the let on line 2.2 is evaluated twice), but the local function created on line 2.3 DOES NOT any explicitly reference it. In fact both times make_adder_9a is called (lines 4.1 and 4.2), the exact same function is returned as seen on line 4.3.

When make_adder_9a returns the SKILL local function created on line 1.3, the call to make_adder_9a returns and all references to the binding created on line 2.2 are subsequently lost. Thereafter, when the function D or E is called, SKILL (at calling time) searches for the currently defined variable named sum which is global.

D = (make_adder_9a)      ; 4.1  ==> funobj:@0x1e666390
E = (make_adder_9a)      ; 4.2  ==> funobj:@0x1e666390
(eq D E)                 ; 4.3  ==> t
(D 1)                    ; 4.4  ==> 1
(D 2)                    ; 4.5  ==> 3
(E 11)                   ; 4.6  ==> 11
(E 22)                   ; 4.7  ==> 33

Calling the functions stored in D and E (on lines 4.4 through 4.7) have much the same effect as directly evaluating a call to a copy of the inline function shown on lines 1.3 and 1.4. For example, lines 4.5 and 4.6 (D 2) and (E 11), are equivalent to the following which has the effect of referencing and modifying the global variable named sum.

(funcall (lambda (n) sum = sum + n)
         2)
(funcall (lambda (n) sum = sum + n)
         11)

Summary

In this article we looked at several differences between SKILL and SKILL++.
  1. funcall -- How to call functions indirectly.
    • SKILL -- You must use funcall: (funcall C 1)
    • SKILL++ -- You may call the function directly: (A 1)
  2. name-space --
    • SKILL -- functions and variables live in different names-spaces; the function C and the variable C are different.
    • SKILL++ -- supports a single name-space; function A and the variable A are the same.
  3. closures -- how free variables are handled
    • SKILL -- does not support closures; a free variable is resolved at run-time and may resolve differently depending on how a function is called.
    • SKILL++ -- supports closures ; a free variable is resolved at compile time, and refers to the same allocated location no matter how the functions references them are called.

Feedback

Please let me know if you find any of the discussion or techniques discussed in this or previous articles useful.

Thanks for reading!

Jim Newton 

Virtuosity: 10 Things I Learned in May by Browsing Cadence Online Support

$
0
0

May was a big month for new videos. It was also a month that saw the release of Virtuoso IC6.1.6, with lots of great new features and the rollout of new enhancements to the Cadence Online Support website.

Videos

1. DMS Basics Series

This is a great series of 10 videos covering various topics in mixed-signal verification, real number modeling, and mixed-signal connectivity. You'll also notice all 10 videos referenced together in sequence as a single "playlist" so you know what order to watch them in.

2. Enter Points

Demonstrates some enhancements in IC6.1.6 whereby the user can enter coordinates for commands using a form instead of pointing in the layout canvas.

3. IC6.1.6 Ruler

Demonstrates several enhancements to the ruler command in IC6.1.6.

4. Palette—IC6.1.6 Enhancement

This video discusses new modes available to invoke Palette, bindkeys, synchronization/desynchronization, editing LayerSets, and editing validity status of LPP. Some of these topics are also discussed in the solution: Palette—How to encapsulate the Layers, Objects, and Grids panels into a single assistant when undocked outside the layout window?

5. Virtuoso Parameterized Layout Generators—VPLGen

VPLGen is a new feature in 616. This video describes setting up VPLGen and generating VPLGen Pcells.

6. Toolbar Manager

This video demonstrates the new features in IC6.1.6, which allow you to easily customize the content and display of existing toolbars and to create your own toolbars.

Product Information

7. Product Documentation for IC6.1.6

As you have probably gathered from the precediing items, the initial release of Virtuoso IC6.1.6 occurred in May. A good place to start learning about new features would be the Virtuoso Platform What's New document. 

8. Cadence Online Support Release Highlights

You may have also noticed several changes to the http://support.cadence.com website interface. The Highlights document summarizes what's new. 

Solutions

9. How to create a single inverter symbol to represent multiple models and pass LVS

A worked example (with database) showing you how to create a parameterized inverter symbol that can represent, for example, multiple voltages, via an inherited model name.

10. Path to access MMSIM products has changed to [installation_directory]/bin in 12.1.1

The path to access the MMSIM products has been changed from <installation>/tools/bin/ or <installation>/tools.<platform>/bin/ directory to the <installation>/bin/ directory.

SKILL for the Skilled: The Partial Predicate Problem

$
0
0
The partial predicate problem describes the type of problem encountered when a function needs to usually return a computed value, but also may need to return a special value indicating that the computation failed. Specifically, the problem arises if the caller cannot distinguish this special value from a successfully calculated value. In this posting of SKILL for the Skilled, we look at several ways to attach this problem in SKILL.

Approach 1: Returning nil to indicate failure

A very common way a SKILL function indicates to its caller that it failed to do what was requested is to return nil. For example, the SKILL function nthelem returns the Nth element of a given list, given an integer N. If the list has less than N elements, it returns nil. For example (nthelem 2 '(10 20 30)) returns 20, but (nthelem 4 '(10 20 30)) returns nil.

A limitation of this approach is that (nthelem 2 '(t nil t)) also returns nil, because nil is the second element. The caller can only trust nil to be the failure case if he knows that nil is not an element of the list.

Here is an implementation of a find function which returns the first element of a given list which matches a given predicate. Note that this example (and most of the examples in this article) only work in Scheme/Skill++ mode.

(defun find_A (predicate data)
  (car (exists x data
         (predicate x))))

Here are some examples of how it works.

(find_A oddp '(2 4 5 6 7 9))==> 5

(find_A stringp '(this is 1 "list" of stuff))==> "list"

(find_A numberp '(this is a list of symbols))==> nil

(find_A listp '(t t t nil t t))==> nil

Notice that the find_A function returns nil in two cases:

  • if there is no element in the given list which matches the predicate
  • if nil is explicitly in the given list and matches the predicate
Thus if find_A returns nil you don't know whether it found something or not.

Approach 2: Returning a given default value on failure

The following implementation of find_B attempts to settle the ambiguity by allowing the caller to specify the return value on the so-called failure case.
(defun find_B (predicate data @key default)
  (let ((tail (exists x data
                (predicate x))))
    (if tail
        (car tail)
        default)))

(find_B stringp '(this is 1 "list" of stuff) ?default 'notfound)==> "list"

(find_B listp '(t t t nil t t) ?default 'notfound)==> nil

(find_B numberp '(this is a list of symbols) ?default 'notfound)==> notfound

A disadvantage of this case is that the caller might find it clumsy at the call-site to provide a value which the given function call would otherwise never return.

Approach 3: Wrapping the return value on success

Another common way is to return a wrapped value. I.e., don't return the value found/computed, but rather return a list whose first element is that computed value. The SKILL member function does just this. (member 5 '(1 2 3 4)) returns nil because the given list does not contain 5; whereas (member 3 '(1 2 3 4) returns a list (3 4). Thus the only time member returns nil is when it didn't find the value being sought.

Another function which uses this approach is errset, which returns nil if the given form to evaluated triggered an error. Otherwise, errset returns a singleton list whose first (and only) element is the value calculated. Thus (errset 6/4) returns (3), while (errset 6/0) returns nil.

An obvious advantage of this wrapping approach is that the failure condition can always be distinguished from the success case. A disadvantage is that the caller who wants to use the calculated value must unwrap the value with an additional call to car, probably after testing whether the value is nil.

Here is an implementation of find_C which wraps its return value. It only returns nil if no element of the list matches the predicate. But the caller must call car to unwrap the value.

(defun find_C (predicate data)
  (let ((tail (exists x data
                (predicate x))))
    (when tail
      (ncons (car tail)))))

(find_C stringp '(this is 1 "list" of stuff) ?default 'notfound)==> ("list")

(find_C listp '(t t t nil t t) ?default 'notfound)==> (nil)

(find_C numberp '(this is a list of symbols) ?default 'notfound)==> nil

Another disadvantage of this approach is that find_C always allocates memory if it successfully finds what its looking for.

Approach 4: Continuation passing

Still another way to solve this problem in SKILL++ is by passing a continuation. This involves organizing your code a bit differently, but in the end allows a lot of flexibility. The idea is to pass an extra argument which is itself a function to call with the computed value if successful.
(defun find_D (predicate data @key (if_found (lambda (x) x)))
  (let ((tail (exists x data
                (predicate x))))
    (when tail
      (if_found (car tail)))))
The find_D function searches the given list for an element matching the condition. If successful, calls the given function, if_found and returns the value it returns. Otherwise it omits calling the if_found and simply returns nil.

Continuation passing is a generalization

As you can see from the examples below, the function find_D is actually a generalization of find_A, find_B, and find_C.

These examples work like find_A.

(find_D stringp '(this is 1 "list" of stuff))==> "list"

(find_D listp '(t t t nil t t))==> nil

(find_D numberp '(this is a list of symbols))==> nil
These examples work like find_B.
(find_D stringp '(this is 1 "list" of stuff) ?if_found (lambda (x) 'notfound))==> "list"

(find_D listp '(t t t nil t t) ?if_found (lambda (x) 'notfound))==> notfound

(find_D numberp '(this is a list of symbols) ?if_found (lambda (x) 'notfound))==> notfound
These examples work like find_C.
(find_D stringp '(this is 1 "list" of stuff) ?if_found ncons)==> ("list")

(find_D listp '(t t t nil t t) ?if_found ncons)==> (nil)

(find_D numberp '(this is a list of symbols) ?if_found ncons)==> nil

An initial reaction of this type of coding might be that it looks more complicated. But in fact, it is often less complicated when you actually try to use it. Why is this? It is because the code at the call-site usually needs to (1) do something with the calculated value. In addition, there must be program logic, to (2) test whether the value corresponds to the success case or the failure case.

The way find_D is intended to be used, the code for case (1) goes inside the function being passed as the ?if_found argument, and the code for case (2) is already inside the find_D implementation. This is shown in the following examples.

Example using continuation passing

Assume we have a function, is_metal_shape?, which figures out whether a given shape is on a metal layer, presumably by looking at the layer name of the shape and looking in the tech file to see whether that layer has "metal" function. Here is an example of how to use find_B and find_D to add such a shape to a particular db-group.

 

(let ((shape (find_B is_metal_shape? cv~>shapes
                 ?default 'notfound))
   (unless (shape == 'notfound)
     (dbAddObjectToGroup dbGroup shape)))

Notice that the call to find_D is actually simpler.

 

(find_D is_metal_shape? cv~>shapes
    ?if_found (lambda (shape)
                (dbAddObjectToGroup dbGroup shape)))

This approach has certain advantages over all the alternatives shown above. The most obvious advantage is that there is no ambiguity at the call-site. The caller does not have to tend with the failure condition. In fact it is the function find_D itself which knows whether the sought element was found and deals with it appropriately.

Handling the found and not-found cases separately

One might also write a version of function find_D with an additional if_not_found keyword argument to handle the other case that the call-site wants to do something different if such an element is not found--for example to trigger an error.
(defun find_E (predicate data @key 
                                (if_found (lambda (x) x))
                                (if_not_found (lambda (_x) nil)))
  (let ((tail (exists x data
                (predicate x))))
    (if tail
        (if_found (car tail))
        (if_not_found))))

Summary

In the above paragraphs, we saw several common ways of dealing with the so-called partial predicate problem in SKILL.
  • Return nil to indicate failure
  • Return a given default value to indicate failure
  • Wrap the return value
  • Pass a continuation to call on success.

In general continuation passing can indeed be very complicated, but there are certainly cases such as the example shown here, where the style is simple to use and eliminates complexity from your code with no added overhead.

See also

Jim Newton


Virtuosity: 20(!) Things I Learned in June by Browsing Cadence Online Support

$
0
0

Wow!  There was an amazing amount of new content added last month.  A lot of new videos and some Really Useful articles.  Enjoy.

Rapid Adoption Kits

1. CPF-AMS Low-Power Mixed-Signal Simulation

CPF-AMS is an extension of mixed-signal simulation to help the designer simulate mixed-signal low-power design with the CPF language. It includes all CPF-related technology applicable in the mixed-signal world, such as power shut off, multiple power supply, and dynamic voltage frequency scaling.  This RAK includes an application note and a database with detailed step-by-step instructions.

2. Abstract Generator

An abstract is a high-level representation of a layout view. Abstracts are used in place of full layouts to improve the performance of place-and-route tools, such as Cadence Encounter. The Abstract Generator tool can create Layout Exchange Format (LEF) file information from a Virtuoso database. Includes a database with step-by-step instructions.

Blog

3. SKILL for the Skilled

This is an excellent long-running (20+ to date) series of blog articles covering many different topics in SKILL and SKILL++ programming. They are not for beginners, but are clear and concise enough that someone with a moderate background in SKILL can benefit from the explanations and best practices described.

Solutions

4. How to Configure and Debug License Issues

One of those documents you never think you need—until you do. Tons of details about Cadence licensing, configuration, daemons, startup, performance, and options. Be sure to download the PDF doc attached to the solution, which includes a table of error code descriptions and two pages of links to other license-related content on COS.

5. Overview and search order of different Virtuoso, Library Manager, and Simulation Environment (SE) customization/startup files

This is definitely one I'll be tucking away for future reference. Information about all the various files Virtuoso uses for setup and customization and where it looks for them (.cdsinit, .cdsenv, display.drf, the .cadence directory, .simrc and more)—all in one concise document. Be sure to download the attached PDF doc, as it has quite a bit more information than is shown in the solution itself. Helpful when you find yourself wondering "Why is it doing that and how do I make it stop?" or "I thought I changed that setting. It must be getting it from a different file".

6. Is there a keyboard map for the bindkeys in VSE-L?

Why, yes there is. Bindkeys are great time-savers. If you can could only remember what they are...

7. Improvements in s-parameter (nport) simulation in MMSIM 12.1 and MMSIM 12.1.1

As described in the text of the solution: "The nport has been greatly improved in MMSIM 12.1 and even more so in MMSIM 12.1.1 for the harmonic balance engine and transient analysis." This solution describes the improvements and how to take best advantage of them.

Videos

I'm not going to describe each one, as their titles are fairly self-explanatory. Here are the links to each one.

8. Instance Editing Mode for Filp and Rotate (IC6.1.6)

9. 616 Wire/Bus Width/Space Editing (IC6.1.6)

10. Copy Mode in Quick Align (IC6.1.6)

11. Convert Instance to Mosaic (IC6.1.6)

12. Pad Opening Info (IC6.1.6)

13. Push Wires in Stretch and Create Wire Commands (IC6.1.6)

14. Smart Snapping in Create Rectangle (IC6.1.6)

15. Show Selection Info Toolbar (IC6.1.6)

16. Placing and Routing a Customer Digital Cell Using Virtuoso

17. VSR Interactive Differential Pair Routing

18. VSR Interactive Guided Bus Routing

19. VSR Interactive Symmetry Routing

20. VSR Interactive Shielded Routing

Stacy Whiteman

Virtuosity: 16 Things I Learned in July by Browsing Cadence Online Support

$
0
0

Feeling a bit lazy this month, but even without digging too deeply, I could find 16 new and interesting bits of content...

Application Notes

1. Adding and Managing CDF Parameters for Fluid Guard Rings (ICADV12.1)

Shows you how to add and update the Component Description Format (CDF) parameters and attributes that affect the geometry of a fluid guard ring (FGR) instance. 

2. Customizing Create Guard Ring Form (ICADV12.1 ISR3)

How to use triggers and SKILL APIs to customize the Create Guard Ring form, change the properties of the pre-defined fields or components and create new components to suit your design requirements.

3. Virtuoso Schematic Editor Shortcuts for Improved Productivity

Guaranteed that you will learn some new tricks by reading through this document!  Includes lots of tips on using wire labels, arrays of instances, the new Probes Assistant, customizing the UI and using bindkeys

Rapid Adoption Kits (RAKs)

4. Schematic Model Generator

Schematic Model Generator (SMG) is an easy-to-use GUI-based tool which allows users to create a reusable building block schematic, which is processes to create a textual behavioral model for a circuit.  The RAK includes an overview presentation, documented tutorial with database and video demonstrations.

5. amsDmv - AMS Design and Model Validation

AmsDmv is a tool to validate similarity in measured results, simulated behavior and interfaces of a given reference (design) and compared (model) blocks, targeted primarily for analog/mixed signal models.  The RAK includes a documented tutorial with database and video demonstration.

You can learn more about both of the above tools--amsDmv and Schematic Model Generator--in the archived webinar: Analog Behavioral Modeling and Model Generation

6. Analog-on-Top Mixed Signal Implementation: Virtuoso Black Box flow with IC61

Introduces the Virtuoso Black Box Floorplan using Virtuoso Floorplanner (VFP).  Includes documented tutorial and database.

7. Mixed Signal Simulation with Real Number Modeling

Learn the basics of invoking mixed signal verification and using real number modeling in both the Virtuoso GUI-based (AVUM) and Text-based command line (AIUM) flows.  Includes overview presentation, documented tutorial with database and video demonstrations.

8. Mixed Signal Verification - Connectivity

Learn how connectivity is handled at the interface of logical, electrical and RNM (real number modeling) nets, what coercion is and how it benefits mixed signal verification, and what an IE (interface element) card is and how it can be used to customize mixed signal connectivity.  Includes overview document and documented tutorial with database.

9. Static and Dynamic Checks

Describes the usage of the Spectre APS/XPS static and dynamic design checks available in MMSIM 12.1 ISR12.  These checks may be used to identify typical design problems including high impedance nodes, DC leakage paths, extreme rise and fall times, excessive device currents, setup and hold timing errors, voltage domain issues or connectivity problems.  RAK includes documented tutorial with database.

Videos

10. Mark Net - IC6.1.6 Enhancement

11. Rectangle Wire Reshape in IC 6.1.6

12. New features in SimVision 13.1 Release

13. VCDL (Virtuoso Connnectivity-Driven Layout)-Device Correspondence to Make Layout Connectivity Driven

14. Defining Common Centroid Constraint

15. Autorouting the Design using Virtuoso Shape-based Router

16. Use VSR (Virtuoso Shape-based Router) Features for Analog Routing

Stacy Whiteman

 

SKILL for the Skilled: How to Copy a Hash Table

$
0
0
In this posting I want to look at ways to copy a hash table in SKILL. There are several ways you might naively try to do this, but some of these naive approaches have gotchas which you should be aware of.

In the following paragraphs several inferior functions will be presented: portable_1, copyTable_2, copyTable_3, copyTable_4, and copyTable_5. Finally three useful robust functions will be presented (copyTable, getHashDefault, and getHashPrintName)which you, the reader, can copy (attach a customer specific prefix to) and use in your own programs.

Copy by iteration

The first way you might try to implement the function copyTable is as follows.
(defun copyTable_1 (hash)
  (let ((new (makeTable 'copy)))
    (foreach key hash
      new[key] = hash[key])
    new))

The function copyTable_1 works perfectly for most applications for which you might want to use it. This approach, unfortunately, has several limiting corner cases which might be important.

Troublesome hash keys

In some sense this example reminds me of little Bobby Tables from xkcd.


The problem is that if you are not in control of the data, then you don't know what might be in there.

If the function copyTable_1 is defined as shown above, it will fail if either of the following keys are found in the hash table: ?, ??, or unbound. While it is indeed unlikely that most people would ever encounter this case, it is nevertheless something to consider if you want your implementation of copyTable which is robust and general purpose.

Try the following test case and see what happens.

(let ((table (makeTable 'x)))
  table[?] = 1
  table[??] = 2
  table['unbound] = 3 
  (copyTable_1 table))*Error* eval: unbound variable - key<<< Stack Trace >>>
(new[key] = hash[key])
foreach(key hash (new[key] = hash[key]))
let(((new makeTable(&))) foreach(key hash (new[key] = hash[key])) new)
copyTable_1(table)
let(((table makeTable(&))) (table[?] = 1) (table[??] = 2) (table['unbound] = 
3) copyTable_1(table))

Using the unbound is easier in SKILL++

The resulting stack-trace is admittedly confusing.

The error is triggered because foreach encounters the symbol unbound in the hash table. On doing so, it effectively sets the variable key to unbound, as if by (key = 'unbound). Thereafter new[key] contains a reference to an unbound variable. In SKILL, an error is triggered if an attempt is made to evaluate a variable whose value is the symbol: unbound.

To demonstrate this to yourself, try the following in SKILL.

(inSkill
  (let ((key 'unbound))
    (printf "the value is [%L]\n" key)))
*Error* eval: unbound variable - key<<< Stack Trace >>>
printf("the value is <%L>\n" key)
let(((key 'unbound)) printf("the value is [%L]\n" key))
inSkill(let(((key &)) printf("the value is [%L]\n" key)))

This problem is easily avoided by using SKILL++. In SKILL++ the unbound symbol is not special: it is a supported feature to set a variable to the symbol unbound, and to evaluate the variable thereafter. Try the same example as above, but using in inScheme rather than inSkill.

(inScheme
  (let ((key 'unbound))
    (printf "the value is [%L]\n" key)))
the value is [unbound]

Redefine the function in SKILL++

We can fix the problem associated with the unbound symbol simply by defining the function either in a .ils file or by surrounding the definition with (inScheme ...).
(inScheme
  (defun copyTable_2 (hash)
    (let ((new (makeTable 'copy)))
      (foreach key hash
        new[key] = hash[key])
      new)))
Now if we run the same test again we see a different result.
(let ((table (makeTable 'x)))
  table[?] = 1
  table[??] = 2
  table['unbound] = 3 
  (copyTable_2 table))
table:copy

More troublesome hash keys: ? or ??

If we test a bit further we find that it has failed for another subtle reason. We can use the tableToList function to examine all the key/value pairs in a hash table. Notice that the content of the original list and the content of the copy are not the same.
(let ((table (makeTable 'x)))
  table[?] = 1
  table[??] = 2
  table['unbound] = 3

  (printf "contents of original:\n--> %L\n"
          (tableToList table))
  (printf "contents of copy:\n--> %L\n"
          (tableToList (copyTable_2 table))))
contents of original:
--> ((unbound 3) (? 1) (?? 2))
contents of copy:
--> ((unbound 3) (? (unbound ? ??)) (?? (unbound 3 ? 1 ?? 2)))

This is not something you normally need to worry about, because if you created the hash table in your SKILL application, then you probably know that neither ? nor ?? is a hash key in your table. However, if the goal is to write a general purpose function for copying any given hash table, then this is an exotic case you must consider.

Why the crazy behavior?

It is not so difficult to understand this crazy behavior. It is because ? and ?? have special meanings to the functions arrayref, get, getq and a few more functions. The following expressions do not retrieve the value of the hash key ?. Rather they all return the list of hash keys.
hash->?
hash[?]
(arrayref hash ?)
(get hash ?)
(getq hash ?)
Similarly hash->?? does not retrieve the value associated with the key ??, it instead returns a special list which embeds they hash keys and values.

This means the line new[key] = hash[key] within the previous versions of copyTable does something special with the value of the variable key is ? (or ??). It does the following: new[?] = hash[?], which assigns the ? key of the new hash table to the list of keys in the old hash table.

Use append with hash tables

To fix this problem, take advantage of the feature of the append SKILL function. It can append two hash tables.
(defun copyTable_3 (hash)
  (let ((new (makeTable 'copy)))
    (append new hash)
    new))
But since, append returns its first argument if the first argument is a hash table, copyTable_3 can be made much simpler.
(defun copyTable_4 (hash)
  (append (makeTable 'copy) 
          hash))
The append function does not have a problem when it encounters the symbols ?, ??, or unbound. Take a look at the same example using copyTable_4.
(let ((table (makeTable 'x)))
  table[?] = 1
  table[??] = 2
  table['unbound] = 3

  (printf "contents of original:\n-->%L\n"
          (tableToList table))
  (printf "contents of copy:\n-->%L\n"
          (tableToList (copyTable_4 table))))
contents of original:
-->((unbound 3) (? 1) (?? 2))
contents of copy:
-->((unbound 3) (? 1) (?? 2))

Even if you are not worried about the special, extremely unlikely hash keys discussed above, using append to copy hash tables makes for smaller and faster code.

Preserving the table name

Depending on how good a copy you need, you might also want the new table to print the same as the original table. Look at this example. One prints as table:data and the other as table:copy.
(let ((table (makeTable 'data)))
  (printf "original: %L\n" table)
  (printf "copy:     %L\n" (copyTable_4 table)))
original: table:data
copy:     table:copy

To fix this problem we need to pass the correct value to makeTable within the copyTable function as follows.

(defun copyTable_5 (hash)
  (append (makeTable (getHashPrintName hash))
          hash))
Here is an implementation of the function getHashPrintName which returns the print name of the given hash table. If the table prints as table:myname, getHashPrintName returns the string "myname".
(defun getHashPrintName (hash)
  (substring (sprintf nil "%L" hash)
             7))
If we test copyTable_5 we see that the two tables do print the same way.
(let ((table (makeTable 'data)))
  (printf "original: %L\n" table)
  (printf "copy:     %L\n" (copyTable_5 table)))
original: table:data
copy:     table:data

Preserving the default value

There is one more important difference between the two hash tables (the one given to copyTable_5 and the one it returns). The difference is how the hash tables behave when accessing a missing key. Take a look at this example.
(letseq ((table (makeTable 'data 0))
         (copy (copyTable_5 table)))
  (printf "default from original: %L\n" table[42])
  (printf "default from copy:     %L\n" copy[42]))
default from original: 0
default from copy:     unbound
You see that if the original hash table was created with a second argument to makeTable, then the copy is liable to return a wrong default value. To fix this problem is a bit challenging but interesting.

We need to pass a second argument to the call to makeTable within copyTable. Moreover, we need to pass the correct value for this second argument, based on the given table. Unfortunately, there is no built-in function in SKILL which will access the default value of a hash table.

Calculating the default value of a hash table

The following function returns the default value of the hash table. It is pretty efficient, and works on the following algorithm.
  • Choose a token key; we use the symbol t in this case, but any key would work.
  • Save away the length of the hash table, by calling (length hash).
  • Save away the value of hash[t].
  • Call (remove hash t) to delete the t key from the table if it is there. If t is not a hash key, no harm done.
  • Evaluate hash[t] once t has been removed. That access will return the default value which we want getHashDefault to return.
  • Determine whether t was in the table at the start by comparing the hash table size before and after deleting the t key.
  • Before getHashDefault returns the default value, restore t to the hash table if and only if it was there originally.

To return a particular value from a SKILL function but first do some cleanup, use prog1.

To do the restoration we save the old value of hash[t] before calling remove, then check the length of the hash before and after calling remove. If the length of the hash changed, then twas a key originally, so restore the value to the saved value. Otherwise, there is no need to restore anything.

(defun getHashDefault (hash)
  (let ((old_size (length hash))
        (old_value hash[t]))
    (remove t hash)
    (prog1 hash[t]
      (unless ((length hash) == old_size)
        hash[t] = old_value))))

A working version of copyTable

All that remains to have a working version is to update copyTable to make use of getHashDefault.
(defun copyTable (hash)
  (append (makeTable (getHashPrintName hash)
                     (getHashDefault hash))
          hash))
We can test it to make sure it works.
(letseq ((table (makeTable 'data 0))
         (copy (copyTable table)))
  (printf "default from original: %L\n" table[42])
  (printf "default from copy:     %L\n" copy[42]))
default from original: 0
default from copy:     0

Summary

In this blog post we saw several things when trying to implement a robust version of copyTable.
  • How to find the default value of a hash table
  • How to find the print name of a hash table
  • How the symbol unbound behaves differently in SKILL vs SKILL++
  • How the symbols ? and ?? behave as hash keys.
  • Some details of the SKILL function makeTable, in particular the first and second arguments.
  • How append and remove work with hash tables.
  • How to use the function tableToList to serialize the contents of a hash table.

Jim Newton

SKILL for the Skilled: Visiting All Permutations

$
0
0
In this posting I want to look at several ways of generating permutations of a list. The problem comes up occasionally in fault analysis as well as a few other applications.

Don't generate the list

It is usually a bad idea to try to generate a list of all permutations as the length of that list can be very large for some lists. E.g., a list of the permutations of a list of length ten will be 10! = 3,628,800. What is generally a better idea is to iterate over the permutations one at a time without ever generating them all into a list. This is sometimes referred to as Visiting the permutations with a visitor function.

Even without accumulating a list of permutations, assuming 1 microsecond per iteration, iterating over the permutations of a list of 24 elements will take longer than the age of the universe. This is assuming the universe is 15 billion years old, which is between 23! and 24! microseconds old. Please someone correct me in the comments below if my off the cuff calculation is wrong.

In this blog we present ways to implement the function map_permutations which can be called as follows: (map_permutations VISITOR DATA), where VISITOR is a unary function which will be called on each permutation, and DATA is a list of items.

Brute force approach

The following implementation of map_permutations_BF is more or less a recursive function which implements N consecutive foreach loops, where N is the length of the given list. Each step, 0<=k<=N, of the recursion makes N-k calls to, the local function next. Thus, next reaches the k=N level N! times which it has each time accumulated one permutation.
(defun map_permutations_BF (visit data "ul")
  (let ((N (length data)))
    (labels ((next (so_far k)
               (cond
                 ((eqv k N)
                  (visit (mapcar car so_far)))
                 (t
                  k++
                  (foreach map items data
                    (unless (memq items so_far)
                      (next (cons items so_far) k)))))))
      (next nil 0))))

The example above is SKILL++ code so as always with SKILL++, you need to save it in a file with a .ils extension, or wrap it in (inScheme ...) if pasting into the CIW window.

Example: printing the permutations

Here is a few of examples of how to use map_permutations_BF. The first example prints each permutation of (1 2 3 4) to standard output.
(map_permutations_BF println'(1 2 3 4 5))
Here is the standard out of evaluating that expression:
(4 3 2 1)
(3 4 2 1)
(4 2 3 1)
(2 4 3 1)
(3 2 4 1)
(2 3 4 1)
(4 3 1 2)
(3 4 1 2)
(4 1 3 2)
(1 4 3 2)
(3 1 4 2)
(1 3 4 2)
(4 2 1 3)
(2 4 1 3)
(4 1 2 3)
(1 4 2 3)
(2 1 4 3)
(1 2 4 3)
(3 2 1 4)
(2 3 1 4)
(3 1 2 4)
(1 3 2 4)
(2 1 3 4)
(1 2 3 4)

Counting the permutations

The second example counts the number of permutations of (1 2 3 4 5 6 7 8 9):
(let ((c 0))
  (map_permutations_BF (lambda (_) c++)'(1 2 3 4 5 6 7 8 9))
  c)==> 362880

Collecting a list of permutations

The third example collects and returns the list of permutations of (1 2 3):
(let (data)
  (map_permutations_BF (lambda (p) (push p data))'(1 2 3))
  data)==> ((1 2 3) (2 1 3) (1 3 2) (3 1 2) (2 3 1) (3 2 1))

Trying different algorithms

A very good reference source for discussions and implementations of algorithms in general is Donald Knuth's bookArt of Computer Programming. In particular Volume 4 Fascicle 2 discusses how to generate permutations. The following are three different algorithms implemented from that fascicle.

PLEASE NOTE that I have not tried to improve the readability of the algorithms, but rather I've tried to keep the SKILL code as close as possible to Knuths pseudo-code from the book. I would advise against trying to improve the code. One great advantage of keeping the code as it is, is that you know it works. Another argument for keeping it as it is, and resist improving is that if someone later comes along and reads your code, they can read the referenced pages of Art of Computer Programming to discover the justifications for the implementation as well as discussion of limitations and performance.

Quick review of SKILL prog/go

As with most of the algorithms in Art of Computer Programming, these algorithms are highly optimized and depend on GOTO. The GOTO operator in SKILL is called go and is only available inside (prog ...) within traditional SKILL. Unfortunately, prog/go is NOT available in SKILL++. This means unless you want to convert all the GOTOs in Knuth's code to some other style, you'll have to use .il as your file name suffix when implementing these functions, or of course you may wrap the code in (inSkill ...).

A moral of this story is that sometimes SKILL++ is good for solving a problem, and sometimes traditional SKILL is better. In the SKILL language you have both together. You can use the dialect which works best for your problem space, or you can mix and match as necessary as your application demands.

The SKILL prog allows its body to call return with an argument which causes the prog for to exit and to return the argument which was given to return. For example:

(prog ()
  (return 42)
  (println 'hello))==> 42
In the previous example, the (prog ...) form returns 42, and the line (println 'hello) is never reached. If while executing the body of the (prog ...) form, no (return ...) is encountered, the (prog ...) form returns nil.

Another Knuth-friendly feature of SKILL prog but not the SKILL++ prog is that it allows top-level labels which are valid unquoted operands for the go operator. In the following example, L0, L1, L2, and L3 are labels. Some of these are used by (go ..); in particular (go L1), (go L2), and (go L0).

(prog (n)
  (go L1)

  L0
  (printf "finished\n")
  (return 'done)

  L1
  n = 0

  L2
  (printf "n=")
  (println n)

  L3
  (if (n < 12)
      then n++
           (go L2)
      else (go L0))

  (printf 'never_reached))
You'll find this prog/go construct useful when transcribing Knuth's algorithms.

Plain changes

The following implements algorithm P (Plain changes) from Donald E. Knuth, The Art of Computer Programming Volume 4 Fascicle 2, Generating All Tuples and Permutations, page 42.
(defun map_permutations_P (visit data "ul")
  (letseq ((n (length data))
           (a (listToVector (cons nil data)))
           (c (makeVector (add1 n)))
           (o (makeVector (add1 n)))
           j
           s
           q)
   (prog ()
     P1 ; Initialize
     (for j 1 n
       c[j] = 0
       o[j] = 1)
     P2 ; Visit
     (funcall visit (cdr (vectorToList a)))
     P3 ; Prepare for change.
     j = n
     s = 0
     P4 ; Ready to change?
     q = c[j] + o[j]
     (cond
       ((q < 0)
        (go P7))
       ((q == j)
        (go P6)))
     P5 ; Change
     (let ((u j-c[j]+s)
           (v j-q+s));; swap a[v] with a[u]
       a[v] = (prog1 a[u]
                     a[u] = a[v]))
     c[j] = q
     (go P2)

     P6 ; Increase s
     (if (j == 1)
         (return)
         (s++))
     P7 ; Switch direction
     o[j] = -o[j]
     j--
     (go P4))))

Converting Knuth algorithms to SKILL

List vs Array In SKILL you normally work with lists, and consequently you might want to visit the permutations of a list. The Knuth algorithms in this article visits the permutation of a fixed size array. Because of this, these implementations of map_permutations expect lists as input and output, while internally use arrays in-keeping with the Knuth algorithm. Converting between list and array is done using listToVector and vectorToList.

 

Zero indexed vs. one indexed SKILL only provided zero-based arrays. Some of the Knuth algorithms use one-based and some used zero-based arrays. Where appropriate, filler elements are used for the 0th element when the Knuth array is one-based. You'll see expressions such as (funcall visit (cdr (vectorToList a))) in the SKILL implementations, which means the corresponding array is one-based in the Knuth algorithm, so we have to discard the 0th element when passing the data to the visit function. The following table summarizes how to use makeVector, vectorToList, and listToVector with zero-indexed and one-indexed arrays.

 


allocatearray to listlist to array
N element
zero-indexed
(makeVector N)(vectorToList myarr)(listToVector mylist)
N element
one-indexed
(makeVector N+1)(cdr (vectorToList myarr))(listToVector (cons nil mylist))

 

Use of prog/go/return As described above the use of labels and GOTO in the Knuth algorithms is easily implemented using the SKILL (prog ...) construct.

 

Use of do/until Some Knuth algorithms use do/until, which is available in SKILL++ but not in SKILL. We solve this problem by using the following do_until macro, which takes a test expression as first argument, and a body of expressions. The effect is that the body of expressions is evaluated once, and thereafter it is evaluated again and again as long as re-evaluating the tests returns nil.
(defmacro do_until (test @rest body)
  `(progn ,@body
          (while (not ,test)
            ,@body)))

 

Swapping elements Swapping two elements, A and B, can be done as followings, but when this is done an appropriate comment is added.
A = (prog1 B
           B = A)

Ehrlich swaps

The following implements algorithm E (Ehrlich swaps) from Donald E. Knuth, The Art of Computer Programming Volume 4 Fascicle 2, Generating All Tuples and Permutations, page 57. According to Gideon Ehrlich, the original author of this algorithm, the most amazing thing about it is that it works.
(defun map_permutations_E (visit data "ul")
  (letseq ((a (listToVector data))
           (n (length data))
           (b (makeVector n))
           (c (makeVector n+1))
           k
           j)
   (prog ()
     E1 ; Initialize
     (for j 0 n-1
       b[j] = j
       c[j+1] = 0)
     E2 ; Visit
     (funcall visit (vectorToList a))
     E3 ; Find k
     k = 1
     (when (c[k] == k)
       (do_until (c[k] < k)
         c[k] = 0
         k++))
     (if (k == n)
         (return)
         c[k] = c[k] + 1)
     E4 ; Swap
     a[0] = (prog1 a[b[k]]
                   a[b[k]] = a[0])
     E5 ; Flip
     j = 1
     k--
     (while (j < k);; swap b[j] and b[k]
       b[j] = (prog1 b[k]
                     b[k] = b[j])
       j++
       k--)
     (go E2))))

Permutation generation by cyclic shifts

The following implements algorithm C (Permutation generation by cyclic shifts) from Donald E. Knuth, The Art of Computer Programming Volume 4 Fascicle 2, Generating All Tuples and Permutations, page 56.
(defun map_permutations_C (visit data "ul")
  (letseq ((x (listToVector (cons nil data)))
           (n (length data))
           a
           k
           j)
    (prog ()
     C1 ; Initialize
     a = (listToVector (cons nil data))
     C2 ; Visit
     (funcall visit (cdr (vectorToList a)))
     k = n

     C3 ; Shift;; (a1 a2 a3 ..ak) <-- (a2 a3 ...ak a1)
     a[k] = (prog1 a[1]
                   (for j 1 k-1 
                        a[j] = a[j+1]))
     (when (a[k] != x[k])
       (go C2))
     C4 ; Decrease k.
     k--
     (when (k > 1)
       (go C3)))))

Summary

In this article we looked at several things.
  • Four functions for visiting all permutations of a given list. Feel free to copy and paste these implementations into your SKILL programs, but please remember to give credit to Donald Knuth where appropriate.
    • map_permutations_BF -- Brute force
    • map_permutations_C -- Cyclic shifts
    • map_permutations_P -- Plain changes
    • map_permutations_E -- Ehrlich swaps
  • Translating algorithms from Donald Knuth's Art of Computer Programming to SKILL
  • Seldom used features of SKILL (prog ...) such as (go ...)
  • Examples using makeVector, listToVector, and vectorToList.

See Also

Virtuosity: 15 Things I Learned in August by Browsing Cadence Online Support

$
0
0

Our folks over in Physical Design have been busy churning out helpful Rapid Adoption Kits to demystify lots of useful features in the Virtuoso Layout Suite.  It's a great opportunity to learn some new productivity-boosting tricks.

Application Notes

1. Virtuoso Spectre Transient Noise Analysis

This document discusses the theoretical background of Spectre's transient noise analysis, its implementation and implications, and the latest use model together with many useful time-saving and circuit diagnostic features.  It also provides several detailed tutorial examples.

Rapid Adoption Kits

2. IC 6.1.6 Rapid Analog Prototyping (RAP) Workshop

This is a front-to-back flow that uses the Virtuoso Constraint System to generate the layout of an analog circuit in an automated manner, in order to obtain early feedback on parasitics and device effects on circuit simulation.  Includes an overview presentation, documented tutorial and database.

3. Virtuoso Parameterized Layout Generators (VPLGen)

VPLGen is a new feature that lets you easily generate and reuse layouts for schematic instances which differ by inherited parameter values so that the layouts can be used repeatedly throughout the hierarchy and reused across design libraries. VPLGen is a Cadence-supported hierarchical pcell that does not require coding.  This RAK includes a documented tutorial and database, as well as a video demonstration.

4. VXL Back Annotation

This workshop walks you through an example of how to create a dummy using VXL and then how to back-annotate it into a schematic.  It includes a documented tutorial and database.

5. Update Binding

This workshop is an example of how to take a layout that was created in a non-connectivity environment and make it VXL compliant. It walks through how using the new Update Binding command to simplify the task. Includes a documented tutorial and database.

6. Introduction to Connectivity-Driven Design in VLS XL

The move from Layout L to Layout XL is becoming increasingly important.  The Layout XL environment contains functionality for the implementation of advanced-node designs, such as wire editing, connectivity- and constraint-driven layouts, mosaics, modgens, and electrically aware design. This Rapid Adoption Kit (RAK) will introduce how productivity can be increased by moving from Virtuoso Layout Suite L to Virtuoso Layout Suite XL.  It includes a documented tutorial and database.

7. QuickView Introduction

QuickView is the Cadence stand-alone data viewing tool. It allows you to draw, examine, compare and validate integrated circuit (IC) data in various layout and pattern data formats. Includes documented tutorial and database.

8. IC6.1.6 Pin to Trunk Device-Level Routing

This material steps through the Pin to Trunk Device-Level Routing Flow in Virtuoso in IC 6.1.6. The objective of this flow is to increase layout productivity through improved routing functionality aimed at device-level interconnects. This flow enables users to quickly connect device pins in a structured topology. Includes documented tutorial and database.

9. IC6.1.6 Pin to Trunk Block-Level Routing

This material steps through the Pin to Trunk Block-Level Routing Flow in Virtuoso in IC 6.1.6. The objective of this flow is to increase layout productivity through improved routing functionality aimed at block-level interconnects. This flow enables users to quickly connect block pins in a structured topology. Includes documented tutorial and database.

10. Stamping Conflict Debugging in LVSDE

Resolving stamping conflicts has remained an area of challenge for the designers. The Stamping Conflict Debugger is aimed at easing this process. This RAK is a workshop on the Stamping Conflict Debugger to help new users learn and use this feature more effectively.  Includes documented tutorial and database.

11. PVS Interactive Short Locator

This is a workshop on the PVS Interactive Short Locator Application. You will learn how to find shorts using the Interactive Short Locator, confirm the cause of the short without modifying the layout, add additional labels to help narrow down the shorted paths, resolve multiple labels shorts, and resolve shorts between unlabeled nets. Includes documented tutorial and database.

Solutions

12. How to get the list of output signals and expressions set in ADE L and ADE XL session using SKILL?

Skill code for both ADE L and ADE XL to obtain the saved output signals and expressions.

13. How to create a device check expression for model or primitive

The device check UI can be confusing.  This solution gives step-by-step instructions on how to set up a device checking expression for all devices which use a specific model or subcircuit.

14. How to read/load design variable from a text file to ADE window?

Skill code to read design variable name/value pairs and load the setup into ADE.

15. SKILL Example of OSM Object Sensitive Menu

Object or context-sensitive menus can be very handy.  Here's an example of how to define your own.

 

SKILL for the Skilled: How to Shuffle a List

$
0
0
The previous post of SKILL for the Skilled presented some ways to systematically visit all permutations of a list. As noted, the time to iterate through all permutations of a large list is prohibitive. If the goal is to find a permutation that meets some criteria then it may work perfectly well to simply test the criteria on randomly chosen permutations of the list and continue doing so until some time-out is reached. Choosing a permutation at random is also called shuffling.

In this article, I want to look at some ways to shuffle a list. In doing so, we'll also see how random numbers work in SKILL and a few examples of how to use them.

Very easy but not very random

An advantage of the implementation of shuffle_a below is that it very easy to remember and easy to implement. An important disadvantage is that it does a poor job of shuffling the given list. It basically asks the SKILL built-in sort to do the work and provides a compare function for sort to use, which provides some randomness. The SKILL expression (random 2) returns either 0 or 1 selected at random. Consequently the expression (onep (random 2)) returns either t or nil chosen at random.
(defun shuffle_a (data)
  (sort (copy data)
        (lambda (_a _b)
          (onep (random 2)))))
For example, (shuffle_a '(1 2)) returns either (1 2) or (2 1), each with 50% likelihood. However (shuffle_a '(1 2 3)) returns one of the following two permutations half of the time (3 1 2), (3 2 1), and one of the other four permutations the other half of the time. Thus, not all permutations are equally likely.

Similarly, there are 24 permutations of (1 2 3 4), but (shuffle_a '(1 2 3 4)) returns one of the following eight permutations half of the time: (1 2 3 4), (1 2 4 3), (2 1 3 4), (2 1 4 3), (3 4 1 2), (3 4 2 1), (4 3 1 2), or (4 3 2 1). And it returns one of the other 16 permutations the other half of the time.

If you need a quick and dirty shuffle algorithm, shuffle_a is decent and easy to remember. But if you need a good shuffle algorithm, use a different one.

Shuffle by partitioning

The following recursive algorithm can be used to shuffle a list of elements. It works similar to a quick sort, but rather than collecting the large and small elements into two respective lists recursively, it randomly partitions some elements into one sub-list and others into another sub-list, then proceeds to apply the same algorithm recursively onto those two sub-lists.
(defun shuffle_b (data)
  (cond
    ((cddr data) ; more than 2 elements?
     (let (left right)
       (foreach item data
         (if (zerop (random 2))
             (push item left)
             (push item right)))
       (nconc (shuffle_b left)
              (shuffle_b right))))
    ((zerop (random 2))
     (reverse data))
    (t
     (copy data))))

Shuffle by random selection

The following algorithm shuffle_c, works by continuing to randomly choose element at random from the list, push that element onto a new list, and remove it from the old list, continuing until a single element remains, then finally pushing it onto the list.

The expression r = (random n) returns a number 0 <= r < n

(defun shuffle_c (data)
  (let ((n (length data))
        r
        p
        shuffled)
    (while (cdr data);; choose a random element: data[r]
      r = (random n) ; randomly choose 0 <= r < n
      p = (nthcdr r data);; add data[r] to shuffled
      (push (car p)
            shuffled);; remove data[r] from data
      data = (nconc (ldiff data p)
                    (cdr p));; decrement n
      n--)
    (when data
      (push (car data) shuffled))
    shuffled))

How do you remove an element from a list?

There is a subtle trick in the code for shuffle_c: how to remove an element from a list. The built-in SKILL function remove won't exactly do the trick. Actually, remove works fine if the elements of the list are unique, but not if the list has duplicate elements. The SKILL remove function actually removes all elements from the given list which are equal to the given element. For example (remove 42 '(0 42 1 42 2 42 3)) will return (1 2 3), whereas we want a function, remove_once which works as: (remove_once 42 '(0 42 1 42 2 42 3)) ==> (0 1 42 2 42 3).

You might implement remove_once as follows:

(defun remove_once (data item)
  (let ((p (member item data)))
    (nconc (ldiff data p)
           (cdr p))))
What the shuffle_c function does is chooses an element of the list by position, and then removes the element in that position. For example, given the list mylist=(42 1 42 2 42 3), if we choose the element in position 2 (0-based index), which is the second 42 in the list, we can divide the list in two halves, right and left. The right portion is obtained by calling (nthcdr 2 mylist) ==> (42 2 42 3). The left portion is obtained by left=(ldiff mylist right)==>(42 1). Given right and left, we can produce a new list equal to the original list but with the element at position 2 removed by appending the two lists left and (cdr right).

How do you select an element at random?

Given a list of elements, the following function will select an element at random from it.
(defun random_element (data)
  (when data
    (nth (random (length data)) data)))
Be careful with this function: if given an empty list, (random 0) triggers an error.

Shorter version of shuffle

The following implementation of shuffle_d is more or less the same algorithm as shuffle_c but uses the helper functions random_element and remove_once.
(defun shuffle_d (data)
  (let (shuffled
        e)
    (while data
      e = (random_element data)
      (push e shuffled)
      data = (remove_once data e))
    shuffled))

Challenge to the reader

If you know another clever way to shuffle a list, please post the code to the comment section of this article below.

Summary

In this article four versions of the shuffle function were presented.
  • shuffle_a: a very short implementation of an inferior sorting algorithm
  • shuffle_b: a recursive divide and conquer shuffle algorithm
  • shuffle_c: an imperative, iterative algorithm for shuffling
  • shuffle_d: similar to shuffle_c but easier because it makes use of helper functions

In addition we looked at two other functions:

  • remove_once: an efficient algorithm for removing a maximum of one element from a list
  • random_element: return a randomly selected element from a list

Virtuosity: 16 Things I Learned in September by Browsing Cadence Online Support

$
0
0

 

Rapid Adoption Kits

By now, I think you know what RAKs are, and that they include a detailed instructional document and database.  Use the title link above to access the main landing page and browse all the available material.

1. DRD-based Interactive Compactor

The DRD-based interactive compactor can help you change the spacing between existing objects in your layout either through compaction or spreading. It can also fix spacing violations between shapes in your layout.

2. FastXOR

PVS XOR is a widely and highly needed application by designers to compare their layout databases.

3. IC 6.1.6 Crosstalk

This kit outlines a strategy for crosstalk prevention during both interactive and automated routing, using Virtuoso constraints.

4. PVS Interactive Short Locator

This is a workshop on the PVS Interactive Short Locator Application. This is designed to help new users learn and use the LVS shorts debugging feature more effectively.

5. Stamping Conflict Debugging in LVSDE

Resolving Stamping Conflicts has remained an area of challenge for the designers and the Stamping Conflict Debugger has been aimed at easing this process. This RAK is a workshop on the Stamping Conflict Debugger to help new users learn and use this feature more effectively.

6. IC 6.1.6 Pin to Trunk Block-Level Routing

This material steps through the Pin to Trunk Block-Level Routing Flow in Virtuoso in IC 6.1.6. The objective of this flow is to increase layout productivity through improved routing functionality aimed at block-level interconnects. This flow enables users to quickly connect block pins in a structured topology.

7. IC 6.1.6 Pin to Trunk Device-Level Routing

This material steps through the Pin to Trunk Device-Level Routing Flow in Virtuoso in IC 6.1.6. The objective of this flow is to increase layout productivity through improved routing functionality aimed at device-level interconnect. This flow enables users to quickly connect device pins in a structured topology.

Videos

8. Using amsDmv To Complement ADE XL Spec Comparision Form

This video highlights several very useful, but under-utilized aspects of the Custom IC tool suite--using ADE XL Spec Comparison to compare measured results, using amsDMV to compare waveforms and using amsDmv as a simulation cockpit to analyze small design changes.

9. Physical Verification System: Debugging DRC Results

This video is intended for new PVS users to help them quickly understand how to analyze and debug the PVS DRC run results using the DRC Debug Environment.

Application Notes

10. PVS Bindings by Name during PVS LVS Run

This document talks about the default PVS behavior around binding cells and pins across layout and schematic as well as discusses various commands to control the binding behavior, mostly using a question and answer style.

11. Circuit Prospector in Virtuoso (IC 6.1.5/6.1.6): An Overview

The Circuit Prospector is another under-utilized Virtuoso feature which can help identify structures and objects in a schematic with important electrical or topological characteristics.  This document provides a guide to setup, usage an customization of this Assistant.

Solutions

12. FAQ: Tips for Using S-Parameters in Spectre and SpectreRF

A great collection of tips and articles on using s-parameters in Spectre and SpectreRF.  Also, check out Tawna's library of useful blog articles in the RF space.

13. SKILL examples of forms, fields and menus

Everything you always wanted to know about creating forms and menus in SKILL, with example database and code.

14. Is there a way to run Mark Net in a user defined area of the design?

Why, yes.  Yes there is.

15. Virtuoso Layout Editor: Change color of the ruler - New Environment Variable

16. Pin Placer verses (sic) Pin Optimizer, which one to use and when?

This solution explains the difference between the 2 pin placement engines in VLS XL and provides guidelines for which one to use and when.

 

Stacy Whiteman 


Virtuosity: 12 Things I Learned in October by Browsing Cadence Online Support

$
0
0

Lots of routing, a little AMS, and finishing off with some fun...

Application Notes

1. Constraint Implementation and Validation in interoperability flow

The Mixed Signal Interoperability (MSI) flow allows designers to seamlessly transfer and implement routing constraints from analog to digital designs.  This document covers the steps required to apply and implement routing constraints in Encounter and validate these constraints using the Physical Verification System-Constraint Validation (PVS-CV) utility.

Rapid Adoption Kits

All RAKs include a detailed instructional document and database.

2. Virtuoso Interconnect Routing using VSR

Describes a new use model for running VSR using the Wire Assistant and top-level signal net routing in an analog top-level design.

3. Static and Dynamic Checks

This material describes the usage of the Spectre APS/XPS static and dynamic design checks. These checks may be used to identify typical design problems including high impedance nodes, DC leakage paths, extreme rise and fall times, excessive device currents, setup and hold timing errors, voltage domain issues, or connectivity problems. While the static checks are basic topology checks, the dynamic checks are performed during a Spectre APS/XPS transient simulation.

4. Mixed-Signal Verification -- System Verilog Real Number Modeling

Introduces the new SV-RNM 1800-2012 LRM capabilities that have been made available to aid in mixed-signal verification flows. It provides a basis for the production-level solution that we currently have in Incisive 12.2/ 13.1 that are SV-RNM 1800-2009 LRM centric. Then the RAK introduces the newer capabilities made available by the SV-RNM 1800-2012 LRM that enhance the SV-RNM modeling performance and functionality. The designer will be able to explore the user-defined types and resolution functions along with the debug capabilities made available by SimVision for these new features. Also includes an overview video.

5. Parasitic-Aware Design Using Custom Cells

ADE GXL Parasitic-Aware Design (PAD) features are used to investigate the effect of parasitic devices on a circuit.  RAK has been designed to highlight the features and functionality of the PAD flow in the IC 6.1.6 release, which enable the user to incorporate parasitic estimates into their simulations using custom parasitic elements.  Also includes an appendix on how to build a custom parasitic cell.  This RAK pairs well with the earlier Parasitic-Aware Design Workshop, which covers the entire PAD flow, including parasitic estimation, filtering extracted parasitics, parasitic stitching from extracted views, and parasitic reporting.

Videos

6. AMS Simulation with Multiple Logic Disciplines and Power Supplies on a Single Instance

Demonstrates how to run an AMS simulation with an instance that has multiple logic disciplines and power supplies. In this particular example, the instance has both 1.2V and 3.3V ports.

7. IC 6.1.6 Pin to Trunk Block-Level Routing

Frequent browsers of the Cadence Online Support Video Library may have noticed that many video demonstrations have been organized into "channels" or playlists.  Perfect for binge-watching on a rainy afternoon.  This channel features two videos covering block-level pin-to-trunk routing basics and routing between blocks.

8. IC 6.1.6 Pin To Trunk Device-Level Routing

These three videos cover device-level pin-to-trunk routing basics, wire assistant overrides, routing scope, and via control.

Cadence Community Blogs

9. IC6.1.6 Virtuoso Space-Based Mixed-Signal Router (VSR)

Nice FAQ-style overview of the new features in the Virtuoso Space-Based Router in the context of chip/block assembly routing in mixed-signal analog-on-top (AoT) designs.

10. Spectre XPS -- Cadence Reinvents FastSPICE Simulation

Although it has been under evaluation at several Early Access Partner customers for some months, the official launch of the Spectre XPS FastSPICE simulator was announced in October.  Initially targeted at SRAM characterization, in conjuction with Cadence's Liberate MX tool, Spectre XPS uses advanced partitioning techniques to achieve tremendous performance gains.  

11. EDA Consortium Extravaganza Celebrates 50 Years of Design Automation

"Engineering" and "Extravaganza" are not two words normally seen together, so you've got to gawk a bit when the geeks come out to play.  The 50-year anniversary, and the event, even made it into the Huffington Post, where, no doubt, my mother is still scratching her head wondering "what exactly is it that you do"? 

12. Unhinged

Our new Editor-in-Chief just released Episode 4 of this dynamic, off-the-wall Web show which combines geeky humor with actual news and interesting interviews in a format well-suited to short-attention-span creatures such as myself.  You may laugh, you may cringe, but you will be entertained. 

 

 

 

 

SKILL for the Skilled: Simple Testing Macros

$
0
0
In this post I want to look at an easy way to write simple self-testing code. This includes using the SKILL built-in assert macro and a few other macros which you can derive from it.

The assert macro

This new macro, assert, was added to SKILL in SKILL version 32. You can find out which version of SKILL you are using with the SKILL functiongetSkillVersion, which returns a string such as"SKILL32.00" or "SKILL33.00".

Using this macro is simple. In its simplest form, you wrap any single expression within (assert ...). At evaluation time this will trigger an error if the expression evaluates to nil.

CIW> (assert 1 + 1 == 2)nil

CIW> (assert 2 + 2 == 5)*Error* ASSERT FAILED: ((2 + 2) == 5)<<< Stack Trace >>>
error("ASSERT FAILED: %L\n" '((2 + 2) == 5))
unless(((2 + 2) == 5) error("ASSERT FAILED: %L\n" '(& == 5)))

You can also specify the error message using printf-style arguments.

CIW> (defun testit (x "n")
       (assert x > 3 "expecting x > 3, not %L" x)
       x-3)

CIW> (testit 12)9

CIW> (testit 2)*Error* expecting x > 3, not 2<<< Stack Trace >>>
error("expecting x > 3, not %L" x)
unless((x > 3) error("expecting x > 3, not %L" x))
testit(2)

What is a macro?

Macros are a feature in many lisps including emacs lisp, common lisp, and SKILL. Consequently, you can find tons of information on the Internet explaining lisp macros. A quick Google search for "lisp macro" returns pages of useful results.

In particular, a SKILL macro is a special type of function which computes and returns another piece of SKILL code in the form of an s-expression. This capability allows SKILL programs to write SKILL programs as a function of their raw, unevaluated, operands at the call-site. Although macros can certainly be abused, when used correctly SKILL macros can greatly enhance readability by abstracting away certain details, or by automating repetitive patterns.

You can find out more about SKILL macros by consulting the Cadence documentation. There is also an Advanced SKILL Programming class which Cadence Educational Services offers.

In-line tests

You can use assert inside your functions for run-time checks. You can also use assert at the top level of your SKILL files for load-time checks. This has an added advantage of helping the person who reads your code to understand how the function you are defining is used. In the following example anyone who reads your function definition can immediately see some examples of how to use the function.
;; Sort a list of strings case independently into alphabetical order.
(defun sort_case_independent (words "l")
  (sort words (lambda (w1 w2)
                (alphalessp (lowerCase w1)
                            (lowerCase w2)))))

(assert nil 
        ; works for empty list?
        == (sort_case_independent nil)) 

(assert '("a"); works for singleton list?
         ==  (sort_case_independent '("a")))) 

(assert '("a" "B")
         == (sort_case_independent '("B" "a")))

(assert '("a" "B")
         == (sort_case_independent '("a" "B")))

(assert '("A" "b")
        == (sort_case_independent '("b" "A")))

(assert '("A" "b")
        == (sort_case_independent '("A" "b")))

(assert '("A" "b" "c" "D")
        == (sort_case_independent '("c" "D" "A" "b")))
Writing your SKILL files to include these top-level assertions has yet another advantage: if someone later modifies your function, sort_case_independent, the tests will run the next time anyone loads the file. This means if an error has been introduced in the function, some sanity testing happens at load time. Furthermore, if someone enhances the function in a way that breaks backward compatibility, the assertion will fail at load time.

Defining assert if it is missing

If you are using a version of Virtuoso, Allegro, etc, which does not contain a definition of assert, you can define it yourself.
(unless (isCallable 'assert)
  (defmacro assert (expression @rest printf_style_args) 
    (if printf_style_args 
        `(unless ,expression
           (error ,@printf_style_args))
        `(unless ,expression
           (error "ASSERTION FAILED: %L\n" ',expression)))))

The assert_test macro

Some unit testing frameworks supply assertion functions such as assert_less, assert_greater, assert_equal, assert_not_equal. It is possible in SKILL to define a single assertion macro called, assert_test, which provides all these capabilities in one. You don't really need assert_equal, assert_not_equal, asset_lessp etc...

This macro is useful for building test cases. This macro attempts to output a helpful message if the assertion fails. The message includes the parameters to the testing expression, and the values they evaluate to. For example:

CIW> A = 33

CIW> B = 22

CIW> (assert_test A+B == B+2) *Error* (A + B)
  --> 3
(B + 2)
  --> 4
FAILED ASSERTION: ((A + B) == (B + 2))<<< Stack Trace >>>
...
CIW> (assert_test A+B > B+2)
*Error* (A + B)
  --> 3
(B + 2)
  --> 4
FAILED ASSERTION: ((A + B) > (B + 2))<<< Stack Trace >>>
...

The intelligent thing about assert_test as can be seen from the above example, is that it constructs an error message which tells you the text of the assertion that failed: ((A + B) == (B + 2)). It also tells you the arguments to the testing function in both raw and evaluated form: (A + B) --> 3 and (B + 2) --> 4

The macro definition is not trivial, but the code is given here. You don't really need to understand how it works in order to use it.

;; ARGUMENTS:
;;   expr - an expression to evaluate, asserting that it does not return nil
;;   ?ident ident - specifies an optional identifier which will be printed with [%L] in
;;                     the output if the assertion fails.  This will help you identify the
;;                     exact assertion that failed when scanning a testing log file.
;;   printf_style_args - additional printed information which will be output if the
;;                     assertion fails.
(defmacro assert_test (expr @key ident @rest printf_style_args)
  (if (atom expr)
      `(assert ,expr)
      (let ((extra (if printf_style_args
                       `(strcat "\n" (sprintf nil ,@printf_style_args))"")))
        (destructuringBind (operator @rest operands) expr
          (letseq ((vars (foreach mapcar _operand operands
                           (gensym)))
                   (bindings (foreach mapcar (var operand) vars operands
                               (list var operand)))
                   (assertion `(,operator ,@vars))
                   (errors (foreach mapcar (var operand) vars operands
                             `(sprintf nil "%L\n  --> %L" ',operand ,var))))
            `(let ,bindings
               (unless ,assertion
                 (error "%s%s%s"
                        (if ',ident
                            (sprintf nil "[%L] " ,ident)"")
                        (buildString (list ,@errors
                                           (sprintf nil "FAILED ASSERTION: %L" ',expr))"\n")
                        ,extra))))))))

The assert_fails macro

With the assertion macros presented above you can pretty robustly make assertions about the return values of functions. A limitation, however, is you cannot easily make assertions about the corner cases where your function triggers an error.

The following macro,assert_fails, provides the ability to assert that an expression triggers an error. For example, thesort_case_independent function defined above will fail, triggering an error, if given a list containing a non-string.

CIW> (sort_case_independent '("a" "b" 42 "c" "d"))*Error* lowerCase: argument #1 should be either a string or a symbol (type template = "S") at line 112 of file "*ciwInPort*" - 42<<< Stack Trace >>>
lowerCase(w2)
alphalessp(lowerCase(w1) lowerCase(w2))
funobj@0x2cac49a8("a" 42)
sort(words lambda((w1 w2) alphalessp(lowerCase(w1) lowerCase(w2))))
sort_case_independent('("a" "b"))

You could fix this by enhancing the function to do something reasonable in such a situation. Or you could simply document the limitation, in which case you might want to extend the in-line test cases as well.

(assert_fails (sort_case_independent '("a" "b" 42 "c" "d")))

Here is an implementation of such a assert_fails macro.

(defmacro assert_fails (expression)
  `(assert (not (errset ,expression))"EXPECTING FAILURE: %L\n"',expression))

Summary

In this article we looked at the assert macro, which is probably in the version of Virtuoso or Allegro you are using, and if not you can easily define it yourself. We also looked at assert_test and assert_fails which you can define yourself. You can use these three macros to easily improve the robustness of your SKILL programs.

See Also

SKILL for the Skilled: SKILL++ hi App Forms

$
0
0
One way to learn how to use the SKILL++ Object System is by extending an application which already exists. Once you understand how extension by inheritance works, it will be easier to implement SKILL++ applications from the ground up. I.e., if you understand inheritance, you can better architect your application to prepare for it.

This episode of SKILL for the Skilled starts with an existing SKILL++ GUI application and extends it several times. This is done each time by declaring a subclass of an existing SKILL++ class and adding methods on existing generic functions.

Overview

The application presented here is a hi GUI which walks an instance of a designated SKILL++ class across a designated design hierarchy. If the class of the instance is the base class smpDescendDesign, each cellView in the hierarchy is silently visited and, thus, is opened into virtual memory.

Please download theSKILL++ code, load the file smpGUI.ils, and call the function smpGUI().

The SKILL++ programmer is allowed to extend this base class to augment, circumvent, or modify some of its behavior.

FILE UNREADABLE

In the following paragraphs we'll extend this application in several ways:

  • Add diagnostic messages for each cellView visited
  • Save the modified cellViews encountered
  • Accommodate descent of schematics
  • Descend schematic with diagnostic messages

The Sample GUI

What does this application do? If you trace the smpWalk function and press Apply on the form, you get an idea of what's happening.

 

|[4]smpWalk(#{smpDescendDesign} db:0x311f7d9a)
|[4]smpWalk (smpDescendDesign t)(#{smpDescendDesign} db:0x311f7d9a)
|[6]smpWalk(#{smpDescendDesign 0x2ef12410} db:0x311f7a9a ?depth 1 ?lineage ... )
|[6]smpWalk (smpDescendDesign t)(#{smpDescendDesign} db:0x311f7a9a ?depth 1 ?lineage ... )
|[6]smpWalk (smpDescendDesign t) --> nil
|[6]smpWalk --> nil

...[stuff omitted]...

|[4]smpWalk (smpDescendDesign t) --> (db:0x311f7bf3 db:0x311f7bf2 db:0x311f7b23 db:0x311f7b22 db:0x311f7b1d ... )
|[4]smpWalk --> (db:0x311f7bf3 db:0x311f7bf2 db:0x311f7b23 db:0x311f7b22 db:0x311f7b1d ... )

The SKILL++ code is given insmpGUI.ils and smpDescendDesign.ils. You can experiment with it by loading startup.ils and calling the function smpGUI().

Extending the Application

A well-written SKILL++ application is extended not by modifying the original source code, but rather by creating sub-classes of built-in classes and providing methods on generic functions. This sample application defines as extensibility points a class named smpDescendDesign and several generic functions, smpDescription, smpWalk,smpGetSwitchMaster, and smpFilterInstances.

If a SKILL++ application is documented well enough, you will be able to read the documentation to understand which classes can be extended and which methods need to be overwritten to accomplish the desired results. Lacking sufficient documentation, you can read the source code comments.

Printing the Design Hierarchy

We want to extend the application so that the Class Name field of the GUI contains both strings: smpDescendDesign and smpPrintDesign. When the user selects smpPrintDesign and presses OK/Apply, an additional thing should happen: in particular, as the hierarchy is being visited, messages should be printed to standard output, indicating information about the cellViews visited, such as the following:
 0 -> ether_adc45n adc_sample_hold     layout: 
 1 -->    gpdk045  pmoscap2v     layout: |C3
 1 -->    gpdk045  pmoscap2v     layout: |C4
 1 --> ether_adc45n inv_2x_hv_small     layout: |I58
 2 --->    gpdk045     nmos2v     layout: |I58/|NM0
 2 --->    gpdk045     pmos2v     layout: |I58/|PM0

What is a Class?

Long-time readers may recall a series of SKILL for the Skilled articles some time ago,Introduction to Classes (parts 1 through 5).

A class in SKILL++ is an object which specifies a common structure of other objects (called instances). A class has at least one parent (called a direct super-class), and zero or more children (called direct sub-classes). The set of all classes form a directed acyclic graph from a special class at the top called t downward from super-class to sub-class ending at leaf-level classes at the bottom. The list of the class itself and all such parent classes starting with the class and terminating at t is called the list of super-classes. In SKILL++ this is a well defined and ordered list.

The class hierarchy is important because a class inherits structure and behavior from all its super-classes.

Creating a Subclass

The SKILL built-in macro defclass is used to create a new class or a sub-class of an existing class. If you don't specify an explicit super-class when creating a new class, its parent will be the special built-in class called standardObject.

To create a class named smpPrintDesign inheriting from smpDescendDesign, use the following syntax:

(defclass smpPrintDesign (smpDescendDesign)
   ())

This defines a simple class hierarchy as shown in the graphic:

FILE UNREADABLE

As far as SKILL++ is concerned, that's all that is necessary to create a class. However, to register the class with the sample application, the API function smpAddClass is provided. You need to call it with the name of the class. This tells the GUI form creation function to add the string "smpPrintDesign" to the cyclic field choices.

(smpAddClass 'smpPrintDesign)
You are free to create other building-block classes without registering them with smpAddClass. Those building-block classes won't appear in the GUI.

At this point you will have two Class Name choices in the GUI, but pressing OK/Apply will do the same thing regardless of which one is active.

What is a Generic Function?

While classes determine hierarchy and structure, generic functions and their methods implement behavior. A generic function declares the interface for all the methods of the same name. SKILL++ enforces parameter list congruency of all the methods of a generic function. SKILL++ programmers must be careful to enforce return value of the methods in a way which makes sense for the particular application.

For example, the return value of smpDescription must be a string because that string will be used as the value of a multi-line-string field in an application form.

The Sample API

This sample application pre-defines several generic functions which together with the smpDescendDesign class form the API for extending the application. A SKILL++ programmer is allowed to specialize methods of these generic functions on application-specific classes derived from smpDescendDesign.

The generic functions with their documentation are repeated here, but may also be found in the file smpGUI.ils.

 

smpDescription
(defgeneric smpDescription (obj))
Returns a string (with embedded \n characters) describing the action to be preformed if OK/Apply is pressed on the GUI while a particular class name is selected.
Methods on this generic function should return a string, possibly strcat'ing the result with callNextMethod().

smpWalk
(defgeneric smpWalk (obj cv @key lineage (depth 0) @rest _others))
Descend the hierarchy of the given cellview. Methods on this generic function may perform some action for side effect on the given cellView. Primary methods should call callNextMethod if they wish the descent to go deeper, and should avoid calling callNextMethod to prune the descent at this point. The return value of smpWalk is not specified.
ARGUMENTS:obj the object being specializedcv the opened cellView which is being visitedlineage a list of instances representing the lineage of this cellView back to the top level. The first element of lineage is the immediate parent of the cellView and the top-level instance is the last element of lineagedepth an integer indicating the hierarchy level. 0 indicates the top-level cellView.

smpFilterInstances
(defgeneric smpFilterInstances (obj cv))
Given a cellView, return the list of instances whose masters should be descended. The order of the list returned from smpFilterInstances is unimportant.

smpGetSwitchMaster
(defgeneric smpGetSwitchMaster (obj inst))
Given an instance in a cellView being visited by smpWalk,smpGetSwitchMaster returns the cellView to descend into. IfsmpGetSwitchMaster returns nil, then the descent is pruned at this point.

Updating the GUI Description Field

The GUI has a description field. We'd like this description to change as the user selects a different class name. We can do this by implementing the method smpDescription specialized on the new class. If you look at the smpDescription documentation above the comment on the generic function definition, you'll see some instructions for implementing methods.

These instructions describe how to implement a method on smpDescription: all smpDescription methods are unary functions, each method is expected to return a string.

(defmethod smpDescription ((_obj smpPrintDesign))
  (strcat (callNextMethod)"\nAnd print the names of each lib/cell/view encountered."))

Now if you interactively select the Class Name smpPrintDesign in the GUI, the description should change as shown here. As you see, the original text Descend the design hierarchy. has been augmented by the string concatenated by the new method.

FILE UNREADABLE

Adding the Diagnostic Messages

We now want to add a method to the smpWalk generic function. For clues on how to do this, consult the smpWalk documentation above. We can see that each method must have two required arguments, and must accept some optional arguments, in particular ?lineage and ?depth.
(defmethod smpWalk ((_obj smpPrintDesign) cv @key lineage (depth 0))
  (printf "%2d " depth)
  (for _i 0 depth (printf "-"))
  (printf "> ")
  (printf "%10s %10s %10s: %s\n"
          cv~>libName cv~>cellName cv~>viewName
          (buildString (reverse lineage~>name) "/"))
  (callNextMethod))

The method smpWalk specializing on smpPrintDesign prints some information about the cellView being visited, then calls callNextMethod, which continues with the descent.

The result of pressing the OK/Apply button is now something like the following being printed to the CIWindow:

 

 0 -> ether_adc45n adc_sample_hold     layout: 
 1 -->    gpdk045  pmoscap2v     layout: |C3
 1 -->    gpdk045  pmoscap2v     layout: |C4
 1 --> ether_adc45n inv_2x_hv_small     layout: |I58
 2 --->    gpdk045     nmos2v     layout: |I58/|NM0
 2 --->    gpdk045     pmos2v     layout: |I58/|PM0

Limiting the Descent

The smpFilterInstances can be implemented for a sub-class to affect which instances get considered for visitation. The documentation for smpFilterInstances is shown above.

The following code defines the class smpSaveDesign and extends the sample GUI, adding the capability to walk the design hierarchy, saving any unsaved cellViews.

FILE UNREADABLE

(defclass smpSaveDesign (smpPrintDesign)
   ())

(defmethod smpDescription ((_obj smpSaveDesign))
  (strcat (callNextMethod)
          "\nAnd save any unsaved cellViews."))

(defmethod smpWalk ((_obj smpSaveDesign) cv @rest _otherArgs)
  (callNextMethod)
  (cond
    ((member cv~>mode '("r" "s"))
     nil)
    ((cv~>modifiedButNotSaved)
     (dbSave cv))))

(defmethod smpFilterInstances ((obj smpSaveDesign) cv)
  (foreach mapcan ih cv~>instHeaders
    (when ih~>instances
      (ncons (car ih~>instances)))))

(smpAddClass 'smpSaveDesign)

In this example a method on smpWalk which saves the cellView if needed. It doesn't try to save cellViews which are read-only, nor scratch cellViews. Also it doesn't try to save anything unless it has modifiedButNotSaved set.

The smpWalk method specializing on smpDescendDesign calls smpFilterInstances to determine the list of instances to consider for descent. A smpFilterInstances method on smpSaveDesign is added here, which DOES NOT call callNextMethod. Since we are descending the hierarchy to save unsaved cellViews, we don't need to visit the same master more than once. This method returns a list of instances in the given cellView, one per instance header, which has an instance. I.e., sometimes instHead~>instances is nil; these are skipped.

Descending a Schematic Hierarchy

The classes shown above are great for walking a layout hierarchy. At each step the smpWalk is called recursively on the master of the instance being examined. To descend a schematic hierarchy, we ignore the master of the instance which is probably the symbol, and open and descend explicitly into the schematic when it exists. To do this we define the class smpSchematicHierarchy and provide the method smpGetSwitchMaster. See the smpGetSwitchMaster documentation.

FILE UNREADABLE

(defclass smpSchematicHierarchy (smpDescendDesign)
   ())

(defmethod smpGetSwitchMaster ((obj smpSchematicHierarchy) inst)
  (when (ddGetObj inst~>libName inst~>cellName "schematic")
    (dbOpenCellViewByType inst~>libName inst~>cellName "schematic")))

(defmethod smpDescription ((_obj smpSchematicHierarchy))
  (strcat (callNextMethod)
          "\nAnd descends a schematic hierarchy by looking for schematic cellviews""\n of symbol instances."))

(smpAddClass 'smpSchematicHierarchy)
The important method implemented here is smpGetSwitchMaster, which tests whether the schematic exists; if so, it opens and returns it. The smpWalk method specializing on smpDescendDesign calls smpGetSwitchMaster to determine which cellView to descend into, if any.

Combining Classes with Multiple Inheritance

SKILL++ supports multiple inheritance. This means a class is in principle allowed to inherit from more than one class.

We can use multiple inheritance to create an option in the sample GUI, which both descends the schematic hierarchy (based on class smpSchematicHierarchy) and prints information as the visiting occurs (based on class smpPrintDesign). To do this we define the class smpPrintSchematic to inherit from both smpPrintDesign and smpSchematicHierarchy, making the graph of the class hierarchy look like the following.

FILE UNREADABLE

Here is the code:

(defclass smpPrintSchematic (smpPrintDesign smpSchematicHierarchy)
   ())

(smpAddClass 'smpPrintSchematic)

Notice in this case that we don't define any new methods. This is because the methods defined thus far suffice for what we need. For example: the smpGetSwitchMaster method for class smpSchematicHierarchy will be used. In addition, since they all call callNextMethod of smpWalk for all the classes, (smpPrintDesign, smpSchematicHierarchy, and smpDescendDesign) will be used. In addition when selecting "smpPrintSchematic" on the GUI, we get a concatenated description of all the parent classes of smpPrintSchematic.

FILE UNREADABLE

Conclusion

Download this sample SKILL++ application, and load it by loading the startup.ils file. Again you'll need to start the GUI by typing smpGUI() into the CIWindow.

In this article we looked at a SKILL++ application which extends a specially designed application form. The application form allows you to select between several different behaviors based on the name of a selected class.

FILE UNREADABLE

The article shows several examples of class and method declarations which extend the given sample application in different ways. In particular it shows some simple examples of:

  • How to create a sub-class of an existing class
  • How to specialize a method on your class
  • How to use callNextMethod
  • How to use simple multiple inheritance

For more specific information on the SKILL++ Object System, please consult the Cadence online documentation. And as always, please post comments or questions below.

See also:

Virtuosity: 12 Things I Learned in November by Browsing Cadence Online Support

$
0
0

New content on a wide variety of topics in November.

Product Information 

1. Cadence Online Support Release Highlights

Find out about all the new improvements which have been made to the Product Pages on COS.

2. PVE Release Mechanism Change Letter

Changes in the way the Physical Verification System (PVS), QRC Extraction and K2 products are released.

Application Notes

3. Troubleshooting connect module issues with the AMS Designer simulator

Describes different types of problems which may be encountered with connect modules in mixed signal/mixed language simulations, and outlines solutions for each.

4. LEF Abstract Generation for IO cells

Describes how to apply the Abstract Generation tool from the Virtuoso IC6.1.5 stream to build optimized LEF abstracts from scratch for use in the rail analysis in Encounter Power Systems.

5. PVS Multi/Distributed Processing

This document provides an introduction, examples, and troubleshooting tips on the job submission process for multi-processing jobs in the Physical Verification System (PVS) tools.

6. How to Identify and Debug Multi-Stamp Errors with Assura

This document helps designers understand the definition of multi-stamp layout errors, what causes them, and how to debug them using the Assura Physical Verification tool.

Videos

7. Extracted View Parameterization

This video demonstrates a new feature in IC 6.1.6 ISR3, which allows you to parameterize extracted or layout/partial layout views in order to perform simulations and optimization to meet design specs, then back-annotate changed and optimized parameter values to the schematic.

Solutions

8. How to arrange assistant tabs on the side

When you stack assistants in Virtuoso, by default the tabs for selecting the stacked assistants appear on the bottom.  This handy feature switches the tabs to the side to make it easier to select the assistant you need.

9. Simulating designs with Out-of-Module references in AMS Designer

This tutorial presents the methods available for reusing a digital testbench that contains an Out-of-Module reference(s) (OOMR) in an analog-mixed signal (AMS) design simulation where SPICE blocks are substituted for some of the digital blocks.  An OOMR is unique in that it is used to reference from one Verilog module to another module and it does not pass through any of the ports. This is very commonly used in purely digital designs. Since this reference does not pass through the ports, special handling is required to make testbenches containing it work in an AMS simulation.

10. ncelab fails with CMINHD, CMINHR, or CMINHE error in INCISV 12.2 ISR

Explains possible scenarios and suggestions for fixing these errors, which can appear in the new release of INCISV due to unconnected inherited connection terminals.

11. Setting up the new Transmission Line workshop library and rfTlineLib

How to find and set up the necessary files and libraries to explore the new TransmissionLineWorkshop and rfTlineLib components.

Blog

12. ICCAD 2013: The New Electrically Aware Design Paradigm

Article discussing a presentation made at ICCAD on Cadence's Electrically Aware Design tools (EAD).   EAD enables design teams to do electrical verification incrementally in real time as each physical layout design decision is made.

 

Stacy Whiteman

Virtuosity: 15 Things I Learned in December 2013 by Browsing Cadence Online Support

$
0
0

With this month's title, I'll need to start adding the year, as this marks the one-year anniversary of the montly series.  I know it's been a useful monthly exercise for me.  Hopefully it has been helpful for everyone out there as well.

Application Notes

1. How to Utilize a Windowing Technique for Accurate DFT

Explains the best way to set up a transient simulation in ADE in order to achieve good results when performing DFT frequency analysis.  Includes detailed background explanations, comparisons of different methods, and examples.

2. Efficiently Moving Between ADE L and XL in a Sequential Design

Describes an efficient and effective methodology for moving between ADE L and ADE XL in the context of sequentially performing design tasks.  Explains how to move test setups from ADE L to XL, how to set up sweeps, corners and specifications, and the best way to add and manage multiple testbenches.

3. Exchanging OA database views between Encounter (EDI) and Virtuoso (IC)

Basic things to know when interoperating with physical hierarchy between Encounter and Virtuoso.

Videos

4. Using the SKILL IDE in IC 6.1.6

5. Using the PCell IDE in IC 6.1.6

6. SKILL IDE Workspaces and Assistants

A playlist of 3 videos (all accessed from the same link above) explaining how to use the SKILL IDE (Integrated Development Environment) and the PCell IDE, which provide tools and utilities to assist in the creation and debugging of SKILL programs and PCells.

Rapid Adoption Kits

7. PVS Configurator

This is a workshop on how to use the PVS Configurator to create configuration files for PVS technology rules setup.  Using configuration files and letting the designers make choices and save them for subsequent runs is an effective way to provide the options to the designer. Using a configuration file also lets the designers choose options directly from the main rule file supplied by the foundry. Includes detailed instructions and database.

8. Working with the Binder to stay XL compliant

When working in the Layout XL connectivity-aware environment, it is extremely important to establish and maintain the correct correspondence between the schematic and layout instances.  The Layout XL Binder makes and manages this correspondence, allowing the extractor to propagate the correct connectivity through the design.  This RAK explains how the Layout XL Binder works and how the layout engineer can leverage its capabilities to stay XL compliant and increase productivity.  Includes detailed step-by-step examples and database.

Solutions

9. What is the reelaborateonalter option for in Spectre?

Besides being a poster child for why one should use CamelCaseForLongOptionNames, the reelaborateonalter option can save a lot of evalution time in simulation when you have a long list of alter statements between analyses.

10. VerilogAMS wreal vectors and arrays

Nice explanation of how vectors and arrays are defined and handled in VerilogAMS wreal models.

11. PVS Quick Reference and Frequently Asked Questions

I love these Quick Reference/FAQ documents.  Not only do they usually give a quick answer to "how do I do that?", they also have lots of links to other relevant documents and resources.  This one is a concise document on how to use the PVS tools for LVS, DRC, ERC and several other flavors of alphabet soup.  Be sure to download the whole PDF document referenced in the solution to get all the content, including pictures.

12. Packaging Testcase to Send to Cadence: NEW mmsimpack Utility Replaces getSpectreFiles

Solving the problem of "I'm still missing file abc.xyz" when you have to send a testcase to Customer Support, mmsimpack is a new utility to create a compressed tarfile containing all the input files required for an MMSIM testcase.

Blogs

13. Top Cadence YouTube Videos of 2013

Yes, Cadence does have its own YouTube channel.  Sadly, no dancing kittens or adorable puppies (although I confess I didn't look at every single video), but you will find product video demos, customer success stories, and interviews with industry folks and our own Cadence R&D stars.

14. SpectreRF Tutorials and Appnotes... Shhhh... We Have a NEW Best Kept Secret!

This is the latest refresh in a series which is consistently high on Cadence's list of most-viewed blog posts.  Read it.  You know Tawna would never waste your time.

15. Support for Low Power Mixed Signal Designs in Virtuoso Schematic XL

Gives an overview of new capabilities in the Virtuoso Schematic Editor XL to support CPF (Common Power Format) export, import, and verification to improve productivity in a low-power design flow.

 

Stacy Whiteman

 

Viewing all 826 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>