• 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. Cadence Layout SKILL: Automating alignment of instances...

Stats

  • Locked Locked
  • Replies 6
  • Subscribers 144
  • Views 11067
  • 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

Cadence Layout SKILL: Automating alignment of instances using prBoundary as reference

Phuc Bui
Phuc Bui over 2 years ago

Hi all,

I'm trying to automate the alignment of two components in Virtuoso Layout (IC 6.1.8-64b) by using some SKILL functions, and I'm wondering if anyone here has experience with coding that sort of script. The process I am trying to automate is in the image. I have two components, which both have prBoundaries defining where they should align. Manually doing Quick Align using the centre of the boundaries is viable for a small component, but the full device has plenty of these little alignments to do and I want to at least try and automate it with SKILL before resorting to optimise the manual Quick Align process. Has anyone here encountered something similar before?

Digging through the Virtuoso Layout SKILL Reference manual suggests that the leAlign function might be the key, but the difficulty is in defining where exactly the objects will be aligned. Ideally the automated script should identify both prBoundaries and snap them together but leAlign looks like it might need a lot of further modification to get that sort of boundary recognition implemented.

  • Cancel
Parents
  • Andrew Beckett
    Andrew Beckett over 2 years ago

    Not a SKILL solution as such, but one implemented as a PCell Designer "App Cell" (see appCell demo library (add-on to CDNLive EMEA 2019 CUS-Techtorial V)). Since there's no procedural interface to quick align, any SKILL-based solution would involve finding the prBoundary object in the lower level cells, transforming the coordinates, finding the centres of the appropriate edges and then computing the amount to move by. I managed to achieve the same thing with a few minutes of interactive work in PCell Designer by creating an appCell that worked on the selected objects. This works out which two edges are nearest and then aligns those (the edges of the prBoundary) using the built-in distance and align commands. 

    I've uploaded a gzipped tar file containing the deployed library (so you don't need PCell Designer to use this):

     forumPCDutils.tar.gz

    To use it, put the library into your cds.lib, and then select the two instances to align and then use: 

    ddGetObj("forumPCDutils" "alignTwoBlocks" "layout")->commands->AlignBoundaries()

    (you could put this on a bindkey). The first selected object is the one that stays still - the second is aligned to it.

    If you want to see how this is done, a screenshot of the structure in PCell Designer is here:

    and the pcd.txt file you can import into PCell Designer is here (you'll need the PCell Designer license for this):

    Fullscreen alignTwoBlocks_layout.pcd.txt Download
    ;;; PCell Designer File (format version 2.2)
    
    ;;; File-out of the PCell Designer pcell "NmosDemo24"/"alignTwoBlocks"/"layout"
    
    (pcdPcell
    
    ;; Mixins
    ?mixins
    nil
    
    ;; Create CDF?
    ?createCDF
    t
    
    ;; Reference cell for CDF
    ?referenceCDF
    nil
    
    ;; Main points
    ?mainPoints
    nil
    
    ;; Initial size
    ?initSize
    nil
    
    ;; Device Variables
    ?devVars
    nil
    
    ;; Device Variables visible to the commands
    ?guiVisibleDevVarNames
    nil
    
    ;; Methods used as commands
    ?methodCommands
    nil
    
    ;; Methods available as AppCell editors
    ?appCellEditors
    ((nil
      commandName AlignBoundaries
      parameters nil
      metaData (nil
                formCaption ""))
     )
    
    ;; PCell and CDF Parameters
    ?classEditorChanges
    ()
    
    ;; Simulation Information
    ?simulation
    (simulation)
    
    ;; Source Cell View
    ?sourceCellView
    ("NmosDemo24" "alignTwoBlocks" "layout")
    
    ;; Last Edited with PCell Designer Version
    ?pcdVersion
    "2-5-21"
    
    ;; Method definitions
    ?methods
    (
      (newCommand command (newCommand
                           ?name AlignBoundaries
                           ?commandClass pcmdCommand
                           ?category "MyAppCells"
                           ?help ""
                           ?parameters nil)
        body (
        (count 
          name (lvalue "numObjects" ?valuep t ?requiredp t ?isExpression nil ?keyword nil)
          source (geo (geo type "Expression" points nil group "" name nil net nil expression (append (selected))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword t))
        (unless 
          condition (string "numObjects==2" ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "The condition to check.")
          else 
          (body (
            (error 
              condition (string "t" ?valuep t ?requiredp t ?isExpression t ?keyword t ?help "The condition for generating the error.")
              style (enumeration "error" ?choices ("error_marker" "warning_marker" "error") ?allowOther nil ?valuep t ?requiredp t ?isExpression nil ?keyword t ?help "The kind of error to generate.")
              message (string "Must only have two objects selected" ?valuep t ?requiredp t ?isExpression nil ?keyword t ?help "The error message to display."))
            )
            ?valuep t
            ?requiredp t
            ?isExpression t
            ?keyword nil
          ))
        (set 
          settings (settings (("second" "nil" ?alert nil ?comment "" ?disabled nil)) ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "The name/value pairs to set."))
        (each 
          name (binding "obj" ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "Each time the body is executed, <i>name</i> will be set to a single result from <i>source</i>. <i>name</i> is only visible within the body.")
          source (geo (geo type "Expression" points nil group "" name nil net nil expression (append (selected))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "The geometry query go loop through.")
          body 
          (body (
            (set 
              settings (settings (("first" "second" ?alert nil ?comment "" ?disabled nil) ("second" "obj" ?alert nil ?comment "" ?disabled nil)) ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "The name/value pairs to set."))
            )
            ?valuep t
            ?requiredp t
            ?isExpression t
            ?keyword nil
          )
          style (each_style items ?valuep t ?requiredp t ?isExpression nil ?keyword t ?help "When looping over a set of shapes, controls whether the body is executed once for every shape, for every edge in every shape, or for every corner in every shape. Shapes are processed clockwise."))
        (distance 
          ?comment "Measure distances of bottom of one object to top of the other"
          name (lvalue "firstBotToTop" ?valuep t ?requiredp nil ?isExpression nil ?keyword nil ?help "The shortest distance of those measured.")
          from (geo (geo type "Expression" points nil group "" name "first" net nil expression (append (bbox (points (name "first"))))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "One or more points, outlines, or shapes.")
          from_handle (handle (edge south) ?valuep t ?requiredp nil ?isExpression nil ?keyword t ?help "Handle at the source shape for the measurement.")
          to (geo (geo type "Expression" points nil group "" name "second" net nil expression (append (bbox (points (name "second"))))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "One or more points, outlines, or shapes.")
          to_handle (handle (edge north) ?valuep t ?requiredp nil ?isExpression nil ?keyword t ?help "Handle for the measurement target.")
          result_type (distance_type y_distance ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "The format to return the disantce in.")
          body 
          (body (
            )
            ?valuep t
            ?requiredp t
            ?isExpression t
            ?keyword nil
          ))
        (distance 
          name (lvalue "secondBotToTop" ?valuep t ?requiredp nil ?isExpression nil ?keyword nil ?help "The shortest distance of those measured.")
          from (geo (geo type "Expression" points nil group "" name "second" net nil expression (append (bbox (points (name "second"))))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "One or more points, outlines, or shapes.")
          from_handle (handle (edge south) ?valuep t ?requiredp nil ?isExpression nil ?keyword t ?help "Handle at the source shape for the measurement.")
          to (geo (geo type "Expression" points nil group "" name "first" net nil expression (append (bbox (points (name "first"))))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "One or more points, outlines, or shapes.")
          to_handle (handle (edge north) ?valuep t ?requiredp nil ?isExpression nil ?keyword t ?help "Handle for the measurement target.")
          result_type (distance_type y_distance ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "The format to return the disantce in.")
          body 
          (body (
            )
            ?valuep t
            ?requiredp t
            ?isExpression t
            ?keyword nil
          ))
        (if 
          ?comment "Depending which distance is the shorter, align the second to the first appropriately"
          condition (string "abs(firstBotToTop)<abs(secondBotToTop)" ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "The condition.")
          then 
          (body (
            (align 
              source (geo (geo grow 0 type "Group" points nil group nil name nil net nil expression nil) ?lazyp nil ?valuep nil ?requiredp nil ?isExpression t ?keyword nil ?help "Shapes to move.")
              from_shape (geo (geo type "Name" points nil group nil name "second" net nil expression nil) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "Reference shape for the alignment.")
              from_handle (handle (point north) ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "Handle at the reference shape for the alignment.")
              target_shape (geo (geo type "Name" points nil group nil name "first" net nil expression nil) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "Target shape to align to.")
              target_handle (handle (point south) ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "Handle for the alignment target.")
              dx (float "0.0" ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "Additional x offset applied to the shapes after aligning.")
              dy (float "0.0" ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "Additional x offset applied to the shapes after aligning.")
              stretch (boolean nil ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "If true, stretch the shapes to align instead of moving them."))
            )
            ?valuep t
            ?requiredp t
            ?isExpression t
            ?keyword nil
          )
          else 
          (body (
            (align 
              source (geo (geo type "Name" points nil group nil name "second" net nil expression nil) ?lazyp nil ?valuep t ?requiredp nil ?isExpression t ?keyword nil ?help "Shapes to move.")
              from_shape (geo (geo type "Expression" points nil group nil name "second" net nil expression (append (bbox (points (name "second"))))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "Reference shape for the alignment.")
              from_handle (handle (point south) ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "Handle at the reference shape for the alignment.")
              target_shape (geo (geo type "Expression" points nil group nil name "first" net nil expression (append (bbox (points (name "first"))))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "Target shape to align to.")
              target_handle (handle (point north) ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "Handle for the alignment target.")
              dx (float "0.0" ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "Additional x offset applied to the shapes after aligning.")
              dy (float "0.0" ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "Additional x offset applied to the shapes after aligning.")
              stretch (boolean nil ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "If true, stretch the shapes to align instead of moving them."))
            )
            ?valuep t
            ?requiredp t
            ?isExpression t
            ?keyword nil
          ))
        
        ))
        
      (method command draw
        body (
        (group 
          ?renderInfo (?renderAsFigGroup nil)
          name (group "toAlign" ?valuep t ?requiredp t ?isExpression nil ?keyword nil ?help "The name of the group to create")
          body 
          (body (
            (instance 
              name (lvalue "" ?valuep t ?requiredp nil ?isExpression nil ?keyword nil ?help "The created instances, if any.")
              master (master ("pcell->libName" "\"blockwithpr\"" "\"layout\"") ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "The PCell master or standard cell view to make an instance of.")
              source (geo (geo type "Points" points ((0 0))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "The origins of the instances to create.")
              inst_name (string "" ?valuep t ?requiredp nil ?isExpression nil ?keyword t ?help "If given, the instance name. If ommitted, the instances will be named automatically.")
              array (sexp "(1 1)" ?valuep nil ?requiredp nil ?isExpression nil ?keyword t ?help "If given, create a mosaic of the requested number of columns and rows.")
              dx (float "0.0" ?valuep nil ?requiredp nil ?isExpression t ?keyword t ?help "When creating a mosaic, the x spacing. May be omitted.")
              dy (float "0.0" ?valuep nil ?requiredp nil ?isExpression t ?keyword t ?help "When creating a mosaic, the x spacing. May be omitted.")
              copy_terminals (boolean nil ?valuep t ?requiredp t ?isExpression nil ?keyword t ?help "If true, all terminals will be copied up to the current instance: the pin figures will be copied, on pins of the same name, on terminals of the same name.")
              transform (transformation ((2.695 -1.375) "R0" 1.0) ?valuep t ?requiredp t ?isExpression t ?keyword t ?help "Each origin point given in source is transformed by this transform; if there is an active current transform, this is taken into account.")
              user_edits (sexp nil ?valuep t ?requiredp t ?isExpression t ?keyword t)
              param_whitelist (sexp nil ?valuep nil ?requiredp nil ?isExpression t ?keyword t)
              param_blacklist (sexp nil ?valuep nil ?requiredp nil ?isExpression t ?keyword t)
              edit_master (boolean nil ?valuep t ?requiredp t ?isExpression nil ?keyword t)
              edit_transform (boolean t ?valuep t ?requiredp t ?isExpression nil ?keyword t)
              parameters (pcell_params (nil nil) ?valuep t ?requiredp t ?isExpression t ?keyword t ?help "Name-value pairs corresponding to the PCell parameters. To use a PCell's CDF interface, use the <code>edit parameters</code> command instead."))
            (instance 
              name (lvalue "" ?valuep t ?requiredp nil ?isExpression nil ?keyword nil ?help "The created instances, if any.")
              master (master ("pcell->libName" "\"blockwithpr\"" "\"layoutTwo\"") ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "The PCell master or standard cell view to make an instance of.")
              source (geo (geo type "Points" points ((0 0))) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword nil ?help "The origins of the instances to create.")
              inst_name (string "" ?valuep t ?requiredp nil ?isExpression nil ?keyword t ?help "If given, the instance name. If ommitted, the instances will be named automatically.")
              array (sexp "(1 1)" ?valuep nil ?requiredp nil ?isExpression nil ?keyword t ?help "If given, create a mosaic of the requested number of columns and rows.")
              dx (float "0.0" ?valuep nil ?requiredp nil ?isExpression t ?keyword t ?help "When creating a mosaic, the x spacing. May be omitted.")
              dy (float "0.0" ?valuep nil ?requiredp nil ?isExpression t ?keyword t ?help "When creating a mosaic, the x spacing. May be omitted.")
              copy_terminals (boolean nil ?valuep t ?requiredp t ?isExpression nil ?keyword t ?help "If true, all terminals will be copied up to the current instance: the pin figures will be copied, on pins of the same name, on terminals of the same name.")
              transform (transformation ((10.48 5.39) "R0" 1.0) ?valuep t ?requiredp t ?isExpression t ?keyword t ?help "Each origin point given in source is transformed by this transform; if there is an active current transform, this is taken into account.")
              user_edits (sexp nil ?valuep t ?requiredp t ?isExpression t ?keyword t)
              param_whitelist (sexp nil ?valuep nil ?requiredp nil ?isExpression t ?keyword t)
              param_blacklist (sexp nil ?valuep nil ?requiredp nil ?isExpression t ?keyword t)
              edit_master (boolean nil ?valuep t ?requiredp t ?isExpression nil ?keyword t)
              edit_transform (boolean t ?valuep t ?requiredp t ?isExpression nil ?keyword t)
              parameters (pcell_params (nil nil) ?valuep t ?requiredp t ?isExpression t ?keyword t ?help "Name-value pairs corresponding to the PCell parameters. To use a PCell's CDF interface, use the <code>edit parameters</code> command instead."))
            )
            ?valuep t
            ?requiredp t
            ?isExpression t
            ?keyword nil
          )
          lock (boolean nil ?valuep t ?requiredp t ?isExpression nil ?keyword t ?help "Add the shapes to a figGroup rather than a group"))
        (select 
          source (geo (geo grow 0 type "Group" points nil group "toAlign" name nil net nil expression nil) ?lazyp nil ?valuep t ?requiredp t ?isExpression t ?keyword t ?help "The shapes to select.")
          reference (geo (geo grow 0 type "Group" points nil group nil name nil net nil expression nil) ?lazyp nil ?valuep nil ?requiredp nil ?isExpression t ?keyword t ?help "Reference for partially selecting shapes")
          reset (boolean nil ?valuep t ?requiredp t ?isExpression nil ?keyword t ?help "Clear selection before selecting the shapes"))
        (AlignBoundaries )
        
        ))
        )
    
    ;; Imported local (Skill++) definitions
    ?importedCode
    nil
    
    ;; Saved state of the GUI
    ?guiData
    (nil
     selectedCommands nil
     activeTab "AlignBoundaries"
     openTabs ("AlignBoundaries" "draw")
     commandPaletteSelection (MyAppCells_AlignBoundaries)
     commandPalette (nil
                     Control\ Flow t
                     Measure t
                     Follow t
                     MyAppCells t))
    
    )
    

    Regards,

    Andrew

    • Cancel
    • Vote Up +1 Vote Down
    • Cancel
  • Phuc Bui
    Phuc Bui over 2 years ago in reply to Andrew Beckett

    Hi Andrew, 

    Thank you very much for this solution, it has been very helpful in my design automation process. Also, just a general thank you for all the other solutions you've provided on other threads, I'm still new to Cadence and EDA overall as an intern, and your many answers on this forum have been amazingly helpful. 

    On a quick note, do you know whether there would be a way to redefine ROD object boundaries in Layout? I'm trying to implement a pure SKILL solution based on what you sent and what Matthias sent down below, and I want to see whether the ROD object boundaries could be redefined, right now Layout is taking the absolute outer edges of the components, but the actual routing is further in and enclosed by other layers so getting things to line up requires getting to the object boundary and then doing a dbMoveFig until the routing matches up. Is there any way around this so the ROD boundary could be defined as where the routing ends?

    Best regards,

    Phuc

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett over 2 years ago in reply to Phuc Bui

    Phuc,

    I'm not sure what you mean by the "ROD object boundaries". I think for an instance, the handles in ROD are based on the bounding box of the instance; my approach was specifically looking for the prBoundary object (which may not extend to the full extent of the cellView); there's no control over this in ROD, unless the prBoundary object has been named with rodNameShape (probably fairly unlikely) - if it had you could refer to the hierarchical name for the rod object in each instance to align to.

    Maybe you weren't talking about aligning the prBoundary objects though - I was guessing based on your description.

    Andrew

    • Cancel
    • Vote Up +1 Vote Down
    • Cancel
  • Phuc Bui
    Phuc Bui over 2 years ago in reply to Andrew Beckett

    Hi Andrew,

    I was experimenting with the solution posted over there by Matthias because it's a pure-SKILL approach that I could integrate into a larger script, something I wasn't sure would be possible for your solution (that might just be because I'm about two weeks into learning SKILL from scratch). That other solution uses rodAlignObj and the ROD handles, so I was wondering whether there is a way to redefine the handles. 

    My hope is that I could define a boundary on the cellview that encloses only the routing, and then I could redefine the ROD handles based on that boundary. I tried to sketch out what I meant. The cells I am using will be rectangular, with the instance bounding box (blue) enclosing the actual devices and routing (red), so would there be a way to redefine the ROD handles to reflect the red boundary instead of the automatically defined instance bounding box? 

    My hope is to use the redefined ROD handles to align the instances and abut them up to the routing boundary, not the outer instance boundary. Something like this sketch below:

      

    If there isn't any simple solution, that's quite alright, the material that you sent, and the other user sent has been very helpful already. Again, thank you very much for your help on this, it is greatly appreciated, Andrew. 

    Best,

    Phuc

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
Reply
  • Phuc Bui
    Phuc Bui over 2 years ago in reply to Andrew Beckett

    Hi Andrew,

    I was experimenting with the solution posted over there by Matthias because it's a pure-SKILL approach that I could integrate into a larger script, something I wasn't sure would be possible for your solution (that might just be because I'm about two weeks into learning SKILL from scratch). That other solution uses rodAlignObj and the ROD handles, so I was wondering whether there is a way to redefine the handles. 

    My hope is that I could define a boundary on the cellview that encloses only the routing, and then I could redefine the ROD handles based on that boundary. I tried to sketch out what I meant. The cells I am using will be rectangular, with the instance bounding box (blue) enclosing the actual devices and routing (red), so would there be a way to redefine the ROD handles to reflect the red boundary instead of the automatically defined instance bounding box? 

    My hope is to use the redefined ROD handles to align the instances and abut them up to the routing boundary, not the outer instance boundary. Something like this sketch below:

      

    If there isn't any simple solution, that's quite alright, the material that you sent, and the other user sent has been very helpful already. Again, thank you very much for your help on this, it is greatly appreciated, Andrew. 

    Best,

    Phuc

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
Children
No Data

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