Quantcast
Channel: Analog/Custom Design
Viewing all 746 articles
Browse latest View live

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

$
0
0
In the previous posts SKILL for the Skilled: Many Ways to Sum a List (Parts 1,2, and 3) we looked at several ways to sum a given list of numbers. We ignored the cases of the given list being very long. In this post, we will examine a way to sum the elements of arbitrarily long lists using recursive functions.

The approach shown in this post (part 4) will only work in Virtuoso IC 6.1; it depends on features which are not available in IC 5.1.41 and earlier. In particular we'll look at the optimizeTailCall status flag and at unwindProtect.

You may read this post in either of two ways. If you would like a way to make arbitrarily deep recursion work in SKILL++, you can look at the usage of the callWithOptimizeTailCall function in sumlist_4a. If you'd like to know how it works, look at the section Some Trickery.

What about large lists?

One disadvantage of the type of recursive function shown in sumlist_3a (seen earlier) is that every pending call to a function in SKILL++ requires stack space, and stack space is limited.

The two functions sumlist_1a and sumlist_3a differ in their ability to handle long lists. To demonstrate this limitation it helps to programmatically generate a very long list. The following expressions will generate a list of length 16,384.

data = (list 1.1 -2.1 2.3 -.04)
(for i 1 12
  data = (append data data))

Now what happens if we try to add up 16,384 numbers?

(sumlist_1a data)
=> 5160.96

(sumlist_3a data)

*Error* sumlist_3a: Runtime Stack Overflow!!!<<< Stack Trace >>>
sumlist_3a(cdr(numbers))
(car(numbers) + sumlist_3a(cdr(numbers)))
if(numbers (car(numbers) + sumlist_3a(cdr(numbers))) 0)
sumlist_3a(cdr(numbers))
(car(numbers) + sumlist_3a(cdr(numbers)))
if(numbers (car(numbers) + sumlist_3a(cdr(numbers))) 0)
sumlist_3a(cdr(numbers))
(car(numbers) + sumlist_3a(cdr(numbers)))
if(numbers (car(numbers) + sumlist_3a(cdr(numbers))) 0)
sumlist_3a(cdr(numbers))
...
(sumlist_3b data)*Error* unknown: Runtime Stack Overflow!!!<<< Stack Trace >>>
sum((sum_so_far + car(rest)) cdr(rest))
if(rest (sum (sum_so_far + car(rest)) cdr(rest)) sum_so_far)
sum((sum_so_far + car(rest)) cdr(rest))
if(rest (sum (sum_so_far + car(rest)) cdr(rest)) sum_so_far)
sum((sum_so_far + car(rest)) cdr(rest))
if(rest (sum (sum_so_far + car(rest)) cdr(rest)) sum_so_far)
sum((sum_so_far + car(rest)) cdr(rest))
if(rest (sum (sum_so_far + car(rest)) cdr(rest)) sum_so_far)
sum((sum_so_far + car(rest)) cdr(rest))
if(rest (sum (sum_so_far + car(rest)) cdr(rest)) sum_so_far)
...

It seems there is not enough stack space to add these numbers using the types of recursive functions we have seen up until now.

Tail call optimization

The subtle difference between sumlist_3a and sumlist_3b is not very important in the simple case, but allows for a very special optimization called tail call optimization. The maximum length of the list you may pass to function sumlist_3a is limited by the stack-space. As seen in the stack-trace above, if the list is too long, we get the error Runtime Stack Overflow!!!.

When tail call optimization is enabled, a function call in tail position does not consume additional stack space, thus allowing such a function call (even a recursive function call) to work like a GOTO.

(defun callWithOptimizeTailCall (thunk)
  (let ((save (status optimizeTailCall)))
    (sstatus optimizeTailCall t)
    (unwindProtect
      (thunk)
      (sstatus optimizeTailCall save))))

In SKILL++, tail call optimization is a run-time switch. This means you need not do anything special when loading your code, but you have to enable the optimization at run-time. The function callWithOptimizeTailCall, defined above, is written to allow the caller to specify which bit of code needs the optimization. It can be used as shown in sumlist_4a defined here.

(defun sumlist_4a (numbers)
  (labels ((sum (sum_so_far rest)
             (if rest
                 (sum (plus sum_so_far (car rest))
                      (cdr rest))
                 sum_so_far)))
    (callWithOptimizeTailCall
       (lambda ()
         (sum 0 numbers)))))

 

OK, Let's Test It

So does it work?

(sumlist_4a data)==> 5160.96

Yippie! no stack trace. SKILL++ was able to make the recursive call 16,384 times with no problem.

Okay, let's stress test it.

(progn data = (append data data)
       data = (append data data)
       data = (append data data)
       (length data))==>131072 

Now let's see if SKILL++ can add up a list of 131,072 numbers by a recursive function:

(sumlist_4a data)==>41287.68 

Yes. No problem.

Manipulating optimizeTailCall

You don't really need to understand how callWithOptimizeTailCall works in order to use it. To use it, the one or more expressions you'd like to evaluate with the optimization enabled must be wrapped in (lambda () ...) and that function of no arguments should be passed to the callWithOptimizeTailCall.
(callWithOptimizeTailCall
  (lambda ()
     ... some code ...
     ... maybe some more code ...
  ))

The promise of callWithOptimizeTailCall is that it will call the given function with tail call optimization enabled, but have no lasting effect on the global optimizeTailCall setting. I.e., if optimizeTailCall was enabled, it will remain enabled; and if it was disabled, it will remain disabled.

Some Trickery

The implementation of callWithOptimizeTailCall uses some pretty low level trickery which some of the readers might find interesting. status E.g., (status debugMode)
returns either t or nil depending on the value of the named (unquoted) status flag. There are several other documented status flags in SKILL. Perhaps the most commonly used ones are debugMode and printinfix. See the Cadence on-line documentation for explanation of others. debugMode Instructs the SKILL VM compiler to include additional debug information into the compiled instructions. printinfix Instructs the SKILL pretty-printer whether to use infix operators (such as +, ||, ', and ->) and C-style function call syntax such as (1+2+3), or whether to use function names (such as plus, or, quote, and getq), and use lisp-style function call syntax such as (plus 1 2 3). sstatus E.g., (sstatus debugMode t)
Sets the value of the named global status flag. All the flags which are available to status are available for sstatus. unwindProtect E.g., (unwindProtect (unsafe_function) (deleteFile fileName)).
This is used when calling a function or evaluating an expression which might trigger an error. unwindProtect takes two operands: an expression to evaluate, and a clean-up expression. The clean-up expression will get evaluated regardless of whether the first expression triggered an error or not. However, unlike errset it does not suppress the error. In the example in callWithOptimizeTailCall, the use of unwindProtect assures that (sstatus optimizeTailCall save), i.e., the call to restore the optimizeTailCall status occurs, even if the given function, thunk triggered an error. optimizeTailCall E.g., (sstatus optimizeTailCall t)
Controls whether tail-call optimization is enabled. Remember to always restore optimizeTailCall to its previous value so as never to globally effect this flag.

Quick Review

In this post you saw a cookbook way of modifying a particular type of recursive function so that it does not need significant stack space at run-time. The main trick is to write the recursion using tail-call style, and use the function callWithOptimizeTailCall to let SKILL++ do the work for you.

More to come

In the upcoming postings we'll look at even more variations of list summing.

See Also

Tail Call

Jim Newton


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

$
0
0
In the most recent posts of SKILL for the Skilled (see previous post here) we looked at different ways to sum a given list of numbers. The goal of these articles is not really to help you sum lists better, but rather to use a simple problem to demonstrate and compare features of the SKILL++ language.

In this posting of we show yet another implementation of sumlist using an operator which will be new to many readers. While the do construct is common to many lisp dialects, particularly Scheme implementations, it is somewhat controversial as some people love it, and some people hate it. Some people find it elegant and expressive, and other find it terse and awkward. I'll present it here and let you decide for yourself.

Summing a list with the (do ...) loop

Recall the implementation of sumlist_1a which accepts a list of numbers as its argument and returns the arithmetic of these numbers.

(defun sumlist_1a (numbers)
(let ((sum 0))
(foreach number numbers
sum = sum + number)
sum))

Here is yet another implementation of the sumlist function which we've seen in the past several postings. This version of the function uses the (do ...) construct.

(defun sumlist_5a (numbers)
(do ((remaining numbers (cdr remaining))
(sum_so_far 0 (plus sum_so_far (car remaining))))
((null remaining)
sum_so_far)))

Evolution of Lisp

There is an interesting paper which is easy to find on the Internet named Evolution of Lisp by Guy Steele and Richard Gabriel. This paper chronicles many of the features of modern Lisp languages. In the paper you can see the origins of many of the features of SKILL, including the rather obscure one we are going to look at today. Although this construct was first introduced into Maclisp in 1972, Evolution of Lisp refers to it as the new-style do.

An excerpt from Evolution of Lisp:

The awful part [of do] is that it uses multiple levels of parentheses as delimiters and you have to get them right or endure strange behavior; only a diehard Lisper could love such a syntax. Arguments over syntax aside, there is something to be said for recognizing that a loop that steps only one variable is pretty useless, in any programming language. It is almost always the case that one variable is used to generate successive values while another is used to accumulate a result. If the loop syntax steps only the generating variable, then the accumulating variable must be stepped "manually" by using assignment statements [...] or some other side effect. The multiple-variable do loop reflects an essential symmetry between generation and accumulation, allowing iteration to be expressed without explicit side effects:

Examining the example

The sumlist_5a uses do to iterate two variables remaining and sum_so_far from their respective initial values to their final values, using respective formulas to update the variables to their next values.

Breaking it down: Step by Step

As was mentioned above, the syntax of do is somewhat off-putting. There are lots of parentheses, and several interdependent components which you have to get right. The following is an itemization of the various parts of the (do ...) loop.

  1. You normally need to declare 0 or more variables: remaining and sum_so_far in this case. Two variables are specified in this case, but you may use as many as you like.
    (do ((remainingnumbers(cdr remaining))
    (sum_so_far0(plus sum_so_far (car remaining))))
    ((null remaining)
    sum_so_far))
  2. Specify an initial value of each variable:
    • remaining = numbers
    • sum_so_far = 0
    (do ((remainingnumbers(cdr remaining))
    (sum_so_far0(plus sum_so_far (car remaining))))
    ((null remaining)
    sum_so_far))
  3. Specify a termination condition. The loop continues until this expression becomes TRUE. In this case:
    • (null remaining)
    specifying to continue until the list remaining is empty.
    (do ((remainingnumbers(cdr remaining))
    (sum_so_far0(plus sum_so_far (car remaining))))
    ((null remaining)
    sum_so_far))
  4. If the condition is not yet TRUE, then each variable is updated as per the corresponding formula. In this case:
    • remaining = (cdr remaining)
    • sum_so_far = (plus sum_so_far (car remaining))
    (do ((remainingnumbers(cdr remaining))
    (sum_so_far0(plus sum_so_far (car remaining))))
    ((null remaining)
    sum_so_far))
  5. When the condition is TRUE, the loop terminates and the specified value is returned. This value is usually depends on some of the iteration variables. In this case:
    • sum_so_far
    (do ((remainingnumbers(cdr remaining))
    (sum_so_far0(plus sum_so_far (car remaining))))
    ((null remaining)
    sum_so_far))
  6. It is not shown above, but you may optionally include 1 or more expressions in the body of the (do ...).
    (do ((remainingnumbers(cdr remaining))
    (sum_so_far0(plus sum_so_far (car remaining))))
    ((null remaining)
    sum_so_far)
    (println remaining)
    (printf "partial sum = %L\n" sum_so_far))

Variations

One way to think of the SKILL do is as a mixture or generalization of several iteration constructs.

Do as for

The following is like a (for ...) loop.

(do ((i 0 i+1))
(i==100
t) ; return t because for always returns t
(println i))
... is equivalent to ...
(for i 0 100
(println i))

Do as foreach

The following is like a (foreach ...) loop.

(let ((some_list '(a b c d)))
(do ((sub some_list (cdr sub))
(item (car some_list) (cadr sub)))
((null sub)
some_list) ; return some_list because foreach returns the list it iterated over
(println item)))
... is equivalent to ...
(let ((some_list '(a b c d)))
(foreach i some_list
(println i)))

Do as a mixture of for and foreach

The (foreach ...) iterates successively through a given list of items. The (for ...) loop iterates successively through a range of integers, given the lower and upper bounds of the range. If you want to iterate one variable through a given list while simultaneously iterating another variable through a range of integers, you can use the (do ...) loop.

(let ((some_list '(a b c d ...)))
(do ((index 0 (add1 index))
(sub some_list (cdr sub))
(item (car some_list) (cadr sub)))
((null sub)
t)
(printf "The %d'th of the list is %L\n" index item)))
Which produces the following output.
The 0'th of the list is a
The 1'th of the list is b
The 2'th of the list is c
The 3'th of the list is d
The 4'th of the list is e
The 5'th of the list is f
The 6'th of the list is g

Summary

The SKILL do can be tricky to use. It allows the programmer control of several parallel iteration variables, all of which are potentially incremented according to different rules. You may also explicitly determine the return value, which is fixed and useless for other iteration constructs such as (for ...). You may also specify a series of expressions to evaluate when the iteration finishes while the iteration variables are still in scope holding their final values.

Hopefully you can use the steps above as sort of a cookbook.

More to come

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

See Also

Jim Newton

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

$
0
0
In a previous post I presented sumlist_2b as a function that would sum lists of length 0, 1, or more.
(defun sumlist_2b (numbers)
  (apply plus 0 0 numbers))

Unfortunately sumlist_2b cannot handle extremely long lists. In this posting, I will introduce sumlist_6 which does not suffer from this limitation.

This posting will not introduce any new SKILL++ primitives. Instead, it will use several primitives which have been introduced in the previous several postings to implement an algorithm which you may not have seen before. If any of these are unknown or mysterious to you, please consult previous postings of SKILL for the Skilled or the Virtuoso on-line documentation.

apply used to call a function indirectly given the function object and a list of arguments. sstatus modify a status variable of the SKILL virtual machine. optimizeTailCall makes function calls in tail position be stack-space neutral. labels defines local function functions which may be mutually recursive unwindProtectdefines a clean-up procedure which is executed even if an error is triggered.

Very long lists

If you attempt to use sumlist_2b on extremely long lists it will fail. You can generate a very long list as follows:

data = (list 1.1 -2.1 2.3 -.04)
(for i 1 15
  data = (append data data))

This produces a list of length 131072. If you attempt to sum this list with sumlist_2b you get the following error.

*Error* plus: too many arguments (at most 65535 expected, 131074 given) - (0 0 1.1 -2.1 2.3 ... )<<< Stack Trace >>>
apply(plus 0 0 numbers)
sumlist_2b(data)

The error message complains about a list of length 131074. This is because data has length 131072, but sumlist_2b prepends two additional 0's to the argument list. 131072 + 2 = 131074.

An esoteric issue?

You may very well consider this an esoteric issue. And you would be right. If you are in charge of generating the data, and you know the number of elements in your list is less than 16K, then there is no need to worry about this corner case. I suspect very few of the readers of this blog have ever, or will ever encounter such a situation. Even with that being the case, I discuss this here in the hopes that some of the techniques may prove useful, even if the exact problem never occurs.

Another reason to present a solution to this particular problem is that in so doing we can combine several other concepts which have been discussed in the previous SKILL for the Skilled blog postings.

The applyReduce function

The following implementation of applyReduce takes advantage of (sstatus optimizeTailCall t) and unwindProtect as seen in a previous post of SKILL for the Skilled.

(defun applyReduce (fun args @key (maxArgs 8192) identity)
  (labels ((apply_head (fun args tail)
             (let ((save (cdr tail)))
               (setcdr tail nil)
               (unwindProtect
                (apply fun args)
                (setcdr tail save))))
           (apply_reduce (args)
             (let ((tail (nthcdr (sub1 maxArgs) args)))
               (if (cdr tail)
                   (apply_reduce (cons (apply_head fun args tail)
                                       (cdr tail)))
                   (apply fun args)))))
    (cond
      ((cdr args) ;; if args has more the two elements
       (let ((save_optimizeTailCall (status optimizeTailCall)))
         (sstatus optimizeTailCall t)
         (unwindProtect
           (apply_reduce args)
           (sstatus optimizeTailCall save_optimizeTailCall))))
      (args;; if args has exactly one element
       (car args))
      (t;; if args is nil
       identity))))

How does it work?

The applyReduce function above calls the given function multiple times, but each time with a maximum of maxArgs number of arguments. The maxArgs parameter defaults to 8192 but you may override it to something smaller, especially to understand better how the function works.

(trace plus)
(applyReduce plus '(.1 .2 .3 .4 .5 .6 .7 .8 .9
                    .11 .12 .13 .14 .15 .16 .17 .18 .19) ?maxArgs 5)
(untrace plus)

In the trace output you can see that the plus function is called several times, but never with more than 5 arguments. After the initial call, the first argument to plus is the return value of the previous call.

||||||||||||||(0.1 + 0.2 + 0.3 + 0.4 + 0.5)
||||||||||||||plus --> 1.5
||||||||||||||||(1.5 + 0.6 + 0.7 + 0.8 + 0.9)
||||||||||||||||plus --> 4.5
||||||||||||||||||(4.5 + 0.11 + 0.12 + 0.13 + 0.14)
||||||||||||||||||plus --> 5.0
||||||||||||||||||||(5.0 + 0.15 + 0.16 + 0.17 + 0.18)
||||||||||||||||||||plus --> 5.66
||||||||||||||||||(5.66 + 0.19)
||||||||||||||||||plus --> 5.85

Now we can use applyReduce to create an efficient sumlist_6 function which is able to add up the elements of an arbitrarily long list. We can use sumlist_6 to add up the 131072 elements of the list data created above.

(defun sumlist_6 (numbers)
  (applyReduce plus numbers ?identity 0))

Testing it, we can see that it works and returns the correct result. We need to test sumlist_6 for a very long list, as well as for the nil list and a singleton list.

(sumlist_6 data)
--> 41287.68

(sumlist_6 nil)
--> 0

(sumlist_6 '(42))
--> 42

What about the performance?

Recall the algorithm implemented as sumlist_1a.

(defun sumlist_1a (numbers)
  (let ((sum 0))
    (foreach number numbers
      sum = sum + number)
    sum))

On the Linux machine which I normally use, evaluating (sumlist_1a data) takes approximately 0.02 seconds, while evaluating (sumlist_6 data) takes about 0.003. I.e., sumlist_6 is about 6 times faster for large lists.

Summary

The technique shown in sumlist_2b is very simple, and requires very little code. Nevertheless, it has the caveat that it will fail for arbitrarily long lists. This usually does not matter, and because this corner case is relatively unlikely to occur, the technique is tempting to use, and in fact is a good solution if the data is under your control and you know the length of the list in question is not extremely large.

The technique shown in sumlist_6 is more correct, at the sacrifice of being more complicated code. That being said, because the complication of sumlist_6 is factored into the implementation of the function applyReduce, the actual implementation of sumlist_6 is as simple as sumlist_2b. I would suggest putting a copy of applyReduce in your personal SKILL function library, with the appropriate customer prefix of course.

More to come

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

Jim Newton

Previous blog posts in this series

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

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

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

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

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

Library "Safe Margins" -- Are They Really Saving Your Design?

$
0
0

Designers need to radically re-think their strategies for timing closure to get the most out of process technologies that are becoming readily available. The additional burdens of creating electrical cell views for timing, power and signal integrity, accounting for process variability, managing leakage power, and hitting a low power budget make obtaining market leading performance extremely difficult.

To overcome these increasing burdens, designers have to reduce margins that exist within timing models provided by a 3rd party IP provider or a central library group. This will require designers to control how standard cell library views are created to better tune their models. The best way to do so is to characterize/re-characterize the library for specific design goals.

Today, designers working on production designs for 40nm, 28-nm and 20nm processes are left with no window to work with - and the situation will only get worse as we head towards 16nm and 14nm. For example, timing signoff can be achieved at the worst case corner at high temperature but not at low temperature. The need for supporting a wide range of process, voltage and temperature corners (PVTs) is increasing due to shrinking device geometries. The impact of variation and temperature inversion is greater for lower voltages, yet these are now the norm due to the need to reduce power.

The sensitivity to process variation, the exponential growth of characterization runs, and the number of data points per each characterization run pose more challenges. The paradox is that the overlying process technology can often deliver on the design goals but the tools, process models and methodology get in the way. Every tool and each modeling level adds margin until all the guard bands eat up all the design space.

The following areas need careful consideration due to their impact on library accuracy and development schedules:

1. Standard cell library characterization solution must offer high productivity for large complex cells and complex I/Os

2. Solution must support libraries from multiple foundries and IP providers

3. Solution must be tightly integrated with a leading edge simulation solution for uncompromised accuracy and scalable performance

4. Solution must support a wide range of PVTs

5. Solution must support low-power, high speed standard cell library variants

6. Generated models must be of utmost precision and accuracy

7. Models must be consistent with other characterization components including large macros and memory blocks

8. Solution offers flexibility in setting measurements and thresholds for items such as:

     a. Setup and hold for flip-flops, latches and clock gating cells
     b. Pin capacitance measurement
     c. Type of pre-driver used

An integrated library characterization solution based on the Cadence Virtuoso platform is depicted below.

By characterizing standard cell libraries, users can get a wide range of choices for how simulation data is captured and measured before it is encapsulated in Liberty format. For example, the value of pin capacitance varies by up to 50% depending on the measurement thresholds and this can have a major impact on hold time closure. For setup and hold values, typically there is additional margin added by the IP provider to avoid warnings further down in the tool flow. For example, some tools do not support negative setup or hold times so margin is added to the library to remove the negative constraints.

For designs that use multiple voltages or dynamic voltage scaling, the delay calculator will use interpolation if the library views do not explicitly cover the voltage being used, nearly always providing pessimistic results. Designers can overcome this by characterizing more voltage corners which reduces built-in margins thus achieving higher accuracy. Moreover, by the time the library is delivered the process models used to create it may have changed. Process models typically tighten their margins as they mature, but if the library isn't up to date, the timing closure tools will not enjoy this benefit and will continue to be over-constrained.

For memory IP blocks, the timing and power models are created by a compiler without knowing the exact context of how the memory block will be used. The memory compiler creates the model for each instance from fitting data derived from the characterization of a small set of memory instances. A more accurate model can be achieved by characterizing the exact size for each instance of each memory block using precise loading, slew rates, voltage and temperature values for each design.

The investment required to do project or design family specific characterization is relatively small. A new library corner for a thousand cells can now be completed in half a day or less using a single eight core machine. Smart characterization tools exist that can automatically setup and optimize the characterization directly from analyzing the transistors and arcs inside the cell. Besides, this allows design teams to take control of the standard cell library delivery schedule so they do not need to rely on the over-burdened central group or to pay additional fees if a new corner or a different characterization setting is required.

Margins are necessary to help safe guard against inaccuracies inherent in abstracting silicon components to the higher models. However, over-margining is creating a huge barrier to the effective use of 45nm and below processes. Using statistical methods will greatly help by providing a more realistic timing answer, but also taking control of the margins inherent in off-the-shelf IP models can also alleviate many timing closure challenges. To accelerate their development schedules, designers should take control of their characterization tasks to create realistic library views instead of relying on over-margined standard cell libraries.

Ahmed Elzeftawi

 

Virtuosity: 10 Things I Learned in December By Browsing Cadence Online Support

$
0
0

In addition to the R&D engineers who actually develop our software, the folks in many other groups here at Cadence put a lot of time and effort into creating a wide variety of documents, presentation and videos to help our users learn to use the software more effectively.  Today we're kicking off a new monthly series to highlight interesting and useful content recently added to the Cadence Online Support website in the Virtuoso Custom IC and Mixed Signal product areas.

I'd like to give a big shout-out of thanks to my colleagues in Customer Support for helping me put this together!

Rapid Adoption Kits 

Last year, I wrote an article in the Things You Didn't Know About Virtuoso series about a new collateral packaging effort in the Custom IC products called Rapid Adoptions Kits (or RAKs).  Since then the concept has really taken off.  Check out the main page to see all the RAKs currently available.  Here are some of the new ones:

1. Virtuoso Integrated Physical Verification System (iPVS) Workshop

iPVS is a capability provided in Virtuoso to continuously check design rules using sign-off  rules as you design.  The RAK includes a workshop database, manual and a video.

2. IC6.1.5 Virtuoso Schematic Editor XL PIEA, Conformal Low Power: Mixed-Signal Low Power Structural Verification

Low power is huge these days.  All you have to do is look at the battery meter on your phone to know that the one thing you want is for that battery to last longer.  The title of this RAK is quite a mouthful, but it will show you how to use the Power Intent Export Assistant (PIEA) in the Virtuoso Schematic Editor to extract the power intent attributes of your design to create macro-level and design-level CPF which can be used in Conformal Lower Power to do low power verification.  The RAK includes a database and instruction/overview documentation.

3. Schematic Model Generator

Virtuoso Schematic Model Generator (SMG) enables the generation of analog/mixed-signal behavioral models using a schematic-like representation of the behavioral model.  The RAK includes a workshop database, manual and 3 videos.

Solutions

"Solutions" on Cadence Online Support (where they live under the heading "Troubleshooting") are generally written in response to one or more user questions and can include a series of instructions, a customized Skill script, a small design example, a work-around to a known issue or any combination of those elements. 

4. VSR and RiDE Technology Requirements and Routing with tcl scripts

What are the technology requirements while working with Virtuoso Space Based Router (VSR) and Routing Integrated Development Environmet (RiDE)?  This solution includes a lab database with instructions, including sample routing scripts.

5. Circuit Prospector: Quick Reference to Basics and Most Referred Solutions

This solution includes a concise document which provides detailed descriptions of how Circuit Prospector works, how it interfaces with your PDK, what each finder and structure consists of and how to customize them.  It also includes pointers to loads of existing solutions and videos, as well as a sample database for you to play with.

6. Differences in signal plotting between ADE XL and ViVA

This solution focuses on the scenario in which you have a setup in ADE XL with multiple tests and you want to compare waveforms with the same name between different tests.  There are many ways to do this and this solution describes some of the subtle complications that can arise in certain cases.

Application Notes

7. Troubleshooting Virtuoso AMS Designer Errors

As if mixed signal simulation wasn't complicated enough, the error messages you get are often cryptic at best.  This document lets you look up the different messages that appear and see what scenarios might have produce that message and what to do about them.  A definite must-read.

8. Virtuoso Visualization & Analysis (ViVA) Bindkey & Icon Reference

Ever watch an expert user with a piece of software and see them execute a smooth sequence of operations seemingly by telepathy?  You ask them how they did it and they say, "oh that's just bindkey such-and-such."  The new ViVA waveform viewer has quite a bit of this "magic" built into it and this document is intended to give you that same quick and efficient power by spelling out all the most useful bindkey combinations.

Videos

9. SimVision Debug Video Series

As someone who makes a lot of instructional videos, I was very impressed by this extensive series of videos on how to use SimVision to debug digital and mixed-signal simulations.  Definitely a must-watch if you need to use this tool.

Webinars

10. Variation Aware Design

Finally, I wanted to point out a webinar series that was broadcast last November and December centered on Virtuoso's Variation Aware Design offerings.  There were 3 webinars in the series, which are now available via the webinar archive on http://www.cadence.com.  The first was called "Understanding the 'What If' to Avoid the 'What Now'" and shows how to use Sensitivity Analysis to identify critical device parameters and tune your design to meet specification.  The second was called "Efficient Design Verification and Yield Estimation" and demonstrated advanced analyses in ADE GXL to perform Worst Case Corners analysis and High Yield Estimation.  The 3rd was called "Detecting and Fixing Layout Dependent Effects" and showed how Virtuoso can help you detect and fix LDE problems by rapidly producing layout and verification results that feed the industry's first LDE-aware design flow.

Thanks for reading and visiting http://support.cadence.com.  Leave a comment if there are any items you found particularly useful that you'd like to share with others.

Stacy Whiteman

 

Introduction to Cadence Virtuoso Advanced Node Design Environment

$
0
0

What can designers do about advanced node technology? This is an introduction to the Cadence Virtuoso Advanced Node design environment, announced Jan. 28, 2013, as a custom/analog design development environment for leading edge-advanced node technology.

Problems of Advanced Node Design

When designing with the most advanced node technologies including 22nm technology and beyond, you will encounter many new problems that no one could have anticipated from previous design work.  New tools in the design flow need not require a completely new design flow.  You should maximize the effectiveness of the current design environment.  Then, you need to handle new problems appropriately and accordingly.

Generally speaking, you will need to pay special attention to the following things with advanced node design.

  • Multiple Patterning
  • Layout Dependent Effects (LDE)

o Length Of Diffusion (LOD) / STI Stress
o Well Proximity Effect
o Interconnect R, C, and inductance

  • Use of Local Interconnect Layers

     

Fig 1 (a) Length Of Diffusion (LOD)         

Fig 1 (b) Interconnect layers

Fig 1 (c) WPE (Well Proximity Effects)

Fig 1(c) shows the WPE (Well Proximity Effect), one of the LDEs. The performance of the devices is different for 26db (~12x) depending on the placement locations of the devices from the edge of the well.

For advanced node design, those problems must be evaluated and considered in the design flow.

Current Design Flow

The current generic design flow that is mainly used for the 45nm and 32nm process nodes is shown in the following Fig 2. The current design flow represents the collected wisdom of many designers over many development processes. Indeed there is a big advantage of the current design flow because the designer has been familiar with it for a long time.

However, it is also true that the flow has the limitations listed below.

  • Each step is connected serially. When some steps have problems, many undesired iterations will be induced.
  • Automation is not used to its full potential.
  • Additional and incremental analysis during the design is not easy.
  • Hidden problems in the design including performance variance affected by LDE can go undetected until a very late phase in the design process.

The problems in the advanced nodes listed above all occurred with the previous technology, too. However, the reason that the current (existing) flow that is like the flow shown in Fig 2 is that the effects of those problems in the previous process technologies were very small, and they could be ignored. In the advanced technology required for 20nm or 14nm designs, those problems cannot be ignored. Therefore, the design process cannot be concluded unless designers detect these problems at an early stage of the design, and take necessary actions.

Also, the architectures of double patterning and Interconnect layers have already emerged in the previous technologies. However, these architectures will make a huge impact on performances of designs in advanced node technology. The design process itself needs to be re-evaluated for the advanced node design.

Fig 2. Current generic design flow

Therefore, an ideal design environment for advanced node technology is a design flow that allows the user to detect and resolve the non-ignorable problems in advanced node technology without invalidating the usability of the current design flow.

Cadence's design flow for advanced node technology

Fig 3 shows the design flow realized by Cadence Virtuoso Advanced Node design environment.

Fig 3. Cadence Virtuoso Advanced Node design environment for advanced node technology

At first, this flow does not require the user to change his current design flow. It has various enhancements for advanced node technology and inherits properties of the familiar, current design flow.

Several enhancements for advanced node technology are introduced below.

Analog Prototyping Step

In this step, the user can perform a simulation and consider the LDE before starting the actual layout. This is realized by prototyping the device layout, and simulating the design while considering LDE parameters. With this step, one can dramatically reduce the design modification work caused by performance gaps between the actual design after place and route is completed, and the initial simulation result.

It will work most effectively for considering the WPE (Well Proximity Effect) and performance effects caused by physical stress of the STI (Shallow Trench Isolation).

This step is very powerful because it not only shows LDE effects in a window, but it also allows the user to quickly check and verify characteristic changes due to LDE by running a simulation.

The following figure (Fig 4) shows this part of the step (Analog Prototyping).

Fig 4. Analog Prototyping

Module generation, Device placement Step

In the real design generation phase, the user can generate modules using Modgen and can place devices using the newly enhanced Pcells.  Device placement work in advanced node technology is like pulling teeth.  Very complex and rigid design rules need to be satisfied.  Fig 5 shows sample complex-design rules related to the device placement.  To effectively support those complex design rules, the Pcell abutment function in Cadence Virtuoso Advanced Node design environment was dramatically enhanced.  The Pcell abutment function evaluates the relationship between the device to be placed and adjacent devices and realizes the best placements of devices automatically.

Fig 5. Example of complex device placement

Routing step

Routing work on the Local Interconnect Layer (LI) differs dramatically from the routing on the other routing layers.  As the figure (Fig 6) shows, the LI layers exist between Metal1 and base layers.   The layers allow contact-free connection.  Also, the length and width of wires on the LI layers are tightly specified.  The layers have very rigid constraints.

 

 

Fig 6. Local Interconnect Layers

In Cadence Virtuoso Advanced Node design environment, the user interface has been enhanced as well as the technology file so that the user can smoothly work on routing wires on the local interconnect layers (Fig 7).

Fig 7. Routing Environment for Local Interconnect Layers

Also, the DPT Assistant (Fig 8) can be used in Cadence Virtuoso Advanced Node design environment so that the routing can easily be done when considering double patterning architecture.  The DPT Assistant immediately informs the user where in a design that double patterning conflict occurs.

 

Fig 8.  Dynamic Coloring Assistant

Fig 9 shows the Odd-Loop Error Detection done by Virtuoso Integrated PVS (IPVS).  In Cadence Virtuoso Advanced Node design environment, PVS, a sign-off level Design Rule Checker is fully integrated.  It performs "Dynamic Signoff Checking" when a design is updated.

Fig 9. Odd-loop error detection

Conclusion

This document has been a quick introduction to new features that are available in Cadence's design development environment for advanced technology.  The Cadence Virtuoso Advanced Node design environment is the brand-new design environment that not only supports each new requirement but also integrates new steps into the existing design flow without needlessly disrupting that flow.

Also, the biggest and hidden advantage of this design flow is that software will obviate the possible design problems inherent in advanced node design even if the designer is unaware of these new problems.  As the result, the flow can minimize undesired iteration in the design process that was the limitation of the previous design flow.  This "Correct-by-Construction" approach realized by Cadence Virtuoso Advanced Node design environment can dramatically reduce the entire design turnaround time.

Hiroshi Ishikawi

Things You Didn't Know About Virtuoso: Drag and Drop

$
0
0

I love it when I'm sitting in a meeting with my colleagues or with a group of customers and someone brings up something about our software that they find annoying and another person says "Wait, why are you doing it that way?  Why don't you just...".  Immediately my mind says "blog time!" 

One such sequence of events happened recently around the concept of "drag and drop."  For those of us who have been around for a while, the idea of dragging objects, particularly text or something non-drawing related, just never springs to mind.  I'll complain all day about having to re-type something or execute a long series of mouse clicks before I even try to just grab it and drag it where I want it to go.

So let's play a little game.  Where are all the places in ADE L/XL/GXL and ViVA that you can perform some useful function by dragging something?  I'm sure I'll miss a few, so feel free to leave additional discoveries in the comments.

In general

ADE L

  • Drag the rows in the different subwindows to reorder them (Design Variables, Outputs, Analyses)

ADE XL

  • Drag the rows in the Output Setup panel up and down to reorder them (within the same test)
  • Drag the columns in the Output Setup panel to rearrange them (sadly it isn't saved, but it can be very handy while you're setting things up)
  • Drag an output expression over to a Global Variable to have that variable value be taken from the result of one test and used as input to a different test (also referred to as combinatorial variable expressions)
  • In the Detail-Transpose results view, drag columns (inputs or outputs) around for easier comparison of measured values.  RMB on the columns in this view to hide them.

Calculator

  • Drag an output expression from the Outputs Setup panel in ADE XL into the calculator buffer to create combinatorial output expressions using results from multiple tests
  • Drag an expression from the Stack to the Buffer
  • Drag an expression from the Expression Editor to the Buffer.  Don't know about the Expression Editor?  Time to learn!
  • Drag one or more expressions from the Stack to the Expression Editor. 
  • Drag one or more expressions from the Stack, Buffer or Expression Editor and--wait for it--drop it into any text area outside of Virtuoso.  Text editor, command line or it will even create a text file on your desktop.
  • Drag an expression from the Stack, Buffer or Expression Editor into any text field in a calculator function form. (This is the one that inspired the "aha" moment I mentioned in the first paragraph.)
  • Bonus Tip: Double-click on any Stack item to move it into the Buffer and push the contents of the Buffer onto the Stack

ViVA Graph

  • Drag traces from one subwindow to another.  (Hold Ctrl while dragging to copy instead of move)
  • Drag traces from one strip to another (Hold Ctrl to copy)
  • Drag a whole strip up or down to reorder
  • Drag traces from the graph to the Subwindows Assistant (Window->Assistants->Subwindows if you haven't seen this).  Here you can drop them in an empty area to create a new subwindow or into one of the existing subwindow thumbnails to put them there
  • Drag signals from the Results Browser onto the graph or the Subwindows Assistant
  • Drag a signal from the Results Browser into the Calculator Buffer
  • Pretty much anything having to do with markers (point/A/B/delta, vertical or horizontal markers and their intercept bubbles) can be moved and rearranged by dragging

So there you have it. What have I missed?  As always, your feedback is welcome.

Stacy Whiteman

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Virtuosity: 10 Things I Learned In January By Browsing Cadence Online Support

$
0
0

This month's highlighted content includes helpful information on wreal modeling, mixed-signal interoperability, verification of digitally-calibrated analog circuits, device and block-level routing and lots more.

Enjoy and don't forget to leave feedback at the top of the individual content pages in COS (Cadence Online Support) to let us know what information you find most useful.

Rapid Adoption Kits

1. Guidelines on Modeling Analog Circuits with wreal

This material illustrates wreal modeling concepts by migrating a Verilog-A based model of an AM modulation-demodulation system to a wreal model with Verilog-AMS. The wreal equivalent of each block is created to build up an all digital simulation for the system.  Guideline steps for creating wreal Verilog-AMS models are developed and used for developing the wreal models.  The RAK includes a workshop database and detailed manual.

2. IC6.1.5 VSR (Virtuoso Space-based Router) Workshop

This workshop highlights interactive, assisted and automatic routing features available in Virtuoso Space-based Router (VSR). During the course of the workshop you will be able to apply these features to do some device and block level routing and analyze your results in R-IDE (Routing Integrated Development Environment).  The RAK includes a workshop database and detailed manual.

Application Notes

3. AMS Designer INCISIVE Command-line Flow Use Model

This document provides an overview of how to run mixed-signal simulations from the command-line interface of the AMS Designer simulator using the "irun" command.  It includes information on setup files, options, command syntax and mixed-language specifics, as well as examples and pointers to the workshop database and other references.

4. Calibrated Verification with ADE XL

This document focuses on a topic about which I am frequently asked by customers--how to use ADE XL to verify digitally calibrated high-performance circuits.  It covers the details of the ADE XL features supporting this technique, including the use of calcVal expressions and pre-run Ocean scripts.  Detailed worked examples are given, complete with accompanying database.

5. Open Access Reference Library Import

Recommendations on how to import a standard cell or IP reference library for use in OpenAccess mixed-signal design using the Incremental Technology Database (ITDB) and LEF Import.

6. ADE XL Quick Start

Given that the ADE XL User Guide is now 846 pages, this document is intended as a quick reference tutorial in using the basic functionality in ADE XL, including setting up tests, running simulations, basic post-processing, sweeps and corners. 

Solutions

7. New AHDL Linter in AMS Designer 12.2

Detailed information on how to use the AHDL linter feature in AMS Designer which enables you to detect modeling issues in Verilog-A and VerilogAMS (AHDL)  languages.

8. Virtuoso Encounter Mixed-Signal Flow: Quick Reference to Basics and Most Referred Solutions

This is a concise and handy reference document for using Virtuoso and Encounter to manage mixed-signal design flows that allow the mixing of digital and analog content throughout the design hierarchy.  It includes information on design and technology data requirements, environmental configuration and an extensive list of links to helpful solutions, reference documents and application notes.

Video

9. Cadence Chemical Mechanical Polishing (CMP) Predictor - An Introduction

An introduction to the Cadence Chemical Mechanical Polishing Predictor.  It also describes the extraction process, simulation flow and steps to monitor the job status and view extraction results.

Blog

10. Introduction to Cadence Virtuoso Advanced Node Design Environment

An introduction to the new features that are available in Cadence's Virtuoso Advanced Node design environment which support the complex requirements inherent in advanced process technologies at 22nm and below.

Stacy Whiteman

 


Virtuosity: 10 Things I Learned in February By Browsing Cadence Online Support

$
0
0

February was a big month for RAKs (Rapid Adoption Kits)!  If you haven't checked out the listings under Resources->Rapid Adoption Kits yet, you're missing out.  You'll find databases with detailed instructions, documentation and videos on many tools, features and flows.   They've become very popular and we're adding more all the time.

We're also featuring content on routing, schematic PCells, ADE XL job distribution, advanced node layout techniques and a new course on high-performance circuit simulation.

Rapid Adoption Kits

1. Creating Custom Connect Rules for AMS Simulation in ADE

Outlines the process used to create custom connect rules for mixed-signal simulation in ADE using AMS Designer.  Includes a database example and step-by-step manual.

2. Spectre Device Checks

Describes the usage of the Spectre device checking feature.  Spectre device checking is used to monitor if devices in your circuit are violating user-defined conditions.  For example, checking to insure that low voltage devices are not inadvertently used in high voltage conditions.  Checks can show warnings or cause the simulation to stop on an error.  This is a very powerful feature, but can be somewhat confusing to set up, so this database and step-by-step manual will be extremely useful.

3. Using Spectre Save-Recover

Spectre and Spectre Accelerated Parallel Simulator (APS) have the capability to save a simulation state and restart the simulation from the saved state file. The simulation state for long simulations can be periodically saved to allow recovery from unforeseen circumstances, such as power outages or disk full issues. The save/recover methodology can also be used to restart simulations with different parameters or inputs. This example will illustrate using Spectre/APS Save-Recover from both ADE and command line based simulation.  Includes database and detailed manual.

4. Basics of Inherited Connections

Often times in design, the same cells need to be used in different parts of the circuit that use different power supply voltages. Inherited connections provide a mechanism to selectively override net connections by placing properties on the parent instance. Therefore the same cell can be used with different power supplies without the need for explicit power and ground pins. This document describes how inherited connections work with a sample design which shows how power and ground connections for the same cell can be different in the schematic hierarchy.   It also outlines the process used to resolve inherited connections.  Includes an example database.

5. Passing Parameters in the Schematic Hierarchy with pPar

Schematics can be parameterized with pPar parameters to allow passing parameters from a parent instance to the lower level schematic (the child). This mechanism facilitates defining high level parameters to define the functionality of the circuit.  Includes database and detailed manual.

Application Notes

6. ADE XL Job Policy Setup

This document (and the complementary ADE XL Simulation Flow document) are essential to getting the most out of the powerful job distribution capabilities in ADE XL.  You'll learn about the different types of job distribution available, their options, how they work and how to set them up.

Solutions

7. How to set non-uniform Routing Tracks with IC6.1.5 VSR-RIDE and do routing

For IC6.1.5 users who wish to do gridded track based routing, this document will demonstrate how to set up the Tcl/tk commands in a script that can be run within Virtuoso Layout GXL or through the Routing Integrated Development Environment (RIDE).  Tracks can be setup for all routing layers of just selected routing layers.

8. Schematic PCell Example with inherited connection.  netExpression added to wire and label display

Includes sample SKILL code and testcase database to create a schematic PCell with inherited connections.

9. How to enable the color engine in ICADV12.1 and later versions like upcoming IC6.1.6

The Virtuoso Layout environment for advanced node design includes features to support metal coloring for double-patterning technologies.  This solution shows you how to enable those features.

Course Announcement

10. High-Performance Simulation using Spectre Simulators v12.1

In this course, you use the enhanced technologies of Virtuoso Spectre Circuit Simulator, including the Accelerated Parallel Simulator (APS), to run fast and accurate analog simulations on large, complex, mixed-signal designs providing a significant performance gain over baseline Spectre simulation.

You explore the use of multithreading options, APS, Fast APS and APS in distributed mode. You verify the circuit performance and identify the potential failure modes by running dynamic checks with Spectre and APS and then examine the results by viewing the appropriate XML file. You run Spectre APS stitching of DSPF, SPEF, and DPF files and a postlayout simulation. You run high-capacity EMIR simulation using Spectre APS.

Stacy Whiteman

 

 

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

Viewing all 746 articles
Browse latest View live


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