The way SKILL++ deals with functions is a bit different than the
way traditional SKILL deals with them. In this posting I'd
like to show how to implement a design hierarchy traversal engine in
SKILL++ and use it as an introduction to SKILL++.
What is SKILL++?
SKILL++ is a subset of the SKILL language, not a separate language as
one might suppose. The term SKILL++ refers to a small but powerful
set of extensions that were made to the core SKILL language in the
mid 1990s and are now available for use in many Cadence tools. In
fact, any Cadence tool that is extensible by loading and executing
SKILL programs is a candidate for using SKILL++, including: Virtuoso,
VirtuosoXL, pipo, Assura, Dracula, PVS, Allegro-PCB, cdnsip and
probably a few more that I'm forgetting.
Basically the language features are:
By contrast, traditional SKILL offers
variables and separate names spaces (sometimes called Lisp-2) for
functions and variables. Dynamic and lexical variables are both
useful for different purposes.
What are dynamic and lexical variables?
These two kinds of variables differ semantically but not
syntactically. All variables appearing in SKILL++ code are considered
lexical while all variables appearing in traditional SKILL code are
If you are wondering what the semantic difference is:
Perceptions of SKILL++
In this article, we'll ignore the SKILL++ object system and
concentrate on the first two of these capabilities -- sometimes referred
to jointly as the SKILL++
Dialect. Unfortunately, the other dialect which lives alongside
Scheme has no official marketing name. For lack of a better name,
we'll call it traditional SKILL. Hereafter, I'll just use the
term SKILL++ rather than the SKILL++ Scheme Dialect.
If you haven't started using SKILL++ yet, the basics are simpler than
you might think. If you are completely new to SKILL, you'll find
SKILL++ easy and intuitive. If you're a long time SKILL programmer,
you'll do things in SKILL++ pretty much the way you would have wanted
to 25 years ago when you started learning SKILL. That is, you've already
learned the hard way; now you can take a look at the easy way.
How to create SKILL++ code
There are several ways to indicate to the SKILL interpreter that you
want to use SKILL++ rather than traditional SKILL. One way is to end
your file names with the .ils extension for SKILL++
and .il for traditional SKILL; file.ils is
SKILL++; file.il is traditional SKILL.
Please assume all the functions in the remainder of this article are
The first example function, walkCvHier, is an ultra-simple
design hierarchy traversal engine. It won't work for all purposes you
might need but is good for the purpose of the examples below.
1.1: (defun walkCvHier (cv consume)1.2: (foreach inst cv~>instances1.3: (walkCvHier inst~>master consume))1.4: (consume cv))
This function is a type of
function, because it takes a function as one of its arguments. It
is also possible with SKILL++ for functions to compute and create
other functions at run time according to their given arguments -- more
about this in a future article. Although it is sometimes possible to
use traditional SKILL to create higher-order functions with limited
capability, my advice is that you always use SKILL++ if you need this
How does it work? The function, walkCvHier, steps through
the explicit cellView hierarchy such as a layout, assuming you always
want to descend into the instance master. At each step of the
hierarchy, a given function is applied. You can pass in any function
as long as that function can take a cellView as its only argument.
The function, walkCvHier, will happily call that function
for you at each cellView found of the hierarchy, every time that
Saving the hierarchy?
Here's an example of how to use walkCvHier. The
function saveCvHier asks walkCvHier to
call dbSave on each cellView in the hierarchy.
2.1: (defun saveCvHier (cv)2.2: (walkCvHier cv dbSave))
The function walkCvHier has a local
variable consume, declared on line 1.1, which it uses on
line 1.4 in the function call position (consume cv).
Because this is SKILL++, consume does not refer to the
global function of that name, but since there is a local variable of
that name, that variable is used instead. The value of that variable
better be a function, else an error will occur at run-time. On line
2.2, the saveCvHier function calls dbSave as
this second argument. There are several important things to note here:
3.1: (defun _reportCv (cv)3.2: (println (list cv~>libName3.3: cv~>cellName3.4: cv~>viewName)))3.5:3.6: (defun reportCvHier (cv)3.7: (walkCvHier cv _reportCv))
The intent here is that _reportCv be private and
only be used in this one place. However, because it is defined
with defun (the same would be true if it were defined
with procedure) it is actually available globally. Its
private status cannot be enforced. In the past, privacy has been
implemented in traditional SKILL using naming conventions and
documentation. SKILL++ provides better fixes this problem by
providing ways systematic ways for
...to be continued...
In the next posting we'll look at local functions, closures (functions
with state), and a few slightly more advanced examples using
the walkCvHier function.
Hi Andrew, thanks for the comments. Dynamic variables are interesting creatures. They certainly provide the SKILL language with an expert feature that few languages can match, especially co-called modern ones. They'll become even more powerful in combination with some cool upcoming features of 615 and 616.
Probably some readers have not grasped the difference between dynamic and lexical scoping (Jim's explanation was a little terse) - in fact most people may have assumed that the scoping was actually lexical in traditional SKILL.
Lexical scoping is probably the more natural - the extent of the binding of a variable is the block of code (or text, hence the term "lexical") that it is defined in. So you can clearly see where a name is valid, and which variable is which. As I think Jim will show in future postings, you can use this lexical scoping for some pretty neat features in SKILL++.
Dynamic scoping can be thought of as a each variable being a stack - as you declare each variable in a let() (or similar) or define it as a formal parameter of a function, it pushes the new value onto the top of the stack, and then pops it off when that let() or function has returned. What this means is that the binding of a variable may not actually be global - it will see whatever is on the top of the stack at that point in the program's execution. Now this can be extremely useful, and allow you to do some powerful things, but it's probably more of an expert feature and for most users the effects of dynamic scoping only show up when you accidentally forget to declare a local variable.
So I would suggest that lexical scoping is more intuitive and easier to grasp and take advantage of for most novice users, and that dynamic scoping (or at least taking advantage of dynamic scoping) is more of an expert feature.
The lesson is - don't be scared by the "++" in the name SKILL++! You can completely ignore the object system if that scares you (although it shouldn't), yet still take advantage of the neat features that come as a result of lexical scoping without it hurting your brain!
Yes that's right. You can also use the inSkill and inScheme macros/primitives to surround code you want evaluated with particular scoping. E.g.,
(defvar XYZ 42))
(defvar XYZ -13))
For those who want to try it out interactively with a REPL, type
(toplevel 'ils) in the CIW and go from there.