• Skip to main content
  • Skip to search
  • Skip to footer
Cadence Home
  • This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

  1. Community Forums
  2. Custom IC SKILL
  3. pp output of `case' expression in procedure definition changes...

Stats

  • Locked Locked
  • Replies 6
  • Subscribers 143
  • Views 3330
  • Members are here 0
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

pp output of `case' expression in procedure definition changes after the procedure is run

tweeksii
tweeksii over 7 years ago

After further debugging, I believe I have gotten to the root cause of my `case' woes:

> getVersion
"@(#)$CDS: virtuoso version ICADV12.3-64b 09/01/2017 10:34 (sjfhw305) $"
> sstatus printinfix nil
nil
> (defun foo ()
    (case (list "M2" "M3")
      ((("M1" "M2")) "V1")
      ((("M2" "M3")) "V2")))
foo
> (pp foo)
(procedure
    (foo)
    (case
        (list "M2" "M3")
        ((("M1" "M2")) "V1")
        ((("M2" "M3")) "V2")
    )
)
nil
> (foo)
"V2"
> (pp foo)
(procedure
    (foo)
    (case
        (list "M2" "M3")
        (("M1" "M2") "V1")
        (("M2" "M3") "V2")
    )
)
nil
> (let ((p (outfile "foo.il")))
    (pp foo p)
    (close p))
t
> (load "foo.il")
function foo redefined
t
> (foo)
nil
> 

Note that the second time `foo' is pretty-printed, the `case' statement has lost one level of parens in each clause, altering the semantics of the definition of `foo' when it is reloaded from its `pp' output.

In our environment, we use `pp' to print pcell code to a file so that it can be massaged (pre-processed) before being passed on to pcDefinePCell.  This unexpected behavior of `pp' caused one engineer's `case' expression to mysteriously fail inside our pcell compilation system.

  • Cancel
  • Andrew Beckett
    Andrew Beckett over 7 years ago

    Tom,

    The problem is that case() doesn't support comparison of a list as the matching value because it would then be ambiguous potentially with the situation where the target branches have a list of alternatives. Clearly it partly works - but it is not documented as working this way; the documentation says that the value compared should be a scalar value. As you can see, the pretty printer doesn't know about this usage and mangles it when decompiling.

    So I would advise using cond instead, or writing your own macro that looks like case, but expands into cond.

    Regards,

    Andrew.

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • tweeksii
    tweeksii over 7 years ago in reply to Andrew Beckett

    Well, my copy of the documentation for `case' (ICADV12.3 Sepetember [sic] 2017) is ambivalent about this.

    In the "Arguments" section, it says:

    l_clause1

    An expression whose first element is an atom or list of atoms to be compared against the value of g_keyForm. The remainder of the l_clause is evaluated if a match is found.

    But then in the "Examples" we have:

    Example 2

    listofnums = list(2 4 6)

    case( listofnums

    (( (1 2 3) ) 'onetwothree)

    (( (1 3 5)

    (7 9 11) ) 'odd)

    (( (2 4 6)

    (8 10 12) ) 'even)

    (t 'unknown))

    => even

    which is clearly an example of a list key.

    The Common Lisp Hyperspec says the first element in a `case' clause is "a designator for a list of objects", which would, by definition, include lists of lists (since a "list" is an "object"), though it does not provide any examples of such.  It does show in the "Notes" section what appears to be a macro expansion for `case', showing it implemented using `cond' and `member', like this code (which I actually tried on an online CLISP interpreter:

    The second `case' statement fails to match because the `member' function apparently defaults to use `eql' or some other test that doesn't work on lists, but you can see the final `cond' shows that it could work if #'equal was specified for the test, and indeed the equivalent code works in SKILL:

    (case 'M2
    ((M1 M2) 'V1)
    ((M3 M4) 'V2))
    (cond ((member 'M2 '(M1 M2)) 'V1)
    ((member 'M2 '(M3 M4)) 'V2))

    (case '(M2 M3)
    (((M1 M2)) 'V1)
    (((M2 M3)) 'V2))
    (cond ((member '(M2 M3) '((M1 M2))) 'V1)
    ((member '(M2 M3) '((M2 M3))) 'V2))

    So to my understanding, there is no ambiguity, even in the key-is-a-list-of-lists situation, because

    (case key
    (keys value))

    expands into (the equivalent of)

    (cond ((member key keys) value))

    when `keys' is a list, and 

    (cond ((member key '(keys) value))

    when `keys' is an atom.  Thus it doesn't matter if `keys' is a list of lists, or a list of atoms: the behavior in both situations makes sense and is unambiguous, and therefore I feel that the SKILL documentation, and the `pp' function, should be updated accordingly.

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett over 7 years ago in reply to tweeksii

    Tom,

    Apologies - I didn't see the second example when I read it - I saw the references to atom and scalar which implied that a list value wouldn't match.

    So I think the decompiler in pp needs to be fixed when the target is a list of a list (it's OK when it's a list of more than one list; the problem only occurs when there is a single list), and the documentation should point out that lists can be used as well as scalar atoms. I suggest you contact customer support for this (please reference this thread).

    As a (slightly awkward) workaround for this, if your original code was changed to:

    (defun foo ()
      (case (list "M2" "M3")
        ((("M1" "M2") ("M1" "M2")) "V1")
        ((("M2" "M3") ("M2" "M3")) "V2")))

    then it survives being pp'd and re-loaded. In this case the target values for each branch are a list of two lists (both identical because presumably you don't have an alternative, but I guess potentially you could reverse the order of the second list to cover the metal layers being out of order if that is ever going to happen in your pcell)

    Regards,

    Andrew.

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • tweeksii
    tweeksii over 7 years ago in reply to Andrew Beckett

    Apologies - I didn't see the second example when I read it - I saw the references to atom and scalar which implied that a list value wouldn't match.

    No worries!  To be honest, I wasn't sure either, until I'd researched it on the CL Hyperspec (which is even sadder considering that I seem to recall being the one who instigated Example 2's inclusion in the documentation in the first place...).  ^^;  

    So I think the decompiler in pp needs to be fixed when the target is a list of a list (it's OK when it's a list of more than one list; the problem only occurs when there is a single list), and the documentation should point out that lists can be used as well as scalar atoms. I suggest you contact customer support for this (please reference this thread).

    Thank you, Andrew--I will!  Some of my colleagues advised going straight to customer support, but I wanted to get your feedback first, because I've known you a long time through these forums, and have come to trust and value your opinion, while the good people in Cadence R&D remain hidden behind Customer Support's Silicon Curtain.

    I've confirmed your workaround code on my end.  I like your idea of including the reversed pair of metal layers:

    (defun foo ()
    (case (list "M2" "M3")
    ((("M1" "M2") ("M2" "M1")) "V1")
    ((("M2" "M3") ("M3" "M2")) "V2")))

    You inspired me to try adding a dummy nil value, which also works:

    (defun foo ()
    (case (list "M2" "M3")
    ((("M1" "M2") ()) "V1")
    ((("M2" "M3") ()) "V2")))

    (Assuming the layer pair can never be empty, of course.)

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett over 7 years ago in reply to tweeksii
    tweeksii said:
    To be honest, I wasn't sure either, until I'd researched it on the CL Hyperspec

    Thanks Tom for the kind words. I should point out that just because it's in the CL Hyperspec doesn't guarantee that the behaviour is or should be the same in SKILL. SKILL originated from Franz Lisp, and not Common Lisp which came later (although various aspects of Common Lisp have inspired newer capabilities in SKILL too). That said, it (Common Listp) is often a good reference for something that's been well thought out and if we can follow it in a compatible way then we should...

    Andrew.

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett over 7 years ago in reply to Andrew Beckett

    Also, if you wouldn't mind letting me know the CCR number when it comes (or even putting me on cc when you file the case - assuming you know my email address), that would be useful.

    Cheers,

    Andrew.

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel

Community Guidelines

The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.

© 2025 Cadence Design Systems, Inc. All Rights Reserved.

  • Terms of Use
  • Privacy
  • Cookie Policy
  • US Trademarks
  • Do Not Sell or Share My Personal Information