• 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. How to flatten a disembodied property list (DPL)?

Stats

  • Locked Locked
  • Replies 6
  • Subscribers 143
  • Views 2426
  • 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

How to flatten a disembodied property list (DPL)?

Sheppie
Sheppie 7 months ago

Hi,

I have a disembodied property list (DPL) which looks like this:

test = list(nil 'a '(1 2 3) 'b nil 'c '( nil 'd '(7 8 9) 'e '(10 11 12) ) 'f '( nil ) 'g '(13 14 15) )

test
(nil a (1 2 3) b nil
c (nil 'd '
    (7 8 9) 'e '
    (10 11 12)
) f (nil) g
(13 14 15)
)

So, every value of the symbol-value pair is either a list (see 'a or 'd or 'e or 'g), an other DPL (see 'c) an empty DPL (see 'f) or nil (see 'b).

After flattening, test should look like:

test
(1 2 3 7 8
    9 10 11 12 13
    14 15
)

So, the DPL has many "hierarchical levels", which all should collapse into one long list. Where an empty DPL and nil should not be included.
The problem I'm having is that I'm having difficulties finding the lowest point, and traversing back.
Or should I just create a procedure that starts at the top and calls itself when it encounters an other DPL?

Any suggestion is appreciated.

Thank you in advance.

With kind regards,

Sjoerd

  • Cancel
  • Andrew Beckett
    Andrew Beckett 7 months ago

    Sjoerd,

    I assume your list is really like this:

    test = list(nil 'a '(1 2 3) 'b nil 'c '( nil d (7 8 9) e (10 11 12) ) 'f '( nil ) 'g '(13 14 15) )

    rather than

    test = list(nil 'a '(1 2 3) 'b nil 'c '( nil 'd '(7 8 9) 'e '(10 11 12) ) 'f '( nil ) 'g '(13 14 15) )

    (otherwise it has quotes embedded within the quoted lists, which wouldn't make sense). Once you quote the outer list, everything within it is quoted.

    This recursive (non-destructive) function does (I think) what you want. It seems a rather unusual requirement...

    procedure(CCFflattenStrangeDPL(dpl)
      let((key value result)
        dpl=cdr(dpl)
        while(dpl
          key=car(dpl)
          value=cadr(dpl)
          dpl=cddr(dpl)
          result=lconc(result
            copy(
              if(listp(value) && !car(value) then
                CCFflattenStrangeDPL(value)
              else
                value
              )
            )
          )
        )
        car(result)
      )
    )
    

    Andrew

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • AaronSymko
    AaronSymko 7 months ago in reply to Andrew Beckett

    Andrew responded as I was coding up a solution, but figured I would post mine as well. Also, please refer to Andrew's post on fixing your DPL structure:

    procedure(flatten(data)
      cond(
        (dplp(data)
          foreach(mapcan key reverse(data->?)
            let((val)
              val = get(data key)
              if(listp(val)
                flatten(copy(val)) ; then
                ncons(val)
              )
            )
          )
        )
        (listp(data)
          foreach(mapcan val data
            if(listp(val)
              flatten(copy(val)) ; then
              ncons(val)
            )
          )
        )
        (t
          error("Unexpected value: %L\n" data)
        )
      )
    )
    test = '(nil a (1 2 3) b nil c (nil d (7 8 9) e (10 11 12)) f (nil) g (13 14 15))
    flatten(test) == '(1 2 3 7 8
        9 10 11 12 13
        14 15
    )
    
    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Sheppie
    Sheppie 7 months ago in reply to Andrew Beckett

    Hi Andrew,

    First of all: I like the procedure name you chose, neat!

    Second: I see now what mistake I made when I typed the test DPL.

    Regarding the strange usecase:

    In my flow, I have documents describing every macro block. In it is the terminal/pin description of that macro. I use Python to extract that information from those documents and generate a functional view (verilog) with all the pins of all macros that go out of the analog toplevel (big macro contaning all other macros) towards the digital macro. This verilog file is used to generate a symbol (create cellview from cellview). This symbol is used as basis to generate the schematic and add all levelshifters (currently over 2000... so way too many to do manually) to this schematic. I have defined the order (left to right) of all pins, such that they align nicely with the location of the sub-macros. Part of the code I've written is that all sub-macros are symbols in a DPL, with the value either being a list of terminal names, or, if sub-orders are defined, a new DPL. For the generation of the pins and adding of the level shifters it's much easier to have a long flat list, rather than a DPL, although that may work as well. So a very specific use-case indeed.

    This is how I specify the order:

    interfaceVars->layout->order = list( nil )
    interfaceVars->layout->order->rfio = nil
    interfaceVars->layout->order->tx = list( nil 'paldo nil 'padac nil 'tres nil 'ldo nil 'dtcycl nil 'skew nil 'other nil 'dca nil 'rfcalgen nil )
    interfaceVars->layout->order->pmu = list( nil 'macro nil 'cldo nil 'gldo nil 'bgap nil 'other nil )
    interfaceVars->layout->order->other = nil
    interfaceVars->layout->order->rxif = list( nil 'iir list( nil 'channel1 nil 'channel2 nil ) 'bbamp nil 'adc nil 'lodiv nil 'other nil )
    interfaceVars->layout->order->rxfe = list( nil 'lna list( nil 'channel1 nil 'channel2 nil ) 'ldo nil 'mix nil ' rfdet nil 'other nil )
    interfaceVars->layout->order->sx = list( nil 'other nil 'logen nil 'dtc nil ' rfsampler nil 'xobuf nil 'rfcounter nil 'dco nil )

    The odd thing is however, that in some cases, when I want to look at the content of order:

    interfaceVars->layout->order

    The order is reversed: first it shows:

    (nil sx (nil other nil logen nil
    dtc nil rfsampler nil xobuf
    nil rfcounter nil dco nil
    ) rxfe (nil lna nil ldo nil
    mix nil rfdet nil other
    nil
    )
    rxif (nil iir nil bbamp nil
    adc nil lodiv nil other
    nil
    ) other nil pmu
    (nil macro nil cldo nil
    gldo nil bgap nil other
    nil
    ) tx (nil paldo nil padac nil
    tres nil ldo nil dtcycl
    nil skew nil other nil
    dca nil rfcalgen nil
    ) rfio nil
    )

    So first sx, and finishes with rfio.

    Whereas when I do:

    interfaceVars->layout->order->?

    It shows the proper order:

    (rfio tx pmu other rxif
        rxfe sx
    )

    Unfortunately, with both your solution and the solution of AaronSymko (see below), the result is in the wrong order. Simply reversing it doesn't work: yes, the macro order becomes correct, but the sub-macro order is still incorrect: it starts with rfio and then tx... But with tx for instance, it starts with rfcalgen, in stead of paldo. When I look at the DPL with the list of terminals/pins (I'll take a look at your code to see where I can properly apply the reverse.

    The odd thing is: with the numerical example I gave it works fine...

    Thank you very much for your quick response, and neat solution, I certainly couldn't do it any shorter or interesting.

    With kind regards,

    Sjoerd

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Sheppie
    Sheppie 7 months ago in reply to AaronSymko

    Hi AAronSymko,

    Thank you very much foryour solution and time. Please see my response to Andrews solution as well.

    With kind regards,

    Sjoerd

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett 7 months ago in reply to Sheppie

    Sjoerd,

    The trouble is that DPLs are not really intended to be treated as an ordered data structure. The mechanism is that when you add a new slot via dpl->slot=value then if there's no key called "slot" in the DPL already, it will insert it at the beginning (after the initial dummy value in the DPL) because that's the most efficient place to add it. The ->? operator happens to collect the slot names in reverse, but I don't think that's documented behaviour (I would be wary of assuming that). Because you are constructing the top-level slots in sequence, these are reversed - but the lower level DPLs are specified literally as lists and so they are in the order they were specified.

    You'd need to know the expected order in each level which adds to the complexity.

    This variant might do what you want:

    CCFflattenStrangeDPL(interfaceVars->layout->order t) ; t is to reverse the top level

    procedure(CCFflattenStrangeDPL(dpl @optional reverse)
      let((key value result subResult)
        dpl=cdr(dpl)
        while(dpl
          key=car(dpl)
          value=cadr(dpl)
          dpl=cddr(dpl)
          subResult=
            copy(
              if(listp(value) && !car(value) then
                ; don't pass down the reverse flag
                ; so only reverse the top level
                CCFflattenStrangeDPL(value)
              else
                value
              )
            )
          if(reverse then
            result=cons(subResult result)
          else
            result=lconc(result subResult)
          )
        )
        if(reverse then
          foreach(mapcan item result item)
        else
          car(result)
        )
      )
    )
    

    Andrew

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Sheppie
    Sheppie 7 months ago in reply to Andrew Beckett

    Hi Andrew,

    Thank you for the explanation. Your explanation confirms what I see happening.

    I'll test the code later today.
    By the way: is there an other (prefered, easily "traversable" ) way of specifying order in Virtuoso/SKILL?

    With kind regards,

    Sjoerd

    • 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