• 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. Create custom ruler to report pre- or post-shrink value

Stats

  • Locked Locked
  • Replies 11
  • Subscribers 142
  • Views 6197
  • 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

Create custom ruler to report pre- or post-shrink value

Sheppie
Sheppie over 5 years ago

Hi,

We're working on a product in a technology which gets shrunk down. What we draw in the layout in a layout editor window, will be shrunk down by 0.855. So a drawn dimension of 1um (1000nm) becomes 0.855um (855nm). Currently we're in the floorplanning phase and in discussions about the final die-size. So every time I measure the new die-size (after more and more blocks report a more realistic area), I have to remember to apply this scaling factor before I report any new values.

With the bindkey <k>  the default ruler can be used to measure, and this ruler measures drawn dimensions. I'd like to have somehting like <shift.><k> to give me a ruler which applies the scaling factor to the measurement. Preferably in a different collor to be able to destinguish from one another.

Is this possible? Can I define my own ruler?

Thank you in advance.

With kind regards,

Sjoerd

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

    Hi Sjoerd,

    I don't think this is possible currently, but I can see the utility of this. I would suggest you contact customer support and request this as an enhancement (I couldn't find a similar enhancement request so far, but I may not have searched sufficiently thoroughly).

    Regards,

    Andrew.

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
Reply
  • Andrew Beckett
    Andrew Beckett over 5 years ago

    Hi Sjoerd,

    I don't think this is possible currently, but I can see the utility of this. I would suggest you contact customer support and request this as an enhancement (I couldn't find a similar enhancement request so far, but I may not have searched sufficiently thoroughly).

    Regards,

    Andrew.

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

    Hi Sjoerd,

    I had an idea this morning and thought of a way you could (sort of) do this by using a custom dynamic display function. You'd need this code (keep the .ils suffix):

    /* abScaledRuler.ils
    
    Author     A.D.Beckett
    Group      Custom IC (UK), Cadence Design Systems Ltd.
    Language   SKILL
    Date       Jan 20, 2020 
    Modified   
    By         
    
    A custom dynamic display function to scale the length of
    a ruler
    
    Use:
    
    abScaledRuler(0.9)
    
    to register, and then Options->Dynamic Display to turn it on
    (only works in VLS XL, and then under Objects, pick Ruler and
    enable Length and Custom SKILL Function)
    
    ***************************************************
    
    SCCS Info: @(#) abScaledRuler.ils 01/20/20.12:12:34 1.1
    
    */
    
    /*************************************************************************
    *                                                                        *
    *                     abScaledRuler(scaleFactor "f")                     *
    *                                                                        *
    *   Specify a custom dynamic display function for rulers which scales    *
    *  the length when you hover over it. The argument is the scale factor   *
    *                                                                        *
    * Note that this is enabled in VLS XL only, via Options->Dynamic Display *
    *                                                                        *
    *************************************************************************/
    
    procedure(abScaledRuler(scaleFactor "f")
        globalProc(abScaledRulerDisplay(dbId @optional _hierPath)
            let(((len 0.0) lastPoint)
                lastPoint=car(dbId~>points)
                foreach(point cdr(dbId~>points)
                    len=len+sqrt(
                        (xCoord(point)-xCoord(lastPoint))**2 +
                        (yCoord(point)-yCoord(lastPoint))**2
                    )
                    lastPoint=point
                )
                sprintf(nil "Scaled: %g" len*scaleFactor)
            )
        )
    
        odcRegRuler("Layout-XL" "abScaledRulerDisplay")
    )
    
    

    Then call abScaledRuler(0.9) or whatever your scale factor is, and then on Options->Dynamic Display (you need to be in VLS XL):

    Then when you hover over a ruler, you'll see:

    Hope that helps!

    Regards,

    Andrew.

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

    Hi Andrew,

    Thanks for you initial response. I haven't looked at you second response yet, will do that after posting this response.

    Since you think it is not possible, I thought I might add something to the measurement function: redefinning the bindkey to my own procedure which post-processes the marker drawn. Please have a look at the code below.

    Fullscreen 0028.SCLLeHiCreateMeasurement.il.txt Download
    hiSetBindKey( "Layout" "<Key>K" "SCLLeHiCreateMeasurement( geGetEditCellView() 0.855 )" )
    
    procedure( SCLLeHiCreateMeasurement( cvId scaleFactor )
    	let( (	preMarkerList postMarkerList newMarkerList
    		points deltaX deltaY distance distanceScaled )
    
    		;;; First, before adding a new ruler, store the list of already present markers.
    		;;; The list after adding will be compared with this list, and the additions will be calculated and reported.
    		preMarkerList = cvId~>markers
    
    		;;; Call of the original procedure.
    		leHiCreateMeasurement()
    
    		;;; Now that the original procedure has finished, store a new list of Markers.
    		;;; Unfortunately, only after the measurement function has been escaped, this code will proceed.
    		postMarkerList = cvId~>markers
    
    		when( newMarkerList = car( last( SCLDiffOfLists( preMarkerList postMarkerList ) ) )
    			foreach( newMarker newMarkerList
    				points = newMarker~>points
    				deltaX = abs( difference( xCoord( car( points ) ) xCoord( cadr( points ) ) ) )
    				deltaY = abs( difference( yCoord( car( points ) ) yCoord( cadr( points ) ) ) )
    				distance = sqrt( plus( times( deltaX deltaX ) times( deltaY deltaY ) ) )
    				distanceScaled = times( distance scaleFactor )
    				printf( "%L %L\n" deltaX deltaY )
    				printf( "After scaling (%L * %L): %L\n" distance  scaleFactor distanceScaled )
    			) ;;; end of foreach newMarker
    		) ;;; end of when
    	) ;;; end of let
    ) ;;; end of procedure SCLLeHiCreateMeasurement
    
    /*
    This procedure performs a diff of two lists. It does not take order into account though.
    Please test the code to see what it does:
    SCLDiffOfLists( list( 1 2 3 4 1 2 3 4 ) list( 3 4 5 6 ) )
    The output should be:
    ((1 2 1 2 3 4) (3 4) (5 6))
    This means:
    (1 2 1 2 3 4) is in the first list, but not in the second.
    	As you can see, if a value appears more then once in list1, but only once in list2, it will report this too.
    	Since this functionality was needed, simply using somethink like setof(item list1 !member( item list2 )) wouldn't work.
    	With the setof implementation the output would be different: (1 2 1 2)
    (3 4) is in both lists.
    (5 6) is not in the first list, but it is in the second list.
    */
    procedure( SCLDiffOfLists( inList1 inList2 )
    	let( ()
    		unless( listp( inList1 )
    			error( "SCLDiffOfLists: argument #1 must be a list - %L" inList1 )
    		) ;;; end of unless
    		unless( listp( inList2 )
    			error( "SCLDiffOfLists: argument #2 must be a list - %L" inList2 )
    		) ;;; end of unless
    
    		list( SCLDiffOfListsCheck( inList1 inList2 ) SCLDiffOfListsCheck( inList1 inList2 "common" ) SCLDiffOfListsCheck( inList2 inList1 ) )
    	) ;;; end of let
    ) ;;; end of procedure SCLDiffOfLists
    
    
    procedure( SCLDiffOfListsCheck( inList1 inList2 @optional ( mode "diff" ) )
    	let( (	( outList nil )
    		( value1 nil ) ( check1 nil )
    		( check2 nil )
    		( occurrance1 nil ) ( occurrance2 nil ) )
    
    		unless( listp( inList1 )
    			error( "SCLDiffOfListsCheck: argument #1 must be a list - %L" inList1 )
    		) ;;; end of unless
    		unless( listp( inList2 )
    			error( "SCLDiffOfListsCheck: argument #2 must be a list - %L" inList2 )
    		) ;;; end of unless
    		unless( stringp( mode )
    			error( "SCLDiffOfListsCheck: argument #3 must be a string - %L" mode )
    		) ;;; end of unless
    		unless( mode == "diff" || mode == "common"
    			error( "SCLDiffOfListsCheck: argument #3 must either be \"diff\" or \"common\" - %L" mode )
    		) ;;; end of unless
    
    		for( i 1 length( inList1 )
    			occurrance1 = 1
    			value1 = nthelem( i inList1 )
    			check1 = member( value1 inList1 )
    			while( ( ( length( inList1 ) - length( check1 ) ) + 1 ) != i
    				check1 = member( value1 cdr( check1 ) )
    				occurrance1++
    			) ;;; end of while
    
    			occurrance2 = 1
    			check2 = member( value1 inList2 )
    			while( occurrance2 <= occurrance1
    				if( check2
    				then
    					when( equal( mode "common" ) && occurrance2 == occurrance1
    						outList = append( outList list( value1 ) )
    					) ;;; end of when
    					check2 = member( value1 cdr( check2 ) )
    					occurrance2++
    				else
    					when( equal( mode "diff" )
    						outList = append( outList list( value1 ) )
    					) ;;; end of when
    					occurrance2 = occurrance1 + 1
    				) ;;; end of if check2
    			) ;;; end of while
    		) ;;; end of for i
    
    		;;; Returning the output...
    		outList
    
    	) ;;; end of let
    ) ;;; end of procedure SCLDiffOfListsCheck
    

    This calculates of each created ruler (marker) the distance and then applies the scaling. This is reported in the CIW. In order for this to work, in the first line the bindkey has been assigned to the custom procedure. Some additional procedures are included in order to make this work right out of the box.

    Next I'll have a look at your code.

    With kind regards,

    Sjoerd

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

    Hi Sjoerd,

    Yes, your approach could work too. 

    Another idea would be to create a label for each selected ruler. Using the code from before you could do something like:

    procedure(abAddLabelToMarkers(@optional (height 0.2) (selected geGetSelSet()) (cv geGetEditCellView()))
      let((label)
        foreach(ruler selected
          when(ruler~>objType=="ruler"
            label=dbCreateLabel(cv "text" car(ruler~>points) abScaledRulerDisplay(ruler) "lowerLeft" "R0" "stick" height)
            label~>parent=ruler
          )
        )
        t
      )
    )

    Something like that, anyway.

    Andrew.

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

    Hi Andrew,

    I've looked at your code and what I like about it is that it is nicely integrated into the Virtuoso (XL) framework. And. from my point of view, that's one of it's drawbacks as well: it only works in XL mode, I mostly work in standard L mode, so then this wouldn't work. The drawback of my implementation is that you need to have the CIW visible in order to see the scaled measurement, which is not needed for your solution. On top of that, a default bindkey has to be modified, which has to be managed in the project as well.

    With your code, you triggerd me on somehting else: multi-segment rulers. I updated my code to enable this as well (it would actually break if a multi segment ruler was created...). This is the update, just to be complete:

    Fullscreen 6215.SCLLeHiCreateMeasurement.il.txt Download
    /*
    Defining the bindkey:
    hiSetBindKey( "Layout" "<Key>K" "SCLLeHiCreateMeasurement( geGetEditCellView() 0.855 )" )
    */
    
    procedure( SCLLeHiCreateMeasurement( cvId scaleFactor )
    	let( (	preMarkerList postMarkerList newMarkerList
    		pointList prevPoint
    		deltaX deltaY distance distanceScaled )
    
    		;;; First, before adding a new ruler, store the list of already present markers.
    		;;; The list after adding will be compared with this list, and the additions will be calculated and reported.
    		preMarkerList = cvId~>markers
    
    		;;; Call of the original procedure.
    		leHiCreateMeasurement()
    
    		;;; Now that the original procedure has finished, store a new list of Markers.
    		;;; Unfortunately, only after the measurement function has been escaped, this code will proceed.
    		postMarkerList = cvId~>markers
    
    		when( newMarkerList = car( last( SCLDiffOfLists( preMarkerList postMarkerList ) ) )
    			foreach( newMarker newMarkerList
    				pointList = newMarker~>points
    				prevPoint = car( pointList )
    				distance = 0.0
    
    				foreach( point cdr( pointList )
    					deltaX = abs( difference( xCoord( point ) xCoord( prevPoint ) ) )
    					deltaY = abs( difference( yCoord( point ) yCoord( prevPoint ) ) )
    					distance = plus( distance sqrt( plus( deltaX**2 deltaY**2 ) ) )
    					prevPoint = point
    				) ;;; end of foreach point
    
    				distanceScaled = times( distance scaleFactor )
    				printf( "After scaling (%L * %L): %L\n" distance  scaleFactor distanceScaled )
    			) ;;; end of foreach newMarker
    		) ;;; end of when newMarkerList
    	) ;;; end of let
    ) ;;; end of procedure SCLLeHiCreateMeasurement
    
    /*
    This procedure performs a diff of two lists. It does not take order into account though.
    Please test the code to see what it does:
    SCLDiffOfLists( list( 1 2 3 4 1 2 3 4 ) list( 3 4 5 6 ) )
    The output should be:
    ((1 2 1 2 3 4) (3 4) (5 6))
    This means:
    (1 2 1 2 3 4) is in the first list, but not in the second.
    	As you can see, if a value appears more then once in list1, but only once in list2, it will report this too.
    	Since this functionality was needed, simply using somethink like setof(item list1 !member( item list2 )) wouldn't work.
    	With the setof implementation the output would be different: (1 2 1 2)
    (3 4) is in both lists.
    (5 6) is not in the first list, but it is in the second list.
    */
    procedure( SCLDiffOfLists( inList1 inList2 )
    	let( ()
    		unless( listp( inList1 )
    			error( "SCLDiffOfLists: argument #1 must be a list - %L" inList1 )
    		) ;;; end of unless
    		unless( listp( inList2 )
    			error( "SCLDiffOfLists: argument #2 must be a list - %L" inList2 )
    		) ;;; end of unless
    
    		list( SCLDiffOfListsCheck( inList1 inList2 ) SCLDiffOfListsCheck( inList1 inList2 "common" ) SCLDiffOfListsCheck( inList2 inList1 ) )
    	) ;;; end of let
    ) ;;; end of procedure SCLDiffOfLists
    
    
    procedure( SCLDiffOfListsCheck( inList1 inList2 @optional ( mode "diff" ) )
    	let( (	( outList nil )
    		( value1 nil ) ( check1 nil )
    		( check2 nil )
    		( occurrance1 nil ) ( occurrance2 nil ) )
    
    		unless( listp( inList1 )
    			error( "SCLDiffOfListsCheck: argument #1 must be a list - %L" inList1 )
    		) ;;; end of unless
    		unless( listp( inList2 )
    			error( "SCLDiffOfListsCheck: argument #2 must be a list - %L" inList2 )
    		) ;;; end of unless
    		unless( stringp( mode )
    			error( "SCLDiffOfListsCheck: argument #3 must be a string - %L" mode )
    		) ;;; end of unless
    		unless( mode == "diff" || mode == "common"
    			error( "SCLDiffOfListsCheck: argument #3 must either be \"diff\" or \"common\" - %L" mode )
    		) ;;; end of unless
    
    		for( i 1 length( inList1 )
    			occurrance1 = 1
    			value1 = nthelem( i inList1 )
    			check1 = member( value1 inList1 )
    			while( ( ( length( inList1 ) - length( check1 ) ) + 1 ) != i
    				check1 = member( value1 cdr( check1 ) )
    				occurrance1++
    			) ;;; end of while
    
    			occurrance2 = 1
    			check2 = member( value1 inList2 )
    			while( occurrance2 <= occurrance1
    				if( check2
    				then
    					when( equal( mode "common" ) && occurrance2 == occurrance1
    						outList = append( outList list( value1 ) )
    					) ;;; end of when
    					check2 = member( value1 cdr( check2 ) )
    					occurrance2++
    				else
    					when( equal( mode "diff" )
    						outList = append( outList list( value1 ) )
    					) ;;; end of when
    					occurrance2 = occurrance1 + 1
    				) ;;; end of if check2
    			) ;;; end of while
    		) ;;; end of for i
    
    		;;; Returning the output...
    		outList
    
    	) ;;; end of let
    ) ;;; end of procedure SCLDiffOfListsCheck
    

    Once again, thank you for your support.

    Kind regards,

    Sjoerd

    ps1:
    I read somewhere that your personal preference is not to put comments like "end of foreach"  after the closing bracket of a foreach loop (or any other if/for/while/unless/when/... statement). Since most of my code is way larger than this, and a typical loop is longer than my monitor is high, I created snippets in my text editor (Sublime Text) which automatically adds those closing comments. That's why you'll see them here as well.

    ps2:
    How do you insert code in such a nice frame (with grey-ish background) on this forum? In order to get something similar I insert a file, but this only supports .txt files, so I have to rename my .il files every time I want ot include it.

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

    Hi Sjoerd,

    To answer your two "ps" questions:

    ps1: I don't like this (I cover this in my "Writing Good SKILL Code" talk which is on Cadence Online Support) because it's too easy for the comments to get out of sync, and with a decent editor with bracket matching it becomes unnecessary. If the code is that long it probably should be broken up into smaller functions (I know that doesn't always make sense) so that's why I tend to avoid doing this nowadays (I went through a phase in the past until I decided it was potentially more confusing if the closing comments no longer matched the opening function).

    ps2: Sometimes this is because I'm pasting from an internal web site I have for my code which formats the code prettily. However, I also sometimes put the code in a file with a .txt suffix and then open that in my web browser (normally Safari, but also does this on Chrome) which then formats it using a Courier font. If I then copy and paste that into the post, it seems to then put the grey box around it.

    Andrew.

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

    Hi Andrew,

    Great idea, and it works great, except: the label does not scale with soom-level of the editor. The text of the ruler always remains readable since it simply gets larger (relative to the design) when zooming-out and gets relatively smaller when zooming-in. When using a label created by dbCreateLabel, it s a fixed size, not relateive to zoom level. Is it possible to create a "smart" label, i.e.: a label that scales with zoom-level?

    Kind regards,

    Sjoerd

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

    Hi Sjoerd,

    Unfortunately there's no "fixed point size" label; I meant to list this as a bit of a limitation...

    So overall my favourite solution of all of these is the dynamic display approach as that's the cleanest.

    Andrew

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

    Hi Andrew,

    I've implemented both solutions, with a "switch" for the user to enable/disable independantly (using a global variable).

    Thanks again for all the time you spend on this.

    Kind regards,

    Sjoerd

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

    Hi Sjoerd,

    I meant to also mention that you can cut out all that type checking code in your functions by using the type-checking string at the end of the argument list. For example:

    procedure( SCLDiffOfLists( inList1 inList2 "ll") ...)

    or

    procedure( SCLDiffOfListsCheck( inList1 inList2 @optional ( mode "diff" ) "llt" ) ...)

    Makes the code a bit cleaner and reduces the need for simple type checks.

    Andrew.

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

    Hi Andrew, This is code from many many years ago, before I learned that a procedure can do type-checking itself. It depends on what type of procedure I write, but if it is for common/general use, I do the type-checking like you proposed, otherwise I just skip it. And yes, it eliminates a lot of lines of code. 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